lst off cas se rel xc xc use vt.equ use debug mx %11 ext reset_tab,reset_all_tabs ext recalc_cursor,recalc_cursor_x,recalc_cursor_y ext erase_line_0,erase_line_1,erase_line_2 ext erase_screen_0,erase_screen_1,erase_screen_2 ext update_sgr ext write_modem,write_modem_str vt100_csi ent debug vt100_csi * 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. ldx #st_vt100 stx state stz pcount stz parms stz parms+1 ; some assume 2 parms. stz pmod * tay ; save for modifier cmp #:MIN blt :rts cmp #:MAX+1 bge :rts sec sbc #:MIN asl tax jmp (:table,x) * :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 :xmod ; < dw :xmod ; = dw :xmod ; > dw :modifier ; ? dw :rts ; @ dw csi_A ; A dw csi_B ; B dw csi_C ; C dw csi_D ; D dw :rts ; E dw :rts ; F dw :rts ; G dw csi_H ; H dw :rts ; I dw csi_J ; J dw csi_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 :rts ; Y dw :rts ; Z dw :rts ; [ dw :rts ; \ dw :rts ; ] dw :rts ; ^ dw :rts ; _ dw :rts ; ` dw :rts ; a dw :rts ; b dw csi_c ; c dw :rts ; d dw :rts ; e dw csi_f ; f dw csi_g ; g dw csi_h ; h dw :rts ; i dw :rts ; j dw :rts ; k dw csi_l ; l dw csi_m ; m dw csi_n ; n dw :rts ; o dw :rts ; p dw csi_q ; q dw csi_r ; r dw :rts ; s dw :rts ; t dw :rts ; u dw :rts ; v dw :rts ; w dw :rts ; x dw csi_y ; y :digit ldx #st_vt100_csi_2 stx state lsr ; undo asl sta parms rts :modifier ldx #st_vt100_csi_2 stx state lda #$80 sta pmod rts :xmod * ignored. ldx #st_vt100_csi_2 stx state rts vt100_csi_bad ent cmp #'@' blt :rts ldx #st_vt100 stx state :rts rts vt100_csi_2 ent debug vt100_csi_2 ldx #st_vt100 stx state cmp #:MIN blt :rts cmp #:MAX+1 bge :rts sec sbc #:MIN asl tax jmp (:table,x) :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 ; ? dw :rts ; @ dw csi_A ; A dw csi_B ; B dw csi_C ; C dw csi_D ; D dw :rts ; E dw :rts ; F dw :rts ; G dw csi_H ; H dw :rts ; I dw csi_J ; J dw csi_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 :rts ; Y dw :rts ; Z dw :rts ; [ dw :rts ; \ dw :rts ; ] dw :rts ; ^ dw :rts ; _ dw :rts ; ` dw :rts ; a dw :rts ; b dw csi_c ; c dw :rts ; d dw :rts ; e dw csi_f ; f dw csi_g ; g dw csi_h ; h dw :rts ; i dw :rts ; j dw :rts ; k dw csi_l ; l dw csi_m ; m dw csi_n ; n dw :rts ; o dw :rts ; p dw csi_q ; q dw csi_r ; r dw :rts ; s dw :rts ; t dw :rts ; u dw :rts ; v dw :rts ; w dw csi_x ; x dw csi_y ; y :modifier ldx #st_vt100_csi_bad stx state rts semi ldx #st_vt100_csi_2 stx state ldx pcount cpx #MAX_PCOUNT bge :big inx stx pcount :big 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 ldx #st_vt100_csi_2 stx state lsr ; undo asl sta r0 ldx pcount lda parms,x * cmp #255 * beq :rts cmp #25 bge :v tay lda :mult,y * clc ; cleared via cmp adc r0 sta parms,x :rts rts :v lda #$255 sta parms,x rts :mult db 0,10,20,30,40,50,60,70,80,90 db 100,110,120,130,140,150,160,170,180,190 db 200,210,220,230,240,250 csi_h ; esc [ ... h (vt100) ; esc [ ? ... h (private) ldy #$80 bra mode_common csi_l ; 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 bge :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 mode_DECCOLM ; 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 bpl :rts sty DECCKM :rts rts mode_DECANM bit pmod bpl :rts sty DECANM cpy #0 bne :rts * switch to vt52 mode ldx #st_vt52 stx state :rts rts mode_DECCOLM * 80/132 mode. * vt102 guide states: * NOTE: When you change the number of columns per line, the screen is erased. * This also sets the scrolling region for full screen (24 lines). * * based on testing, this always clears the screen and resets x/y, regardless of current mode. * bit pmod bpl :rts sty DECCOLM lda #0 sta DECTM lda #23 sta DECBM stz x stz y phy jsr recalc_cursor jsr erase_screen_2 ply :rts rts mode_DECSCNM bit pmod bpl :rts * todo - invert on-screen characters? sty DECSCNM :rts rts mode_DECOM bit pmod bpl :rts sty DECOM ; move to the new home position stz x stz y cpy #0 beq :rts lda DECTM sta y phy jsr recalc_cursor ply :rts rts mode_DECAWM bit pmod bpl :rts sty DECAWM :rts rts mode_DECARM bit pmod bpl :rts sty DECARM :rts rts mode_LNM bit pmod bmi :rts sty LNM :rts rts csi_m * esc [ ... m * 0 - attributes off * 1 - bold * 4 - underscore * 5 - blink * 7 - inverted inc pcount ldx #0 :loop lda parms,x cmp #8 bge :next tay lda SGR and :and,y ora :or,y sta SGR :next inx cpx pcount blt :loop jmp update_sgr :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 csi_g * ESC [ g, ESC [ 0 g - clear tab at column * ESC [ 3 g - clear all tabs lda parms beq :0 cmp #3 beq :3 rts :0 ldx x jmp reset_tab :3 jmp reset_all_tabs p1 mac lda parms bne ok lda #1 ok sta parms <<< * cursor movement. * if private mode, no effect. csi_A * up * if cursor is outside the scrolling region, it is not locked to the scrolling region. bit pmod bmi :rts p1 lda y cmp DECTM beq :rts bcc :simple sec sbc parms bcc :top cmp DECTM * bcc :top * bra :sta bcs :sta :top lda DECTM bra :sta :rts rts :simple * lda y sec sbc parms * bcc :0 ; clear indicate underflow. bcc :sta :0 lda #0 :sta sta y jmp recalc_cursor_y csi_B * down bit pmod bmi :rts p1 lda y cmp DECBM beq :rts bge :simple clc adc parms bcs :bottom ; overflow cmp DECBM bcc :sta :bottom lda DECBM bra :sta :rts rts :simple clc adc parms bcs :23 ; overflow cmp #24 bcc :sta :23 lda #23 :sta sta y jmp recalc_cursor_y csi_C * right * in column 80, no effect. bit pmod bmi :rts lda x cmp #79 bcs :rts p1 lda x * and #$7f clc adc parms bcs :79 ; overflow cmp #80 bcc :sta :79 lda #79 :sta sta x jmp recalc_cursor_x :rts rts csi_D * left bit pmod bmi :rts p1 lda x and #$7f sec sbc parms * bcc :0 ; underflow bcs :sta :0 lda #0 :sta sta x jmp recalc_cursor_x :rts rts csi_f csi_H ; direct cursor addressing debug csi_H * honors origin * large numbers are clamped * 0 or 1 treated as 1 (1-based counting) * based on testing, esc [ 253-255 H will position outside the scrolling * region when DECOM is active (to first 3 lines, respectively) * this is not emulated. * y lda parms beq :yy dec :yy bit DECOM bmi :org cmp #23 blt :yyy lda #23 :yyy sta y bra :x :org clc adc DECTM cmp DECBM blt :org1 lda DECBM :org1 sta y * x :x ldx parms+1 beq :xx dex :xx cpx #79 blt :xxx ldx #79 :xxx stx x jmp recalc_cursor csi_r ; scrolling region debug csi_r * based on testing * esc [ n r (no second parmeter) is equivalent to esc [ n ; 24 r * esc [ r sets scrolling region to 1 ; 24 ( in accordance with above ) * 24 is assumed value for second parameter * invalid parameters exit without updating * based on testing, row parameters are not affected by DECOM. lda parms beq :p1 dec parms :p1 lda parms+1 beq :p2 dec parms+1 bra :check :p2 lda #23 sta parms+1 :check * 23 max ldx parms+0 cpx #23+1 bge :rts ldx parms+1 cpx #23+1 bge :rts * must be at least 1 line lda parms+1 sec sbc parms beq :rts bmi :rts * move cursor to origin. lda parms sta DECTM sta y lda parms+1 sta DECBM stz x bit DECOM bmi :j stz y :j jmp recalc_cursor :rts rts csi_J ; erase screen lda parms cmp #2+1 bcs :rts asl tax jmp (:table,x) :rts rts :table dw erase_screen_0 dw erase_screen_1 dw erase_screen_2 csi_K ; erase line lda parms cmp #2+1 bcs :rts asl tax jmp (:table,x) :rts rts :table dw erase_line_0 dw erase_line_1 dw erase_line_2 csi_q ; LEDs rts csi_n ; status report bit LOCAL bmi :rts lda parms cmp #5 beq :dsr cmp #6 beq :cpr :rts rts :dsr ; report status lda #ESC jsr write_modem lda #'[' jsr write_modem lda #'0' jsr write_modem lda #'n' jmp write_modem :cpr ; cursor report * returned y is in terms of DECOM. lda #ESC jsr write_modem lda #'[' jsr write_modem lda y bit DECOM bpl :y sec sbc DECTM :y inc jsr write_digit lda #';' jsr write_modem lda x inc jsr write_digit lda #'R' jmp write_modem write_digit * digit must be in the range 1-80 cmp #10 bcs :multi :0 ora #'0' jmp write_modem :multi ldx #8 ]loop cmp :table,x bcs :ok dex bra ]loop :ok sec sbc :table,x pha ; save txa ora #'0' jsr write_modem pla bra :0 :table db 0,10,20,30,40,50,60,70,80 csi_c ; what are you? * DA - Device Attributes mx %11 php rep #$10 ; long x/y ldy #:response jsr write_modem_str plp rts :response asc 1b,'[?1;0c',00 ; No options. csi_y ; invoke confidence test * ??? rts csi_x ; request terminal parameters * DECREQTPARM – Request Terminal Parameters mx %11 lda parms cmp #2 bcs :rts inc inc ora #'0' sta :response+2 php rep #$10 ; long x/y ldy #:response jsr write_modem_str plp :rts rts :response asc 1b,'[x;1;1;112;112;1;0x',00 ; no parity, 8-bits, 9600/9600, 16x multiplier, no stp flags. sav vt100.csi.L