# ip65 technical reference

# File : drivers/c64_vt100.s

     vt100 emulation for C64
     vt100 emulation for C64
     originally from CaTer - Copyright Lars Stollenwerk 2003
     CaTer homepage is http://formica.nusseis.de/Cater/
     converted for use with ip65 by Jonno Downes, 2009.
     this version is for C64 only
     CaTer originally licensed under GPL
     Lars Stollenwerk has agreed to relicense the code in this file under MPL (Oct 2009)

     to use:
     1) call vt100_init_terminal
     2) for every 'inbound' data (received from remote host), call "vt100_process_inbound_char" - this will update the screen
     3) pass every keypress into vt100_transform_outbound_char. on return from this call,
          Y = 0 means don't send anything as a result of this keypress
          Y = 1 means A contains single character to send to remote host
          Y = 2 means AX points at null terminated string to send to remote host (e.g. an ANSI escape sequence)

## functions

function | description
--- | ---
vt100_init_terminal |
    intialize VT100 emulation state
    inputs: none
    outputs: none
vt100_process_inbound_char |
    process incoming character
    inputs:
     A is inbound character
    outputs:
     none, but screen and/or terminal state is updated.
vt100_transform_outbound_char |
     *************************************
     *
     * outgoing data
     *
     *************************************
     -------------------------------------
     given a single char (read from keyboard)
     work out what data should be sent to the remote host.
     input:
     A = keypress
     output:
     Y=0 - no data to be sent (i.e. ignore keypress)
     Y=1 - A contains single byte to send
     Y=2 - AX points to null terminated string to send
     -------------------------------------
    Y=0 nothing to send
    Y=1 A = char to send
    Y=2 AX=pointer to asciiz string to send

## implementation

    ; vt100 emulation for C64
    ; vt100 emulation for C64
    ; originally from CaTer - Copyright Lars Stollenwerk 2003
    ; CaTer homepage is http://formica.nusseis.de/Cater/
    ; converted for use with ip65 by Jonno Downes, 2009.
    ; this version is for C64 only
    ; CaTer originally licensed under GPL
    ; Lars Stollenwerk has agreed to relicense the code in this file under MPL (Oct 2009)
    ;
    ; to use:
    ; 1) call vt100_init_terminal
    ; 2) for every 'inbound' data (received from remote host), call "vt100_process_inbound_char" - this will update the screen
    ; 3) pass every keypress into vt100_transform_outbound_char. on return from this call,
    ;      Y = 0 means don't send anything as a result of this keypress
    ;      Y = 1 means A contains single character to send to remote host
    ;      Y = 2 means AX points at null terminated string to send to remote host (e.g. an ANSI escape sequence)

    .include "../inc/common.i"

    .export vt100_init_terminal
    .export vt100_process_inbound_char
    .export vt100_transform_outbound_char

    .import beep

    ; --- colour values ---
    col_black       = $00
    col_white       = $01
    col_red         = $02
    col_cyan        = $03
    col_purple      = $04
    col_green       = $05
    col_blue        = $06
    col_yellow      = $07
    col_orange      = $08
    col_brown       = $09
    col_light_red   = $0a
    col_gray_1      = $0b
    col_gray_2      = $0c
    col_light_green = $0d
    col_light_blue  = $0e
    col_gray_3      = $0f

    ; --- colours ---
    ; vanilla     f  bold 1
    ; underline   e       3
    ; blink       5       d
    ; blink uline a       7
    charmode_vanilla = $0f
    charmode_bold = $01
    charmode_underline = $0e
    charmode_underline_bold = $03
    charmode_blink = $05
    charmode_blink_bold = $0d
    charmode_blink_underline = $0a
    charmode_blink_underline_bold = $07

    ; text background
    text_background_colour = col_black

    .segment "APP_SCRATCH"
    escape_buffer: .res $100

    .zeropage

    ; --- esc mode ---
    ; $00 = normal
    ; $0f = esc mode
    ; $ff = esc [ mode
    ; $f0 = ignore one char
    escape_mode: .res 1

    ; --- Vector ---
    ; four vectors in zeropage
    ; for temporary use
    temp_ptr_z: .res 2
    temp_ptr_y: .res 2
    temp_ptr_x: .res 2
    temp_ptr_w: .res 2

    escape_buffer_length: .res 1  ; points to first free position
    escape_parameter: .res 1       ; numeric parameter in esc sequence
    scroll_region_start: .res 1
    scroll_region_end: .res 1

    ; font_mode contains three bits
    ; bit 0 = bold
    ; bit 1 = underline
    ; bit 2 = blink
    ; bit 7 = direct ANSI ESC colour
    font_mode: .res 1

    direct_colour: .res 1

    ; --- crsr save area ---
    ; here is crsr info saved with
    ; ESC 7 and restored from with
    ; ESC 8

    saved_font_mode:  .res 1
    saved_reverse_mode:  .res 1
    saved_row:  .res 1
    saved_column:  .res 1

    print_ptr: .res 2 ;temp vector for printing to screen

    ; -------------------------------------
    ; memory map
    ;
    ; -------------------------------------

    ; --- screen ---
    ; $0400 - $07ff
    Screen = $0400

    ; --- escape buffer ---
    ; $0800 - $0bff

    ; --- char ---
    ; $2000 - $27ff
    font_table = $2000

    ; -------------------------------------
    ; constant declaration
    ;
    ; -------------------------------------

    esc = $1b
    brace = $5b

    .code

    ;intialize VT100 emulation state
    ;inputs: none
    ;outputs: none
    vt100_init_terminal:

      jsr initialise_variables ; init memory variables
      jsr initialise_font; init font
      jsr initialise_screen ; init screen
      rts

    ;process incoming character
    ;inputs:
    ; A is inbound character
    ;outputs:
    ; none, but screen and/or terminal state is updated.

    vt100_process_inbound_char:
      tay
      lda escape_mode   ; handle esc mode
      beq :+            ; to far for branch to escape
      jmp handle_escape_char
    :
      lda ascii_to_petscii,y         ; ASCII to PETSCII
      beq @done         ; ignore non-printing chars
      cmp #$01          ; something special?
      beq handle_special_char
      jsr print_to_screen ; print to screen
    @done:
      rts

    do_cr:
      ldx $d6     ; get row
      ldy #$00    ; set col=0
      jsr cursor_plot   ; set crsr
      rts

    do_line_feed:
      ldx $d6     ; crsr line
      cpx scroll_region_end     ; end scroll region?
      bne down_one_line    ;  no -> go on
      jsr cursor_off
      jsr scroll_up_scrollregion   ;  yes -> scroll up
      jsr cursor_on
      rts

    handle_special_char:
      tya         ; restore original char
      cmp #$0d    ; CR?
      beq do_cr

      cmp #$08    ; BS?
      bne @not_bs
      ldy $d3     ; get col
      beq @bs_done   ; stop at left margin
      dey         ; dec column
      ldx $d6     ; get row
      jsr cursor_plot   ; set crsr
    @bs_done:
      rts
    @not_bs:
      cmp #$1b    ; esc?
      bne @not_escape
      lda #$0f    ; set esc mode
      sta escape_mode
      rts
    @not_escape:
      cmp #$07    ; BEL?
      bne @not_bell
      jsr beep
      rts
    @not_bell:
      cmp #$0a    ; LF?
      beq do_line_feed

    @not_lf:
      cmp #$09    ; TAB?
      bne @not_tab
      lda $d3     ; crsr col
      and #$f8    ; (col DIV 8) * 8
      clc         ; col + 8
      adc #$08
      cmp #$28    ; col=40?
      bne @not_last_col     ; no -> skip
      lda #$27    ; yes -> col=39
    @not_last_col:
      tay         ; col to y
      ldx $d6     ; line to x
      jsr cursor_plot   ; set crsr
      rts

    @not_tab:
      rts

    down_one_line:
      cpx #$18    ; end of screen?
      bne @not_end_of_screen     ;  no -> go on
      rts         ;  yes -> do nothing
    @not_end_of_screen:
      inx         ; next line
      ldy $d3     ; get col
      jsr cursor_plot   ; set crsr
      rts

    ; esc mode
    ; data in Y
    ; escape_mode <> $00 in A
    handle_escape_char:
      tax         ; save escape_mode
      and #$0f    ; escape_mode = $0f?
      bne @not_discard_mode

    ; --- discard mode --- escape_mode = $f0
    ;discard char
      lda #$00    ; reset escape_mode
      sta escape_mode
      rts

    @not_discard_mode:
      txa         ; restore escape_mode
      and #$f0    ; escape_mode = $ff?
      beq @short_escape_mode    ; no -> short Emode
      jmp long_escape_mode    ; yes -> long Emode

    ; short esc mode
    ; escape_mode = $0f
    ; process first char
    @short_escape_mode:
      tya         ; restore char
    ; --- [ ---
      cmp #brace  ; [ ?
      bne @not_brace
      lda #$ff    ; set esc [ mode
      sta escape_mode
      rts
    ; --- ( ---
    @not_brace:
            cmp #$28    ; ( ?
            bne :+
            jmp set_discard_mode
    ; --- ) ---
    :
      cmp #$29    ; ) ?
      bne :+
      jmp set_discard_mode
    ; --- # ---
    :
      cmp #$23    ; # ?
      bne :+
      jmp set_discard_mode
    ; --- D --- index
    :
        cmp #$44    ; D ?
        bne :+
        jsr do_line_feed      ; same as LF
        jmp done_escape
    ; --- M --- reverse index
    :
      cmp #$4d    ; M ?
      bne @not_M
      ldx $d6     ; get crsr row
      cpx scroll_region_start     ; top of scroll reg?
      bne :+
      jsr cursor_off    ; yes -> scroll down
      jsr scroll_down_scrollregion
      jsr cursor_on
      jmp done_escape
    :
      cpx #$00    ; top of screen?
      bne :+
      jmp done_escape    ; yes -> do nothing
    :
      dex         ; one line up
      ldy $d3     ; get crsr col
      jsr cursor_plot   ; set crsr
      jmp done_escape

    @not_M:
    ; --- E --- next line
      cmp #$45    ; E ?
      bne :+
      jsr do_cr
      jsr do_line_feed
      jmp done_escape
    :
    ; --- 7 --- save crsr
      cmp #$37    ; 7?
      bne :+
      lda font_mode    ; save font
      sta saved_font_mode
      lda $c7     ; save reverse mode
      sta saved_reverse_mode
      ldx $d6     ; save position
      ldy $d3
      stx saved_row
      sty saved_column
      jmp done_escape
    :
    ; --- 8 --- restore crsr
      cmp #$38    ; 8?
      bne :+
      ldx saved_row ; restore pos
      ldy saved_column
      jsr cursor_plot
      lda saved_reverse_mode   ; restore ..
      sta $c7     ; .. reverse mode
      ldx saved_font_mode   ; restore font
      stx font_mode
      lda font_attribute_table,x
      sta $0286   ; set colour
      jmp done_escape

    ; --- unknown ---
    :
    ; --- reset ESC mode ---
    done_escape:
      lda #$00    ; reset escape_mode
      sta escape_mode
      rts

    ; --- set Discard mode ---
    set_discard_mode:
      lda #$f0    ; set esc mode $f0
      sta escape_mode
      rts

    ; -------------------------------------
    ; [ esc mode
    ;
    ; escape_mode = $ff
    ; -------------------------------------

    long_escape_mode:
      tya         ; restore char
      ldy escape_buffer_length
      sta escape_buffer,y  ; store char
      iny
      sty escape_buffer_length   ; inc esc buffer
      jsr test_if_letter   ; test letter
      bcs :+     ; process command
      rts

    ; --- process esc command ---
    ; A = last char
    ; Y = escape_buffer_length
    ; X counts processed command chars
    :
      ldx #$00    ; first char

    ; --- A --- crsr up
      cmp #$41    ; A?
      bne @not_A
      jsr get_number_from_esc_seq  ; get argument
      lda escape_parameter    ; escape_parameter = 0...
      bne :+
      inc escape_parameter    ; .. means 1
    :
      lda $d6     ; get crsr row
      sec
      sbc escape_parameter    ; row = row - up
      cmp scroll_region_start     ; stop at top of ..
      bpl :+    ; ..scroll region
      lda scroll_region_start
    :
      tax         ; x is row
      ldy $d3     ; y is col
      jsr cursor_plot   ; set crsr
      jmp @end_escape_seq

    ; --- B --- crsr down
    @not_A:
      cmp #$42    ; B?
      bne @not_B
      jsr get_number_from_esc_seq  ; get argument
      lda escape_parameter    ; escape_parameter = 0...
      bne :+
      inc escape_parameter    ; .. means 1
    :
      lda $d6     ; get crsr row
      clc
      adc escape_parameter    ; row = row + down
      cmp scroll_region_end     ; outside scrregion?
      bcs :+    ; yes -> branch
      tax         ; x is row
      jmp @skip
    :
     ldx scroll_region_end     ; x = row = scroll_region_end
    @skip:
      ldy $d3     ; y is col
      jsr cursor_plot   ; set crsr
      jmp @end_escape_seq

    ; --- C --- crsr right
    @not_B:
      cmp #$43    ; C?
      bne @not_C
      jsr get_number_from_esc_seq  ; get argument
      lda escape_parameter    ; escape_parameter = 0...
      bne :+
      inc escape_parameter    ; .. means 1
    :
      lda $d3     ; get crsr col
      clc
      adc escape_parameter    ; col = col + right
      cmp #$27    ; outside screen?
      bcs :+    ; yes -> branch
      tay
      jmp @skip2
    :
      ldy #$27    ; y=col=left margin
    @skip2:
      ldx $d6     ; x is row
      jsr cursor_plot   ; set crsr
      jmp @end_escape_seq

    ; --- D --- crsr left
    @not_C:
      cmp #$44    ; D?
      bne @not_D
      jsr get_number_from_esc_seq  ; get argument
      lda escape_parameter    ; escape_parameter = 0...
      bne :+
      inc escape_parameter    ; .. means 1
    :
      lda $d3     ; get crsr col
      sec
      sbc escape_parameter    ; col = col - left
      bpl :+      ; stop at left..
      lda #$00    ; ..margin
    :
      tay         ; y is col
      ldx $d6     ; x is row
      jsr cursor_plot   ; set crsr
      jmp @end_escape_seq

    ; --- m ---  font attributes
    @not_D:
      cmp #$6d    ; m?
      bne @not_m
    @next_font_attribute:
      jsr get_number_from_esc_seq
      pha         ; save nondigit char
      lda escape_parameter    ; parameter to A
      ; -- 0 --
      bne :+    ; 0?
      sta font_mode    ; set font = vanilla
      sta $c7     ; reverse off
      jmp @end_font_attribute    ; jmp next par
      ; -- 1 -- bold
    :
      cmp #$01
      bne :+
      lda font_mode    ; set bold
      ora #$01
      sta font_mode
      jmp @end_font_attribute    ; next char
      ; -- 4 -- underline
    :
      cmp #$04
      bne :+
      lda font_mode    ; set u_line
      ora #$02
      sta font_mode
      jmp @end_font_attribute    ; next char
      ; -- 5 -- blink
    :
      cmp #$05
      bne :+
      lda font_mode    ; set blink
      ora #$04
      sta font_mode
      jmp @end_font_attribute    ; next char
      ; -- 7 -- reverse
    :
      cmp #$07
      bne :+
      lda #$01    ; set revers
      sta $c7
      jmp @end_font_attribute    ; next char
    :
      ; -- 30 - 37 --
      cmp #38     ; >= 38?
      bcs @end_font_attribute
      cmp #30     ; < 30?
      bcc @end_font_attribute
      sbc #30     ; pointer for table
      sta direct_colour
      lda #$80    ; set direct colour
      sta font_mode

    @end_font_attribute:  ; -- next char --
      pla         ; get nondigit char
      cmp #$3b    ; is semicolon?
      beq @next_font_attribute    ; then next cahr
      ; -- set colour --
      lda font_mode    ;
      bmi :+    ; bit 7->direct col
      tax         ; font to colour
      lda font_attribute_table,x
      sta $0286   ; set colour
      jmp @end_escape_seq
    :
      ; -- set direct colour --
      ldx direct_colour ; colour maping
      lda direct_colour_table,x
      sta $0286   ; set colour
      jmp @end_escape_seq

    ; --- K --- erase line
    @not_m:
      cmp #$4b      ; K?
      bne @not_K
      jsr get_number_from_esc_seq    ; get parameter
      lda escape_parameter      ; in A
      ; -- 0 -- crsr to end of line
      bne :+
      jsr erase_to_end_of_line    ; erase end line
      jmp @end_escape_seq
      ; -- 1 -- begin to crsr
    :
      cmp #$01
      bne :+
      jsr erase_line_to_cursor    ; erase beg line
      jmp @end_escape_seq
      ; -- 2 -- whole line
    :
      cmp #$02
      bne :+      ; par undefined
      ldx $d6       ; line in X
      jsr erase_line_by_number      ; erase line
      sta $ce       ; del char ..
                    ; ..under crsr
    :
      jmp @end_escape_seq

    ; --- f --- same as H
    @not_K:
      cmp #$66
      bne @not_f
      jmp @set_cursor_position      ; same as H

    ; --- H --- cursor position
    @not_f:
      cmp #$48
      bne @not_H
    @set_cursor_position:
      cpy #$01    ; no par means home
      bne :+
      ; -- home --
      ldx #$00
      ldy #$00
      jsr cursor_plot   ; set crsr
      jmp @end_escape_seq
      ; -- row, col --
    :
      jsr get_number_from_esc_seq
      cmp #$3b    ; is ;?
      bne @end_set_cursor_position    ; no -> error
      ; -- prepare row --
      ldy escape_parameter    ; get row
      bne :+    ; 0 means 1
      iny
    :
      dey         ; line 1 -> line 0
      cpy #$19    ; >= 25?..
      bcs @end_set_cursor_position    ; ..error!
      sty temp_ptr_x ; save row
      ; -- prepare col
      jsr get_number_from_esc_seq
      ldy escape_parameter    ; get col
      bne :+    ; 0 means 1
      iny
    :
      dey         ; line 1 -> line 0
      cpy #$28    ; >= 40?..
      bcs @end_set_cursor_position    ; ..error!
      ldx temp_ptr_x ; restore row to X
      jsr cursor_plot   ; set crsr
    @end_set_cursor_position:
      jmp @end_escape_seq

    ; --- J --- erase screen
    @not_H:
      cmp #$4a      ;J?
      bne @not_J
      jsr get_number_from_esc_seq    ; get parameter
      lda escape_parameter      ; in A
      ; -- 0 -- crsr to end
      bne @not_cursor_to_end
      jsr erase_to_end_of_line    ; del rest of line
      ldx $d6       ; get crsr line
    @erase_next_line:
      inx           ; next line
      cpx #$19      ; line 25?
      bcs @end_escape_seq     ; then end
      txa
      pha           ; save X
      jsr erase_line_by_number      ; erase line
      pla
      tax           ; restore X
      jmp @erase_next_line      ; next line
      ; -- 1 -- beg of screen to crsr
    @not_cursor_to_end:
      cmp #$01
      bne @not_start_to_cursor
      jsr erase_line_to_cursor    ; del start of ln
      ldx $d6       ; get crsr line
    :
      dex           ; previous line
      bmi @end_escape_seq     ; neg line -> end
      txa
      pha           ; save X
      jsr erase_line_by_number      ; erase line
      pla
      tax           ; restore X
      jmp :-
      ; -- 2 -- del screen
    @not_start_to_cursor:
      cmp #$02      ; unknown?
      bne @end_escape_seq     ; then ingnore
      ldx #$18      ; start at ln 24
    :
      txa
      pha           ; save X
      jsr erase_line_by_number      ; erase line
      pla
      tax           ; restore X
      dex           ; previous line
      bpl :-
      jmp @end_escape_seq

    ; --- r ---  set scroll region
    @not_J:
      cmp #$72    ; r?
      bne @not_r
      ; -- prepare top --
      jsr get_number_from_esc_seq
      cmp #$3b    ; is ;?
      bne @error_in_escape_seq   ; no -> error
      ldy escape_parameter    ; get top
      dey         ; line 1 -> line 0
      cpy #$19    ; >=25?..
      bcs @error_in_escape_seq   ; ..error!
      sty temp_ptr_x ; save top
      ; -- prepare bottom --
      jsr get_number_from_esc_seq
      ldy escape_parameter    ; get bottom
      dey         ; line 1 -> line 0
      cpy #$19    ; >=25?..
      bcs @error_in_escape_seq   ; ..error!
      sty temp_ptr_y ; save bottom
      ; -- validate lines --
      lda temp_ptr_x ; restore top
      cmp temp_ptr_y ; >= bottom?..
      bcs @error_in_escape_seq   ; ..error!
      sta scroll_region_start     ; top -> SRStart
      sty scroll_region_end     ; bottom -> SREnd
      ; -- home crsr
      ldx #$00
      ldy #$00
      jsr cursor_plot
    @error_in_escape_seq:
      jmp @end_escape_seq

    @not_r:
    ; --- unknown ---
    @end_escape_seq:
      lda #$00
      sta escape_buffer_length   ; reset esc buffer
      sta escape_mode   ; reset esc mode
      rts

    ; -------------------------------------
    ; Test letter
    ;
    ; char in A
    ; returns carry = 1 for A = letter
    ; -------------------------------------
    test_if_letter:
      cmp #$41    ; smaller then A?
      bcs :+     ; no -> go on
      rts         ; return no letter
    :
      cmp #$5b    ; smaller then Z+1?
      bcs :+     ; no -> go on
      sec         ; return letter
      rts
    :
      cmp #$61    ; smaller then a?
      bcs :+     ; no -> go on
      rts         ; return no letter
    :
      cmp #$7b    ; smaller then z+1?
      bcs :+     ; no -> go on
      sec         ; return letter
      rts
    :
      clc         ; return no letter
      rts

    ; -------------------------------------
    ; test digit
    ;
    ; char in A
    ; returns carry = 1 for A = digit
    ; -------------------------------------

    test_if_digit:
      cmp #$30    ; smaller then 0?
      bcs :+     ; no -> go on
      rts         ; return no digit
    :
      cmp #$3a    ; smaller then 9+1?
      bcs :+     ; no -> go on
      sec         ; return digit
      rts
    :
      clc         ; return no digit
      rts

    ; -------------------------------------
    ; get decimal number from esc sequence
    ;
    ; esc sequence in escape_buffer
    ; first index to process in X
    ; returns: number escape_parameter
    ;          first non digit char in  A
    ; -------------------------------------
    get_number_from_esc_seq:
      lda #$00    ; assume $00
      sta escape_parameter
    @next_digit:
      lda escape_buffer,x  ; get next char
      inx
      jsr test_if_digit   ; digit?
      bcc @done     ; no -> return
      sbc #$30    ; ascii to #
      pha         ; save digit
      ; old value * 10
      ; 10a = ( 4a + a ) * 2
      lda escape_parameter
      asl
      asl         ; ( 4a
      clc
      adc escape_parameter    ; + a )
      asl         ; *2
      sta escape_parameter    ; = 10a
      ; add new digit
      pla         ; resore new digit
      clc
      adc escape_parameter
      sta escape_parameter
      jmp @next_digit     ; next char
    @done:
      rts

    ; *************************************
    ; *
    ; * outgoing data
    ; *
    ; *************************************
    ; -------------------------------------
    ; given a single char (read from keyboard)
    ; work out what data should be sent to the remote host.
    ; input:
    ; A = keypress
    ; output:
    ; Y=0 - no data to be sent (i.e. ignore keypress)
    ; Y=1 - A contains single byte to send
    ; Y=2 - AX points to null terminated string to send
    ; -------------------------------------

    ;Y=0 nothing to send
    ;Y=1 A = char to send
    ;Y=2 AX=pointer to asciiz string to send

    vt100_transform_outbound_char:
      tay
      lda petscii_to_ascii,y   ; PETSCII to ASCII
      bne :+
      ldy #0  ; ignore key
      rts
    :
      cmp #$ff
      beq output_string
      cmp #$fe
      beq command_key  ; command key
      ;default - send (possibly transformed) single char
      ldy #1      ;means A contains single byte to send
    @done:
    rts

    ; -------------------------------------
    ; create an ansi control sequence
    ; -------------------------------------

    output_string:
      tya         ; restore original key

    ; --- crsr U ---
      cmp #$91    ; test crsr U
      bne @not_U
      ldax  #ansi_cursor_up
      ldy   #2
      rts
    ; --- crsr L ---
    @not_U:
      cmp #$9d    ; test crsr L
      bne @not_L
      ldax #ansi_cursor_left
      ldy #2
      rts
    @not_L:
      cmp #$0d  ;test CR
      bne @not_CR
      ldax #crlf
      ldy #2
      rts

    @not_CR:
      ldy #0  ;must be some kind of error
      rts

    ; -------------------------------------
    ; keypress was a command key
    ; -------------------------------------

    command_key:
            tya         ; restore character

    ; --- crsr R ---
    ; ---   ^]   ---
    ; both events send $1d
      cmp #$1d
      bne @not_crsr_R
      lda #$04    ; test control Key
      bit $028d
      beq @cursor_right   ; not pressed
      ; control ] is pressed
      tya         ; send ^]
      ldy #1
      rts

    ; crsr R
    @cursor_right:
      ldax #ansi_cursor_right
      ldy #2
      rts

    ; --- crsr D ---
    ; ---   ^Q   ---
    ; both events send char $11
    @not_crsr_R:
      cmp #$11    ;^Q / crsr down
      bne @not_crsr_D
      lda #$04    ; test control Key
      bit $028d
      beq @cursor_down   ; not pressed
      ; control Q is pressed
      tya         ; send ^Q
      ldy #1
      rts

      ; crsr down is pressed
    @cursor_down:
      ldax #ansi_cursor_down
      ldy #2
      rts

    ; --- HOME key ---
    ; ---    ^S    ---
    ; both events send char $13
    @not_crsr_D:
      cmp #$13    ;^S / HOME
      bne @not_home
      lda #$04    ; test control Key
      bit $028d
      beq @home  ; not pressed
      ; control S is pressed
      tya         ; send ^S
      ldy #1
      rts

    @home:
      lda #$09 ; send TAB
      ldy #1
      rts

    ; --- DEL key ---
    ; ---    ^T    ---
    ; both events send char $14
    @not_home:
      cmp #$14    ;^T / DEL
      bne @not_del
      lda #$04    ; test control Key
      bit $028d
      beq @del   ; not pressed
      ; control T is pressed
      tya         ; send ^T
      ldy #1
      rts

      ; send DEL
    @del:
      lda #$08
      ldy #1
      rts

    ; --- unknown C=-Key ---
    @not_del:
          ldy #0 ;means don't send anything
          rts

    ; *************************************
    ; *
    ; * screen handling
    ; *
    ; *************************************

    ; --- these variables become updated ---
    ;     on crsr movement.
    ;
    ; $d1 $d2  start of screen line
    ; $d3      crsr column
    ; $d6      crsr row
    ; $f3 $f4  start of colour line
    ; $0286    colour

    ; --- these variables become updated ---
    ;     on crsr switching.
    ;
    ; $cc    crsr flag, 0 = on
    ; $cd    crsr blink counter
    ; $ce    char under crsr
    ; $cf    crsr blink phase, 0 normal
    ; $0287  colour under crsr

    ; -------------------------------------
    ; switch curser off and restore char.
    ; this has to be done before every crsr
    ; movement.
    ; After movement there has to be a jump
    ; to cursor_on.
    ; -------------------------------------
    cursor_off:
      pha         ; save registers
      tya
      pha

      ldy #$01    ; crsr of
      sty $cc
      lda $cf     ; crsr revers?
      beq :+     ; no -> return
      dey         ; set normal phase
      sty $cf
      ldy $d3     ; get column
      lda $ce     ; restore char
      sta ($d1),y
      lda $0287   ; restore colour
      sta ($f3),y

    :
      pla         ; restore registers
      tay
      pla
      rts

    ; -------------------------------------
    ; opposite of cursor_off
    ; -------------------------------------

    cursor_on:
      pha
      tya
      pha

      ldy $d3     ; get column
      lda ($d1),y ; save chr
      sta $ce
      eor #$80    ; reverse char
      sta ($d1),y
      lda ($f3),y ; save colour
      sta $0287
      lda $0286   ; set crsr colour
      sta ($f3),y
      inc $cf     ; set reverse phase
      lda #$14    ; set crsr counter..
      sta $cd     ; ..to max
      lda #$00    ; cursor on
      sta $cc

      pla
      tay
      pla
      rts

    ; -------------------------------------
    ; moves the crsr to column Y
    ; and line X
    ; the crsr ist turned off during
    ; operation
    ; destroys all registers
    ; -------------------------------------
    cursor_plot:
      jsr cursor_off

      stx $d6     ; set row
      sty $d3     ; set col
      jsr set_line_vectors
      ldx temp_ptr_x ; set screen line
      ldy temp_ptr_x+1
      stx $d1
      sty $d2
      ldx temp_ptr_y ; set color line
      ldy temp_ptr_y+1
      stx $f3
      sty $f4

      jsr cursor_on
      rts

    ; -------------------------------------
    ; Print char in A to screen
    ; being aware of the crsr state
    ; -------------------------------------

    print_to_screen:
      jsr cursor_off
      jsr plot_char
      jsr cursor_on
      rts

    ; -------------------------------------
    ; print char to screen
    ; char = $ff means no output
    ; chr in A
    ; X and Y unaffected
    ; -------------------------------------

    plot_char:
      sta temp_ptr_x ; save char
      txa         ; save registers
      pha
      tya
      pha
      lda temp_ptr_x ; restore char

    ; PETSCII to ScreenCode (SC)
    ; --- $c0-$ff ---   - illegal -
      cmp #$c0
      bcc :+
      jmp end_plot_char   ; no output
    ; --- $a0-$bf ---   C=(latin-1) chars
    :
      cmp #$a0
      bcc :+
      sbc #$40    ; SC = PET - $40
      jmp @check_for_reverse
    ; --- $80-$9f ---   - illegal -
    :
      cmp #$80
      bcc :+
      jmp end_plot_char   ; no output
    ; --- $60-$7f ---  kapital letters
    :
      cmp #$60
      bcc :+
      sbc #$20    ; SC = PET - $20
      jmp @check_for_reverse
    ; --- $40-$5f ---  small letters
    :
      cmp #$40
      bcc :+
      sbc #$40    ; SC = PET - $40
      jmp @check_for_reverse
    ; --- $20-$3f ---  interpunction
    :
      cmp #$20
      bcc :+
      jmp @check_for_reverse   ; SC = PET
    ; --- $00-$1f ---  - illegal -
    :
      jmp end_plot_char   ; no output

    ; --- handle reverse mode---
    @check_for_reverse:
      ldx $c7     ; reverse mode?
      beq @put_char
      ora #$80    ; reverse char

    ; --- put char to screen ---
    @put_char:
      ldy $d3     ; get crsr col
      cpy #$28    ;col = 40
      bcc @no_line_wrap
                  ;the only way we end up trying to write to column 40 should
                  ;be if we skipped the normal line wrap after writing to col 39
                  ;because we are at the end of the scroll region
                  ;that means we should do a scroll up and then write this char at col 0
      pha
      jsr scroll_up_scrollregion
      pla
      ldy #$00    ; begin of line
      sty $d3
    @no_line_wrap:
      sta ($d1),y ; char to screen
      lda $0286   ; get colour
      sta ($f3),y ; set colour

    ; --- move on crsr ---

      ldx $d6     ; get crsr row
      cpx scroll_region_end     ; end of scroll reg?
      beq @dont_scroll_yet     ; we don't want to trigger a scroll of the whole screen unless
                                        ; we are actually writing a char. we shouldn't scroll just when
                                        ; writing to the bottom right hand screen (else e.g. the title bar
                                        ; in 'nano' gets pushed off the top of the screen.
                                        ;
      cpy #$27    ; col = 39?
      beq move_to_next_line   ; yes -> new line
    @dont_scroll_yet:
      iny         ; move on
      sty $d3

    end_plot_char:
      pla         ; restore registers
      tay
      pla
      tax
      rts

    ; -------------------------------------
    ; subtask of plot_char
    ; ends at end_plot_char
    ; -------------------------------------
    move_to_next_line:
      ldx $d6     ; get crsr row
      cpx scroll_region_end     ; end of scroll reg?
      beq @scroll_up     ; yes -> branche
      cpx #$18    ; line 24?
      beq end_plot_char   ; yes -> crsr stays
    ; --- normal wrap ---
      inx         ; increase line
      stx $d6
      ldy #$00    ; begin of line
      sty $d3
      jsr set_line_vectors
      ldx temp_ptr_x ; set screen line
      ldy temp_ptr_x+1
      stx $d1
      sty $d2
      ldx temp_ptr_y ; set colour line
      ldy temp_ptr_y+1
      stx $f3
      sty $f4
      jmp end_plot_char
    ; --- scroll up ---
    @scroll_up:
      jsr scroll_up_scrollregion
      ldy #$00    ; begin of line
      sty $d3
      jmp end_plot_char

    scroll_up_scrollregion:
      ldx scroll_region_start     ; get first line
    @scroll_one_line:
      ; -- new line: --
      ; -- temp_ptr_z and temp_ptr_w --
      jsr set_line_vectors
      lda temp_ptr_x ; screen line
      ldy temp_ptr_x+1
      sta temp_ptr_z
      sty temp_ptr_z+1
      lda temp_ptr_y ; colour line
      ldy temp_ptr_y+1
      sta temp_ptr_w
      sty temp_ptr_w+1
      ; -- old line: --
      ; -- temp_ptr_x and temp_ptr_y
      inx             ; old line
      jsr set_line_vectors
      ; -- copy chars and colours --
      ldy #$27        ; col 39
    @scroll_one_char:
      lda (temp_ptr_x),y ; copy char
      sta (temp_ptr_z),y
      lda (temp_ptr_y),y ; copy colour
      sta (temp_ptr_w),y
      dey
      bpl @scroll_one_char
      cpx scroll_region_end         ; last line?
      bne @scroll_one_line         ; no -> go on
      jsr erase_line_by_vector       ; del last line

      rts

    scroll_down_scrollregion:
      ldx scroll_region_end     ; get last line
    @scroll_one_line:
      jsr set_line_vectors
      lda temp_ptr_x ; screen line
      ldy temp_ptr_x+1
      sta temp_ptr_z
      sty temp_ptr_z+1
      lda temp_ptr_y ; colour line
      ldy temp_ptr_y+1
      sta temp_ptr_w
      sty temp_ptr_w+1
      ; -- old line: --
      ; -- temp_ptr_x and temp_ptr_y
      dex             ; old line
      jsr set_line_vectors
      ; -- copy chars ond colours --
      ldy #$27        ; col 39
    @scroll_one_char:
      lda (temp_ptr_x),y ; copy char
      sta (temp_ptr_z),y
      lda (temp_ptr_y),y ; copy colour
      sta (temp_ptr_w),y
      dey
      bpl @scroll_one_char
      cpx scroll_region_start         ; first line?
      bne @scroll_one_line         ; no -> go on
      jsr erase_line_by_vector       ; del first line

      rts

    ; -------------------------------------
    ; print string to screen
    ; string: chars, terminated by $00
    ; start lo in x
    ; start hi in y
    ; affects A
    ; takes care of crsr
    ; the string must be smaller
    ;   than 255 chrs
    ; -------------------------------------

    plot_string:
      stx print_ptr   ; store start vector
      sty print_ptr+1
      jsr cursor_off
      ldy #$00
    @next_char:
      lda (print_ptr),y
      beq @end_string      ; $00 terminates string
      jsr plot_char
      iny
      jmp @next_char

    @end_string:
      jsr cursor_on
      rts

    ; -------------------------------------
    ; delete screen line
    ; (Erase Line)
    ;
    ; line number in X
    ;
    ; erase_line_by_vector needs line vectors in temp_ptr_x
    ;                         and temp_ptr_y
    ;
    ; destroys all registers
    ; returns $20 (space) in A
    ; -------------------------------------

    erase_line_by_number:
      jsr set_line_vectors ; line start in temp_ptr_x
              ; col  start in temp_ptr_y

    ; erase chars
    erase_line_by_vector:
      ldy #$27      ; col 39
      lda #$20      ; load space
    :
      sta (temp_ptr_x),y ; clear char
      dey
      bpl :-

    ; set colour
      ldy #$27      ; col 39
      lda #charmode_vanilla      ; load vanilla
    :
      sta (temp_ptr_y),y ; clear char
      dey
      bpl :-

      rts

    ; -------------------------------------
    ; delete screen line from crsr to end
    ; (Erase End of Line)
    ; destroys all registers
    ; -------------------------------------

    erase_to_end_of_line:
      jsr cursor_off
    ; erase chars
      ldy $d3       ; get crsr col
      lda #$20      ; load space
    :
      sta ($d1),y   ; clear char
      iny
      cpy #$28      ; pos 40?
      bne :-      ; next char
      sta $ce       ; del char ..
                    ; ..under crsr
    ; set colour
      ldy $d3       ; get crsr col
      lda #charmode_vanilla      ; load vanilla
    :
      sta ($f3),y   ; set colour
      iny
      cpy #$28      ; pos 40?
      bne :-      ; next char
      jsr cursor_on
      rts

    ; -------------------------------------
    ; delete screen line up to crsr
    ; (Erase Begin of Line)
    ; destroys all registers
    ; -------------------------------------
    erase_line_to_cursor:
    ; erase chars
      ldy $d3       ; get crsr col
      lda #$20      ; load space
    :
      sta ($d1),y   ; clear char
      dey
      bpl :-      ; pos>=0 -> next
      sta $ce       ; del char ..
                    ; ..under crsr
    ; set colour
      ldy $d3       ; get crsr col
      lda #charmode_vanilla      ; load vanilla
    :
      sta ($f3),y   ; clear char
      dey
      bpl :-        ; pos>=0 -> next
      rts

    ; -------------------------------------
    ; set line vectors
    ;
    ; line no in X
    ; destroys A and Y
    ;
    ; sets start of screen line in temp_ptr_x
    ; sets start of colour line in temp_ptr_y
    ; -------------------------------------

    set_line_vectors:
      lda $ecf0,x   ; get lo byte
      sta temp_ptr_x
      sta temp_ptr_y
      ; determin hi byte
      ldy #$04      ; hi byte
      cpx #$07      ; line < 7?
      bcc @got_line_vector
      iny
      cpx #$0d      ; line < 13?
      bcc @got_line_vector
      iny
      cpx #$14      ; line < 20?
      bcc @got_line_vector
      iny           ; line 20-24
    @got_line_vector:
      sty temp_ptr_x+1
      tya
      clc           ; colour RAM =
      adc #$d4      ; video RAM + d4
      sta temp_ptr_y+1
      rts

    ; -------------------------------------
    ; init routines
    ;
    ; -------------------------------------

    initialise_screen:

     ;--- set background ---
      lda #text_background_colour
      sta $d021
    ; --- disable Shift C= ---
      lda #$80
      sta $0291
    ; --- erase screen ---
      ldx #$18      ; start at ln 24
    @erase_one_line:
      txa
      pha           ; save X
      jsr erase_line_by_number      ; erase line
      pla
      tax           ; restore X
      dex           ; previous line
      bpl @erase_one_line

      lda #charmode_vanilla     ; load vanilla
      sta $0286
    ; --- crsr on ---
      jsr cursor_off
      jsr cursor_on
    ; --- put crsr ---
      jsr do_cr
      jsr do_line_feed
      jsr do_line_feed

      rts

    initialise_variables:
      lda #$00
      sta escape_mode
      sta escape_buffer_length
      sta scroll_region_start
      sta font_mode
      sta saved_font_mode
      sta saved_reverse_mode
      sta saved_row
      sta saved_column

      lda #$18    ; last line
      sta scroll_region_end     ; = 24

      rts

    reverse_font_table = font_table + $0400
    initialise_font:
      sei
      ldx #ROM_FONT
      stx temp_ptr_z
      sty temp_ptr_z+1
      ldx #font_table
      stx temp_ptr_y
      sty temp_ptr_y+1
      ldx #reverse_font_table
      stx temp_ptr_x
      sty temp_ptr_x+1

    ; copy font
      ldx #$04      ; copy 4 pages = 1KB
      ldy #$00
    :
      lda (temp_ptr_z),y
      sta (temp_ptr_y),y
      eor #$ff      ; reverse char
      sta (temp_ptr_x),y
      iny
      bne :-
      ; switch to next page
      inc temp_ptr_z+1
      inc temp_ptr_y+1
      inc temp_ptr_x+1
      dex
      bne :-

    ; enable font
      lda $d018
      and #$f1
      ora #$09
      sta $d018
      cli
      rts

    .rodata
    font_attribute_table:    ; bits mean blink, underline, bold
    .byte charmode_vanilla, charmode_bold                           ; 000 001
    .byte charmode_underline, charmode_underline_bold               ; 010 011
    .byte charmode_blink, charmode_blink_bold                       ; 100 101
    .byte charmode_blink_underline, charmode_blink_underline_bold   ; 110 111

    direct_colour_table:
    ;ANSI 30   31 32 32   34 35 36 37
    ;    blk   rd gr ye  blu mg cy wh
    .byte  0, $0a, 5, 7, $0e, 4, 3, 1

    ansi_cursor_up:     .byte esc, brace, $41, $00 ; esc [ A
    ansi_cursor_down:   .byte esc, brace, $42, $00 ; esc [ B
    ansi_cursor_right:  .byte esc, brace, $43, $00 ; esc [ C
    ansi_cursor_left:   .byte esc, brace, $44, $00 ; esc [ D
    crlf:  .byte $0d,$0a,0

    ; -------------------------------------
    ; table ASCII  to PETSCII
    ;
    ; these characters cat be printed
    ;
    ; pet=$00 means ignore the char
    ; pet=$01 means do something complicated
    ; -------------------------------------
    ascii_to_petscii:
      .byte $00   ; $00
      .byte $00   ; $01
      .byte $00   ; $02
      .byte $00   ; $03
      .byte $00   ; $04
      .byte $00   ; $05
      .byte $00   ; $06
      .byte $01   ; $07 BEL
      .byte $01   ; $08 BS/DEL
      .byte $01   ; $09 TAB
      .byte $01   ; $0a LF
      .byte $00   ; $0b
      .byte $00   ; $0c
      .byte $01   ; $0d CR
      .byte $00   ; $0e
      .byte $00   ; $0f
      .byte $00   ; $10
      .byte $00   ; $11
      .byte $00   ; $12
      .byte $00   ; $13
      .byte $00   ; $14
      .byte $00   ; $15
      .byte $00   ; $16
      .byte $00   ; $17
      .byte $00   ; $18
      .byte $00   ; $19
      .byte $00   ; $1a
      .byte $01   ; $1b ESC
      .byte $00   ; $1c
      .byte $00   ; $1d
      .byte $00   ; $1e
      .byte $00   ; $1f
      .byte $20   ; $20  1:1
      .byte $21   ; $21  1:1
      .byte $22   ; $22  1:1
      .byte $23   ; $23  1:1
      .byte $24   ; $24  1:1
      .byte $25   ; $25  1:1
      .byte $26   ; $26  1:1
      .byte $27   ; $27  1:1
      .byte $28   ; $28  1:1
      .byte $29   ; $29  1:1
      .byte $2a   ; $2a  1:1
      .byte $2b   ; $2b  1:1
      .byte $2c   ; $2c  1:1
      .byte $2d   ; $2d  1:1
      .byte $2e   ; $2e  1:1
      .byte $2f   ; $2f  1:1
      .byte $30   ; $30  1:1
      .byte $31   ; $31  1:1
      .byte $32   ; $32  1:1
      .byte $33   ; $33  1:1
      .byte $34   ; $34  1:1
      .byte $35   ; $35  1:1
      .byte $36   ; $36  1:1
      .byte $37   ; $37  1:1
      .byte $38   ; $38  1:1
      .byte $39   ; $39  1:1
      .byte $3a   ; $3a  1:1
      .byte $3b   ; $3b  1:1
      .byte $3c   ; $3c  1:1
      .byte $3d   ; $3d  1:1
      .byte $3e   ; $3e  1:1
      .byte $3f   ; $3f  1:1
      .byte $40   ; $40  1:1
      .byte $61   ; $41 -----
      .byte $62   ; $42
      .byte $63   ; $43
      .byte $64   ; $44 capital
      .byte $65   ; $45
      .byte $66   ; $46
      .byte $67   ; $47
      .byte $68   ; $48
      .byte $69   ; $49
      .byte $6a   ; $4a
      .byte $6b   ; $4b
      .byte $6c   ; $4c
      .byte $6d   ; $4d letters
      .byte $6e   ; $4e
      .byte $6f   ; $4f
      .byte $70   ; $50
      .byte $71   ; $51
      .byte $72   ; $52
      .byte $73   ; $53
      .byte $74   ; $54
      .byte $75   ; $55
      .byte $76   ; $56
      .byte $77   ; $57
      .byte $78   ; $58
      .byte $79   ; $59
      .byte $7a   ; $5a -----
      .byte $5b   ; $5b  1:1
      .byte $5c   ; $5c  1:1
      .byte $5d   ; $5d  1:1
      .byte $5e   ; $5e  1:1
      .byte $5f   ; $5f  1:1
      .byte $60   ; $60  1:1
      .byte $41   ; $61 -----
      .byte $42   ; $62
      .byte $43   ; $63
      .byte $44   ; $64 small
      .byte $45   ; $65
      .byte $46   ; $66
      .byte $47   ; $67
      .byte $48   ; $68
      .byte $49   ; $69
      .byte $4a   ; $6a
      .byte $4b   ; $6b letters
      .byte $4c   ; $6c
      .byte $4d   ; $6d
      .byte $4e   ; $6e
      .byte $4f   ; $6f
      .byte $50   ; $70
      .byte $51   ; $71
      .byte $52   ; $72
      .byte $53   ; $73
      .byte $54   ; $74
      .byte $55   ; $75
      .byte $56   ; $76
      .byte $57   ; $77
      .byte $58   ; $78
      .byte $59   ; $79
      .byte $5a   ; $7a -----
      .byte $7b   ; $7b  1:1 {
      .byte $7c   ; $7c  1:1 |
      .byte $7d   ; $7d  1:1 }
      .byte $7e   ; $7e  1:1 ~
      .byte $00   ; $7f
      .byte $00   ; $80
      .byte $00   ; $81
      .byte $00   ; $82
      .byte $00   ; $83
      .byte $00   ; $84
      .byte $00   ; $85
      .byte $00   ; $86
      .byte $00   ; $87
      .byte $00   ; $88
      .byte $00   ; $89
      .byte $00   ; $8a
      .byte $00   ; $8b
      .byte $00   ; $8c
      .byte $00   ; $8d
      .byte $00   ; $8e
      .byte $00   ; $8f
      .byte $00   ; $90
      .byte $00   ; $91
      .byte $00   ; $92
      .byte $00   ; $93
      .byte $00   ; $94
      .byte $00   ; $95
      .byte $00   ; $96
      .byte $00   ; $97
      .byte $00   ; $98
      .byte $00   ; $99
      .byte $00   ; $9a
      .byte $00   ; $9b
      .byte $00   ; $9c
      .byte $00   ; $9d
      .byte $00   ; $9e
      .byte $00   ; $9f
      .byte $20   ; $a0
      .byte $7f   ; $a1
      .byte $7f   ; $a2
      .byte $bf   ; $a3
      .byte $be   ; $a4
      .byte $7f   ; $a5
      .byte $73   ; $a6
      .byte $b5   ; $a7
      .byte $53   ; $a8
      .byte $bb   ; $a9
      .byte $7f   ; $aa
      .byte $bc   ; $ab
      .byte $7f   ; $ac
      .byte $2d   ; $ad
      .byte $7f   ; $ae
      .byte $7f   ; $af
      .byte $ba   ; $b0
      .byte $b8   ; $b1
      .byte $b6   ; $b2
      .byte $b7   ; $b3
      .byte $7a   ; $b4
      .byte $b9   ; $b5
      .byte $7f   ; $b6
      .byte $7f   ; $b7
      .byte $5a   ; $b8
      .byte $7f   ; $b9
      .byte $7f   ; $ba
      .byte $bd   ; $bb
      .byte $b0   ; $bc
      .byte $b0   ; $bd
      .byte $79   ; $be
      .byte $7f   ; $bf
      .byte $a5   ; $c0
      .byte $61   ; $c1
      .byte $a4   ; $c2
      .byte $61   ; $c3
      .byte $a3   ; $c4
      .byte $a4   ; $c5
      .byte $7f   ; $c6
      .byte $63   ; $c7
      .byte $ad   ; $c8
      .byte $ab   ; $c9
      .byte $ac   ; $ca
      .byte $65   ; $cb
      .byte $69   ; $cc
      .byte $69   ; $cd
      .byte $69   ; $ce
      .byte $69   ; $cf
      .byte $64   ; $d0
      .byte $6e   ; $d1
      .byte $6f   ; $d2
      .byte $6f   ; $d3
      .byte $b1   ; $d4
      .byte $6f   ; $d5
      .byte $af   ; $d6
      .byte $7f   ; $d7
      .byte $6f   ; $d8
      .byte $75   ; $d9
      .byte $75   ; $da
      .byte $75   ; $db
      .byte $b3   ; $dc
      .byte $79   ; $dd
      .byte $7f   ; $de
      .byte $b4   ; $df
      .byte $a2   ; $e0
      .byte $41   ; $e1
      .byte $a1   ; $e2
      .byte $41   ; $e3
      .byte $a0   ; $e4
      .byte $a1   ; $e5
      .byte $7f   ; $e6
      .byte $a6   ; $e7
      .byte $aa   ; $e8
      .byte $a8   ; $e9
      .byte $a9   ; $ea
      .byte $a7   ; $eb
      .byte $49   ; $ec
      .byte $49   ; $ed
      .byte $49   ; $ee
      .byte $49   ; $ef
      .byte $7f   ; $f0
      .byte $4e   ; $f1
      .byte $4f   ; $f2
      .byte $4f   ; $f3
      .byte $b1   ; $f4
      .byte $4f   ; $f5
      .byte $ae   ; $f6
      .byte $7f   ; $f7
      .byte $4f   ; $f8
      .byte $55   ; $f9
      .byte $55   ; $fa
      .byte $55   ; $fb
      .byte $b2   ; $fc
      .byte $59   ; $fd
      .byte $7f   ; $fe
      .byte $59   ; $ff

    ; -------------------------------------
    ; table PETSCII  to ASCII
    ;
    ; these characters can be typed with
    ; the keyboard
    ;
    ; ascii = $00 means ignore key
    ; ascii = $ff menas send string
    ; ascii = $fe means do something
    ;             complicated (command key)
    ; -------------------------------------
    petscii_to_ascii:
      .byte $00   ; $00
      .byte $01   ; $01
      .byte $02   ; $02
      .byte $03   ; $03
      .byte $04   ; $04
      .byte $05   ; $05
      .byte $06   ; $06
      .byte $07   ; $07
      .byte $08   ; $08 DEL
      .byte $09   ; $09 TAB
      .byte $0a   ; $0a
      .byte $0b   ; $0b
      .byte $0c   ; $0c
      .byte $ff   ; $0d CR
      .byte $0e   ; $0e
      .byte $0f   ; $0f
      .byte $10   ; $10
      .byte $fe   ; $11 ^Q (crsr down)
      .byte $12   ; $12
      .byte $fe   ; $13 ^S TAB (HOME)
      .byte $fe   ; $14 ^T BS  (DEL)
      .byte $15   ; $15
      .byte $16   ; $16
      .byte $17   ; $17
      .byte $18   ; $18
      .byte $19   ; $19
      .byte $1a   ; $1a
      .byte $1b   ; $1b ESC
      .byte $1c   ; $1c
      .byte $fe   ; $1d ^](crsr right)
      .byte $1e   ; $1e
      .byte $1f   ; $1f
      .byte $20   ; $20 SPACE
      .byte $21   ; $21 !
      .byte $22   ; $22 "
      .byte $23   ; $23 #
      .byte $24   ; $24 $
      .byte $25   ; $25 %
      .byte $26   ; $26 &
      .byte $27   ; $27 '
      .byte $28   ; $28 (
      .byte $29   ; $29 )
      .byte $2a   ; $2a *
      .byte $2b   ; $2b +
      .byte $2c   ; $2c ,
      .byte $2d   ; $2d -
      .byte $2e   ; $2e .
      .byte $2f   ; $2f /
      .byte $30   ; $30 0
      .byte $31   ; $31 1
      .byte $32   ; $32 2
      .byte $33   ; $33 3
      .byte $34   ; $34 4
      .byte $35   ; $35 5
      .byte $36   ; $36 6
      .byte $37   ; $37 7
      .byte $38   ; $38 8
      .byte $39   ; $39 9
      .byte $3a   ; $3a :
      .byte $3b   ; $3b ;
      .byte $3c   ; $3c <
      .byte $3d   ; $3d =
      .byte $3e   ; $3e >
      .byte $3f   ; $3f ?
      .byte $40   ; $40 @
      .byte $61   ; $41 a
      .byte $62   ; $42 b
      .byte $63   ; $43 c
      .byte $64   ; $44 d
      .byte $65   ; $45 e
      .byte $66   ; $46 f
      .byte $67   ; $47 g
      .byte $68   ; $48 h
      .byte $69   ; $49 i
      .byte $6a   ; $4a j
      .byte $6b   ; $4b k
      .byte $6c   ; $4c l
      .byte $6d   ; $4d m
      .byte $6e   ; $4e n
      .byte $6f   ; $4f o
      .byte $70   ; $50 p
      .byte $71   ; $51 q
      .byte $72   ; $52 r
      .byte $73   ; $53 s
      .byte $74   ; $54 t
      .byte $75   ; $55 u
      .byte $76   ; $56 v
      .byte $77   ; $57 w
      .byte $78   ; $58 x
      .byte $79   ; $59 y
      .byte $7a   ; $5a z
      .byte $5b   ; $5b [
      .byte $5c   ; $5c \ (Pound)
      .byte $5d   ; $5d ]
      .byte $5e   ; $5e ^
      .byte $1b   ; $5f ESC ( <- )
      .byte $00   ; $60
      .byte $41   ; $61 A
      .byte $42   ; $62 B
      .byte $43   ; $63 C
      .byte $44   ; $64 D
      .byte $45   ; $65 E
      .byte $46   ; $66 F
      .byte $47   ; $67 G
      .byte $48   ; $68 H
      .byte $49   ; $69 I
      .byte $4a   ; $6a J
      .byte $4b   ; $6b K
      .byte $4c   ; $6c L
      .byte $4d   ; $6d M
      .byte $4e   ; $6e N
      .byte $4f   ; $6f O
      .byte $50   ; $70 P
      .byte $51   ; $71 Q
      .byte $52   ; $72 R
      .byte $53   ; $73 S
      .byte $54   ; $74 T
      .byte $55   ; $75 U
      .byte $56   ; $76 V
      .byte $57   ; $77 W
      .byte $58   ; $78 X
      .byte $59   ; $79 Y
      .byte $5a   ; $7a Z
      .byte $00   ; $7b
      .byte $00   ; $7c
      .byte $00   ; $7d
      .byte $00   ; $7e
      .byte $00   ; $7f
      .byte $00   ; $80
      .byte $00   ; $81
      .byte $00   ; $82
      .byte $00   ; $83
      .byte $00   ; $84
      .byte $00   ; $85 (f1)
      .byte $00   ; $86 (f3)
      .byte $00   ; $87 (f5)
      .byte $00   ; $88 (f7)
      .byte $00   ; $89 (f2)
      .byte $00   ; $8a (f4)
      .byte $00   ; $8b (f6)
      .byte $00   ; $8c (f8)
      .byte $00   ; $8d (Shift RET)
      .byte $00   ; $8e
      .byte $00   ; $8f
      .byte $00   ; $90
      .byte $ff   ; $91 (crsr up)
      .byte $00   ; $92
      .byte $00   ; $93 (Shift Clr/Home)
      .byte $7f   ; $94 DEL (Shift Ins/Del)
      .byte $00   ; $95
      .byte $00   ; $96
      .byte $00   ; $97
      .byte $00   ; $98
      .byte $00   ; $99
      .byte $00   ; $9a
      .byte $00   ; $9b
      .byte $00   ; $9c
      .byte $ff   ; $9d (crsr left)
      .byte $00   ; $9e
      .byte $00   ; $9f
      .byte $00   ; $a0 (Shift Space)
      .byte $00   ; $a1
      .byte $00   ; $a2
      .byte $00   ; $a3
      .byte $00   ; $a4
      .byte $00   ; $a5
      .byte $00   ; $a6
      .byte $00   ; $a7
      .byte $00   ; $a8
      .byte $7c   ; $a9 | (Shift Pound)
      .byte $00   ; $aa
      .byte $00   ; $ab
      .byte $fe   ; $ac  C= D
      .byte $00   ; $ad
      .byte $fe   ; $ae  C= S
      .byte $00   ; $af
      .byte $fe   ; $b0  C= A
      .byte $00   ; $b1
      .byte $fe   ; $b2  C= R
      .byte $00   ; $b3
      .byte $00   ; $b4
      .byte $00   ; $b5
      .byte $fe   ; $b6  C= L
      .byte $00   ; $b7
      .byte $00   ; $b8
      .byte $00   ; $b9
      .byte $60   ; $ba ` ( Shift @ )
      .byte $00   ; $bb
      .byte $fe   ; $bc  C= C
      .byte $00   ; $bd
      .byte $00   ; $be
      .byte $fe   ; $bf  C= B
      .byte $5f   ; $c0 _ ( Shift * )
      .byte $41   ; $c1 -----
      .byte $42   ; $c2
      .byte $43   ; $c3 capital
      .byte $44   ; $c4
      .byte $45   ; $c5 letters
      .byte $46   ; $c6
      .byte $47   ; $c7 generate
      .byte $48   ; $c8
      .byte $49   ; $c9 these
      .byte $4a   ; $ca
      .byte $4b   ; $cb codes
      .byte $4c   ; $cc
      .byte $4d   ; $cd
      .byte $4e   ; $ce
      .byte $4f   ; $cf
      .byte $50   ; $d0
      .byte $51   ; $d1
      .byte $52   ; $d2
      .byte $53   ; $d3
      .byte $54   ; $d4
      .byte $55   ; $d5
      .byte $56   ; $d6
      .byte $57   ; $d7
      .byte $58   ; $d8
      .byte $59   ; $d9
      .byte $5a   ; $da -----
      .byte $7b   ; $db { ( Shift + )
      .byte $00   ; $dc   ( C= -   )
      .byte $7d   ; $dd } ( Shift - )
      .byte $7e   ; $de ~ ( Pi )
      .byte $00   ; $df
      .byte $00   ; $e0
      .byte $00   ; $e1
      .byte $00   ; $e2
      .byte $00   ; $e3
      .byte $00   ; $e4
      .byte $00   ; $e5
      .byte $00   ; $e6
      .byte $00   ; $e7
      .byte $00   ; $e8
      .byte $00   ; $e9
      .byte $00   ; $ea
      .byte $00   ; $eb
      .byte $00   ; $ec
      .byte $00   ; $ed
      .byte $00   ; $ee
      .byte $00   ; $ef
      .byte $00   ; $f0
      .byte $00   ; $f1
      .byte $00   ; $f2
      .byte $00   ; $f3
      .byte $00   ; $f4
      .byte $00   ; $f5
      .byte $00   ; $f6
      .byte $00   ; $f7
      .byte $00   ; $f8
      .byte $00   ; $f9
      .byte $00   ; $fa
      .byte $00   ; $fb
      .byte $00   ; $fc
      .byte $00   ; $fd
      .byte $00   ; $fe
      .byte $00   ; $ff

    ROM_FONT:
    .incbin "../inc/vt100_font.bin"

    ;-- LICENSE FOR c64_vt100.s --
    ; The contents of this file are subject to the Mozilla Public License
    ; Version 1.1 (the "License"); you may not use this file except in
    ; compliance with the License. You may obtain a copy of the License at
    ; http://www.mozilla.org/MPL/
    ;
    ; Software distributed under the License is distributed on an "AS IS"
    ; basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
    ; License for the specific language governing rights and limitations
    ; under the License.
    ;
    ; The Initial Developer of the Original Code is Lars Stollenwerk.
    ;
    ; Portions created by the Initial Developer are Copyright (C) 2003
    ; Lars Stollenwerk. All Rights Reserved.
    ;
    ;Contributor(s): Jonno Downes
    ; -- LICENSE END --