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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 96 - (show annotations)
Thu Aug 2 13:58:26 2007 UTC (16 years, 8 months ago) by dpavlin
Original Path: M6502/Orao.pm
File size: 11149 byte(s)
added SDL event loop around CPU emulation
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->boot if ( ! $self->booted );
144 $self->loop;
145 };
146
147 =head1 Helper functions
148
149 =head2 load_rom
150
151 called to init memory and load initial rom images
152
153 $orao->load_rom;
154
155 =cut
156
157 sub load_rom {
158 my ($self, $loaded_files) = @_;
159
160 #my $time_base = time();
161
162 foreach my $addr ( sort keys %$loaded_files ) {
163 my $path = $loaded_files->{$addr};
164 $self->load_image( $path, $addr );
165 }
166 }
167
168 # write chunk directly into memory, updateing vram if needed
169 sub _write_chunk {
170 my $self = shift;
171 my ( $addr, $chunk ) = @_;
172 $self->write_chunk( $addr, $chunk );
173 my $end = $addr + length($chunk);
174 my ( $f, $t ) = ( 0x6000, 0x7fff );
175
176 if ( $end < $f || $addr >= $t ) {
177 warn "skip vram update\n";
178 return;
179 };
180
181 $f = $addr if ( $addr > $f );
182 $t = $end if ( $end < $t );
183
184 warn sprintf("refresh video ram %04x-%04x\n", $f, $t);
185 # foreach my $a ( $f .. $t ) {
186 # $self->vram( $a - 0x6000 , $mem[ $a ] );
187 # }
188 $self->render( @mem[ 0x6000 .. 0x7fff ] );
189 $self->render_mem( @mem ) if $self->show_mem;
190 }
191
192 =head2 load_image
193
194 Load binary files, ROM images and Orao Emulator files
195
196 $orao->load_image( '/path/to/file', 0x1000 );
197
198 Returns true on success.
199
200 =cut
201
202 sub load_image {
203 my $self = shift;
204 my ( $path, $addr ) = @_;
205
206 if ( ! -e $path ) {
207 warn "ERROR: file $path doesn't exist\n";
208 return;
209 }
210
211 my $size = -s $path || confess "no size for $path: $!";
212
213 my $buff = read_file( $path );
214
215 if ( $size == 65538 ) {
216 $addr = 0;
217 warn sprintf "loading oraoemu 64k dump %s at %04x - %04x %02x\n", $path, $addr, $addr+$size-1, $size;
218 $self->_write_chunk( $addr, substr($buff,2) );
219 return 1;
220 } elsif ( $size == 32800 ) {
221 $addr = 0;
222 warn sprintf "loading oraoemu 1.3 dump %s at %04x - %04x %02x\n", $path, $addr, $addr+$size-1, $size;
223 $self->_write_chunk( $addr, substr($buff,0x20) );
224 return 1;
225 }
226 printf "loading %s at %04x - %04x %02x\n", $path, $addr, $addr+$size-1, $size;
227 $self->_write_chunk( $addr, $buff );
228 return 1;
229
230 my $chunk;
231
232 my $pos = 0;
233
234 while ( my $long = substr($buff,$pos,4) ) {
235 my @b = split(//, $long, 4);
236 $chunk .=
237 ( $b[3] || '' ) .
238 ( $b[2] || '' ) .
239 ( $b[1] || '' ) .
240 ( $b[0] || '' );
241 $pos += 4;
242 }
243
244 $self->_write_chunk( $addr, $chunk );
245
246 return 1;
247 };
248
249 =head2 save_dump
250
251 $orao->save_dump( 'filename', $from, $to );
252
253 =cut
254
255 sub save_dump {
256 my $self = shift;
257
258 my ( $path, $from, $to ) = @_;
259
260 $from ||= 0;
261 $to ||= 0xffff;
262
263 open(my $fh, '>', $path) || die "can't open $path: $!";
264 print $fh $self->read_chunk( $from, $to );
265 close($fh);
266
267 my $size = -s $path;
268 warn sprintf "saved %s %d %x bytes\n", $path, $size, $size;
269 }
270
271 =head2 hexdump
272
273 $orao->hexdump( $address );
274
275 =cut
276
277 sub hexdump {
278 my $self = shift;
279 my $a = shift;
280 return sprintf(" %04x %s\n", $a,
281 join(" ",
282 map {
283 if ( defined($_) ) {
284 sprintf( "%02x", $_ )
285 } else {
286 ' '
287 }
288 } @mem[ $a .. $a+8 ]
289 )
290 );
291 }
292
293 =head1 Memory management
294
295 Orao implements all I/O using mmap addresses. This was main reason why
296 L<Acme::6502> was just too slow to handle it.
297
298 =cut
299
300 =head2 read
301
302 Read from memory
303
304 $byte = read( $address );
305
306 =cut
307
308 sub read {
309 my $self = shift;
310 my ($addr) = @_;
311 my $byte = $mem[$addr];
312 confess sprintf("can't find memory at address %04x",$addr) unless defined($byte);
313 warn sprintf("# Orao::read(%04x) = %02x\n", $addr, $byte) if $self->trace;
314
315 # keyboard
316
317 if ( first { $addr == $_ } @kbd_ports ) {
318 warn sprintf("keyboard port: %04x\n",$addr);
319 } elsif ( $addr == 0x87fc ) {
320 warn "0x87fc - arrows/back\n";
321 =for pascal
322 if VKey=VK_RIGHT then Result:=16;
323 if VKey=VK_DOWN then Result:=128;
324 if VKey=VK_UP then Result:=192;
325 if VKey=VK_LEFT then Result:=224;
326 if Ord(KeyPressed)=VK_BACK then Result:=224;
327 =cut
328 } elsif ( $addr == 0x87fd ) {
329 warn "0x87fd - enter\n";
330 =for pascal
331 if KeyPressed=Chr(13) then begin
332 Mem[$FC]:=13;
333 Result:=0;
334 end;
335 =cut
336 } elsif ( $addr == 0x87fa ) {
337 warn "0x87fa = F1 - F4\n";
338 =for pascal
339 if VKey=VK_F4 then Result:=16;
340 if VKey=VK_F3 then Result:=128;
341 if VKey=VK_F2 then Result:=192;
342 if VKey=VK_F1 then Result:=224;
343 =cut
344 } elsif ( $addr == 0x87fb ) {
345 warn "0x87fb\n";
346 =for pascal
347 if KeyPressed=Chr(32) then Result:=32;
348 if KeyPressed='"' then Result:=16;
349 if KeyPressed='!' then Result:=16;
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 VKey=191 then Result:=16;
363 =cut
364 }
365
366 $self->mmap_pixel( $addr, 0, $byte, 0 );
367 return $byte;
368 }
369
370 =head2 write
371
372 Write into emory
373
374 write( $address, $byte );
375
376 =cut
377
378 sub write {
379 my $self = shift;
380 my ($addr,$byte) = @_;
381 warn sprintf("# Orao::write(%04x,%02x)\n", $addr, $byte) if $self->trace;
382
383 if ( $addr >= 0x6000 && $addr < 0x8000 ) {
384 $self->vram( $addr - 0x6000 , $byte );
385 }
386
387 if ( $addr == 0x8800 ) {
388 warn sprintf "sound ignored: %x\n", $byte;
389 }
390
391 if ( $addr > 0xafff ) {
392 confess sprintf "write access 0x%04x > 0xafff aborting\n", $addr;
393 }
394
395 $self->mmap_pixel( $addr, $byte, 0, 0 );
396
397 $mem[$addr] = $byte;
398 return;
399 }
400
401 =head1 Command Line
402
403 Command-line debugging intrerface is implemented for communication with
404 emulated device
405
406 =head2 prompt
407
408 my ( $entered_line, @p ) = $orao->prompt( $address, $last_command );
409
410 =cut
411
412 my $last = 'r 1';
413
414 sub prompt {
415 my $self = shift;
416 $self->app->sync;
417 my $a = shift;
418 print STDERR $self->hexdump( $a ),
419 $last ? "[$last] " : '',
420 "> ";
421 my $in = <STDIN>;
422 chomp($in);
423 warn "## prompt got: $in\n" if $self->debug;
424 $in ||= $last;
425 $last = $in;
426 return ( $in, split(/\s+/, $in) ) if $in;
427 }
428
429 =head2 cli
430
431 $orao->cli();
432
433 =cut
434
435 my $show_R = 0;
436
437 sub cli {
438 my $self = shift;
439 my $a = $PC || confess "no pc?";
440 my $run_for = 0;
441 warn $self->dump_R() if $show_R;
442 while ( my ($line, @v) = $self->prompt( $a, $last ) ) {
443 my $c = shift @v;
444 next unless defined($c);
445 my $v = shift @v;
446 $v = hex($v) if $v && $v =~ m/^[0-9a-f]+$/;
447 @v = map { hex($_) } @v;
448 printf "## a: %04x parsed cli: c:%s v:%s %s\n", $a, $c, ($v || 'undef'), join(",",@v) if $self->debug;
449 if ( $c =~ m/^[qx]/i ) {
450 exit;
451 } elsif ( $c eq '?' ) {
452 my $t = $self->trace ? 'on' : 'off' ;
453 my $d = $self->debug ? 'on' : 'off' ;
454 warn <<__USAGE__;
455 Usage:
456
457 x|q\t\texit
458 e 6000 6010\tdump memory, +/- to walk forward/backward
459 m 1000 ff 00\tput ff 00 on 1000
460 j|u 1000\t\tjump (change pc)
461 r 42\t\trun 42 instruction opcodes
462 t\t\ttrace [$t]
463 d\t\tdebug [$d]
464
465 __USAGE__
466 warn $self->dump_R;
467 } elsif ( $c =~ m/^e/i ) {
468 $a = $v if defined($v);
469 my $to = shift @v;
470 $to = $a + 32 if ( ! $to || $to <= $a );
471 $to = 0xffff if ( $to > 0xffff );
472 my $lines = int( ($to - $a + 8) / 8 );
473 printf "## e %04x %04x (%d bytes) lines: %d\n", $a, $to, ($to-$a), $lines;
474 while ( --$lines ) {
475 print $self->hexdump( $a );
476 $a += 8;
477 }
478 $last = '+';
479 $show_R = 0;
480 } elsif ( $c =~ m/^\+/ ) {
481 $a += 8;
482 $show_R = 0;
483 } elsif ( $c =~ m/^\-/ ) {
484 $a -= 8;
485 $show_R = 0;
486 } elsif ( $c =~ m/^m/i ) {
487 $a = $v if defined($v);
488 $self->poke_code( $a, @v );
489 printf "poke %d bytes at %04x\n", $#v + 1, $a;
490 $last = '+';
491 $show_R = 0;
492 } elsif ( $c =~ m/^l/i ) {
493 my $to = shift @v || 0x1000;
494 $a = $to;
495 $self->load_image( $v, $a );
496 $last = '';
497 } elsif ( $c =~ m/^s/i ) {
498 $self->save_dump( $v || 'mem.dump', @v );
499 $last = '';
500 } elsif ( $c =~ m/^r/i ) {
501 $run_for = $v || 1;
502 print "run_for $run_for instructions\n";
503 $show_R = 1;
504 last;
505 } elsif ( $c =~ m/^(u|j)/ ) {
506 my $to = $v || $a;
507 printf "set pc to %04x\n", $to;
508 $PC = $to; # remember for restart
509 $run_for = 1;
510 $last = "r $run_for";
511 $show_R = 1;
512 last;
513 } elsif ( $c =~ m/^t/ ) {
514 $self->trace( not $self->trace );
515 print "trace ", $self->trace ? 'on' : 'off', "\n";
516 $last = '';
517 } elsif ( $c =~ m/^d/ ) {
518 $self->debug( not $self->debug );
519 print "debug ", $self->debug ? 'on' : 'off', "\n";
520 $last = '';
521 } else {
522 warn "# ignored $line\n" if ($line);
523 $last = '';
524 }
525 }
526
527 return $run_for;
528 }
529
530 =head1 AUTHOR
531
532 Dobrica Pavlinusic, C<< <dpavlin@rot13.org> >>
533
534 =head1 BUGS
535
536 =head1 ACKNOWLEDGEMENTS
537
538 See also L<http://www.foing.hr/~fng_josip/orao.htm> which is source of all
539 info about this machine (and even hardware implementation from 2007).
540
541 =head1 COPYRIGHT & LICENSE
542
543 Copyright 2007 Dobrica Pavlinusic, All Rights Reserved.
544
545 This program is free software; you can redistribute it and/or modify it
546 under the same terms as Perl itself.
547
548 =cut
549
550 1; # End of Orao

  ViewVC Help
Powered by ViewVC 1.1.26