http://csiflabs.cs.ucdavis.edu/~ssdavis/40/prog4.pdf For this assignment you wil
ID: 3667022 • Letter: H
Question
http://csiflabs.cs.ucdavis.edu/~ssdavis/40/prog4.pdf
For this assignment you will be adding more functionality to Program #3. Program #4 will process assembly code that involves arrays, function calls, and branching. As usual, all implementation code should be in the .cpp files. You are free (and encouraged) to use my source code for p3 as your starting point. In any case, you may wish to refer to my code if you do not understand some of the references in the instructions. There are two new assembly files, test4.s and test5.s. They and their C files are attached. Both files rely on using labels to indicate addresses in the code, e.g. jg .L2, and call _Z3fooii. Your program will need to be able to find a label and determine its address. You will have a Labels class object that will hold the labels with their addresses. There are two new registers, edx, and flags. edx is a data register often used to hold an index. For arrays, we have a new addressing mode, scaled index mode. There is only one example this week: -72(%ebp, %edx, 4). The address of the value sought would be the value in ebp + four times the value in edx minus 72, i.e., %ebp + 4 * %edx – 72. The four is used because it is the size of an int. The sixth register is the flags register. For this assignment, we will only use two bits of the flags int. The sign flag, SF, is bit 7 (counting with bit zero as the least significant bit) and is set to one when a result is negative, else it is zero. The zero flag, ZF, is bit 6, and is set to one when a result is zero, else it is set to zero. There are no instructions that explicitly set or read the flags register, but many instructions implicitly use it. cmpl, addl, incl, andl, and subl all can change the flags. The two conditional jump instructions, jg, and jle rely on the values of the flags to determine whether the eip should be changed. You should alter your code to process test4.s, and then add the code to make test5.s work. Your code should compile without warnings, and run properly with test2.s after each step. 1. test4.s 1.1. Change the Reader::read() method to an overloaded extraction (>>) operator, and have main() call it by having main() opening the file. 1.2. Create a Labels class, and create a labels object in main(). 1.2.1. class Labels has an array of 100 Instructions, and an int count. 1.2.2. Intel assembly language labels start their lines with either “.L”,or an underscore and end with a colon. When storing a label you should strip the colon from it. 1.2.3. main() should call an overloaded extraction operator (>>) to read the labels into its array from the file. This means the file will be read twice, once by Labels’ operator>>, and once by the Reader’s operator>>. To rewind an open file you will need to use seekg(), but before that you will need to call clear() to the failbit of the file. The Reader’s operator>> code is a good place to use as a starting point. 1.2.4. Add a find() method that takes a string and returns its address. 1.3. Alter the Registers class in the following manner. 1.3.1. Add the edx and flags registers to regs, and add edx and flags to the enum. 1.3.2. Change Registers::initialize() to a default constructor, and just initialize memory[1000] in main(). 1.3.2.1. Initialize edx to zero, and flags to 0xC0. 1.3.3. Replace the print() method with an overloaded insertion operator (<<) that also prints the edx and flags registers. main() will now print out the instruction’s info. 1.3.4. Move the enum into the public part of the Registers class. This would allow other classes to safely have their own ebx, esp … constants. 1.3.4.1. Change all parameters that were RegName to Registers::RegName 1.3.4.2. Change all code outside the Registers class that references the enums to Registers::enum, e.g. Registers::eax. 1.3.5. Add a += operator that takes an int as its parameter, and adds it to the existing value of esp. It returns the new value of esp. Note that this can be used to add or subtract values from the esp. Use of this operator should replace the Registers::set(…Registers::get()) esp calls in decoder.cpp 1.3.6. address() will have to altered to handle labels and scaled index addressing mode. It will now have a Labels object has an additional const reference parameter. At this point, scaled index addressing mode can be differentiated because it is the only addressing mode that involves two commas. The actual registers involved in scaled index addressing mode cannot be assumed to be always the same. Since functions not using switch() should have bodies of less than 35 lines, the address() method will become too large. You should create additional new functions to handle the complexity of addressing modes. I spun off a private helper function called stringToRegNum(). Beware that since parse() uses strtok(), address() and its subroutines cannot. 1.3.7. Alter the Decoder::parse() by passing it a Labels object that is passed to address(). 1.4. Alter the existing Decoder addl, subl, andl functions to change the bits in the flags register based on their results. 1.4.1. Add a setFlags() method to Registers that will set the flags based on the int parameter. Note that the parameter is the result of the operation, not the value to which the flags should be set. 1.5. Decoder now has these additional instructions to implement, and call from Decoder::execute() 1.5.1. Add appropriate methods to the Registers class to read a specific flag as a bool. 1.5.2. cmpl operand1, operand2 : sets the flags register based on subtracting operand1 from operand2. The result is not stored anywhere. 1.5.3. incl operand : increments operand, and sets the SF and ZF accordingly. 1.5.4. jg address : jump if greater, so place address in eip if SF is clear and ZF is clear. 1.5.5. jle address : jump if less than or equal, so place address in eip if SF is set or ZF is set. 1.5.6. jmp address : place address in the eip. 1.5.7. leal operand1, operand2 : load the effective address of operand1 into operand2. 1.5.7.1. For example, the effective address of –16(%ebp) is the value held in ebp minus 16. The address() method will not help you with determining the effective address of operand1. 1.5.7.2. You will have to write your own parsing code for operand1. However, for this assignment, you may assume that it will always use the value held in ebp. So you need only parse out the number in front of the register. 1.6. Your program should now be able to run test4.s 2. test5.s 2.1. The Reader operator>> will have to ignore labels that start with an underscore. 2.2. The Labels operator>> will now have to detect “main:”. main(), should set the eip to the address of “main:”. 2.3. Decoder will now have these additional instruction to implement, can call from Decoder::execute() 2.3.1. call address : pushes the value in eip on the stack and puts address in eip. To push on the stack you decrement esp by four, and then copy eip to the location that esp now indicates. Make sure your pushl also does this in the same order, first decrement, then copy. 2.3.2. sall operand1, operand2 : shift operand2 left operand1 bits, and changes the flags accordingly. Further specifications: 1.1. const must be used wherever possible in function headings. This includes parameters, return types, and functions themselves. Note that there is no need to label parameters and return types that are passed by value as const. 1.2.You may assume that all input will be valid, and not require any form of range checking. 1.3.Your Makefile file must contain six pairs of compiling lines, and one pair of linking lines. You must use g++ with the –g –Wall –ansi options for compiling and linking. You will lose one point for each warning. 1.4.You will find test?.c, test?.S, and my own executable in ~ssdavis/40/p4 Thursday morning. You will find my p3 source code available in ~ssdavis/40/p3/SeansSrc on Thursday morning
Its due in 3 hours.
Explanation / Answer
decoder.cpp
#include <cstring>
#include <cstdlib>
#include "decoder.h"
void Decoder::parse(Instruction *instruction,
Registers *registers,
Labels *labels,
int *memory)
{
char *token;
char *instructionCopy = new char[strlen(instruction->getInfo()) + 1];
// copy info to operate on it, instead of the original
strcpy(instructionCopy, instruction->getInfo());
// get the first token, which is saved in opcode
token = strtok(instructionCopy, " ");
strcpy(opcode, token);
token = strtok(NULL, " ");
if (token != NULL) // save the second token, if it exists
{
operand1 = registers->address(token, memory, *labels);
token = strtok(NULL, " ");
if (token != NULL) // save the third token, if it exists
operand2 = registers->address(token, memory, *labels);
} // if
delete[] instructionCopy;
} // parse()
void Decoder::execute(Registers *registers,
int *memory,
Instruction *instruction)
{
if (!strcmp(opcode, "addl")) addl(registers);
if (!strcmp(opcode, "andl")) andl(registers);
if (!strcmp(opcode, "movl")) movl();
if (!strcmp(opcode, "subl")) subl(registers);
if (!strcmp(opcode, "ret")) ret(registers, memory);
if (!strcmp(opcode, "leave")) leave(registers, memory);
if (!strcmp(opcode, "pushl")) pushl(registers, memory);
if (!strcmp(opcode, "cmpl")) cmpl(registers);
if (!strcmp(opcode, "incl")) incl(registers);
if (!strcmp(opcode, "jg")) jg(registers);
if (!strcmp(opcode, "jle")) jle(registers);
if (!strcmp(opcode, "jmp")) jmp(registers);
if (!strcmp(opcode, "leal")) leal(registers, instruction);
if (!strcmp(opcode, "call")) call(registers, memory);
if (!strcmp(opcode, "sall")) sall(registers);
} // execute()
void Decoder::addl(Registers *registers) const
{
int result = *operand2 += *operand1;
registers->setFlags(result);
} // addl()
void Decoder::andl(Registers *registers) const
{
int result = *operand2 = (*operand1 & *operand2);
registers->setFlags(result);
} // andl()
void Decoder::movl()
{
*operand2 = *operand1;
} // movl()
void Decoder::subl(Registers *registers) const
{
*operand2 = *operand2 - *operand1;
int result = *operand2;
registers->setFlags(result);
} // subl()
void Decoder::cmpl(Registers *registers) const
{
int result = *operand2 - *operand1;
registers->setFlags(result);
} // cmpl
void Decoder::incl(Registers *registers)
{
*operand1 += 1;
int result = *operand1;
registers->setFlags(result);
} // incl
void Decoder::jg(Registers *registers)
{
if (!registers->getSF() && !registers->getZF()) // SF and ZF clear
registers->set(Registers::eip, *operand1);
} // jg
void Decoder::jle(Registers *registers)
{
if (registers->getSF() || registers->getZF()) // SF or ZF set
registers->set(Registers::eip, *operand1);
} // jle
void Decoder::jmp(Registers *registers)
{
registers->set(Registers::eip, *operand1);
} // jmp
void Decoder::leal(Registers *registers, Instruction *instruction)
{
char regNames[6][7] = {"eax", "ebp", "esp", "eip", "edx"};
char *info = new char[strlen(instruction->getInfo()) + 1];
strcpy(info, instruction->getInfo());
// get additive
int additive = 0;
char *p = info;
char *additiveEnd = strstr(info, "(");
while (p < additiveEnd)
{
if (*p++ == ' ')
additive = atoi(p);
} // while
int regNum;
int regNum1 = 0;
int regNum2 = 0;
size_t commaIndex = strstr(info, ",") - info;
for (regNum = Registers::eax; regNum <= Registers::edx; regNum++)
{
char *found = strstr(info, regNames[regNum]);
if (found)
{
if (found < &info[commaIndex]) regNum1 = regNum;
else regNum2 = regNum; // else
} // if
} // for
registers->set(regNum2, registers->get(regNum1) + additive);
} // leal
void Decoder::leave(Registers *registers, int memory[]) const
{
registers->set(Registers::esp, registers->get(Registers::ebp));
registers->set(Registers::ebp, memory[registers->get(Registers::esp)]);
*registers += 4;
} // leave()
void Decoder::ret(Registers *registers, int memory[]) const
{
int val = memory[registers->get(Registers::esp)];
registers->set(Registers::eip, val);
*registers += 4;
} // ret()
void Decoder::pushl(Registers *registers, int memory[]) const
{
*registers += -4;
memory[registers->get(Registers::esp)] = *operand1;
} // pushl()
void Decoder::call(Registers *registers, int memory[])
{
*registers += -4;
memory[registers->get(Registers::esp)] = registers->get(Registers::eip);
registers->set(Registers::eip, *operand1);
} // call
void Decoder::sall(Registers *registers)
{
*operand2 = *operand2 << *operand1;
registers->setFlags(*operand2);
} // sall
decoder.h
#ifndef DECODER_H
#define DECODER_H
#include "instruction.h"
#include "registers.h"
#include "labels.h"
class Decoder
{
private:
char opcode[20];
int *operand1;
int *operand2;
void addl(Registers *registers) const;
void andl(Registers *registers) const;
void movl();
void subl(Registers *registers) const;
void leave(Registers *registers, int memory[]) const;
void ret(Registers *registers, int memory[]) const;
void pushl(Registers *registers, int memory[]) const;
void cmpl(Registers *registers) const;
void incl(Registers *registers);
void jg(Registers *registers);
void jle(Registers *registers);
void jmp(Registers *registers);
void leal(Registers *registers, Instruction *instruction);
void call(Registers *registers, int memory[]);
void sall(Registers *registers);
public:
void execute(Registers *registers, int *memory, Instruction *instruction);
void parse(Instruction *instruction,
Registers *registers,
Labels *labels,
int *memory);
}; // Decoder
#endif
instruction.cpp
#include <cstring>
#include "instruction.h"
Instruction::Instruction()
{
info = NULL;
} // Constructor
Instruction::~Instruction()
{
if (info != NULL)
{
delete[] info;
info = NULL;
} // if
} // Destructor
const char *Instruction::getInfo() const
{
return info;
} // getInfo()
int Instruction::getAddress() const
{
return address;
} // getAddress
void Instruction::setInfo(const char *newInfo)
{
info = new char[strlen(newInfo) + 1];
strcpy(info, newInfo);
} // setInfo()
void Instruction::setAddress(int newAddress)
{
address = newAddress;
} // setAddress()
instruction.h
#ifndef INSTRUCTION_H
#define INSTRUCTION_H
class Instruction
{
char *info;
int address;
public:
Instruction();
~Instruction();
const char *getInfo() const;
int getAddress() const;
void setInfo(const char *newInfo);
void setAddress(int newAddress);
}; // Instruction
#endif
labels.cpp
#include <iostream>
#include <fstream>
#include "labels.h"
ifstream &operator>>(ifstream &inf, Labels &labels)
{
string lineBuffer;
int lineNumber = 0;
if (inf)
{
inf.seekg(0);
while (getline(inf, lineBuffer))
labels.processLine(lineBuffer, &lineNumber);
inf.clear();
} // if
return inf;
} // >>
void Labels::processLine(string lineBuffer, int *lineNumber)
{
int endsWithColon = lineBuffer[lineBuffer.length() - 1] == ':';
int startsWithL = (lineBuffer[0] == '.' && lineBuffer[1] == 'L');
int startsWithOther = (lineBuffer[0] == '_' || lineBuffer[0] == 'm');
if (endsWithColon && (startsWithL || startsWithOther) ) // check line
{
lineBuffer[lineBuffer.length() - 1] = ''; // strip colon
const char *info = lineBuffer.c_str();
instructions[count].setInfo(info);
instructions[count].setAddress(100 + *lineNumber * 4);
count++;
return; // skip instr counting because we know it's a label
} // if
if (lineBuffer[0] != '.'
&& lineBuffer[1] != '.'
&& lineBuffer[0] != '_') // count instr lines beginning with '.' or '_'
*lineNumber += 1;
} // processLine
int Labels::find(char *info) const
{
size_t i;
for (i = 0; i < sizeof(instructions); i++)
{
if (strcmp(info, instructions[i].getInfo()) == 0)
return instructions[i].getAddress();
} // for
return 0;
} // find
labels.h
#ifndef CPU2_LABELS_H
#define CPU2_LABELS_H
#include <iosfwd>
#include <cstring>
#include "instruction.h"
using namespace std;
class Labels
{
private:
Instruction instructions[100];
int count;
public:
friend ifstream& operator>>(ifstream& inf, Labels &labels);
int find(char *info) const;
void processLine(string lineBuffer, int *lineNumber);
}; // Labels
#endif //CPU2_LABELS_H
main.cpp
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <iomanip>
#include "reader.h"
#include "decoder.h"
using namespace std;
int main(int argc, char *argv[])
{
Reader reader;
Decoder decoder;
Registers registers = Registers();
Labels labels;
Instruction *instruction;
int memory[1001];
memory[1001] = 0;
ifstream inf(argv[1]);
if (argc == 1) // no argument
{
cout << "A file needs to be passed as command line parameter";
exit(1);
} // if
inf >> reader;
inf >> labels;
char main[] = "main";
registers.set(Registers::eip, labels.find(main)); // set eip to main:
int i = 0;
while (registers.get(Registers::eip) != 0) // main loop
{
instruction = reader.fetch(®isters);
decoder.parse(instruction, ®isters, &labels, memory);
decoder.execute(®isters, memory, instruction);
cout << left << setw(20) << instruction->getInfo() + 1;
cout << registers;
i++;
} // while
exit(0);
} // main()
reader.cpp
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <iomanip>
#include "reader.h"
#include "decoder.h"
using namespace std;
int main(int argc, char *argv[])
{
Reader reader;
Decoder decoder;
Registers registers = Registers();
Labels labels;
Instruction *instruction;
int memory[1001];
memory[1001] = 0;
ifstream inf(argv[1]);
if (argc == 1) // no argument
{
cout << "A file needs to be passed as command line parameter";
exit(1);
} // if
inf >> reader;
inf >> labels;
char main[] = "main";
registers.set(Registers::eip, labels.find(main)); // set eip to main:
int i = 0;
while (registers.get(Registers::eip) != 0) // main loop
{
instruction = reader.fetch(®isters);
decoder.parse(instruction, ®isters, &labels, memory);
decoder.execute(®isters, memory, instruction);
cout << left << setw(20) << instruction->getInfo() + 1;
cout << registers;
i++;
} // while
exit(0);
} // main()
reader.h
#ifndef READER_H
#define READER_H
#include <cstring>
#include "instruction.h"
#include "registers.h"
using namespace std;
class Reader
{
private:
Instruction lines[1000];
public:
void saveLine(string lineBuffer, int *lineNumber);
friend ifstream& operator>>(ifstream &inf, Reader &reader);
Instruction *fetch(Registers *registers);
}; // class
#endif
registers.cpp
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <iomanip>
#include "registers.h"
using namespace std;
Registers::Registers()
{
regs[Registers::esp] = 1000;
regs[Registers::ebp] = 996;
regs[Registers::eip] = 100;
regs[Registers::eax] = 0;
regs[Registers::edx] = 0;
regs[Registers::flags] = 0xC0;
} // initialize()
ostream &operator<<(ostream &out,
Registers ®isters)
{
cout << " eip: " << right << setw(3) << registers.regs[Registers::eip];
cout << " eax: " << right << setw(3) << registers.regs[Registers::eax];
cout << " ebp: " << right << setw(3) << registers.regs[Registers::ebp];
cout << " esp: " << right << setw(3) << registers.regs[Registers::esp];
cout << " edx: " << right << setw(3) << registers.regs[Registers::edx];
cout << " flags: " << right << setw(3) << registers.regs[Registers::flags];
cout << " ";
return out;
} // <<
int *Registers::address(char *operand, int memory[], const Labels &labels)
{
static int value;
if (operand == NULL) return NULL;
if (operand[0] == '$') // immediate mode
{
value = atoi(&operand[1]);
return &value;
} // if
if (operand[0] == '.' || operand[0] == '_') // operand is a label
{
value = labels.find(operand);
return &value;
} // if
size_t i;
int commaIndex = 0;
for (i = 0; i < strlen(operand); i++)
{
if (operand[i] == ',')
{
if (commaIndex == 0) // scale index mode
commaIndex = i;
else // scale
{
return processScale(extractAdditive(operand), operand, commaIndex, memory);
} // else
} // if
} // for
return processNormal(operand, memory);
} // address()
int Registers::extractAdditive(char *operand)
{
size_t i;
int additive = 0;
for (i = 0; i < strlen(operand); i++)
{
if (operand[i] == '(') // additive
{
char *additiveString = new char[i];
memcpy(additiveString, operand, i);
additiveString[i] = 0; // Add terminator
additive = atoi(additiveString);
return additive;
} // if
} // for
return additive;
} // extractAdditive
int * Registers::processScale(int additive, char *operand, int commaIndex, int *memory)
{
int scale = 0;
char *p = operand;
while (*p)
{
if (*p++ == ',')
scale = atoi(p);
}
int regNum1 = 0;
int regNum2 = 0;
int regNum;
char regNames[6][7] = {"eax", "ebp", "esp", "eip", "edx", "flags"};
// find regs
for (regNum = Registers::eax; regNum <= Registers::eip; regNum++)
{
char *found = strstr(operand, regNames[regNum]);
if (found)
{
if (found < &operand[commaIndex]) // reg before comma
regNum1 = regNum;
else // reg after comma
regNum2 = regNum;
} // if
} // for
return &memory[regs[regNum1] + scale * regs[regNum2] + additive];
} // processScale
int *Registers::processNormal(char *operand, int memory[])
{
char regNames[6][7] = {"eax", "ebp", "esp", "eip", "edx", "flags"};
char *ptr;
int regNum, index;
for (regNum = Registers::eax; regNum <= Registers::eip; regNum++)
if (strstr(operand, regNames[regNum]))
break;
ptr = strchr(operand, '(');
if (ptr) // some form of indirect addressing
{
*ptr = ''; // terminate operand str at first '('
index = atoi(operand); // will return 0 if no number there!
return &memory[regs[regNum] + index];
} // if
else // direct addressing
return ®s[regNum];
} // processNormal
int Registers::get(int address) const
{
size_t a = (size_t) address;
if (a < sizeof(regs))
{
return regs[address];
} // if
else // index outside range
{
return 0;
} // else
} // get()
void Registers::set(int address, int value)
{
size_t a = (size_t) address;
if (a < sizeof(regs) / sizeof(int))
regs[address] = value;
} // set()
int Registers::operator+=(int num)
{
regs[Registers::esp] += num;
return regs[Registers::esp];
} // +=
void Registers::setFlags(int result)
{
if (result < 0) // SF
regs[Registers::flags] |= 1 << 7;
else regs[Registers::flags] &= ~(1 << 7); // clear
if (result == 0) // ZF
regs[Registers::flags] |= 1 << 6;
else regs[Registers::flags] &= ~(1 << 6); // clear
} // setFlags
bool Registers::getSF()
{
bool set = ((regs[Registers::flags] >> 7) & 1) == 1;
return set;
} // getSF
bool Registers::getZF()
{
bool set = ((regs[Registers::flags] >> 6) & 1) == 1;
return set;
} // getZF
registers.h
#ifndef REGISTERS_H
#define REGISTERS_H
#include <iostream>
#include "instruction.h"
#include "labels.h"
using namespace std;
class Registers
{
int regs[6];
int extractAdditive(char *operand);
int * processScale(int additive, char *operand, int commaIndex, int *memory);
int *processNormal(char *operand, int memory[]);
public:
int *address(char *operand, int memory[], const Labels &labels);
typedef enum {eax, ebp, esp, eip, edx, flags} RegName;
Registers();
friend ostream& operator<<(ostream &os, Registers ®isters);
int operator+=(int num);
int get(int address) const;
void set(int address, int value);
bool getSF();
bool getZF();
void setFlags(int num);
}; // Registers
#endif
test1.c
#include <stdio.h>
int main()
{
int a, b, c;
a = 7;
b = 15;
c = a + b;
return c;
}
test1.s
.file "test1.c"
.text
.align 2
.globl main
.type main,@function
main:
.LFB2:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
subl $24, %esp
.LCFI2:
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
movl $7, -4(%ebp)
movl $15, -8(%ebp)
movl -8(%ebp), %eax
addl -4(%ebp), %eax
movl %eax, -12(%ebp)
movl -12(%ebp), %eax
leave
ret
.LFE2:
.Lfe1:
.size main,.Lfe1-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.2.3 20030502 (Red Hat Linux 3.2.3-49)"
authors.csv
abcd@ucdavis.edu,xxx,xxx
defg@ucdavis.edu,xxx,xxx
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.