1 // Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
5 #include "blargg_endian.h"
7 //#include "hes_cpu_log.h"
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 */
20 // TODO: support T flag, including clearing it at appropriate times?
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;
26 #define FLUSH_TIME() (void) (s.time = s_time)
27 #define CACHE_TIME() (void) (s_time = s.time)
29 #include "hes_cpu_io.h"
31 #include "blargg_source.h"
33 #ifdef BLARGG_NONPORTABLE
34 #define PAGE_OFFSET( addr ) (addr)
36 #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
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;
49 void Cpu_init( struct Hes_Cpu
* this )
51 this->state
= &this->state_
;
54 void Cpu_reset( struct Hes_Cpu
* this )
56 check( this->state
== &state_
);
57 this->state
= &this->state_
;
59 this->state_
.time
= 0;
60 this->state_
.base
= 0;
61 this->irq_time
= future_hes_time
;
62 this->end_time
= future_hes_time
;
64 this->r
.status
= st_i
;
71 blargg_verify_byte_order();
74 void Cpu_set_mmr( struct Hes_Emu
* this, int reg
, int bank
)
76 assert( (unsigned) reg
<= page_count
); // allow page past end to be set
77 assert( (unsigned) bank
< 0x100 );
78 this->cpu
.mmr
[reg
] = bank
;
79 uint8_t const* code
= CPU_SET_MMR( this, reg
, bank
);
80 this->cpu
.state
->code_map
[reg
] = code
- PAGE_OFFSET( reg
<< page_shift
);
83 #define TIME (s_time + s.base)
85 #define READ( addr ) CPU_READ( this, (addr), TIME )
86 #define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );}
87 #define READ_LOW( addr ) (cpu->ram [(int) (addr)])
88 #define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
89 #define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )])
91 #define SET_SP( v ) (sp = ((v) + 1) | 0x100)
92 #define GET_SP() ((sp - 1) & 0xFF)
93 #define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
95 // even on x86, using short and unsigned char was slower
97 typedef unsigned fuint16
;
98 typedef unsigned fuint8
;
99 typedef blargg_long fint32
;
101 bool Cpu_run( struct Hes_Emu
* this, hes_time_t end_time
)
103 bool illegal_encountered
= false;
106 struct Hes_Cpu
* cpu
= &this->cpu
;
107 cpu
->state
->time
+= Cpu_update_end_time( cpu
, cpu
->r
.status
, (cpu
->end_time
= end_time
), cpu
->irq_time
);
109 struct state_t s
= cpu
->state_
;
112 // even on x86, using s.time in place of s_time was slower
113 fint16 s_time
= s
.time
;
115 struct registers_t
* r
= &cpu
->r
;
125 #define IS_NEG (nz & 0x8080)
127 #define CALC_STATUS( out ) do {\
128 out = status & (st_v | st_d | st_i);\
129 out |= ((nz >> 8) | nz) & st_n;\
130 out |= c >> 8 & st_c;\
131 if ( !(nz & 0xFF) ) out |= st_z;\
134 #define SET_STATUS( in ) do {\
135 status = in & (st_v | st_d | st_i);\
142 fuint16 c
; // carry set if (c & 0x100) != 0
143 fuint16 nz
; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
145 fuint8 temp
= r
->status
;
156 hes_time_t correct
= end_time_
;
157 if ( !(status
& st_i
) && correct
> irq_time_
)
159 check( s
.base
== correct
);
162 if ( count == 1844 ) Debugger();
163 if ( s.base != correct ) dprintf( "%ld\n", count );
169 check( (unsigned) GET_SP() < 0x100 );
170 check( (unsigned) a
< 0x100 );
171 check( (unsigned) x
< 0x100 );
173 uint8_t const* instr
= s
.code_map
[pc
>> page_shift
];
176 // TODO: eliminate this special case
177 #ifdef BLARGG_NONPORTABLE
182 instr
+= PAGE_OFFSET( pc
);
187 // TODO: each reference lists slightly different timing values, ugh
188 static uint8_t const clock_table
[256] ICONST_ATTR
=
189 {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
190 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6,// 0
191 4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1
192 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2
193 4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3
194 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4
195 4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5
196 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6
197 4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7
198 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8
199 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9
200 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A
201 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B
202 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C
203 4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D
204 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E
205 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F
209 data
= clock_table
[opcode
];
210 if ( (s_time
+= data
) >= 0 )
211 goto possibly_out_of_time
;
217 log_cpu( "new", pc
- 1, opcode
, instr
[0], instr
[1], instr
[2],
218 instr
[3], instr
[4], instr
[5] );
219 //log_opcode( opcode );
224 possibly_out_of_time
:
225 if ( s_time
< (int) data
)
226 goto almost_out_of_time
;
232 #define GET_MSB() (instr [1])
233 #define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB());
234 #define GET_ADDR() GET_LE16( instr )
236 // TODO: is the penalty really always added? the original 6502 was much better
237 //#define PAGE_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8)
238 #define PAGE_CROSS_PENALTY( lsb )
242 // TODO: more efficient way to handle negative branch that wraps PC around
243 #define BRANCH( cond )\
245 fint16 offset = (int8_t) data;\
247 if ( !(cond) ) goto branch_not_taken;\
248 pc = (uint16_t) (pc + offset);\
253 BRANCH( !((uint8_t) nz
) );
256 BRANCH( (uint8_t) nz
);
262 BRANCH( !(c
& 0x100) )
268 BRANCH( !(status
& st_v
) )
271 BRANCH( status
& st_v
)
281 if ( pc
== idle_addr
+ 1 )
298 fuint16 t
= 0x101 * READ_LOW( data
);
302 BRANCH( t
& (1 << (opcode
>> 4)) )
305 case 0x4C: // JMP abs
309 case 0x7C: // JMP (ind+X)
311 case 0x6C:{// JMP (ind)
312 data
+= 0x100 * GET_MSB();
313 pc
= GET_LE16( &READ_PROG( data
) );
320 WRITE_LOW( 0x100 | (sp
- 1), pc
>> 8 );
321 sp
= (sp
- 2) | 0x100;
326 fuint16 temp
= pc
+ 1;
328 WRITE_LOW( 0x100 | (sp
- 1), temp
>> 8 );
329 sp
= (sp
- 2) | 0x100;
330 WRITE_LOW( sp
, temp
);
335 pc
= 0x100 * READ_LOW( 0x100 | (sp
- 0xFF) );
336 pc
+= 1 + READ_LOW( sp
);
337 sp
= (sp
- 0xFE) | 0x100;
345 case 0xBD:{// LDA abs,X
346 PAGE_CROSS_PENALTY( data
+ x
);
347 fuint16 addr
= GET_ADDR() + x
;
349 CPU_READ_FAST( this, addr
, TIME
, nz
);
354 case 0x9D:{// STA abs,X
355 fuint16 addr
= GET_ADDR() + x
;
357 CPU_WRITE_FAST( this, addr
, a
, TIME
);
361 case 0x95: // STA zp,x
362 data
= (uint8_t) (data
+ x
);
365 WRITE_LOW( data
, a
);
368 case 0xAE:{// LDX abs
369 fuint16 addr
= GET_ADDR();
371 CPU_READ_FAST( this, addr
, TIME
, nz
);
377 a
= nz
= READ_LOW( data
);
385 case 0x91: // STA (ind),Y
386 addr
= 0x100 * READ_LOW( (uint8_t) (data
+ 1) );
387 addr
+= READ_LOW( data
) + y
;
391 case 0x81: // STA (ind,X)
392 data
= (uint8_t) (data
+ x
);
393 case 0x92: // STA (ind)
394 addr
= 0x100 * READ_LOW( (uint8_t) (data
+ 1) );
395 addr
+= READ_LOW( data
);
399 case 0x99: // STA abs,Y
401 case 0x8D: // STA abs
402 addr
= data
+ 0x100 * GET_MSB();
405 CPU_WRITE_FAST( this, addr
, a
, TIME
);
411 case 0xA1: // LDA (ind,X)
412 data
= (uint8_t) (data
+ x
);
413 case 0xB2: // LDA (ind)
414 addr
= 0x100 * READ_LOW( (uint8_t) (data
+ 1) );
415 addr
+= READ_LOW( data
);
419 case 0xB1:// LDA (ind),Y
420 addr
= READ_LOW( data
) + y
;
421 PAGE_CROSS_PENALTY( addr
);
422 addr
+= 0x100 * READ_LOW( (uint8_t) (data
+ 1) );
426 case 0xB9: // LDA abs,Y
428 PAGE_CROSS_PENALTY( data
);
429 case 0xAD: // LDA abs
430 addr
= data
+ 0x100 * GET_MSB();
433 CPU_READ_FAST( this, addr
, TIME
, nz
);
438 case 0xBE:{// LDX abs,y
439 PAGE_CROSS_PENALTY( data
+ y
);
440 fuint16 addr
= GET_ADDR() + y
;
443 x
= nz
= READ( addr
);
448 case 0xB5: // LDA zp,x
449 a
= nz
= READ_LOW( (uint8_t) (data
+ x
) );
453 case 0xA9: // LDA #imm
461 case 0x3C: // BIT abs,x
463 case 0x2C:{// BIT abs
471 case 0x34: // BIT zp,x
472 data
= (uint8_t) (data
+ x
);
474 data
= READ_LOW( data
);
475 case 0x89: // BIT imm
482 goto loop
; // Z should be clear, and nz must be non-zero if nz & a is
483 nz
<<= 8; // set Z flag without affecting N flag
489 case 0xB3: // TST abs,x
490 addr
= GET_MSB() + x
;
493 case 0x93: // TST abs
496 addr
+= 0x100 * instr
[2];
504 case 0xA3: // TST zp,x
505 nz
= READ_LOW( (uint8_t) (GET_MSB() + x
) );
509 nz
= READ_LOW( GET_MSB() );
515 goto loop
; // Z should be clear, and nz must be non-zero if nz & data is
516 nz
<<= 8; // set Z flag without affecting N flag
521 case 0x0C: // TSB abs
522 case 0x1C: // TRB abs
527 // TODO: everyone lists different behaviors for the status flags, ugh
530 addr
= data
+ ram_addr
;
533 nz
= a
| READ( addr
);
535 nz
^= a
; // bits from a will already be set, so this clears them
553 READ_LOW( data
) &= ~(1 << (opcode
>> 4));
565 READ_LOW( data
) |= 1 << ((opcode
>> 4) - 8);
570 case 0x9E: // STZ abs,x
572 case 0x9C: // STZ abs
580 case 0x74: // STZ zp,x
581 data
= (uint8_t) (data
+ x
);
584 WRITE_LOW( data
, 0 );
587 case 0x94: // STY zp,x
588 data
= (uint8_t) (data
+ x
);
591 WRITE_LOW( data
, y
);
594 case 0x96: // STX zp,y
595 data
= (uint8_t) (data
+ y
);
598 WRITE_LOW( data
, x
);
601 case 0xB6: // LDX zp,y
602 data
= (uint8_t) (data
+ y
);
604 data
= READ_LOW( data
);
605 case 0xA2: // LDX #imm
611 case 0xB4: // LDY zp,x
612 data
= (uint8_t) (data
+ x
);
614 data
= READ_LOW( data
);
615 case 0xA0: // LDY #imm
621 case 0xBC: // LDY abs,X
623 PAGE_CROSS_PENALTY( data
);
624 case 0xAC:{// LDY abs
625 fuint16 addr
= data
+ 0x100 * GET_MSB();
628 y
= nz
= READ( addr
);
635 case 0x8C: // STY abs
639 case 0x8E: // STX abs
643 fuint16 addr
= GET_ADDR();
654 case 0xEC:{// CPX abs
655 fuint16 addr
= GET_ADDR();
664 data
= READ_LOW( data
);
665 case 0xE0: // CPX #imm
673 case 0xCC:{// CPY abs
674 fuint16 addr
= GET_ADDR();
683 data
= READ_LOW( data
);
684 case 0xC0: // CPY #imm
694 #define ARITH_ADDR_MODES( op )\
695 case op - 0x04: /* (ind,x) */\
696 data = (uint8_t) (data + x);\
697 case op + 0x0D: /* (ind) */\
698 data = 0x100 * READ_LOW( (uint8_t) (data + 1) ) + READ_LOW( data );\
700 case op + 0x0C:{/* (ind),y */\
701 fuint16 temp = READ_LOW( data ) + y;\
702 PAGE_CROSS_PENALTY( temp );\
703 data = temp + 0x100 * READ_LOW( (uint8_t) (data + 1) );\
706 case op + 0x10: /* zp,X */\
707 data = (uint8_t) (data + x);\
708 case op + 0x00: /* zp */\
709 data = READ_LOW( data );\
711 case op + 0x14: /* abs,Y */\
714 case op + 0x18: /* abs,X */\
717 PAGE_CROSS_PENALTY( data );\
718 case op + 0x08: /* abs */\
722 data = READ( data );\
724 case op + 0x04: /* imm */\
727 ARITH_ADDR_MODES( 0xC5 ) // CMP
734 ARITH_ADDR_MODES( 0x25 ) // AND
739 ARITH_ADDR_MODES( 0x45 ) // EOR
744 ARITH_ADDR_MODES( 0x05 ) // ORA
751 ARITH_ADDR_MODES( 0xE5 ) // SBC
755 ARITH_ADDR_MODES( 0x65 ) // ADC
757 if ( status
& st_d
) {
758 dprintf( "Decimal mode not supported\n" );
760 fint16 carry
= c
>> 8 & 1;
761 fint16 ov
= (a
^ 0x80) + carry
+ (int8_t) data
; // sign-extend
763 status
|= ov
>> 2 & 0x40;
764 c
= nz
= a
+ data
+ carry
;
787 case 0x2A: { // ROL A
789 fint16 temp
= c
>> 8 & 1;
796 case 0x5E: // LSR abs,X
798 case 0x4E: // LSR abs
800 case 0x6E: // ROR abs
804 int temp
= READ( data
);
805 nz
= (c
>> 1 & 0x80) | (temp
>> 1);
810 case 0x3E: // ROL abs,X
814 case 0x1E: // ASL abs,X
816 case 0x0E: // ASL abs
818 case 0x2E: // ROL abs
823 nz
|= (c
= READ( data
) << 1);
826 WRITE( data
, (uint8_t) nz
);
830 case 0x7E: // ROR abs,X
834 case 0x76: // ROR zp,x
835 data
= (uint8_t) (data
+ x
);
838 case 0x56: // LSR zp,x
839 data
= (uint8_t) (data
+ x
);
844 int temp
= READ_LOW( data
);
845 nz
= (c
>> 1 & 0x80) | (temp
>> 1);
850 case 0x36: // ROL zp,x
851 data
= (uint8_t) (data
+ x
);
854 case 0x16: // ASL zp,x
855 data
= (uint8_t) (data
+ x
);
861 nz
|= (c
= READ_LOW( data
) << 1);
864 // Increment/decrement
866 #define INC_DEC_AXY( reg, n ) reg = (uint8_t) (nz = reg + n); goto loop;
886 case 0xF6: // INC zp,x
887 data
= (uint8_t) (data
+ x
);
892 case 0xD6: // DEC zp,x
893 data
= (uint8_t) (data
+ x
);
897 nz
+= READ_LOW( data
);
900 WRITE_LOW( data
, nz
);
903 case 0xFE: // INC abs,x
904 data
= x
+ GET_ADDR();
907 case 0xEE: // INC abs
913 case 0xDE: // DEC abs,x
914 data
= x
+ GET_ADDR();
917 case 0xCE: // DEC abs
925 WRITE( data
, (uint8_t) nz
);
952 SET_SP( x
); // verified (no flag change)
959 #define SWAP_REGS( r1, r2 ) {\
1002 fuint8 temp
= READ_LOW( sp
);
1003 pc
= READ_LOW( 0x100 | (sp
- 0xFF) );
1004 pc
|= READ_LOW( 0x100 | (sp
- 0xFE) ) * 0x100;
1005 sp
= (sp
- 0xFD) | 0x100;
1008 r
->status
= status
; // update externally-visible I flag
1009 if ( (data
^ status
) & st_i
)
1011 hes_time_t new_time
= cpu
->end_time
;
1012 if ( !(status
& st_i
) && new_time
> cpu
->irq_time
)
1013 new_time
= cpu
->irq_time
;
1014 blargg_long delta
= s
.base
- new_time
;
1021 #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100
1036 fuint8 temp
= POP();
1037 fuint8 changed
= status
^ temp
;
1039 if ( !(changed
& st_i
) )
1040 goto loop
; // I flag didn't change
1041 if ( status
& st_i
)
1049 CALC_STATUS( temp
);
1050 PUSH( temp
| st_b
);
1077 if ( !(status
& st_i
) )
1081 r
->status
= status
; // update externally-visible I flag
1082 blargg_long delta
= s
.base
- cpu
->irq_time
;
1085 if ( TIME
< cpu
->irq_time
)
1089 s
.base
= cpu
->irq_time
;
1094 if ( delta
>= s_time
+ 1 )
1096 // delayed irq until after next instruction
1097 s
.base
+= s_time
+ 1;
1099 cpu
->irq_time
= s
.base
; // TODO: remove, as only to satisfy debug check in loop
1103 dprintf( "Delayed CLI not supported\n" ); // TODO: implement
1108 if ( status
& st_i
)
1112 r
->status
= status
; // update externally-visible I flag
1113 blargg_long delta
= s
.base
- cpu
->end_time
;
1114 s
.base
= cpu
->end_time
;
1118 dprintf( "Delayed SEI not supported\n" ); // TODO: implement
1125 fuint8
const bits
= data
; // avoid using data across function call
1128 for ( i
= 0; i
< 8; i
++ )
1129 if ( bits
& (1 << i
) )
1130 /* this->cpu.set_mmr( i, a ); */
1131 Cpu_set_mmr( this, i
, a
);
1137 byte
const* in
= cpu
->mmr
;
1144 while ( (data
>>= 1) != 0 );
1151 fuint16 addr
= opcode
>> 4;
1156 CPU_WRITE_VDP( this, addr
, data
, TIME
);
1165 dprintf( "CSL not supported\n" );
1166 illegal_encountered
= true;
1173 //fuint16 operand = GET_MSB();
1174 dprintf( "SET not handled\n" );
1178 illegal_encountered
= true;
1197 in_inc
= in_alt
^ 1;
1219 fuint16 in
= GET_LE16( instr
+ 0 );
1220 fuint16 out
= GET_LE16( instr
+ 2 );
1221 int count
= GET_LE16( instr
+ 4 );
1225 WRITE_LOW( 0x100 | (sp
- 1), y
);
1226 WRITE_LOW( 0x100 | (sp
- 2), a
);
1227 WRITE_LOW( 0x100 | (sp
- 3), x
);
1231 // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O
1232 fuint8 t
= READ( in
);
1253 assert( (unsigned) opcode
<= 0xFF );
1254 dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode
, (int) pc
- 1 );
1255 illegal_encountered
= true;
1269 WRITE_LOW( 0x100 | (sp
- 1), pc
>> 8 );
1270 WRITE_LOW( 0x100 | (sp
- 2), pc
);
1271 pc
= GET_LE16( &READ_PROG( 0xFFF0 ) + result_
);
1273 sp
= (sp
- 3) | 0x100;
1275 CALC_STATUS( temp
);
1278 WRITE_LOW( sp
, temp
);
1282 r
->status
= status
; // update externally-visible I flag
1284 blargg_long delta
= s
.base
- cpu
->end_time
;
1285 s
.base
= cpu
->end_time
;
1295 CPU_DONE( this, TIME
, result_
);
1312 CALC_STATUS( temp
);
1317 cpu
->state
= &cpu
->state_
;
1319 return illegal_encountered
;