#include "sm83.h" #include "mem.h" #include "debug.h" #include namespace sm83 { static uint32_t clock = 4194304; static uint32_t t = 0; static uint16_t current_opcode_address = 0; bool options[SM83_NUM_OPTIONS] = { true, false }; #define f0 0b00000001 #define f1 0b00000010 #define f2 0b00000100 #define f3 0b00001000 #define fC 0b00010000 #define fH 0b00100000 #define fN 0b01000000 #define fZ 0b10000000 #define cUnconditional 255 #define cNZ 0 #define cZ 1 #define cNC 2 #define cC 3 uint8_t regs[13]; uint8_t *_rF = ®s[0]; uint8_t *_rA = ®s[1]; uint8_t *_rC = ®s[2]; uint8_t *_rB = ®s[3]; uint8_t *_rE = ®s[4]; uint8_t *_rD = ®s[5]; uint8_t *_rL = ®s[6]; uint8_t *_rH = ®s[7]; uint16_t *_rAF = (uint16_t*)®s[0]; uint16_t *_rBC = (uint16_t*)®s[2]; uint16_t *_rDE = (uint16_t*)®s[4]; uint16_t *_rHL = (uint16_t*)®s[6]; uint16_t *_rSP = (uint16_t*)®s[8]; uint16_t *_rPC = (uint16_t*)®s[10]; uint8_t *_rIME = ®s[12]; bool exit_from_halt = false; int pending_ei = 0; #define rA (*_rA) #define rF (*_rF) #define rB (*_rB) #define rC (*_rC) #define rD (*_rD) #define rE (*_rE) #define rH (*_rH) #define rL (*_rL) #define rAF (*_rAF) #define rBC (*_rBC) #define rDE (*_rDE) #define rHL (*_rHL) #define rSP (*_rSP) #define rPC (*_rPC) #define ime (*_rIME) #define EX(a,b) {auto temp=a;a=b;b=temp;} bool reading_m1 = false; uint8_t READ_MEM_8(const uint16_t addr, const bool code=false) { if (debug::isbreak(addr, 2)) debug::stop(); t+=4; const uint8_t tag = mem::getTag(addr); if ( !(tag&MEMTAG_IGNORE) ) { if (!code) { if ( tag & MEMTAG_INST ) { } else { mem::setTag(addr, tag | MEMTAG_DATA); } } else { if ( (reading_m1) && ( tag & MEMTAG_DATA ) ) { } } } reading_m1 = false; return mem::readMem(addr); } uint8_t READ_MEM_8() { const uint8_t data = READ_MEM_8(rPC, true); const uint8_t tag = mem::getTag(rPC); if ( !(tag & MEMTAG_IGNORE) ) mem::setTag(rPC, tag | MEMTAG_CODE); rPC++; return data; } uint8_t READ_M1() { reading_m1 = true; return READ_MEM_8(); } uint16_t READ_MEM_16() { const uint8_t L = READ_MEM_8(); const uint8_t H = READ_MEM_8(); return (H << 8) + L; } uint16_t READ_MEM_16(const uint16_t addr) { return READ_MEM_8(addr) + (READ_MEM_8(addr+1) << 8); } const uint8_t WRITE_MEM_8(const uint16_t addr, const uint8_t value) { t+=4; mem::writeMem(addr, value); if (debug::isbreak(addr, 4)) debug::stop(); debug::setmemmodified(addr); const uint8_t tag = mem::getTag(addr); if ( !(tag & MEMTAG_IGNORE) ) { if ( tag & MEMTAG_INST ) { //printf("WARNING! WRITING DATA OVER CODE!!! $%4X\n", addr); //z80debug::stop(); } else { mem::setTag(addr, tag | MEMTAG_DATA | MEMTAG_TDATA); } } return value; } void WRITE_MEM_16(const uint16_t addr, const uint16_t value) { WRITE_MEM_8(addr, value & 0xff); WRITE_MEM_8(addr+1, (value>>8) & 0xff); } void PUSH(uint16_t b) { rSP-=2; WRITE_MEM_16(rSP, b); t+=4; } void POP(uint16_t *a) { *a = READ_MEM_16(rSP); rSP+=2; } inline void KEEP_FLAGS(const uint8_t flags) { rF &= flags; } inline void SET_FLAGS(const uint8_t flags) { rF |= flags; } void INC16(uint16_t *reg) { t+=4; (*reg)++; } void DEC16(uint16_t *reg) { t+=4; (*reg)--; } void ADD16(uint16_t* a, uint16_t b) { t+=4; const uint32_t res = *a + b; KEEP_FLAGS(fZ); if ( ((*a&0xfff)+(b&0xfff))&0x1000 ) SET_FLAGS(fH); if ( res > 0xffff ) SET_FLAGS(fC); *a = (uint16_t)res; } void INC8(uint8_t *reg) { uint8_t value = *reg + 1; KEEP_FLAGS(fC); if (value==0) SET_FLAGS(fZ); if ( (value & 0x0f) == 0x00 ) SET_FLAGS(fH); *reg = value; } void DEC8(uint8_t *reg) { uint8_t value = *reg - 1; KEEP_FLAGS(fC); SET_FLAGS(fN); if (value==0) SET_FLAGS(fZ); if ( (value & 0x0f) == 0x0f ) SET_FLAGS(fH); *reg = value; } void INCMEM8(const uint16_t addr) { uint8_t value = READ_MEM_8(addr); value++; KEEP_FLAGS(fC); if (value==0) SET_FLAGS(fZ); if ( (value & 0x0f) == 0x00 ) SET_FLAGS(fH); WRITE_MEM_8(addr, value); } void DECMEM8(const uint16_t addr) { uint8_t value = READ_MEM_8(addr); value--; KEEP_FLAGS(fC); SET_FLAGS(fN); if (value==0) SET_FLAGS(fZ); if ( (value & 0x0f) == 0x0f ) SET_FLAGS(fH); WRITE_MEM_8(addr, value); } void ADD8(uint8_t b) { const uint8_t res = rA + b; rF=0; if (res==0) SET_FLAGS(fZ); if ( (res & 0x0f) < (rA & 0x0f) ) SET_FLAGS(fH); if ( res < rA ) SET_FLAGS(fC); rA = (uint8_t)res; } void ADC8(uint8_t b) { if (!(rF & fC)) ADD8(b); else { uint16_t res = rA + b + 1; rF=0; if (res==0) SET_FLAGS(fZ); if ( (res & 0x0f) <= (rA & 0x0f) ) SET_FLAGS(fH); if ( res > 255 ) SET_FLAGS(fC); rA = (uint8_t)res; } } void SUB8(uint8_t b, const bool update=true) { const uint8_t res = rA - b; rF=0; if (res==0) SET_FLAGS(fZ); SET_FLAGS(fN); if ( (res & 0x0f) > (rA & 0x0f) ) SET_FLAGS(fH); if ( res > rA ) SET_FLAGS(fC); if (update) rA = (uint8_t)res; } void SBC8(uint8_t b) { if (!(rF & fC)) SUB8(b); else { const uint8_t res = rA - b - 1; rF=0; if (res==0) SET_FLAGS(fZ); SET_FLAGS(fN); if ( (res & 0x0f) >= (rA & 0x0f) ) SET_FLAGS(fH); if ( res >= rA ) SET_FLAGS(fC); rA = (uint8_t)res; } } void AND(uint8_t b) { rA = rA & b; rF=fH; if (rA==0) SET_FLAGS(fZ); } void XOR(uint8_t b) { rA = rA ^ b; rF=0; if (rA==0) SET_FLAGS(fZ); } void OR(uint8_t b) { rA = rA | b; rF=0; if (rA==0) SET_FLAGS(fZ); } void CP(uint8_t b) { SUB8(b, false); } void RLCA() { const bool should_carry = rA & 0x80; rA = (rA>>7) | (rA<<1); rF = 0; if (should_carry) SET_FLAGS(fC); } void RRCA() { const bool should_carry = rA & 0x01; rA = (rA<<7) | (rA>>1); rF = 0; if (should_carry) SET_FLAGS(fC); } void RLA() { const bool should_carry = rA & 0x80; rA = (rA<<1) | ((rF&fC)>>4); rF = 0; if (should_carry) SET_FLAGS(fC); } void RRA() { const bool should_carry = rA & 0x01; rA = ((rF&fC)<<3) | (rA>>1); rF = 0; if (should_carry) SET_FLAGS(fC); } /* void new_DAA() { uint8_t cf = rA > 0x99; uint8_t diff = ((rF & fH) || (rA & 0xF) > 9) ? 6 : 0; if ((rF & fC) || cf) diff |= 0x60; diff = (rF & fN) ? rA - diff : rA + diff; KEEP_FLAGS(fN | fC); FLAGS_SZXY(diff); SET_FLAGS(!(diff) << 6); SET_FLAGS((rA ^ diff) & fH); SET_PARITY_FLAG(diff); SET_FLAGS(cf); rA = diff; t+=4; } */ void DAA() { bool carry_set = false; uint8_t old = rA; if ( (rA & 0x0F)>9 || (rF & fH) ) rA += 0x06; if ( (rA >> 4)>9 || (rF & fC) ) { rA += 0x60; carry_set = true; } KEEP_FLAGS(fN); if (rA==0) SET_FLAGS(fZ); if ( (rA & 0x0F) < (old & 0x0F) ) SET_FLAGS(fH); if (carry_set) SET_FLAGS(fC); } void CPL() { rA = ~rA; KEEP_FLAGS(fZ | fC); SET_FLAGS(fH | fN); } void SCF() { KEEP_FLAGS( fZ ); SET_FLAGS(fC); } void CCF() { const bool carry = (rF & fC); KEEP_FLAGS( fZ ); SET_FLAGS(carry ? fH : fC); } void JR(uint8_t cond = cUnconditional) { const int8_t d = (int8_t)READ_MEM_8(); if ( cond == cUnconditional || ( (cond == cNZ) && !(rF & fZ) ) || ( (cond == cZ) && (rF & fZ) ) || ( (cond == cNC) && !(rF & fC) ) || ( (cond == cC) && (rF & fC) ) ) { rPC = rPC + d; t+=4; } } void JP(uint8_t cond, uint16_t addr, const bool fast = false) { if ( cond == cUnconditional || ( (cond == cNZ) && !(rF & fZ) ) || ( (cond == cZ) && (rF & fZ) ) || ( (cond == cNC) && !(rF & fC) ) || ( (cond == cC) && (rF & fC) ) ) rPC = addr; if (!fast) t+=4; } void CALL(uint8_t cond, uint16_t addr) { if ( cond == cUnconditional || ( (cond == cNZ) && !(rF & fZ) ) || ( (cond == cZ) && (rF & fZ) ) || ( (cond == cNC) && !(rF & fC) ) || ( (cond == cC) && (rF & fC) ) ) { PUSH(rPC); rPC = addr; /*if (options[Z80_OPTION_BREAK_ON_RET]) { calls_stacked++; printf("CALL: calls stacked: %i", calls_stacked); }*/ } } void RET(uint8_t cond) { t+=4; if ( cond == cUnconditional || ( (cond == cNZ) && !(rF & fZ) ) || ( (cond == cZ) && (rF & fZ) ) || ( (cond == cNC) && !(rF & fC) ) || ( (cond == cC) && (rF & fC) ) ) { POP(_rPC); if (cond != cUnconditional) t += 4; /*if (options[Z80_OPTION_BREAK_ON_RET]) { if (calls_stacked>0) { calls_stacked--; printf("RET: calls still stacked: %i\n", calls_stacked); } else { printf("RET: BREAK\n"); options[Z80_OPTION_BREAK_ON_RET] = false; z80debug::setcursor(rPC); z80debug::history::store(); z80debug::stop(); } }*/ } } void DI(); void processInterrupts() { const uint8_t IF = mem::readMem(0xff0f); if ( (IF & 0x1f) == 0) return; DI(); PUSH(rPC); t+=20; if (IF & INTERRUPT_VBLANK) { rPC = 0x40; mem::writeMem(0xff0f, IF & ~INTERRUPT_VBLANK); } else if (IF & INTERRUPT_LCD) { rPC = 0x40; mem::writeMem(0xff0f, IF & ~INTERRUPT_LCD); } else if (IF & INTERRUPT_TIMER) { rPC = 0x40; mem::writeMem(0xff0f, IF & ~INTERRUPT_TIMER); } else if (IF & INTERRUPT_SERIAL) { rPC = 0x40; mem::writeMem(0xff0f, IF & ~INTERRUPT_SERIAL); } else if (IF & INTERRUPT_JOYPAD) { rPC = 0x40; mem::writeMem(0xff0f, IF & ~INTERRUPT_JOYPAD); } /*if (options[Z80_OPTION_BREAK_ON_INTERRUPT]) { printf("Break on interrupt! 0x%2x, PC: 0x%2x\n", address, rPC); z80debug::setcursor(rPC); z80debug::history::store(); z80debug::stop(); }*/ } void RETI() { RET(cUnconditional); *_rIME = 1; processInterrupts(); } void RST(uint8_t vec) { t-=4; // PUSH i RST duren el mateix en la gameboy, per tant llevem els 4 t que duiem de llegir el opcode PUSH(rPC); rPC = vec; } void DI() { pending_ei = 0; // If interrupts are disabled right after enabling them, they keep disabled *_rIME = 0; } void EI() { pending_ei = 1; // Enabling interrupts is delayed by one instruction } void actualEI() { *_rIME = 1; processInterrupts(); } void HALT() { if (exit_from_halt) { exit_from_halt = false; } else { //printf("HALT\n"); rPC--; } } void STOP() { HALT(); } void interrupt(uint8_t type) { exit_from_halt = true; const uint8_t IE = mem::readMem(0xffff); const uint8_t IF = mem::readMem(0xff0f); mem::writeMem(0xff0f, IF || (IE & type)); processInterrupts(); } static inline const uint8_t RLC(const uint8_t v) { const uint8_t res = (v>>7) | (v<<1); rF=0; if (res==0) SET_FLAGS(fZ); if (res&1) SET_FLAGS(fC); return res; } static inline const uint8_t RRC(const uint8_t v) { const uint8_t res = (v<<7) | (v>>1); rF=0; if (res==0) SET_FLAGS(fZ); if (res&0x80) SET_FLAGS(fC); return res; } static inline const uint8_t RL(const uint8_t v) { const uint8_t res = (v<<1) | ((rF&fC)>>4); rF=0; if (res==0) SET_FLAGS(fZ); if (v&0x80) SET_FLAGS(fC); return res; } static inline const uint8_t RR(const uint8_t v) { const uint8_t res = ((rF&fC)<<3) | (v>>1); rF=0; if (res==0) SET_FLAGS(fZ); if (v&1) SET_FLAGS(fC); return res; } static inline const uint8_t SLA(const uint8_t v) { const uint8_t res = (v<<1); rF=0; if (res==0) SET_FLAGS(fZ); if (v&0x80) SET_FLAGS(fC); return res; } static inline const uint8_t SRA(const uint8_t v) { const uint8_t res = (v&0x80) | (v>>1); rF=0; if (res==0) SET_FLAGS(fZ); if (v&1) SET_FLAGS(fC); return res; } static inline const uint8_t SWAP(const uint8_t v) { const uint8_t res = (v<<4) | (v>>4); rF=0; if (res==0) SET_FLAGS(fZ); //if (v&0x80) SET_FLAGS(fC); // [TODO] fC reset o calculat? return res; } static inline const uint8_t SRL(const uint8_t v) { const uint8_t res = (v>>1); rF=0; if (res==0) SET_FLAGS(fZ); if (v&1) SET_FLAGS(fC); return res; } void BIT(uint8_t pos, uint8_t v) { const uint8_t res = v & 1<