mirror of
https://github.com/mkwong98/HDNes.git
synced 2025-04-02 10:32:07 -04:00
658 lines
21 KiB
C++
658 lines
21 KiB
C++
#include "cpu.h"
|
|
#include "memBus.h"
|
|
#include "../gameManager.h"
|
|
|
|
cpu::cpu()
|
|
{
|
|
instructionLen[0] = 6;
|
|
instructionLen[1] = 3;
|
|
instructionLen[2] = 2;
|
|
instructionLen[3] = 4;
|
|
instructionLen[4] = 5;
|
|
instructionLen[5] = 4;
|
|
instructionLen[6] = 4;
|
|
instructionLen[7] = 4;
|
|
|
|
flagMask[0] = 0x01;
|
|
flagMask[1] = 0x02;
|
|
flagMask[2] = 0x04;
|
|
flagMask[3] = 0x08;
|
|
flagMask[4] = 0x10;
|
|
flagMask[5] = 0x20;
|
|
flagMask[6] = 0x40;
|
|
flagMask[7] = 0x80;
|
|
|
|
branchFlag[0] = FLAG_N;
|
|
branchFlag[1] = FLAG_V;
|
|
branchFlag[2] = FLAG_C;
|
|
branchFlag[3] = FLAG_Z;
|
|
|
|
for(Uint8 i = 0; i < 256; ++i){
|
|
addressMode = (i >> 2) & 0x07;
|
|
operation = (i >> 5) & 0x07;
|
|
opGroup = i & 0x03;
|
|
if(addressMode == 4 && opGroup == 0){ //branch xxx10000
|
|
opHdl[i] = &cpu::opcodeBranch;
|
|
}
|
|
else if(addressMode == 6 && opGroup == 0){ //flag xxx11000
|
|
opHdl[i] = &cpu::opcodeFlag;
|
|
}
|
|
else if(addressMode > 4 && operation > 4 && opGroup == 0){ //0xx0xx00
|
|
opHdl[i] = &cpu::opcodeCtrl;
|
|
}
|
|
else if((nextInstruction[0] & 0x8F) == 0x8A){ //1xxx1010
|
|
opHdl[i] = &cpu::opcodeMisB;
|
|
}
|
|
else if((nextInstruction[0] & 0x9F) == 0x88){ //1xx01000
|
|
opHdl[i] = &cpu::opcodeMisA;
|
|
}
|
|
else if(operation == 4){
|
|
opHdl[i] = &cpu::opcodeST;
|
|
}
|
|
else if(operation == 5){
|
|
opHdl[i] = &cpu::opcodeLD;
|
|
}
|
|
else if(opGroup == 0){
|
|
opHdl[i] = &cpu::opcodeHandler0;
|
|
}
|
|
else if(opGroup == 1){
|
|
opHdl[i] = &cpu::opcodeHandler1;
|
|
}
|
|
else if(opGroup == 2){
|
|
opHdl[i] = &cpu::opcodeHandler2;
|
|
}
|
|
else if(opGroup == 3){
|
|
opHdl[i] = &cpu::opcodeHandler3;
|
|
}
|
|
}
|
|
|
|
adHdl[0] = &cpu::resolveAddress0;
|
|
adHdl[1] = &cpu::resolveAddress1;
|
|
adHdl[2] = &cpu::resolveAddress2;
|
|
adHdl[3] = &cpu::resolveAddress3;
|
|
adHdl[4] = &cpu::resolveAddress4;
|
|
adHdl[5] = &cpu::resolveAddress5;
|
|
adHdl[6] = &cpu::resolveAddress6;
|
|
adHdl[7] = &cpu::resolveAddress7;
|
|
adHdl[8] = &cpu::resolveAddress8;
|
|
}
|
|
|
|
cpu::~cpu()
|
|
{
|
|
//dtor
|
|
}
|
|
|
|
Uint8 cpu::getNextInstructionLength(){
|
|
return instructionTicks;
|
|
}
|
|
|
|
void cpu::processInstruction(){
|
|
newState = state;
|
|
nextInstruction[0] = mb->memRead(newState.programCounter);
|
|
++(newState.programCounter);
|
|
addressMode = (nextInstruction[0] >> 2) & 0x07;
|
|
operation = (nextInstruction[0] >> 5) & 0x07;
|
|
opGroup = nextInstruction[0] & 0x03;
|
|
(this->*opHdl[nextInstruction[0]])();
|
|
}
|
|
|
|
void cpu::opcodeBranch(){
|
|
instructionType = OP_TYPE_CPU;
|
|
instructionTicks = 2;
|
|
nextInstruction[1] = mb->memRead(newState.programCounter);
|
|
++(newState.programCounter);
|
|
bool branchResult;
|
|
if(operation & 0x01){
|
|
branchResult = checkFlag(branchFlag[(operation >> 1) & 0x03]);
|
|
}
|
|
else{
|
|
branchResult = !checkFlag(branchFlag[(operation >> 1) & 0x03]);
|
|
}
|
|
if(branchResult){
|
|
++instructionTicks;
|
|
Uint16 pcPage;
|
|
pcPage = newState.programCounter & 0xFF00;
|
|
newState.programCounter += (nextInstruction[1] & 0x80 ? -(nextInstruction[1] & 0x7F) - 1 : nextInstruction[1]);
|
|
if(pcPage != (newState.programCounter & 0xFF00)) ++instructionTicks;
|
|
}
|
|
}
|
|
|
|
void cpu::opcodeFlag(){
|
|
instructionTicks = 2;
|
|
instructionType = OP_TYPE_CPU;
|
|
if(nextInstruction[0] == 0x98){ //TYA
|
|
newState.reg[REG_INDEX_Y] = newState.reg[REG_ACCUMULATOR];
|
|
updateFlag(FLAG_Z, !newState.reg[REG_INDEX_Y]);
|
|
updateFlag(FLAG_N, newState.reg[REG_INDEX_Y] & 0x80);
|
|
}
|
|
else{
|
|
Uint8 flagID;
|
|
switch(operation & 0x06){
|
|
case 0:
|
|
flagID = FLAG_C;
|
|
break;
|
|
case 2:
|
|
flagID = FLAG_I;
|
|
break;
|
|
case 4:
|
|
flagID = FLAG_V;
|
|
break;
|
|
case 6:
|
|
flagID = FLAG_D;
|
|
break;
|
|
}
|
|
if((operation & 0x01) && (operation != 0x05)){
|
|
setFlag(flagID);
|
|
}
|
|
else{
|
|
clearFlag(flagID);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cpu::opcodeCtrl(){
|
|
if(addressMode == 0){
|
|
switch(operation){
|
|
case 0x00: //BRK
|
|
instructionTicks = 7;
|
|
instructionType = OP_TYPE_BRK;
|
|
setFlag(FLAG_I);
|
|
break;
|
|
case 0x01: //JSR
|
|
instructionTicks = 6;
|
|
instructionType = OP_TYPE_JSR;
|
|
nextInstruction[1] = mb->memRead(newState.programCounter);
|
|
++(newState.programCounter);
|
|
nextInstruction[2] = mb->memRead(newState.programCounter);
|
|
break;
|
|
case 0x02: //RTI
|
|
instructionTicks = 6;
|
|
instructionType = OP_TYPE_CPU;
|
|
++(newState.reg[REG_STACK_PTR]);
|
|
newState.reg[REG_STATUS] = (mb->memRead(0x0100 + newState.reg[REG_STACK_PTR]) & 0xCF) | (newState.reg[REG_STATUS] & 0x30); //ignore bit 4 and 5
|
|
//immediately effective
|
|
state.reg[REG_STATUS] = newState.reg[REG_STATUS];
|
|
++(newState.reg[REG_STACK_PTR]);
|
|
newState.programCounter = (mb->memRead(0x0100 + newState.reg[REG_STACK_PTR]) & 0x00FF);
|
|
++(newState.reg[REG_STACK_PTR]);
|
|
newState.programCounter += (mb->memRead(0x0100 + newState.reg[REG_STACK_PTR]) << 8) ;
|
|
break;
|
|
case 0x03: //RTS
|
|
instructionTicks = 6;
|
|
instructionType = OP_TYPE_CPU;
|
|
++(newState.reg[REG_STACK_PTR]);
|
|
newState.programCounter = (mb->memRead(0x0100 + newState.reg[REG_STACK_PTR]) & 0x00FF);
|
|
++(newState.reg[REG_STACK_PTR]);
|
|
newState.programCounter += (mb->memRead(0x0100 + newState.reg[REG_STACK_PTR]) << 8) ;
|
|
++(newState.programCounter);
|
|
break;
|
|
}
|
|
}
|
|
else if(addressMode == 2){
|
|
switch(operation){
|
|
case 0x00:
|
|
//PHP, bit 4 and 5 are always 1 in stack
|
|
pushStack(newState.reg[REG_STATUS] | 0x30);
|
|
break;
|
|
case 0x01:
|
|
//PLP , ignore bit 4 and 5
|
|
newState.reg[REG_STATUS] = (pullStack() & 0xCF) | (newState.reg[REG_STATUS] & 0x30);
|
|
break;
|
|
case 0x028:
|
|
//PHA
|
|
pushStack(newState.reg[REG_ACCUMULATOR]);
|
|
break;
|
|
case 0x03:
|
|
//PLA
|
|
newState.reg[REG_ACCUMULATOR] = pullStack();
|
|
break;
|
|
}
|
|
}
|
|
else{
|
|
if(operation == 0x01){ //BIT
|
|
instructionType = OP_TYPE_CPU;
|
|
instructionTicks = (addressMode == 0x01 ? 3 : 4);
|
|
opValue = getValue((addressMode == 0x01 ? 1 : 3), pageCrossed);
|
|
outValue = opValue & newState.reg[REG_ACCUMULATOR];
|
|
updateFlag(FLAG_Z, !outValue);
|
|
updateFlag(FLAG_V, opValue & 0x40);
|
|
updateFlag(FLAG_N, opValue & 0x80);
|
|
}
|
|
else{ //JMP
|
|
instructionType = OP_TYPE_CPU;
|
|
outAddress = resolveAddress(3, pageCrossed);
|
|
if(operation == 0x02){
|
|
instructionTicks = 3;
|
|
newState.programCounter = outAddress;
|
|
}
|
|
else{
|
|
instructionTicks = 5;
|
|
newState.programCounter = (mb->memRead(outAddress + 1) << 8) + mb->memRead(outAddress);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cpu::opcodeMisA(){
|
|
instructionTicks = 2;
|
|
instructionType = OP_TYPE_CPU;
|
|
switch(operation){
|
|
case 0x04:
|
|
//DEY
|
|
newState.reg[REG_INDEX_Y] = (newState.reg[REG_INDEX_Y] ? newState.reg[REG_INDEX_Y] - 1 : 0xFF);
|
|
updateFlag(FLAG_Z, !newState.reg[REG_INDEX_Y]);
|
|
updateFlag(FLAG_N, newState.reg[REG_INDEX_Y] & 0x80);
|
|
break;
|
|
case 0x05:
|
|
//TAY
|
|
newState.reg[REG_INDEX_Y] = newState.reg[REG_ACCUMULATOR];
|
|
updateFlag(FLAG_Z, !newState.reg[REG_INDEX_Y]);
|
|
updateFlag(FLAG_N, newState.reg[REG_INDEX_Y] & 0x80);
|
|
case 0x06:
|
|
//INY
|
|
newState.reg[REG_INDEX_Y] = (newState.reg[REG_INDEX_Y] < 0xFF ? newState.reg[REG_INDEX_Y] + 1 : 0);
|
|
updateFlag(FLAG_Z, !newState.reg[REG_INDEX_Y]);
|
|
updateFlag(FLAG_N, newState.reg[REG_INDEX_Y] & 0x80);
|
|
break;
|
|
case 0x07:
|
|
//INX
|
|
newState.reg[REG_INDEX_X] = (newState.reg[REG_INDEX_X] < 0xFF ? newState.reg[REG_INDEX_X] + 1 : 0);
|
|
updateFlag(FLAG_Z, !newState.reg[REG_INDEX_X]);
|
|
updateFlag(FLAG_N, newState.reg[REG_INDEX_X] & 0x80);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void cpu::opcodeMisB(){
|
|
instructionTicks = 2;
|
|
instructionType = OP_TYPE_CPU;
|
|
|
|
switch(operation){
|
|
case 0x8A: //TXA
|
|
newState.reg[REG_ACCUMULATOR] = newState.reg[REG_INDEX_X];
|
|
updateFlag(FLAG_Z, !newState.reg[REG_ACCUMULATOR]);
|
|
updateFlag(FLAG_N, newState.reg[REG_ACCUMULATOR] & 0x80);
|
|
break;
|
|
case 0x9A: //TXS
|
|
newState.reg[REG_STACK_PTR] = newState.reg[REG_INDEX_X];
|
|
break;
|
|
case 0xAA: //TAX
|
|
newState.reg[REG_INDEX_X] = newState.reg[REG_ACCUMULATOR];
|
|
updateFlag(FLAG_Z, !newState.reg[REG_INDEX_X]);
|
|
updateFlag(FLAG_N, newState.reg[REG_INDEX_X] & 0x80);
|
|
break;
|
|
case 0xBA: //TSX
|
|
newState.reg[REG_INDEX_X] = newState.reg[REG_STACK_PTR];
|
|
updateFlag(FLAG_Z, !newState.reg[REG_INDEX_X]);
|
|
updateFlag(FLAG_N, newState.reg[REG_INDEX_X] & 0x80);
|
|
break;
|
|
case 0xCA: //DEX
|
|
newState.reg[REG_INDEX_X] = (newState.reg[REG_INDEX_X] ? newState.reg[REG_INDEX_X] - 1 : 0xFF);
|
|
updateFlag(FLAG_Z, !newState.reg[REG_INDEX_X]);
|
|
updateFlag(FLAG_N, newState.reg[REG_INDEX_X] & 0x80);
|
|
break;
|
|
case 0xEA: //NOP
|
|
break;
|
|
}
|
|
}
|
|
|
|
void cpu::opcodeST(){
|
|
instructionTicks = instructionLen[addressMode];
|
|
instructionType = OP_TYPE_OUT;
|
|
switch(opGroup){
|
|
case 1:
|
|
if(addressMode == 4 || addressMode == 6 || addressMode == 7) ++instructionTicks;
|
|
break;
|
|
case 2:
|
|
if(addressMode == 5) addressMode = 8;
|
|
break;
|
|
}
|
|
outValue = newState.reg[opGroup];
|
|
outAddress = resolveAddress(addressMode, pageCrossed);
|
|
}
|
|
|
|
void cpu::opcodeLD(){
|
|
if(opGroup != 1 && addressMode == 0) addressMode = 2;
|
|
instructionTicks = instructionLen[addressMode];
|
|
instructionType = OP_TYPE_CPU;
|
|
|
|
if(opGroup == 2 && addressMode == 5) addressMode = 8;
|
|
if(opGroup == 2 && addressMode == 7) addressMode = 6;
|
|
|
|
newState.reg[opGroup] = getValue(addressMode, pageCrossed);
|
|
updateFlag(FLAG_Z, !newState.reg[opGroup]);
|
|
updateFlag(FLAG_N, newState.reg[opGroup] & 0x80);
|
|
if(pageCrossed) ++instructionTicks;
|
|
}
|
|
|
|
void cpu::opcodeHandler0(){ //xxxxxx00
|
|
if((nextInstruction[0] & 0x80) == 0x80){ //1xxxxx00
|
|
if(operation == 6){ //CPY
|
|
if(addressMode == 0) addressMode = 2;
|
|
instructionTicks = instructionLen[addressMode];
|
|
instructionType = OP_TYPE_CPU;
|
|
opValue = getValue(addressMode, pageCrossed);
|
|
compare(newState.reg[REG_INDEX_Y], opValue);
|
|
}
|
|
else if(operation == 7){ //CPX
|
|
if(addressMode == 0) addressMode = 2;
|
|
instructionTicks = instructionLen[addressMode];
|
|
instructionType = OP_TYPE_CPU;
|
|
opValue = getValue(addressMode, pageCrossed);
|
|
compare(newState.reg[REG_INDEX_X], opValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cpu::opcodeHandler1(){ //PPPAAA01
|
|
instructionTicks = instructionLen[addressMode];
|
|
instructionType = OP_TYPE_CPU;
|
|
opValue = getValue(addressMode, pageCrossed);
|
|
if(pageCrossed) ++instructionTicks;
|
|
if(operation == 6){//CMP
|
|
compare(newState.reg[REG_ACCUMULATOR], opValue);
|
|
}
|
|
else{
|
|
Uint16 tmpValue;
|
|
switch(operation){
|
|
case 0:
|
|
//ORA
|
|
newState.reg[REG_ACCUMULATOR] = newState.reg[REG_ACCUMULATOR] | opValue;
|
|
break;
|
|
case 1:
|
|
//AND
|
|
newState.reg[REG_ACCUMULATOR] = newState.reg[REG_ACCUMULATOR] & opValue;
|
|
break;
|
|
case 2:
|
|
//EOR
|
|
newState.reg[REG_ACCUMULATOR] = newState.reg[REG_ACCUMULATOR] ^ opValue;
|
|
break;
|
|
case 3:
|
|
//ADC
|
|
tmpValue = newState.reg[REG_ACCUMULATOR] + opValue + (checkFlag(FLAG_C) ? 1 : 0);
|
|
newState.reg[REG_ACCUMULATOR] = tmpValue & 0x00FF;
|
|
updateFlag(FLAG_C, tmpValue > 0x00FF);
|
|
updateFlag(FLAG_V, (((newState.reg[REG_ACCUMULATOR] ^ opValue) & (newState.reg[REG_ACCUMULATOR] ^ state.reg[REG_ACCUMULATOR]) & 0x80) != 0));
|
|
break;
|
|
case 7:
|
|
//SBC
|
|
tmpValue = newState.reg[REG_ACCUMULATOR];
|
|
tmpValue = tmpValue - opValue;
|
|
if(checkFlag(FLAG_C)) --tmpValue;
|
|
newState.reg[REG_ACCUMULATOR] = tmpValue & 0x00FF;
|
|
updateFlag(FLAG_C, tmpValue < 0x100);
|
|
updateFlag(FLAG_V, ((newState.reg[REG_ACCUMULATOR] ^ (opValue ^ 0xFF)) & (newState.reg[REG_ACCUMULATOR] ^ state.reg[REG_ACCUMULATOR]) & 0x80) != 0);
|
|
}
|
|
updateFlag(FLAG_Z, !newState.reg[REG_ACCUMULATOR]);
|
|
updateFlag(FLAG_N, newState.reg[REG_ACCUMULATOR] & 0x80);
|
|
}
|
|
}
|
|
|
|
void cpu::opcodeHandler2(){ //xxxxxx10
|
|
instructionTicks = 2;
|
|
if(addressMode == 2){
|
|
instructionType = OP_TYPE_CPU;
|
|
opValue = newState.reg[REG_ACCUMULATOR];
|
|
}
|
|
else{
|
|
instructionType = OP_TYPE_OUT;
|
|
instructionTicks += 2;
|
|
if(addressMode == 7) ++instructionTicks;
|
|
outAddress = resolveAddress(addressMode, pageCrossed);
|
|
opValue = mb->memRead(outAddress);
|
|
}
|
|
switch(operation){
|
|
case 0:
|
|
//ASL
|
|
outValue = (opValue << 1) & 0xFE;
|
|
updateFlag(FLAG_C, opValue & 0x80);
|
|
break;
|
|
case 1:
|
|
//ROL
|
|
outValue = ((opValue << 1) & 0xFE) | (checkFlag(FLAG_C) ? 0x01 : 0x00);
|
|
updateFlag(FLAG_C, opValue & 0x80);
|
|
break;
|
|
case 2:
|
|
//LSR
|
|
outValue = (opValue >> 1) & 0x7F;
|
|
updateFlag(FLAG_C, opValue & 0x01);
|
|
break;
|
|
case 3:
|
|
//ROR
|
|
outValue = ((opValue >> 1) & 0x7F) | (checkFlag(FLAG_C) ? 0x80 : 0x00);
|
|
updateFlag(FLAG_C, opValue & 0x01);
|
|
break;
|
|
case 6:
|
|
//DEC
|
|
outValue = (opValue ? opValue - 1 : 0xFF);
|
|
break;
|
|
case 7:
|
|
//INC
|
|
outValue = (opValue < 0xFF ? opValue + 1 : 0);
|
|
break;
|
|
}
|
|
updateFlag(FLAG_Z, !outValue);
|
|
updateFlag(FLAG_N, outValue & 0x80);
|
|
if(addressMode == 2){
|
|
newState.reg[REG_ACCUMULATOR] = outValue;
|
|
}
|
|
}
|
|
|
|
void cpu::opcodeHandler3(){ //xxxxxx11
|
|
//unsupported op
|
|
instructionType = OP_TYPE_CPU;
|
|
instructionTicks = 2;
|
|
}
|
|
|
|
void cpu::pushStack(Uint8 value){
|
|
instructionTicks = 3;
|
|
instructionType = OP_TYPE_OUT;
|
|
outValue = value;
|
|
outAddress = 0x0100 + newState.reg[REG_STACK_PTR];
|
|
--(newState.reg[REG_STACK_PTR]);
|
|
}
|
|
|
|
Uint8 cpu::pullStack(){
|
|
instructionTicks = 4;
|
|
instructionType = OP_TYPE_CPU;
|
|
++(newState.reg[REG_STACK_PTR]);
|
|
return mb->memRead(0x0100 + newState.reg[REG_STACK_PTR]);
|
|
}
|
|
|
|
void cpu::compare(Uint8 regValue, Uint8 opValue){
|
|
updateFlag(FLAG_C, regValue >= opValue);
|
|
updateFlag(FLAG_Z, regValue == opValue);
|
|
updateFlag(FLAG_N, ((regValue - opValue) & 0x80) != 0);
|
|
}
|
|
|
|
Uint8 cpu::getValue(Uint8 addressMode, bool& hasCrossPage){
|
|
if(addressMode == 2){
|
|
//#v
|
|
nextInstruction[1] = mb->memRead(newState.programCounter);
|
|
++(newState.programCounter);
|
|
hasCrossPage = false;
|
|
return nextInstruction[1];
|
|
}
|
|
else{
|
|
return mb->memRead(resolveAddress(addressMode, hasCrossPage));
|
|
}
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress(Uint8 addressMode, bool& hasCrossPage){
|
|
Uint16 tmpAddress;
|
|
nextInstruction[1] = mb->memRead(newState.programCounter);
|
|
++(newState.programCounter);
|
|
hasCrossPage = false;
|
|
return (this->*adHdl[addressMode])(hasCrossPage);
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress0(bool& hasCrossPage){ //(d,x)
|
|
return mb->memRead((nextInstruction[1] + state.reg[REG_INDEX_X]) & 0x00FF) + (mb->memRead((nextInstruction[1] + state.reg[REG_INDEX_X] + 1) & 0x00FF) << 8);
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress1(bool& hasCrossPage){ //d
|
|
return nextInstruction[1];
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress2(bool& hasCrossPage){ //#v
|
|
return 0;
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress3(bool& hasCrossPage){ //a
|
|
nextInstruction[2] = mb->memRead(newState.programCounter);
|
|
++(newState.programCounter);
|
|
return (nextInstruction[2] >> 8) + nextInstruction[1];
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress4(bool& hasCrossPage){ //(d),y
|
|
Uint16 tmpAddress;
|
|
tmpAddress = mb->memRead((nextInstruction[1]) + newState.reg[REG_INDEX_Y]);
|
|
if(tmpAddress > 0x00FF){
|
|
hasCrossPage = true;
|
|
}
|
|
return tmpAddress + (mb->memRead((nextInstruction[1] + state.reg[REG_INDEX_X] + 1) & 0x00FF) << 8);
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress5(bool& hasCrossPage){ //d,x
|
|
return (nextInstruction[1] + state.reg[REG_INDEX_X]) & 0x00FF;
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress6(bool& hasCrossPage){ //a,y
|
|
Uint16 tmpAddress;
|
|
nextInstruction[2] = mb->memRead(newState.programCounter);
|
|
++(newState.programCounter);
|
|
tmpAddress = nextInstruction[1] + newState.reg[REG_INDEX_Y];
|
|
if(tmpAddress > 0x00FF){
|
|
hasCrossPage = true;
|
|
}
|
|
return tmpAddress + mb->memRead(nextInstruction[2] << 8);
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress7(bool& hasCrossPage){ //a,x
|
|
Uint16 tmpAddress;
|
|
nextInstruction[2] = mb->memRead(newState.programCounter);
|
|
++(newState.programCounter);
|
|
tmpAddress = nextInstruction[1] + newState.reg[REG_INDEX_X];
|
|
if(tmpAddress > 0x00FF){
|
|
hasCrossPage = true;
|
|
}
|
|
return tmpAddress + mb->memRead(nextInstruction[2] << 8);
|
|
}
|
|
|
|
Uint16 cpu::resolveAddress8(bool& hasCrossPage){ //d,y
|
|
return (nextInstruction[1] + state.reg[REG_INDEX_Y]) & 0x00FF;
|
|
}
|
|
|
|
void cpu::updateFlag(Uint8 flag, bool value){
|
|
if(value){
|
|
setFlag(flag);
|
|
}
|
|
else{
|
|
clearFlag(flag);
|
|
}
|
|
}
|
|
|
|
void cpu::setFlag(Uint8 flag){
|
|
newState.reg[REG_STATUS] = newState.reg[REG_STATUS] | flagMask[flag];
|
|
}
|
|
|
|
void cpu::clearFlag(Uint8 flag){
|
|
newState.reg[REG_STATUS] = newState.reg[REG_STATUS] & ~flagMask[flag];
|
|
}
|
|
|
|
bool cpu::checkFlag(Uint8 flag){
|
|
return state.reg[REG_STATUS] & flagMask[flag];
|
|
}
|
|
|
|
void cpu::runInstruction(){
|
|
if(instructionType == OP_TYPE_OUT){
|
|
mb->memWrite(outAddress, outValue);
|
|
}
|
|
else if(instructionType == OP_TYPE_BRK){
|
|
interruptJump(0xFFFE, 0xFFFF);
|
|
}
|
|
else if(instructionType == OP_TYPE_JSR){
|
|
mb->memWrite(0x0100 + newState.reg[REG_STACK_PTR], newState.programCounter >> 8);
|
|
--(newState.reg[REG_STACK_PTR]);
|
|
mb->memWrite(0x0100 + newState.reg[REG_STACK_PTR], newState.programCounter);
|
|
--(newState.reg[REG_STACK_PTR]);
|
|
newState.programCounter = ((nextInstruction[2] << 8) | nextInstruction[1]);
|
|
}
|
|
state = newState;
|
|
serviceInterrupt();
|
|
}
|
|
|
|
void cpu::reset(){
|
|
state.reg[REG_STACK_PTR] -= 3;
|
|
state.reg[REG_STATUS] = state.reg[REG_STATUS] | flagMask[FLAG_I];
|
|
state.programCounter = ((mb->memRead(0xFFFD) << 8) | mb->memRead(0xFFFC));
|
|
}
|
|
|
|
void cpu::saveState(fstream* statefile){
|
|
}
|
|
|
|
void cpu::loadState(fstream* statefile){
|
|
}
|
|
|
|
void cpu::init(){
|
|
mb = gameManager::gm->mb;
|
|
}
|
|
|
|
void cpu::init2(){
|
|
state.reg[REG_STATUS] = 0x34;
|
|
state.reg[REG_ACCUMULATOR] = 0;
|
|
state.reg[REG_INDEX_X] = 0;
|
|
state.reg[REG_INDEX_Y] = 0;
|
|
state.reg[REG_STACK_PTR] = 0xFD;
|
|
state.programCounter = ((mb->memRead(0xFFFD) << 8) | mb->memRead(0xFFFC));
|
|
|
|
for(Uint8 i = 0; i < 3; ++i){
|
|
flagNMI[i] = false;
|
|
flagIRQ[i] = false;
|
|
}
|
|
}
|
|
|
|
void cpu::pullNMILow(){
|
|
flagNMI[INTERRUPT_FLAG_IS_LOW_NEW] = true;
|
|
}
|
|
|
|
void cpu::pullIRQLow(){
|
|
flagIRQ[INTERRUPT_FLAG_IS_LOW_NEW] = true;
|
|
}
|
|
|
|
void cpu::runInterruptDetector(){
|
|
//NMI raises when becomes low or is raised already
|
|
flagNMI[INTERRUPT_FLAG_DETECTOR_OUTPUT] = ((flagNMI[INTERRUPT_FLAG_IS_LOW_NEW] && !flagNMI[INTERRUPT_FLAG_IS_LOW_OLD]) || flagNMI[INTERRUPT_FLAG_DETECTOR_OUTPUT]);
|
|
|
|
//IRQ raises when low
|
|
flagIRQ[INTERRUPT_FLAG_DETECTOR_OUTPUT] = flagIRQ[INTERRUPT_FLAG_IS_LOW_NEW];
|
|
}
|
|
|
|
void cpu::updateOldInterruptFlag(){
|
|
flagNMI[INTERRUPT_FLAG_IS_LOW_OLD] = flagNMI[INTERRUPT_FLAG_IS_LOW_NEW];
|
|
flagIRQ[INTERRUPT_FLAG_IS_LOW_OLD] = flagIRQ[INTERRUPT_FLAG_IS_LOW_NEW];
|
|
}
|
|
|
|
void cpu::serviceInterrupt(){
|
|
if(flagNMI[INTERRUPT_FLAG_DETECTOR_OUTPUT]){
|
|
flagNMI[INTERRUPT_FLAG_DETECTOR_OUTPUT] = false;
|
|
interruptJump(0xFFFA, 0xFFFB);
|
|
state = newState;
|
|
}
|
|
else if(flagIRQ[INTERRUPT_FLAG_DETECTOR_OUTPUT] && !checkFlag(FLAG_I)){
|
|
interruptJump(0xFFFE, 0xFFFF);
|
|
state = newState;
|
|
}
|
|
}
|
|
|
|
void cpu::interruptJump(Uint16 vectorL, Uint16 vectorH){
|
|
mb->memWrite(0x0100 + newState.reg[REG_STACK_PTR], newState.programCounter >> 8);
|
|
--(newState.reg[REG_STACK_PTR]);
|
|
mb->memWrite(0x0100 + newState.reg[REG_STACK_PTR], newState.programCounter);
|
|
--(newState.reg[REG_STACK_PTR]);
|
|
mb->memWrite(0x0100 + newState.reg[REG_STACK_PTR], newState.reg[REG_STATUS] | 0x30);
|
|
--(newState.reg[REG_STACK_PTR]);
|
|
newState.programCounter = ((mb->memRead(vectorH) << 8) | mb->memRead(vectorL));
|
|
setFlag(FLAG_I);
|
|
}
|
|
|