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