// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ /* Last validated with zexall 2006.11.21 5:26 PM * Doesn't implement the R register or immediate interrupt after EI. * Address wrap-around isn't completely correct, but is prevented from crashing emulator. */ #include "Ay_Cpu.h" #include "blargg_endian.h" #include //#include "z80_cpu_log.h" /* Copyright (C) 2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define SYNC_TIME() (void) (s.time = s_time) #define RELOAD_TIME() (void) (s_time = s.time) // Callbacks to emulator #define CPU_OUT( cpu, addr, data, TIME )\ ay_cpu_out( cpu, TIME, addr, data ) #define CPU_IN( cpu, addr, TIME )\ ay_cpu_in( cpu, addr ) #include "blargg_source.h" // flags, named with hex value for clarity int const S80 = 0x80; int const Z40 = 0x40; int const F20 = 0x20; int const H10 = 0x10; int const F08 = 0x08; int const V04 = 0x04; int const P04 = 0x04; int const N02 = 0x02; int const C01 = 0x01; #define SZ28P( n ) szpc [n] #define SZ28PC( n ) szpc [n] #define SZ28C( n ) (szpc [n] & ~P04) #define SZ28( n ) SZ28C( n ) #define SET_R( n ) (void) (r.r = n) #define GET_R() (r.r) Ay_Cpu::Ay_Cpu() { state = &state_; for ( int i = 0x100; --i >= 0; ) { int even = 1; for ( int p = i; p; p >>= 1 ) even ^= p; int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04); szpc [i] = n; szpc [i + 0x100] = n | C01; } szpc [0x000] |= Z40; szpc [0x100] |= Z40; } void Ay_Cpu::reset( void* m ) { mem = (uint8_t*) m; check( state == &state_ ); state = &state_; state_.time = 0; state_.base = 0; end_time_ = 0; memset( &r, 0, sizeof r ); } #define TIME (s_time + s.base) #define READ_PROG( addr ) (mem [addr]) #define INSTR( offset ) READ_PROG( pc + (offset) ) #define GET_ADDR() GET_LE16( &READ_PROG( pc ) ) #define READ( addr ) READ_PROG( addr ) #define WRITE( addr, data ) (void) (READ_PROG( addr ) = data) #define READ_WORD( addr ) GET_LE16( &READ_PROG( addr ) ) #define WRITE_WORD( addr, data ) SET_LE16( &READ_PROG( addr ), data ) #define IN( addr ) CPU_IN( this, addr, TIME ) #define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) #if BLARGG_BIG_ENDIAN #define R8( n, offset ) ((r8_ - offset) [n]) #elif BLARGG_LITTLE_ENDIAN #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) #else #error "Byte order of CPU must be known" #endif //#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) // help compiler see that it can just adjust stack offset, saving an extra instruction #define R16( n, shift, offset )\ (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) #define CASE5( a, b, c, d, e ) /* FALLTHRU */ case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e #define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f #define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g #define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h // high four bits are $ED time - 8, low four bits are $DD/$FD time - 8 static byte const ed_dd_timing [0x100] = { //0 1 2 3 4 5 6 7 8 9 A B C D E F 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00, 0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, 0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, 0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, 0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0, 0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, 0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00, 0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, }; bool Ay_Cpu::run( cpu_time_t end_time ) { set_end_time( end_time ); state_t s = this->state_; this->state = &s; bool warning = false; union { regs_t rg; pairs_t rp; uint8_t r8_ [8]; // indexed uint16_t r16_ [4]; }; rg = this->r.b; cpu_time_t s_time = s.time; uint8_t* const mem = this->mem; // cache uint16_t pc = r.pc; uint16_t sp = r.sp; uint16_t ix = r.ix; // TODO: keep in memory for direct access? uint16_t iy = r.iy; int flags = r.b.flags; goto loop; jr_not_taken: s_time -= 5; goto loop; call_not_taken: s_time -= 7; jp_not_taken: pc += 2; loop: check( (unsigned long) pc < 0x10000 ); check( (unsigned long) sp < 0x10000 ); check( (unsigned) flags < 0x100 ); check( (unsigned) ix < 0x10000 ); check( (unsigned) iy < 0x10000 ); uint8_t opcode; opcode = READ_PROG( pc ); pc++; static byte const base_timing [0x100] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1 12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2 12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B 11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C 11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F }; uint16_t data; data = base_timing [opcode]; if ( (s_time += data) >= 0 ) goto possibly_out_of_time; almost_out_of_time: data = READ_PROG( pc ); #ifdef Z80_CPU_LOG_H //log_opcode( opcode, READ_PROG( pc ) ); z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); #endif switch ( opcode ) { possibly_out_of_time: if ( s_time < (int) data ) goto almost_out_of_time; s_time -= data; goto out_of_time; // Common case 0x00: // NOP CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. goto loop; case 0x08:{// EX AF,AF' int temp = r.alt.b.a; r.alt.b.a = rg.a; rg.a = temp; temp = r.alt.b.flags; r.alt.b.flags = flags; flags = temp; goto loop; } case 0xD3: // OUT (imm),A pc++; OUT( data + rg.a * 0x100, rg.a ); goto loop; case 0x2E: // LD L,imm pc++; rg.l = data; goto loop; case 0x3E: // LD A,imm pc++; rg.a = data; goto loop; case 0x3A:{// LD A,(addr) uint16_t addr = GET_ADDR(); pc += 2; rg.a = READ( addr ); goto loop; } // Conditional #define ZERO (flags & Z40) #define CARRY (flags & C01) #define EVEN (flags & P04) #define MINUS (flags & S80) // JR #define JR( cond ) {\ int disp = (int8_t) data;\ pc++;\ if ( !(cond) )\ goto jr_not_taken;\ pc += disp;\ goto loop;\ } case 0x20: JR( !ZERO ) // JR NZ,disp case 0x28: JR( ZERO ) // JR Z,disp case 0x30: JR( !CARRY ) // JR NC,disp case 0x38: JR( CARRY ) // JR C,disp case 0x18: JR( true ) // JR disp case 0x10:{// DJNZ disp int temp = rg.b - 1; rg.b = temp; JR( temp ) } // JP #define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; case 0xC2: JP( !ZERO ) // JP NZ,addr case 0xCA: JP( ZERO ) // JP Z,addr case 0xD2: JP( !CARRY ) // JP NC,addr case 0xDA: JP( CARRY ) // JP C,addr case 0xE2: JP( !EVEN ) // JP PO,addr case 0xEA: JP( EVEN ) // JP PE,addr case 0xF2: JP( !MINUS ) // JP P,addr case 0xFA: JP( MINUS ) // JP M,addr case 0xC3: // JP addr pc = GET_ADDR(); goto loop; case 0xE9: // JP HL pc = rp.hl; goto loop; // RET #define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; case 0xC0: RET( !ZERO ) // RET NZ case 0xC8: RET( ZERO ) // RET Z case 0xD0: RET( !CARRY ) // RET NC case 0xD8: RET( CARRY ) // RET C case 0xE0: RET( !EVEN ) // RET PO case 0xE8: RET( EVEN ) // RET PE case 0xF0: RET( !MINUS ) // RET P case 0xF8: RET( MINUS ) // RET M case 0xC9: // RET ret_taken: pc = READ_WORD( sp ); sp = uint16_t (sp + 2); goto loop; // CALL #define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; case 0xC4: CALL( !ZERO ) // CALL NZ,addr case 0xCC: CALL( ZERO ) // CALL Z,addr case 0xD4: CALL( !CARRY ) // CALL NC,addr case 0xDC: CALL( CARRY ) // CALL C,addr case 0xE4: CALL( !EVEN ) // CALL PO,addr case 0xEC: CALL( EVEN ) // CALL PE,addr case 0xF4: CALL( !MINUS ) // CALL P,addr case 0xFC: CALL( MINUS ) // CALL M,addr case 0xCD:{// CALL addr call_taken: uint16_t addr = pc + 2; pc = GET_ADDR(); sp = uint16_t (sp - 2); WRITE_WORD( sp, addr ); goto loop; } case 0xFF: // RST if ( (pc - 1) > 0xFFFF ) { pc = uint16_t (pc - 1); s_time -= 11; goto loop; } CASE7( C7, CF, D7, DF, E7, EF, F7 ): data = pc; pc = opcode & 0x38; goto push_data; /* fallthrough */ // PUSH/POP case 0xF5: // PUSH AF data = rg.a * 0x100u + flags; goto push_data; case 0xC5: // PUSH BC case 0xD5: // PUSH DE case 0xE5: // PUSH HL data = R16( opcode, 4, 0xC5 ); push_data: sp = uint16_t (sp - 2); WRITE_WORD( sp, data ); goto loop; case 0xF1: // POP AF flags = READ( sp ); rg.a = READ( sp + 1 ); sp = uint16_t (sp + 2); goto loop; case 0xC1: // POP BC case 0xD1: // POP DE case 0xE1: // POP HL R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); sp = uint16_t (sp + 2); goto loop; // ADC/ADD/SBC/SUB case 0x96: // SUB (HL) case 0x86: // ADD (HL) flags &= ~C01; /* fallthrough */ case 0x9E: // SBC (HL) case 0x8E: // ADC (HL) data = READ( rp.hl ); goto adc_data; case 0xD6: // SUB A,imm case 0xC6: // ADD imm flags &= ~C01; /* fallthrough */ case 0xDE: // SBC A,imm case 0xCE: // ADC imm pc++; goto adc_data; CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r flags &= ~C01; /* fallthrough */ CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r data = R8( opcode & 7, 0 ); adc_data: { int result = data + (flags & C01); data ^= rg.a; flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes if ( flags ) result = -result; result += rg.a; data ^= result; flags |=(data & H10) | ((data - -0x80) >> 6 & V04) | SZ28C( result & 0x1FF ); rg.a = result; goto loop; } // CP case 0xBE: // CP (HL) data = READ( rp.hl ); goto cp_data; case 0xFE: // CP imm pc++; goto cp_data; CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r data = R8( opcode, 0xB8 ); cp_data: { int result = rg.a - data; flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01); data ^= rg.a; flags |=(((result ^ rg.a) & data) >> 5 & V04) | (((data & H10) ^ result) & (S80 | H10)); if ( (uint8_t) result ) goto loop; flags |= Z40; goto loop; } // ADD HL,rp case 0x39: // ADD HL,SP data = sp; goto add_hl_data; case 0x09: // ADD HL,BC case 0x19: // ADD HL,DE case 0x29: // ADD HL,HL data = R16( opcode, 4, 0x09 ); add_hl_data: { blargg_ulong sum = rp.hl + data; data ^= rp.hl; rp.hl = sum; flags = (flags & (S80 | Z40 | V04)) | (sum >> 16) | (sum >> 8 & (F20 | F08)) | ((data ^ sum) >> 8 & H10); goto loop; } case 0x27:{// DAA int a = rg.a; if ( a > 0x99 ) flags |= C01; int adjust = 0x60 & -(flags & C01); if ( flags & H10 || (a & 0x0F) > 9 ) adjust |= 0x06; if ( flags & N02 ) adjust = -adjust; a += adjust; flags = (flags & (C01 | N02)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); rg.a = a; goto loop; } /* case 0x27:{// DAA // more optimized, but probably not worth the obscurity int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 adjust |= 0x06; if ( f & N02 ) adjust = -adjust; int a = rg.a + adjust; flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); rg.a = a; goto loop; } */ // INC/DEC case 0x34: // INC (HL) data = READ( rp.hl ) + 1; WRITE( rp.hl, data ); goto inc_set_flags; CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r data = ++R8( opcode >> 3, 0 ); inc_set_flags: flags = (flags & C01) | (((data & 0x0F) - 1) & H10) | SZ28( (uint8_t) data ); if ( data != 0x80 ) goto loop; flags |= V04; goto loop; case 0x35: // DEC (HL) data = READ( rp.hl ) - 1; WRITE( rp.hl, data ); goto dec_set_flags; CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r data = --R8( opcode >> 3, 0 ); dec_set_flags: flags = (flags & C01) | N02 | (((data & 0x0F) + 1) & H10) | SZ28( (uint8_t) data ); if ( data != 0x7F ) goto loop; flags |= V04; goto loop; case 0x03: // INC BC case 0x13: // INC DE case 0x23: // INC HL R16( opcode, 4, 0x03 )++; goto loop; case 0x33: // INC SP sp = uint16_t (sp + 1); goto loop; case 0x0B: // DEC BC case 0x1B: // DEC DE case 0x2B: // DEC HL R16( opcode, 4, 0x0B )--; goto loop; case 0x3B: // DEC SP sp = uint16_t (sp - 1); goto loop; // AND case 0xA6: // AND (HL) data = READ( rp.hl ); goto and_data; case 0xE6: // AND imm pc++; goto and_data; CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r data = R8( opcode, 0xA0 ); and_data: rg.a &= data; flags = SZ28P( rg.a ) | H10; goto loop; // OR case 0xB6: // OR (HL) data = READ( rp.hl ); goto or_data; case 0xF6: // OR imm pc++; goto or_data; CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r data = R8( opcode, 0xB0 ); or_data: rg.a |= data; flags = SZ28P( rg.a ); goto loop; // XOR case 0xAE: // XOR (HL) data = READ( rp.hl ); goto xor_data; case 0xEE: // XOR imm pc++; goto xor_data; CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r data = R8( opcode, 0xA8 ); xor_data: rg.a ^= data; flags = SZ28P( rg.a ); goto loop; // LD CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r WRITE( rp.hl, R8( opcode, 0x70 ) ); goto loop; CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); goto loop; CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm R8( opcode >> 3, 0 ) = data; pc++; goto loop; case 0x36: // LD (HL),imm pc++; WRITE( rp.hl, data ); goto loop; CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) R8( opcode >> 3, 8 ) = READ( rp.hl ); goto loop; case 0x01: // LD rp,imm case 0x11: case 0x21: R16( opcode, 4, 0x01 ) = GET_ADDR(); pc += 2; goto loop; case 0x31: // LD sp,imm sp = GET_ADDR(); pc += 2; goto loop; case 0x2A:{// LD HL,(addr) uint16_t addr = GET_ADDR(); pc += 2; rp.hl = READ_WORD( addr ); goto loop; } case 0x32:{// LD (addr),A uint16_t addr = GET_ADDR(); pc += 2; WRITE( addr, rg.a ); goto loop; } case 0x22:{// LD (addr),HL uint16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, rp.hl ); goto loop; } case 0x02: // LD (BC),A case 0x12: // LD (DE),A WRITE( R16( opcode, 4, 0x02 ), rg.a ); goto loop; case 0x0A: // LD A,(BC) case 0x1A: // LD A,(DE) rg.a = READ( R16( opcode, 4, 0x0A ) ); goto loop; case 0xF9: // LD SP,HL sp = rp.hl; goto loop; // Rotate case 0x07:{// RLCA uint16_t temp = rg.a; temp = (temp << 1) | (temp >> 7); flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08 | C01)); rg.a = temp; goto loop; } case 0x0F:{// RRCA uint16_t temp = rg.a; flags = (flags & (S80 | Z40 | P04)) | (temp & C01); temp = (temp << 7) | (temp >> 1); flags |= temp & (F20 | F08); rg.a = temp; goto loop; } case 0x17:{// RLA blargg_ulong temp = (rg.a << 1) | (flags & C01); flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08)) | (temp >> 8); rg.a = temp; goto loop; } case 0x1F:{// RRA uint16_t temp = (flags << 7) | (rg.a >> 1); flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08)) | (rg.a & C01); rg.a = temp; goto loop; } // Misc case 0x2F:{// CPL uint16_t temp = ~rg.a; flags = (flags & (S80 | Z40 | P04 | C01)) | (temp & (F20 | F08)) | (H10 | N02); rg.a = temp; goto loop; } case 0x3F:{// CCF flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | (flags << 4 & H10) | (rg.a & (F20 | F08)); goto loop; } case 0x37: // SCF flags = (flags & (S80 | Z40 | P04)) | C01 | (rg.a & (F20 | F08)); goto loop; case 0xDB: // IN A,(imm) pc++; rg.a = IN( data + rg.a * 0x100 ); goto loop; case 0xE3:{// EX (SP),HL uint16_t temp = READ_WORD( sp ); WRITE_WORD( sp, rp.hl ); rp.hl = temp; goto loop; } case 0xEB:{// EX DE,HL uint16_t temp = rp.hl; rp.hl = rp.de; rp.de = temp; goto loop; } case 0xD9:{// EXX DE,HL uint16_t temp = r.alt.w.bc; r.alt.w.bc = rp.bc; rp.bc = temp; temp = r.alt.w.de; r.alt.w.de = rp.de; rp.de = temp; temp = r.alt.w.hl; r.alt.w.hl = rp.hl; rp.hl = temp; goto loop; } case 0xF3: // DI r.iff1 = 0; r.iff2 = 0; goto loop; case 0xFB: // EI r.iff1 = 1; r.iff2 = 1; // TODO: delayed effect goto loop; case 0x76: // HALT goto halt; //////////////////////////////////////// CB prefix { case 0xCB: unsigned data2; data2 = INSTR( 1 ); (void) data2; // TODO is this the same as data in all cases? pc++; switch ( data ) { // Rotate left #define RLC( read, write ) {\ uint8_t result = read;\ result = uint8_t (result << 1) | (result >> 7);\ flags = SZ28P( result ) | (result & C01);\ write;\ goto loop;\ } case 0x06: // RLC (HL) s_time += 7; data = rp.hl; rlc_data_addr: RLC( READ( data ), WRITE( data, result ) ) CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r uint8_t& reg = R8( data, 0 ); RLC( reg, reg = result ) } #define RL( read, write ) {\ uint16_t result = (read << 1) | (flags & C01);\ flags = SZ28PC( result );\ write;\ goto loop;\ } case 0x16: // RL (HL) s_time += 7; data = rp.hl; rl_data_addr: RL( READ( data ), WRITE( data, result ) ) CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r uint8_t& reg = R8( data, 0x10 ); RL( reg, reg = result ) } #define SLA( read, add, write ) {\ uint16_t result = (read << 1) | add;\ flags = SZ28PC( result );\ write;\ goto loop;\ } case 0x26: // SLA (HL) s_time += 7; data = rp.hl; sla_data_addr: SLA( READ( data ), 0, WRITE( data, result ) ) CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r uint8_t& reg = R8( data, 0x20 ); SLA( reg, 0, reg = result ) } case 0x36: // SLL (HL) s_time += 7; data = rp.hl; sll_data_addr: SLA( READ( data ), 1, WRITE( data, result ) ) CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r uint8_t& reg = R8( data, 0x30 ); SLA( reg, 1, reg = result ) } // Rotate right #define RRC( read, write ) {\ uint8_t result = read;\ flags = result & C01;\ result = uint8_t (result << 7) | (result >> 1);\ flags |= SZ28P( result );\ write;\ goto loop;\ } case 0x0E: // RRC (HL) s_time += 7; data = rp.hl; rrc_data_addr: RRC( READ( data ), WRITE( data, result ) ) CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r uint8_t& reg = R8( data, 0x08 ); RRC( reg, reg = result ) } #define RR( read, write ) {\ uint8_t result = read;\ uint8_t temp = result & C01;\ result = uint8_t (flags << 7) | (result >> 1);\ flags = SZ28P( result ) | temp;\ write;\ goto loop;\ } case 0x1E: // RR (HL) s_time += 7; data = rp.hl; rr_data_addr: RR( READ( data ), WRITE( data, result ) ) CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r uint8_t& reg = R8( data, 0x18 ); RR( reg, reg = result ) } #define SRA( read, write ) {\ uint8_t result = read;\ flags = result & C01;\ result = (result & 0x80) | (result >> 1);\ flags |= SZ28P( result );\ write;\ goto loop;\ } case 0x2E: // SRA (HL) data = rp.hl; s_time += 7; sra_data_addr: SRA( READ( data ), WRITE( data, result ) ) CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r uint8_t& reg = R8( data, 0x28 ); SRA( reg, reg = result ) } #define SRL( read, write ) {\ uint8_t result = read;\ flags = result & C01;\ result >>= 1;\ flags |= SZ28P( result );\ write;\ goto loop;\ } case 0x3E: // SRL (HL) s_time += 7; data = rp.hl; srl_data_addr: SRL( READ( data ), WRITE( data, result ) ) CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r uint8_t& reg = R8( data, 0x38 ); SRL( reg, reg = result ) } // BIT { unsigned temp; CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL) s_time += 4; temp = READ( rp.hl ); flags &= C01; goto bit_temp; CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r temp = R8( data & 7, 0 ); flags = (flags & C01) | (temp & (F20 | F08)); bit_temp: int masked = temp & 1 << (data >> 3 & 7); flags |=(masked & S80) | H10 | ((masked - 1) >> 8 & (Z40 | P04)); goto loop; } // SET/RES CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) s_time += 7; int temp = READ( rp.hl ); int bit = 1 << (data >> 3 & 7); temp |= bit; // SET if ( !(data & 0x40) ) temp ^= bit; // RES WRITE( rp.hl, temp ); goto loop; } CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); goto loop; CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7)); goto loop; } assert( false ); } //////////////////////////////////////// ED prefix { case 0xED: pc++; s_time += ed_dd_timing [data] >> 4; switch ( data ) { { blargg_ulong temp; case 0x72: // SBC HL,SP case 0x7A: // ADC HL,SP temp = sp; if ( 0 ) case 0x42: // SBC HL,BC case 0x52: // SBC HL,DE case 0x62: // SBC HL,HL case 0x4A: // ADC HL,BC case 0x5A: // ADC HL,DE case 0x6A: // ADC HL,HL temp = R16( data >> 3 & 6, 1, 0 ); blargg_ulong sum = temp + (flags & C01); flags = ~data >> 2 & N02; if ( flags ) sum = -sum; sum += rp.hl; temp ^= rp.hl; temp ^= sum; flags |=(sum >> 16 & C01) | (temp >> 8 & H10) | (sum >> 8 & (S80 | F20 | F08)) | ((temp - -0x8000) >> 14 & V04); rp.hl = sum; if ( (uint16_t) sum ) goto loop; flags |= Z40; goto loop; } CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) int temp = IN( rp.bc ); R8( data >> 3, 8 ) = temp; flags = (flags & C01) | SZ28P( temp ); goto loop; } case 0x71: // OUT (C),0 rg.flags = 0; CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r OUT( rp.bc, R8( data >> 3, 8 ) ); goto loop; { unsigned temp; case 0x73: // LD (ADDR),SP temp = sp; if ( 0 ) case 0x43: // LD (ADDR),BC case 0x53: // LD (ADDR),DE temp = R16( data, 4, 0x43 ); uint16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, temp ); goto loop; } case 0x4B: // LD BC,(ADDR) case 0x5B:{// LD DE,(ADDR) uint16_t addr = GET_ADDR(); pc += 2; R16( data, 4, 0x4B ) = READ_WORD( addr ); goto loop; } case 0x7B:{// LD SP,(ADDR) uint16_t addr = GET_ADDR(); pc += 2; sp = READ_WORD( addr ); goto loop; } case 0x67:{// RRD uint8_t temp = READ( rp.hl ); WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); temp = (rg.a & 0xF0) | (temp & 0x0F); flags = (flags & C01) | SZ28P( temp ); rg.a = temp; goto loop; } case 0x6F:{// RLD uint8_t temp = READ( rp.hl ); WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); temp = (rg.a & 0xF0) | (temp >> 4); flags = (flags & C01) | SZ28P( temp ); rg.a = temp; goto loop; } CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG opcode = 0x10; // flag to do SBC instead of ADC flags &= ~C01; data = rg.a; rg.a = 0; goto adc_data; { int inc; case 0xA9: // CPD case 0xB9: // CPDR inc = -1; if ( 0 ) case 0xA1: // CPI case 0xB1: // CPIR inc = +1; uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); int result = rg.a - temp; flags = (flags & C01) | N02 | ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); if ( !(uint8_t) result ) flags |= Z40; result -= (flags & H10) >> 4; flags |= result & F08; flags |= result << 4 & F20; if ( !--rp.bc ) goto loop; flags |= V04; if ( flags & Z40 || data < 0xB0 ) goto loop; pc -= 2; s_time += 5; goto loop; } { int inc; case 0xA8: // LDD case 0xB8: // LDDR inc = -1; if ( 0 ) case 0xA0: // LDI case 0xB0: // LDIR inc = +1; uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); addr = rp.de; rp.de = addr + inc; WRITE( addr, temp ); temp += rg.a; flags = (flags & (S80 | Z40 | C01)) | (temp & F08) | (temp << 4 & F20); if ( !--rp.bc ) goto loop; flags |= V04; if ( data < 0xB0 ) goto loop; pc -= 2; s_time += 5; goto loop; } { int inc; case 0xAB: // OUTD case 0xBB: // OTDR inc = -1; if ( 0 ) case 0xA3: // OUTI case 0xB3: // OTIR inc = +1; uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); int b = --rg.b; flags = (temp >> 6 & N02) | SZ28( b ); if ( b && data >= 0xB0 ) { pc -= 2; s_time += 5; } OUT( rp.bc, temp ); goto loop; } { int inc; case 0xAA: // IND case 0xBA: // INDR inc = -1; if ( 0 ) case 0xA2: // INI case 0xB2: // INIR inc = +1; uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = IN( rp.bc ); int b = --rg.b; flags = (temp >> 6 & N02) | SZ28( b ); if ( b && data >= 0xB0 ) { pc -= 2; s_time += 5; } WRITE( addr, temp ); goto loop; } case 0x47: // LD I,A r.i = rg.a; goto loop; case 0x4F: // LD R,A SET_R( rg.a ); debug_printf( "LD R,A not supported\n" ); warning = true; goto loop; case 0x57: // LD A,I rg.a = r.i; goto ld_ai_common; case 0x5F: // LD A,R rg.a = GET_R(); debug_printf( "LD A,R not supported\n" ); warning = true; ld_ai_common: flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); goto loop; CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN r.iff1 = r.iff2; goto ret_taken; case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 r.im = 0; goto loop; case 0x56: case 0x76: // IM 1 r.im = 1; goto loop; case 0x5E: case 0x7E: // IM 2 r.im = 2; goto loop; default: debug_printf( "Opcode $ED $%02X not supported\n", data ); warning = true; goto loop; } assert( false ); } //////////////////////////////////////// DD/FD prefix { uint16_t ixy; case 0xDD: ixy = ix; goto ix_prefix; case 0xFD: ixy = iy; ix_prefix: pc++; unsigned data2 = READ_PROG( pc ); s_time += ed_dd_timing [data] & 0x0F; switch ( data ) { // TODO: more efficient way of avoid negative address #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; // ADD/ADC/SUB/SBC case 0x96: // SUB (IXY+disp) case 0x86: // ADD (IXY+disp) flags &= ~C01; /*fallthrough*/ case 0x9E: // SBC (IXY+disp) case 0x8E: // ADC (IXY+disp) pc++; opcode = data; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto adc_data; case 0x94: // SUB HXY case 0x84: // ADD HXY flags &= ~C01; /*fallthrough*/ case 0x9C: // SBC HXY case 0x8C: // ADC HXY opcode = data; data = ixy >> 8; goto adc_data; case 0x95: // SUB LXY case 0x85: // ADD LXY flags &= ~C01; /* fallthrough */ case 0x9D: // SBC LXY case 0x8D: // ADC LXY opcode = data; data = (uint8_t) ixy; goto adc_data; { unsigned temp; case 0x39: // ADD IXY,SP temp = sp; goto add_ixy_data; case 0x29: // ADD IXY,HL temp = ixy; goto add_ixy_data; case 0x09: // ADD IXY,BC case 0x19: // ADD IXY,DE temp = R16( data, 4, 0x09 ); add_ixy_data: { blargg_ulong sum = ixy + temp; temp ^= ixy; ixy = (uint16_t) sum; flags = (flags & (S80 | Z40 | V04)) | (sum >> 16) | (sum >> 8 & (F20 | F08)) | ((temp ^ sum) >> 8 & H10); goto set_ixy; } } // AND case 0xA6: // AND (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto and_data; case 0xA4: // AND HXY data = ixy >> 8; goto and_data; case 0xA5: // AND LXY data = (uint8_t) ixy; goto and_data; // OR case 0xB6: // OR (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto or_data; case 0xB4: // OR HXY data = ixy >> 8; goto or_data; case 0xB5: // OR LXY data = (uint8_t) ixy; goto or_data; // XOR case 0xAE: // XOR (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto xor_data; case 0xAC: // XOR HXY data = ixy >> 8; goto xor_data; case 0xAD: // XOR LXY data = (uint8_t) ixy; goto xor_data; // CP case 0xBE: // CP (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto cp_data; case 0xBC: // CP HXY data = ixy >> 8; goto cp_data; case 0xBD: // CP LXY data = (uint8_t) ixy; goto cp_data; // LD CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r data = R8( data, 0x70 ); if ( 0 ) case 0x36: // LD (IXY+disp),imm pc++, data = READ_PROG( pc ); pc++; WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); goto loop; CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY R8( data >> 3, 8 ) = ixy >> 8; goto loop; case 0x64: // LD HXY,HXY case 0x6D: // LD LXY,LXY goto loop; CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY R8( data >> 3, 8 ) = ixy; goto loop; CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) pc++; R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto loop; case 0x26: // LD HXY,imm pc++; goto ld_hxy_data; case 0x65: // LD HXY,LXY data2 = (uint8_t) ixy; goto ld_hxy_data; CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r data2 = R8( data, 0x60 ); ld_hxy_data: ixy = (uint8_t) ixy | (data2 << 8); goto set_ixy; case 0x2E: // LD LXY,imm pc++; goto ld_lxy_data; case 0x6C: // LD LXY,HXY data2 = ixy >> 8; goto ld_lxy_data; CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r data2 = R8( data, 0x68 ); ld_lxy_data: ixy = (ixy & 0xFF00) | data2; set_ixy: if ( opcode == 0xDD ) { ix = ixy; goto loop; } iy = ixy; goto loop; case 0xF9: // LD SP,IXY sp = ixy; goto loop; case 0x22:{// LD (ADDR),IXY uint16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, ixy ); goto loop; } case 0x21: // LD IXY,imm ixy = GET_ADDR(); pc += 2; goto set_ixy; case 0x2A:{// LD IXY,(addr) uint16_t addr = GET_ADDR(); ixy = READ_WORD( addr ); pc += 2; goto set_ixy; } // DD/FD CB prefix case 0xCB: { data = IXY_DISP( ixy, (int8_t) data2 ); pc++; data2 = READ_PROG( pc ); pc++; switch ( data2 ) { case 0x06: goto rlc_data_addr; // RLC (IXY) case 0x16: goto rl_data_addr; // RL (IXY) case 0x26: goto sla_data_addr; // SLA (IXY) case 0x36: goto sll_data_addr; // SLL (IXY) case 0x0E: goto rrc_data_addr; // RRC (IXY) case 0x1E: goto rr_data_addr; // RR (IXY) case 0x2E: goto sra_data_addr; // SRA (IXY) case 0x3E: goto srl_data_addr; // SRL (IXY) CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) uint8_t temp = READ( data ); int masked = temp & 1 << (data2 >> 3 & 7); flags = (flags & C01) | H10 | (masked & S80) | ((masked - 1) >> 8 & (Z40 | P04)); goto loop; } CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) int temp = READ( data ); int bit = 1 << (data2 >> 3 & 7); temp |= bit; // SET if ( !(data2 & 0x40) ) temp ^= bit; // RES WRITE( data, temp ); goto loop; } default: debug_printf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); warning = true; goto loop; } assert( false ); } // INC/DEC case 0x23: // INC IXY ixy = uint16_t (ixy + 1); goto set_ixy; case 0x2B: // DEC IXY ixy = uint16_t (ixy - 1); goto set_ixy; case 0x34: // INC (IXY+disp) ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) + 1; WRITE( ixy, data ); goto inc_set_flags; case 0x35: // DEC (IXY+disp) ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) - 1; WRITE( ixy, data ); goto dec_set_flags; case 0x24: // INC HXY ixy = uint16_t (ixy + 0x100); data = ixy >> 8; goto inc_xy_common; case 0x2C: // INC LXY data = uint8_t (ixy + 1); ixy = (ixy & 0xFF00) | data; inc_xy_common: if ( opcode == 0xDD ) { ix = ixy; goto inc_set_flags; } iy = ixy; goto inc_set_flags; case 0x25: // DEC HXY ixy = uint16_t (ixy - 0x100); data = ixy >> 8; goto dec_xy_common; case 0x2D: // DEC LXY data = uint8_t (ixy - 1); ixy = (ixy & 0xFF00) | data; dec_xy_common: if ( opcode == 0xDD ) { ix = ixy; goto dec_set_flags; } iy = ixy; goto dec_set_flags; // PUSH/POP case 0xE5: // PUSH IXY data = ixy; goto push_data; case 0xE1:{// POP IXY ixy = READ_WORD( sp ); sp = uint16_t (sp + 2); goto set_ixy; } // Misc case 0xE9: // JP (IXY) pc = ixy; goto loop; case 0xE3:{// EX (SP),IXY uint16_t temp = READ_WORD( sp ); WRITE_WORD( sp, ixy ); ixy = temp; goto set_ixy; } default: debug_printf( "Unnecessary DD/FD prefix encountered\n" ); warning = true; pc--; goto loop; } assert( false ); } } debug_printf( "Unhandled main opcode: $%02X\n", opcode ); assert( false ); halt: s_time &= 3; // increment by multiple of 4 out_of_time: pc--; s.time = s_time; rg.flags = flags; r.ix = ix; r.iy = iy; r.sp = sp; r.pc = pc; this->r.b = rg; this->state_ = s; this->state = &this->state_; return warning; }