… | |
… | |
21 | # ./vt102 bash |
21 | # ./vt102 bash |
22 | # ./vt102 telnet towel.blinkenlights.nl |
22 | # ./vt102 telnet towel.blinkenlights.nl |
23 | # ./vt102 curl http://artscene.textfiles.com/vt100/trekvid.vt |
23 | # ./vt102 curl http://artscene.textfiles.com/vt100/trekvid.vt |
24 | # ./vt102 curl http://artscene.textfiles.com/vt100/surf.vt # in 3d! |
24 | # ./vt102 curl http://artscene.textfiles.com/vt100/surf.vt # in 3d! |
25 | |
25 | |
26 | # TODO: ctrl |
26 | # TODO: ctrl key map |
27 | |
27 | |
28 | use common::sense; |
28 | use common::sense; |
29 | |
29 | |
30 | $| = 1; |
30 | $| = 1; |
31 | |
31 | |
… | |
… | |
108 | $NVRCMD[($NVRLATCH >> 1) & 7]($a1 * 10 + $a0, $NVRLATCH & 1) |
108 | $NVRCMD[($NVRLATCH >> 1) & 7]($a1 * 10 + $a0, $NVRLATCH & 1) |
109 | } |
109 | } |
110 | |
110 | |
111 | ############################################################################# |
111 | ############################################################################# |
112 | |
112 | |
113 | my $DC11 = 0; # 4 bit commands |
113 | my $DC11_REVERSE = 0; |
114 | my $DC12 = 0; |
|
|
115 | |
114 | |
116 | my $XON = 1; # false if terminal wants us to pause |
115 | my $XON = 1; # false if terminal wants us to pause |
117 | my $PUSARTCMD; |
116 | my $PUSARTCMD; |
118 | |
117 | |
119 | my @KXMIT; # current scan queue |
118 | my @KXMIT; # current scan queue |
… | |
… | |
155 | |
154 | |
156 | sub out_62 { |
155 | sub out_62 { |
157 | $NVRLATCH = shift; |
156 | $NVRLATCH = shift; |
158 | } |
157 | } |
159 | |
158 | |
160 | 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 | |
161 | sub out_c2 { } # unknown |
166 | sub out_c2 { } # unknown |
162 | sub out_d2 { $DC12 = shift } |
167 | sub out_d2 { } # 0..3 == 80c/132c/60hz/50hz |
163 | |
168 | |
164 | sub out_82 { |
169 | sub out_82 { |
165 | # keyboard |
170 | # keyboard |
166 | |
171 | |
167 | # CLICK STARTSCAN ONLINE LOCKED | CTS DSR INSERT L1(?) |
172 | # CLICK STARTSCAN ONLINE LOCKED | CTS DSR INSERT L1(?) |
… | |
… | |
448 | (map chr, 0x020 .. 0x7e), |
453 | (map chr, 0x020 .. 0x7e), |
449 | ); |
454 | ); |
450 | |
455 | |
451 | utf8::encode $_ for @chr; |
456 | utf8::encode $_ for @chr; |
452 | |
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 | } |
|
|
470 | |
453 | sub prscr { |
471 | sub prscr { |
454 | my $i = 0x2000; |
472 | my $i = 0x2000; |
455 | |
473 | |
456 | 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; |
|
|
475 | |
|
|
476 | $scr .= "\e[?5" . ($DC11_REVERSE ? "h" : "l"); |
457 | |
477 | |
458 | line: |
478 | line: |
459 | 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 | |
460 | $scr .= sprintf "%2d |", ++$y; |
482 | $scr .= sprintf "%2d |", ++$y; |
461 | |
483 | |
462 | for (0..140) { |
484 | for (0..139) { |
463 | my $c = $M[$i++]; |
485 | my $c = $M[$i]; |
464 | |
|
|
465 | # printf "%04x %02x\n", $i-1,$c; |
|
|
466 | |
486 | |
467 | if ($c == 0x7f) { # also 0xff, but the firmware avoids that |
487 | if ($c == 0x7f) { # also 0xff, but the firmware avoids that |
468 | $scr .= "|\x1b[K\n"; |
488 | $scr .= "\e[m|\e[K\n"; |
469 | |
489 | |
470 | my $a1 = $M[$i++]; |
490 | my $a1 = $M[$i + 1]; |
471 | my $a0 = $M[$i++]; |
491 | my $a0 = $M[$i + 2]; |
472 | |
492 | |
473 | $i = 0x2000 + (($a1 * 256 + $a0) & 0xfff); |
493 | $i = 0x2000 + (($a1 * 256 + $a0) & 0xfff); |
474 | |
494 | |
475 | next line; |
495 | next line; |
476 | } |
496 | } |
477 | |
497 | |
478 | $scr .= "\x1b[7m" if $c & 0x80; |
498 | my $sgr = $sgr[ ($M[$i++ + 0x1000] & 15) | ($c & 0x80)]; |
479 | $scr .= $chr[$c & 0x7f] // sprintf "[%02x]", $c & 0x7f; |
499 | |
480 | $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]; |
481 | } |
508 | } |
482 | |
509 | |
483 | $scr .= "\x1b[K\noverflow\x1b[K\n"; |
510 | $scr .= "\e[K\nvideo overflow\e[K\n"; |
484 | last; |
511 | last; |
485 | } |
512 | } |
486 | |
513 | |
|
|
514 | $scr .= "\e[m"; |
|
|
515 | |
487 | if (0) { |
516 | if (0) { |
488 | $scr .= "\x1b[K\n"; |
517 | $scr .= "\e[K\n"; |
489 | for my $o (0x200 .. 0x232) { |
518 | for my $o (0x200 .. 0x232) { |
490 | $scr .= sprintf "%04x:", $o * 16; |
519 | $scr .= sprintf "%04x:", $o * 16; |
491 | for (0..15) { |
520 | for (0..15) { |
492 | $scr .= sprintf " %02x", $M[$o * 16 + $_]; |
521 | $scr .= sprintf " %02x", $M[$o * 16 + $_]; |
493 | } |
522 | } |
494 | $scr .= "\x1b[K\n"; |
523 | $scr .= "\e[K\n"; |
495 | } |
524 | } |
496 | } |
525 | } |
497 | |
526 | |
498 | $scr .= "\x1b[J"; |
527 | $scr .= "\e[J"; |
499 | |
528 | |
500 | syswrite STDOUT, $scr; |
529 | syswrite STDOUT, $scr; |
501 | } |
530 | } |
502 | |
531 | |
503 | ############################################################################# |
532 | ############################################################################# |
… | |
… | |
555 | 0x7b, -0x7b, # leave setup |
584 | 0x7b, -0x7b, # leave setup |
556 | ); |
585 | ); |
557 | |
586 | |
558 | ############################################################################# |
587 | ############################################################################# |
559 | |
588 | |
560 | # 0x80 shift, 0x100 ctrl, 0x200 toggle |
589 | # 0x080 shift, 0x100 ctrl |
561 | my %KEYMAP = ( |
590 | my %KEYMAP = ( |
562 | "\t" => 0x3a, |
591 | "\t" => 0x3a, |
563 | "\r" => 0x64, |
592 | "\r" => 0x64, |
564 | "\n" => 0x44, |
593 | "\n" => 0x44, |
|
|
594 | |
|
|
595 | "\x00" => 0x77 | 0x100, # CTRL-SPACE |
|
|
596 | "\x1c" => 0x45 | 0x100, # CTRL-\ |
|
|
597 | "\x1d" => 0x14 | 0x100, # CTRL-] |
|
|
598 | "\x1e" => 0x24 | 0x100, # CTRL-~ |
|
|
599 | "\x1f" => 0x75 | 0x100, # CTRL-? |
565 | |
600 | |
566 | # hardcoded rxvt keys |
601 | # hardcoded rxvt keys |
567 | "\e" => 0x2a, # ESC |
602 | "\e" => 0x2a, # ESC |
568 | "\e[3~" => 0x03, # DC |
603 | "\e[3~" => 0x03, # DC |
569 | "\e[5~" => 0x7e, # CAPS LOCK (prior) |
604 | "\e[5~" => 0x7e, # CAPS LOCK (prior) |
… | |
… | |
591 | "779ad5a9a8b8a755a6b5b6b466256575" . "351a3929283837273626d656e634e5f5" . "b9"; |
626 | "779ad5a9a8b8a755a6b5b6b466256575" . "351a3929283837273626d656e634e5f5" . "b9"; |
592 | |
627 | |
593 | @KEYMAP{map chr, 0x5b .. 0x7e} = unpack "C*", pack "H*", |
628 | @KEYMAP{map chr, 0x5b .. 0x7e} = unpack "C*", pack "H*", |
594 | "154514b7a5" . "244a6879591949485816574746766706" . "050a185a0817780969077a95c594a4"; |
629 | "154514b7a5" . "244a6879591949485816574746766706" . "050a185a0817780969077a95c594a4"; |
595 | |
630 | |
596 | $KEYMAP{"\x3f" & $_} ||= $KEYMAP{$_} | 0x100 for "a" .. "z"; # ctrl |
631 | $KEYMAP{"\x1f" & $_} ||= $KEYMAP{$_} | 0x100 for "a" .. "z"; # ctrl |
597 | $KEYMAP{uc $_} ||= $KEYMAP{$_} | 0x080 for "a" .. "z"; # shift |
632 | $KEYMAP{uc $_} ||= $KEYMAP{$_} | 0x080 for "a" .. "z"; # shift |
598 | |
633 | |
599 | my $KEYMATCH = join "|", map quotemeta, reverse sort keys %KEYMAP; |
634 | my $KEYMATCH = join "|", map quotemeta, reverse sort keys %KEYMAP; |
600 | $KEYMATCH = qr{^($KEYMATCH)}s; |
635 | $KEYMATCH = qr{^($KEYMATCH)}s; |
601 | |
636 | |