/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 11 - (show 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 package ACME::6502;
2
3 use warnings FATAL => 'all';
4 use strict;
5 use Carp;
6 use TieMem;
7 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
40 =head2 BUILD
41
42 =head2 AUTOMETHOD
43
44 =cut
45
46 sub BUILD {
47 my ($self, $id, $args) = @_;
48
49 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 my @os;
57 my ($a, $x, $y, $s, $p, $pc) = (0) x 6;
58 my $mmap;
59
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 $pc &&= 0xffff;
425 }
426 } else {
427 while ($ic-- > 0) {
428 my $op = $mem[$pc];
429 # printf "pc: %04x %s\n", $pc, $op;
430 $pc++;
431 $decode[$op]->();
432 }
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 if ( $a < 0 ) {
444 $buf = substr($buf, -$a);
445 $a = 0;
446 }
447 $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 },
497
498 mmap => sub {
499 my ( $addr, $sub ) = @_;
500 confess "callback not sub" unless ref($sub) eq 'CODE';
501 $mmap->{$addr} = $sub;
502 }
503 };
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