/[VRac]/ACME-6502/lib/ACME/6502.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /ACME-6502/lib/ACME/6502.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6 - (hide annotations)
Sun Jul 29 13:58:23 2007 UTC (16 years, 11 months ago) by dpavlin
File size: 33878 byte(s)
load_row with negative offset will skip so many bytes from beginning
1 dpavlin 4 package ACME::6502;
2 dpavlin 3
3     use warnings FATAL => 'all';
4     use strict;
5     use Carp;
6 dpavlin 5 use TieMem;
7 dpavlin 3 use Class::Std;
8    
9     use version; our $VERSION = qv('0.0.6');
10    
11     # CPU flags
12     use constant {
13     N => 0x80,
14     V => 0x40,
15     R => 0x20,
16     B => 0x10,
17     D => 0x08,
18     I => 0x04,
19     Z => 0x02,
20     C => 0x01
21     };
22    
23     use constant FLAGS => 'NVRBDIZC';
24    
25     # Other CPU constants
26     use constant {
27     STACK => 0x0100,
28     BREAK => 0xFFFE
29     };
30    
31     # Opcode to thunk into perlspace
32     use constant {
33     ESCAPE_OP => 0x0B,
34     ESCAPE_SIG => 0xAD
35     };
36    
37     my %cpu : ATTR;
38    
39 dpavlin 5
40     =head2 BUILD
41    
42     =head2 AUTOMETHOD
43    
44     =cut
45    
46 dpavlin 3 sub BUILD {
47     my ($self, $id, $args) = @_;
48    
49 dpavlin 5 confess "no vram?" unless $args->{vram};
50    
51     my @mem;
52     my @ram = (0) x 65536;
53     tie @mem, 'TieMem', $args;
54     @ram = ();
55    
56 dpavlin 3 my @os;
57     my ($a, $x, $y, $s, $p, $pc) = (0) x 6;
58 dpavlin 5 my $mmap;
59 dpavlin 3
60     # Page used to allocate vector thunks
61     my $jumptab = $args->{jumptab} || 0xFA00;
62    
63     my $bad_inst = sub {
64     croak sprintf("Bad instruction at &%04x (&%02x)\n",
65     $pc - 1, $mem[$pc - 1]);
66     };
67    
68     my @zn = (Z, (0) x 127, (N) x 128);
69    
70     my $inst = sub {
71     my $src = join("\n", @_);
72     my $cr = eval "sub { @_ }";
73     confess "$@" if $@;
74     return $cr;
75     };
76    
77     my @decode = (
78     $inst->( # 00 BRK
79     _push('($pc + 1) >> 8', '($pc + 1)'),
80     _push('$p | B'),
81     '$p = $p | I & ~D;',
82     _jmp_i(BREAK)
83     ),
84     $inst->(_ora(_zpix())), # 01 ORA (zp, x)
85     $bad_inst, # 02
86     $bad_inst, # 03
87     $inst->(_tsb(_zp())), # 04 TSB zp
88     $inst->(_ora(_zp())), # 05 ORA zp
89     $inst->(_asl(_zp())), # 06 ASL zp
90     $bad_inst, # 07
91     $inst->(_push('$p | R')), # 08 PHP
92     $inst->(_ora(_imm())), # 09 ORA #imm
93     $inst->(_asl(_acc())), # 0A ASL A
94     $bad_inst, # 0B
95     $inst->(_tsb(_abs())), # 0C TSB zp
96     $inst->(_ora(_abs())), # 0D ORA abs
97     $inst->(_asl(_abs())), # 0E ASL abs
98     $bad_inst, # 0F BBR0 rel
99     $inst->(_bfz(_rel(), N)), # 10 BPL rel
100     $inst->(_ora(_zpiy())), # 11 ORA (zp), y
101     $inst->(_ora(_zpi())), # 12 ORA (zp)
102     $bad_inst, # 13
103     $inst->(_trb(_zpi())), # 14 TRB (zp)
104     $inst->(_ora(_zpx())), # 15 ORA zp, x
105     $inst->(_asl(_zpx())), # 16 ASL zp, x
106     $bad_inst, # 17
107     $inst->('$p &= ~C;'), # 18 CLC
108     $inst->(_ora(_absy())), # 19 ORA abs, y
109     $inst->(_inc(_acc())), # 1A INC A
110     $bad_inst, # 1B
111     $inst->(_trb(_abs())), # 1C TRB abs
112     $inst->(_ora(_absx())), # 1D ORA abs, x
113     $inst->(_asl(_absx())), # 1E ASL abs, x
114     $bad_inst, # 1F BBR1 rel
115     $inst->( # 20 JSR
116     _push('($pc + 1) >> 8', '($pc + 1)'),
117     _jmp()
118     ),
119     $inst->(_and(_zpix())), # 21 AND (zp, x)
120     $bad_inst, # 22
121     $bad_inst, # 23
122     $inst->(_bit(_zp())), # 24 BIT zp
123     $inst->(_and(_zp())), # 25 AND zp
124     $inst->(_rol(_zp())), # 26 ROL zp
125     $bad_inst, # 27
126     $inst->(_pop_p()), # 28 PLP
127     $inst->(_and(_imm())), # 29 AND #imm
128     $inst->(_rol(_acc())), # 2A ROL A
129     $bad_inst, # 2B
130     $inst->(_bit(_abs())), # 2C BIT abs
131     $inst->(_and(_abs())), # 2D AND abs
132     $inst->(_rol(_abs())), # 2E ROL abs
133     $bad_inst, # 2F BBR2 rel
134     $inst->(_bfnz(_rel(), N)), # 30 BPL rel
135     $inst->(_and(_zpiy())), # 31 AND (zp), y
136     $inst->(_and(_zpi())), # 32 AND (zp)
137     $bad_inst, # 33
138     $inst->(_bit(_zpx())), # 34 BIT zp, x
139     $inst->(_and(_zpx())), # 35 AND zp, x
140     $inst->(_rol(_zpx())), # 36 ROL zp, x
141     $bad_inst, # 37
142     $inst->('$p |= C;'), # 38 SEC
143     $inst->(_and(_absy())), # 39 AND abs, y
144     $inst->(_dec(_acc())), # 3A DEC A
145     $bad_inst, # 3B
146     $inst->(_bit(_absx())), # 3C BIT abs, x
147     $inst->(_and(_absx())), # 3D AND abs, x
148     $inst->(_rol(_absx())), # 3E ROL abs, x
149     $bad_inst, # 3F BBR3 rel
150     $inst->(_pop('$p'), _rts()), # 40 RTI
151     $inst->(_eor(_zpix())), # 41 EOR (zp, x)
152     $bad_inst, # 42
153     $bad_inst, # 43
154     $bad_inst, # 44
155     $inst->(_eor(_zp())), # 45 EOR zp
156     $inst->(_lsr(_zp())), # 46 LSR zp
157     $bad_inst, # 47
158     $inst->(_push('$a')), # 48 PHA
159     $inst->(_eor(_imm())), # 49 EOR imm
160     $inst->(_lsr(_acc())), # 4A LSR A
161     $bad_inst, # 4B
162     $inst->(_jmp()), # 4C JMP abs
163     $inst->(_eor(_abs())), # 4D EOR abs
164     $inst->(_lsr(_abs())), # 4E LSR abs
165     $bad_inst, # 4F BBR4 rel
166     $inst->(_bfz(_rel(), V)), # 50 BVC rel
167     $inst->(_eor(_zpiy())), # 51 EOR (zp), y
168     $inst->(_eor(_zpi())), # 52 EOR (zp)
169     $bad_inst, # 53
170     $bad_inst, # 54
171     $inst->(_eor(_zpx())), # 55 EOR zp, x
172     $inst->(_lsr(_zpx())), # 56 LSR zp, x
173     $bad_inst, # 57
174     $inst->('$p &= ~I;'), # 58 CLI
175     $inst->(_eor(_absy())), # 59 EOR abs, y
176     $inst->(_push('$y')), # 5A PHY
177     $bad_inst, # 5B
178     $bad_inst, # 5C
179     $inst->(_eor(_absx())), # 5D EOR abs, x
180     $inst->(_lsr(_absx())), # 5E LSR abs, x
181     $bad_inst, # 5F BBR5 rel
182     $inst->(_rts()), # 60 RTS
183     $inst->(_adc(_zpx())), # 61 ADC zp, x
184     $bad_inst, # 62
185     $bad_inst, # 63
186     $inst->(_sto(_zp(), '0')), # 64 STZ zp
187     $inst->(_adc(_zp())), # 65 ADC zp
188     $inst->(_ror(_zp())), # 66 ROR zp
189     $bad_inst, # 67
190     $inst->(_pop('$a'), _status('$a')), # 68 PLA
191     $inst->(_adc(_imm())), # 69 ADC #imm
192     $inst->(_ror(_acc())), # 6A ROR A
193     $bad_inst, # 6B
194     $inst->(_jmpi()), # 6C JMP (abs)
195     $inst->(_adc(_abs())), # 6D ADC abs
196     $inst->(_ror(_abs())), # 6E ROR abs
197     $bad_inst, # 6F BBR6 rel
198     $inst->(_bfnz(_rel(), V)), # 70 BVS rel
199     $inst->(_adc(_zpiy())), # 71 ADC (zp), y
200     $inst->(_adc(_zpi())), # 72 ADC (zp)
201     $bad_inst, # 73
202     $inst->(_sto(_zpx(), '0')), # 74 STZ zp, x
203     $inst->(_adc(_zpx())), # 75 ADC zp, x
204     $inst->(_adc(_zpx())), # 76 ROR zp, x
205     $bad_inst, # 77
206     $inst->('$p |= I;'), # 78 STI
207     $inst->(_adc(_absy())), # 79 ADC abs, y
208     $inst->(_pop('$y'), _status('$y')), # 7A PLY
209     $bad_inst, # 7B
210     $inst->(_jmpix()), # 7C JMP (abs, x)
211     $inst->(_adc(_absx())), # 7D ADC abs, x
212     $inst->(_ror(_absx())), # 7E ROR abs, x
213     $bad_inst, # 7F BBR7 rel
214     $inst->(_bra(_rel())), # 80 BRA rel
215     $inst->(_sto(_zpix(), '$a')), # 81 STA (zp, x)
216     $bad_inst, # 82
217     $bad_inst, # 83
218     $inst->(_sto(_zp(), '$y')), # 84 STY zp
219     $inst->(_sto(_zp(), '$a')), # 85 STA zp
220     $inst->(_sto(_zp(), '$x')), # 86 STX zp
221     $bad_inst, # 87
222     $inst->(_dec(('', '$y'))), # 88 DEY
223     $inst->(_bit(_imm())), # 89 BIT #imm
224     $inst->('$a = $x;' . _status('$a')), # 8A TXA
225     $bad_inst, # 8B
226     $inst->(_sto(_abs(), '$y')), # 8C STY abs
227     $inst->(_sto(_abs(), '$a')), # 8D STA abs
228     $inst->(_sto(_abs(), '$x')), # 8E STX abs
229     $bad_inst, # 8F BBS0 rel
230     $inst->(_bfz(_rel(), C)), # 90 BCC rel
231     $inst->(_sto(_zpiy(), '$a')), # 91 STA (zp), y
232     $inst->(_sto(_zpi(), '$a')), # 92 STA (zp)
233     $bad_inst, # 93
234     $inst->(_sto(_zpx(), '$y')), # 94 STY zp, x
235     $inst->(_sto(_zpx(), '$a')), # 95 STA zp, x
236     $inst->(_sto(_zpy(), '$x')), # 96 STX zp, y
237     $bad_inst, # 97
238     $inst->('$a = $y;' . _status('$a')), # 98 TYA
239     $inst->(_sto(_absy(), '$a')), # 99 STA abs, y
240     $inst->('$s = $x;'), # 9A TXS
241     $bad_inst, # 9B
242     $inst->(_sto(_abs(), '0')), # 9C STZ abs
243     $inst->(_sto(_absx(), '$a')), # 9D STA abs, x
244     $inst->(_sto(_absx(), '0')), # 9E STZ abs, x
245     $bad_inst, # 9F BBS1 rel
246     $inst->(_lod(_imm(), '$y')), # A0 LDY #imm
247     $inst->(_lod(_zpix(), '$a')), # A1 LDA (zp, x)
248     $inst->(_lod(_imm(), '$x')), # A2 LDX #imm
249     $bad_inst, # A3
250     $inst->(_lod(_zp(), '$y')), # A4 LDY zp
251     $inst->(_lod(_zp(), '$a')), # A5 LDA zp
252     $inst->(_lod(_zp(), '$x')), # A6 LDX zp
253     $bad_inst, # A7
254     $inst->('$y = $a;' . _status('$y')), # A8 TAY
255     $inst->(_lod(_imm(), '$a')), # A9 LDA #imm
256     $inst->('$x = $a;' . _status('$x')), # AA TAX
257     $bad_inst, # AB
258     $inst->(_lod(_abs(), '$y')), # AC LDY abs
259     $inst->(_lod(_abs(), '$a')), # AD LDA abs
260     $inst->(_lod(_abs(), '$x')), # AE LDX abs
261     $bad_inst, # AF BBS2 rel
262     $inst->(_bfnz(_rel(), C)), # B0 BCS rel
263     $inst->(_lod(_zpiy(), '$a')), # B1 LDA (zp), y
264     $inst->(_lod(_zpi(), '$a')), # B2 LDA (zp)
265     $bad_inst, # B3
266     $inst->(_lod(_zpx(), '$y')), # B4 LDY zp, x
267     $inst->(_lod(_zpx(), '$a')), # B5 LDA zp, x
268     $inst->(_lod(_zpy(), '$x')), # B6 LDX zp, y
269     $bad_inst, # B7
270     $inst->('$p &= ~V;'), # B8 CLV
271     $inst->(_lod(_absy(), '$a')), # B9 LDA abs, y
272     $inst->('$x = $s;'), # BA TSX
273     $bad_inst, # BB
274     $inst->(_lod(_absx(), '$y')), # BC LDY abs, x
275     $inst->(_lod(_absx(), '$a')), # BD LDA abs, x
276     $inst->(_lod(_absy(), '$x')), # BE LDX abs, y
277     $bad_inst, # BF BBS3 rel
278     $inst->(_cmp(_imm(), '$y')), # C0 CPY #imm
279     $inst->(_cmp(_zpix(), '$a')), # C1 CMP (zp, x)
280     $bad_inst, # C2
281     $bad_inst, # C3
282     $inst->(_cmp(_zp(), '$y')), # C4 CPY zp
283     $inst->(_cmp(_zp(), '$a')), # C5 CMP zp
284     $inst->(_dec(_zp())), # C6 DEC zp
285     $bad_inst, # C7
286     $inst->(_inc(('', '$y'))), # C8 INY
287     $inst->(_cmp(_imm(), '$a')), # C9 CMP #imm
288     $inst->(_dec(('', '$x'))), # CA DEX
289     $bad_inst, # CB
290     $inst->(_cmp(_abs(), '$y')), # CC CPY abs
291     $inst->(_cmp(_abs(), '$a')), # CD CMP abs
292     $inst->(_dec(_abs())), # CE DEC abs
293     $bad_inst, # CF BBS4 rel
294     $inst->(_bfz(_rel(), Z)), # D0 BNE rel
295     $inst->(_cmp(_zpiy(), '$a')), # D1 CMP (zp), y
296     $inst->(_cmp(_zpi(), '$a')), # D2 CMP (zp)
297     $bad_inst, # D3
298     $bad_inst, # D4
299     $inst->(_cmp(_zpx(), '$a')), # D5 CMP zp, x
300     $inst->(_dec(_zpx())), # D6 DEC zp, x
301     $bad_inst, # D7
302     $inst->('$p &= ~D;'), # D8 CLD
303     $inst->(_cmp(_absy(), '$a')), # D9 CMP abs, y
304     $inst->(_push('$x')), # DA PHX
305     $bad_inst, # DB
306     $bad_inst, # DC
307     $inst->(_cmp(_absx(), '$a')), # DD CMP abs, x
308     $inst->(_dec(_absx())), # DE DEC abs, x
309     $bad_inst, # DF BBS5 rel
310     $inst->(_cmp(_imm(), '$x')), # E0 CPX #imm
311     $inst->(_sbc(_zpix(), '$a')), # E1 SBC (zp, x)
312     $bad_inst, # E2
313     $bad_inst, # E3
314     $inst->(_cmp(_zp(), '$x')), # E4 CPX zp
315     $inst->(_sbc(_zp())), # E5 SBC zp
316     $inst->(_inc(_zp())), # E6 INC zp
317     $bad_inst, # E7
318     $inst->(_inc(('', '$x'))), # E8 INX
319     $inst->(_sbc(_imm())), # E9 SBC #imm
320     $inst->(), # EA NOP
321     $bad_inst, # EB
322     $inst->(_cmp(_abs(), '$x')), # EC CPX abs
323     $inst->(_sbc(_abs())), # ED SBC abs
324     $inst->(_inc(_abs())), # EE INC abs
325     $bad_inst, # EF BBS6 rel
326     $inst->(_bfnz(_rel(), Z)), # F0 BEQ rel
327     $inst->(_sbc(_zpiy())), # F1 SBC (zp), y
328     $inst->(_sbc(_zpi())), # F2 SBC (zp)
329     $bad_inst, # F3
330     $bad_inst, # F4
331     $inst->(_sbc(_zpx())), # F5 SBC zp, x
332     $inst->(_inc(_zpx())), # F6 INC zp, x
333     $bad_inst, # F7
334     $inst->('$p |= D;'), # F8 SED
335     $inst->(_sbc(_absy())), # F9 SBC abs, y
336     $inst->(_pop('$x'), _status('$x')), # FA PLX
337     $bad_inst, # FB
338     $bad_inst, # FC
339     $inst->(_sbc(_absx())), # FD SBC abs, x
340     $inst->(_inc(_absx())), # FE INC abs, x
341     $bad_inst # FF BBS7 rel
342     );
343    
344     confess "Escape handler opcode not available"
345     unless $decode[ESCAPE_OP] == $bad_inst;
346    
347     # Patch in the OS escape op handler
348     $decode[ESCAPE_OP] = sub {
349     if ($mem[$pc] != ESCAPE_SIG) {
350     $bad_inst->();
351     } else {
352     $pc += 2;
353     $self->call_os($mem[$pc - 1]);
354     }
355     };
356    
357     $cpu{$id} = {
358     set_jumptab => sub {
359     $jumptab = shift;
360     },
361    
362     read_str => sub {
363     my $addr = shift;
364     my $str = '';
365     while ($mem[$addr] != 0x0D) {
366     $str .= chr($mem[$addr++]);
367     }
368     return $str;
369     },
370    
371     read_chunk => sub {
372     my ($from, $to) = @_;
373     return pack('C*', @mem[$from .. $to - 1]);
374     },
375    
376     write_chunk => sub {
377     my ($addr, $chunk) = @_;
378     my $len = length($chunk);
379     splice @mem, $addr, $len, unpack('C*', $chunk);
380     },
381    
382     read_8 => sub {
383     my $addr = shift;
384     return $mem[$addr];
385     },
386    
387     write_8 => sub {
388     my ($addr, $val) = @_;
389     $mem[$addr] = $val;
390     },
391    
392     read_16 => sub {
393     my $addr = shift;
394     return $mem[$addr] | ($mem[$addr + 1] << 8);
395     },
396    
397     write_16 => sub {
398     my ($addr, $val) = @_;
399     $mem[$addr + 0] = ($val >> 0) & 0xFF;
400     $mem[$addr + 1] = ($val >> 8) & 0xFF;
401     },
402    
403     read_32 => sub {
404     my $addr = shift;
405     return $mem[$addr] | ($mem[$addr + 1] << 8) |
406     ($mem[$addr + 2] << 16) | ($mem[$addr + 3] << 32);
407     },
408    
409     write_32 => sub {
410     my ($addr, $val) = @_;
411     $mem[$addr + 0] = ($val >> 0) & 0xFF;
412     $mem[$addr + 1] = ($val >> 8) & 0xFF;
413     $mem[$addr + 2] = ($val >> 16) & 0xFF;
414     $mem[$addr + 3] = ($val >> 24) & 0xFF;
415     },
416    
417     run => sub {
418     my $ic = shift;
419     my $cb = shift;
420     if (defined($cb)) {
421     while ($ic-- > 0) {
422     $cb->($pc, $mem[$pc], $a, $x, $y, $s, $p);
423     $decode[$mem[$pc++]]->();
424     }
425     } else {
426     while ($ic-- > 0) {
427 dpavlin 5 my $op = $mem[$pc];
428     # printf "pc: %04x %s\n", $pc, $op;
429     $pc++;
430     $decode[$op]->();
431 dpavlin 3 }
432     }
433     },
434    
435     load_rom => sub {
436     my ($f, $a) = @_;
437     open my $fh, '<', $f or croak "Can't read $f ($!)\n";
438     binmode $fh;
439     my $sz = -s $fh;
440     sysread $fh, my $buf, $sz
441     or croak "Error reading $f ($!)\n";
442 dpavlin 6 if ( $a < 0 ) {
443     $buf = substr($buf, -$a);
444     $a = 0;
445     }
446 dpavlin 3 $self->write_chunk($a, $buf);
447     },
448    
449     poke_code => sub {
450     my $addr = shift;
451     $mem[$addr++] = $_ for @_;
452     },
453    
454     make_vector => sub {
455     my ($call, $vec, $func) = @_;
456     $mem[$call + 0] = 0x6C; # JMP (indirect)
457     $mem[$call + 1] = $vec & 0xFF;
458     $mem[$call + 2] = ($vec >> 8) & 0xFF;
459    
460     my $addr = $jumptab;
461     $mem[$jumptab++] = ESCAPE_OP;
462     $mem[$jumptab++] = ESCAPE_SIG;
463     $mem[$jumptab++] = $func;
464     $mem[$jumptab++] = 0x60;
465    
466     $mem[$vec + 0] = $addr & 0xFF;
467     $mem[$vec + 1] = ($addr >> 8) & 0xFF;
468     },
469    
470     get_state => sub {
471     return ($a, $x, $y, $s, $p, $pc);
472     },
473    
474     get_xy => sub {
475     return $x || ($y << 8);
476     },
477    
478     set_xy => sub {
479     my $v = shift;
480     $x = $v & 0xFF;
481     $y = ($v >> 8) & 0xFF;
482     },
483    
484     decode_flags => sub {
485     my $f = shift;
486     my $b = 0x80;
487     my $n = FLAGS;
488     my $desc = '';
489     while ($n) {
490     $desc .= ($f & $b) ? substr($n, 0, 1) : '-';
491     $n = substr($n, 1);
492     $b >>= 1;
493     }
494     return $desc;
495 dpavlin 5 },
496    
497     mmap => sub {
498     my ( $addr, $sub ) = @_;
499     confess "callback not sub" unless ref($sub) eq 'CODE';
500     $mmap->{$addr} = $sub;
501     }
502 dpavlin 3 };
503    
504     # Generate register accessors.
505     for my $reg (qw(a x y s p pc)) {
506     $cpu{$id}->{"get_$reg"} = eval "sub { return \$$reg; }";
507     $cpu{$id}->{"set_$reg"} = eval "sub { \$$reg = \$_[0]; }";
508     }
509     }
510    
511     sub AUTOMETHOD {
512     my ($self, $id, @args) = @_;
513     my $methname = $_;
514    
515     if (exists($cpu{$id}->{$methname})) {
516     return sub {
517     return $cpu{$id}->{$methname}->(@args);
518     }
519     }
520    
521     return;
522     }
523    
524     sub call_os {
525     my $self = shift;
526     my $id = ident($self);
527     croak "call_os() not supported";
528     }
529    
530     # Functions that generate code fragments
531     sub _push {
532     my $r = '';
533     for (@_) {
534     $r .=
535     '$mem[STACK + $s] = (' . $_
536     . ') & 0xFF; $s = ($s - 1) & 0xFF;' . "\n";
537     }
538     return $r;
539     }
540    
541     sub _pop {
542     my $r = '';
543     for (@_) {
544     $r .=
545     '$s = ($s + 1) & 0xFF; ' . $_
546     . ' = $mem[STACK + $s];' . "\n";
547     }
548     return $r;
549     }
550    
551     sub _pop_p {
552     return '$s = ($s + 1) & 0xFF; $p = $mem[STACK + $s] | R | B;'
553     . "\n";
554     }
555    
556     # Addressing modes return a list containing setup code, lvalue
557     sub _zpix {
558     return (
559     'my $ea = $mem[$pc++] + $x; '
560     . '$ea = $mem[$ea & 0xFF] | ($mem[($ea + 1) & 0xFF] << 8)'
561     . ";\n",
562     '$mem[$ea]'
563     );
564     }
565    
566     sub _zpi {
567     return (
568     'my $ea = $mem[$pc++]; '
569     . '$ea = $mem[$ea & 0xFF] | ($mem[($ea + 1) & 0xFF] << 8)'
570     . ";\n",
571     '$mem[$ea]'
572     );
573     }
574    
575     sub _zpiy {
576     return (
577     'my $ea = $mem[$pc++]; '
578     . '$ea = ($mem[$ea & 0xFF] | ($mem[($ea + 1) & 0xFF] << 8)) + $y'
579     . ";\n",
580     '$mem[$ea]'
581     );
582     }
583    
584     sub _zp {
585     return ('my $ea = $mem[$pc++];' . "\n", '$mem[$ea]');
586     }
587    
588     sub _zpx {
589     return ('my $ea = ($mem[$pc++] + $x) & 0xFF;' . "\n", '$mem[$ea]');
590     }
591    
592     sub _zpy {
593     return ('my $ea = ($mem[$pc++] + $y) & 0xFF;' . "\n", '$mem[$ea]');
594     }
595    
596     sub _abs {
597     return ('my $ea = $mem[$pc] | ($mem[$pc+1] << 8); $pc += 2;' . "\n",
598     '$mem[$ea]');
599     }
600    
601     sub _absx {
602     return (
603     'my $ea = ($mem[$pc] | ($mem[$pc+1] << 8)) + $x; $pc += 2;'
604     . "\n",
605     '$mem[$ea]'
606     );
607     }
608    
609     sub _absy {
610     return (
611     'my $ea = ($mem[$pc] | ($mem[$pc+1] << 8)) + $y; $pc += 2;'
612     . "\n",
613     '$mem[$ea]'
614     );
615     }
616    
617     sub _imm {
618     return ('my $v = $mem[$pc++];' . "\n", '$v');
619     }
620    
621     sub _acc {
622     return ('', '$a');
623     }
624    
625     sub _rel {
626     # Doesn't return an lvalue
627     return ('my $t = $mem[$pc++];' . "\n",
628     '($pc + $t - (($t & 0x80) ? 0x100 : 0))');
629     }
630    
631     sub _status {
632     my $reg = shift || '$a';
633     return '$p = ($p & ~(N | Z) | $zn[' . $reg . ']);' . "\n";
634     }
635    
636     sub _ora {
637     return $_[0] . '$a |= ' . $_[1] . ";\n" . _status();
638     }
639    
640     sub _and {
641     return $_[0] . '$a &= ' . $_[1] . ";\n" . _status();
642     }
643    
644     sub _eor {
645     return $_[0] . '$a ^= ' . $_[1] . ";\n" . _status();
646     }
647    
648     sub _bit {
649     return $_[0]
650     . '$p = ($p & ~(N|V)) | ('
651     . $_[1]
652     . ' & (N|V));' . "\n"
653     . 'if (($a & '
654     . $_[1]
655     . ') == 0) { $p |= Z; } else { $p &= ~Z; }' . "\n";
656     }
657    
658     sub _asl {
659     return $_[0]
660     . 'my $w = ('
661     . $_[1]
662     . ') << 1; ' . "\n"
663     . 'if ($w & 0x100) { $p |= C; $w &= ~0x100; } else { $p &= ~C; }'
664     . "\n"
665     . _status('$w')
666     . $_[1]
667     . ' = $w;' . "\n";
668     }
669    
670     sub _lsr {
671     return $_[0]
672     . 'my $w = '
673     . $_[1] . ";\n"
674     . 'if (($w & 1) != 0) { $p |= C; } else { $p &= ~C; }' . "\n"
675     . '$w >>= 1;' . "\n"
676     . _status('$w')
677     . $_[1]
678     . ' = $w;' . "\n";
679     }
680    
681     sub _rol {
682     return $_[0]
683     . 'my $w = ('
684     . $_[1]
685     . ' << 1) | ($p & C);' . "\n"
686     . 'if ($w >= 0x100) { $p |= C; $w -= 0x100; } else { $p &= ~C; };'
687     . "\n"
688     . _status('$w')
689     . $_[1]
690     . ' = $w;' . "\n";
691     }
692    
693     sub _ror {
694     return $_[0]
695     . 'my $w = '
696     . $_[1]
697     . ' | (($p & C) << 8);' . "\n"
698     . 'if (($w & 1) != 0) { $p |= C; } else { $p &= ~C; }' . "\n"
699     . '$w >>= 1;' . "\n"
700     . _status('$w')
701     . $_[1]
702     . ' = $w;' . "\n";
703     }
704    
705     sub _sto {
706     return $_[0] . "$_[1] = $_[2];\n";
707     }
708    
709     sub _lod {
710     return $_[0] . "$_[2] = $_[1];\n" . _status($_[2]);
711     }
712    
713     sub _cmp {
714     return $_[0]
715     . 'my $w = '
716     . $_[2] . ' - '
717     . $_[1] . ";\n"
718     . 'if ($w < 0) { $w += 0x100; $p &= ~C; } else { $p |= C; }'
719     . "\n"
720     . _status('$w');
721     }
722    
723     sub _tsb {
724     return 'croak "TSB not supported\n";' . "\n";
725     }
726    
727     sub _trb {
728     return 'croak "TRB not supported\n";' . "\n";
729     }
730    
731     sub _inc {
732     return $_[0]
733     . $_[1] . ' = ('
734     . $_[1]
735     . ' + 1) & 0xFF;' . "\n"
736     . _status($_[1]);
737     }
738    
739     sub _dec {
740     return $_[0]
741     . $_[1] . ' = ('
742     . $_[1]
743     . ' + 0xFF) & 0xFF;' . "\n"
744     . _status($_[1]);
745     }
746    
747     sub _adc {
748     return $_[0]
749     . 'my $w = '
750     . $_[1] . ";\n"
751     . 'if ($p & D) {' . "\n"
752     . 'my $lo = ($a & 0x0F) + ($w & 0x0F) + ($p & C);' . "\n"
753     . 'if ($lo > 9) { $lo += 6; }' . "\n"
754     . 'my $hi = ($a >> 4) + ( $w >> 4) + ($lo > 15 ? 1 : 0);' . "\n"
755     . '$a = ($lo & 0x0F) | ($hi << 4);' . "\n"
756     . '$p = ($p & ~C) | ($hi > 15 ? C : 0);' . "\n"
757     . '} else {' . "\n"
758     . 'my $lo = $a + $w + ($p & C);' . "\n"
759     . '$p &= ~(N | V | Z | C);' . "\n"
760     . '$p |= (~($a ^ $w) & ($a ^ $lo) & 0x80 ? V : 0) | ($lo & 0x100 ? C : 0);'
761     . "\n"
762     . '$a = $lo & 0xFF;' . "\n"
763     . _status() . '}' . "\n";
764     }
765    
766     sub _sbc {
767     return $_[0]
768     . 'my $w = '
769     . $_[1] . ";\n"
770     . 'if ($p & D) {' . "\n"
771     . 'my $lo = ($a & 0x0F) - ($w & 0x0F) - (~$p & C);' . "\n"
772     . 'if ($lo & 0x10) { $lo -= 6; }' . "\n"
773     . 'my $hi = ($a >> 4) - ($w >> 4) - (($lo & 0x10) >> 4);' . "\n"
774     . 'if ($hi & 0x10) { $hi -= 6; }' . "\n"
775     . '$a = ($lo & 0x0F) | ($hi << 4);' . "\n"
776     . '$p = ($p & ~C) | ($hi > 15 ? 0 : C);' . "\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 ? 0 : C);'
781     . "\n"
782     . '$a = $lo & 0xFF;' . "\n"
783     . _status() . '}' . "\n";
784     }
785    
786     sub _bra {
787     return $_[0] . '$pc = ' . $_[1] . ";\n";
788     }
789    
790     sub _bfz {
791     return $_[0]
792     . 'if (($p & '
793     . $_[2]
794     . ') == 0) { $pc = '
795     . $_[1] . '; }' . "\n";
796     }
797    
798     sub _bfnz {
799     return $_[0]
800     . 'if (($p & '
801     . $_[2]
802     . ') != 0) { $pc = '
803     . $_[1] . '; }' . "\n";
804     }
805    
806     sub _jmp_i {
807     my $a = shift;
808     return '$pc = $mem[' . $a
809     . '] | ($mem['
810     . $a
811     . ' + 1] << 8);' . "\n";
812     }
813    
814     sub _jmp {
815     return _jmp_i('$pc');
816     }
817    
818     sub _jmpi {
819     return 'my $w = $mem[$pc] | ($mem[$pc + 1] << 8); ' . _jmp_i('$w');
820     }
821    
822     sub _jmpix {
823     return 'my $w = ($mem[$pc] | ($mem[$pc + 1] << 8)) + $x; '
824     . _jmp_i('$w');
825     }
826    
827     sub _rts {
828     return 'my ($lo, $hi); '
829     . _pop('$lo')
830     . _pop('$hi')
831     . '$pc = ($lo | ($hi << 8)) + 1;' . "\n";
832     }
833    
834     1;
835     __END__
836    
837     =head1 NAME
838    
839     Acme::6502 - Pure Perl 65C02 simulator.
840    
841     =head1 VERSION
842    
843     This document describes Acme::6502 version 0.0.6
844    
845     =head1 SYNOPSIS
846    
847     use Acme::6502;
848    
849     my $cpu = Acme::6502->new();
850    
851     # Set start address
852     $cpu->set_pc(0x8000);
853    
854     # Load ROM image
855     $cpu->load_rom('myrom.rom', 0x8000);
856    
857     # Run for 1,000,000 instructions then return
858     $cpu->run(1_000_000);
859    
860     =head1 DESCRIPTION
861    
862     Imagine the nightmare scenario: your boss tells you about a legacy
863     system you have to support. How bad could it be? COBOL? Fortran? Worse:
864     it's an embedded 6502 system run by a family of squirrels (see Dilberts
865     passim). Fortunately there's a pure Perl 6502 emulator that works so
866     well the squirrels will never know the difference.
867    
868     =head1 INTERFACE
869    
870     =over
871    
872     =item C<call_os( $vec_number )>
873    
874     Subclass to provide OS entry points. OS vectors are installed by calling
875     C<make_vector>. When the vector is called C<call_os()> will be called
876     with the vector number.
877    
878     =item C<get_a()>
879    
880     Read the current value of the processor A register (accumulator).
881    
882     =item C<get_p()>
883    
884     Read the current value of the processor status register.
885    
886     =item C<get_pc()>
887    
888     Read the current value of the program counter.
889    
890     =item C<get_s()>
891    
892     Read the current value of the stack pointer.
893    
894     =item C<get_x()>
895    
896     Read the current value of the processor X index register.
897    
898     =item C<get_y()>
899    
900     Read the current value of the processor X index register.
901    
902     =item C<get_xy()>
903    
904     Read the value of X and Y as a sixteen bit number. X forms the lower 8
905     bits of the value and Y forms the upper 8 bits.
906    
907     =item C<get_state()>
908    
909     Returns an array containing the values of the A, X, Y, S, P and SP.
910    
911     =item C<set_a( $value )>
912    
913     Set the value of the processor A register (accumulator).
914    
915     =item C<set_p( $value )>
916    
917     Set the value of the processor status register.
918    
919     =item C<set_pc( $value )>
920    
921     Set the value of the program counter.
922    
923     =item C<set_s( $value )>
924    
925     Set the value of the stack pointer.
926    
927     =item C<set_x( $value )>
928    
929     Set the value of the X index register.
930    
931     =item C<set_y( $value )>
932    
933     Set the value of the Y index register.
934    
935     =item C<set_xy( $value )>
936    
937     Set the value of the X and Y registers to the specified sixteen bit
938     number. X gets the lower 8 bits, Y gets the upper 8 bits.
939    
940     =item C<set_jumptab( $addr )>
941    
942     Set the address of the block of memory that will be used to hold the
943     thunk blocks that correspond with vectored OS entry points. Each thunk
944     takes four bytes.
945    
946     =item C<load_rom( $filename, $addr )>
947    
948     Load a ROM image at the specified address.
949    
950     =item C<make_vector( $jmp_addr, $vec_addr, $vec_number )>
951    
952     Make a vectored entry point for an emulated OS. C<$jmp_addr> is the
953     address where an indirect JMP instruction (6C) will be placed,
954     C<$vec_addr> is the address of the vector and C<$vec_number> will be
955     passed to C<call_os> when the OS call is made.
956    
957     =item C<poke_code( $addr, @bytes )>
958    
959     Poke code directly at the specified address.
960    
961     =item C<read_8( $addr )>
962    
963     Read a byte at the specified address.
964    
965     =item C<read_16( $addr )>
966    
967     Read a sixteen bit (low, high) word at the specified address.
968    
969     =item C<read_32( $addr )>
970    
971     Read a 32 bit word at the specified address.
972    
973     =item C<read_chunk( $start, $end )>
974    
975     Read a chunk of data from C<$start> to C<$end> - 1 into a string.
976    
977     =item C<read_str( $addr )>
978    
979     Read a carriage return terminated (0x0D) string from the
980     specified address.
981    
982     =item C<run( $count [, $callback ] )>
983    
984     Execute the specified number of instructions and return. Optionally a
985     callback may be provided in which case it will be called before each
986     instruction is executed:
987    
988     my $cb = sub {
989     my ($pc, $inst, $a, $x, $y, $s, $p) = @_;
990     # Maybe output trace info
991     }
992    
993     $cpu->run(100, $cb);
994    
995     =item C<write_8( $addr, $value )>
996    
997     Write the byte at the specified address.
998    
999     =item C<write_16( $addr, $value )>
1000    
1001     Write a sixteen bit (low, high) value at the specified address.
1002    
1003     =item C<write_32( $addr, $value )>
1004    
1005     Write a 32 bit value at the specified address.
1006    
1007     =item C<write_chunk( $addr, $string )>
1008    
1009     Write a chunk of data to memory.
1010    
1011     =back
1012    
1013     =head1 DIAGNOSTICS
1014    
1015     =over
1016    
1017     =item C<< Bad instruction at %s (%s) >>
1018    
1019     The emulator hit an illegal 6502 instruction.
1020    
1021     =back
1022    
1023     =head1 CONFIGURATION AND ENVIRONMENT
1024    
1025     Acme::6502 requires no configuration files or environment variables.
1026    
1027     =head1 DEPENDENCIES
1028    
1029     C<Acme::6502> needs C<Class::Std>.
1030    
1031     =head1 INCOMPATIBILITIES
1032    
1033     None reported.
1034    
1035     =head1 BUGS AND LIMITATIONS
1036    
1037     Doesn't have support for hardware emulation hooks - so memory mapped I/O
1038     is out of the question until someone fixes it.
1039    
1040     Please report any bugs or feature requests to
1041     C<bug-acme-6502@rt.cpan.org>, or through the web interface at
1042     L<http://rt.cpan.org>.
1043    
1044     =head1 AUTHOR
1045    
1046     Andy Armstrong C<< <andy@hexten.net> >>
1047    
1048     =head1 LICENCE AND COPYRIGHT
1049    
1050     Copyright (c) 2006, Andy Armstrong C<< <andy@hexten.net> >>. All rights reserved.
1051    
1052     This module is free software; you can redistribute it and/or
1053     modify it under the same terms as Perl itself. See L<perlartistic>.
1054    
1055     =head1 DISCLAIMER OF WARRANTY
1056    
1057     BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
1058     FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
1059     OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
1060     PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
1061     EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1062     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
1063     ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
1064     YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
1065     NECESSARY SERVICING, REPAIR, OR CORRECTION.
1066    
1067     IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
1068     WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
1069     REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
1070     LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
1071     OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
1072     THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
1073     RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
1074     FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
1075     SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
1076     SUCH DAMAGES.

  ViewVC Help
Powered by ViewVC 1.1.26