/[VRac]/M6502/Orao.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 /M6502/Orao.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 97 - (show annotations)
Thu Aug 2 14:07:52 2007 UTC (16 years, 8 months ago) by dpavlin
File size: 11197 byte(s)
it's allmost usable. Without keyboard, that is :-)
1 package Orao;
2
3 use warnings;
4 use strict;
5
6 use Carp qw/confess/;
7 use lib './lib';
8 #use Time::HiRes qw(time);
9 use File::Slurp;
10 use Data::Dump qw/dump/;
11 use List::Util qw/first/;
12 use M6502;
13
14 use base qw(Class::Accessor M6502 Screen Prefs);
15 __PACKAGE__->mk_accessors(qw(booted));
16
17 =head1 NAME
18
19 Orao - Orao emulator
20
21 =head1 VERSION
22
23 Version 0.04
24
25 =cut
26
27 our $VERSION = '0.04';
28
29 =head1 SUMMARY
30
31 Emulator or Orao 8-bit 6502 machine popular in Croatia
32
33 =cut
34
35 my @kbd_ports = (
36 0x87FC,0x87FD,0x87FA,0x87FB,0x87F6,0x87F7,
37 0x87EE,0x87EF,0x87DE,0x87DF,0x87BE,0x87BF,
38 0x877E,0x877F,0x86FE,0x86FF,0x85FE,0x85FF,
39 0x83FE,0x83FF,
40 );
41
42 =head1 FUNCTIONS
43
44 =head2 boot
45
46 Start emulator, open L<Screen>, load initial ROM images, and render memory
47
48 my $orao = Orao->new({});
49 $orao->boot;
50
51 =cut
52
53 our $orao;
54
55 select(STDERR); $| = 1;
56
57 sub boot {
58 my $self = shift;
59 warn "Orao calling upstream init\n";
60 $self->SUPER::init(
61 read => sub { $self->read( @_ ) },
62 write => sub { $self->write( @_ ) },
63 );
64
65 warn "Orao $Orao::VERSION emulation starting\n";
66
67 warn "emulating ", $#mem, " bytes of memory\n";
68
69 $self->open_screen;
70 $self->load_rom({
71 0x1000 => 'dump/SCRINV.BIN',
72 # should be 0x6000, but oraoemu has 2 byte prefix
73 0x5FFE => 'dump/screen.dmp',
74 0xC000 => 'rom/BAS12.ROM',
75 0xE000 => 'rom/CRT12.ROM',
76 });
77
78 # $PC = 0xDD11; # BC
79 # $PC = 0xC274; # MC
80
81 $PC = 0xff89;
82
83 $orao = $self;
84
85 # $self->prompt( 0x1000 );
86
87 my ( $trace, $debug ) = ( $self->trace, $self->debug );
88 $self->trace( 0 );
89 $self->debug( 0 );
90
91 $self->render( @mem[ 0x6000 .. 0x7fff ] );
92
93 if ( $self->show_mem ) {
94
95 warn "rendering memory map\n";
96
97 $self->render_mem( @mem );
98
99 my @mmap = (
100 0x0000, 0x03FF, 'nulti blok',
101 0x0400, 0x5FFF, 'korisnièki RAM (23K)',
102 0x6000, 0x7FFF, 'video RAM',
103 0x8000, 0x9FFF, 'sistemske lokacije',
104 0xA000, 0xAFFF, 'ekstenzija',
105 0xB000, 0xBFFF, 'DOS',
106 0xC000, 0xDFFF, 'BASIC ROM',
107 0xE000, 0xFFFF, 'sistemski ROM',
108 );
109
110 } else {
111
112 warn "rendering video memory\n";
113 $self->render( @mem[ 0x6000 .. 0x7fff ] );
114
115 }
116 $self->sync;
117 $self->trace( $trace );
118 $self->debug( $debug );
119
120 #( $A, $P, $X, $Y, $S, $IPeriod ) = ( 1, 2, 3, 4, 5, 6 );
121
122 warn "Orao boot finished",
123 $self->trace ? ' trace' : '',
124 $self->debug ? ' debug' : '',
125 "\n";
126
127 M6502::reset();
128
129 $self->booted( 1 );
130 }
131
132 =head2 run
133
134 Run interactive emulation loop
135
136 $orao->run;
137
138 =cut
139
140 sub run {
141 my $self = shift;
142
143 $self->show_mem( 1 );
144
145 $self->boot if ( ! $self->booted );
146 $self->loop;
147 };
148
149 =head1 Helper functions
150
151 =head2 load_rom
152
153 called to init memory and load initial rom images
154
155 $orao->load_rom;
156
157 =cut
158
159 sub load_rom {
160 my ($self, $loaded_files) = @_;
161
162 #my $time_base = time();
163
164 foreach my $addr ( sort keys %$loaded_files ) {
165 my $path = $loaded_files->{$addr};
166 $self->load_image( $path, $addr );
167 }
168 }
169
170 # write chunk directly into memory, updateing vram if needed
171 sub _write_chunk {
172 my $self = shift;
173 my ( $addr, $chunk ) = @_;
174 $self->write_chunk( $addr, $chunk );
175 my $end = $addr + length($chunk);
176 my ( $f, $t ) = ( 0x6000, 0x7fff );
177
178 if ( $end < $f || $addr >= $t ) {
179 warn "skip vram update\n";
180 return;
181 };
182
183 $f = $addr if ( $addr > $f );
184 $t = $end if ( $end < $t );
185
186 warn sprintf("refresh video ram %04x-%04x\n", $f, $t);
187 # foreach my $a ( $f .. $t ) {
188 # $self->vram( $a - 0x6000 , $mem[ $a ] );
189 # }
190 $self->render( @mem[ 0x6000 .. 0x7fff ] );
191 $self->render_mem( @mem ) if $self->show_mem;
192 }
193
194 =head2 load_image
195
196 Load binary files, ROM images and Orao Emulator files
197
198 $orao->load_image( '/path/to/file', 0x1000 );
199
200 Returns true on success.
201
202 =cut
203
204 sub load_image {
205 my $self = shift;
206 my ( $path, $addr ) = @_;
207
208 if ( ! -e $path ) {
209 warn "ERROR: file $path doesn't exist\n";
210 return;
211 }
212
213 my $size = -s $path || confess "no size for $path: $!";
214
215 my $buff = read_file( $path );
216
217 if ( $size == 65538 ) {
218 $addr = 0;
219 warn sprintf "loading oraoemu 64k dump %s at %04x - %04x %02x\n", $path, $addr, $addr+$size-1, $size;
220 $self->_write_chunk( $addr, substr($buff,2) );
221 return 1;
222 } elsif ( $size == 32800 ) {
223 $addr = 0;
224 warn sprintf "loading oraoemu 1.3 dump %s at %04x - %04x %02x\n", $path, $addr, $addr+$size-1, $size;
225 $self->_write_chunk( $addr, substr($buff,0x20) );
226 return 1;
227 }
228 printf "loading %s at %04x - %04x %02x\n", $path, $addr, $addr+$size-1, $size;
229 $self->_write_chunk( $addr, $buff );
230 return 1;
231
232 my $chunk;
233
234 my $pos = 0;
235
236 while ( my $long = substr($buff,$pos,4) ) {
237 my @b = split(//, $long, 4);
238 $chunk .=
239 ( $b[3] || '' ) .
240 ( $b[2] || '' ) .
241 ( $b[1] || '' ) .
242 ( $b[0] || '' );
243 $pos += 4;
244 }
245
246 $self->_write_chunk( $addr, $chunk );
247
248 return 1;
249 };
250
251 =head2 save_dump
252
253 $orao->save_dump( 'filename', $from, $to );
254
255 =cut
256
257 sub save_dump {
258 my $self = shift;
259
260 my ( $path, $from, $to ) = @_;
261
262 $from ||= 0;
263 $to ||= 0xffff;
264
265 open(my $fh, '>', $path) || die "can't open $path: $!";
266 print $fh $self->read_chunk( $from, $to );
267 close($fh);
268
269 my $size = -s $path;
270 warn sprintf "saved %s %d %x bytes\n", $path, $size, $size;
271 }
272
273 =head2 hexdump
274
275 $orao->hexdump( $address );
276
277 =cut
278
279 sub hexdump {
280 my $self = shift;
281 my $a = shift;
282 return sprintf(" %04x %s\n", $a,
283 join(" ",
284 map {
285 if ( defined($_) ) {
286 sprintf( "%02x", $_ )
287 } else {
288 ' '
289 }
290 } @mem[ $a .. $a+8 ]
291 )
292 );
293 }
294
295 =head1 Memory management
296
297 Orao implements all I/O using mmap addresses. This was main reason why
298 L<Acme::6502> was just too slow to handle it.
299
300 =cut
301
302 =head2 read
303
304 Read from memory
305
306 $byte = read( $address );
307
308 =cut
309
310 sub read {
311 my $self = shift;
312 my ($addr) = @_;
313 my $byte = $mem[$addr];
314 confess sprintf("can't find memory at address %04x",$addr) unless defined($byte);
315 warn sprintf("# Orao::read(%04x) = %02x\n", $addr, $byte) if $self->trace;
316
317 # keyboard
318
319 if ( first { $addr == $_ } @kbd_ports ) {
320 warn sprintf("keyboard port: %04x\n",$addr) if $self->trace;
321 } elsif ( $addr == 0x87fc ) {
322 warn "0x87fc - arrows/back\n";
323 =for pascal
324 if VKey=VK_RIGHT then Result:=16;
325 if VKey=VK_DOWN then Result:=128;
326 if VKey=VK_UP then Result:=192;
327 if VKey=VK_LEFT then Result:=224;
328 if Ord(KeyPressed)=VK_BACK then Result:=224;
329 =cut
330 } elsif ( $addr == 0x87fd ) {
331 warn "0x87fd - enter\n";
332 =for pascal
333 if KeyPressed=Chr(13) then begin
334 Mem[$FC]:=13;
335 Result:=0;
336 end;
337 =cut
338 } elsif ( $addr == 0x87fa ) {
339 warn "0x87fa = F1 - F4\n";
340 =for pascal
341 if VKey=VK_F4 then Result:=16;
342 if VKey=VK_F3 then Result:=128;
343 if VKey=VK_F2 then Result:=192;
344 if VKey=VK_F1 then Result:=224;
345 =cut
346 } elsif ( $addr == 0x87fb ) {
347 warn "0x87fb\n";
348 =for pascal
349 if KeyPressed=Chr(32) then Result:=32;
350 if KeyPressed='"' then Result:=16;
351 if KeyPressed='!' then Result:=16;
352 if KeyPressed='$' then Result:=16;
353 if KeyPressed='%' then Result:=16;
354 if KeyPressed='&' then Result:=16;
355 if KeyPressed='(' then Result:=16;
356 if KeyPressed=')' then Result:=16;
357 if KeyPressed='=' then Result:=16;
358 if KeyPressed='#' then Result:=16;
359 if KeyPressed='+' then Result:=16;
360 if KeyPressed='*' then Result:=16;
361 if KeyPressed='?' then Result:=16;
362 if KeyPressed='<' then Result:=16;
363 if KeyPressed='>' then Result:=16;
364 if VKey=191 then Result:=16;
365 =cut
366 }
367
368 $self->mmap_pixel( $addr, 0, $byte, 0 );
369 return $byte;
370 }
371
372 =head2 write
373
374 Write into emory
375
376 write( $address, $byte );
377
378 =cut
379
380 sub write {
381 my $self = shift;
382 my ($addr,$byte) = @_;
383 warn sprintf("# Orao::write(%04x,%02x)\n", $addr, $byte) if $self->trace;
384
385 if ( $addr >= 0x6000 && $addr < 0x8000 ) {
386 $self->vram( $addr - 0x6000 , $byte );
387 }
388
389 if ( $addr == 0x8800 ) {
390 warn sprintf "sound ignored: %x\n", $byte;
391 }
392
393 if ( $addr > 0xafff ) {
394 confess sprintf "write access 0x%04x > 0xafff aborting\n", $addr;
395 }
396
397 $self->mmap_pixel( $addr, $byte, 0, 0 );
398
399 $mem[$addr] = $byte;
400 return;
401 }
402
403 =head1 Command Line
404
405 Command-line debugging intrerface is implemented for communication with
406 emulated device
407
408 =head2 prompt
409
410 my ( $entered_line, @p ) = $orao->prompt( $address, $last_command );
411
412 =cut
413
414 my $last = 'r 1';
415
416 sub prompt {
417 my $self = shift;
418 $self->app->sync;
419 my $a = shift;
420 print $self->hexdump( $a ),
421 $last ? "[$last] " : '',
422 "> ";
423 my $in = <STDIN>;
424 chomp($in);
425 warn "## prompt got: $in\n" if $self->debug;
426 $in ||= $last;
427 $last = $in;
428 return ( $in, split(/\s+/, $in) ) if $in;
429 }
430
431 =head2 cli
432
433 $orao->cli();
434
435 =cut
436
437 my $show_R = 0;
438
439 sub cli {
440 my $self = shift;
441 my $a = $PC || confess "no pc?";
442 my $run_for = 0;
443 warn $self->dump_R() if $show_R;
444 while ( my ($line, @v) = $self->prompt( $a, $last ) ) {
445 my $c = shift @v;
446 next unless defined($c);
447 my $v = shift @v;
448 $v = hex($v) if $v && $v =~ m/^[0-9a-f]+$/;
449 @v = map { hex($_) } @v;
450 printf "## a: %04x parsed cli: c:%s v:%s %s\n", $a, $c, ($v || 'undef'), join(",",@v) if $self->debug;
451 if ( $c =~ m/^[qx]/i ) {
452 exit;
453 } elsif ( $c eq '?' ) {
454 my $t = $self->trace ? 'on' : 'off' ;
455 my $d = $self->debug ? 'on' : 'off' ;
456 warn <<__USAGE__;
457 Usage:
458
459 x|q\t\texit
460 e 6000 6010\tdump memory, +/- to walk forward/backward
461 m 1000 ff 00\tput ff 00 on 1000
462 j|u 1000\t\tjump (change pc)
463 r 42\t\trun 42 instruction opcodes
464 t\t\ttrace [$t]
465 d\t\tdebug [$d]
466
467 __USAGE__
468 warn $self->dump_R;
469 $last = '';
470 } elsif ( $c =~ m/^e/i ) {
471 $a = $v if defined($v);
472 my $to = shift @v;
473 $to = $a + 32 if ( ! $to || $to <= $a );
474 $to = 0xffff if ( $to > 0xffff );
475 my $lines = int( ($to - $a + 8) / 8 );
476 printf "## e %04x %04x (%d bytes) lines: %d\n", $a, $to, ($to-$a), $lines;
477 while ( --$lines ) {
478 print $self->hexdump( $a );
479 $a += 8;
480 }
481 $last = '+';
482 $show_R = 0;
483 } elsif ( $c =~ m/^\+/ ) {
484 $a += 8;
485 $show_R = 0;
486 } elsif ( $c =~ m/^\-/ ) {
487 $a -= 8;
488 $show_R = 0;
489 } elsif ( $c =~ m/^m/i ) {
490 $a = $v if defined($v);
491 $self->poke_code( $a, @v );
492 printf "poke %d bytes at %04x\n", $#v + 1, $a;
493 $last = '+';
494 $show_R = 0;
495 } elsif ( $c =~ m/^l/i ) {
496 my $to = shift @v || 0x1000;
497 $a = $to;
498 $self->load_image( $v, $a );
499 $last = '';
500 } elsif ( $c =~ m/^s/i ) {
501 $self->save_dump( $v || 'mem.dump', @v );
502 $last = '';
503 } elsif ( $c =~ m/^r/i ) {
504 $run_for = $v || 1;
505 print "run_for $run_for instructions\n";
506 $show_R = 1;
507 last;
508 } elsif ( $c =~ m/^(u|j)/ ) {
509 my $to = $v || $a;
510 printf "set pc to %04x\n", $to;
511 $PC = $to; # remember for restart
512 $run_for = 1;
513 $last = "r $run_for";
514 $show_R = 1;
515 last;
516 } elsif ( $c =~ m/^t/ ) {
517 $self->trace( not $self->trace );
518 print "trace ", $self->trace ? 'on' : 'off', "\n";
519 $last = '';
520 } elsif ( $c =~ m/^d/ ) {
521 $self->debug( not $self->debug );
522 print "debug ", $self->debug ? 'on' : 'off', "\n";
523 $last = '';
524 } else {
525 warn "# ignored $line\n" if ($line);
526 $last = '';
527 }
528 }
529
530 return $run_for;
531 }
532
533 =head1 AUTHOR
534
535 Dobrica Pavlinusic, C<< <dpavlin@rot13.org> >>
536
537 =head1 BUGS
538
539 =head1 ACKNOWLEDGEMENTS
540
541 See also L<http://www.foing.hr/~fng_josip/orao.htm> which is source of all
542 info about this machine (and even hardware implementation from 2007).
543
544 =head1 COPYRIGHT & LICENSE
545
546 Copyright 2007 Dobrica Pavlinusic, All Rights Reserved.
547
548 This program is free software; you can redistribute it and/or modify it
549 under the same terms as Perl itself.
550
551 =cut
552
553 1; # End of Orao

  ViewVC Help
Powered by ViewVC 1.1.26