I'm still working on the same project as in my previous post, http://www.daniweb.com/forums/post723480.html. Its a program that should serve as an assembly code debugger. For simplicity I only use one instruction, ADDI, here. The program should read the instructions from a text file, load the desired information onto the registers, and output the registers when the dump() method is called. For instance the line of assembly code:
addi r1, r2, 5
Should add 5 to whatever is in r2, and place that in r1.
I thought the best approach would be to use a class and to have that class have separate methods to place a line of text in a string(parser), perform the addi instruction thus updating the wanted register (addi), and display what the registers hold (dump). Although this works it is not enough to get me what I need to make the assembler work. I created a function, called step, that successfully runs through all the instructions in the file I have at the bottom "file.txt". You can test it by uncommenting the debo.dump(); on line 35 of test.cpp and running it. For some reason however, if I call the function debo.dump() outside of the function, it acts as if nothing has happened to the registers. I don't understand what is happening. If I could make it so that my function will actually change the registers even in a function and keep them changed in the main program it would help me a ton, or if anyone can think of a better approach then let me know.

The program consists of a header, implementation, and a main program file. Below all that is the text file it reads from.

/*
 * scope.h
 *
 *  Created on: Nov 4, 2008
 *      Author: Jake
 */

#ifndef SCOPE_H_
#define SCOPE_H_

#include<iostream>
using std::cout;
using std::hex;
#include <fstream>
using std::ifstream;
#include <string>
using std::string;

class debugger
{
public:
	debugger();
	string parser(char* file, int line);
	int addi(char A, char B, int NUM);
	void dump();
private:
	int r0;
	int r1;
	int r2;
	int r3;
	int r4;
	int r5;
	int r6;
	int r7;
	int r8;
	int r9;
	int curline;
};

#endif /* SCOPE_H_ */

-----------------------------------------------------------------------------------------------

/*
 * scope.cpp
 *
 *  Created on: Nov 4, 2008
 *      Author: Jake
 */


#include "scope.h"

debugger::debugger() : r0(0), r1(0), r2(0), r3(0), r4(0), r5(0), r6(0), r7(0), r8(0), r9(0), curline(0)
{
}
/*Parameters: "file" is an array of characters representing the name of the file to be read. "line" is an integer representing the file's line to be converted into a string and returned
 *Purpose: Transfer a line from the file into a string and return it to the main program
 */
string debugger::parser(char* file, int line)
{
	ifstream fileinput;
	fileinput.open(file);
	if(!fileinput) //if the file could not be opened, print an error message and return with an error
	{
		string error = "ERROR: FILE COULD NOT BE OPENED ";
		return(error);
	}
	else //otherwise read from the file and write to the screen
	{
		string data; //the desired line is stored here
		while(curline<=line) //get to the right line and store it in the string, data
		{
			getline(fileinput, data);
			++curline;
		}
		fileinput.close();
		return(data);
	}
}
/*Parameters: Parsed character that could represent register B or register C. All of the registers
 *Purpose: Run through a switch statement to determine which character was read from the file and if 0-9 was not read display an error message.
 *Purpose: Primarily this was written to save lines of code. In cases where a register that does not need to be changed needs to be determined,
 *Purpose: this applies.
 */
int getrB(char B, int r0, int r1, int r2, int r3, int r4, int r5, int r6, int r7, int r8, int r9, int curline) //determines what register to pull data from, used in methods to save lines of code
{
	int tempB;
	switch(B)
	{
		case('0'):
			tempB = r0;
			break;
		case('1'):
			tempB = r1;
			break;
		case('2'):
			tempB = r2;
			break;
		case('3'):
			tempB = r3;
			break;
		case('4'):
			tempB = r4;
			break;
		case('5'):
			tempB = r5;
			break;
		case('6'):
			tempB = r6;
			break;
		case('7'):
			tempB = r7;
			break;
		case('8'):
			tempB = r8;
			break;
		case('9'):
			tempB = r9;
			break;
		default:
			cout << "ERROR: no number for register on line" << curline << "\n";
			return(1);
			break;
	}
	return(tempB);
}

/*Parameters: characters indicating what rA and rB are as well as an integer between  and 32767 to be added to RB
 *Parameters: 6th character is rA, 10th is rB, and 13th-18th represents a number
 *Purpose: find out which private data member rB is and temporarily store it in a variable, add rB to
 *Purpose: NUM and store the result in rA, using a switch statement to find out what that is
 */
int debugger::addi(char A, char B, int NUM)
{
	int tempB = getrB(B,r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,curline);
	int tempA = tempB + NUM;
	switch(A)
	{
		case('1'):
			r1 = tempA;
			break;
		case('2'):
			r2 = tempA;
			break;
		case('3'):
			r3 = tempA;
			break;
		case('4'):
			r4 = tempA;
			break;
		case('5'):
			r5 = tempA;
			break;
		case('6'):
			r6 = tempA;
			break;
		case('7'):
			r7 = tempA;
			break;
		case('8'):
			r8 = tempA;
			break;
		case('9'):
			r9 = tempA;
			break;
		default:
			cout << "ERROR: no number for rA found on line " << curline << "\n";
			return(1);
	}
	curline = 0; //need to reset the line to zero after each instruction so the same one isn't parsed over and over!!
	return(0);
}

void debugger::dump()
{
	cout << "r1:	" << r1 << "	r2:	" << r2 << "\n";
}

------------------------------------------------------------------------------------------------

/*
 * test.cpp
 *
 *  Created on: Nov 4, 2008
 *      Author: Jake
 */

#include "scope.h"

/*Parameters: the line of data that has been returned by the parser method (the desired line of data)
 *Purpose: Take the 13th through 18th character from the line of data, and copy it to a string holding five characters.
 *Purpose: This string represents the NUM value in the addi function. Convert the string into an integer and return the integer.
 */
int getnum(string data)
{
	string num;
	for(int i=0; i<5; i++)
	{
		num+=data[i+13];
	}
	int numint = atoi(num.c_str());
	return(numint);
}

void step(char* file, string data, debugger debo)
{
	for(int i=0; i<2; i++)
	{
		data = debo.parser(file,i);
		if(data[0]=='a' && data[1]=='d' && data[2]=='d' && data[3]=='i' && data[4]==' ' && data[5]=='r') //if addi instruction
			{
				debo.addi(data[6],data[10],getnum(data));
			}
	}
//	debo.dump();
}

int main(int argc, char* argv[])
{
	if(argc==2)
	{
		debugger debo;
		string data;
		step(argv[1],data,debo);
		debo.dump();
		return(0);
	}
	else
	{
		cout << "ERROR: MUST PROVIDE EXACTLY TWO ARGUMENTS" << "\n";
		return(1);
	}
}

-----------------------------------------------------------------------------------------
file.txt(don't include this line in the file)
addi r2, r3, 5
addi r2, r2, 8

It's so simple: you pass your debugger by value (see step function signature). So step works with copy of debo (another debugger object!), debo var was intact...
Pass it by reference:

void step(char* file, string data, debugger& debo)

It seems better solution is to define register file of your virtual CPU as an array...

Moreover, why this class called debugger? Think about better object model. You have CPU emulator, it performs instructions. You overload this class with redundant functions (read file, compile/assemby then execute and print state)...

Three in one is a good approach in perfumery but you make cross-programming system...

lol wow, that works like a charm. for future reference what do you think i could do to make the program less redundant?

thanks for your help,
jake

Obvious improvement: declare register file (all CPU general purpose registers) as

int reg[10];
const size_t REGS = 10;
...
int reg[REGS];

Now you may use short and clear code instead cumbersome switches:

/// let cmdreg is a reg char field from a assembler line
int regnum = cmdreg - '0';
...
if (regnum >= 0 && regnum < REGS) {
    temp = reg[regnum];
    ...
} 
else { // wrong register number

No need to write awkward r0, r1, r2... argument lists, pass reg array - that's all.

Desired improvements: remember the basic principle DIVIDE&CONQUER. Define Instruction class for CPU instructions (op code and operands), CPU class (to perform Instructions), Assembler class (to translate code from text line to Instruction). Select a code line from the external file in separate routine then pass it to Assembler object then pass Instruction to CPU object...
Collect all these program artifacts into the Debugger...

Good luck!

This article has been dead for over six months. Start a new discussion instead.