/[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 4 - (hide annotations)
Sun Jul 29 01:52:27 2007 UTC (16 years, 11 months ago) by dpavlin
File size: 33734 byte(s)


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

  ViewVC Help
Powered by ViewVC 1.1.26