/src/vlc/contrib/contrib-build/game-music-emu/gme/Sap_Cpu.cpp
Line | Count | Source |
1 | | // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ |
2 | | |
3 | | #include "Sap_Cpu.h" |
4 | | |
5 | | #include <limits.h> |
6 | | #include "blargg_endian.h" |
7 | | |
8 | | //#include "nes_cpu_log.h" |
9 | | |
10 | | /* Copyright (C) 2003-2006 Shay Green. This module is free software; you |
11 | | can redistribute it and/or modify it under the terms of the GNU Lesser |
12 | | General Public License as published by the Free Software Foundation; either |
13 | | version 2.1 of the License, or (at your option) any later version. This |
14 | | module is distributed in the hope that it will be useful, but WITHOUT ANY |
15 | | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
16 | | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
17 | | details. You should have received a copy of the GNU Lesser General Public |
18 | | License along with this module; if not, write to the Free Software Foundation, |
19 | | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ |
20 | | |
21 | 0 | #define FLUSH_TIME() (void) (s.time = s_time) |
22 | 0 | #define CACHE_TIME() (void) (s_time = s.time) |
23 | | |
24 | | #include "sap_cpu_io.h" |
25 | | |
26 | | #ifndef CPU_DONE |
27 | 0 | #define CPU_DONE( cpu, time, result_out ) { result_out = -1; } |
28 | | #endif |
29 | | |
30 | | #include "blargg_source.h" |
31 | | |
32 | | enum { |
33 | | st_n = 0x80, |
34 | | st_v = 0x40, |
35 | | st_r = 0x20, |
36 | | st_b = 0x10, |
37 | | st_d = 0x08, |
38 | | st_i = 0x04, |
39 | | st_z = 0x02, |
40 | | st_c = 0x01 |
41 | | }; |
42 | | |
43 | | void Sap_Cpu::reset( void* new_mem ) |
44 | 0 | { |
45 | 0 | check( state == &state_ ); |
46 | 0 | state = &state_; |
47 | 0 | mem = (uint8_t*) new_mem; |
48 | 0 | r.status = st_i; |
49 | 0 | r.sp = 0xFF; |
50 | 0 | r.pc = 0; |
51 | 0 | r.a = 0; |
52 | 0 | r.x = 0; |
53 | 0 | r.y = 0; |
54 | 0 | state_.time = 0; |
55 | 0 | state_.base = 0; |
56 | 0 | irq_time_ = future_sap_time; |
57 | 0 | end_time_ = future_sap_time; |
58 | |
|
59 | 0 | blargg_verify_byte_order(); |
60 | 0 | } |
61 | | |
62 | 0 | #define TIME (s_time + s.base) |
63 | 0 | #define READ( addr ) CPU_READ( this, (addr), TIME ) |
64 | 0 | #define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} |
65 | 0 | #define READ_LOW( addr ) (mem [int (addr)]) |
66 | 0 | #define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) |
67 | 0 | #define READ_PROG( addr ) (READ_LOW( addr )) |
68 | | |
69 | 0 | #define SET_SP( v ) (sp = ((v) + 1) | 0x100) |
70 | 0 | #define GET_SP() ((sp - 1) & 0xFF) |
71 | 0 | #define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) |
72 | | |
73 | | bool Sap_Cpu::run( sap_time_t end_time ) |
74 | 0 | { |
75 | 0 | bool illegal_encountered = false; |
76 | 0 | set_end_time( end_time ); |
77 | 0 | state_t s = this->state_; |
78 | 0 | this->state = &s; |
79 | 0 | int32_t s_time = s.time; |
80 | 0 | uint8_t* const mem = this->mem; // cache |
81 | | |
82 | | // registers |
83 | 0 | uint16_t pc = r.pc; |
84 | 0 | uint8_t a = r.a; |
85 | 0 | uint8_t x = r.x; |
86 | 0 | uint8_t y = r.y; |
87 | 0 | uint16_t sp; |
88 | 0 | SET_SP( r.sp ); |
89 | | |
90 | | // status flags |
91 | 0 | #define IS_NEG (nz & 0x8080) |
92 | |
|
93 | 0 | #define CALC_STATUS( out ) do {\ |
94 | 0 | out = status & (st_v | st_d | st_i);\ |
95 | 0 | out |= ((nz >> 8) | nz) & st_n;\ |
96 | 0 | out |= c >> 8 & st_c;\ |
97 | 0 | if ( !(nz & 0xFF) ) out |= st_z;\ |
98 | 0 | } while ( 0 ) |
99 | |
|
100 | 0 | #define SET_STATUS( in ) do {\ |
101 | 0 | status = in & (st_v | st_d | st_i);\ |
102 | 0 | nz = in << 8;\ |
103 | 0 | c = nz;\ |
104 | 0 | nz |= ~in & st_z;\ |
105 | 0 | } while ( 0 ) |
106 | |
|
107 | 0 | uint8_t status; |
108 | 0 | uint16_t c; // carry set if (c & 0x100) != 0 |
109 | 0 | uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 |
110 | 0 | { |
111 | 0 | uint8_t temp = r.status; |
112 | 0 | SET_STATUS( temp ); |
113 | 0 | } |
114 | |
|
115 | 0 | goto loop; |
116 | 0 | dec_clock_loop: |
117 | 0 | s_time--; |
118 | 0 | loop: |
119 | |
|
120 | | #ifndef NDEBUG |
121 | | { |
122 | | sap_time_t correct = end_time_; |
123 | | if ( !(status & st_i) && correct > irq_time_ ) |
124 | | correct = irq_time_; |
125 | | check( s.base == correct ); |
126 | | } |
127 | | #endif |
128 | |
|
129 | 0 | check( (unsigned) GET_SP() < 0x100 ); |
130 | 0 | check( (unsigned) a < 0x100 ); |
131 | 0 | check( (unsigned) x < 0x100 ); |
132 | 0 | check( (unsigned) y < 0x100 ); |
133 | |
|
134 | 0 | uint8_t opcode = mem [pc]; |
135 | 0 | pc++; |
136 | 0 | uint8_t const* instr = mem + pc; |
137 | |
|
138 | 0 | static uint8_t const clock_table [256] = |
139 | 0 | {// 0 1 2 3 4 5 6 7 8 9 A B C D E F |
140 | 0 | 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 |
141 | 0 | 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 |
142 | 0 | 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 |
143 | 0 | 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 |
144 | 0 | 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 |
145 | 0 | 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 |
146 | 0 | 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 |
147 | 0 | 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 |
148 | 0 | 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 |
149 | 0 | 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 |
150 | 0 | 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A |
151 | 0 | 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B |
152 | 0 | 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C |
153 | 0 | 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D |
154 | 0 | 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E |
155 | 0 | 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F |
156 | 0 | }; // 0x00 was 7 |
157 | |
|
158 | 0 | uint16_t data; |
159 | 0 | data = clock_table [opcode]; |
160 | 0 | if ( (s_time += data) >= 0 ) |
161 | 0 | goto possibly_out_of_time; |
162 | 0 | almost_out_of_time: |
163 | |
|
164 | 0 | data = *instr; |
165 | |
|
166 | | #ifdef NES_CPU_LOG_H |
167 | | nes_cpu_log( "cpu_log", pc - 1, opcode, instr [0], instr [1] ); |
168 | | #endif |
169 | |
|
170 | 0 | switch ( opcode ) |
171 | 0 | { |
172 | 0 | possibly_out_of_time: |
173 | 0 | if ( s_time < (int) data ) |
174 | 0 | goto almost_out_of_time; |
175 | 0 | s_time -= data; |
176 | 0 | goto out_of_time; |
177 | | |
178 | | // Macros |
179 | | |
180 | 0 | #define GET_MSB() (instr [1]) |
181 | 0 | #define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) |
182 | 0 | #define GET_ADDR() GET_LE16( instr ) |
183 | | |
184 | 0 | #define NO_PAGE_CROSSING( lsb ) |
185 | 0 | #define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8; |
186 | | |
187 | 0 | #define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; |
188 | | |
189 | 0 | #define IND_Y( cross, out ) {\ |
190 | 0 | uint16_t temp = READ_LOW( data ) + y;\ |
191 | 0 | out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ |
192 | 0 | cross( temp );\ |
193 | 0 | } |
194 | | |
195 | 0 | #define IND_X( out ) {\ |
196 | 0 | uint16_t temp = data + x;\ |
197 | 0 | out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ |
198 | 0 | } |
199 | | |
200 | 0 | #define ARITH_ADDR_MODES( op )\ |
201 | 0 | case op - 0x04: /* (ind,x) */\ |
202 | 0 | IND_X( data )\ |
203 | 0 | goto ptr##op;\ |
204 | 0 | case op + 0x0C: /* (ind),y */\ |
205 | 0 | IND_Y( HANDLE_PAGE_CROSSING, data )\ |
206 | 0 | goto ptr##op;\ |
207 | 0 | case op + 0x10: /* zp,X */\ |
208 | 0 | data = uint8_t (data + x);/*FALLTHRU*/\ |
209 | 0 | case op + 0x00: /* zp */\ |
210 | 0 | data = READ_LOW( data );\ |
211 | 0 | goto imm##op;\ |
212 | 0 | case op + 0x14: /* abs,Y */\ |
213 | 0 | data += y;\ |
214 | 0 | goto ind##op;\ |
215 | 0 | case op + 0x18: /* abs,X */\ |
216 | 0 | data += x;\ |
217 | 0 | ind##op:\ |
218 | 0 | HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/\ |
219 | 0 | case op + 0x08: /* abs */\ |
220 | 0 | ADD_PAGE();\ |
221 | 0 | ptr##op:\ |
222 | 0 | FLUSH_TIME();\ |
223 | 0 | data = READ( data );\ |
224 | 0 | CACHE_TIME();/*FALLTHRU*/\ |
225 | 0 | case op + 0x04: /* imm */\ |
226 | 0 | imm##op: |
227 | | |
228 | | // TODO: more efficient way to handle negative branch that wraps PC around |
229 | 0 | #define BRANCH( cond )\ |
230 | 0 | {\ |
231 | 0 | int16_t offset = (int8_t) data;\ |
232 | 0 | uint16_t extra_clock = (++pc & 0xFF) + offset;\ |
233 | 0 | if ( !(cond) ) goto dec_clock_loop;\ |
234 | 0 | pc += offset;\ |
235 | 0 | s_time += extra_clock >> 8 & 1;\ |
236 | 0 | goto loop;\ |
237 | 0 | } |
238 | | |
239 | | // Often-Used |
240 | | |
241 | 0 | case 0xB5: // LDA zp,x |
242 | 0 | a = nz = READ_LOW( uint8_t (data + x) ); |
243 | 0 | pc++; |
244 | 0 | goto loop; |
245 | | |
246 | 0 | case 0xA5: // LDA zp |
247 | 0 | a = nz = READ_LOW( data ); |
248 | 0 | pc++; |
249 | 0 | goto loop; |
250 | | |
251 | 0 | case 0xD0: // BNE |
252 | 0 | BRANCH( (uint8_t) nz ); |
253 | |
|
254 | 0 | case 0x20: { // JSR |
255 | 0 | uint16_t temp = pc + 1; |
256 | 0 | pc = GET_ADDR(); |
257 | 0 | WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); |
258 | 0 | sp = (sp - 2) | 0x100; |
259 | 0 | WRITE_LOW( sp, temp ); |
260 | 0 | goto loop; |
261 | 0 | } |
262 | | |
263 | 0 | case 0x4C: // JMP abs |
264 | 0 | pc = GET_ADDR(); |
265 | 0 | goto loop; |
266 | | |
267 | 0 | case 0xE8: // INX |
268 | 0 | INC_DEC_XY( x, 1 ) |
269 | | |
270 | 0 | case 0x10: // BPL |
271 | 0 | BRANCH( !IS_NEG ) |
272 | | |
273 | 0 | ARITH_ADDR_MODES( 0xC5 ) // CMP |
274 | 0 | nz = a - data; |
275 | 0 | pc++; |
276 | 0 | c = ~nz; |
277 | 0 | nz &= 0xFF; |
278 | 0 | goto loop; |
279 | | |
280 | 0 | case 0x30: // BMI |
281 | 0 | BRANCH( IS_NEG ) |
282 | | |
283 | 0 | case 0xF0: // BEQ |
284 | 0 | BRANCH( !(uint8_t) nz ); |
285 | |
|
286 | 0 | case 0x95: // STA zp,x |
287 | 0 | data = uint8_t (data + x);/*FALLTHRU*/ |
288 | 0 | case 0x85: // STA zp |
289 | 0 | pc++; |
290 | 0 | WRITE_LOW( data, a ); |
291 | 0 | goto loop; |
292 | | |
293 | 0 | case 0xC8: // INY |
294 | 0 | INC_DEC_XY( y, 1 ) |
295 | | |
296 | 0 | case 0xA8: // TAY |
297 | 0 | y = a; |
298 | 0 | nz = a; |
299 | 0 | goto loop; |
300 | | |
301 | 0 | case 0x98: // TYA |
302 | 0 | a = y; |
303 | 0 | nz = y; |
304 | 0 | goto loop; |
305 | | |
306 | 0 | case 0xAD:{// LDA abs |
307 | 0 | unsigned addr = GET_ADDR(); |
308 | 0 | pc += 2; |
309 | 0 | nz = READ( addr ); |
310 | 0 | a = nz; |
311 | 0 | goto loop; |
312 | 0 | } |
313 | | |
314 | 0 | case 0x60: // RTS |
315 | 0 | pc = 1 + READ_LOW( sp ); |
316 | 0 | pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); |
317 | 0 | sp = (sp - 0xFE) | 0x100; |
318 | 0 | goto loop; |
319 | | |
320 | 0 | { |
321 | 0 | uint16_t addr; |
322 | |
|
323 | 0 | case 0x99: // STA abs,Y |
324 | 0 | addr = y + GET_ADDR(); |
325 | 0 | pc += 2; |
326 | 0 | if ( addr <= 0x7FF ) |
327 | 0 | { |
328 | 0 | WRITE_LOW( addr, a ); |
329 | 0 | goto loop; |
330 | 0 | } |
331 | 0 | goto sta_ptr; |
332 | | |
333 | 0 | case 0x8D: // STA abs |
334 | 0 | addr = GET_ADDR(); |
335 | 0 | pc += 2; |
336 | 0 | if ( addr <= 0x7FF ) |
337 | 0 | { |
338 | 0 | WRITE_LOW( addr, a ); |
339 | 0 | goto loop; |
340 | 0 | } |
341 | 0 | goto sta_ptr; |
342 | | |
343 | 0 | case 0x9D: // STA abs,X (slightly more common than STA abs) |
344 | 0 | addr = x + GET_ADDR(); |
345 | 0 | pc += 2; |
346 | 0 | if ( addr <= 0x7FF ) |
347 | 0 | { |
348 | 0 | WRITE_LOW( addr, a ); |
349 | 0 | goto loop; |
350 | 0 | } |
351 | 0 | sta_ptr: |
352 | 0 | FLUSH_TIME(); |
353 | 0 | WRITE( addr, a ); |
354 | 0 | CACHE_TIME(); |
355 | 0 | goto loop; |
356 | | |
357 | 0 | case 0x91: // STA (ind),Y |
358 | 0 | IND_Y( NO_PAGE_CROSSING, addr ) |
359 | 0 | pc++; |
360 | 0 | goto sta_ptr; |
361 | | |
362 | 0 | case 0x81: // STA (ind,X) |
363 | 0 | IND_X( addr ) |
364 | 0 | pc++; |
365 | 0 | goto sta_ptr; |
366 | |
|
367 | 0 | } |
368 | | |
369 | 0 | case 0xA9: // LDA #imm |
370 | 0 | pc++; |
371 | 0 | a = data; |
372 | 0 | nz = data; |
373 | 0 | goto loop; |
374 | | |
375 | | // common read instructions |
376 | 0 | { |
377 | 0 | uint16_t addr; |
378 | |
|
379 | 0 | case 0xA1: // LDA (ind,X) |
380 | 0 | IND_X( addr ) |
381 | 0 | pc++; |
382 | 0 | goto a_nz_read_addr; |
383 | | |
384 | 0 | case 0xB1:// LDA (ind),Y |
385 | 0 | addr = READ_LOW( data ) + y; |
386 | 0 | HANDLE_PAGE_CROSSING( addr ); |
387 | 0 | addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); |
388 | 0 | pc++; |
389 | 0 | a = nz = READ_PROG( addr ); |
390 | 0 | if ( (addr ^ 0x8000) <= 0x9FFF ) |
391 | 0 | goto loop; |
392 | 0 | goto a_nz_read_addr; |
393 | | |
394 | 0 | case 0xB9: // LDA abs,Y |
395 | 0 | HANDLE_PAGE_CROSSING( data + y ); |
396 | 0 | addr = GET_ADDR() + y; |
397 | 0 | pc += 2; |
398 | 0 | a = nz = READ_PROG( addr ); |
399 | 0 | if ( (addr ^ 0x8000) <= 0x9FFF ) |
400 | 0 | goto loop; |
401 | 0 | goto a_nz_read_addr; |
402 | | |
403 | 0 | case 0xBD: // LDA abs,X |
404 | 0 | HANDLE_PAGE_CROSSING( data + x ); |
405 | 0 | addr = GET_ADDR() + x; |
406 | 0 | pc += 2; |
407 | 0 | a = nz = READ_PROG( addr ); |
408 | 0 | if ( (addr ^ 0x8000) <= 0x9FFF ) |
409 | 0 | goto loop; |
410 | 0 | a_nz_read_addr: |
411 | 0 | FLUSH_TIME(); |
412 | 0 | a = nz = READ( addr ); |
413 | 0 | CACHE_TIME(); |
414 | 0 | goto loop; |
415 | |
|
416 | 0 | } |
417 | | |
418 | | // Branch |
419 | | |
420 | 0 | case 0x50: // BVC |
421 | 0 | BRANCH( !(status & st_v) ) |
422 | | |
423 | 0 | case 0x70: // BVS |
424 | 0 | BRANCH( status & st_v ) |
425 | | |
426 | 0 | case 0xB0: // BCS |
427 | 0 | BRANCH( c & 0x100 ) |
428 | | |
429 | 0 | case 0x90: // BCC |
430 | 0 | BRANCH( !(c & 0x100) ) |
431 | | |
432 | | // Load/store |
433 | | |
434 | 0 | case 0x94: // STY zp,x |
435 | 0 | data = uint8_t (data + x);/*FALLTHRU*/ |
436 | 0 | case 0x84: // STY zp |
437 | 0 | pc++; |
438 | 0 | WRITE_LOW( data, y ); |
439 | 0 | goto loop; |
440 | | |
441 | 0 | case 0x96: // STX zp,y |
442 | 0 | data = uint8_t (data + y);/*FALLTHRU*/ |
443 | 0 | case 0x86: // STX zp |
444 | 0 | pc++; |
445 | 0 | WRITE_LOW( data, x ); |
446 | 0 | goto loop; |
447 | | |
448 | 0 | case 0xB6: // LDX zp,y |
449 | 0 | data = uint8_t (data + y);/*FALLTHRU*/ |
450 | 0 | case 0xA6: // LDX zp |
451 | 0 | data = READ_LOW( data );/*FALLTHRU*/ |
452 | 0 | case 0xA2: // LDX #imm |
453 | 0 | pc++; |
454 | 0 | x = data; |
455 | 0 | nz = data; |
456 | 0 | goto loop; |
457 | | |
458 | 0 | case 0xB4: // LDY zp,x |
459 | 0 | data = uint8_t (data + x);/*FALLTHRU*/ |
460 | 0 | case 0xA4: // LDY zp |
461 | 0 | data = READ_LOW( data );/*FALLTHRU*/ |
462 | 0 | case 0xA0: // LDY #imm |
463 | 0 | pc++; |
464 | 0 | y = data; |
465 | 0 | nz = data; |
466 | 0 | goto loop; |
467 | | |
468 | 0 | case 0xBC: // LDY abs,X |
469 | 0 | data += x; |
470 | 0 | HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ |
471 | 0 | case 0xAC:{// LDY abs |
472 | 0 | unsigned addr = data + 0x100 * GET_MSB(); |
473 | 0 | pc += 2; |
474 | 0 | FLUSH_TIME(); |
475 | 0 | y = nz = READ( addr ); |
476 | 0 | CACHE_TIME(); |
477 | 0 | goto loop; |
478 | 0 | } |
479 | | |
480 | 0 | case 0xBE: // LDX abs,y |
481 | 0 | data += y; |
482 | 0 | HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ |
483 | 0 | case 0xAE:{// LDX abs |
484 | 0 | unsigned addr = data + 0x100 * GET_MSB(); |
485 | 0 | pc += 2; |
486 | 0 | FLUSH_TIME(); |
487 | 0 | x = nz = READ( addr ); |
488 | 0 | CACHE_TIME(); |
489 | 0 | goto loop; |
490 | 0 | } |
491 | | |
492 | 0 | { |
493 | 0 | uint8_t temp; |
494 | 0 | case 0x8C: // STY abs |
495 | 0 | temp = y; |
496 | 0 | goto store_abs; |
497 | | |
498 | 0 | case 0x8E: // STX abs |
499 | 0 | temp = x; |
500 | 0 | store_abs: |
501 | 0 | unsigned addr = GET_ADDR(); |
502 | 0 | pc += 2; |
503 | 0 | if ( addr <= 0x7FF ) |
504 | 0 | { |
505 | 0 | WRITE_LOW( addr, temp ); |
506 | 0 | goto loop; |
507 | 0 | } |
508 | 0 | FLUSH_TIME(); |
509 | 0 | WRITE( addr, temp ); |
510 | 0 | CACHE_TIME(); |
511 | 0 | goto loop; |
512 | 0 | } |
513 | | |
514 | | // Compare |
515 | | |
516 | 0 | case 0xEC:{// CPX abs |
517 | 0 | unsigned addr = GET_ADDR(); |
518 | 0 | pc++; |
519 | 0 | FLUSH_TIME(); |
520 | 0 | data = READ( addr ); |
521 | 0 | CACHE_TIME(); |
522 | 0 | goto cpx_data; |
523 | 0 | } |
524 | | |
525 | 0 | case 0xE4: // CPX zp |
526 | 0 | data = READ_LOW( data );/*FALLTHRU*/ |
527 | 0 | case 0xE0: // CPX #imm |
528 | 0 | cpx_data: |
529 | 0 | nz = x - data; |
530 | 0 | pc++; |
531 | 0 | c = ~nz; |
532 | 0 | nz &= 0xFF; |
533 | 0 | goto loop; |
534 | | |
535 | 0 | case 0xCC:{// CPY abs |
536 | 0 | unsigned addr = GET_ADDR(); |
537 | 0 | pc++; |
538 | 0 | FLUSH_TIME(); |
539 | 0 | data = READ( addr ); |
540 | 0 | CACHE_TIME(); |
541 | 0 | goto cpy_data; |
542 | 0 | } |
543 | | |
544 | 0 | case 0xC4: // CPY zp |
545 | 0 | data = READ_LOW( data ); // FALLTHRU |
546 | 0 | case 0xC0: // CPY #imm |
547 | 0 | cpy_data: |
548 | 0 | nz = y - data; |
549 | 0 | pc++; |
550 | 0 | c = ~nz; |
551 | 0 | nz &= 0xFF; |
552 | 0 | goto loop; |
553 | | |
554 | | // Logical |
555 | | |
556 | 0 | ARITH_ADDR_MODES( 0x25 ) // AND |
557 | 0 | nz = (a &= data); |
558 | 0 | pc++; |
559 | 0 | goto loop; |
560 | | |
561 | 0 | ARITH_ADDR_MODES( 0x45 ) // EOR |
562 | 0 | nz = (a ^= data); |
563 | 0 | pc++; |
564 | 0 | goto loop; |
565 | | |
566 | 0 | ARITH_ADDR_MODES( 0x05 ) // ORA |
567 | 0 | nz = (a |= data); |
568 | 0 | pc++; |
569 | 0 | goto loop; |
570 | | |
571 | 0 | case 0x2C:{// BIT abs |
572 | 0 | unsigned addr = GET_ADDR(); |
573 | 0 | pc += 2; |
574 | 0 | status &= ~st_v; |
575 | 0 | nz = READ( addr ); |
576 | 0 | status |= nz & st_v; |
577 | 0 | if ( a & nz ) |
578 | 0 | goto loop; |
579 | 0 | nz <<= 8; // result must be zero, even if N bit is set |
580 | 0 | goto loop; |
581 | 0 | } |
582 | | |
583 | 0 | case 0x24: // BIT zp |
584 | 0 | nz = READ_LOW( data ); |
585 | 0 | pc++; |
586 | 0 | status &= ~st_v; |
587 | 0 | status |= nz & st_v; |
588 | 0 | if ( a & nz ) |
589 | 0 | goto loop; |
590 | 0 | nz <<= 8; // result must be zero, even if N bit is set |
591 | 0 | goto loop; |
592 | | |
593 | | // Add/subtract |
594 | | |
595 | 0 | ARITH_ADDR_MODES( 0xE5 ) // SBC |
596 | 0 | case 0xEB: // unofficial equivalent |
597 | 0 | data ^= 0xFF; |
598 | 0 | goto adc_imm; |
599 | | |
600 | 0 | ARITH_ADDR_MODES( 0x65 ) // ADC |
601 | 0 | adc_imm: { |
602 | 0 | check( !(status & st_d) ); |
603 | 0 | int16_t carry = c >> 8 & 1; |
604 | 0 | int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend |
605 | 0 | status &= ~st_v; |
606 | 0 | status |= ov >> 2 & 0x40; |
607 | 0 | c = nz = a + data + carry; |
608 | 0 | pc++; |
609 | 0 | a = (uint8_t) nz; |
610 | 0 | goto loop; |
611 | 0 | } |
612 | | |
613 | | // Shift/rotate |
614 | | |
615 | 0 | case 0x4A: // LSR A |
616 | 0 | c = 0;/*FALLTHRU*/ |
617 | 0 | case 0x6A: // ROR A |
618 | 0 | nz = c >> 1 & 0x80; |
619 | 0 | c = a << 8; |
620 | 0 | nz |= a >> 1; |
621 | 0 | a = nz; |
622 | 0 | goto loop; |
623 | | |
624 | 0 | case 0x0A: // ASL A |
625 | 0 | nz = a << 1; |
626 | 0 | c = nz; |
627 | 0 | a = (uint8_t) nz; |
628 | 0 | goto loop; |
629 | | |
630 | 0 | case 0x2A: { // ROL A |
631 | 0 | nz = a << 1; |
632 | 0 | int16_t temp = c >> 8 & 1; |
633 | 0 | c = nz; |
634 | 0 | nz |= temp; |
635 | 0 | a = (uint8_t) nz; |
636 | 0 | goto loop; |
637 | 0 | } |
638 | | |
639 | 0 | case 0x5E: // LSR abs,X |
640 | 0 | data += x;/*FALLTHRU*/ |
641 | 0 | case 0x4E: // LSR abs |
642 | 0 | c = 0;/*FALLTHRU*/ |
643 | 0 | case 0x6E: // ROR abs |
644 | 0 | ror_abs: { |
645 | 0 | ADD_PAGE(); |
646 | 0 | FLUSH_TIME(); |
647 | 0 | int temp = READ( data ); |
648 | 0 | nz = (c >> 1 & 0x80) | (temp >> 1); |
649 | 0 | c = temp << 8; |
650 | 0 | goto rotate_common; |
651 | 0 | } |
652 | | |
653 | 0 | case 0x3E: // ROL abs,X |
654 | 0 | data += x; |
655 | 0 | goto rol_abs; |
656 | | |
657 | 0 | case 0x1E: // ASL abs,X |
658 | 0 | data += x;/*FALLTHRU*/ |
659 | 0 | case 0x0E: // ASL abs |
660 | 0 | c = 0;/*FALLTHRU*/ |
661 | 0 | case 0x2E: // ROL abs |
662 | 0 | rol_abs: |
663 | 0 | ADD_PAGE(); |
664 | 0 | nz = c >> 8 & 1; |
665 | 0 | FLUSH_TIME(); |
666 | 0 | nz |= (c = READ( data ) << 1); |
667 | 0 | rotate_common: |
668 | 0 | pc++; |
669 | 0 | WRITE( data, (uint8_t) nz ); |
670 | 0 | CACHE_TIME(); |
671 | 0 | goto loop; |
672 | | |
673 | 0 | case 0x7E: // ROR abs,X |
674 | 0 | data += x; |
675 | 0 | goto ror_abs; |
676 | | |
677 | 0 | case 0x76: // ROR zp,x |
678 | 0 | data = uint8_t (data + x); |
679 | 0 | goto ror_zp; |
680 | | |
681 | 0 | case 0x56: // LSR zp,x |
682 | 0 | data = uint8_t (data + x);/*FALLTHRU*/ |
683 | 0 | case 0x46: // LSR zp |
684 | 0 | c = 0;/*FALLTHRU*/ |
685 | 0 | case 0x66: // ROR zp |
686 | 0 | ror_zp: { |
687 | 0 | int temp = READ_LOW( data ); |
688 | 0 | nz = (c >> 1 & 0x80) | (temp >> 1); |
689 | 0 | c = temp << 8; |
690 | 0 | goto write_nz_zp; |
691 | 0 | } |
692 | | |
693 | 0 | case 0x36: // ROL zp,x |
694 | 0 | data = uint8_t (data + x); |
695 | 0 | goto rol_zp; |
696 | | |
697 | 0 | case 0x16: // ASL zp,x |
698 | 0 | data = uint8_t (data + x);/*FALLTHRU*/ |
699 | 0 | case 0x06: // ASL zp |
700 | 0 | c = 0;/*FALLTHRU*/ |
701 | 0 | case 0x26: // ROL zp |
702 | 0 | rol_zp: |
703 | 0 | nz = c >> 8 & 1; |
704 | 0 | nz |= (c = READ_LOW( data ) << 1); |
705 | 0 | goto write_nz_zp; |
706 | | |
707 | | // Increment/decrement |
708 | | |
709 | 0 | case 0xCA: // DEX |
710 | 0 | INC_DEC_XY( x, -1 ) |
711 | | |
712 | 0 | case 0x88: // DEY |
713 | 0 | INC_DEC_XY( y, -1 ) |
714 | | |
715 | 0 | case 0xF6: // INC zp,x |
716 | 0 | data = uint8_t (data + x);/*FALLTHRU*/ |
717 | 0 | case 0xE6: // INC zp |
718 | 0 | nz = 1; |
719 | 0 | goto add_nz_zp; |
720 | | |
721 | 0 | case 0xD6: // DEC zp,x |
722 | 0 | data = uint8_t (data + x);/*FALLTHRU*/ |
723 | 0 | case 0xC6: // DEC zp |
724 | 0 | nz = (uint16_t) -1; |
725 | 0 | add_nz_zp: |
726 | 0 | nz += READ_LOW( data ); |
727 | 0 | write_nz_zp: |
728 | 0 | pc++; |
729 | 0 | WRITE_LOW( data, nz ); |
730 | 0 | goto loop; |
731 | | |
732 | 0 | case 0xFE: // INC abs,x |
733 | 0 | data = x + GET_ADDR(); |
734 | 0 | goto inc_ptr; |
735 | | |
736 | 0 | case 0xEE: // INC abs |
737 | 0 | data = GET_ADDR(); |
738 | 0 | inc_ptr: |
739 | 0 | nz = 1; |
740 | 0 | goto inc_common; |
741 | | |
742 | 0 | case 0xDE: // DEC abs,x |
743 | 0 | data = x + GET_ADDR(); |
744 | 0 | goto dec_ptr; |
745 | | |
746 | 0 | case 0xCE: // DEC abs |
747 | 0 | data = GET_ADDR(); |
748 | 0 | dec_ptr: |
749 | 0 | nz = (uint16_t) -1; |
750 | 0 | inc_common: |
751 | 0 | FLUSH_TIME(); |
752 | 0 | nz += READ( data ); |
753 | 0 | pc += 2; |
754 | 0 | WRITE( data, (uint8_t) nz ); |
755 | 0 | CACHE_TIME(); |
756 | 0 | goto loop; |
757 | | |
758 | | // Transfer |
759 | | |
760 | 0 | case 0xAA: // TAX |
761 | 0 | x = a; |
762 | 0 | nz = a; |
763 | 0 | goto loop; |
764 | | |
765 | 0 | case 0x8A: // TXA |
766 | 0 | a = x; |
767 | 0 | nz = x; |
768 | 0 | goto loop; |
769 | | |
770 | 0 | case 0x9A: // TXS |
771 | 0 | SET_SP( x ); // verified (no flag change) |
772 | 0 | goto loop; |
773 | | |
774 | 0 | case 0xBA: // TSX |
775 | 0 | x = nz = GET_SP(); |
776 | 0 | goto loop; |
777 | | |
778 | | // Stack |
779 | | |
780 | 0 | case 0x48: // PHA |
781 | 0 | PUSH( a ); // verified |
782 | 0 | goto loop; |
783 | | |
784 | 0 | case 0x68: // PLA |
785 | 0 | a = nz = READ_LOW( sp ); |
786 | 0 | sp = (sp - 0xFF) | 0x100; |
787 | 0 | goto loop; |
788 | | |
789 | 0 | case 0x40:{// RTI |
790 | 0 | uint8_t temp = READ_LOW( sp ); |
791 | 0 | pc = READ_LOW( 0x100 | (sp - 0xFF) ); |
792 | 0 | pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; |
793 | 0 | sp = (sp - 0xFD) | 0x100; |
794 | 0 | data = status; |
795 | 0 | SET_STATUS( temp ); |
796 | 0 | this->r.status = status; // update externally-visible I flag |
797 | 0 | if ( (data ^ status) & st_i ) |
798 | 0 | { |
799 | 0 | sap_time_t new_time = end_time_; |
800 | 0 | if ( !(status & st_i) && new_time > irq_time_ ) |
801 | 0 | new_time = irq_time_; |
802 | 0 | int32_t delta = s.base - new_time; |
803 | 0 | s.base = new_time; |
804 | 0 | s_time += delta; |
805 | 0 | } |
806 | 0 | goto loop; |
807 | 0 | } |
808 | | |
809 | 0 | case 0x28:{// PLP |
810 | 0 | uint8_t temp = READ_LOW( sp ); |
811 | 0 | sp = (sp - 0xFF) | 0x100; |
812 | 0 | uint8_t changed = status ^ temp; |
813 | 0 | SET_STATUS( temp ); |
814 | 0 | if ( !(changed & st_i) ) |
815 | 0 | goto loop; // I flag didn't change |
816 | 0 | if ( status & st_i ) |
817 | 0 | goto handle_sei; |
818 | 0 | goto handle_cli; |
819 | 0 | } |
820 | | |
821 | 0 | case 0x08: { // PHP |
822 | 0 | uint8_t temp; |
823 | 0 | CALC_STATUS( temp ); |
824 | 0 | PUSH( temp | (st_b | st_r) ); |
825 | 0 | goto loop; |
826 | 0 | } |
827 | | |
828 | 0 | case 0x6C:{// JMP (ind) |
829 | 0 | data = GET_ADDR(); |
830 | 0 | pc = READ_PROG( data ); |
831 | 0 | data = (data & 0xFF00) | ((data + 1) & 0xFF); |
832 | 0 | pc |= 0x100 * READ_PROG( data ); |
833 | 0 | goto loop; |
834 | 0 | } |
835 | | |
836 | 0 | case 0x00: // BRK |
837 | 0 | goto handle_brk; |
838 | | |
839 | | // Flags |
840 | | |
841 | 0 | case 0x38: // SEC |
842 | 0 | c = (uint16_t) ~0; |
843 | 0 | goto loop; |
844 | | |
845 | 0 | case 0x18: // CLC |
846 | 0 | c = 0; |
847 | 0 | goto loop; |
848 | | |
849 | 0 | case 0xB8: // CLV |
850 | 0 | status &= ~st_v; |
851 | 0 | goto loop; |
852 | | |
853 | 0 | case 0xD8: // CLD |
854 | 0 | status &= ~st_d; |
855 | 0 | goto loop; |
856 | | |
857 | 0 | case 0xF8: // SED |
858 | 0 | status |= st_d; |
859 | 0 | goto loop; |
860 | | |
861 | 0 | case 0x58: // CLI |
862 | 0 | if ( !(status & st_i) ) |
863 | 0 | goto loop; |
864 | 0 | status &= ~st_i; |
865 | 0 | handle_cli: { |
866 | 0 | this->r.status = status; // update externally-visible I flag |
867 | 0 | int32_t delta = s.base - irq_time_; |
868 | 0 | if ( delta <= 0 ) |
869 | 0 | { |
870 | 0 | if ( TIME < irq_time_ ) |
871 | 0 | goto loop; |
872 | 0 | goto delayed_cli; |
873 | 0 | } |
874 | 0 | s.base = irq_time_; |
875 | 0 | s_time += delta; |
876 | 0 | if ( s_time < 0 ) |
877 | 0 | goto loop; |
878 | | |
879 | 0 | if ( delta >= s_time + 1 ) |
880 | 0 | { |
881 | | // delayed irq until after next instruction |
882 | 0 | s.base += s_time + 1; |
883 | 0 | s_time = -1; |
884 | 0 | irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop |
885 | 0 | goto loop; |
886 | 0 | } |
887 | 0 | delayed_cli: |
888 | 0 | debug_printf( "Delayed CLI not emulated\n" ); |
889 | 0 | goto loop; |
890 | 0 | } |
891 | | |
892 | 0 | case 0x78: // SEI |
893 | 0 | if ( status & st_i ) |
894 | 0 | goto loop; |
895 | 0 | status |= st_i; |
896 | 0 | handle_sei: { |
897 | 0 | this->r.status = status; // update externally-visible I flag |
898 | 0 | int32_t delta = s.base - end_time_; |
899 | 0 | s.base = end_time_; |
900 | 0 | s_time += delta; |
901 | 0 | if ( s_time < 0 ) |
902 | 0 | goto loop; |
903 | 0 | debug_printf( "Delayed SEI not emulated\n" ); |
904 | 0 | goto loop; |
905 | 0 | } |
906 | | |
907 | | // Unofficial |
908 | | |
909 | | // SKW - Skip word |
910 | 0 | case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: |
911 | 0 | HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/ |
912 | 0 | case 0x0C: |
913 | 0 | pc++;/*FALLTHRU*/ |
914 | | // SKB - Skip byte |
915 | 0 | case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: |
916 | 0 | case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: |
917 | 0 | pc++; |
918 | 0 | goto loop; |
919 | | |
920 | | // NOP |
921 | 0 | case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: |
922 | 0 | goto loop; |
923 | | |
924 | | // Unimplemented |
925 | | |
926 | | // halt |
927 | | //case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: |
928 | | //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2: |
929 | | |
930 | 0 | default: |
931 | 0 | illegal_encountered = true; |
932 | 0 | pc--; |
933 | 0 | goto stop; |
934 | 0 | } |
935 | 0 | assert( false ); |
936 | |
|
937 | 0 | int result_; |
938 | 0 | handle_brk: |
939 | 0 | if ( (pc - 1) >= idle_addr ) |
940 | 0 | goto idle_done; |
941 | 0 | pc++; |
942 | 0 | result_ = 4; |
943 | 0 | debug_printf( "BRK executed\n" ); |
944 | |
|
945 | 0 | interrupt: |
946 | 0 | { |
947 | 0 | s_time += 7; |
948 | |
|
949 | 0 | WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); |
950 | 0 | WRITE_LOW( 0x100 | (sp - 2), pc ); |
951 | 0 | pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); |
952 | |
|
953 | 0 | sp = (sp - 3) | 0x100; |
954 | 0 | uint8_t temp; |
955 | 0 | CALC_STATUS( temp ); |
956 | 0 | temp |= st_r; |
957 | 0 | if ( result_ ) |
958 | 0 | temp |= st_b; // TODO: incorrectly sets B flag for IRQ |
959 | 0 | WRITE_LOW( sp, temp ); |
960 | |
|
961 | 0 | status &= ~st_d; |
962 | 0 | status |= st_i; |
963 | 0 | this->r.status = status; // update externally-visible I flag |
964 | |
|
965 | 0 | int32_t delta = s.base - end_time_; |
966 | 0 | s.base = end_time_; |
967 | 0 | s_time += delta; |
968 | 0 | goto loop; |
969 | 0 | } |
970 | | |
971 | 0 | idle_done: |
972 | | //s_time = 0; |
973 | 0 | pc--; |
974 | 0 | goto stop; |
975 | 0 | out_of_time: |
976 | 0 | pc--; |
977 | 0 | FLUSH_TIME(); |
978 | 0 | CPU_DONE( this, TIME, result_ ); |
979 | 0 | CACHE_TIME(); |
980 | 0 | if ( result_ >= 0 ) |
981 | 0 | goto interrupt; |
982 | 0 | if ( s_time < 0 ) |
983 | 0 | goto loop; |
984 | | |
985 | 0 | stop: |
986 | |
|
987 | 0 | s.time = s_time; |
988 | |
|
989 | 0 | r.pc = pc; |
990 | 0 | r.sp = GET_SP(); |
991 | 0 | r.a = a; |
992 | 0 | r.x = x; |
993 | 0 | r.y = y; |
994 | |
|
995 | 0 | { |
996 | 0 | uint8_t temp; |
997 | 0 | CALC_STATUS( temp ); |
998 | 0 | r.status = temp; |
999 | 0 | } |
1000 | |
|
1001 | 0 | this->state_ = s; |
1002 | 0 | this->state = &this->state_; |
1003 | |
|
1004 | 0 | return illegal_encountered; |
1005 | 0 | } |
1006 | | |