--- vt102/vt102 2014/12/01 20:12:07 1.9 +++ vt102/vt102 2014/12/03 02:07:44 1.10 @@ -17,32 +17,82 @@ # If this file contains embedded ROMs, the above copyright notice does # not apply to them. -# this hack is not considered release ready in and way, shape, or form -# ./vt102 bash -# ./vt102 telnet towel.blinkenlights.nl -# ./vt102 curl http://artscene.textfiles.com/vt100/trekvid.vt -# ./vt102 curl http://artscene.textfiles.com/vt100/surf.vt # in 3d! - use strict; -use feature qw(state); #use common::sense; -$| = 1; - my $VT102 = 1; -my $AVO = $VT102 || 1; +my $VT131 = 0; +my $AVO = 1; my $KBD = 1; +if ($ARGV[0] =~ /^-?-vt100$/) { + shift; $VT102 = 0; $AVO = 0; +} + +if ($ARGV[0] =~ /^-?-vt100\+avo$/) { + shift; $VT102 = 0; $AVO = 1; +} + +if ($ARGV[0] =~ /^-?-vt102$/) { + shift; # default +} + +if ($ARGV[0] =~ /^-?-vt131$/) { + shift; $VT131 = 1; +} + +if ($ARGV[0] =~ /^-/) { + die < + +EOF +} + ############################################################################# -# rom initialising +# ROM/hardware init -my $ROM = do { +my $ROMS = do { binmode DATA; local $/; }; -0x6001 == length $ROM or die "corrupted rom image"; +0x6801 == length $ROMS or die "corrupted rom image"; binmode STDOUT; @@ -50,17 +100,17 @@ # populate mem with rom contents if ($VT102) { - @M[0x0000 .. 0x1fff] = unpack "C*", substr $ROM, 0x2000, 0x2000; - @M[0x8000 .. 0x9fff] = unpack "C*", substr $ROM, 0x4000, 0x2000; + @M[0x0000 .. 0x1fff] = unpack "C*", substr $ROMS, 0x2000, 0x2000; + @M[0x8000 .. 0x9fff] = unpack "C*", substr $ROMS, 0x4000, 0x2000; + @M[0xa000 .. 0xa7ff] = unpack "C*", substr $ROMS, 0x6000, 0x0800 if $VT131; } else { - @M[0x0000 .. 0x1fff] = unpack "C*", substr $ROM, 0x0000, 0x2000; + @M[0x0000 .. 0x1fff] = unpack "C*", substr $ROMS, 0x0000, 0x2000; } ############################################################################# -# cpu registers and I/O support +# 8085 CPU registers and I/O support my $PTY; # the pty we allocated, if any -my $PRSTATUS = 0; # 8080/8085 registers # b, c, d, e, h, l, a @@ -76,7 +126,7 @@ my $CLK; # rather inexact clock ############################################################################# -# the dreaded nvr1400 chip. not needed to get it going, but provided for reference +# the dreaded NVR1400 chip. not needed to get it going, but provided anyway # nvram my @NVR = (0x3fff) x 100; # vt102: 214e accum, 214f only lower 8 bit used, first 44 bytes @@ -109,6 +159,7 @@ } ############################################################################# +# I/O ports - output my $DC11_REVERSE = 0; @@ -169,8 +220,8 @@ sub out_82 { # keyboard - # CLICK STARTSCAN ONLINE LOCKED | CTS DSR INSERT L1(?) - # CLICK STARTSCAN ONLINE LOCKED | LED1 LED2 LED3 LED4 + # CLICK STARTSCAN ONLINE LOCKED | CTS DSR INS L1 + # CLICK STARTSCAN ONLINE LOCKED | L1 L2 L3 L4 $KSTATUS = $_[0]; # start new scan unless scan in progress @@ -203,9 +254,10 @@ } ############################################################################# +# I/O ports - input my $NVRBIT; -my $LBA; +my $LBA6; # twice the frequenxy of LBA7 sub in_00 { # pusart data # interrupt not generated here, because infinite @@ -224,19 +276,19 @@ 0x20 } -sub in_0f { } # unknown, connected to out 2f +sub in_0f { 0xff } # vt102 unknown, connected to out 2f sub in_42 { # flag buffer - ++$LBA; + ++$LBA6; - $NVRBIT = nvr ? 0x20 : 0x00 if ($LBA & 0x3) == 0x2; + $NVRBIT = nvr ? 0x20 : 0x00 if ($LBA6 & 0x3) == 0x2; # KBD_XMITEMPTY LBA7 NVRDATA ODDFIELD - OPTION !GFX !AVO PUSART_TXRDY my $f = 0x85 | $NVRBIT; $f |= 0x02 unless $AVO; - $f |= 0x40 if $LBA & 0x2; + $f |= 0x40 if $LBA6 & 0x2; $f } @@ -248,56 +300,73 @@ shift @KXMIT } -sub in_03 { 0xff } # unknown, printer uart input? -sub in_0b { 0xff } # unknown -sub in_17 { 0xff } # unknown, printer status clear by reading? -sub in_1b { 0xff } # unknown +sub in_03 { 0xff } # vt102 unknown, printer uart input? +sub in_0b { 0xff } # vt102 unknown +sub in_17 { 0xff } # vt102 unknown, printer status clear by reading? +sub in_1b { 0xff } # vt102 unknown ############################################################################# +# 8085 cpu opcodes and flag handling sub sf { # set flags (ZSC - AP not implemented) - $FS = $_[0] & 0x080; - $FZ = ($_[0] & 0x0ff) == 0; - $FC = $_[0] & 0x100; + $FS = $_[0] & 0x080; + $FZ = !($_[0] & 0x0ff); + $FC = $_[0] & 0x100; + + $_[0] &= 0xff; +} - $_[0] & 0xff +sub sf8 { # set flags (ZSC - AP not implemented) + $FS = $_[0] & 0x080; + $FZ = !($_[0] & 0x0ff); + $FC = 0; } sub sf_nc { # set flags except carry $FS = $_[0] & 0x080; $FZ = ($_[0] & 0x0ff) == 0; - $_[0] & 0xff + $_[0] &= 0xff; } -my @op = map { sprintf "status(); die 'unknown op %02x'", $_ } 0 .. 255; -my @ops; +my @op = map { sprintf "status(); die 'unknown op %02x'", $_ } 0x00 .. 0xff; my @reg = qw($B $C $D $E $H $L $M[$H*256+$L] $A); my @cc = ('if !$FZ', 'if $FZ', 'if !$FC', 'if $FC', ';die', ';die', 'if !$FS', 'if $FS'); # die == unimplemented $FP parity +$op[0x00] = ''; + # mov r,r / r,M / M,r for my $s (0..7) { for my $d (0..7) { - $op[0x40 + $d * 8 + $s] = "$reg[$d] = $reg[$s]"; + $op[0x40 + $d * 8 + $s] = "$reg[$d] = $reg[$s]"; # mov } } -$op[0x00] = ''; +$op[0x76] = 'die "HLT"'; # hlt (mov m,m) + +# mvi r / M +$op[0x06 + $_ * 8] = "$reg[$_] = IMM8" for 0..7; $op[0x01] = '($B, $C) = (IMM16 >> 8, IMM16 & 0xff)'; # lxi $op[0x11] = '($D, $E) = (IMM16 >> 8, IMM16 & 0xff)'; # lxi $op[0x21] = '($H, $L) = (IMM16 >> 8, IMM16 & 0xff)'; # lxi -$op[0x31] = '$SP = IMM16' ; # lxi #d# 0xf000 because of limited stack +$op[0x31] = '$SP = IMM16' ; # lxi $op[0x02] = '$M[$B * 256 + $C] = $A'; # stax $op[0x12] = '$M[$D * 256 + $E] = $A'; # stax $op[0x32] = '$M[IMM16 ] = $A'; # sta +$op[0x0a] = '$A = $M[$B * 256 + $C]'; # ldax b +$op[0x1a] = '$A = $M[$D * 256 + $E]'; # ldax d +$op[0x3a] = '$A = $M[IMM16]'; # lda + +$op[0x22] = '($M[IMM16], $M[IMM16 + 1]) = ($L, $H)'; # shld +$op[0x2a] = '($L, $H) = ($M[IMM16], $M[IMM16 + 1])'; # lhld + sub inxdcx($$$) { - $x = ($_[0] * 256 + $_[1] + $_[2]) & 0xffff; - $_[0] = $x >> 8; - $_[1] = $x & 0xff; + $x = $_[0] * 256 + $_[1] + $_[2]; + ($_[0], $_[1]) = (($x >> 8) & 0xff, $x & 0xff); } $op[0x03] = 'inxdcx $B, $C, 1'; # inx @@ -310,11 +379,16 @@ $op[0x3b] = '--$SP' ; # dcx # "no carry" doesn't seem to be needed for vt100 - optimize? -$op[0x04 + $_ * 8] = "$reg[$_] = sf_nc $reg[$_] + 1" for 0..7; # inr -$op[0x05 + $_ * 8] = "$reg[$_] = sf_nc $reg[$_] - 1" for 0..7; # dcr +$op[0x04 + $_ * 8] = "sf_nc ++$reg[$_]" for 0..7; # inr +$op[0x05 + $_ * 8] = "sf_nc --$reg[$_]" for 0..7; # dcr -# mvi r / M -$op[0x06 + $_ * 8] = "$reg[$_] = IMM8" for 0..7; +$op[0x07] = ' $FC = $A & 0x80; $A = (($A << 1) + ($FC && 0x01)) & 0xff '; # rlc +$op[0x17] = ' ($FC, $A) = ($A & 0x80, (($A << 1) + ($FC && 0x01)) & 0xff)'; # ral + +$op[0x0f] = ' $FC = $A & 0x01; $A = ($A >> 1) | ($FC && 0x80) '; # rrc +$op[0x1f] = ' ($FC, $A) = ($A & 0x01, ($A >> 1) | ($FC && 0x80))'; # rar + +$op[0x2f] = '$A ^= 0xff'; # cma # getting this insn wrong (its the only 16 bit insn to modify flags) # wasted three of my best days with mindless vt102 rom reverse engineering @@ -330,99 +404,84 @@ $op[0x29] = 'dad $H * 256 + $L'; # dad $op[0x39] = 'dad $SP '; # dad -$op[0x07] = ' $FC = $A >> 7; $A = ($A * 2 + $FC) & 0xff '; # rlc -$op[0x17] = ' ($FC, $A) = ($A >> 7, ($A * 2 + $FC) & 0xff)'; # ral - -$op[0x0f] = ' $FC = $A & 1; $A = ($A >> 1) | ($FC && 0x80) '; # rrc -$op[0x1f] = ' ($FC, $A) = ($A & 1, ($A >> 1) | ($FC && 0x80))'; # rar - -$op[0x0a] = '$A = $M[$B * 256 + $C]'; # ldax b -$op[0x1a] = '$A = $M[$D * 256 + $E]'; # ldax d -$op[0x3a] = '$A = $M[IMM16]'; # lda - -$op[0x20] = '$A = $INTPEND * 16 + $INTMASK + ($IFF && 8)'; # rim (incomplete) -$op[0x30] = '$INTMASK = $A & 7 if $A & 8'; # sim (incomplete) - -$op[0x22] = '($M[IMM16], $M[IMM16 + 1]) = ($L, $H)'; # shld -$op[0x2a] = '($L, $H) = ($M[IMM16], $M[IMM16 + 1])'; # lhld - -# yeah, the fucking setup screens actually use daa... -$op[0x27] = ' - my ($h, $l); - - ($h, $l) = ($A >> 4, $A & 15); - - if ($l > 9 || $FA) { - $A = sf $A + 6; - ($h, $l) = ($A >> 4, $A & 15); - } - - if ($h > 9 || $FC) { - $h += 6; - $A = ($h * 16 + $l) & 0xff; - } -'; # daa, almost certainly borked, also, acarry not set by sf - -$op[0x2f] = '$A ^= 0xff'; # cma - -$op[0x37] = '$FC = 1 '; # stc -$op[0x3f] = '$FC = !$FC'; # cmc - -$op[0x76] = 'die "HLT"'; # hlt - -$op[0x80 + $_] = '$A = sf $A + ' . $reg[$_] for 0..7; # add -$op[0x88 + $_] = '$A = sf $A + $FC + ' . $reg[$_] for 0..7; # adc -$op[0x90 + $_] = '$A = sf $A - ' . $reg[$_] for 0..7; # sub -$op[0x98 + $_] = '$A = sf $A - $FC - ' . $reg[$_] for 0..7; # sbb -$op[0xa0 + $_] = '$A = sf $A & ' . $reg[$_] for 0..7; # ana -$op[0xa8 + $_] = '$A = sf $A ^ ' . $reg[$_] for 0..7; # xra -$op[0xb0 + $_] = '$A = sf $A | ' . $reg[$_] for 0..7; # ora -$op[0xb8 + $_] = ' sf $A - ' . $reg[$_] for 0..7; # cmp +$op[0x80 + $_] = 'sf $A += + ' . $reg[$_] for 0..7; # add +$op[0x88 + $_] = 'sf $A += ($FC && 1) + ' . $reg[$_] for 0..7; # adc +$op[0x90 + $_] = 'sf $A -= + ' . $reg[$_] for 0..7; # sub +$op[0x98 + $_] = 'sf $A -= ($FC && 1) + ' . $reg[$_] for 0..7; # sbb +$op[0xa0 + $_] = 'sf8 $A &= ' . $reg[$_] for 0..7; # ana +$op[0xa8 + $_] = 'sf8 $A ^= ' . $reg[$_] for 0..7; # xra +$op[0xb0 + $_] = 'sf8 $A |= ' . $reg[$_] for 0..7; # ora +$op[0xb8 + $_] = 'sf $x = $A - ' . $reg[$_] for 0..7; # cmp # possible todo: optimize ora a, maybe xra a -$op[0xc6 + $_] = '$A = sf $A + IMM8'; # adi -$op[0xd6 + $_] = '$A = sf $A - IMM8'; # sui -$op[0xe6 + $_] = '$A = sf $A & IMM8'; # ani -$op[0xee + $_] = '$A = sf $A ^ IMM8'; # xri -$op[0xf6 + $_] = '$A = sf $A | IMM8'; # ori -$op[0xfe + $_] = ' sf $A - IMM8'; # cpi +$op[0xc6] = 'sf $A += IMM8'; # adi +# ce ADI NYI +$op[0xd6] = 'sf $A -= IMM8'; # sui +# de SBI NYI +$op[0xe6] = 'sf8 $A &= IMM8'; # ani +$op[0xee] = 'sf8 $A ^= IMM8'; # xri +$op[0xf6] = 'sf8 $A |= IMM8'; # ori +$op[0xfe] = 'sf $A - IMM8'; # cpi + +$op[0xc5] = 'PUSH $B; PUSH $C'; +$op[0xd5] = 'PUSH $D; PUSH $E'; +$op[0xe5] = 'PUSH $H; PUSH $L'; +$op[0xf5] = 'PUSH $A; PUSH +($FS && 0x80) | ($FZ && 0x40) | ($FA && 0x10) | ($FP && 0x04) | ($FC && 0x01)'; # psw $op[0xc1] = '($C, $B) = (POP, POP)'; # pop $op[0xd1] = '($E, $D) = (POP, POP)'; # pop $op[0xe1] = '($L, $H) = (POP, POP)'; # pop -$op[0xf1] = '($x, $A) = (POP, POP); ($FS, $FZ, $FA, $FP, $FC) = (!!($x & 0x80), !!($x & 0x40), !!($x & 0x10), !!($x & 0x04), !!($x & 0x01))'; # pop psw +$op[0xf1] = '($x, $A) = (POP, POP); ($FS, $FZ, $FA, $FP, $FC) = ($x & 0x80, $x & 0x40, $x & 0x10, $x & 0x04, $x & 0x01)'; # pop psw -$op[0xeb] = '($D, $E, $H, $L) = ($H, $L, $D, $E)'; # xchg - $op[0xc2 + $_ * 8] = 'BRA IMM16 ' . $cc[$_] for 0..7; # jcc $op[0xc3] = 'JMP IMM16'; # jmp $op[0xc4 + $_ * 8] = '(PUSH PC >> 8), (PUSH PC & 0xff), (BRA IMM16) ' . $cc[$_] for 0..7; # ccc $op[0xcd] = '(PUSH PC >> 8), (PUSH PC & 0xff), (BRA IMM16)'; # call -$op[0xc7 + $_ * 8] = "JMP $_ * 8" for 0..7; # rst - $op[0xc0 + $_ * 8] = 'BRA POP + POP * 256 ' . $cc[$_] for 0..7; # rcc $op[0xc9] = 'JMP POP + POP * 256'; # ret -$op[0xc5] = 'PUSH $B; PUSH $C'; -$op[0xd5] = 'PUSH $D; PUSH $E'; -$op[0xe5] = 'PUSH $H; PUSH $L'; -$op[0xf5] = 'PUSH $A; PUSH +($FS && 0x80) | ($FZ && 0x40) | ($FA && 0x10) | ($FP && 0x04) | ($FC && 0x01)'; # psw +$op[0xc7 + $_ * 8] = "JMP $_ * 8" for 0..7; # rst + +$op[0xe9] = 'JMP $H * 256 + $L'; # pchl +# f9 SPHL NYI + +$op[0x37] = '$FC = 1 '; # stc +$op[0x3f] = '$FC = !$FC'; # cmc $op[0xd3] = 'OUT'; # out $op[0xdb] = 'IN'; # in -# e3 xthl @ 917b, hl <-> (sp) +$op[0xeb] = '($D, $E, $H, $L) = ($H, $L, $D, $E)'; # xchg + +# e3 xthl NYI # @ 917b, hl <-> (sp) -$op[0xe9] = 'JMP $H * 256 + $L'; # pchl +$op[0x20] = '$A = $INTPEND * 16 + $INTMASK + ($IFF && 8)'; # rim (incomplete) +$op[0x30] = '$INTMASK = $A & 7 if $A & 8'; # sim (incomplete) $op[0xf3] = '$IFF = 0'; # DI $op[0xfb] = '$IFF = 1'; # EI -@ops = @op; # for debugging #d# +# yeah, the fucking setup screens actually use daa... +$op[0x27] = ' + my ($h, $l); + + ($h, $l) = ($A >> 4, $A & 15); + + if ($l > 9 || $FA) { + sf $A += 6; + ($h, $l) = ($A >> 4, $A & 15); + } + + if ($h > 9 || $FC) { + $h += 6; + $A = ($h * 16 + $l) & 0xff; + } +'; # daa, almost certainly borked, also, acarry not set by sf ############################################################################# +# print cpu status for debugging purposes # print cpu status, for debugging sub status { @@ -436,12 +495,13 @@ . ($FC ? "1" : "0") . ($FA ? "1" : "0") . ($FP ? "1" : "0"), - $M[$PC], $ops[$M[$PC]]; + $M[$PC], $op[$M[$PC]]; } ############################################################################# +# video emulation -my @chr = ( +my @CHARMAP = ( " " , "\x{29eb}", "\x{2592}", "\x{2409}", "\x{240c}", "\x{240d}", "\x{240a}", "\x{00b0}", "\x{00b1}", "\x{2424}", "\x{240b}", "\x{2518}", @@ -453,25 +513,37 @@ (map chr, 0x020 .. 0x7e), ); -utf8::encode $_ for @chr; +utf8::encode $_ for @CHARMAP; -my @sgr; # sgr sequences for attributes +my @SGR; # sgr sequences for attributes for (0x00 .. 0xff) { my $sgr = ""; + # ~1 sgr 5 blink + # ~2 sgr 4 underline + # ~4 sgr 1 bold + # 0x80 in attr, sgr 7, reversed + $sgr .= ";5" unless $_ & 0x01; $sgr .= ";4" unless $_ & 0x02; $sgr .= ";1" unless $_ & 0x04; $sgr .= ";7" if $_ & 0x80; - $sgr[$_] = "\e[${sgr}m"; + $SGR[$_] = "\e[${sgr}m"; } -sub prscr { +my @LED = $VT102 + ? qw(L1 INSERT DSR CTS LOCKED LOCAL SCAN BEEP) + : qw(L4 L3 L2 L1 LOCKED LOCAL SCAN BEEP); + +# display screen +sub display { my $i = 0x2000; - my $scr = sprintf "\e[H--- KBD %08b CLK %d\e[K\n", $KSTATUS, $CLK; + my $leds = join " ", map $KSTATUS & 2**$_ ? "\e[7m$LED[$_]\e[m" : "$LED[$_]", reverse 0 .. $#LED; + + my $scr = sprintf "\e[H--- LED [ %s ] CLK %d\e[K\n", $leds, $CLK; $scr .= "\e[?5" . ($DC11_REVERSE ? "h" : "l"); @@ -495,101 +567,27 @@ next line; } - my $sgr = $sgr[ ($M[$i++ + 0x1000] & 15) | ($c & 0x80)]; - - # ~1 sgr 5 blink - # ~2 sgr 4 underline - # ~4 sgr 1 bold - # 0x80 in attr, sgr 7, reversed + my $sgr = $SGR[ ($M[$i++ + 0x1000] & 15) | ($c & 0x80)]; $scr .= $prev_sgr = $sgr if $sgr ne $prev_sgr; - $scr .= $chr[$c & 0x7f]; + $scr .= $CHARMAP[$c & 0x7f]; } $scr .= "\e[K\nvideo overflow\e[K\n"; last; } - $scr .= "\e[m"; - - if (0) { - $scr .= "\e[K\n"; - for my $o (0x200 .. 0x232) { - $scr .= sprintf "%04x:", $o * 16; - for (0..15) { - $scr .= sprintf " %02x", $M[$o * 16 + $_]; - } - $scr .= "\e[K\n"; - } - } - - $scr .= "\e[J"; + $scr .= "\e[m\e[J"; syswrite STDOUT, $scr; } ############################################################################# - -if (@ARGV) { - require IO::Pty; - $PTY = IO::Pty->new; - - my $slave = $PTY->slave; - - $PTY->set_winsize (24, 80); - - unless (fork) { - $ENV{TERM} = $VT102 ? "vt102" : "vt100"; - - close $PTY; - - open STDIN , "<&", $slave; - open STDOUT, ">&", $slave; - open STDERR, ">&", $slave; - - system "stty ixoff erase ^H"; - - $PTY->make_slave_controlling_terminal; - $PTY->close_slave; - - exec @ARGV; - } - - $PTY->close_slave; - -} else { - open $PTY, " 0x3a, "\r" => 0x64, "\n" => 0x44, @@ -624,28 +622,26 @@ "\e[11~" => 0x41, # F4 ); -@KEYMAP{map chr, 0x20..0x40} = unpack "C*", pack "H*", - "779ad5a9a8b8a755a6b5b6b466256575" . "351a3929283837273626d656e634e5f5" . "b9"; - -@KEYMAP{map chr, 0x5b .. 0x7e} = unpack "C*", pack "H*", - "154514b7a5" . "244a6879591949485816574746766706" . "050a185a0817780969077a95c594a4"; +@KEYMAP{map chr, 0x20 .. 0x40, 0x5b .. 0x7e} = unpack "C*", pack "H*", + "779ad5a9a8b8a755a6b5b6b466256575" . "351a3929283837273626d656e634e5f5" . "b9" # 20..40 + . "154514b7a5" . "244a6879591949485816574746766706" . "050a185a0817780969077a95c594a4"; # 5b..7e $KEYMAP{"\x1f" & $_} ||= $KEYMAP{$_} | 0x100 for "a" .. "z"; # ctrl -$KEYMAP{uc $_} ||= $KEYMAP{$_} | 0x080 for "a" .. "z"; # shift +$KEYMAP{"\x20" ^ $_} ||= $KEYMAP{$_} | 0x080 for "a" .. "z"; # shift my $KEYMATCH = join "|", map quotemeta, reverse sort keys %KEYMAP; $KEYMATCH = qr{^($KEYMATCH)}s; +my %KMOD; + sub key { my ($key) = @_; - state %MOD; + push @KQUEUE, -0x7c if !($key & 0x100) && delete $KMOD{0x7c}; # ctrl-up + push @KQUEUE, -0x7d if !($key & 0x080) && delete $KMOD{0x7d}; # shift-up - push @KQUEUE, -0x7c if !($key & 0x100) && delete $MOD{0x7c}; # ctrl-up - push @KQUEUE, -0x7d if !($key & 0x080) && delete $MOD{0x7d}; # shift-up - - push @KQUEUE, 0x7c if $key & 0x100 && !$MOD{0x7c}++; # ctrl-down - push @KQUEUE, 0x7d if $key & 0x080 && !$MOD{0x7d}++; # shift-down + push @KQUEUE, 0x7c if $key & 0x100 && !$KMOD{0x7c}++; # ctrl-down + push @KQUEUE, 0x7d if $key & 0x080 && !$KMOD{0x7d}++; # shift-down $key &= 0x7f; push @KQUEUE, $key, -$key; @@ -668,12 +664,64 @@ } ############################################################################# +# initial key input, to set up online mode etc. +# could be done via nvram defaults + +@KQUEUE = ( + 0x7b, -0x7b, # setup + 0, # delay + 0x28, -0x28, # 4, toggle local/online + 0x38, -0x38, # 5, setup b + 0, # delay + (0x10, -0x10) x 2, # cursor right + 0x37, -0x37, # 6 toggle soft scroll + (0x10, -0x10) x 1, # cursor right + 0x37, -0x37, # 6 toggle autorepeat off + (0x10, -0x10) x 8, # cursor right + 0x37, -0x37, # 6 toggle keyclick + (0x10, -0x10) x 1, # cursor right + $VT102 ? () : (0x37, -0x37), # 6 toggle ansi/vt52 + (0x10, -0x10) x 7, # cursor right + 0x37, -0x37, # 6 toggle wrap around + 0x7b, -0x7b, # leave setup +); + +############################################################################# +# process/pty management + +require IO::Pty; +$PTY = IO::Pty->new; + +my $slave = $PTY->slave; + +$PTY->set_winsize (24, 80); + +unless (fork) { + $ENV{TERM} = $VT102 ? "vt102" : "vt100"; + + close $PTY; + + open STDIN , "<&", $slave; + open STDOUT, ">&", $slave; + open STDERR, ">&", $slave; + + system "stty ixoff erase ^H"; + + $PTY->make_slave_controlling_terminal; + $PTY->close_slave; + + @ARGV = "sh" unless @ARGV; + exec @ARGV; +} + +$PTY->close_slave; + +############################################################################# +# the actual hardware simulator my @ICACHE; # compiled instruction cache -# the cpu while () { - # execute extended basic blocks $PC = ($ICACHE[$PC] ||= do { my $pc = $PC; @@ -681,18 +729,7 @@ my $insn = ""; # the jit compiler - for (0..15) { - - # optional tracing support - if (0) { - $insn .= qq< - if (\$PRSTATUS) { - status $pc; - die unless --\$PRSTATUS; - } - >; - } - + for (0..31) { my $imm; my $op = $op[$M[$pc++]]; @@ -727,10 +764,10 @@ unless ($CLK & 0xf) { # do I/O - unless ($CLK & 0x7ff) { + unless ($CLK & 0xfff) { # pty/serial I/O - unless (@PUSARTRECV || @KQUEUE || !$PTY) { + unless ((@PUSARTRECV >= 128) || @KQUEUE || !$PTY) { my $rin = ""; (vec $rin, fileno $PTY, 1) = 1; if (select $rin, undef, undef, 0) { @@ -761,15 +798,13 @@ } # handle video hardware - - unless ($CLK & 0x1fff) { - prscr; + unless ($CLK & 0x3fff) { + display; } } # the interrupt logic - $x = $INTPEND & ~$INTMASK; - if (($RST || $x) && $IFF) { + if (($RST || ($INTPEND & ~$INTMASK)) && $IFF) { # rst 1 kbd data available # rst 2 pusart xmit+recv flag # rst 4 vertical retrace @@ -779,10 +814,12 @@ # trap vt125 mbi init h(?) my $vec; + $x = $INTPEND & ~$INTMASK; + if ($x & 1) { $vec = 0x2c; $INTPEND &= ~1; } elsif ($x & 2) { $vec = 0x34; $INTPEND &= ~2; } elsif ($x & 4) { $vec = 0x3c; $INTPEND &= ~4; -# } elsif ($RST ) { $vec = $RST * 8; $RST = 0; # for some reason, this breaks vt102 +# } elsif ($RST ) { $vec = $RST * 8; $RST = 0; # the vt102 firmware doesn't like combined interrupts } elsif ($RST & 1) { $vec = 0x08; $RST &= ~1; # separate is better for vt102 } elsif ($RST & 2) { $vec = 0x10; $RST &= ~2; } elsif ($RST & 4) { $vec = 0x20; $RST &= ~4; @@ -798,6 +835,20 @@ } } +############################################################################# +# roms in the data section + one newline +# +# vt100 @ 0x0000+0x0800 23-032E2 +# vt100 @ 0x0800+0x0800 23-061E2 +# vt100 @ 0x1000+0x0800 23-033E2 +# vt100 @ 0x1800+0x0800 23-034E2 +# +# vt102 @ 0x0000+0x8000 23-226E4 +# vt102 @ 0x8000+0x8000 23-225E4 +# +# vt131 @ 0xa000+0x0800 23-280E2 +# + __DATA__ 1N ;0>b/BWog/2!b>>>ӂ xӂ:h zW͢uۂG|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 2!0* w4ʘ> 2! ~î!N ̓/2!! "R !""  @@ -910,4 +961,6 @@ \lkgfa';jhds .,nbx/m vczT=* :B!4 !O!,ͳʄmÝ ! 0>2ͳʥ†">02I y>c}" 2. y2N!~2/ z#ͫ:/ w:N!O:/ O#:. <õ!N!蝾:&0> 2 !{!>!q{" ÎWaitp!O!'6#'M_! 0R_:B!7N  -L8>2\0͍2\0:y!ʟ25!24!̞D!;!~G6̞:70xˆ!F!~ɞ˞w:5!ڞ:4!ឯ͆;:2\0:70ʫx!z!«!F!~ w!B0~w:40x·!l 62\0+2\012 vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +L8>2\0͍2\0:y!ʟ25!24!̞D!;!~G6̞:70xˆ!F!~ɞ˞w:5!ڞ:4!ឯ͆;:2\0:70ʫx!z!«!F!~ w!B0~w:40x·!l 62\0+2\012 vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv^y5k6! +"~ACŠ>Bw2"!z"pv"x">Cw2"!n"pv"l"!!~< N[>2,!NAp##!(0S{}ˠ>w# »xE##ö6T]#zpw#sX6#N|p}Hpv"x"7: +"C*C!(0:!@W ==}wï2C!2D!z5*b Q!{!| gH:d G| gW]>6#k<|eoozW>Ê