File Coverage

blib/lib/Acme/6502.pm
Criterion Covered Total %
statement 130 200 65.0
branch 5 14 35.7
condition 4 9 44.4
subroutine 63 78 80.7
pod 19 19 100.0
total 221 320 69.0


line stmt bran cond sub pod time code
1             package Acme::6502;
2              
3 3     3   90798 use warnings FATAL => 'all';
  3         7  
  3         149  
4 3     3   19 use strict;
  3         6  
  3         108  
5 3     3   17 use Carp;
  3         9  
  3         408  
6              
7             our $VERSION = '0.77';
8              
9             # CPU flags
10             use constant {
11 3         656 N => 0x80,
12             V => 0x40,
13             R => 0x20,
14             B => 0x10,
15             D => 0x08,
16             I => 0x04,
17             Z => 0x02,
18             C => 0x01
19 3     3   26 };
  3         4  
20              
21 3     3   18 use constant FLAGS => 'NVRBDIZC';
  3         4  
  3         179  
22              
23             # Other CPU constants
24             use constant {
25 3         191 STACK => 0x0100,
26             BREAK => 0xFFFE
27 3     3   17 };
  3         5  
28              
29             # Opcode to thunk into perlspace
30             use constant {
31 3         237 ESCAPE_OP => 0x0B,
32             ESCAPE_SIG => 0xAD
33 3     3   17 };
  3         5  
34              
35             BEGIN {
36 3     3   10 for my $reg ( qw(a x y s p pc) ) {
37 3     3   16 no strict 'refs';
  3         6228  
  3         615  
38 18         98 *{ __PACKAGE__ . "\::get_${reg}" } = sub {
39 1952     1952   440060 my $self = shift;
40 1952         17970 return $self->{ reg }->{ $reg };
41 18         60 };
42 18         20158 *{ __PACKAGE__ . "\::set_${reg}" } = sub {
43 1628     1628   154058 my ( $self, $v ) = @_;
44 1628         5270 $self->{ reg }->{ $reg } = $v;
45 18         98 };
46             }
47             }
48              
49             sub new {
50 253     253 1 396084 my $class = shift;
51 253         1918 my $self = bless { }, $class;
52              
53 253         1067 $self->_BUILD( @_ );
54              
55 253         1808 return $self;
56             }
57              
58             my @OP_CACHE;
59              
60             sub _BUILD {
61 253     253   588 my( $self, $args ) = @_;
62              
63 253   50     1912 $args ||= {};
64              
65 253         925278 $self->{ mem } = [ ( 0 ) x 65536 ];
66             $self->{ reg } = {
67 253         1804 map { $_ => 0 } qw( a x y s p pc )
  1518         5823  
68             };
69 253         1387 $self->{ os } = [ ];
70 253   50     2323 $self->{ jumptab } = $args->{ jumptab } || 0xFA00;
71 253         17401 $self->{ zn } = [ $self->Z, ( 0 ) x 127, ( $self->N ) x 128 ];
72              
73 253         2607 my $bad_inst = $self->can( '_bad_inst' );
74              
75 253 100       1480 @OP_CACHE = (
76             _inst( # 00 BRK
77             _push( '($pc + 1) >> 8', '($pc + 1)' ),
78             _push( '$p | B' ),
79             '$p = $p | I | B & ~D;',
80             _jmp_i( BREAK )
81             ),
82             _inst( _ora( _zpix() ) ), # 01 ORA (zp, x)
83             $bad_inst, # 02
84             $bad_inst, # 03
85             _inst( _tsb( _zp() ) ), # 04 TSB zp
86             _inst( _ora( _zp() ) ), # 05 ORA zp
87             _inst( _asl( _zp() ) ), # 06 ASL zp
88             $bad_inst, # 07
89             _inst( _push( '$p | R' ) ), # 08 PHP
90             _inst( _ora( _imm() ) ), # 09 ORA #imm
91             _inst( _asl( _acc() ) ), # 0A ASL A
92             $bad_inst, # 0B
93             _inst( _tsb( _abs() ) ), # 0C TSB zp
94             _inst( _ora( _abs() ) ), # 0D ORA abs
95             _inst( _asl( _abs() ) ), # 0E ASL abs
96             $bad_inst, # 0F BBR0 rel
97             _inst( _bfz( _rel(), N ) ), # 10 BPL rel
98             _inst( _ora( _zpiy() ) ), # 11 ORA (zp), y
99             _inst( _ora( _zpi() ) ), # 12 ORA (zp)
100             $bad_inst, # 13
101             _inst( _trb( _zpi() ) ), # 14 TRB (zp)
102             _inst( _ora( _zpx() ) ), # 15 ORA zp, x
103             _inst( _asl( _zpx() ) ), # 16 ASL zp, x
104             $bad_inst, # 17
105             _inst( '$p &= ~C;' ), # 18 CLC
106             _inst( _ora( _absy() ) ), # 19 ORA abs, y
107             _inst( _inc( _acc() ) ), # 1A INC A
108             $bad_inst, # 1B
109             _inst( _trb( _abs() ) ), # 1C TRB abs
110             _inst( _ora( _absx() ) ), # 1D ORA abs, x
111             _inst( _asl( _absx() ) ), # 1E ASL abs, x
112             $bad_inst, # 1F BBR1 rel
113             _inst( # 20 JSR
114             _push( '($pc + 1) >> 8', '($pc + 1)' ),
115             _jmp()
116             ),
117             _inst( _and( _zpix() ) ), # 21 AND (zp, x)
118             $bad_inst, # 22
119             $bad_inst, # 23
120             _inst( _bit( _zp() ) ), # 24 BIT zp
121             _inst( _and( _zp() ) ), # 25 AND zp
122             _inst( _rol( _zp() ) ), # 26 ROL zp
123             $bad_inst, # 27
124             _inst( _pop_p() ), # 28 PLP
125             _inst( _and( _imm() ) ), # 29 AND #imm
126             _inst( _rol( _acc() ) ), # 2A ROL A
127             $bad_inst, # 2B
128             _inst( _bit( _abs() ) ), # 2C BIT abs
129             _inst( _and( _abs() ) ), # 2D AND abs
130             _inst( _rol( _abs() ) ), # 2E ROL abs
131             $bad_inst, # 2F BBR2 rel
132             _inst( _bfnz( _rel(), N ) ), # 30 BMI rel
133             _inst( _and( _zpiy() ) ), # 31 AND (zp), y
134             _inst( _and( _zpi() ) ), # 32 AND (zp)
135             $bad_inst, # 33
136             _inst( _bit( _zpx() ) ), # 34 BIT zp, x
137             _inst( _and( _zpx() ) ), # 35 AND zp, x
138             _inst( _rol( _zpx() ) ), # 36 ROL zp, x
139             $bad_inst, # 37
140             _inst( '$p |= C;' ), # 38 SEC
141             _inst( _and( _absy() ) ), # 39 AND abs, y
142             _inst( _dec( _acc() ) ), # 3A DEC A
143             $bad_inst, # 3B
144             _inst( _bit( _absx() ) ), # 3C BIT abs, x
145             _inst( _and( _absx() ) ), # 3D AND abs, x
146             _inst( _rol( _absx() ) ), # 3E ROL abs, x
147             $bad_inst, # 3F BBR3 rel
148             _inst( _rti() ), # 40 RTI
149             _inst( _eor( _zpix() ) ), # 41 EOR (zp, x)
150             $bad_inst, # 42
151             $bad_inst, # 43
152             $bad_inst, # 44
153             _inst( _eor( _zp() ) ), # 45 EOR zp
154             _inst( _lsr( _zp() ) ), # 46 LSR zp
155             $bad_inst, # 47
156             _inst( _push( '$a' ) ), # 48 PHA
157             _inst( _eor( _imm() ) ), # 49 EOR imm
158             _inst( _lsr( _acc() ) ), # 4A LSR A
159             $bad_inst, # 4B
160             _inst( _jmp() ), # 4C JMP abs
161             _inst( _eor( _abs() ) ), # 4D EOR abs
162             _inst( _lsr( _abs() ) ), # 4E LSR abs
163             $bad_inst, # 4F BBR4 rel
164             _inst( _bfz( _rel(), V ) ), # 50 BVC rel
165             _inst( _eor( _zpiy() ) ), # 51 EOR (zp), y
166             _inst( _eor( _zpi() ) ), # 52 EOR (zp)
167             $bad_inst, # 53
168             $bad_inst, # 54
169             _inst( _eor( _zpx() ) ), # 55 EOR zp, x
170             _inst( _lsr( _zpx() ) ), # 56 LSR zp, x
171             $bad_inst, # 57
172             _inst( '$p &= ~I;' ), # 58 CLI
173             _inst( _eor( _absy() ) ), # 59 EOR abs, y
174             _inst( _push( '$y' ) ), # 5A PHY
175             $bad_inst, # 5B
176             $bad_inst, # 5C
177             _inst( _eor( _absx() ) ), # 5D EOR abs, x
178             _inst( _lsr( _absx() ) ), # 5E LSR abs, x
179             $bad_inst, # 5F BBR5 rel
180             _inst( _rts() ), # 60 RTS
181             _inst( _adc( _zpix() ) ), # 61 ADC (zp, x)
182             $bad_inst, # 62
183             $bad_inst, # 63
184             _inst( _sto( _zp(), '0' ) ), # 64 STZ zp
185             _inst( _adc( _zp() ) ), # 65 ADC zp
186             _inst( _ror( _zp() ) ), # 66 ROR zp
187             $bad_inst, # 67
188             _inst( _pop( '$a' ), _status( '$a' ) ), # 68 PLA
189             _inst( _adc( _imm() ) ), # 69 ADC #imm
190             _inst( _ror( _acc() ) ), # 6A ROR A
191             $bad_inst, # 6B
192             _inst( _jmpi() ), # 6C JMP (abs)
193             _inst( _adc( _abs() ) ), # 6D ADC abs
194             _inst( _ror( _abs() ) ), # 6E ROR abs
195             $bad_inst, # 6F BBR6 rel
196             _inst( _bfnz( _rel(), V ) ), # 70 BVS rel
197             _inst( _adc( _zpiy() ) ), # 71 ADC (zp), y
198             _inst( _adc( _zpi() ) ), # 72 ADC (zp)
199             $bad_inst, # 73
200             _inst( _sto( _zpx(), '0' ) ), # 74 STZ zp, x
201             _inst( _adc( _zpx() ) ), # 75 ADC zp, x
202             _inst( _ror( _zpx() ) ), # 76 ROR zp, x
203             $bad_inst, # 77
204             _inst( '$p |= I;' ), # 78 SEI
205             _inst( _adc( _absy() ) ), # 79 ADC abs, y
206             _inst( _pop( '$y' ), _status( '$y' ) ), # 7A PLY
207             $bad_inst, # 7B
208             _inst( _jmpix() ), # 7C JMP (abs, x)
209             _inst( _adc( _absx() ) ), # 7D ADC abs, x
210             _inst( _ror( _absx() ) ), # 7E ROR abs, x
211             $bad_inst, # 7F BBR7 rel
212             _inst( _bra( _rel() ) ), # 80 BRA rel
213             _inst( _sto( _zpix(), '$a' ) ), # 81 STA (zp, x)
214             $bad_inst, # 82
215             $bad_inst, # 83
216             _inst( _sto( _zp(), '$y' ) ), # 84 STY zp
217             _inst( _sto( _zp(), '$a' ) ), # 85 STA zp
218             _inst( _sto( _zp(), '$x' ) ), # 86 STX zp
219             $bad_inst, # 87
220             _inst( _dec( ( '', '$y' ) ) ), # 88 DEY
221             _inst( _bit( _imm() ) ), # 89 BIT #imm
222             _inst( '$a = $x;' . _status( '$a' ) ), # 8A TXA
223             $bad_inst, # 8B
224             _inst( _sto( _abs(), '$y' ) ), # 8C STY abs
225             _inst( _sto( _abs(), '$a' ) ), # 8D STA abs
226             _inst( _sto( _abs(), '$x' ) ), # 8E STX abs
227             $bad_inst, # 8F BBS0 rel
228             _inst( _bfz( _rel(), C ) ), # 90 BCC rel
229             _inst( _sto( _zpiy(), '$a' ) ), # 91 STA (zp), y
230             _inst( _sto( _zpi(), '$a' ) ), # 92 STA (zp)
231             $bad_inst, # 93
232             _inst( _sto( _zpx(), '$y' ) ), # 94 STY zp, x
233             _inst( _sto( _zpx(), '$a' ) ), # 95 STA zp, x
234             _inst( _sto( _zpy(), '$x' ) ), # 96 STX zp, y
235             $bad_inst, # 97
236             _inst( '$a = $y;' . _status( '$a' ) ), # 98 TYA
237             _inst( _sto( _absy(), '$a' ) ), # 99 STA abs, y
238             _inst( '$s = $x;' ), # 9A TXS
239             $bad_inst, # 9B
240             _inst( _sto( _abs(), '0' ) ), # 9C STZ abs
241             _inst( _sto( _absx(), '$a' ) ), # 9D STA abs, x
242             _inst( _sto( _absx(), '0' ) ), # 9E STZ abs, x
243             $bad_inst, # 9F BBS1 rel
244             _inst( _lod( _imm(), '$y' ) ), # A0 LDY #imm
245             _inst( _lod( _zpix(), '$a' ) ), # A1 LDA (zp, x)
246             _inst( _lod( _imm(), '$x' ) ), # A2 LDX #imm
247             $bad_inst, # A3
248             _inst( _lod( _zp(), '$y' ) ), # A4 LDY zp
249             _inst( _lod( _zp(), '$a' ) ), # A5 LDA zp
250             _inst( _lod( _zp(), '$x' ) ), # A6 LDX zp
251             $bad_inst, # A7
252             _inst( '$y = $a;' . _status( '$y' ) ), # A8 TAY
253             _inst( _lod( _imm(), '$a' ) ), # A9 LDA #imm
254             _inst( '$x = $a;' . _status( '$x' ) ), # AA TAX
255             $bad_inst, # AB
256             _inst( _lod( _abs(), '$y' ) ), # AC LDY abs
257             _inst( _lod( _abs(), '$a' ) ), # AD LDA abs
258             _inst( _lod( _abs(), '$x' ) ), # AE LDX abs
259             $bad_inst, # AF BBS2 rel
260             _inst( _bfnz( _rel(), C ) ), # B0 BCS rel
261             _inst( _lod( _zpiy(), '$a' ) ), # B1 LDA (zp), y
262             _inst( _lod( _zpi(), '$a' ) ), # B2 LDA (zp)
263             $bad_inst, # B3
264             _inst( _lod( _zpx(), '$y' ) ), # B4 LDY zp, x
265             _inst( _lod( _zpx(), '$a' ) ), # B5 LDA zp, x
266             _inst( _lod( _zpy(), '$x' ) ), # B6 LDX zp, y
267             $bad_inst, # B7
268             _inst( '$p &= ~V;' ), # B8 CLV
269             _inst( _lod( _absy(), '$a' ) ), # B9 LDA abs, y
270             _inst( '$x = $s;', _set_nz( '$x' ) ), # BA TSX
271             $bad_inst, # BB
272             _inst( _lod( _absx(), '$y' ) ), # BC LDY abs, x
273             _inst( _lod( _absx(), '$a' ) ), # BD LDA abs, x
274             _inst( _lod( _absy(), '$x' ) ), # BE LDX abs, y
275             $bad_inst, # BF BBS3 rel
276             _inst( _cmp( _imm(), '$y' ) ), # C0 CPY #imm
277             _inst( _cmp( _zpix(), '$a' ) ), # C1 CMP (zp, x)
278             $bad_inst, # C2
279             $bad_inst, # C3
280             _inst( _cmp( _zp(), '$y' ) ), # C4 CPY zp
281             _inst( _cmp( _zp(), '$a' ) ), # C5 CMP zp
282             _inst( _dec( _zp() ) ), # C6 DEC zp
283             $bad_inst, # C7
284             _inst( _inc( ( '', '$y' ) ) ), # C8 INY
285             _inst( _cmp( _imm(), '$a' ) ), # C9 CMP #imm
286             _inst( _dec( ( '', '$x' ) ) ), # CA DEX
287             $bad_inst, # CB
288             _inst( _cmp( _abs(), '$y' ) ), # CC CPY abs
289             _inst( _cmp( _abs(), '$a' ) ), # CD CMP abs
290             _inst( _dec( _abs() ) ), # CE DEC abs
291             $bad_inst, # CF BBS4 rel
292             _inst( _bfz( _rel(), Z ) ), # D0 BNE rel
293             _inst( _cmp( _zpiy(), '$a' ) ), # D1 CMP (zp), y
294             _inst( _cmp( _zpi(), '$a' ) ), # D2 CMP (zp)
295             $bad_inst, # D3
296             $bad_inst, # D4
297             _inst( _cmp( _zpx(), '$a' ) ), # D5 CMP zp, x
298             _inst( _dec( _zpx() ) ), # D6 DEC zp, x
299             $bad_inst, # D7
300             _inst( '$p &= ~D;' ), # D8 CLD
301             _inst( _cmp( _absy(), '$a' ) ), # D9 CMP abs, y
302             _inst( _push( '$x' ) ), # DA PHX
303             $bad_inst, # DB
304             $bad_inst, # DC
305             _inst( _cmp( _absx(), '$a' ) ), # DD CMP abs, x
306             _inst( _dec( _absx() ) ), # DE DEC abs, x
307             $bad_inst, # DF BBS5 rel
308             _inst( _cmp( _imm(), '$x' ) ), # E0 CPX #imm
309             _inst( _sbc( _zpix(), '$a' ) ), # E1 SBC (zp, x)
310             $bad_inst, # E2
311             $bad_inst, # E3
312             _inst( _cmp( _zp(), '$x' ) ), # E4 CPX zp
313             _inst( _sbc( _zp() ) ), # E5 SBC zp
314             _inst( _inc( _zp() ) ), # E6 INC zp
315             $bad_inst, # E7
316             _inst( _inc( ( '', '$x' ) ) ), # E8 INX
317             _inst( _sbc( _imm() ) ), # E9 SBC #imm
318             _inst(), # EA NOP
319             $bad_inst, # EB
320             _inst( _cmp( _abs(), '$x' ) ), # EC CPX abs
321             _inst( _sbc( _abs() ) ), # ED SBC abs
322             _inst( _inc( _abs() ) ), # EE INC abs
323             $bad_inst, # EF BBS6 rel
324             _inst( _bfnz( _rel(), Z ) ), # F0 BEQ rel
325             _inst( _sbc( _zpiy() ) ), # F1 SBC (zp), y
326             _inst( _sbc( _zpi() ) ), # F2 SBC (zp)
327             $bad_inst, # F3
328             $bad_inst, # F4
329             _inst( _sbc( _zpx() ) ), # F5 SBC zp, x
330             _inst( _inc( _zpx() ) ), # F6 INC zp, x
331             $bad_inst, # F7
332             _inst( '$p |= D;' ), # F8 SED
333             _inst( _sbc( _absy() ) ), # F9 SBC abs, y
334             _inst( _pop( '$x' ), _status( '$x' ) ), # FA PLX
335             $bad_inst, # FB
336             $bad_inst, # FC
337             _inst( _sbc( _absx() ) ), # FD SBC abs, x
338             _inst( _inc( _absx() ) ), # FE INC abs, x
339             $bad_inst, # FF BBS7 rel
340             ) if !@OP_CACHE;
341 253         21257 $self->{ ops } = [ @OP_CACHE ];
342              
343             confess "Escape handler opcode not available"
344 253 50       1635 unless $self->{ ops }->[ ESCAPE_OP ] == $bad_inst;
345              
346             # Patch in the OS escape op handler
347             $self->{ ops }->[ ESCAPE_OP ] = sub {
348 0     0   0 my $self = shift;
349 0 0       0 if ( $self->{ mem }->[ $self->{ reg }->{ pc } ] != ESCAPE_SIG ) {
350 0         0 $bad_inst->( $self );
351             }
352             else {
353 0         0 $self->{ reg }->{ pc } += 2;
354 0         0 $self->call_os( $self->{ mem }->[ $self->{ reg }->{ pc } - 1 ] );
355             }
356 253         3370 };
357             }
358              
359             sub set_jumptab {
360 0     0 1 0 my $self = shift;
361 0         0 $self->{ jumptab } = shift;
362             }
363              
364             sub get_state {
365 529     529 1 847 my $self = shift;
366 529         883 return @{ $self->{ reg } }{ qw( a x y s p pc ) };
  529         2447  
367             }
368              
369             sub get_xy {
370 0     0 1 0 my $self = shift;
371 0   0     0 return $self->get_x || ( $self->get_y << 8 );
372             }
373              
374             sub set_xy {
375 0     0 1 0 my $self = shift;
376 0         0 my $v = shift;
377 0         0 $self->set_x( $v & 0xFF );
378 0         0 $self->set_y( ( $v >> 8 ) & 0xFF );
379             }
380              
381             sub read_str {
382 0     0 1 0 my $self = shift;
383 0         0 my $addr = shift;
384 0         0 my $str = '';
385              
386 0         0 while ( $self->{ mem }->[ $addr ] != 0x0D ) {
387 0         0 $str .= chr( $self->{ mem }->[ $addr++ ] );
388             }
389              
390 0         0 return $str;
391             }
392              
393             sub read_chunk {
394 0     0 1 0 my $self = shift;
395 0         0 my ( $from, $to ) = @_;
396              
397 0         0 return pack( 'C*', @{ $self->{ mem } }[ $from .. $to - 1 ] );
  0         0  
398             }
399              
400             sub write_chunk {
401 0     0 1 0 my $self = shift;
402 0         0 my ( $addr, $chunk ) = @_;
403              
404 0         0 my $len = length( $chunk );
405 0         0 splice @{ $self->{ mem } }, $addr, $len, unpack( 'C*', $chunk );
  0         0  
406             }
407              
408             sub read_8 {
409 283     283 1 14595 my $self = shift;
410 283         414 my $addr = shift;
411              
412 283         2327 return $self->{ mem }->[ $addr ];
413             }
414              
415             sub write_8 {
416 235     235 1 129394 my $self = shift;
417 235         516 my( $addr, $val ) = @_;
418              
419 235         863 $self->{ mem }->[ $addr ] = $val;
420             }
421              
422             sub read_16 {
423 0     0 1 0 my $self = shift;
424 0         0 my $addr = shift;
425              
426 0         0 return $self->{ mem }->[ $addr ] | ( $self->{ mem }->[ $addr + 1 ] << 8 );
427             }
428              
429             sub write_16 {
430 0     0 1 0 my $self = shift;
431 0         0 my( $addr, $val ) = @_;
432              
433 0         0 $self->{ mem }->[ $addr ] = $val & 0xFF;
434 0         0 $self->{ mem }->[ $addr + 1 ] = ( $val >> 8 ) & 0xFF;
435             }
436              
437             sub read_32 {
438 0     0 1 0 my $self = shift;
439 0         0 my $addr = shift;
440              
441             return $self->{ mem }->[ $addr ]
442             | ( $self->{ mem }->[ $addr + 1 ] << 8 )
443             | ( $self->{ mem }->[ $addr + 2 ] << 16 )
444 0         0 | ( $self->{ mem }->[ $addr + 3 ] << 32 );
445             }
446              
447             sub write_32 {
448 0     0 1 0 my $self = shift;
449 0         0 my( $addr, $val ) = @_;
450              
451 0         0 $self->{ mem }->[ $addr ] = $val & 0xFF;
452 0         0 $self->{ mem }->[ $addr + 1 ] = ( $val >> 8 ) & 0xFF;
453 0         0 $self->{ mem }->[ $addr + 2 ] = ( $val >> 16 ) & 0xFF;
454 0         0 $self->{ mem }->[ $addr + 3 ] = ( $val >> 24 ) & 0xFF;
455             }
456              
457             sub poke_code {
458 490     490 1 177968 my $self = shift;
459 490         926 my $addr = shift;
460              
461 490         41651 $self->{ mem }->[ $addr++ ] = $_ for @_;
462             }
463              
464             sub load_rom {
465 0     0 1 0 my $self = shift;
466 0         0 my ( $f, $a ) = @_;
467              
468 0 0       0 open my $fh, '<', $f or croak "Can't read $f ($!)\n";
469 0         0 binmode $fh;
470 0         0 my $sz = -s $fh;
471 0 0       0 sysread $fh, my $buf, $sz or croak "Error reading $f ($!)\n";
472 0         0 close $fh;
473              
474 0         0 $self->write_chunk( $a, $buf );
475             }
476              
477             sub call_os {
478 0     0 1 0 croak "call_os() not supported";
479             }
480              
481             sub run {
482 529     529 1 23409 my $self = shift;
483 529         852 my $ic = shift;
484 529         784 my $cb = shift;
485              
486 529         2661 while ( $ic-- > 0 ) {
487 529         1753 my( $a, $x, $y, $s, $p, $pc ) = $self->get_state;
488 529 50       2132 $cb->( $pc, $self->{ mem }->[ $pc ], $a, $x, $y, $s, $p ) if defined $cb;
489 529         1451 $self->set_pc( $pc + 1 );
490 529         20719 $self->{ ops }->[ $self->{ mem }->[ $pc ] ]->( $self );
491             }
492             }
493              
494             sub make_vector {
495 0     0 1 0 my $self = shift;
496 0         0 my ( $call, $vec, $func ) = @_;
497              
498 0         0 $self->{ mem }->[ $call ] = 0x6C; # JMP (indirect)
499 0         0 $self->{ mem }->[ $call + 1 ] = $vec & 0xFF;
500 0         0 $self->{ mem }->[ $call + 2 ] = ( $vec >> 8 ) & 0xFF;
501              
502 0         0 my $jumptab = $self->{ jumptab };
503 0         0 my $addr = $jumptab;
504 0         0 $self->{ mem }->[ $jumptab++ ] = ESCAPE_OP;
505 0         0 $self->{ mem }->[ $jumptab++ ] = ESCAPE_SIG;
506 0         0 $self->{ mem }->[ $jumptab++ ] = $func;
507 0         0 $self->{ mem }->[ $jumptab++ ] = 0x60;
508              
509 0         0 $self->set_jumptab( $jumptab );
510              
511 0         0 $self->{ mem }->[ $vec ] = $addr & 0xFF;
512 0         0 $self->{ mem }->[ $vec + 1 ] = ( $addr >> 8 ) & 0xFF;
513             }
514              
515             sub _inst {
516 178     178   408 my $src = join( "\n", @_ );
517              
518             # registers
519 178         5147 $src =~ s{\$(a|x|y|s|p|pc)\b}{\$self->{reg}->{$1}}g;
520              
521             # memory and zn access
522 178         1880 $src =~ s{\$(mem|zn)\[}{\$self->{$1}->[}g;
523              
524 178         33350 my $cr = eval "sub { my \$self=shift; ${src} }";
525 178 50       462 confess "$@" if $@;
526 178         928 return $cr;
527             }
528              
529             sub _bad_inst {
530 0     0   0 my $self = shift;
531 0         0 my $pc = $self->get_pc;
532              
533             croak sprintf( "Bad instruction at &%04x (&%02x)\n",
534 0         0 $pc - 1, $self->{ mem }->[ $pc - 1 ] );
535             }
536              
537             # Functions that generate code fragments
538             sub _set_nz {
539             return
540 1     1   7 '$p &= ~(N|Z);' . 'if( '
541             . $_[0]
542             . ' & 0x80){ $p |= N }'
543             . 'elsif( '
544             . $_[0]
545             . ' == 0 ){ $p |= Z }';
546             }
547              
548             sub _push {
549 7     7   13 my $r = '';
550 7         18 for ( @_ ) {
551 9         36 $r
552             .= '$mem[STACK + $s] = ('
553             . $_
554             . ') & 0xFF; $s = ($s - 1) & 0xFF;' . "\n";
555             }
556 7         26 return $r;
557             }
558              
559             sub _pop {
560 8     8   13 my $r = '';
561 8         17 for ( @_ ) {
562 8         28 $r .= '$s = ($s + 1) & 0xFF; ' . $_ . ' = $mem[STACK + $s];' . "\n";
563             }
564 8         38 return $r;
565             }
566              
567             sub _pop_p {
568 1     1   5 return '$s = ($s + 1) & 0xFF; $p = $mem[STACK + $s] | R; $p &= ~B;'
569             . "\n";
570             }
571              
572             # Addressing modes return a list containing setup code, lvalue
573             sub _zpix {
574             return (
575 8     8   59 'my $ea = $mem[$pc++] + $x; '
576             . '$ea = $mem[$ea & 0xFF] | ($mem[($ea + 1) & 0xFF] << 8)' . ";\n",
577             '$mem[$ea]'
578             );
579             }
580              
581             sub _zpi {
582             return (
583 9     9   40 'my $ea = $mem[$pc++]; '
584             . '$ea = $mem[$ea & 0xFF] | ($mem[($ea + 1) & 0xFF] << 8)' . ";\n",
585             '$mem[$ea]'
586             );
587             }
588              
589             sub _zpiy {
590             return (
591 8     8   38 'my $ea = $mem[$pc++]; '
592             . '$ea = ($mem[$ea & 0xFF] | ($mem[($ea + 1) & 0xFF] << 8)) + $y'
593             . ";\n",
594             '$mem[$ea]'
595             );
596             }
597              
598             sub _zp {
599 23     23   94 return ( 'my $ea = $mem[$pc++];' . "\n", '$mem[$ea]' );
600             }
601              
602             sub _zpx {
603 18     18   75 return ( 'my $ea = ($mem[$pc++] + $x) & 0xFF;' . "\n", '$mem[$ea]' );
604             }
605              
606             sub _zpy {
607 2     2   8 return ( 'my $ea = ($mem[$pc++] + $y) & 0xFF;' . "\n", '$mem[$ea]' );
608             }
609              
610             sub _abs {
611 24     24   297 return ( 'my $ea = $mem[$pc] | ($mem[$pc+1] << 8); $pc += 2;' . "\n",
612             '$mem[$ea]' );
613             }
614              
615             sub _absx {
616             return (
617 17     17   71 'my $ea = ($mem[$pc] | ($mem[$pc+1] << 8)) + $x; $pc += 2;' . "\n",
618             '$mem[$ea]'
619             );
620             }
621              
622             sub _absy {
623             return (
624 9     9   37 'my $ea = ($mem[$pc] | ($mem[$pc+1] << 8)) + $y; $pc += 2;' . "\n",
625             '$mem[$ea]'
626             );
627             }
628              
629             sub _imm {
630 12     12   57 return ( 'my $v = $mem[$pc++];' . "\n", '$v' );
631             }
632              
633             sub _acc {
634 6     6   26 return ( '', '$a' );
635             }
636              
637             sub _rel {
638             # Doesn't return an lvalue
639 9     9   48 return ( 'my $t = $mem[$pc++];' . "\n",
640             '($pc + $t - (($t & 0x80) ? 0x100 : 0))' );
641             }
642              
643             sub _status {
644 120   100 120   444 my $reg = shift || '$a';
645 120         759 return '$p = ($p & ~(N | Z) | $zn[' . $reg . ']);' . "\n";
646             }
647              
648             sub _ora {
649 9     9   31 return $_[0] . '$a |= ' . $_[1] . ";\n" . _status();
650             }
651              
652             sub _and {
653 9     9   27 return $_[0] . '$a &= ' . $_[1] . ";\n" . _status();
654             }
655              
656             sub _eor {
657 9     9   33 return $_[0] . '$a ^= ' . $_[1] . ";\n" . _status();
658             }
659              
660             sub _bit {
661             return
662 5     5   29 $_[0]
663             . '$p = ($p & ~(N|V)) | ('
664             . $_[1]
665             . ' & (N|V));' . "\n"
666             . 'if (($a & '
667             . $_[1]
668             . ') == 0) { $p |= Z; } else { $p &= ~Z; }' . "\n";
669             }
670              
671             sub _asl {
672             return
673 5     5   22 $_[0]
674             . 'my $w = ('
675             . $_[1]
676             . ') << 1; ' . "\n"
677             . 'if ($w & 0x100) { $p |= C; $w &= ~0x100; } else { $p &= ~C; }'
678             . "\n"
679             . _status( '$w' )
680             . $_[1]
681             . ' = $w;' . "\n";
682             }
683              
684             sub _lsr {
685             return
686 5     5   23 $_[0]
687             . 'my $w = '
688             . $_[1] . ";\n"
689             . 'if (($w & 1) != 0) { $p |= C; } else { $p &= ~C; }' . "\n"
690             . '$w >>= 1;' . "\n"
691             . _status( '$w' )
692             . $_[1]
693             . ' = $w;' . "\n";
694             }
695              
696             sub _rol {
697             return
698 5     5   22 $_[0]
699             . 'my $w = ('
700             . $_[1]
701             . ' << 1) | ($p & C);' . "\n"
702             . 'if ($w >= 0x100) { $p |= C; $w -= 0x100; } else { $p &= ~C; };'
703             . "\n"
704             . _status( '$w' )
705             . $_[1]
706             . ' = $w;' . "\n";
707             }
708              
709             sub _ror {
710             return
711 5     5   23 $_[0]
712             . 'my $w = '
713             . $_[1]
714             . ' | (($p & C) << 8);' . "\n"
715             . 'if (($w & 1) != 0) { $p |= C; } else { $p &= ~C; }' . "\n"
716             . '$w >>= 1;' . "\n"
717             . _status( '$w' )
718             . $_[1]
719             . ' = $w;' . "\n";
720             }
721              
722             sub _sto {
723 18     18   79 return $_[0] . "$_[1] = $_[2];\n";
724             }
725              
726             sub _lod {
727 19     19   69 return $_[0] . "$_[2] = $_[1];\n" . _status( $_[2] );
728             }
729              
730             sub _cmp {
731             return
732 15     15   56 $_[0]
733             . 'my $w = '
734             . $_[2] . ' - '
735             . $_[1] . ";\n"
736             . 'if ($w < 0) { $w += 0x100; $p &= ~C; } else { $p |= C; }' . "\n"
737             . _status( '$w' );
738             }
739              
740             sub _tsb {
741 2     2   8 return 'croak "TSB not supported\n";' . "\n";
742             }
743              
744             sub _trb {
745 2     2   7 return 'croak "TRB not supported\n";' . "\n";
746             }
747              
748             sub _inc {
749             return
750 7     7   32 $_[0]
751             . $_[1] . ' = ('
752             . $_[1]
753             . ' + 1) & 0xFF;' . "\n"
754             . _status( $_[1] );
755             }
756              
757             sub _dec {
758             return
759 7     7   33 $_[0]
760             . $_[1] . ' = ('
761             . $_[1]
762             . ' + 0xFF) & 0xFF;' . "\n"
763             . _status( $_[1] );
764             }
765              
766             sub _adc {
767             return
768 9     9   54 $_[0]
769             . 'my $w = '
770             . $_[1] . ";\n"
771             . 'if ($p & D) {' . "\n"
772             . 'my $lo = ($a & 0x0F) + ($w & 0x0F) + ($p & C);' . "\n"
773             . 'if ($lo > 9) { $lo += 6; }' . "\n"
774             . 'my $hi = ($a >> 4) + ( $w >> 4) + ($lo > 15 ? 1 : 0);' . "\n"
775             . '$a = ($lo & 0x0F) | ($hi << 4);' . "\n"
776             . '$p = ($p & ~C) | ($hi > 15 ? C : 0);' . "\n"
777             . '} else {' . "\n"
778             . 'my $lo = $a + $w + ($p & C);' . "\n"
779             . '$p &= ~(N | V | Z | C);' . "\n"
780             . '$p |= (~($a ^ $w) & ($a ^ $lo) & 0x80 ? V : 0) | ($lo & 0x100 ? C : 0);'
781             . "\n"
782             . '$a = $lo & 0xFF;' . "\n"
783             . _status() . '}' . "\n";
784             }
785              
786             sub _sbc {
787             return
788 9     9   63 $_[0]
789             . 'my $w = '
790             . $_[1] . ";\n"
791             . 'if ($p & D) {' . "\n"
792             . 'my $lo = ($a & 0x0F) - ($w & 0x0F) - (~$p & C);' . "\n"
793             . 'if ($lo & 0x10) { $lo -= 6; }' . "\n"
794             . 'my $hi = ($a >> 4) - ($w >> 4) - (($lo & 0x10) >> 4);' . "\n"
795             . 'if ($hi & 0x10) { $hi -= 6; }' . "\n"
796             . '$a = ($lo & 0x0F) | ($hi << 4);' . "\n"
797             . '$p = ($p & ~C) | ($hi > 15 ? 0 : C);' . "\n"
798             . '} else {' . "\n"
799             . 'my $lo = $a - $w - (~$p & C);' . "\n"
800             . '$p &= ~(N | V | Z | C);' . "\n"
801             . '$p |= (($a ^ $w) & ($a ^ $lo) & 0x80 ? V : 0) | ($lo & 0x100 ? 0 : C);'
802             . "\n"
803             . '$a = $lo & 0xFF;' . "\n"
804             . _status() . '}' . "\n";
805             }
806              
807             sub _bra {
808 1     1   8 return $_[0] . '$pc = ' . $_[1] . ";\n";
809             }
810              
811             sub _bfz {
812             return
813 4     4   29 $_[0]
814             . 'if (($p & '
815             . $_[2]
816             . ') == 0) { $pc = '
817             . $_[1] . '; }' . "\n";
818             }
819              
820             sub _bfnz {
821             return
822 4     4   28 $_[0]
823             . 'if (($p & '
824             . $_[2]
825             . ') != 0) { $pc = '
826             . $_[1] . '; }' . "\n";
827             }
828              
829             sub _jmp_i {
830 4     4   9 my $a = shift;
831 4         27 return '$pc = $mem[' . $a . '] | ($mem[' . $a . ' + 1] << 8);' . "\n";
832             }
833              
834             sub _jmp_i_bug {
835 1     1   2 my $a = shift;
836              
837             # this should emulate a page boundary bug:
838             # JMP 0x80FF fetches from 0x80FF and 0x8000
839             # instead of 0x80FF and 0x8100
840 1         6 my $b = "($a & 0xFF00) | (($a + 1) & 0xFF)";
841 1         8 return '$pc = $mem[' . $a . '] | ($mem[' . $b . '] << 8);' . "\n";
842             }
843              
844             sub _jmp {
845 2     2   7 return _jmp_i( '$pc' );
846             }
847              
848             sub _jmpi {
849 1     1   6 return 'my $w = $mem[$pc] | ($mem[$pc + 1] << 8); '
850             . _jmp_i_bug( '$w' );
851             }
852              
853             sub _jmpix {
854 1     1   5 return 'my $w = ($mem[$pc] | ($mem[$pc + 1] << 8)) + $x; '
855             . _jmp_i( '$w' );
856             }
857              
858             sub _rti {
859             return
860 1     1   4 _pop( '$p' )
861             . '$p |= R;'
862             . 'my ($lo, $hi); '
863             . _pop( '$lo' )
864             . _pop( '$hi' )
865             . '$pc = $lo | ($hi << 8);' . "\n";
866             }
867              
868             sub _rts {
869             return
870 1     1   4 'my ($lo, $hi); '
871             . _pop( '$lo' )
872             . _pop( '$hi' )
873             . '$pc = ($lo | ($hi << 8)) + 1;' . "\n";
874             }
875              
876             1;
877             __END__