/[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 11 - (hide annotations)
Sun Jul 29 15:44:10 2007 UTC (16 years, 11 months ago) by dpavlin
File size: 33899 byte(s)
wrap PC around 0xffff
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 dpavlin 11 $pc &&= 0xffff;
425 dpavlin 3 }
426     } else {
427     while ($ic-- > 0) {
428 dpavlin 5 my $op = $mem[$pc];
429     # printf "pc: %04x %s\n", $pc, $op;
430     $pc++;
431     $decode[$op]->();
432 dpavlin 3 }
433     }
434     },
435    
436     load_rom => sub {
437     my ($f, $a) = @_;
438     open my $fh, '<', $f or croak "Can't read $f ($!)\n";
439     binmode $fh;
440     my $sz = -s $fh;
441     sysread $fh, my $buf, $sz
442     or croak "Error reading $f ($!)\n";
443 dpavlin 6 if ( $a < 0 ) {
444     $buf = substr($buf, -$a);
445     $a = 0;
446     }
447 dpavlin 3 $self->write_chunk($a, $buf);
448     },
449    
450     poke_code => sub {
451     my $addr = shift;
452     $mem[$addr++] = $_ for @_;
453     },
454    
455     make_vector => sub {
456     my ($call, $vec, $func) = @_;
457     $mem[$call + 0] = 0x6C; # JMP (indirect)
458     $mem[$call + 1] = $vec & 0xFF;
459     $mem[$call + 2] = ($vec >> 8) & 0xFF;
460    
461     my $addr = $jumptab;
462     $mem[$jumptab++] = ESCAPE_OP;
463     $mem[$jumptab++] = ESCAPE_SIG;
464     $mem[$jumptab++] = $func;
465     $mem[$jumptab++] = 0x60;
466    
467     $mem[$vec + 0] = $addr & 0xFF;
468     $mem[$vec + 1] = ($addr >> 8) & 0xFF;
469     },
470    
471     get_state => sub {
472     return ($a, $x, $y, $s, $p, $pc);
473     },
474    
475     get_xy => sub {
476     return $x || ($y << 8);
477     },
478    
479     set_xy => sub {
480     my $v = shift;
481     $x = $v & 0xFF;
482     $y = ($v >> 8) & 0xFF;
483     },
484    
485     decode_flags => sub {
486     my $f = shift;
487     my $b = 0x80;
488     my $n = FLAGS;
489     my $desc = '';
490     while ($n) {
491     $desc .= ($f & $b) ? substr($n, 0, 1) : '-';
492     $n = substr($n, 1);
493     $b >>= 1;
494     }
495     return $desc;
496 dpavlin 5 },
497    
498     mmap => sub {
499     my ( $addr, $sub ) = @_;
500     confess "callback not sub" unless ref($sub) eq 'CODE';
501     $mmap->{$addr} = $sub;
502     }
503 dpavlin 3 };
504    
505     # Generate register accessors.
506     for my $reg (qw(a x y s p pc)) {
507     $cpu{$id}->{"get_$reg"} = eval "sub { return \$$reg; }";
508     $cpu{$id}->{"set_$reg"} = eval "sub { \$$reg = \$_[0]; }";
509     }
510     }
511    
512     sub AUTOMETHOD {
513     my ($self, $id, @args) = @_;
514     my $methname = $_;
515    
516     if (exists($cpu{$id}->{$methname})) {
517     return sub {
518     return $cpu{$id}->{$methname}->(@args);
519     }
520     }
521    
522     return;
523     }
524    
525     sub call_os {
526     my $self = shift;
527     my $id = ident($self);
528     croak "call_os() not supported";
529     }
530    
531     # Functions that generate code fragments
532     sub _push {
533     my $r = '';
534     for (@_) {
535     $r .=
536     '$mem[STACK + $s] = (' . $_
537     . ') & 0xFF; $s = ($s - 1) & 0xFF;' . "\n";
538     }
539     return $r;
540     }
541    
542     sub _pop {
543     my $r = '';
544     for (@_) {
545     $r .=
546     '$s = ($s + 1) & 0xFF; ' . $_
547     . ' = $mem[STACK + $s];' . "\n";
548     }
549     return $r;
550     }
551    
552     sub _pop_p {
553     return '$s = ($s + 1) & 0xFF; $p = $mem[STACK + $s] | R | B;'
554     . "\n";
555     }
556    
557     # Addressing modes return a list containing setup code, lvalue
558     sub _zpix {
559     return (
560     'my $ea = $mem[$pc++] + $x; '
561     . '$ea = $mem[$ea & 0xFF] | ($mem[($ea + 1) & 0xFF] << 8)'
562     . ";\n",
563     '$mem[$ea]'
564     );
565     }
566    
567     sub _zpi {
568     return (
569     'my $ea = $mem[$pc++]; '
570     . '$ea = $mem[$ea & 0xFF] | ($mem[($ea + 1) & 0xFF] << 8)'
571     . ";\n",
572     '$mem[$ea]'
573     );
574     }
575    
576     sub _zpiy {
577     return (
578     'my $ea = $mem[$pc++]; '
579     . '$ea = ($mem[$ea & 0xFF] | ($mem[($ea + 1) & 0xFF] << 8)) + $y'
580     . ";\n",
581     '$mem[$ea]'
582     );
583     }
584    
585     sub _zp {
586     return ('my $ea = $mem[$pc++];' . "\n", '$mem[$ea]');
587     }
588    
589     sub _zpx {
590     return ('my $ea = ($mem[$pc++] + $x) & 0xFF;' . "\n", '$mem[$ea]');
591     }
592    
593     sub _zpy {
594     return ('my $ea = ($mem[$pc++] + $y) & 0xFF;' . "\n", '$mem[$ea]');
595     }
596    
597     sub _abs {
598     return ('my $ea = $mem[$pc] | ($mem[$pc+1] << 8); $pc += 2;' . "\n",
599     '$mem[$ea]');
600     }
601    
602     sub _absx {
603     return (
604     'my $ea = ($mem[$pc] | ($mem[$pc+1] << 8)) + $x; $pc += 2;'
605     . "\n",
606     '$mem[$ea]'
607     );
608     }
609    
610     sub _absy {
611     return (
612     'my $ea = ($mem[$pc] | ($mem[$pc+1] << 8)) + $y; $pc += 2;'
613     . "\n",
614     '$mem[$ea]'
615     );
616     }
617    
618     sub _imm {
619     return ('my $v = $mem[$pc++];' . "\n", '$v');
620     }
621    
622     sub _acc {
623     return ('', '$a');
624     }
625    
626     sub _rel {
627     # Doesn't return an lvalue
628     return ('my $t = $mem[$pc++];' . "\n",
629     '($pc + $t - (($t & 0x80) ? 0x100 : 0))');
630     }
631    
632     sub _status {
633     my $reg = shift || '$a';
634     return '$p = ($p & ~(N | Z) | $zn[' . $reg . ']);' . "\n";
635     }
636    
637     sub _ora {
638     return $_[0] . '$a |= ' . $_[1] . ";\n" . _status();
639     }
640    
641     sub _and {
642     return $_[0] . '$a &= ' . $_[1] . ";\n" . _status();
643     }
644    
645     sub _eor {
646     return $_[0] . '$a ^= ' . $_[1] . ";\n" . _status();
647     }
648    
649     sub _bit {
650     return $_[0]
651     . '$p = ($p & ~(N|V)) | ('
652     . $_[1]
653     . ' & (N|V));' . "\n"
654     . 'if (($a & '
655     . $_[1]
656     . ') == 0) { $p |= Z; } else { $p &= ~Z; }' . "\n";
657     }
658    
659     sub _asl {
660     return $_[0]
661     . 'my $w = ('
662     . $_[1]
663     . ') << 1; ' . "\n"
664     . 'if ($w & 0x100) { $p |= C; $w &= ~0x100; } else { $p &= ~C; }'
665     . "\n"
666     . _status('$w')
667     . $_[1]
668     . ' = $w;' . "\n";
669     }
670    
671     sub _lsr {
672     return $_[0]
673     . 'my $w = '
674     . $_[1] . ";\n"
675     . 'if (($w & 1) != 0) { $p |= C; } else { $p &= ~C; }' . "\n"
676     . '$w >>= 1;' . "\n"
677     . _status('$w')
678     . $_[1]
679     . ' = $w;' . "\n";
680     }
681    
682     sub _rol {
683     return $_[0]
684     . 'my $w = ('
685     . $_[1]
686     . ' << 1) | ($p & C);' . "\n"
687     . 'if ($w >= 0x100) { $p |= C; $w -= 0x100; } else { $p &= ~C; };'
688     . "\n"
689     . _status('$w')
690     . $_[1]
691     . ' = $w;' . "\n";
692     }
693    
694     sub _ror {
695     return $_[0]
696     . 'my $w = '
697     . $_[1]
698     . ' | (($p & C) << 8);' . "\n"
699     . 'if (($w & 1) != 0) { $p |= C; } else { $p &= ~C; }' . "\n"
700     . '$w >>= 1;' . "\n"
701     . _status('$w')
702     . $_[1]
703     . ' = $w;' . "\n";
704     }
705    
706     sub _sto {
707     return $_[0] . "$_[1] = $_[2];\n";
708     }
709    
710     sub _lod {
711     return $_[0] . "$_[2] = $_[1];\n" . _status($_[2]);
712     }
713    
714     sub _cmp {
715     return $_[0]
716     . 'my $w = '
717     . $_[2] . ' - '
718     . $_[1] . ";\n"
719     . 'if ($w < 0) { $w += 0x100; $p &= ~C; } else { $p |= C; }'
720     . "\n"
721     . _status('$w');
722     }
723    
724     sub _tsb {
725     return 'croak "TSB not supported\n";' . "\n";
726     }
727    
728     sub _trb {
729     return 'croak "TRB not supported\n";' . "\n";
730     }
731    
732     sub _inc {
733     return $_[0]
734     . $_[1] . ' = ('
735     . $_[1]
736     . ' + 1) & 0xFF;' . "\n"
737     . _status($_[1]);
738     }
739    
740     sub _dec {
741     return $_[0]
742     . $_[1] . ' = ('
743     . $_[1]
744     . ' + 0xFF) & 0xFF;' . "\n"
745     . _status($_[1]);
746     }
747    
748     sub _adc {
749     return $_[0]
750     . 'my $w = '
751     . $_[1] . ";\n"
752     . 'if ($p & D) {' . "\n"
753     . 'my $lo = ($a & 0x0F) + ($w & 0x0F) + ($p & C);' . "\n"
754     . 'if ($lo > 9) { $lo += 6; }' . "\n"
755     . 'my $hi = ($a >> 4) + ( $w >> 4) + ($lo > 15 ? 1 : 0);' . "\n"
756     . '$a = ($lo & 0x0F) | ($hi << 4);' . "\n"
757     . '$p = ($p & ~C) | ($hi > 15 ? C : 0);' . "\n"
758     . '} else {' . "\n"
759     . 'my $lo = $a + $w + ($p & C);' . "\n"
760     . '$p &= ~(N | V | Z | C);' . "\n"
761     . '$p |= (~($a ^ $w) & ($a ^ $lo) & 0x80 ? V : 0) | ($lo & 0x100 ? C : 0);'
762     . "\n"
763     . '$a = $lo & 0xFF;' . "\n"
764     . _status() . '}' . "\n";
765     }
766    
767     sub _sbc {
768     return $_[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 & 0x10) { $lo -= 6; }' . "\n"
774     . 'my $hi = ($a >> 4) - ($w >> 4) - (($lo & 0x10) >> 4);' . "\n"
775     . 'if ($hi & 0x10) { $hi -= 6; }' . "\n"
776     . '$a = ($lo & 0x0F) | ($hi << 4);' . "\n"
777     . '$p = ($p & ~C) | ($hi > 15 ? 0 : C);' . "\n"
778     . '} else {' . "\n"
779     . 'my $lo = $a - $w - (~$p & C);' . "\n"
780     . '$p &= ~(N | V | Z | C);' . "\n"
781     . '$p |= (($a ^ $w) & ($a ^ $lo) & 0x80 ? V : 0) | ($lo & 0x100 ? 0 : C);'
782     . "\n"
783     . '$a = $lo & 0xFF;' . "\n"
784     . _status() . '}' . "\n";
785     }
786    
787     sub _bra {
788     return $_[0] . '$pc = ' . $_[1] . ";\n";
789     }
790    
791     sub _bfz {
792     return $_[0]
793     . 'if (($p & '
794     . $_[2]
795     . ') == 0) { $pc = '
796     . $_[1] . '; }' . "\n";
797     }
798    
799     sub _bfnz {
800     return $_[0]
801     . 'if (($p & '
802     . $_[2]
803     . ') != 0) { $pc = '
804     . $_[1] . '; }' . "\n";
805     }
806    
807     sub _jmp_i {
808     my $a = shift;
809     return '$pc = $mem[' . $a
810     . '] | ($mem['
811     . $a
812     . ' + 1] << 8);' . "\n";
813     }
814    
815     sub _jmp {
816     return _jmp_i('$pc');
817     }
818    
819     sub _jmpi {
820     return 'my $w = $mem[$pc] | ($mem[$pc + 1] << 8); ' . _jmp_i('$w');
821     }
822    
823     sub _jmpix {
824     return 'my $w = ($mem[$pc] | ($mem[$pc + 1] << 8)) + $x; '
825     . _jmp_i('$w');
826     }
827    
828     sub _rts {
829     return 'my ($lo, $hi); '
830     . _pop('$lo')
831     . _pop('$hi')
832     . '$pc = ($lo | ($hi << 8)) + 1;' . "\n";
833     }
834    
835     1;
836     __END__
837    
838     =head1 NAME
839    
840     Acme::6502 - Pure Perl 65C02 simulator.
841    
842     =head1 VERSION
843    
844     This document describes Acme::6502 version 0.0.6
845    
846     =head1 SYNOPSIS
847    
848     use Acme::6502;
849    
850     my $cpu = Acme::6502->new();
851    
852     # Set start address
853     $cpu->set_pc(0x8000);
854    
855     # Load ROM image
856     $cpu->load_rom('myrom.rom', 0x8000);
857    
858     # Run for 1,000,000 instructions then return
859     $cpu->run(1_000_000);
860    
861     =head1 DESCRIPTION
862    
863     Imagine the nightmare scenario: your boss tells you about a legacy
864     system you have to support. How bad could it be? COBOL? Fortran? Worse:
865     it's an embedded 6502 system run by a family of squirrels (see Dilberts
866     passim). Fortunately there's a pure Perl 6502 emulator that works so
867     well the squirrels will never know the difference.
868    
869     =head1 INTERFACE
870    
871     =over
872    
873     =item C<call_os( $vec_number )>
874    
875     Subclass to provide OS entry points. OS vectors are installed by calling
876     C<make_vector>. When the vector is called C<call_os()> will be called
877     with the vector number.
878    
879     =item C<get_a()>
880    
881     Read the current value of the processor A register (accumulator).
882    
883     =item C<get_p()>
884    
885     Read the current value of the processor status register.
886    
887     =item C<get_pc()>
888    
889     Read the current value of the program counter.
890    
891     =item C<get_s()>
892    
893     Read the current value of the stack pointer.
894    
895     =item C<get_x()>
896    
897     Read the current value of the processor X index register.
898    
899     =item C<get_y()>
900    
901     Read the current value of the processor X index register.
902    
903     =item C<get_xy()>
904    
905     Read the value of X and Y as a sixteen bit number. X forms the lower 8
906     bits of the value and Y forms the upper 8 bits.
907    
908     =item C<get_state()>
909    
910     Returns an array containing the values of the A, X, Y, S, P and SP.
911    
912     =item C<set_a( $value )>
913    
914     Set the value of the processor A register (accumulator).
915    
916     =item C<set_p( $value )>
917    
918     Set the value of the processor status register.
919    
920     =item C<set_pc( $value )>
921    
922     Set the value of the program counter.
923    
924     =item C<set_s( $value )>
925    
926     Set the value of the stack pointer.
927    
928     =item C<set_x( $value )>
929    
930     Set the value of the X index register.
931    
932     =item C<set_y( $value )>
933    
934     Set the value of the Y index register.
935    
936     =item C<set_xy( $value )>
937    
938     Set the value of the X and Y registers to the specified sixteen bit
939     number. X gets the lower 8 bits, Y gets the upper 8 bits.
940    
941     =item C<set_jumptab( $addr )>
942    
943     Set the address of the block of memory that will be used to hold the
944     thunk blocks that correspond with vectored OS entry points. Each thunk
945     takes four bytes.
946    
947     =item C<load_rom( $filename, $addr )>
948    
949     Load a ROM image at the specified address.
950    
951     =item C<make_vector( $jmp_addr, $vec_addr, $vec_number )>
952    
953     Make a vectored entry point for an emulated OS. C<$jmp_addr> is the
954     address where an indirect JMP instruction (6C) will be placed,
955     C<$vec_addr> is the address of the vector and C<$vec_number> will be
956     passed to C<call_os> when the OS call is made.
957    
958     =item C<poke_code( $addr, @bytes )>
959    
960     Poke code directly at the specified address.
961    
962     =item C<read_8( $addr )>
963    
964     Read a byte at the specified address.
965    
966     =item C<read_16( $addr )>
967    
968     Read a sixteen bit (low, high) word at the specified address.
969    
970     =item C<read_32( $addr )>
971    
972     Read a 32 bit word at the specified address.
973    
974     =item C<read_chunk( $start, $end )>
975    
976     Read a chunk of data from C<$start> to C<$end> - 1 into a string.
977    
978     =item C<read_str( $addr )>
979    
980     Read a carriage return terminated (0x0D) string from the
981     specified address.
982    
983     =item C<run( $count [, $callback ] )>
984    
985     Execute the specified number of instructions and return. Optionally a
986     callback may be provided in which case it will be called before each
987     instruction is executed:
988    
989     my $cb = sub {
990     my ($pc, $inst, $a, $x, $y, $s, $p) = @_;
991     # Maybe output trace info
992     }
993    
994     $cpu->run(100, $cb);
995    
996     =item C<write_8( $addr, $value )>
997    
998     Write the byte at the specified address.
999    
1000     =item C<write_16( $addr, $value )>
1001    
1002     Write a sixteen bit (low, high) value at the specified address.
1003    
1004     =item C<write_32( $addr, $value )>
1005    
1006     Write a 32 bit value at the specified address.
1007    
1008     =item C<write_chunk( $addr, $string )>
1009    
1010     Write a chunk of data to memory.
1011    
1012     =back
1013    
1014     =head1 DIAGNOSTICS
1015    
1016     =over
1017    
1018     =item C<< Bad instruction at %s (%s) >>
1019    
1020     The emulator hit an illegal 6502 instruction.
1021    
1022     =back
1023    
1024     =head1 CONFIGURATION AND ENVIRONMENT
1025    
1026     Acme::6502 requires no configuration files or environment variables.
1027    
1028     =head1 DEPENDENCIES
1029    
1030     C<Acme::6502> needs C<Class::Std>.
1031    
1032     =head1 INCOMPATIBILITIES
1033    
1034     None reported.
1035    
1036     =head1 BUGS AND LIMITATIONS
1037    
1038     Doesn't have support for hardware emulation hooks - so memory mapped I/O
1039     is out of the question until someone fixes it.
1040    
1041     Please report any bugs or feature requests to
1042     C<bug-acme-6502@rt.cpan.org>, or through the web interface at
1043     L<http://rt.cpan.org>.
1044    
1045     =head1 AUTHOR
1046    
1047     Andy Armstrong C<< <andy@hexten.net> >>
1048    
1049     =head1 LICENCE AND COPYRIGHT
1050    
1051     Copyright (c) 2006, Andy Armstrong C<< <andy@hexten.net> >>. All rights reserved.
1052    
1053     This module is free software; you can redistribute it and/or
1054     modify it under the same terms as Perl itself. See L<perlartistic>.
1055    
1056     =head1 DISCLAIMER OF WARRANTY
1057    
1058     BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
1059     FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
1060     OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
1061     PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
1062     EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1063     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
1064     ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
1065     YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
1066     NECESSARY SERVICING, REPAIR, OR CORRECTION.
1067    
1068     IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
1069     WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
1070     REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
1071     LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
1072     OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
1073     THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
1074     RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
1075     FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
1076     SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
1077     SUCH DAMAGES.

  ViewVC Help
Powered by ViewVC 1.1.26