From 492a3bfbec93d598441816310ae61b683c07755e Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 4 Sep 2021 17:52:35 -0400 Subject: [PATCH] adding everything in progress, regardless of state. --- screen.S | 198 ++++++++++++++++++++++++ table.py | 73 +++++++++ vt.equ.S | 60 ++++++++ vt100.S | 177 ++++++++++++++++++++++ vt100.csi.S | 413 +++++++++++++++++++++++++++++++++++++++++++++++++++ vt100.ctrl.S | 103 +++++++++++++ vt100.esc.S | 164 ++++++++++++++++++++ vt100.link.S | 22 +++ vt100.main.S | 147 ++++++++++++++++++ vt100.tabs.S | 77 ++++++++++ vt100.vt52.S | 228 ++++++++++++++++++++++++++++ 11 files changed, 1662 insertions(+) create mode 100644 screen.S create mode 100644 table.py create mode 100644 vt.equ.S create mode 100644 vt100.S create mode 100644 vt100.csi.S create mode 100644 vt100.ctrl.S create mode 100644 vt100.esc.S create mode 100644 vt100.link.S create mode 100644 vt100.main.S create mode 100644 vt100.tabs.S create mode 100644 vt100.vt52.S diff --git a/screen.S b/screen.S new file mode 100644 index 0000000..b9990cb --- /dev/null +++ b/screen.S @@ -0,0 +1,198 @@ + + rel + mx %11 + + ent erase_line + ent erase_line_0 + ent erase_line_1 + ent erase_line_2 + +erase_line +; a = 0 - erase x - eol (inclusive) +; a = 1 - erase 0 - x (inclusive) +; a = 2 - erase 0 - eol + cmp #2 + bcs :rts + asl + tax + jmp (:table,x) +:rts rts + +:table + dw erase_line_0 + dw erase_line_1 + dw erase_line_2 + +erase_line_2 +* erase entire line. + php + rep #$30 + ldy #38 + lda #" " +:loop + sta [text01],y + sta (text00),y + dey + dey + bpl :loop + plp + rts + + mx %11 + +erase_line_0 +* erase to eol + + lda x + lsr + tay + lda #" " + bcs :half +:loop + sta [text01],y +:half sta (text00),y + iny + cpy #40 + bcc :loop + rts + +erase_line_1 +* erase to x (inclusive) + + lda x + lsr + tay + lda #" " + bcc :half + +:loop + sta (text00),y +:half sta [text01],y + dey + bpl :loop + rts + + +erase_screen ent +; a = 0 - erase [cursor, end] (inclusive) +; a = 1 - erase [start, cursor] (inclusive) +; a = 2 - erase [start, end] + cmp #2 + bcs :rts + asl + tax + jmp (:table,x) +:rts rts + +:table + dw erase_screen_0 + dw erase_screen_1 + dw erase_screen_2 + +erase_screen_2 ent +* erase the entire screen. + php + rep #$30 + lda #" " ; high bit set. + +c00 cc $0400 +c01 cc $0480 +c02 cc $0500 +c03 cc $0580 +c04 cc $0600 +c05 cc $0680 +c06 cc $0700 +c07 cc $0780 +c08 cc $0428 +c09 cc $04a8 +c10 cc $0528 +c11 cc $05a8 +c12 cc $0628 +c13 cc $06a8 +c14 cc $0728 +c15 cc $07a8 +c16 cc $0450 +c17 cc $04d0 +c18 cc $0550 +c19 cc $05d0 +c20 cc $0650 +c21 cc $06d0 +c22 cc $0750 +c23 cc $07d0 + plp + rts + + mx %11 + +erase_screen_0 ent +* erase from cursor to the end. + mx %11 + ldx #0 ; for jmp (,x) + lda x + ora y + beq :all + + lda x + beq :x0 + jsr erase_line_0 + lda y + inc + bra :x1 +:x0 + lda y +:x1 cmp #23 + bcs :rts + asl + tax +:all php ; clear_table will plp. + rep #$30 + lda #" " + jmp (clear_table,x) + +:rts rts + + +erase_screen_1 ent +* erase from start to cursor. + jsr erase_line_1 + lda y + bne :ok + rts +:ok + php + rep #$30 + lda y + dey + asl + tax + lda #" " + jmp (:clear_table,x) +:clear_table + + +c23 cc $07d0 +c22 cc $0750 +c21 cc $06d0 +c20 cc $0650 +c19 cc $05d0 +c18 cc $0550 +c17 cc $04d0 +c16 cc $0450 +c15 cc $07a8 +c14 cc $0728 +c13 cc $06a8 +c12 cc $0628 +c11 cc $05a8 +c10 cc $0528 +c09 cc $04a8 +c08 cc $0428 +c07 cc $0780 +c06 cc $0700 +c05 cc $0680 +c04 cc $0600 +c03 cc $0580 +c02 cc $0500 +c01 cc $0480 +c00 cc $0400 + plp + rts \ No newline at end of file diff --git a/table.py b/table.py new file mode 100644 index 0000000..e11e9c3 --- /dev/null +++ b/table.py @@ -0,0 +1,73 @@ + + +import sys + + +special = { +} +for x in range(0,0x20): special[chr(x)] = '^' + chr(0x40 + x) + +cmap = { + '\'': '\'', + '\"': '\"', + '?': '?', + '\\': '\\', + 'a': '\a', + 'b': '\b', + 'f': '\f', + 'n': '\n', + 'r': '\r', + 't': '\t', + 'v': '\v', +} + + + +argv = sys.argv[1:] + +chars = [] +for arg in argv: + # ^X is a control character + if len(arg) == 2 and arg[0] == '^': + c = chr(ord(arg[1]) & 0x1f) + chars.append(c) + continue + # \X is an escaped character + if len(arg) == 2 and arg[0] == '\\': + c = arg[1] + if c in cmap: chars.append(cmap[c]) + continue + + # X-Y is a range of characters. + if len(arg) == 4 and arg[1] == '-': + a = arg[0] + b = arg[2] + for c in range(ord(a),ord(b)+1): + chars.append(chr(c)) + continue + + chars.extend(arg) + +chars = list(set(chars)) +chars.sort() + +if not chars: exit(1) + +mmin = ord(chars[0]) +mmax = ord(chars[-1]) + +print(":MIN\tequ {}".format(mmin)) +print(":MAX\tequ {}".format(mmax)) +print() + + + + +print(":table") +for x in range(mmin, mmax+1): + c = chr(x) + print("\tdw $0\t; {}".format(special.get(c, c))) + +# for c in chars: +# x = ord(c) +# print("\tdw $0\t; {}".format(special.get(c, c))) diff --git a/vt.equ.S b/vt.equ.S new file mode 100644 index 0000000..9d13b58 --- /dev/null +++ b/vt.equ.S @@ -0,0 +1,60 @@ + +st_vt52 equ 0 +st_vt52_esc equ 2 +st_vt52_dca equ 4 +st_vt100 equ 6 +st_vt100_esc equ 8 +st_vt100_csi equ 10 +st_vt100_csi_2 equ 12 +st_vt100_esc_pound equ 14 ; # +st_vt100_esc_lparen equ 16 ; ( +st_vt100_esc_rparen equ 18 ; ) + +st_vt100_esc_bad equ 20 +st_vt100_csi_bad equ 22 + +ESC equ $1b + + + dum 0 +state ds 2 +x ds 2 +y ds 2 +DECTM ds 2 ; top margin +DECBM ds 2 ; bottom margin + +LOCAL ds 2 ; local mode +DECANM ds 2 ; ansi/vt52 +DECKPAM ds 2 ; alternate keypad +DECCKM ds 2 ; cursor key modes +DECOM ds 2 ; origin +DECSCNM ds 2 ; screen mode +DECAWM ds 2 ; wrap +DECARM ds 2 ; auto repeat + + +LNM ds 2 ; new line +SGR ds 2 ; graphics, bit 1 = bold, 4 = underscore, 5 = blink, 7 = inverse + +* not supported + +*CHARSET ds 2 ; +*GRAPHICS ds 2 ; + +*DECCOLM ds 2 ; character per line (80/132) +*DECINLM ds 2 ; interlace +*DECSCLM ds 2 ; scroll mode + +* DECDHL - double height line +* DECDWL - double width line + + +* parameters +MAX_PCOUNT equ 8 +pcount ds 2 +parms ds MAX_PCOUNT +pmod ds 2 + +r0 ds 2 + + dend diff --git a/vt100.S b/vt100.S new file mode 100644 index 0000000..0cfbf3a --- /dev/null +++ b/vt100.S @@ -0,0 +1,177 @@ + + + +vt100 + mx %11 + and #$7f + cmp #' ' + bcs :notctrl + asl + tax + jmp (ctrl,x) +:notctrl + ldx state + jmp (:state_table,x) + + + +:state_table + ext vt52_esc,vt52_dca + ext vt100_esc,vt100_csi,vt100_csi_2 + ext vt100_esc_pound,vt100_esc_lparen,vt100_esc_rparen + + dw char + dw vt52_esc + dw vt52_dca + dw char + dw vt100_esc + dw vt100_csi + dw vt100_csi_2 + dw vt100_esc_pound + dw vt100_esc_lparen + dw vt100_esc_rparen + + + + + +vt100_csi_bad ent + cmp #'@' + blt :rts + ldx #st_vt100 + stx state +:rts rts + + + + +esc_csi + stz pcount + stz params + stz params+1 + stz csi_private + lda #st_csi + sta state + rts + + +csi + inc state + inc state + cmp #'?' + bne csi0 + lda #$80 + sta csi_private + rts + +csi0 + cmp #';' + beq :semi + cmp #'0' + bcc csi_final + cmp #'9'+1 + bcs csi_final +:num + and #$0f ; 0-9 + tay ; save + ldx pcount + lda params,x + asl ; x 2 + sta params,x + asl ; x 4 + asl ; x 8 + clc + adc params,x + sta params,x + tya + clc + adc params,x + sta params,x + rts + + +:semi + ldx pcount + inx + cpx #MAX_PCOUNT + bcs :srts + stx pcount + stz params,x + +:srts + rts + + +csi_final +* c, h, l have private modes. + bit csi_private + bmi :priv + + cmp #:MIN_FINAL + bcc :rts + cmp #:MAX_FINAL+1 + bcs :rts + asl + tax + jmp (:table,x) +:rts rts + +:priv + cmp #:MIN_FINAL_PRIV + bcc :rts + cmp #:MAX_FINAL_PRIV+1 + bcs :rts + asl + tax + jmp (:table_priv,x) + + +csi +* +* ESC [ encountered. +* +* ? -> DEC private +* 0-9 -> parameter value +* ; parameter delim +* other - final character. + + cmp #'0' + blt :notnum + cmp #'9'+1 + bge :notnum + + sep #$30 + and #$0f + tay + ldx pcount + lda parms,x + asl + pha + asl + asl + clc + adc 1,s + sta 1,s + tya + adc 1,s + ply + sta parms,x + sep #$30 +]rts rts +:notnum + cmp #';' + bne :notsemi + ldx pcount + cpx #MAXPCOUNT + bge ]rts + inx + stx pcount + stz parms, + stz parms+1,x +]rts rts +:notsemi + cmp #'?' + bne :final + sta ptype + rts +:final \ No newline at end of file diff --git a/vt100.csi.S b/vt100.csi.S new file mode 100644 index 0000000..3280282 --- /dev/null +++ b/vt100.csi.S @@ -0,0 +1,413 @@ + + + + rel + xc + xc + + use vt.equ + + +vt100_csi ent + inc state + inc state + + stz pcount + stz parms + stz parms+1 ; some assume 2 parms. + stz pmod + + tay ; save for modifier + cmp #:MIN + blt :bad + cmp #:MAX+1 + bge :bad + sec + sbc #:MIN + asl + tax + jmp (:table,x) +:bad + ldx #st_vt100 + stx state +:rts rts + +:table + +* 0123456789;ycnlhgrqJKmABCDHf + +* based on testing - +* everything except '0' - '?' and control chars +* will finish. +* '?' only matters for h/l +* a misplaced ? (or anything in '0' - '?', except 0-9;) +* will cancel the sequence AFTER it's finished. +* < = > ? are allowed as an initial modifier but only '?' is private +* a mis-placed < = > ? will prevent 0x20-0x2f from terminating the sequence. + +:MIN equ 48 +:MAX equ 121 +:table + dw :digit ; 0 + dw :digit ; 1 + dw :digit ; 2 + dw :digit ; 3 + dw :digit ; 4 + dw :digit ; 5 + dw :digit ; 6 + dw :digit ; 7 + dw :digit ; 8 + dw :digit ; 9 + dw digit ; : + dw semi + dw :rts ; < + dw :rts ; = + dw :rts ; > + dw :modifier ; ? + dw $0 ; @ + dw $0 ; A + dw $0 ; B + dw $0 ; C + dw $0 ; D + dw $0 ; E + dw $0 ; F + dw $0 ; G + dw $0 ; H + dw $0 ; I + dw $0 ; J + dw $0 ; K + dw $0 ; L + dw $0 ; M + dw $0 ; N + dw $0 ; O + dw $0 ; P + dw $0 ; Q + dw $0 ; R + dw $0 ; S + dw $0 ; T + dw $0 ; U + dw $0 ; V + dw $0 ; W + dw $0 ; X + dw $0 ; Y + dw $0 ; Z + dw $0 ; [ + dw $0 ; \ + dw $0 ; ] + dw $0 ; ^ + dw $0 ; _ + dw $0 ; ` + dw $0 ; a + dw $0 ; b + dw $0 ; c + dw $0 ; d + dw $0 ; e + dw $0 ; f + dw $0 ; g + dw $0 ; h + dw $0 ; i + dw $0 ; j + dw $0 ; k + dw $0 ; l + dw $0 ; m + dw $0 ; n + dw $0 ; o + dw $0 ; p + dw $0 ; q + dw $0 ; r + dw $0 ; s + dw $0 ; t + dw $0 ; u + dw $0 ; v + dw $0 ; w + dw $0 ; x + dw $0 ; y + + +:digit + lsr ; undo asl + sta parms + rts +:modifier + ldx #$40 ; bit -> v + stx pmod + rts + + +vt100_csi_2 ent + + ; if < '0' and invalid modifier, don't terminate. + bit pmod + bmi :badmod + cmp #'@' + blt :0 + ldx #st_vt100 + stx state + +:0 cmp #:MIN + blt :bad + cmp #:MAX+1 + bge :bad + sec + sbc #:MIN + jmp (:table,x) + +:badmod + cmp #'@' + blt :rts + +:bad + ldx #st_vt100 + stx state +:rts rts + +:MIN equ 48 +:MAX equ 121 +:table + dw digit ; 0 + dw digit ; 1 + dw digit ; 2 + dw digit ; 3 + dw digit ; 4 + dw digit ; 5 + dw digit ; 6 + dw digit ; 7 + dw digit ; 8 + dw digit ; 9 + dw digit ; : + dw semi + dw :modifier ; < + dw :modifier ; = + dw :modifier ; > + dw :modifier ; ? + +:modifier + lda #$80 + sta pmod + rts + +semi + ldx pcount + cpx #MAX_PCOUNT + bge :rts + + inx + stx pcount + stz parms,x +:rts rts + +* parameter digit. clamped to 255 (250+ rounds up to 255) +* in 132 is the largest valid parameter so this is ok. +digit + lsr ; undo asl + sta r0 + ldx pcount + lda parms,x +* cmp #255 +* beq :rts + cmp #25 + bge :v + + tay + lda :table,y +* clc ; cleared via cmp + adc r1 + sta parms,x + + +:rts + rts +:v + lda #$255 + sta parms,x + rts + +:table da 0,10,20,30,40,50,60,70,80,90 + da 100,110,120,130,140,150,160,170,180,190 + da 200,210,220,230,240,250 + + + + + +set_mode + ; esc [ ... h (vt100) + ; esc [ ? ... h (private) + ldy #$80 + bra mode_common + +reset_mode + ; esc [ ... l (vt100) + ; esc [ ? ... l (private) + ldy #0 + +mode_common + inc pcount + ldx #0 +:loop lda parms,x + cmp #:MIN + blt :next + cmp #:MAX+1 + bgt :next + phx + asl + tax + jsr (:table,x) + plx + +:next inx + cpx pcount + blt :loop +:rts rts + +:MIN equ 0 +:MAX equ 20 +:table + dw :rts ; error + dw mode_DECCKM + dw mode_DECANM + dw :rts ; DECCOLM + dw :rts ; DECSCLM + dw mode_DECSCNM + dw mode_DECOM + dw mode_DECAWM + dw mode_DECARM + dw :rts ; DECINLM + dw :rts ; 10 + dw :rts ; 11 + dw :rts ; 12 + dw :rts ; 13 + dw :rts ; 14 + dw :rts ; 15 + dw :rts ; 16 + dw :rts ; 17 + dw :rts ; 18 + dw :rts ; 19 + dw mode_LNM +*:mask dw 0,$40,$40,$40,$40,$40,$40,$40,$40,$40 +* dw 0,0,0,0,0,0,0,0,0,0,0 + + +mode_DECCKM + bit pmod + bvc :rts + + sty DECCKM +:rts rts + +mode_DECANM + bit pmod + bvc :rts + + sty DECANM + cpy #0 + bne :rts +* switch to vt52 mode + ldx #st_vt52 + stx state +:rts rts + + +*mode_DECCOLM +* sty DECCOLM +* rts + + +mode_DECSCNM + bit pmod + bvc :rts + +* todo - invert on-screen characters? + sty DECSCNM +:rts rts + +mode_DECOM + bit pmod + bvc :rts + + sty DECOM + ; move to the new home position + stz x + stz y + cpy #0 + beq :rts + lda #DECTM + sta y + + phy + jsr update_cursor + ply + +:rts rts + +mode_DECAWM + bit pmod + bvc :rts + + sty DECAWM +:rts rts + +mode_DECARM + bit pmod + bvc :rts + + sty DECARM +:rts rts + +mode_LNM + bit pmod + bvs :rts + + sty LNM +:rts rts + + +char_attr +* esc [ ... m + inc pcount + ldx #0 +:loop lda parms,x + cmp #8 + bge :next + tay + lda SGR + and :and,y + ora :or,y + lda :table,y + sta SGR + +:next inx + cpx pcount + blt :loop +:rts rts + + +:and db $00,$ff,$ff,$ff,$ff,$ff,$ff,$ff + +:or db %0000_0000,%0000_0010,%0000_0000,%0000_0000 + db %0001_0000,%0010_0000,%0000_0000,%1000_0000 + + + +reset_tab +* ESC H - set tab at current column [vt100.s] +* ESC [ g, ESC [ 0 g - clear tab at column +* ESC [ 3 g - clear all tabs + + lda parm + beq :0 + cmp #3 + beq :3 + rts +:0 + ldx x + stz tabs,x + rts +:3 + ldx #80-1 +:loop stz tabs,x + dex + bpl :loop + rts + + sav vt100.csi.L diff --git a/vt100.ctrl.S b/vt100.ctrl.S new file mode 100644 index 0000000..5a2a166 --- /dev/null +++ b/vt100.ctrl.S @@ -0,0 +1,103 @@ + + +*control chars + ext draw_char,update_cursor + +control ent + + asl + tax + jmp (:table,x) + +:table + dw :rts ; ^@ + dw :rts ; ^A + dw :rts ; ^B + dw :rts ; ^C + dw :rts ; ^D + dw enq ; ^E + dw :rts ; ^F + dw bel ; ^G + dw bs ; ^H + dw tab ; ^I + dw lf ; ^J + dw vt ; ^K + dw ff ; ^L + dw cr ; ^M + dw so ; ^N + dw si ; ^O + dw :rts ; ^P + dw xon ; ^Q + dw :rts ; ^R + dw xoff ; ^S + dw :rts ; ^T + dw :rts ; ^U + dw :rts ; ^V + dw :rts ; ^W + dw can ; ^X + dw :rts ; ^Y + dw sub ; ^Z + dw esc ; ^[ + dw :rts ; ^\ + dw :rts ; ^] + dw :rts ; ^^ + dw :rts ; ^_ + +:rts rts + +enq +* send answer back message. +* answer back message is a user-controllable string of text sent as-is +* (with no specific terminator character) + rts +bel +* todo - trigger nice ensoniq beep. + rts + + +bs +* backspace, no wrap + lda x + beq :rts + dec x + jmp update_cursor +:rts rts + +tab +* go to next tab stop + rts + +lf +vt +ff +* vt and ff interpreted as lf +* LNM: also do cr. + bit LNM + bpl :lnm + stz x +:lnm + lda y + cmp BM ; bottom margin + bne :iny + jmp scroll_up +:iny inc y + jmp update_cursor + +cr + stz x + jmp update_cursor + +xon +xoff +* flow control... + rts +can +sub +* cancel esc sequence and display error character + stz state + rts + +esc + lda #st_esc + sta state + rts \ No newline at end of file diff --git a/vt100.esc.S b/vt100.esc.S new file mode 100644 index 0000000..415d719 --- /dev/null +++ b/vt100.esc.S @@ -0,0 +1,164 @@ + + + rel + xc + xc + + use vt.equ + +vt100_esc ent +* #[()=>cH78DEM +* based on testing, unspecified chars in the 0x20-0x2f range cause it to gobble +* chars until 0x30- terminator (which ends the sequence but does not take an action) + +* esc 1 -> hangs? [undocumented] + + + ldx #st_vt100 + stx state + + cmp #:MIN + blt :bad + cmp #:MAX+1 + bge :rts + sec + sbc #:MIN + asl + tax + jmp (:table,x) + +:bad + ldx #st_vt100_esc_bad + stx state +:rts + rts + +:MIN equ 35 +:MAX equ 99 + +:table + dw :pound ; # + dw :bad ; $ + dw :bad ; % + dw :bad ; & + dw :bad ; ' + dw :lparen ; ( + dw :rparen ; ) + dw :bad ; * + dw :bad ; + + dw :bad ; , + dw :bad ; - + dw :bad ; . + dw :bad ; / + dw :rts ; 0 + dw :rts ; 1 + dw :rts ; 2 + dw :rts ; 3 + dw :rts ; 4 + dw :rts ; 5 + dw :rts ; 6 + dw esc_7 ; 7 + dw esc_8 ; 8 + dw :rts ; 9 + dw :rts ; : + dw :rts ; ; + dw :rts ; < + dw esc_eq ; = + dw esc_gt ; > + dw :rts ; ? + dw :rts ; @ + dw :rts ; A + dw :rts ; B + dw :rts ; C + dw esc_D ; D + dw esc_E ; E + dw :rts ; F + dw :rts ; G + dw esc_H ; H + dw :rts ; I + dw :rts ; J + dw :rts ; K + dw :rts ; L + dw esc_M ; M + dw :rts ; N + dw :rts ; O + dw :rts ; P + dw :rts ; Q + dw :rts ; R + dw :rts ; S + dw :rts ; T + dw :rts ; U + dw :rts ; V + dw :rts ; W + dw :rts ; X + dw :rts ; Y + dw :rts ; Z + dw :rts ; [ + dw :rts ; \ + dw :rts ; ] + dw :rts ; ^ + dw :rts ; _ + dw :rts ; ` + dw :rts ; a + dw :rts ; b + dw esc_c ; c + + + +:lparen + ldx #st_vt100_esc_lparen + stx state + rts + +:rparen + ldx #st_vt100_esc_rparen + stx state + rts + +:pound + ldx #st_vt100_esc_pound + stx state + rts + + +esc_7 ; save cursor position, graphic rendition, and character set. + rts +esc_8 ; restore cursor position, graphic rendition, and character set. + rts + +esc_eq ; enter alternate keypad mode + lda #$80 + sta DECKPAM + rts +esc_gt ; exit alternate keypad mode + stz DECKPAM + rts + +esc_H ; set tab stop + ext set_tab + ldx x + jmp set_tab + +esc_E ; next line + stz x + ; drop through +esc_D ; index + rts + +esc_M ; reverse index + rts + +esc_c ; reset terminal. + rts + + +vt100_esc_bad ent + cmp #'0' + blt :rts + ldx #st_vt100 + stx state +:rts + rts + + + sav vt100.esc.L diff --git a/vt100.link.S b/vt100.link.S new file mode 100644 index 0000000..6276036 --- /dev/null +++ b/vt100.link.S @@ -0,0 +1,22 @@ + + ovr all + +* binary linker + lkv 0 + org $6000 + + asm boot.S + asm vt100.ctrl.S + + + + lnk boot.S + pos ; reset + + + pos EMU_SIZE +EMU_SIZE ext +EMU_BLOCKS geq EMU_SIZE+511\512 + + + sav vt100.bin \ No newline at end of file diff --git a/vt100.main.S b/vt100.main.S new file mode 100644 index 0000000..3a2123a --- /dev/null +++ b/vt100.main.S @@ -0,0 +1,147 @@ +vt100 + mx %11 + and #$7f + cmp #' ' + bcs :notctrl + asl + tax + jmp (:ctrl_table,x) +:notctrl + ldx state + jmp (:state_table,x) + + + +:state_table + ext vt52_esc,vt52_dca_1,vt52_dca_2 + ext vt100_esc,vt100_csi,vt100_csi_2 + ext vt100_esc_pound,vt100_esc_lparen,vt100_esc_rparen + + dw char + dw vt52_esc + dw vt52_dca_1 + dw vt52_dca_2 + dw char + dw vt100_esc + dw vt100_csi + dw vt100_csi_2 + dw vt100_esc_pound + dw vt100_esc_lparen + dw vt100_esc_rparen + + +:ctrl_table + dw ctrl_00,ctrl_01,ctrl_02,ctrl_03 + dw ctrl_04,ctrl_05,ctrl_06,ctrl_07 + dw ctrl_08,ctrl_09,ctrl_0a,ctrl_0b + dw ctrl_0c,ctrl_0d,ctrl_0e,ctrl_0f + dw ctrl_10,ctrl_11,ctrl_12,ctrl_13 + dw ctrl_14,ctrl_15,ctrl_16,ctrl_17 + dw ctrl_18,ctrl_19,ctrl_1a,ctrl_1b + dw ctrl_1c,ctrl_1d,ctrl_1e,ctrl_1f + + + +ctrl_00 +ctrl_01 +ctrl_02 +ctrl_03 +ctrl_04 +ctrl_05 ; answer ENQ +ctrl_06 +ctrl_0e ; G1 character set +ctrl_0f ; G0 character set +ctrl_10 +ctrl_11 ; XON +ctrl_12 +ctrl_13 ; XOFF +ctrl_14 +ctrl_15 +ctrl_16 +ctrl_17 +ctrl_19 +ctrl_1c +ctrl_1d +ctrl_1e +ctrl_1f + rts + + +ctrl_07 ; ring the bell. + rts + +ctrl_1b ; escape - +* vt100 - aborts current escape sequence and starts a new one. +* vt52 - esc esc aborts and starts new +* vt50 - esc esc aborts + lda DECANM + bne :vt52 + lda #st_vt100_esc + sta state + rts + +:vt52 + lda #st_vt52_esc + sta state + rts + +ctrl_18 +ctrl_1a +* vt100 - abort current escape sequence +* and display error character. + lda DECANM + bne :vt52 + lda #st_vt100 + sta state + rts + +:vt52 + lda #st_vt52 + sta state + rts + + +ctrl_09 ; tab +* vt100 has adjustable tabs. + ext tab_table + + ldx x +lp: + cpx #79 + bcs :rts + lda tab_table,x + bne :hit + inx + bra :lp + + stx x + + jmp update_cursor +:rts rts + +ctrl_0a ; line feed - cursor down w/ scroll +ctrl_0b ; vertical tab +ctrl_0c ; form feed. + +* if LNM is active, equivalent to CR, LF + + lda #LNM + bne :lnm + stz x +:lnm + + lda y + cmp #23 + blt :simple + + lda #" " + sta cursor_saved_char + jmp scroll_down + +:simple + inc y + jmp update_cursor + +ctrl_0d ; carriage return - cursor to column 0. + stz x + jmp update_cursor diff --git a/vt100.tabs.S b/vt100.tabs.S new file mode 100644 index 0000000..5e21e12 --- /dev/null +++ b/vt100.tabs.S @@ -0,0 +1,77 @@ + + + rel + xc + xc + + use vt.equ + + +init_tabs ent + + + ldx #80 +:loop stz tabs,x + dex + bpl :loop + + lda #72 + ldy #$80 +:tloop tax + sty tabs,x + sec + sbc #8 + bne :tloop + + rts +*:table db 8*1,8*2,8*3,8*4,8*5,8*6,8*7,8*8*,8*9 + +set_tab ent +* input x = x + +* ldx x + cpx #80 + bge :rts + lda #$80 + sta tabs,x +:rts rts + +reset_tab ent +* input x = x + +* ldx x + cpx #80 + bge :rts + stz tabs,x +:rts rts + + +reset_all_tabs ent + + ldx #80-1 +:loop stz tabs,x + dex + bpl :loop + rts + +next_tab_stop ent +* input x = x + +* ldx x + cpx 79 + bge :80 + +:loop bit tabs,x + bmi :rts + inx + cpx #80 + bcc :loop + + +:80 ldx #80 +:rts rts + + +tabs ds 80 + + sav vt100.tabs.L diff --git a/vt100.vt52.S b/vt100.vt52.S new file mode 100644 index 0000000..1fd464e --- /dev/null +++ b/vt100.vt52.S @@ -0,0 +1,228 @@ +* +* vt52 emulation for the vt100 +* +* ESC < exits + + lst off + + rel + xc + xc + + use vt.equ + + ext update_cursor,write_modem,draw_char + + + +vt52_esc ent +* ABCDFGHIJKYZ<>= + + + ldx #st_vt52 + stx state + + cmp #:MIN + blt :rts + cmp #:MAX+1 + bge :rts + sec + sbc #:MIN + asl + tax + jmp (:table,x) + +:rts + rts + + +:MIN equ 60 +:MAX equ 90 +:table + dw esc_lt ; < + dw esc_eq ; = + dw esc_gt ; > + dw :rts ; ? + dw :rts ; @ + dw esc_A ; A + dw esc_B ; B + dw esc_C ; C + dw esc_D ; D + dw :rts ; E + dw esc_F ; F + dw esc_G ; G + dw esc_H ; H + dw esc_I ; I + dw esc_J ; J + dw esc_K ; K + dw :rts ; L + dw :rts ; M + dw :rts ; N + dw :rts ; O + dw :rts ; P + dw :rts ; Q + dw :rts ; R + dw :rts ; S + dw :rts ; T + dw :rts ; U + dw :rts ; V + dw :rts ; W + dw :rts ; X + dw esc_Y ; Y + dw esc_Z ; Z + + + +esc_lt +* based on testing, this also resets graphics mode +* (which we don't support anyhow.) + lda #$80 + sta DECANM + lda #st_vt100 + sta state + rts + + +esc_eq ; enter alternate keypad mode + lda #$80 + sta DECKPAM + rts + +esc_gt ; exit alternate keypad mode + stz DECKPAM + rts + +* cursor movement respects the scrolling region. +esc_A ; cursor up. + lda y + beq :rts + cmp DECTM + beq :rts + dec y + jmp update_cursor + +:rts rts + + +esc_B ; cursor down + lda y + cmp #79 + beq :rts + cmp DECBM + beq :rts + iny y + jmp update_cursor + +:rts rts + + +esc_C ; cursor right + + lda x + cmp #79 + bcs :rts + inc x + jmp update_cursor +:rts rts + +esc_D ; cursor left + + lda x + beq :rts + dec x + jmp update_cursor +:rts rts + +esc_I ; cursor up w/ line scroll +* based on testing, scrolling only occurs within the +* scroll region. + rts +esc_J rts +esc_K rts + + +esc_H ; cursor home + ; based on testing, does not respect scrolling region but does + ; respect origin mode. + stz x +* stz y +* bit DECOM +* bpl :go + lda DECTM + sta y + +:go jmp update_cursor + + +esc_F ; enter graphics mode +* lda #%0010 +* tsb mode + rts +esc_G ; exit graphics mode +* lda #%0010 +* trb mode + rts + +esc_Y ; direct cursor address +* vt100 - does not take effect until the end. +* based on testing, there is internal state information, +* so esc Y a esc B esc Y b is equivalent to esc Y a b +* +* if width exceeded, clamps at right margin. +* if height exceeded, does not change. + lda #st_vt52_dca + sta state + rts + +esc_Z ; terminal identity. + ; return ESC / Z + ; based on testing, no display in local mode + bit LOCAL + bmi :local + lda #ESC + jsr write_modem + lda #'/' + jsr write_modem + lda #'Z' + jmp write_modem + +:local rts +* lda #'Z' +* jmp draw_char + + + +vta52_dca ent +* this differs from esc [ H in that invalid +* values are ignored rather than clamped. +* based on testing, does not respect DECOM. +* based on testing, state is saved if ESC aborts, even +* if switching to vt100 mode and back or ^X to cancel. + + sec + sbc #' ' + + bit :tmp + bmi :go + ora #$80 + sta :tmp + rts + +:go ; a = x + cmp #80 + bge :y + sta x +:y lda :tmp + and #$7f + cmp #24 + bge :update + sta y +:update + stz :tmp + jmp update_cursor + + +:tmp ds 2 + + + sav vt100.vt52.L