1 // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ 2 3 #include "Hes_Cpu.h" 4 5 #include "blargg_endian.h" 6 7 //#include "hes_cpu_log.h" 8 9 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you 10 can redistribute it and/or modify it under the terms of the GNU Lesser 11 General Public License as published by the Free Software Foundation; either 12 version 2.1 of the License, or (at your option) any later version. This 13 module is distributed in the hope that it will be useful, but WITHOUT ANY 14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 16 details. You should have received a copy of the GNU Lesser General Public 17 License along with this module; if not, write to the Free Software Foundation, 18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 19 20 // TODO: support T flag, including clearing it at appropriate times? 21 22 // all zero-page should really use whatever is at page 1, but that would 23 // reduce efficiency quite a bit 24 int const ram_addr = 0x2000; 25 26 #define FLUSH_TIME() (void) (s.time = s_time) 27 #define CACHE_TIME() (void) (s_time = s.time) 28 29 #include "hes_cpu_io.h" 30 31 #include "blargg_source.h" 32 33 #if BLARGG_NONPORTABLE 34 #define PAGE_OFFSET( addr ) (addr) 35 #else 36 #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) 37 #endif 38 39 // status flags 40 int const st_n = 0x80; 41 int const st_v = 0x40; 42 int const st_t = 0x20; 43 int const st_b = 0x10; 44 int const st_d = 0x08; 45 int const st_i = 0x04; 46 int const st_z = 0x02; 47 int const st_c = 0x01; 48 49 void Hes_Cpu::reset() 50 { 51 check( state == &state_ ); 52 state = &state_; 53 54 state_.time = 0; 55 state_.base = 0; customer_creditnull56 irq_time_ = future_hes_time; 57 end_time_ = future_hes_time; 58 customer_creditnull59 r.status = st_i; 60 r.sp = 0; 61 r.pc = 0; 62 r.a = 0; 63 r.x = 0; 64 r.y = 0; 65 66 blargg_verify_byte_order(); 67 } 68 69 void Hes_Cpu::set_mmr( int reg, int bank ) 70 { 71 assert( (unsigned) reg <= page_count ); // allow page past end to be set 72 assert( (unsigned) bank < 0x100 ); 73 mmr [reg] = bank; 74 uint8_t const* code = CPU_SET_MMR( this, reg, bank ); 75 state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift ); 76 } 77 78 #define TIME (s_time + s.base) 79 80 #define READ( addr ) CPU_READ( this, (addr), TIME ) 81 #define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} 82 #define READ_LOW( addr ) (ram [int (addr)]) 83 #define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) 84 #define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) 85 86 #define SET_SP( v ) (sp = ((v) + 1) | 0x100) 87 #define GET_SP() ((sp - 1) & 0xFF) 88 #define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) 89 90 bool Hes_Cpu::run( hes_time_t end_time ) 91 { 92 bool illegal_encountered = false; 93 set_end_time( end_time ); 94 state_t s = this->state_; 95 this->state = &s; 96 // even on x86, using s.time in place of s_time was slower 97 blargg_long s_time = s.time; 98 99 // registers 100 uint_fast16_t pc = r.pc; 101 uint_fast8_t a = r.a; 102 uint_fast8_t x = r.x; 103 uint_fast8_t y = r.y; 104 uint_fast16_t sp; 105 SET_SP( r.sp ); 106 107 #define IS_NEG (nz & 0x8080) 108 109 #define CALC_STATUS( out ) do {\ 110 out = status & (st_v | st_d | st_i);\ 111 out |= ((nz >> 8) | nz) & st_n;\ 112 out |= c >> 8 & st_c;\ 113 if ( !(nz & 0xFF) ) out |= st_z;\ 114 } while ( 0 ) 115 116 #define SET_STATUS( in ) do {\ 117 status = in & (st_v | st_d | st_i);\ 118 nz = in << 8;\ 119 c = nz;\ 120 nz |= ~in & st_z;\ 121 } while ( 0 ) 122 123 uint_fast8_t status; 124 uint_fast16_t c; // carry set if (c & 0x100) != 0 125 uint_fast16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 126 { 127 uint_fast8_t temp = r.status; 128 SET_STATUS( temp ); 129 } 130 131 goto loop; 132 branch_not_taken: 133 s_time -= 2; 134 loop: 135 136 #ifndef NDEBUG 137 { 138 hes_time_t correct = end_time_; 139 if ( !(status & st_i) && correct > irq_time_ ) 140 correct = irq_time_; 141 check( s.base == correct ); 142 /* 143 static long count; 144 if ( count == 1844 ) Debugger(); 145 if ( s.base != correct ) debug_printf( "%ld\n", count ); 146 count++; 147 */ 148 } 149 #endif 150 151 check( (unsigned) GET_SP() < 0x100 ); 152 check( (unsigned) a < 0x100 ); 153 check( (unsigned) x < 0x100 ); 154 155 uint8_t const* instr = s.code_map [pc >> page_shift]; 156 uint_fast8_t opcode; 157 158 // TODO: eliminate this special case 159 #if BLARGG_NONPORTABLE 160 opcode = instr [pc]; 161 pc++; 162 instr += pc; 163 #else 164 instr += PAGE_OFFSET( pc ); 165 opcode = *instr++; 166 pc++; 167 #endif 168 169 // TODO: each reference lists slightly different timing values, ugh 170 static uint8_t const clock_table [256] = 171 {// 0 1 2 3 4 5 6 7 8 9 A B C D E F 172 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6,// 0 173 4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1 174 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2 175 4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3 176 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4 177 4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5 178 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6 179 4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7 180 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8 181 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9 182 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A 183 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B 184 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C 185 4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D 186 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E 187 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F 188 }; // 0x00 was 8 189 190 uint_fast16_t data; 191 data = clock_table [opcode]; 192 if ( (s_time += data) >= 0 ) 193 goto possibly_out_of_time; 194 almost_out_of_time: 195 196 data = *instr; 197 198 #ifdef HES_CPU_LOG_H 199 log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2], 200 instr [3], instr [4], instr [5] ); 201 //log_opcode( opcode ); 202 #endif 203 204 switch ( opcode ) 205 { 206 possibly_out_of_time: 207 if ( s_time < (int) data ) 208 goto almost_out_of_time; 209 s_time -= data; 210 goto out_of_time; 211 212 // Macros 213 214 #define GET_MSB() (instr [1]) 215 #define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB()); 216 #define GET_ADDR() GET_LE16( instr ) 217 218 // TODO: is the penalty really always added? the original 6502 was much better 219 //#define PAGE_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8) 220 #define PAGE_CROSS_PENALTY( lsb ) 221 222 // Branch 223 224 // TODO: more efficient way to handle negative branch that wraps PC around 225 #define BRANCH( cond )\ 226 {\ 227 int_fast16_t offset = (int8_t) data;\ 228 pc++;\ 229 if ( !(cond) ) goto branch_not_taken;\ 230 pc = uint16_t (pc + offset);\ 231 goto loop;\ 232 } 233 234 case 0xF0: // BEQ 235 BRANCH( !((uint8_t) nz) ); 236 237 case 0xD0: // BNE 238 BRANCH( (uint8_t) nz ); 239 240 case 0x10: // BPL 241 BRANCH( !IS_NEG ); 242 243 case 0x90: // BCC 244 BRANCH( !(c & 0x100) ) 245 246 case 0x30: // BMI 247 BRANCH( IS_NEG ) 248 249 case 0x50: // BVC 250 BRANCH( !(status & st_v) ) 251 252 case 0x70: // BVS 253 BRANCH( status & st_v ) 254 255 case 0xB0: // BCS 256 BRANCH( c & 0x100 ) 257 258 case 0x80: // BRA 259 branch_taken: 260 BRANCH( true ); 261 262 case 0xFF: 263 if ( pc == idle_addr + 1 ) 264 goto idle_done; 265 case 0x0F: // BBRn 266 case 0x1F: 267 case 0x2F: 268 case 0x3F: 269 case 0x4F: 270 case 0x5F: 271 case 0x6F: 272 case 0x7F: 273 case 0x8F: // BBSn 274 case 0x9F: 275 case 0xAF: 276 case 0xBF: 277 case 0xCF: 278 case 0xDF: 279 case 0xEF: { 280 uint_fast16_t t = 0x101 * READ_LOW( data ); 281 t ^= 0xFF; 282 pc++; 283 data = GET_MSB(); 284 BRANCH( t & (1 << (opcode >> 4)) ) 285 } 286 287 case 0x4C: // JMP abs 288 pc = GET_ADDR(); 289 goto loop; 290 291 case 0x7C: // JMP (ind+X) 292 data += x; 293 case 0x6C:{// JMP (ind) 294 data += 0x100 * GET_MSB(); 295 pc = GET_LE16( &READ_PROG( data ) ); 296 goto loop; 297 } 298 299 // Subroutine 300 301 case 0x44: // BSR 302 WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); 303 sp = (sp - 2) | 0x100; 304 WRITE_LOW( sp, pc ); 305 goto branch_taken; 306 307 case 0x20: { // JSR 308 uint_fast16_t temp = pc + 1; 309 pc = GET_ADDR(); 310 WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); 311 sp = (sp - 2) | 0x100; 312 WRITE_LOW( sp, temp ); 313 goto loop; 314 } 315 316 case 0x60: // RTS 317 pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); 318 pc += 1 + READ_LOW( sp ); 319 sp = (sp - 0xFE) | 0x100; 320 goto loop; 321 322 case 0x00: // BRK 323 goto handle_brk; 324 325 // Common 326 327 case 0xBD:{// LDA abs,X 328 PAGE_CROSS_PENALTY( data + x ); 329 uint_fast16_t addr = GET_ADDR() + x; 330 pc += 2; 331 CPU_READ_FAST( this, addr, TIME, nz ); 332 a = nz; 333 goto loop; 334 } 335 336 case 0x9D:{// STA abs,X 337 uint_fast16_t addr = GET_ADDR() + x; 338 pc += 2; 339 CPU_WRITE_FAST( this, addr, a, TIME ); 340 goto loop; 341 } 342 343 case 0x95: // STA zp,x 344 data = uint8_t (data + x); 345 case 0x85: // STA zp 346 pc++; 347 WRITE_LOW( data, a ); 348 goto loop; 349 350 case 0xAE:{// LDX abs 351 uint_fast16_t addr = GET_ADDR(); 352 pc += 2; 353 CPU_READ_FAST( this, addr, TIME, nz ); 354 x = nz; 355 goto loop; 356 } 357 358 case 0xA5: // LDA zp 359 a = nz = READ_LOW( data ); 360 pc++; 361 goto loop; 362 363 // Load/store 364 365 { 366 uint_fast16_t addr; 367 case 0x91: // STA (ind),Y 368 addr = 0x100 * READ_LOW( uint8_t (data + 1) ); 369 addr += READ_LOW( data ) + y; 370 pc++; 371 goto sta_ptr; 372 373 case 0x81: // STA (ind,X) 374 data = uint8_t (data + x); 375 case 0x92: // STA (ind) 376 addr = 0x100 * READ_LOW( uint8_t (data + 1) ); 377 addr += READ_LOW( data ); 378 pc++; 379 goto sta_ptr; 380 381 case 0x99: // STA abs,Y 382 data += y; 383 case 0x8D: // STA abs 384 addr = data + 0x100 * GET_MSB(); 385 pc += 2; 386 sta_ptr: 387 CPU_WRITE_FAST( this, addr, a, TIME ); 388 goto loop; 389 } 390 391 { 392 uint_fast16_t addr; 393 case 0xA1: // LDA (ind,X) 394 data = uint8_t (data + x); 395 case 0xB2: // LDA (ind) 396 addr = 0x100 * READ_LOW( uint8_t (data + 1) ); 397 addr += READ_LOW( data ); 398 pc++; 399 goto a_nz_read_addr; 400 401 case 0xB1:// LDA (ind),Y 402 addr = READ_LOW( data ) + y; 403 PAGE_CROSS_PENALTY( addr ); 404 addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); 405 pc++; 406 goto a_nz_read_addr; 407 408 case 0xB9: // LDA abs,Y 409 data += y; 410 PAGE_CROSS_PENALTY( data ); 411 case 0xAD: // LDA abs 412 addr = data + 0x100 * GET_MSB(); 413 pc += 2; 414 a_nz_read_addr: 415 CPU_READ_FAST( this, addr, TIME, nz ); 416 a = nz; 417 goto loop; 418 } 419 420 case 0xBE:{// LDX abs,y 421 PAGE_CROSS_PENALTY( data + y ); 422 uint_fast16_t addr = GET_ADDR() + y; 423 pc += 2; 424 FLUSH_TIME(); 425 x = nz = READ( addr ); 426 CACHE_TIME(); 427 goto loop; 428 } 429 430 case 0xB5: // LDA zp,x 431 a = nz = READ_LOW( uint8_t (data + x) ); 432 pc++; 433 goto loop; 434 435 case 0xA9: // LDA #imm 436 pc++; 437 a = data; 438 nz = data; 439 goto loop; 440 441 // Bit operations 442 443 case 0x3C: // BIT abs,x 444 data += x; 445 case 0x2C:{// BIT abs 446 uint_fast16_t addr; 447 ADD_PAGE( addr ); 448 FLUSH_TIME(); 449 nz = READ( addr ); 450 CACHE_TIME(); 451 goto bit_common; 452 } 453 case 0x34: // BIT zp,x 454 data = uint8_t (data + x); 455 case 0x24: // BIT zp 456 data = READ_LOW( data ); 457 case 0x89: // BIT imm 458 nz = data; 459 bit_common: 460 pc++; 461 status &= ~st_v; 462 status |= nz & st_v; 463 if ( nz & a ) 464 goto loop; // Z should be clear, and nz must be non-zero if nz & a is 465 nz <<= 8; // set Z flag without affecting N flag 466 goto loop; 467 468 { 469 uint_fast16_t addr; 470 471 case 0xB3: // TST abs,x 472 addr = GET_MSB() + x; 473 goto tst_abs; 474 475 case 0x93: // TST abs 476 addr = GET_MSB(); 477 tst_abs: 478 addr += 0x100 * instr [2]; 479 pc++; 480 FLUSH_TIME(); 481 nz = READ( addr ); 482 CACHE_TIME(); 483 goto tst_common; 484 } 485 486 case 0xA3: // TST zp,x 487 nz = READ_LOW( uint8_t (GET_MSB() + x) ); 488 goto tst_common; 489 490 case 0x83: // TST zp 491 nz = READ_LOW( GET_MSB() ); 492 tst_common: 493 pc += 2; 494 status &= ~st_v; 495 status |= nz & st_v; 496 if ( nz & data ) 497 goto loop; // Z should be clear, and nz must be non-zero if nz & data is 498 nz <<= 8; // set Z flag without affecting N flag 499 goto loop; 500 501 { 502 uint_fast16_t addr; 503 case 0x0C: // TSB abs 504 case 0x1C: // TRB abs 505 addr = GET_ADDR(); 506 pc++; 507 goto txb_addr; 508 509 // TODO: everyone lists different behaviors for the status flags, ugh 510 case 0x04: // TSB zp 511 case 0x14: // TRB zp 512 addr = data + ram_addr; 513 txb_addr: 514 FLUSH_TIME(); 515 nz = a | READ( addr ); 516 if ( opcode & 0x10 ) 517 nz ^= a; // bits from a will already be set, so this clears them 518 status &= ~st_v; 519 status |= nz & st_v; 520 pc++; 521 WRITE( addr, nz ); 522 CACHE_TIME(); 523 goto loop; 524 } 525 526 case 0x07: // RMBn 527 case 0x17: 528 case 0x27: 529 case 0x37: 530 case 0x47: 531 case 0x57: 532 case 0x67: 533 case 0x77: 534 pc++; 535 READ_LOW( data ) &= ~(1 << (opcode >> 4)); 536 goto loop; 537 538 case 0x87: // SMBn 539 case 0x97: 540 case 0xA7: 541 case 0xB7: 542 case 0xC7: 543 case 0xD7: 544 case 0xE7: 545 case 0xF7: 546 pc++; 547 READ_LOW( data ) |= 1 << ((opcode >> 4) - 8); 548 goto loop; 549 550 // Load/store 551 552 case 0x9E: // STZ abs,x 553 data += x; 554 case 0x9C: // STZ abs 555 ADD_PAGE( data ); 556 pc++; 557 FLUSH_TIME(); 558 WRITE( data, 0 ); 559 CACHE_TIME(); 560 goto loop; 561 562 case 0x74: // STZ zp,x 563 data = uint8_t (data + x); 564 case 0x64: // STZ zp 565 pc++; 566 WRITE_LOW( data, 0 ); 567 goto loop; 568 569 case 0x94: // STY zp,x 570 data = uint8_t (data + x); 571 case 0x84: // STY zp 572 pc++; 573 WRITE_LOW( data, y ); 574 goto loop; 575 576 case 0x96: // STX zp,y 577 data = uint8_t (data + y); 578 case 0x86: // STX zp 579 pc++; 580 WRITE_LOW( data, x ); 581 goto loop; 582 583 case 0xB6: // LDX zp,y 584 data = uint8_t (data + y); 585 case 0xA6: // LDX zp 586 data = READ_LOW( data ); 587 case 0xA2: // LDX #imm 588 pc++; 589 x = data; 590 nz = data; 591 goto loop; 592 593 case 0xB4: // LDY zp,x 594 data = uint8_t (data + x); 595 case 0xA4: // LDY zp 596 data = READ_LOW( data ); 597 case 0xA0: // LDY #imm 598 pc++; 599 y = data; 600 nz = data; 601 goto loop; 602 603 case 0xBC: // LDY abs,X 604 data += x; 605 PAGE_CROSS_PENALTY( data ); 606 case 0xAC:{// LDY abs 607 uint_fast16_t addr = data + 0x100 * GET_MSB(); 608 pc += 2; 609 FLUSH_TIME(); 610 y = nz = READ( addr ); 611 CACHE_TIME(); 612 goto loop; 613 } 614 615 { 616 uint_fast8_t temp; 617 case 0x8C: // STY abs 618 temp = y; 619 goto store_abs; 620 621 case 0x8E: // STX abs 622 temp = x; 623 store_abs: 624 uint_fast16_t addr = GET_ADDR(); 625 pc += 2; 626 FLUSH_TIME(); 627 WRITE( addr, temp ); 628 CACHE_TIME(); 629 goto loop; 630 } 631 632 // Compare 633 634 case 0xEC:{// CPX abs 635 uint_fast16_t addr = GET_ADDR(); 636 pc++; 637 FLUSH_TIME(); 638 data = READ( addr ); 639 CACHE_TIME(); 640 goto cpx_data; 641 } 642 643 case 0xE4: // CPX zp 644 data = READ_LOW( data ); 645 case 0xE0: // CPX #imm 646 cpx_data: 647 nz = x - data; 648 pc++; 649 c = ~nz; 650 nz &= 0xFF; 651 goto loop; 652 653 case 0xCC:{// CPY abs 654 uint_fast16_t addr = GET_ADDR(); 655 pc++; 656 FLUSH_TIME(); 657 data = READ( addr ); 658 CACHE_TIME(); 659 goto cpy_data; 660 } 661 662 case 0xC4: // CPY zp 663 data = READ_LOW( data ); 664 case 0xC0: // CPY #imm 665 cpy_data: 666 nz = y - data; 667 pc++; 668 c = ~nz; 669 nz &= 0xFF; 670 goto loop; 671 672 // Logical 673 674 #define ARITH_ADDR_MODES( op )\ 675 case op - 0x04: /* (ind,x) */\ 676 data = uint8_t (data + x);\ 677 case op + 0x0D: /* (ind) */\ 678 data = 0x100 * READ_LOW( uint8_t (data + 1) ) + READ_LOW( data );\ 679 goto ptr##op;\ 680 case op + 0x0C:{/* (ind),y */\ 681 uint_fast16_t temp = READ_LOW( data ) + y;\ 682 PAGE_CROSS_PENALTY( temp );\ 683 data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ 684 goto ptr##op;\ 685 }\ 686 case op + 0x10: /* zp,X */\ 687 data = uint8_t (data + x);\ 688 case op + 0x00: /* zp */\ 689 data = READ_LOW( data );\ 690 goto imm##op;\ 691 case op + 0x14: /* abs,Y */\ 692 data += y;\ 693 goto ind##op;\ 694 case op + 0x18: /* abs,X */\ 695 data += x;\ 696 ind##op:\ 697 PAGE_CROSS_PENALTY( data );\ 698 case op + 0x08: /* abs */\ 699 ADD_PAGE( data );\ 700 ptr##op:\ 701 FLUSH_TIME();\ 702 data = READ( data );\ 703 CACHE_TIME();\ 704 case op + 0x04: /* imm */\ 705 imm##op: 706 707 ARITH_ADDR_MODES( 0xC5 ) // CMP 708 nz = a - data; 709 pc++; 710 c = ~nz; 711 nz &= 0xFF; 712 goto loop; 713 714 ARITH_ADDR_MODES( 0x25 ) // AND 715 nz = (a &= data); 716 pc++; 717 goto loop; 718 719 ARITH_ADDR_MODES( 0x45 ) // EOR 720 nz = (a ^= data); 721 pc++; 722 goto loop; 723 724 ARITH_ADDR_MODES( 0x05 ) // ORA 725 nz = (a |= data); 726 pc++; 727 goto loop; 728 729 // Add/subtract 730 731 ARITH_ADDR_MODES( 0xE5 ) // SBC 732 data ^= 0xFF; 733 goto adc_imm; 734 735 ARITH_ADDR_MODES( 0x65 ) // ADC 736 adc_imm: { 737 if ( status & st_d ) 738 debug_printf( "Decimal mode not supported\n" ); 739 int_fast16_t carry = c >> 8 & 1; 740 int_fast16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend 741 status &= ~st_v; 742 status |= ov >> 2 & 0x40; 743 c = nz = a + data + carry; 744 pc++; 745 a = (uint8_t) nz; 746 goto loop; 747 } 748 749 // Shift/rotate 750 751 case 0x4A: // LSR A 752 c = 0; 753 case 0x6A: // ROR A 754 nz = c >> 1 & 0x80; 755 c = a << 8; 756 nz |= a >> 1; 757 a = nz; 758 goto loop; 759 760 case 0x0A: // ASL A 761 nz = a << 1; 762 c = nz; 763 a = (uint8_t) nz; 764 goto loop; 765 766 case 0x2A: { // ROL A 767 nz = a << 1; 768 int_fast16_t temp = c >> 8 & 1; 769 c = nz; 770 nz |= temp; 771 a = (uint8_t) nz; 772 goto loop; 773 } 774 775 case 0x5E: // LSR abs,X 776 data += x; 777 case 0x4E: // LSR abs 778 c = 0; 779 case 0x6E: // ROR abs 780 ror_abs: { 781 ADD_PAGE( data ); 782 FLUSH_TIME(); 783 int temp = READ( data ); 784 nz = (c >> 1 & 0x80) | (temp >> 1); 785 c = temp << 8; 786 goto rotate_common; 787 } 788 789 case 0x3E: // ROL abs,X 790 data += x; 791 goto rol_abs; 792 793 case 0x1E: // ASL abs,X 794 data += x; 795 case 0x0E: // ASL abs 796 c = 0; 797 case 0x2E: // ROL abs 798 rol_abs: 799 ADD_PAGE( data ); 800 nz = c >> 8 & 1; 801 FLUSH_TIME(); 802 nz |= (c = READ( data ) << 1); 803 rotate_common: 804 pc++; 805 WRITE( data, (uint8_t) nz ); 806 CACHE_TIME(); 807 goto loop; 808 809 case 0x7E: // ROR abs,X 810 data += x; 811 goto ror_abs; 812 813 case 0x76: // ROR zp,x 814 data = uint8_t (data + x); 815 goto ror_zp; 816 817 case 0x56: // LSR zp,x 818 data = uint8_t (data + x); 819 case 0x46: // LSR zp 820 c = 0; 821 case 0x66: // ROR zp 822 ror_zp: { 823 int temp = READ_LOW( data ); 824 nz = (c >> 1 & 0x80) | (temp >> 1); 825 c = temp << 8; 826 goto write_nz_zp; 827 } 828 829 case 0x36: // ROL zp,x 830 data = uint8_t (data + x); 831 goto rol_zp; 832 833 case 0x16: // ASL zp,x 834 data = uint8_t (data + x); 835 case 0x06: // ASL zp 836 c = 0; 837 case 0x26: // ROL zp 838 rol_zp: 839 nz = c >> 8 & 1; 840 nz |= (c = READ_LOW( data ) << 1); 841 goto write_nz_zp; 842 843 // Increment/decrement 844 845 #define INC_DEC_AXY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; 846 847 case 0x1A: // INA 848 INC_DEC_AXY( a, +1 ) 849 850 case 0xE8: // INX 851 INC_DEC_AXY( x, +1 ) 852 853 case 0xC8: // INY 854 INC_DEC_AXY( y, +1 ) 855 856 case 0x3A: // DEA 857 INC_DEC_AXY( a, -1 ) 858 859 case 0xCA: // DEX 860 INC_DEC_AXY( x, -1 ) 861 862 case 0x88: // DEY 863 INC_DEC_AXY( y, -1 ) 864 865 case 0xF6: // INC zp,x 866 data = uint8_t (data + x); 867 case 0xE6: // INC zp 868 nz = 1; 869 goto add_nz_zp; 870 871 case 0xD6: // DEC zp,x 872 data = uint8_t (data + x); 873 case 0xC6: // DEC zp 874 nz = (unsigned) -1; 875 add_nz_zp: 876 nz += READ_LOW( data ); 877 write_nz_zp: 878 pc++; 879 WRITE_LOW( data, nz ); 880 goto loop; 881 882 case 0xFE: // INC abs,x 883 data = x + GET_ADDR(); 884 goto inc_ptr; 885 886 case 0xEE: // INC abs 887 data = GET_ADDR(); 888 inc_ptr: 889 nz = 1; 890 goto inc_common; 891 892 case 0xDE: // DEC abs,x 893 data = x + GET_ADDR(); 894 goto dec_ptr; 895 896 case 0xCE: // DEC abs 897 data = GET_ADDR(); 898 dec_ptr: 899 nz = (unsigned) -1; 900 inc_common: 901 FLUSH_TIME(); 902 nz += READ( data ); 903 pc += 2; 904 WRITE( data, (uint8_t) nz ); 905 CACHE_TIME(); 906 goto loop; 907 908 // Transfer 909 910 case 0xA8: // TAY 911 y = a; 912 nz = a; 913 goto loop; 914 915 case 0x98: // TYA 916 a = y; 917 nz = y; 918 goto loop; 919 920 case 0xAA: // TAX 921 x = a; 922 nz = a; 923 goto loop; 924 925 case 0x8A: // TXA 926 a = x; 927 nz = x; 928 goto loop; 929 930 case 0x9A: // TXS 931 SET_SP( x ); // verified (no flag change) 932 goto loop; 933 934 case 0xBA: // TSX 935 x = nz = GET_SP(); 936 goto loop; 937 938 #define SWAP_REGS( r1, r2 ) {\ 939 uint_fast8_t t = r1;\ 940 r1 = r2;\ 941 r2 = t;\ 942 goto loop;\ 943 } 944 945 case 0x02: // SXY 946 SWAP_REGS( x, y ); 947 948 case 0x22: // SAX 949 SWAP_REGS( a, x ); 950 951 case 0x42: // SAY 952 SWAP_REGS( a, y ); 953 954 case 0x62: // CLA 955 a = 0; 956 goto loop; 957 958 case 0x82: // CLX 959 x = 0; 960 goto loop; 961 962 case 0xC2: // CLY 963 y = 0; 964 goto loop; 965 966 // Stack 967 968 case 0x48: // PHA 969 PUSH( a ); 970 goto loop; 971 972 case 0xDA: // PHX 973 PUSH( x ); 974 goto loop; 975 976 case 0x5A: // PHY 977 PUSH( y ); 978 goto loop; 979 980 case 0x40:{// RTI 981 uint_fast8_t temp = READ_LOW( sp ); 982 pc = READ_LOW( 0x100 | (sp - 0xFF) ); 983 pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; 984 sp = (sp - 0xFD) | 0x100; 985 data = status; 986 SET_STATUS( temp ); 987 this->r.status = status; // update externally-visible I flag 988 if ( (data ^ status) & st_i ) 989 { 990 hes_time_t new_time = end_time_; 991 if ( !(status & st_i) && new_time > irq_time_ ) 992 new_time = irq_time_; 993 blargg_long delta = s.base - new_time; 994 s.base = new_time; 995 s_time += delta; 996 } 997 goto loop; 998 } 999 1000 #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100 1001 1002 case 0x68: // PLA 1003 a = nz = POP(); 1004 goto loop; 1005 1006 case 0xFA: // PLX 1007 x = nz = POP(); 1008 goto loop; 1009 1010 case 0x7A: // PLY 1011 y = nz = POP(); 1012 goto loop; 1013 1014 case 0x28:{// PLP 1015 uint_fast8_t temp = POP(); 1016 uint_fast8_t changed = status ^ temp; 1017 SET_STATUS( temp ); 1018 if ( !(changed & st_i) ) 1019 goto loop; // I flag didn't change 1020 if ( status & st_i ) 1021 goto handle_sei; 1022 goto handle_cli; 1023 } 1024 #undef POP 1025 1026 case 0x08: { // PHP 1027 uint_fast8_t temp; 1028 CALC_STATUS( temp ); 1029 PUSH( temp | st_b ); 1030 goto loop; 1031 } 1032 1033 // Flags 1034 1035 case 0x38: // SEC 1036 c = (unsigned) ~0; 1037 goto loop; 1038 1039 case 0x18: // CLC 1040 c = 0; 1041 goto loop; 1042 1043 case 0xB8: // CLV 1044 status &= ~st_v; 1045 goto loop; 1046 1047 case 0xD8: // CLD 1048 status &= ~st_d; 1049 goto loop; 1050 1051 case 0xF8: // SED 1052 status |= st_d; 1053 goto loop; 1054 1055 case 0x58: // CLI 1056 if ( !(status & st_i) ) 1057 goto loop; 1058 status &= ~st_i; 1059 handle_cli: { 1060 this->r.status = status; // update externally-visible I flag 1061 blargg_long delta = s.base - irq_time_; 1062 if ( delta <= 0 ) 1063 { 1064 if ( TIME < irq_time_ ) 1065 goto loop; 1066 goto delayed_cli; 1067 } 1068 s.base = irq_time_; 1069 s_time += delta; 1070 if ( s_time < 0 ) 1071 goto loop; 1072 1073 if ( delta >= s_time + 1 ) 1074 { 1075 // delayed irq until after next instruction 1076 s.base += s_time + 1; 1077 s_time = -1; 1078 irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop 1079 goto loop; 1080 } 1081 delayed_cli: 1082 debug_printf( "Delayed CLI not supported\n" ); // TODO: implement 1083 goto loop; 1084 } 1085 1086 case 0x78: // SEI 1087 if ( status & st_i ) 1088 goto loop; 1089 status |= st_i; 1090 handle_sei: { 1091 this->r.status = status; // update externally-visible I flag 1092 blargg_long delta = s.base - end_time_; 1093 s.base = end_time_; 1094 s_time += delta; 1095 if ( s_time < 0 ) 1096 goto loop; 1097 debug_printf( "Delayed SEI not supported\n" ); // TODO: implement 1098 goto loop; 1099 } 1100 1101 // Special 1102 1103 case 0x53:{// TAM 1104 uint_fast8_t const bits = data; // avoid using data across function call 1105 pc++; 1106 for ( int i = 0; i < 8; i++ ) 1107 if ( bits & (1 << i) ) 1108 set_mmr( i, a ); 1109 goto loop; 1110 } 1111 1112 case 0x43:{// TMA 1113 pc++; 1114 byte const* in = mmr; 1115 do 1116 { 1117 if ( data & 1 ) 1118 a = *in; 1119 in++; 1120 } 1121 while ( (data >>= 1) != 0 ); 1122 goto loop; 1123 } 1124 1125 case 0x03: // ST0 1126 case 0x13: // ST1 1127 case 0x23:{// ST2 1128 uint_fast16_t addr = opcode >> 4; 1129 if ( addr ) 1130 addr++; 1131 pc++; 1132 FLUSH_TIME(); 1133 CPU_WRITE_VDP( this, addr, data, TIME ); 1134 CACHE_TIME(); 1135 goto loop; 1136 } 1137 1138 case 0xEA: // NOP 1139 goto loop; 1140 1141 case 0x54: // CSL 1142 debug_printf( "CSL not supported\n" ); 1143 illegal_encountered = true; 1144 goto loop; 1145 1146 case 0xD4: // CSH 1147 goto loop; 1148 1149 case 0xF4: { // SET 1150 //fuint16 operand = GET_MSB(); 1151 debug_printf( "SET not handled\n" ); 1152 //switch ( data ) 1153 //{ 1154 //} 1155 illegal_encountered = true; 1156 goto loop; 1157 } 1158 1159 // Block transfer 1160 1161 { 1162 uint_fast16_t in_alt; 1163 int_fast16_t in_inc; 1164 uint_fast16_t out_alt; 1165 int_fast16_t out_inc; 1166 1167 case 0xE3: // TIA 1168 in_alt = 0; 1169 goto bxfer_alt; 1170 1171 case 0xF3: // TAI 1172 in_alt = 1; 1173 bxfer_alt: 1174 in_inc = in_alt ^ 1; 1175 out_alt = in_inc; 1176 out_inc = in_alt; 1177 goto bxfer; 1178 1179 case 0xD3: // TIN 1180 in_inc = 1; 1181 out_inc = 0; 1182 goto bxfer_no_alt; 1183 1184 case 0xC3: // TDD 1185 in_inc = -1; 1186 out_inc = -1; 1187 goto bxfer_no_alt; 1188 1189 case 0x73: // TII 1190 in_inc = 1; 1191 out_inc = 1; 1192 bxfer_no_alt: 1193 in_alt = 0; 1194 out_alt = 0; 1195 bxfer: 1196 uint_fast16_t in = GET_LE16( instr + 0 ); 1197 uint_fast16_t out = GET_LE16( instr + 2 ); 1198 int count = GET_LE16( instr + 4 ); 1199 if ( !count ) 1200 count = 0x10000; 1201 pc += 6; 1202 WRITE_LOW( 0x100 | (sp - 1), y ); 1203 WRITE_LOW( 0x100 | (sp - 2), a ); 1204 WRITE_LOW( 0x100 | (sp - 3), x ); 1205 FLUSH_TIME(); 1206 do 1207 { 1208 // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O 1209 uint_fast8_t t = READ( in ); 1210 in += in_inc; 1211 in &= 0xFFFF; 1212 s.time += 6; 1213 if ( in_alt ) 1214 in_inc = -in_inc; 1215 WRITE( out, t ); 1216 out += out_inc; 1217 out &= 0xFFFF; 1218 if ( out_alt ) 1219 out_inc = -out_inc; 1220 } 1221 while ( --count ); 1222 CACHE_TIME(); 1223 goto loop; 1224 } 1225 1226 // Illegal 1227 1228 default: 1229 debug_printf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); 1230 illegal_encountered = true; 1231 goto loop; 1232 } 1233 assert( false ); 1234 1235 int result_; 1236 handle_brk: 1237 pc++; 1238 result_ = 6; 1239 1240 interrupt: 1241 { 1242 s_time += 7; 1243 1244 WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); 1245 WRITE_LOW( 0x100 | (sp - 2), pc ); 1246 pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ ); 1247 1248 sp = (sp - 3) | 0x100; 1249 uint_fast8_t temp; 1250 CALC_STATUS( temp ); 1251 if ( result_ == 6 ) 1252 temp |= st_b; 1253 WRITE_LOW( sp, temp ); 1254 1255 status &= ~st_d; 1256 status |= st_i; 1257 this->r.status = status; // update externally-visible I flag 1258 1259 blargg_long delta = s.base - end_time_; 1260 s.base = end_time_; 1261 s_time += delta; 1262 goto loop; 1263 } 1264 1265 idle_done: 1266 s_time = 0; 1267 out_of_time: 1268 pc--; 1269 FLUSH_TIME(); 1270 CPU_DONE( this, TIME, result_ ); 1271 CACHE_TIME(); 1272 if ( result_ > 0 ) 1273 goto interrupt; 1274 if ( s_time < 0 ) 1275 goto loop; 1276 1277 s.time = s_time; 1278 1279 r.pc = pc; 1280 r.sp = GET_SP(); 1281 r.a = a; 1282 r.x = x; 1283 r.y = y; 1284 1285 { 1286 uint_fast8_t temp; 1287 CALC_STATUS( temp ); 1288 r.status = temp; 1289 } 1290 1291 this->state_ = s; 1292 this->state = &this->state_; 1293 1294 return illegal_encountered; 1295 } 1296