Skip to content

Commit

Permalink
Performance optimizations.
Browse files Browse the repository at this point in the history
Performance optimizations. Emulation speed measurement. Bug fixes.
  • Loading branch information
makarcz committed Aug 21, 2016
1 parent 20d12b5 commit 3cd0bc3
Show file tree
Hide file tree
Showing 12 changed files with 2,914 additions and 301 deletions.
140 changes: 99 additions & 41 deletions MKCpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ void MKCpu::InitCpu()
mReg.PageBoundary = false;
mLocalMem = false;
mExitAtLastRTS = true;
mEnableHistory = false; // performance decrease when enabled
if (NULL == mpMem) {
mpMem = new Memory();
if (NULL == mpMem) {
Expand All @@ -359,15 +360,15 @@ void MKCpu::InitCpu()
mLocalMem = true;
}
// Set default BRK vector ($FFFE -> $FFF0)
mpMem->Poke8bit(0xFFFE,0xF0); // LSB
mpMem->Poke8bit(0xFFFF,0xFF); // MSB
mpMem->Poke8bitImg(0xFFFE,0xF0); // LSB
mpMem->Poke8bitImg(0xFFFF,0xFF); // MSB
// Put RTI opcode at BRK procedure address.
mpMem->Poke8bit(0xFFF0, OPCODE_RTI);
mpMem->Poke8bitImg(0xFFF0, OPCODE_RTI);
// Set default RESET vector ($FFFC -> $0200)
mpMem->Poke8bit(0xFFFC,0x00); // LSB
mpMem->Poke8bit(0xFFFD,0x02); // MSB
mpMem->Poke8bitImg(0xFFFC,0x00); // LSB
mpMem->Poke8bitImg(0xFFFD,0x02); // MSB
// Set BRK code at the RESET procedure address.
mpMem->Poke8bit(0x0200,OPCODE_BRK);
mpMem->Poke8bitImg(0x0200,OPCODE_BRK);
}

/*
Expand Down Expand Up @@ -922,24 +923,24 @@ unsigned short MKCpu::GetArgWithMode(unsigned short addr, int mode)
/*
*--------------------------------------------------------------------
* Method: Disassemble()
* Purpose: Disassemble instruction and argument per addressing mode
* Arguments: n/a - internal
* Purpose: Disassemble op-code exec. history item.
* Arguments: histit - pointer to OpCodeHistItem type
* Returns: 0
*--------------------------------------------------------------------
*/
unsigned short MKCpu::Disassemble()
unsigned short MKCpu::Disassemble(OpCodeHistItem *histit)
{
char sArg[40];
char sFmt[20];

strcpy(sFmt, "%s ");
strcat(sFmt, mArgFmtTbl[mReg.LastAddrMode].c_str());
strcat(sFmt, mArgFmtTbl[histit->LastAddrMode].c_str());
sprintf(sArg, sFmt,
((mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.length() > 0
? (mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.c_str() : "???"),
mReg.LastArg);
((mOpCodesMap[(eOpCodes)histit->LastOpCode]).amf.length() > 0
? (mOpCodesMap[(eOpCodes)histit->LastOpCode]).amf.c_str() : "???"),
histit->LastArg);
for (unsigned int i=0; i<strlen(sArg); i++) sArg[i] = toupper(sArg[i]);
mReg.LastInstr = sArg;
histit->LastInstr = sArg;

return 0;
}
Expand All @@ -963,20 +964,20 @@ unsigned short MKCpu::Disassemble(unsigned short opcaddr, char *instrbuf)
int opcode = -1;
int addrmode = -1;

opcode = mpMem->Peek8bit(addr++);
opcode = mpMem->Peek8bitImg(addr++);
addrmode = (mOpCodesMap[(eOpCodes)opcode]).amf.length() > 0
? (mOpCodesMap[(eOpCodes)opcode]).addrmode : -1;

if (addrmode < 0 || NULL == instrbuf) return 0;
switch (mAddrModesLen[addrmode])
{
case 2:
sprintf(sBuf, "$%02x ", mpMem->Peek8bit(addr));
sprintf(sBuf, "$%02x ", mpMem->Peek8bitImg(addr));
break;

case 3:
sprintf(sBuf, "$%02x $%02x", mpMem->Peek8bit(addr),
mpMem->Peek8bit(addr+1));
sprintf(sBuf, "$%02x $%02x", mpMem->Peek8bitImg(addr),
mpMem->Peek8bitImg(addr+1));
break;

default:
Expand All @@ -985,7 +986,7 @@ unsigned short MKCpu::Disassemble(unsigned short opcaddr, char *instrbuf)
}
strcpy(sFmt, "$%04x: $%02x %s %s ");
strcat(sFmt, mArgFmtTbl[addrmode].c_str());
sprintf(sArg, sFmt, opcaddr, mpMem->Peek8bit(opcaddr), sBuf,
sprintf(sArg, sFmt, opcaddr, mpMem->Peek8bitImg(opcaddr), sBuf,
((mOpCodesMap[(eOpCodes)opcode]).amf.length() > 0
? (mOpCodesMap[(eOpCodes)opcode]).amf.c_str() : "???"),
GetArgWithMode(addr,addrmode));
Expand Down Expand Up @@ -3821,19 +3822,22 @@ Regs *MKCpu::ExecOpcode(unsigned short memaddr)
mReg.LastAddr = memaddr;
unsigned char opcode = OPCODE_BRK;

// skip until all the cycles were completed
// The op-code action was executed already once.
// Now skip if the clock cycles for this op-code are not yet completed.
if (mReg.CyclesLeft > 0) {
mReg.CyclesLeft--;
return &mReg;
}

// if no IRQ waiting, get the next opcode and advance PC
// If no IRQ waiting, get the next opcode and advance PC.
// Otherwise the opcode is OPCODE_BRK and with IrqPending
// flag set the IRQ sequence will be executed.
if (!mReg.IrqPending) {
opcode = mpMem->Peek8bit(mReg.PtrAddr++);
}

// load CPU instruction details from map
OpCode instrdet = mOpCodesMap[(eOpCodes)opcode];
OpCode *instrdet = &mOpCodesMap[(eOpCodes)opcode];

SetFlag(false, FLAGS_BRK); // reset BRK flag - we want to detect
mReg.SoftIrq = false; // software interrupt each time it
Expand All @@ -3843,27 +3847,34 @@ Regs *MKCpu::ExecOpcode(unsigned short memaddr)
mReg.LastAddrMode = ADDRMODE_UND;
mReg.LastArg = 0;

string s = (instrdet.amf.length() > 0
? instrdet.amf.c_str() : "???");
string s = (instrdet->amf.length() > 0
? instrdet->amf.c_str() : "???");

if (s.compare("ILL") == 0) {
// trap any illegal opcode
mReg.SoftIrq = true;
} else {
// execute legal opcode
mReg.CyclesLeft = instrdet.time - 1;
OpCodeHdlrFn pfun = instrdet.pfun;
// reset remaining cycles counter and execute legal opcode
mReg.CyclesLeft = instrdet->time - 1;
OpCodeHdlrFn pfun = instrdet->pfun;
if (NULL != pfun) (this->*pfun)();
}


Disassemble();
char histentry[80];
sprintf(histentry,
"$%04x: %-16s \t$%02x | $%02x | $%02x | $%02x | $%02x",
mReg.LastAddr, mReg.LastInstr.c_str(), mReg.Acc, mReg.IndX,
mReg.IndY, mReg.Flags, mReg.PtrStack);
Add2History(histentry);
// Update history/log of recently executed op-codes/instructions.
if (mEnableHistory) {

OpCodeHistItem histentry;
histentry.LastAddr = mReg.LastAddr;
histentry.Acc = mReg.Acc;
histentry.IndX = mReg.IndX;
histentry.IndY = mReg.IndY;
histentry.Flags = mReg.Flags;
histentry.PtrStack = mReg.PtrStack;
histentry.LastOpCode = mReg.LastOpCode;
histentry.LastAddrMode = mReg.LastAddrMode;
histentry.LastArg = mReg.LastArg;
Add2History(histentry);
}

return &mReg;
}
Expand All @@ -3884,28 +3895,75 @@ Regs *MKCpu::GetRegs()
/*
*--------------------------------------------------------------------
* Method: Add2History()
* Purpose: Add entry to execute history.
* Purpose: Add entry with last executed op-code, arguments and
* CPU status to execute history.
* Arguments: s - string (entry)
* Returns: n/a
*--------------------------------------------------------------------
*/
void MKCpu::Add2History(string s)
void MKCpu::Add2History(OpCodeHistItem histitem)
{
mExecHistory.push(s);
while (mExecHistory.size() > 20) mExecHistory.pop();
mExecHistory.push(histitem);
while (mExecHistory.size() > OPCO_HIS_SIZE) mExecHistory.pop();
}

/*
*--------------------------------------------------------------------
* Method: GetExecHistory()
* Purpose: Return queue with execute history.
* Purpose: Disassemble op-codes execute history stored in
* mExecHistory and create/return queue of strings with
* execute history in symbolic form (assembly mnemonics,
* properly converted arguments in corresponding addressing
* mode notation that adheres to MOS-6502 industry
* standard.)
* Arguments: n/a
* Returns: queue<string>
*--------------------------------------------------------------------
*/
queue<string> MKCpu::GetExecHistory()
{
return mExecHistory;
queue<string> ret;
queue<OpCodeHistItem> exechist(mExecHistory);

while (exechist.size()) {
OpCodeHistItem item = exechist.front();
Disassemble(&item);
char histentry[80];
sprintf(histentry,
"$%04x: %-16s \t$%02x | $%02x | $%02x | $%02x | $%02x",
item.LastAddr, item.LastInstr.c_str(), item.Acc, item.IndX,
item.IndY, item.Flags, item.PtrStack);
ret.push(histentry);
exechist.pop();
}

return ret;
}

/*
*--------------------------------------------------------------------
* Method: EnableExecHistory()
* Purpose: Enable/disable recording of op-codes execute history.
* Arguments: bool - true = enable / false = disable
* Returns: n/a
*--------------------------------------------------------------------
*/
void MKCpu::EnableExecHistory(bool enexehist)
{
mEnableHistory = enexehist;
}

/*
*--------------------------------------------------------------------
* Method: IsExecHistoryEnabled()
* Purpose: Check if op-code execute history is enabled.
* Arguments: n/a
* Returns: bool - true = enabled / false = disabled
*--------------------------------------------------------------------
*/
bool MKCpu::IsExecHistoryEnabled()
{
return mEnableHistory;
}

/*
Expand Down
25 changes: 22 additions & 3 deletions MKCpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ using namespace std;
namespace MKBasic {

#define DISS_BUF_SIZE 60 // disassembled instruction buffer size
#define OPCO_HIS_SIZE 20 // size of op-code execute history queue

struct Regs {
unsigned char Acc; // 8-bit accumulator
Expand Down Expand Up @@ -427,6 +428,8 @@ class MKCpu
Regs *GetRegs();
void SetRegs(Regs r);
queue<string> GetExecHistory();
void EnableExecHistory(bool enexehist);
bool IsExecHistoryEnabled();
unsigned short Disassemble(unsigned short addr,
char *instrbuf); // Disassemble instruction in memory, return next instruction addr.
void Reset(); // reset CPU
Expand All @@ -435,14 +438,29 @@ class MKCpu
protected:

private:

// keeps all needed data to disassemble op-codes in execute history queue
struct OpCodeHistItem {
unsigned char Acc; // 8-bit accumulator
unsigned char IndX; // 8-bit index register X
unsigned char IndY; // 8-bit index register Y
unsigned char Flags; // CPU flags
unsigned char PtrStack; // 8-bit stack pointer (0-255).
unsigned short LastAddr; // PC at the time of previous op-code
string LastInstr; // instruction and argument executed in previous step
int LastOpCode; // op-code of last instruction
unsigned short LastArg; // argument to the last instruction
int LastAddrMode; // addressing mode of last instruction
};

struct Regs mReg; // CPU registers
Memory *mpMem; // pointer to memory object
bool mLocalMem; // true - memory locally allocated
OpCodesMap mOpCodesMap; // hash table of all opcodes
int mAddrModesLen[ADDRMODE_LENGTH]; // array of instructions lengths per addressing mode
string mArgFmtTbl[ADDRMODE_LENGTH]; // array of instructions assembly formats per addressing mode
queue<string> mExecHistory; // history of last 20 op-codes with arguments and registers statuses
queue<OpCodeHistItem> mExecHistory; // keep the op-codes execute history
bool mEnableHistory; // enable/disable execute history


void InitCpu();
Expand All @@ -466,8 +484,9 @@ class MKCpu
unsigned short GetAddrWithMode(int mode); // Get address of the byte argument with specified addr. mode
unsigned short GetArgWithMode(unsigned short opcaddr,
int mode); // Get argument from address with specified addr. mode
unsigned short Disassemble(); // Disassemble instruction and argument per addressing mode
void Add2History(string s); // add entry to op-codes execute history
unsigned short Disassemble(OpCodeHistItem *histit); // Disassemble op-code exec history item
//void Add2History(string s); // add entry to op-codes execute history
void Add2History(OpCodeHistItem histitem); // add entry to op-codes execute history
bool PageBoundary(unsigned short startaddr,
unsigned short endaddr); // detect if page boundary was crossed

Expand Down
20 changes: 14 additions & 6 deletions MemMapDev.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,8 @@ Device MemMapDev::GetDevice(int devnum)
++devit
) {

Device dev = *devit;
if (dev.num == devnum) {
ret = dev;
if (devit->num == devnum) {
ret = *devit;
break;
}
}
Expand Down Expand Up @@ -287,6 +286,7 @@ int MemMapDev::getch()
return c;
}
}

#endif

/*
Expand All @@ -307,9 +307,17 @@ unsigned char MemMapDev::ReadCharKb(bool nonblock)
static int c = ' ';
if (mIOEcho && isprint(c)) putchar(c);
fflush(stdout);
if (!nonblock) while(!kbhit());
else c = 0;
c = getch();

if (nonblock) {
// get a keystroke only if character is already in buffer
if (kbhit()) c = getch();
else c = 0;

} else {
// wait for a keystroke, then get the character from buffer
while(!kbhit());
c = getch();
}
#if defined(LINUX)
if (c == 3) { // capture CTRL-C in CONIO mode
reset_terminal_mode();
Expand Down
2 changes: 1 addition & 1 deletion MemMapDev.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ class MemMapDev {

void set_conio_terminal_mode();
int kbhit();
int getch();
int getch();

#endif

Expand Down
Loading

0 comments on commit 3cd0bc3

Please sign in to comment.