… | |
… | |
32 | my $VT102 = 1; |
32 | my $VT102 = 1; |
33 | my $AVO = $VT102 || 1; |
33 | my $AVO = $VT102 || 1; |
34 | my $KBD = 1; |
34 | my $KBD = 1; |
35 | |
35 | |
36 | ############################################################################# |
36 | ############################################################################# |
37 | |
37 | # rom initialising |
38 | my $PTY; # the pty we allocated, if any |
|
|
39 | |
38 | |
40 | my $ROM = do { |
39 | my $ROM = do { |
41 | binmode DATA; |
40 | binmode DATA; |
42 | local $/; |
41 | local $/; |
43 | <DATA> |
42 | <DATA> |
… | |
… | |
56 | } else { |
55 | } else { |
57 | @M[0x0000 .. 0x1fff] = unpack "C*", substr $ROM, 0x0000, 0x2000; |
56 | @M[0x0000 .. 0x1fff] = unpack "C*", substr $ROM, 0x0000, 0x2000; |
58 | } |
57 | } |
59 | |
58 | |
60 | ############################################################################# |
59 | ############################################################################# |
|
|
60 | # cpu registers and I/O support |
61 | |
61 | |
|
|
62 | my $PTY; # the pty we allocated, if any |
62 | my $PRSTATUS = 0; |
63 | my $PRSTATUS = 0; |
63 | |
64 | |
64 | # 8080/8085 registers |
65 | # 8080/8085 registers |
65 | # b, c, d, e, h, l, a |
66 | # b, c, d, e, h, l, a |
66 | my ($A, $B, $C, $D, $E, $H, $L, $A); |
67 | my ($A, $B, $C, $D, $E, $H, $L, $A); |
… | |
… | |
73 | my $x; # dummy temp for instructions |
74 | my $x; # dummy temp for instructions |
74 | |
75 | |
75 | my $CLK; # rather inexact clock |
76 | my $CLK; # rather inexact clock |
76 | |
77 | |
77 | ############################################################################# |
78 | ############################################################################# |
|
|
79 | # the dreaded nvr1400 chip. not needed to get it going, but provided for reference |
78 | |
80 | |
79 | # nvram |
81 | # nvram |
80 | my @NVR = (0x3fff) x 100; # vt102: 214e accum, 214f only lower 8 bit used, first 44 bytes |
82 | my @NVR = (0x3fff) x 100; # vt102: 214e accum, 214f only lower 8 bit used, first 44 bytes |
81 | my $NVRADDR; |
83 | my $NVRADDR; |
82 | my $NVRDATA; |
84 | my $NVRDATA; |
83 | my $NVRLATCH; |
85 | my $NVRLATCH; |
84 | |
86 | |
85 | #$NVR[$_] = $_ for 0..99;#d# |
|
|
86 | |
|
|
87 | my @NVRCMD = ( |
87 | my @NVRCMD = ( |
88 | sub { # accept data |
|
|
89 | $NVRDATA = ($NVRDATA << 1) + $_[1]; |
88 | sub { $NVRDATA = ($NVRDATA << 1) + $_[1]; }, # 0 accept data |
90 | }, |
89 | sub { $NVRADDR = ($NVRADDR << 1) + $_[1]; }, # 1 accept addr |
91 | sub { # accept addr |
90 | sub { ($NVRDATA <<= 1) & 0x4000 }, # 2 shift out |
92 | $NVRADDR = ($NVRADDR << 1)+ $_[1]; |
91 | undef, # 3 not used, will barf |
93 | }, |
|
|
94 | sub { # shift out |
|
|
95 | my $bit = $NVRDATA & 0x02000; |
|
|
96 | $NVRDATA *= 2; |
|
|
97 | $bit |
|
|
98 | }, |
|
|
99 | undef, |
|
|
100 | sub { # write |
|
|
101 | print "NVR WRITE $_[0]\n";#d# |
|
|
102 | $NVR[$_[0]] = $NVRDATA & 0x3fff; |
92 | sub { $NVR[$_[0]] = $NVRDATA & 0x3fff; }, # 4 write |
103 | }, |
93 | sub { $NVR[$_[0]] = 0x3fff; }, # 5 erase |
104 | sub { # erase |
94 | sub { $NVRDATA = $NVR[$_[0]]; }, # 6 read |
105 | print "NVR ERASE $_[0]\n";#d# |
95 | sub { }, # 7 standby |
106 | $NVR[$_[0]] = 0x3fff; |
|
|
107 | }, |
|
|
108 | sub { # read |
|
|
109 | # print "NVR READ $_[0] = $NVR[$_[0]]\n";#d# |
|
|
110 | $NVRDATA = $NVR[$_[0]]; |
|
|
111 | }, |
|
|
112 | sub { # standby |
|
|
113 | }, |
|
|
114 | ); |
96 | ); |
115 | |
97 | |
116 | my @bitidx; |
98 | my @bitidx; |
117 | $bitidx[1 << $_] = 9 - $_ for 0..9; |
99 | $bitidx[1 << $_] = 9 - $_ for 0..9; |
118 | |
100 | |
… | |
… | |
126 | $NVRCMD[($NVRLATCH >> 1) & 7]($a1 * 10 + $a0, $NVRLATCH & 1) |
108 | $NVRCMD[($NVRLATCH >> 1) & 7]($a1 * 10 + $a0, $NVRLATCH & 1) |
127 | } |
109 | } |
128 | |
110 | |
129 | ############################################################################# |
111 | ############################################################################# |
130 | |
112 | |
131 | my $DC11 = 0; # 4 bit commands |
113 | my $DC11_REVERSE = 0; |
132 | my $DC12 = 0; |
|
|
133 | |
114 | |
134 | my $XON = 1; # false if terminal wants us to pause |
115 | my $XON = 1; # false if terminal wants us to pause |
135 | my $PUSARTCMD; |
116 | my $PUSARTCMD; |
136 | |
117 | |
137 | my @KXMIT; # current scan queue |
118 | my @KXMIT; # current scan queue |
… | |
… | |
173 | |
154 | |
174 | sub out_62 { |
155 | sub out_62 { |
175 | $NVRLATCH = shift; |
156 | $NVRLATCH = shift; |
176 | } |
157 | } |
177 | |
158 | |
178 | sub out_a2 { $DC11 = shift } |
159 | sub out_a2 { |
|
|
160 | my $dc11 = 0x0f & shift; |
|
|
161 | |
|
|
162 | $DC11_REVERSE = 1 if $dc11 == 0b1010; |
|
|
163 | $DC11_REVERSE = 0 if $dc11 == 0b1011; |
|
|
164 | } |
|
|
165 | |
179 | sub out_c2 { } # unknown |
166 | sub out_c2 { } # unknown |
180 | sub out_d2 { $DC12 = shift } |
167 | sub out_d2 { } # 0..3 == 80c/132c/60hz/50hz |
181 | |
168 | |
182 | sub out_82 { |
169 | sub out_82 { |
183 | # keyboard |
170 | # keyboard |
184 | |
171 | |
185 | # CLICK STARTSCAN ONLINE LOCKED | CTS DSR INSERT L1(?) |
172 | # CLICK STARTSCAN ONLINE LOCKED | CTS DSR INSERT L1(?) |
… | |
… | |
453 | } |
440 | } |
454 | |
441 | |
455 | ############################################################################# |
442 | ############################################################################# |
456 | |
443 | |
457 | my @chr = ( |
444 | my @chr = ( |
458 | " ", |
445 | " " , "\x{29eb}", "\x{2592}", "\x{2409}", |
459 | "\x{29eb}", |
446 | "\x{240c}", "\x{240d}", "\x{240a}", "\x{00b0}", |
460 | "\x{2592}", |
447 | "\x{00b1}", "\x{2424}", "\x{240b}", "\x{2518}", |
461 | "\x{2409}", |
448 | "\x{2510}", "\x{250c}", "\x{2514}", "\x{253c}", |
462 | "\x{240c}", |
449 | "\x{23ba}", "\x{23bb}", "\x{2500}", "\x{23bc}", |
463 | "\x{240d}", |
450 | "\x{23bd}", "\x{251c}", "\x{2524}", "\x{2534}", |
464 | "\x{240a}", |
451 | "\x{252c}", "\x{2502}", "\x{2264}", "\x{2265}", |
465 | "\x{00b0}", |
452 | "\x{03c0}", "\x{2260}", "\x{00a3}", "\x{00b7}", |
466 | "\x{00b1}", |
|
|
467 | "\x{2424}", |
|
|
468 | "\x{240b}", |
|
|
469 | "\x{2518}", |
|
|
470 | "\x{2510}", |
|
|
471 | "\x{250c}", |
|
|
472 | "\x{2514}", |
|
|
473 | "\x{253c}", |
|
|
474 | "\x{23ba}", |
|
|
475 | "\x{23bb}", |
|
|
476 | "\x{2500}", |
|
|
477 | "\x{23bc}", |
|
|
478 | "\x{23bd}", |
|
|
479 | "\x{251c}", |
|
|
480 | "\x{2524}", |
|
|
481 | "\x{2534}", |
|
|
482 | "\x{252c}", |
|
|
483 | "\x{2502}", |
|
|
484 | "\x{2264}", |
|
|
485 | "\x{2265}", |
|
|
486 | "\x{03c0}", |
|
|
487 | "\x{2260}", |
|
|
488 | "\x{00a3}", |
|
|
489 | "\x{00b7}", |
|
|
490 | (map chr, 0x020 .. 0x7e), |
453 | (map chr, 0x020 .. 0x7e), |
491 | "?", |
|
|
492 | ); |
454 | ); |
493 | |
455 | |
494 | utf8::encode $_ for @chr; |
456 | utf8::encode $_ for @chr; |
|
|
457 | |
|
|
458 | my @sgr; # sgr sequences for attributes |
|
|
459 | |
|
|
460 | for (0x00 .. 0xff) { |
|
|
461 | my $sgr = ""; |
|
|
462 | |
|
|
463 | $sgr .= ";5" unless $_ & 0x01; |
|
|
464 | $sgr .= ";4" unless $_ & 0x02; |
|
|
465 | $sgr .= ";1" unless $_ & 0x04; |
|
|
466 | $sgr .= ";7" if $_ & 0x80; |
|
|
467 | |
|
|
468 | $sgr[$_] = "\e[${sgr}m"; |
|
|
469 | } |
495 | |
470 | |
496 | sub prscr { |
471 | sub prscr { |
497 | my $i = 0x2000; |
472 | my $i = 0x2000; |
498 | |
473 | |
499 | my $scr = sprintf "\x1b[H--- KBD %08b CLK %d PC %04x RST %03b IFF %01b PUS %02x IM %03b\x1b[K\n", $KSTATUS, $CLK, $PC, $RST, $IFF, $PUSARTCMD, $INTMASK; |
474 | my $scr = sprintf "\e[H--- KBD %08b CLK %d\e[K\n", $KSTATUS, $CLK; |
500 | |
475 | |
|
|
476 | $scr .= "\e[?5" . ($DC11_REVERSE ? "h" : "l"); |
|
|
477 | |
501 | line: |
478 | line: |
502 | for my $y (0 .. 25) { |
479 | for my $y (0 .. 25) { # ntsc, two vblank delay lines, up to 24 text lines |
|
|
480 | my $prev_sgr; |
|
|
481 | |
503 | $scr .= sprintf "%2d |", ++$y; |
482 | $scr .= sprintf "%2d |", ++$y; |
504 | |
483 | |
505 | for (0..140) { |
484 | for (0..139) { |
506 | my $c = $M[$i++]; |
485 | my $c = $M[$i]; |
507 | |
|
|
508 | # printf "%04x %02x\n", $i-1,$c; |
|
|
509 | |
486 | |
510 | if ($c == 0x7f) { # also 0xff, but the firmware avoids that |
487 | if ($c == 0x7f) { # also 0xff, but the firmware avoids that |
511 | $scr .= "|\x1b[K\n"; |
488 | $scr .= "\e[m|\e[K\n"; |
512 | |
489 | |
513 | my $a1 = $M[$i++]; |
490 | my $a1 = $M[$i + 1]; |
514 | my $a0 = $M[$i++]; |
491 | my $a0 = $M[$i + 2]; |
515 | |
492 | |
516 | $i = 0x2000 + (($a1 * 256 + $a0) & 0xfff); |
493 | $i = 0x2000 + (($a1 * 256 + $a0) & 0xfff); |
517 | |
494 | |
518 | next line; |
495 | next line; |
519 | } |
496 | } |
520 | |
497 | |
521 | $scr .= "\x1b[7m" if $c & 0x80; |
498 | my $sgr = $sgr[ ($M[$i++ + 0x1000] & 15) | ($c & 0x80)]; |
522 | $scr .= $chr[$c & 0x7f] // sprintf "[%02x]", $c & 0x7f; |
499 | |
523 | $scr .= "\x1b[m" if $c & 0x80; |
500 | # ~1 sgr 5 blink |
|
|
501 | # ~2 sgr 4 underline |
|
|
502 | # ~4 sgr 1 bold |
|
|
503 | # 0x80 in attr, sgr 7, reversed |
|
|
504 | |
|
|
505 | $scr .= $prev_sgr = $sgr if $sgr ne $prev_sgr; |
|
|
506 | |
|
|
507 | $scr .= $chr[$c & 0x7f]; |
524 | } |
508 | } |
525 | |
509 | |
526 | $scr .= "\x1b[K\noverflow\x1b[K\n"; |
510 | $scr .= "\e[K\nvideo overflow\e[K\n"; |
527 | last; |
511 | last; |
528 | } |
512 | } |
529 | |
513 | |
|
|
514 | $scr .= "\e[m"; |
|
|
515 | |
530 | if (0) { |
516 | if (0) { |
531 | $scr .= "\x1b[K\n"; |
517 | $scr .= "\e[K\n"; |
532 | for my $o (0x200 .. 0x232) { |
518 | for my $o (0x200 .. 0x232) { |
533 | $scr .= sprintf "%04x:", $o * 16; |
519 | $scr .= sprintf "%04x:", $o * 16; |
534 | for (0..15) { |
520 | for (0..15) { |
535 | $scr .= sprintf " %02x", $M[$o * 16 + $_]; |
521 | $scr .= sprintf " %02x", $M[$o * 16 + $_]; |
536 | } |
522 | } |
537 | $scr .= "\x1b[K\n"; |
523 | $scr .= "\e[K\n"; |
538 | } |
524 | } |
539 | } |
525 | } |
540 | |
526 | |
541 | $scr .= "\x1b[J"; |
527 | $scr .= "\e[J"; |
542 | |
528 | |
543 | syswrite STDOUT, $scr; |
529 | syswrite STDOUT, $scr; |
544 | } |
530 | } |
545 | |
531 | |
546 | ############################################################################# |
532 | ############################################################################# |
… | |
… | |
727 | eval "use integer; sub { $insn }" or die "$insn: $@" |
713 | eval "use integer; sub { $insn }" or die "$insn: $@" |
728 | })->(); |
714 | })->(); |
729 | |
715 | |
730 | ++$CLK; |
716 | ++$CLK; |
731 | |
717 | |
732 | #TODO: just check on ret instructions or so |
718 | # things we do from time too time only |
|
|
719 | unless ($CLK & 0xf) { |
|
|
720 | # do I/O |
|
|
721 | |
|
|
722 | unless ($CLK & 0x7ff) { |
|
|
723 | |
|
|
724 | # pty/serial I/O |
|
|
725 | unless (@PUSARTRECV || @KQUEUE || !$PTY) { |
|
|
726 | my $rin = ""; (vec $rin, fileno $PTY, 1) = 1; |
|
|
727 | |
|
|
728 | if (select $rin, undef, undef, 0) { |
|
|
729 | sysread $PTY, my $buf, 256; |
|
|
730 | push @PUSARTRECV, unpack "C*", $buf; |
|
|
731 | } |
|
|
732 | } |
|
|
733 | |
|
|
734 | # keyboard input |
|
|
735 | if ($KBD) { |
|
|
736 | while (select my $rin = "\x01", undef, undef, 0) { |
|
|
737 | sysread STDIN, $STDIN_BUF, 1, length $STDIN_BUF |
|
|
738 | or last; |
|
|
739 | } |
|
|
740 | |
|
|
741 | stdin_parse if length $STDIN_BUF; |
|
|
742 | } |
|
|
743 | } |
|
|
744 | |
|
|
745 | # kick off various interrupts |
|
|
746 | |
|
|
747 | $RST |= 2 if @PUSARTRECV && $XON; # VT100, but works on vt102, too (probably not used on real hardware though) |
|
|
748 | #$INTPEND |= 2 if @PUSARTRECV && $XON; # VT102, 6.5 rxrdy |
|
|
749 | |
|
|
750 | # kick off vertical retrace form time to time |
|
|
751 | unless ($CLK & 0x1ff) { |
|
|
752 | $RST |= 4; # vertical retrace |
|
|
753 | } |
|
|
754 | |
|
|
755 | # handle video hardware |
|
|
756 | |
|
|
757 | unless ($CLK & 0x1fff) { |
|
|
758 | prscr; |
|
|
759 | } |
|
|
760 | } |
|
|
761 | |
733 | # the interrupt logic |
762 | # the interrupt logic |
734 | $x = $INTPEND & ~$INTMASK; |
763 | $x = $INTPEND & ~$INTMASK; |
735 | |
|
|
736 | if (($RST || $x) && $IFF) { |
764 | if (($RST || $x) && $IFF) { |
737 | # rst 1 kbd data available |
765 | # rst 1 kbd data available |
738 | # rst 2 pusart xmit+recv flag |
766 | # rst 2 pusart xmit+recv flag |
739 | # rst 4 vertical retrace |
767 | # rst 4 vertical retrace |
740 | # 5.5 vt125 mb7 trans ready (serial send?) |
768 | # 5.5 vt125 mb7 trans ready (serial send?) |
… | |
… | |
757 | $M[--$SP] = $PC >> 8; |
785 | $M[--$SP] = $PC >> 8; |
758 | $M[--$SP] = $PC & 0xff; |
786 | $M[--$SP] = $PC & 0xff; |
759 | $PC = $vec; |
787 | $PC = $vec; |
760 | |
788 | |
761 | $IFF = 0; |
789 | $IFF = 0; |
762 | } |
|
|
763 | |
|
|
764 | # things we do from time too time only |
|
|
765 | unless ($CLK & 0xf) { |
|
|
766 | # do I/O |
|
|
767 | |
|
|
768 | unless ($CLK & 0x7ff) { |
|
|
769 | |
|
|
770 | # pty/serial I/O |
|
|
771 | unless (@PUSARTRECV || @KQUEUE || !$PTY) { |
|
|
772 | my $rin = ""; (vec $rin, fileno $PTY, 1) = 1; |
|
|
773 | |
|
|
774 | if (select $rin, undef, undef, 0) { |
|
|
775 | sysread $PTY, my $buf, 256; |
|
|
776 | push @PUSARTRECV, unpack "C*", $buf; |
|
|
777 | } |
|
|
778 | } |
|
|
779 | |
|
|
780 | # keyboard input |
|
|
781 | if ($KBD) { |
|
|
782 | while (select my $rin = "\x01", undef, undef, 0) { |
|
|
783 | sysread STDIN, $STDIN_BUF, 1, length $STDIN_BUF |
|
|
784 | or last; |
|
|
785 | } |
|
|
786 | |
|
|
787 | stdin_parse if length $STDIN_BUF; |
|
|
788 | } |
|
|
789 | } |
|
|
790 | |
|
|
791 | # kick off various interrupts |
|
|
792 | |
|
|
793 | $RST |= 2 if @PUSARTRECV && $XON; # VT100, but works on vt102, too (probably not used on real hardware though) |
|
|
794 | #$INTPEND |= 2 if @PUSARTRECV && $XON; # VT102, 6.5 rxrdy |
|
|
795 | |
|
|
796 | # kick off vertical retrace form time to time |
|
|
797 | unless ($CLK & 0x3ff) { |
|
|
798 | $RST |= 4; # vertical retrace |
|
|
799 | } |
|
|
800 | |
|
|
801 | # handle video hardware |
|
|
802 | |
|
|
803 | unless ($CLK & 0x1fff) { |
|
|
804 | prscr; |
|
|
805 | } |
|
|
806 | } |
790 | } |
807 | } |
791 | } |
808 | |
792 | |
809 | __DATA__ |
793 | __DATA__ |
810 | 1N ; 0 >b/BWog<Gӂ,O $
O [ xI,ڥ # yOt ͤ[ zW>/2!b>>g$>%
!h w-!h >-4!j pO:{ y:! u:x!_yA[>y
>yA[>?y@
:x!yA[P>>O[>>[Î:!ʵyA>>OlyAPÇ!:!S!h ~ ~ : O͓: Ô!20!2!!!yAG~"&=w< w:!/!!A:!Ey2!~1N ! ~eBi<2!͢:P =2S!~6ʘ!!6 |
794 | 1N ; 0 >b/BWog<Gӂ,O $
O [ xI,ڥ # yOt ͤ[ zW>/2!b>>g$>%
!h w-!h >-4!j pO:{ y:! u:x!_yA[>y
>yA[>?y@
:x!yA[P>>O[>>[Î:!ʵyA>>OlyAPÇ!:!S!h ~ ~ : O͓: Ô!20!2!!!yAG~"&=w< w:!/!!A:!Ey2!~1N ! ~eBi<2!͢:P =2S!~6ʘ!!6 |