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

  ViewVC Help
Powered by ViewVC 1.1.26