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