diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-25 21:32:25 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-04-25 22:13:20 +0200 |
commit | f40bfc9267b13b54e6379dfe7539447662879d24 (patch) | |
tree | 9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs/libspc/spc_cpu.c | |
parent | a0009907de7a0107d49040d8a180f140e2eff299 (diff) | |
download | rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip |
Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97
Reviewed-on: http://gerrit.rockbox.org/137
Reviewed-by: Nils Wallménius <nils@rockbox.org>
Tested-by: Nils Wallménius <nils@rockbox.org>
Diffstat (limited to 'lib/rbcodec/codecs/libspc/spc_cpu.c')
-rw-r--r-- | lib/rbcodec/codecs/libspc/spc_cpu.c | 1049 |
1 files changed, 1049 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libspc/spc_cpu.c b/lib/rbcodec/codecs/libspc/spc_cpu.c new file mode 100644 index 0000000000..23dcc257de --- /dev/null +++ b/lib/rbcodec/codecs/libspc/spc_cpu.c | |||
@@ -0,0 +1,1049 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Adam Gashlin (hcs) | ||
11 | * Copyright (C) 2004-2007 Shay Green (blargg) | ||
12 | * Copyright (C) 2002 Brad Martin | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version 2 | ||
17 | * of the License, or (at your option) any later version. | ||
18 | * | ||
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
20 | * KIND, either express or implied. | ||
21 | * | ||
22 | ****************************************************************************/ | ||
23 | |||
24 | /* The CPU portion (shock!) */ | ||
25 | #include "codeclib.h" | ||
26 | #include "spc_codec.h" | ||
27 | #include "spc_profiler.h" | ||
28 | |||
29 | #undef check | ||
30 | #define check assert | ||
31 | |||
32 | #define READ( addr ) (SPC_read( this, addr, spc_time_ )) | ||
33 | #define WRITE( addr, value ) (SPC_write( this, addr, value, spc_time_ )) | ||
34 | |||
35 | #define READ_DP( addr ) READ( (addr) + dp ) | ||
36 | #define WRITE_DP( addr, value ) WRITE( (addr) + dp, value ) | ||
37 | |||
38 | #define READ_PROG16( addr ) GET_LE16( RAM + (addr) ) | ||
39 | |||
40 | #define READ_PC( pc ) (*(pc)) | ||
41 | #define READ_PC16( pc ) GET_LE16( pc ) | ||
42 | |||
43 | #define SET_PC( n ) (pc = RAM + (n)) | ||
44 | #define GET_PC() (pc - RAM) | ||
45 | |||
46 | static unsigned char const cycle_table [0x100] = { | ||
47 | 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, /* 0 */ | ||
48 | 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, /* 1 */ | ||
49 | 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, /* 2 */ | ||
50 | 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, /* 3 */ | ||
51 | 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, /* 4 */ | ||
52 | 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, /* 5 */ | ||
53 | 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, /* 6 */ | ||
54 | 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, /* 7 */ | ||
55 | 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, /* 8 */ | ||
56 | 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,/* 9 */ | ||
57 | 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, /* A */ | ||
58 | 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, /* B */ | ||
59 | 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, /* C */ | ||
60 | 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, /* D */ | ||
61 | 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, /* E */ | ||
62 | 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 /* F */ | ||
63 | }; | ||
64 | |||
65 | #define MEM_BIT() CPU_mem_bit( this, pc, spc_time_ ) | ||
66 | |||
67 | static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ ) | ||
68 | ICODE_ATTR_SPC; | ||
69 | |||
70 | static unsigned CPU_mem_bit( THIS, uint8_t const* pc, long const spc_time_ ) | ||
71 | { | ||
72 | unsigned addr = READ_PC16( pc ); | ||
73 | unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13); | ||
74 | return (t << 8) & 0x100; | ||
75 | } | ||
76 | |||
77 | /* status flags */ | ||
78 | enum { st_n = 0x80 }; | ||
79 | enum { st_v = 0x40 }; | ||
80 | enum { st_p = 0x20 }; | ||
81 | enum { st_b = 0x10 }; | ||
82 | enum { st_h = 0x08 }; | ||
83 | enum { st_i = 0x04 }; | ||
84 | enum { st_z = 0x02 }; | ||
85 | enum { st_c = 0x01 }; | ||
86 | |||
87 | #define IS_NEG (nz & 0x880) | ||
88 | |||
89 | #define CALC_STATUS( out )\ | ||
90 | {\ | ||
91 | out = status & ~(st_n | st_z | st_c);\ | ||
92 | out |= (c >> 8) & st_c;\ | ||
93 | out |= (dp >> 3) & st_p;\ | ||
94 | if ( IS_NEG ) out |= st_n;\ | ||
95 | if ( !(nz & 0xFF) ) out |= st_z;\ | ||
96 | } | ||
97 | |||
98 | #define SET_STATUS( in )\ | ||
99 | {\ | ||
100 | status = in & ~(st_n | st_z | st_c | st_p);\ | ||
101 | c = in << 8;\ | ||
102 | nz = ((in << 4) & 0x800) | (~in & st_z);\ | ||
103 | dp = (in << 3) & 0x100;\ | ||
104 | } | ||
105 | |||
106 | |||
107 | /* stack */ | ||
108 | #define PUSH( v ) (*--sp = (uint8_t) (v)) | ||
109 | #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) | ||
110 | #define POP() (*sp++) | ||
111 | #define SET_SP( v ) (sp = RAM + 0x101 + (v)) | ||
112 | #define GET_SP() (sp - 0x101 - RAM) | ||
113 | |||
114 | long CPU_run( THIS, long start_time ) | ||
115 | { | ||
116 | #if 0 | ||
117 | ENTER_TIMER(cpu); | ||
118 | #endif | ||
119 | |||
120 | register long spc_time_ = start_time; | ||
121 | |||
122 | #ifdef CPU_ARM | ||
123 | uint8_t* const ram_ = ram.ram; | ||
124 | #undef RAM | ||
125 | #define RAM ram_ | ||
126 | #endif | ||
127 | |||
128 | int a = this->r.a; | ||
129 | int x = this->r.x; | ||
130 | int y = this->r.y; | ||
131 | |||
132 | uint8_t const* pc; | ||
133 | SET_PC( this->r.pc ); | ||
134 | |||
135 | uint8_t* sp; | ||
136 | SET_SP( this->r.sp ); | ||
137 | |||
138 | int status; | ||
139 | int c; | ||
140 | int nz; | ||
141 | unsigned dp; | ||
142 | { | ||
143 | int temp = this->r.status; | ||
144 | SET_STATUS( temp ); | ||
145 | } | ||
146 | |||
147 | goto loop; | ||
148 | |||
149 | /* main loop */ | ||
150 | cbranch_taken_loop: | ||
151 | pc += *(int8_t const*) pc; | ||
152 | spc_time_ += 2; | ||
153 | inc_pc_loop: | ||
154 | pc++; | ||
155 | loop: | ||
156 | check( (unsigned) GET_PC() < 0x10000 ); | ||
157 | check( (unsigned) GET_SP() < 0x100 ); | ||
158 | check( (unsigned) a < 0x100 ); | ||
159 | check( (unsigned) x < 0x100 ); | ||
160 | check( (unsigned) y < 0x100 ); | ||
161 | |||
162 | unsigned opcode = *pc; | ||
163 | int cycles = this->cycle_table [opcode]; | ||
164 | unsigned data = *++pc; | ||
165 | if ( (spc_time_ += cycles) > 0 ) | ||
166 | goto out_of_time; | ||
167 | switch ( opcode ) | ||
168 | { | ||
169 | |||
170 | /* Common instructions */ | ||
171 | |||
172 | #define BRANCH( cond )\ | ||
173 | {\ | ||
174 | pc++;\ | ||
175 | int offset = (int8_t) data;\ | ||
176 | if ( cond ) {\ | ||
177 | pc += offset;\ | ||
178 | spc_time_ += 2;\ | ||
179 | }\ | ||
180 | goto loop;\ | ||
181 | } | ||
182 | |||
183 | case 0xF0: /* BEQ (most common) */ | ||
184 | BRANCH( !(uint8_t) nz ) | ||
185 | |||
186 | case 0xD0: /* BNE */ | ||
187 | BRANCH( (uint8_t) nz ) | ||
188 | |||
189 | case 0x3F: /* CALL */ | ||
190 | PUSH16( GET_PC() + 2 ); | ||
191 | SET_PC( READ_PC16( pc ) ); | ||
192 | goto loop; | ||
193 | |||
194 | case 0x6F: /* RET */ | ||
195 | SET_PC( POP() ); | ||
196 | pc += POP() * 0x100; | ||
197 | goto loop; | ||
198 | |||
199 | #define CASE( n ) case n: | ||
200 | |||
201 | /* Define common address modes based on opcode for immediate mode. Execution | ||
202 | ends with data set to the address of the operand. */ | ||
203 | #define ADDR_MODES( op )\ | ||
204 | CASE( op - 0x02 ) /* (X) */\ | ||
205 | data = x + dp;\ | ||
206 | pc--;\ | ||
207 | goto end_##op;\ | ||
208 | CASE( op + 0x0F ) /* (dp)+Y */\ | ||
209 | data = READ_PROG16( data + dp ) + y;\ | ||
210 | goto end_##op;\ | ||
211 | CASE( op - 0x01 ) /* (dp+X) */\ | ||
212 | data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ | ||
213 | goto end_##op;\ | ||
214 | CASE( op + 0x0E ) /* abs+Y */\ | ||
215 | data += y;\ | ||
216 | goto abs_##op;\ | ||
217 | CASE( op + 0x0D ) /* abs+X */\ | ||
218 | data += x;\ | ||
219 | CASE( op - 0x03 ) /* abs */\ | ||
220 | abs_##op:\ | ||
221 | data += 0x100 * READ_PC( ++pc );\ | ||
222 | goto end_##op;\ | ||
223 | CASE( op + 0x0C ) /* dp+X */\ | ||
224 | data = (uint8_t) (data + x);\ | ||
225 | CASE( op - 0x04 ) /* dp */\ | ||
226 | data += dp;\ | ||
227 | end_##op: | ||
228 | |||
229 | /* 1. 8-bit Data Transmission Commands. Group I */ | ||
230 | |||
231 | ADDR_MODES( 0xE8 ) /* MOV A,addr */ | ||
232 | /*case 0xE4:*/ /* MOV a,dp (most common) */ | ||
233 | mov_a_addr: | ||
234 | a = nz = READ( data ); | ||
235 | goto inc_pc_loop; | ||
236 | case 0xBF: /* MOV A,(X)+ */ | ||
237 | data = x + dp; | ||
238 | x = (uint8_t) (x + 1); | ||
239 | pc--; | ||
240 | goto mov_a_addr; | ||
241 | |||
242 | case 0xE8: /* MOV A,imm */ | ||
243 | a = data; | ||
244 | nz = data; | ||
245 | goto inc_pc_loop; | ||
246 | |||
247 | case 0xF9: /* MOV X,dp+Y */ | ||
248 | data = (uint8_t) (data + y); | ||
249 | case 0xF8: /* MOV X,dp */ | ||
250 | data += dp; | ||
251 | goto mov_x_addr; | ||
252 | case 0xE9: /* MOV X,abs */ | ||
253 | data = READ_PC16( pc ); | ||
254 | pc++; | ||
255 | mov_x_addr: | ||
256 | data = READ( data ); | ||
257 | case 0xCD: /* MOV X,imm */ | ||
258 | x = data; | ||
259 | nz = data; | ||
260 | goto inc_pc_loop; | ||
261 | |||
262 | case 0xFB: /* MOV Y,dp+X */ | ||
263 | data = (uint8_t) (data + x); | ||
264 | case 0xEB: /* MOV Y,dp */ | ||
265 | data += dp; | ||
266 | goto mov_y_addr; | ||
267 | case 0xEC: /* MOV Y,abs */ | ||
268 | data = READ_PC16( pc ); | ||
269 | pc++; | ||
270 | mov_y_addr: | ||
271 | data = READ( data ); | ||
272 | case 0x8D: /* MOV Y,imm */ | ||
273 | y = data; | ||
274 | nz = data; | ||
275 | goto inc_pc_loop; | ||
276 | |||
277 | /* 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 */ | ||
278 | |||
279 | ADDR_MODES( 0xC8 ) /* MOV addr,A */ | ||
280 | WRITE( data, a ); | ||
281 | goto inc_pc_loop; | ||
282 | |||
283 | { | ||
284 | int temp; | ||
285 | case 0xCC: /* MOV abs,Y */ | ||
286 | temp = y; | ||
287 | goto mov_abs_temp; | ||
288 | case 0xC9: /* MOV abs,X */ | ||
289 | temp = x; | ||
290 | mov_abs_temp: | ||
291 | WRITE( READ_PC16( pc ), temp ); | ||
292 | pc += 2; | ||
293 | goto loop; | ||
294 | } | ||
295 | |||
296 | case 0xD9: /* MOV dp+Y,X */ | ||
297 | data = (uint8_t) (data + y); | ||
298 | case 0xD8: /* MOV dp,X */ | ||
299 | WRITE( data + dp, x ); | ||
300 | goto inc_pc_loop; | ||
301 | |||
302 | case 0xDB: /* MOV dp+X,Y */ | ||
303 | data = (uint8_t) (data + x); | ||
304 | case 0xCB: /* MOV dp,Y */ | ||
305 | WRITE( data + dp, y ); | ||
306 | goto inc_pc_loop; | ||
307 | |||
308 | case 0xFA: /* MOV dp,dp */ | ||
309 | data = READ( data + dp ); | ||
310 | case 0x8F: /* MOV dp,#imm */ | ||
311 | WRITE_DP( READ_PC( ++pc ), data ); | ||
312 | goto inc_pc_loop; | ||
313 | |||
314 | /* 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. */ | ||
315 | |||
316 | case 0x7D: /* MOV A,X */ | ||
317 | a = x; | ||
318 | nz = x; | ||
319 | goto loop; | ||
320 | |||
321 | case 0xDD: /* MOV A,Y */ | ||
322 | a = y; | ||
323 | nz = y; | ||
324 | goto loop; | ||
325 | |||
326 | case 0x5D: /* MOV X,A */ | ||
327 | x = a; | ||
328 | nz = a; | ||
329 | goto loop; | ||
330 | |||
331 | case 0xFD: /* MOV Y,A */ | ||
332 | y = a; | ||
333 | nz = a; | ||
334 | goto loop; | ||
335 | |||
336 | case 0x9D: /* MOV X,SP */ | ||
337 | x = nz = GET_SP(); | ||
338 | goto loop; | ||
339 | |||
340 | case 0xBD: /* MOV SP,X */ | ||
341 | SET_SP( x ); | ||
342 | goto loop; | ||
343 | |||
344 | /*case 0xC6:*/ /* MOV (X),A (handled by MOV addr,A in group 2) */ | ||
345 | |||
346 | case 0xAF: /* MOV (X)+,A */ | ||
347 | WRITE_DP( x, a ); | ||
348 | x++; | ||
349 | goto loop; | ||
350 | |||
351 | /* 5. 8-BIT LOGIC OPERATION COMMANDS */ | ||
352 | |||
353 | #define LOGICAL_OP( op, func )\ | ||
354 | ADDR_MODES( op ) /* addr */\ | ||
355 | data = READ( data );\ | ||
356 | case op: /* imm */\ | ||
357 | nz = a func##= data;\ | ||
358 | goto inc_pc_loop;\ | ||
359 | { unsigned addr;\ | ||
360 | case op + 0x11: /* X,Y */\ | ||
361 | data = READ_DP( y );\ | ||
362 | addr = x + dp;\ | ||
363 | pc--;\ | ||
364 | goto addr_##op;\ | ||
365 | case op + 0x01: /* dp,dp */\ | ||
366 | data = READ_DP( data );\ | ||
367 | case op + 0x10: /*dp,imm*/\ | ||
368 | addr = READ_PC( ++pc ) + dp;\ | ||
369 | addr_##op:\ | ||
370 | nz = data func READ( addr );\ | ||
371 | WRITE( addr, nz );\ | ||
372 | goto inc_pc_loop;\ | ||
373 | } | ||
374 | |||
375 | LOGICAL_OP( 0x28, & ); /* AND */ | ||
376 | |||
377 | LOGICAL_OP( 0x08, | ); /* OR */ | ||
378 | |||
379 | LOGICAL_OP( 0x48, ^ ); /* EOR */ | ||
380 | |||
381 | /* 4. 8-BIT ARITHMETIC OPERATION COMMANDS */ | ||
382 | |||
383 | ADDR_MODES( 0x68 ) /* CMP addr */ | ||
384 | data = READ( data ); | ||
385 | case 0x68: /* CMP imm */ | ||
386 | nz = a - data; | ||
387 | c = ~nz; | ||
388 | nz &= 0xFF; | ||
389 | goto inc_pc_loop; | ||
390 | |||
391 | case 0x79: /* CMP (X),(Y) */ | ||
392 | data = READ_DP( x ); | ||
393 | nz = data - READ_DP( y ); | ||
394 | c = ~nz; | ||
395 | nz &= 0xFF; | ||
396 | goto loop; | ||
397 | |||
398 | case 0x69: /* CMP (dp),(dp) */ | ||
399 | data = READ_DP( data ); | ||
400 | case 0x78: /* CMP dp,imm */ | ||
401 | nz = READ_DP( READ_PC( ++pc ) ) - data; | ||
402 | c = ~nz; | ||
403 | nz &= 0xFF; | ||
404 | goto inc_pc_loop; | ||
405 | |||
406 | case 0x3E: /* CMP X,dp */ | ||
407 | data += dp; | ||
408 | goto cmp_x_addr; | ||
409 | case 0x1E: /* CMP X,abs */ | ||
410 | data = READ_PC16( pc ); | ||
411 | pc++; | ||
412 | cmp_x_addr: | ||
413 | data = READ( data ); | ||
414 | case 0xC8: /* CMP X,imm */ | ||
415 | nz = x - data; | ||
416 | c = ~nz; | ||
417 | nz &= 0xFF; | ||
418 | goto inc_pc_loop; | ||
419 | |||
420 | case 0x7E: /* CMP Y,dp */ | ||
421 | data += dp; | ||
422 | goto cmp_y_addr; | ||
423 | case 0x5E: /* CMP Y,abs */ | ||
424 | data = READ_PC16( pc ); | ||
425 | pc++; | ||
426 | cmp_y_addr: | ||
427 | data = READ( data ); | ||
428 | case 0xAD: /* CMP Y,imm */ | ||
429 | nz = y - data; | ||
430 | c = ~nz; | ||
431 | nz &= 0xFF; | ||
432 | goto inc_pc_loop; | ||
433 | |||
434 | { | ||
435 | int addr; | ||
436 | case 0xB9: /* SBC (x),(y) */ | ||
437 | case 0x99: /* ADC (x),(y) */ | ||
438 | pc--; /* compensate for inc later */ | ||
439 | data = READ_DP( x ); | ||
440 | addr = y + dp; | ||
441 | goto adc_addr; | ||
442 | case 0xA9: /* SBC dp,dp */ | ||
443 | case 0x89: /* ADC dp,dp */ | ||
444 | data = READ_DP( data ); | ||
445 | case 0xB8: /* SBC dp,imm */ | ||
446 | case 0x98: /* ADC dp,imm */ | ||
447 | addr = READ_PC( ++pc ) + dp; | ||
448 | adc_addr: | ||
449 | nz = READ( addr ); | ||
450 | goto adc_data; | ||
451 | |||
452 | /* catch ADC and SBC together, then decode later based on operand */ | ||
453 | #undef CASE | ||
454 | #define CASE( n ) case n: case (n) + 0x20: | ||
455 | ADDR_MODES( 0x88 ) /* ADC/SBC addr */ | ||
456 | data = READ( data ); | ||
457 | case 0xA8: /* SBC imm */ | ||
458 | case 0x88: /* ADC imm */ | ||
459 | addr = -1; /* A */ | ||
460 | nz = a; | ||
461 | adc_data: { | ||
462 | if ( opcode & 0x20 ) | ||
463 | data ^= 0xFF; /* SBC */ | ||
464 | int carry = (c >> 8) & 1; | ||
465 | int ov = (nz ^ 0x80) + carry + (int8_t) data; /* sign-extend */ | ||
466 | int hc = (nz & 15) + carry; | ||
467 | c = nz += data + carry; | ||
468 | hc = (nz & 15) - hc; | ||
469 | status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | | ||
470 | ((hc >> 1) & st_h); | ||
471 | if ( addr < 0 ) { | ||
472 | a = (uint8_t) nz; | ||
473 | goto inc_pc_loop; | ||
474 | } | ||
475 | WRITE( addr, (uint8_t) nz ); | ||
476 | goto inc_pc_loop; | ||
477 | } | ||
478 | |||
479 | } | ||
480 | |||
481 | /* 6. ADDITION & SUBTRACTION COMMANDS */ | ||
482 | |||
483 | #define INC_DEC_REG( reg, n )\ | ||
484 | nz = reg + n;\ | ||
485 | reg = (uint8_t) nz;\ | ||
486 | goto loop; | ||
487 | |||
488 | case 0xBC: INC_DEC_REG( a, 1 ) /* INC A */ | ||
489 | case 0x3D: INC_DEC_REG( x, 1 ) /* INC X */ | ||
490 | case 0xFC: INC_DEC_REG( y, 1 ) /* INC Y */ | ||
491 | |||
492 | case 0x9C: INC_DEC_REG( a, -1 ) /* DEC A */ | ||
493 | case 0x1D: INC_DEC_REG( x, -1 ) /* DEC X */ | ||
494 | case 0xDC: INC_DEC_REG( y, -1 ) /* DEC Y */ | ||
495 | |||
496 | case 0x9B: /* DEC dp+X */ | ||
497 | case 0xBB: /* INC dp+X */ | ||
498 | data = (uint8_t) (data + x); | ||
499 | case 0x8B: /* DEC dp */ | ||
500 | case 0xAB: /* INC dp */ | ||
501 | data += dp; | ||
502 | goto inc_abs; | ||
503 | case 0x8C: /* DEC abs */ | ||
504 | case 0xAC: /* INC abs */ | ||
505 | data = READ_PC16( pc ); | ||
506 | pc++; | ||
507 | inc_abs: | ||
508 | nz = ((opcode >> 4) & 2) - 1; | ||
509 | nz += READ( data ); | ||
510 | WRITE( data, (uint8_t) nz ); | ||
511 | goto inc_pc_loop; | ||
512 | |||
513 | /* 7. SHIFT, ROTATION COMMANDS */ | ||
514 | |||
515 | case 0x5C: /* LSR A */ | ||
516 | c = 0; | ||
517 | case 0x7C:{/* ROR A */ | ||
518 | nz = ((c >> 1) & 0x80) | (a >> 1); | ||
519 | c = a << 8; | ||
520 | a = nz; | ||
521 | goto loop; | ||
522 | } | ||
523 | |||
524 | case 0x1C: /* ASL A */ | ||
525 | c = 0; | ||
526 | case 0x3C:{/* ROL A */ | ||
527 | int temp = (c >> 8) & 1; | ||
528 | c = a << 1; | ||
529 | nz = c | temp; | ||
530 | a = (uint8_t) nz; | ||
531 | goto loop; | ||
532 | } | ||
533 | |||
534 | case 0x0B: /* ASL dp */ | ||
535 | c = 0; | ||
536 | data += dp; | ||
537 | goto rol_mem; | ||
538 | case 0x1B: /* ASL dp+X */ | ||
539 | c = 0; | ||
540 | case 0x3B: /* ROL dp+X */ | ||
541 | data = (uint8_t) (data + x); | ||
542 | case 0x2B: /* ROL dp */ | ||
543 | data += dp; | ||
544 | goto rol_mem; | ||
545 | case 0x0C: /* ASL abs */ | ||
546 | c = 0; | ||
547 | case 0x2C: /* ROL abs */ | ||
548 | data = READ_PC16( pc ); | ||
549 | pc++; | ||
550 | rol_mem: | ||
551 | nz = (c >> 8) & 1; | ||
552 | nz |= (c = READ( data ) << 1); | ||
553 | WRITE( data, (uint8_t) nz ); | ||
554 | goto inc_pc_loop; | ||
555 | |||
556 | case 0x4B: /* LSR dp */ | ||
557 | c = 0; | ||
558 | data += dp; | ||
559 | goto ror_mem; | ||
560 | case 0x5B: /* LSR dp+X */ | ||
561 | c = 0; | ||
562 | case 0x7B: /* ROR dp+X */ | ||
563 | data = (uint8_t) (data + x); | ||
564 | case 0x6B: /* ROR dp */ | ||
565 | data += dp; | ||
566 | goto ror_mem; | ||
567 | case 0x4C: /* LSR abs */ | ||
568 | c = 0; | ||
569 | case 0x6C: /* ROR abs */ | ||
570 | data = READ_PC16( pc ); | ||
571 | pc++; | ||
572 | ror_mem: { | ||
573 | int temp = READ( data ); | ||
574 | nz = ((c >> 1) & 0x80) | (temp >> 1); | ||
575 | c = temp << 8; | ||
576 | WRITE( data, nz ); | ||
577 | goto inc_pc_loop; | ||
578 | } | ||
579 | |||
580 | case 0x9F: /* XCN */ | ||
581 | nz = a = (a >> 4) | (uint8_t) (a << 4); | ||
582 | goto loop; | ||
583 | |||
584 | /* 8. 16-BIT TRANSMISION COMMANDS */ | ||
585 | |||
586 | case 0xBA: /* MOVW YA,dp */ | ||
587 | a = READ_DP( data ); | ||
588 | nz = (a & 0x7F) | (a >> 1); | ||
589 | y = READ_DP( (uint8_t) (data + 1) ); | ||
590 | nz |= y; | ||
591 | goto inc_pc_loop; | ||
592 | |||
593 | case 0xDA: /* MOVW dp,YA */ | ||
594 | WRITE_DP( data, a ); | ||
595 | WRITE_DP( (uint8_t) (data + 1), y ); | ||
596 | goto inc_pc_loop; | ||
597 | |||
598 | /* 9. 16-BIT OPERATION COMMANDS */ | ||
599 | |||
600 | case 0x3A: /* INCW dp */ | ||
601 | case 0x1A:{/* DECW dp */ | ||
602 | data += dp; | ||
603 | |||
604 | /* low byte */ | ||
605 | int temp = READ( data ); | ||
606 | temp += ((opcode >> 4) & 2) - 1; /* +1 for INCW, -1 for DECW */ | ||
607 | nz = ((temp >> 1) | temp) & 0x7F; | ||
608 | WRITE( data, (uint8_t) temp ); | ||
609 | |||
610 | /* high byte */ | ||
611 | data = ((uint8_t) (data + 1)) + dp; | ||
612 | temp >>= 8; | ||
613 | temp = (uint8_t) (temp + READ( data )); | ||
614 | nz |= temp; | ||
615 | WRITE( data, temp ); | ||
616 | |||
617 | goto inc_pc_loop; | ||
618 | } | ||
619 | |||
620 | case 0x9A: /* SUBW YA,dp */ | ||
621 | case 0x7A: /* ADDW YA,dp */ | ||
622 | { | ||
623 | /* read 16-bit addend */ | ||
624 | int temp = READ_DP( data ); | ||
625 | int sign = READ_DP( (uint8_t) (data + 1) ); | ||
626 | temp += 0x100 * sign; | ||
627 | status &= ~(st_v | st_h); | ||
628 | |||
629 | /* to do: fix half-carry for SUBW (it's probably wrong) */ | ||
630 | |||
631 | /* for SUBW, negate and truncate to 16 bits */ | ||
632 | if ( opcode & 0x80 ) { | ||
633 | temp = (temp ^ 0xFFFF) + 1; | ||
634 | sign = temp >> 8; | ||
635 | } | ||
636 | |||
637 | /* add low byte (A) */ | ||
638 | temp += a; | ||
639 | a = (uint8_t) temp; | ||
640 | nz = (temp | (temp >> 1)) & 0x7F; | ||
641 | |||
642 | /* add high byte (Y) */ | ||
643 | temp >>= 8; | ||
644 | c = y + temp; | ||
645 | nz = (nz | c) & 0xFF; | ||
646 | |||
647 | /* half-carry (temporary avoids CodeWarrior optimizer bug) */ | ||
648 | unsigned hc = (c & 15) - (y & 15); | ||
649 | status |= (hc >> 4) & st_h; | ||
650 | |||
651 | /* overflow if sign of YA changed when previous sign | ||
652 | and addend sign were same */ | ||
653 | status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v; | ||
654 | |||
655 | y = (uint8_t) c; | ||
656 | |||
657 | goto inc_pc_loop; | ||
658 | } | ||
659 | |||
660 | case 0x5A: { /* CMPW YA,dp */ | ||
661 | int temp = a - READ_DP( data ); | ||
662 | nz = ((temp >> 1) | temp) & 0x7F; | ||
663 | temp = y + (temp >> 8); | ||
664 | temp -= READ_DP( (uint8_t) (data + 1) ); | ||
665 | nz |= temp; | ||
666 | c = ~temp; | ||
667 | nz &= 0xFF; | ||
668 | goto inc_pc_loop; | ||
669 | } | ||
670 | |||
671 | /* 10. MULTIPLICATION & DIVISON COMMANDS */ | ||
672 | |||
673 | case 0xCF: { /* MUL YA */ | ||
674 | unsigned temp = y * a; | ||
675 | a = (uint8_t) temp; | ||
676 | nz = ((temp >> 1) | temp) & 0x7F; | ||
677 | y = temp >> 8; | ||
678 | nz |= y; | ||
679 | goto loop; | ||
680 | } | ||
681 | |||
682 | case 0x9E: /* DIV YA,X */ | ||
683 | { | ||
684 | /* behavior based on SPC CPU tests */ | ||
685 | |||
686 | status &= ~(st_h | st_v); | ||
687 | |||
688 | if ( (y & 15) >= (x & 15) ) | ||
689 | status |= st_h; | ||
690 | |||
691 | if ( y >= x ) | ||
692 | status |= st_v; | ||
693 | |||
694 | unsigned ya = y * 0x100 + a; | ||
695 | if ( y < x * 2 ) | ||
696 | { | ||
697 | a = ya / x; | ||
698 | y = ya - a * x; | ||
699 | } | ||
700 | else | ||
701 | { | ||
702 | a = 255 - (ya - x * 0x200) / (256 - x); | ||
703 | y = x + (ya - x * 0x200) % (256 - x); | ||
704 | } | ||
705 | |||
706 | nz = (uint8_t) a; | ||
707 | a = (uint8_t) a; | ||
708 | |||
709 | goto loop; | ||
710 | } | ||
711 | |||
712 | /* 11. DECIMAL COMPENSATION COMMANDS */ | ||
713 | |||
714 | /* seem unused */ | ||
715 | /* case 0xDF: */ /* DAA */ | ||
716 | /* case 0xBE: */ /* DAS */ | ||
717 | |||
718 | /* 12. BRANCHING COMMANDS */ | ||
719 | |||
720 | case 0x2F: /* BRA rel */ | ||
721 | pc += (int8_t) data; | ||
722 | goto inc_pc_loop; | ||
723 | |||
724 | case 0x30: /* BMI */ | ||
725 | BRANCH( IS_NEG ) | ||
726 | |||
727 | case 0x10: /* BPL */ | ||
728 | BRANCH( !IS_NEG ) | ||
729 | |||
730 | case 0xB0: /* BCS */ | ||
731 | BRANCH( c & 0x100 ) | ||
732 | |||
733 | case 0x90: /* BCC */ | ||
734 | BRANCH( !(c & 0x100) ) | ||
735 | |||
736 | case 0x70: /* BVS */ | ||
737 | BRANCH( status & st_v ) | ||
738 | |||
739 | case 0x50: /* BVC */ | ||
740 | BRANCH( !(status & st_v) ) | ||
741 | |||
742 | case 0x03: /* BBS dp.bit,rel */ | ||
743 | case 0x23: | ||
744 | case 0x43: | ||
745 | case 0x63: | ||
746 | case 0x83: | ||
747 | case 0xA3: | ||
748 | case 0xC3: | ||
749 | case 0xE3: | ||
750 | pc++; | ||
751 | if ( (READ_DP( data ) >> (opcode >> 5)) & 1 ) | ||
752 | goto cbranch_taken_loop; | ||
753 | goto inc_pc_loop; | ||
754 | |||
755 | case 0x13: /* BBC dp.bit,rel */ | ||
756 | case 0x33: | ||
757 | case 0x53: | ||
758 | case 0x73: | ||
759 | case 0x93: | ||
760 | case 0xB3: | ||
761 | case 0xD3: | ||
762 | case 0xF3: | ||
763 | pc++; | ||
764 | if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) ) | ||
765 | goto cbranch_taken_loop; | ||
766 | goto inc_pc_loop; | ||
767 | |||
768 | case 0xDE: /* CBNE dp+X,rel */ | ||
769 | data = (uint8_t) (data + x); | ||
770 | /* fall through */ | ||
771 | case 0x2E: /* CBNE dp,rel */ | ||
772 | pc++; | ||
773 | if ( READ_DP( data ) != a ) | ||
774 | goto cbranch_taken_loop; | ||
775 | goto inc_pc_loop; | ||
776 | |||
777 | case 0xFE: /* DBNZ Y,rel */ | ||
778 | y = (uint8_t) (y - 1); | ||
779 | BRANCH( y ) | ||
780 | |||
781 | case 0x6E: { /* DBNZ dp,rel */ | ||
782 | pc++; | ||
783 | unsigned temp = READ_DP( data ) - 1; | ||
784 | WRITE_DP( (uint8_t) data, (uint8_t) temp ); | ||
785 | if ( temp ) | ||
786 | goto cbranch_taken_loop; | ||
787 | goto inc_pc_loop; | ||
788 | } | ||
789 | |||
790 | case 0x1F: /* JMP (abs+X) */ | ||
791 | SET_PC( READ_PC16( pc ) + x ); | ||
792 | /* fall through */ | ||
793 | case 0x5F: /* JMP abs */ | ||
794 | SET_PC( READ_PC16( pc ) ); | ||
795 | goto loop; | ||
796 | |||
797 | /* 13. SUB-ROUTINE CALL RETURN COMMANDS */ | ||
798 | |||
799 | case 0x0F:{/* BRK */ | ||
800 | check( 0 ); /* untested */ | ||
801 | PUSH16( GET_PC() + 1 ); | ||
802 | SET_PC( READ_PROG16( 0xFFDE ) ); /* vector address verified */ | ||
803 | int temp; | ||
804 | CALC_STATUS( temp ); | ||
805 | PUSH( temp ); | ||
806 | status = (status | st_b) & ~st_i; | ||
807 | goto loop; | ||
808 | } | ||
809 | |||
810 | case 0x4F: /* PCALL offset */ | ||
811 | PUSH16( GET_PC() + 1 ); | ||
812 | SET_PC( 0xFF00 + data ); | ||
813 | goto loop; | ||
814 | |||
815 | case 0x01: /* TCALL n */ | ||
816 | case 0x11: | ||
817 | case 0x21: | ||
818 | case 0x31: | ||
819 | case 0x41: | ||
820 | case 0x51: | ||
821 | case 0x61: | ||
822 | case 0x71: | ||
823 | case 0x81: | ||
824 | case 0x91: | ||
825 | case 0xA1: | ||
826 | case 0xB1: | ||
827 | case 0xC1: | ||
828 | case 0xD1: | ||
829 | case 0xE1: | ||
830 | case 0xF1: | ||
831 | PUSH16( GET_PC() ); | ||
832 | SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); | ||
833 | goto loop; | ||
834 | |||
835 | /* 14. STACK OPERATION COMMANDS */ | ||
836 | |||
837 | { | ||
838 | int temp; | ||
839 | case 0x7F: /* RET1 */ | ||
840 | temp = POP(); | ||
841 | SET_PC( POP() ); | ||
842 | pc += POP() << 8; | ||
843 | goto set_status; | ||
844 | case 0x8E: /* POP PSW */ | ||
845 | temp = POP(); | ||
846 | set_status: | ||
847 | SET_STATUS( temp ); | ||
848 | goto loop; | ||
849 | } | ||
850 | |||
851 | case 0x0D: { /* PUSH PSW */ | ||
852 | int temp; | ||
853 | CALC_STATUS( temp ); | ||
854 | PUSH( temp ); | ||
855 | goto loop; | ||
856 | } | ||
857 | |||
858 | case 0x2D: /* PUSH A */ | ||
859 | PUSH( a ); | ||
860 | goto loop; | ||
861 | |||
862 | case 0x4D: /* PUSH X */ | ||
863 | PUSH( x ); | ||
864 | goto loop; | ||
865 | |||
866 | case 0x6D: /* PUSH Y */ | ||
867 | PUSH( y ); | ||
868 | goto loop; | ||
869 | |||
870 | case 0xAE: /* POP A */ | ||
871 | a = POP(); | ||
872 | goto loop; | ||
873 | |||
874 | case 0xCE: /* POP X */ | ||
875 | x = POP(); | ||
876 | goto loop; | ||
877 | |||
878 | case 0xEE: /* POP Y */ | ||
879 | y = POP(); | ||
880 | goto loop; | ||
881 | |||
882 | /* 15. BIT OPERATION COMMANDS */ | ||
883 | |||
884 | case 0x02: /* SET1 */ | ||
885 | case 0x22: | ||
886 | case 0x42: | ||
887 | case 0x62: | ||
888 | case 0x82: | ||
889 | case 0xA2: | ||
890 | case 0xC2: | ||
891 | case 0xE2: | ||
892 | case 0x12: /* CLR1 */ | ||
893 | case 0x32: | ||
894 | case 0x52: | ||
895 | case 0x72: | ||
896 | case 0x92: | ||
897 | case 0xB2: | ||
898 | case 0xD2: | ||
899 | case 0xF2: { | ||
900 | data += dp; | ||
901 | int bit = 1 << (opcode >> 5); | ||
902 | int mask = ~bit; | ||
903 | if ( opcode & 0x10 ) | ||
904 | bit = 0; | ||
905 | WRITE( data, (READ( data ) & mask) | bit ); | ||
906 | goto inc_pc_loop; | ||
907 | } | ||
908 | |||
909 | case 0x0E: /* TSET1 abs */ | ||
910 | case 0x4E:{/* TCLR1 abs */ | ||
911 | data = READ_PC16( pc ); | ||
912 | pc += 2; | ||
913 | unsigned temp = READ( data ); | ||
914 | nz = temp & a; | ||
915 | temp &= ~a; | ||
916 | if ( !(opcode & 0x40) ) | ||
917 | temp |= a; | ||
918 | WRITE( data, temp ); | ||
919 | goto loop; | ||
920 | } | ||
921 | |||
922 | case 0x4A: /* AND1 C,mem.bit */ | ||
923 | c &= MEM_BIT(); | ||
924 | pc += 2; | ||
925 | goto loop; | ||
926 | |||
927 | case 0x6A: /* AND1 C,/mem.bit */ | ||
928 | check( 0 ); /* untested */ | ||
929 | c &= ~MEM_BIT(); | ||
930 | pc += 2; | ||
931 | goto loop; | ||
932 | |||
933 | case 0x0A: /* OR1 C,mem.bit */ | ||
934 | check( 0 ); /* untested */ | ||
935 | c |= MEM_BIT(); | ||
936 | pc += 2; | ||
937 | goto loop; | ||
938 | |||
939 | case 0x2A: /* OR1 C,/mem.bit */ | ||
940 | check( 0 ); /* untested */ | ||
941 | c |= ~MEM_BIT(); | ||
942 | pc += 2; | ||
943 | goto loop; | ||
944 | |||
945 | case 0x8A: /* EOR1 C,mem.bit */ | ||
946 | c ^= MEM_BIT(); | ||
947 | pc += 2; | ||
948 | goto loop; | ||
949 | |||
950 | case 0xEA: { /* NOT1 mem.bit */ | ||
951 | data = READ_PC16( pc ); | ||
952 | pc += 2; | ||
953 | unsigned temp = READ( data & 0x1FFF ); | ||
954 | temp ^= 1 << (data >> 13); | ||
955 | WRITE( data & 0x1FFF, temp ); | ||
956 | goto loop; | ||
957 | } | ||
958 | |||
959 | case 0xCA: { /* MOV1 mem.bit,C */ | ||
960 | data = READ_PC16( pc ); | ||
961 | pc += 2; | ||
962 | unsigned temp = READ( data & 0x1FFF ); | ||
963 | unsigned bit = data >> 13; | ||
964 | temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit); | ||
965 | WRITE( data & 0x1FFF, temp ); | ||
966 | goto loop; | ||
967 | } | ||
968 | |||
969 | case 0xAA: /* MOV1 C,mem.bit */ | ||
970 | c = MEM_BIT(); | ||
971 | pc += 2; | ||
972 | goto loop; | ||
973 | |||
974 | /* 16. PROGRAM STATUS FLAG OPERATION COMMANDS */ | ||
975 | |||
976 | case 0x60: /* CLRC */ | ||
977 | c = 0; | ||
978 | goto loop; | ||
979 | |||
980 | case 0x80: /* SETC */ | ||
981 | c = ~0; | ||
982 | goto loop; | ||
983 | |||
984 | case 0xED: /* NOTC */ | ||
985 | c ^= 0x100; | ||
986 | goto loop; | ||
987 | |||
988 | case 0xE0: /* CLRV */ | ||
989 | status &= ~(st_v | st_h); | ||
990 | goto loop; | ||
991 | |||
992 | case 0x20: /* CLRP */ | ||
993 | dp = 0; | ||
994 | goto loop; | ||
995 | |||
996 | case 0x40: /* SETP */ | ||
997 | dp = 0x100; | ||
998 | goto loop; | ||
999 | |||
1000 | case 0xA0: /* EI */ | ||
1001 | check( 0 ); /* untested */ | ||
1002 | status |= st_i; | ||
1003 | goto loop; | ||
1004 | |||
1005 | case 0xC0: /* DI */ | ||
1006 | check( 0 ); /* untested */ | ||
1007 | status &= ~st_i; | ||
1008 | goto loop; | ||
1009 | |||
1010 | /* 17. OTHER COMMANDS */ | ||
1011 | |||
1012 | case 0x00: /* NOP */ | ||
1013 | goto loop; | ||
1014 | |||
1015 | /*case 0xEF:*/ /* SLEEP */ | ||
1016 | /*case 0xFF:*/ /* STOP */ | ||
1017 | case 0xFF: | ||
1018 | c |= 1; /* force switch table to have 256 entries, | ||
1019 | hopefully helping optimizer */ | ||
1020 | } /* switch */ | ||
1021 | |||
1022 | /* unhandled instructions fall out of switch so emulator can catch them */ | ||
1023 | |||
1024 | out_of_time: | ||
1025 | /* undo partial execution of opcode */ | ||
1026 | spc_time_ -= this->cycle_table [*--pc]; | ||
1027 | { | ||
1028 | int temp; | ||
1029 | CALC_STATUS( temp ); | ||
1030 | this->r.status = (uint8_t) temp; | ||
1031 | } | ||
1032 | |||
1033 | this->r.pc = GET_PC(); | ||
1034 | this->r.sp = (uint8_t) GET_SP(); | ||
1035 | this->r.a = (uint8_t) a; | ||
1036 | this->r.x = (uint8_t) x; | ||
1037 | this->r.y = (uint8_t) y; | ||
1038 | |||
1039 | #if 0 | ||
1040 | EXIT_TIMER(cpu); | ||
1041 | #endif | ||
1042 | return spc_time_; | ||
1043 | } | ||
1044 | |||
1045 | void CPU_Init( THIS ) | ||
1046 | { | ||
1047 | ci->memcpy( this->cycle_table, cycle_table, sizeof cycle_table ); | ||
1048 | } | ||
1049 | |||