        Name ccgibm
; File CCGIBM.ASM
; Tektronix emulator for use with MS Kermit/IBM.

;CHINESE
ifdef   MSDOS
        include msgibm.dat
else
        include ccgibm.dat
endif
code    segment public 'code'
        extrn   outchr:near, beep:near, scrseg:near, cmblnk:near
        extrn   clrmod:near, savescr:near, cptchr:near, pcwait:near
        extrn   restscr:near, getflgs:near, clrbuf:near, vtans52:near
        extrn   iseof:near, beep:near
        assume  cs:code, ds:datas, es:nothing

; Initialise TEK mode by setting high resolution screen, etc

tekini  PROC NEAR
        push    ax                      ; do presence tests
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    es
        mov     bx,portval              ; get port flow control chars:
        mov     bx,[bx].flowc           ; bh=xon, bl=xoff or both are nulls
        mov     flow,bx                 ; save here
        mov     ax,bx                   ; get flow control word
        cmp     al,0                    ; able to do xoff?
        je      tekin0                  ; e = no
        call    outmodem                ; tell host xoff while we change modes
tekin0: mov     bx,vtemu.att_ptr        ; emulator screen color ptr
        mov     al,[bx]
        mov     gfcol,al                ; save foreground color
        and     gfcol,0fh               ; save just foreground bits
        and     al,70h                  ; select background color, no bold
        mov     cl,4
        shr     al,cl                   ; get background colors
        mov     gbcol,al                ; set graphics background color
        mov     ah,15                   ; get current screen mode
        int     screen
        cmp     al,3                    ; in a mono/color text mode (2/3)?
        jbe     tekin1                  ; be = yes
        cmp     al,mono                 ; mono text mode (7)?
        je      tekin1                  ; e = yes
        cmp     tekflg,0                ; are we active as Tek device now?
        je      tekin1                  ; e = no
        jmp     tekin13                 ; yes, don't redo graphics setup
tekin1: mov     curmode,al              ; save mode here
        mov     ah,3                    ; get cursor position
        xor     bh,bh                   ; page 0
        int     screen
        mov     cursor,dx               ; save position
        call    savescr                 ; save text screen
                                        ; Presence tests.
tekin2: mov     graph_mode,cga          ; Color. Assume CGA
        mov     segscn,segcga           ; assume cga screen segment
        mov     gpage,0                 ; graphics page 0 but no page 1
        mov     putc,offset gputc       ; CGA character display routine
        mov     gcplot,offset gcgen     ; General character plot routine
        mov     psetup,offset psetupc   ; CGA plot setup routine
        mov     plotptr,offset pltcga   ; CGA dot plot routine
        mov     pincy,offset pincyc     ; CGA inc y routine
        mov     xmult,5                 ; CGA. Scale TEK to PC by 640/1024
        mov     xdiv,8                  ;  so that 0-1023 converts to 0-639
        mov     xmax,640-8              ; x-coord of rightmost character
        mov     ymult,10                ; vertical scale for IBM is 200/780
        mov     ydiv,39                 ;
        mov     ybot,199                ; Bottom of screen is Y=199
        mov     al,tekgraf              ; user video board specification
        cmp     al,0                    ; auto-sensing?
        je      tekin2c                 ; e = yes (default)
        cmp     al,1                    ; user wants CGA?
        jne     tekin2a                 ; ne = no
        jmp     tekin13                 ; do CGA
tekin2a:cmp     al,4                    ; user wants Hercules?
        jne     tekin2b                 ; ne = no
        jmp     tekin8                  ; do Hercules
tekin2b:cmp     al,5                    ; user wants AT&T style?
        jne     tekin2c                 ; ne = no
        jmp     tekin7                  ; do AT&T kind
                                        ; do auto-sensing of display board
                                        ; test for EGA
tekin2c:mov     ax,1200H                ; EGA: Bios alternate select
        mov     bl,10H                  ; Ask for EGA info
        mov     bh,0ffH                 ; Bad info, for testing
        mov     cl,0fH                  ; Reserved switch settings
        int     screen                  ; EGA, are you there?
        and     cl,0fh                  ; four lower switches
        cmp     cl,0cH                  ; Test reserved switch settings
        jb      tekin3                  ; b = ega present
        jmp     tekin7                  ; else no EGA, check other adapters

tekin3: mov     ax,40h                  ; check Bios 40:87h for ega being
        mov     es,ax                   ;  the active display adapter
        test    byte ptr es:[87h],8     ; is ega active?
        jz      tekin3a                 ; z = yes
        jmp     tekin7                  ; ega is inactive, check others
tekin3a:cmp     bl,1                    ; is there 128KB on ega board?
        jb      tekin4                  ; b = less, so no screen saves
        mov     gpage,1                 ; >=128 KB, use two graphics pages
tekin4: mov     graph_mode,ega          ; assume high resolution color
        cmp     cl,3                    ; high resolution color?
        je      tekin5                  ; e = yes
        cmp     cl,9                    ; high resolution color?
        je      tekin5                  ; e = yes
        mov     graph_mode,monoega      ; assume mono monitor on ega board
        test    bh,1                    ; ega mono mode in effect?
        jnz     tekin5                  ; nz = yes
        mov     graph_mode,colorega     ; say ordinary cga on ega board, 64KB
        mov     gpage,1                 ; is enough memory with 200 scan lines
        jmp     short tekin5a           ; use current cga parameters
tekin5: mov     ybot,349                ; text screen bottom is 349 on EGA
        mov     ymult,35                ;
        mov     ydiv,78                 ; scale y by 350/780
tekin5a:mov     segscn,segega           ; use ega screen segment
        mov     psetup,offset psetupe   ; plot setup routine
        mov     plotptr,offset pltega   ; ega dot plot routine
        mov     pincy,offset pincye     ; inc y routine
        mov     putc,offset gputc       ; character display routine
        mov     gcplot,offset gcega     ; EGA character plot routine
        call    fixcolor                ; correct color mapping for some bds
        jmp     tekin13                 ; end of EGA part, do VGA tests below

tekin7: mov     ax,0fc00h               ; Olivetti/AT&T 6300, check rom id
        mov     es,ax
        mov     di,0                    ; start here
        mov     graph_mode,olivetti     ; Olivetti
        mov     cx,attlen               ; length of logo
        mov     si,offset ATTLOGO       ; master string
        repe    cmpsb                   ; do a match
        je      tekin7c                 ; e = a match
        mov     di,0050h                ; look here too
        mov     si,offset ATTLOGO
        mov     cx,attlen
        repe    cmpsb
        je      tekin7c                 ; e = a match
        mov     di,2014h                ; and look here
        mov     si,offset ATTLOGO
        mov     cx,attlen
        repe    cmpsb                   ; do a match
        je      tekin7c                 ; e = a match, else try other types
tekin7a:mov     graph_mode,toshiba
        mov     ax,0f000h               ; Check for Toshiba T3100, rom scan
        mov     es,ax
        mov     di,0014h                ; start here
        mov     si,offset TOSHLOGO      ; master string
        mov     cx,toshlen              ; length
        repe    cmpsb                   ; do a match
        je      tekin7c                 ; e = a match, else try other types
tekin7b:mov     graph_mode,vaxmate      ; DEC VAXmate II
        mov     ax,0f000h               ; Check for VAXmate II rom signature
        mov     es,ax
        mov     di,0e000h               ; start here
        mov     si,offset DECLOGO       ; master string
        mov     cx,declen               ; length
        repe    cmpsb                   ; do a match
        jne     tekin7d                 ; ne = mismatch, try other types

                                        ; Olivetti/AT&T, Toshiba, VAXmate
tekin7c:mov     gpage,0                 ; only page 0 with 640 by 400 mode
        mov     segscn,segcga           ; use cga screen segment (0b800h)
        mov     psetup,offset psetupo   ; plot setup routine
        mov     plotptr,offset pltcga   ; cga dot plot routine
        mov     pincy,offset pincyh     ; inc y routine (Herc style addresses)
        mov     putc,offset gputc       ; character display routine
        mov     gcplot,offset gcgen     ; General character plot routine
        mov     ybot,399                ; bottom of screen is y = 399
        mov     ymult,20                ; vertical scale = 400/780
        mov     ydiv,39                 ; same as cga setup
        jmp     tekin13

tekin7d:cmp     curmode,mono            ; mono text mode?
        je      tekin8                  ; e = yes
        jmp     tekin11                 ; ne = no, try cga
                                        ; test for Hercules
tekin8: call    scrseg                  ; get screen segment, test Environment
        cmp     tv_mode,0               ; Environment active?
        je      tekin8a                 ; e = no, ok to test for Hercules
        jmp     tekin10                 ; don't do Herc mode, do Mono
tekin8a:mov     dx,hstatus              ; Herc status port
        in      al,dx                   ; read it
        mov     bl,al                   ; save here
        and     bl,80h                  ; remember retrace bit
        mov     cx,0ffffh               ; do many times (for fast machines)
tekin8b:mov     dx,hstatus              ; check status port
        in      al,dx
        and     al,80h                  ; select bit
        jmp     $+2                     ; use a little time
        cmp     bl,al                   ; did it change?
        loope   tekin8b                 ; test again if not
        je      tekin10                 ; e = no change in bit, not Herc
        mov     graph_mode,hercules     ; say have Herc board
        mov     segscn,seghga           ; assume hga screen segment
        mov     putc,offset gputc       ; character display routine
        mov     gcplot,offset gcgen     ; General character plot routine
        mov     psetup,offset psetuph   ; plot setup routine to use
        mov     plotptr,offset pltcga   ; use cga dot plot routine for Herc
        mov     pincy,offset pincyh     ; inc y routine
        mov     xmult,45                ; Scale TEK to Hercules by 720/1024
        mov     xdiv,64                 ;  so that 0-1023 converts to 0-719
        mov     xmax,720-8              ; x-coord of rightmost character
        mov     ymult,87                ; vertical scale for Hercules is
        mov     ydiv,195                ;  348/780
        mov     ybot,347                ; bottom of screen is y = 347
        mov     ax,seghga               ; segment of Herc video display
        mov     es,ax
        mov     al,es:[8000h]           ; read original contents, page 1
        not     byte ptr es:[8000h]     ; write new pattern
        mov     ah,es:[8000h]           ; read back
        not     byte ptr es:[8000h]     ; restore original contents
        not     ah                      ; invert this too
        cmp     ah,al                   ; same (memory present?)
        jne     tekin9                  ; ne = not same, no memory there
        mov     gpage,1                 ; say two pages of display memory
tekin9: jmp     tekin13
                                        ; set to MONO
tekin10:mov     graph_mode,mono         ; force monochrome adapter text
        mov     segscn,segmono          ; assume mono screen segment
        call    scrseg                  ; Environments: get virtual screen
        mov     segscn,ax               ;  seg returned in ax and es:di
        mov     gpage,0
        mov     putc,offset mputc       ; character display routine
        mov     psetup,offset psetupm   ; plot setup routine to use
        mov     plotptr,offset pltmon   ; use hga dot plot routine
        mov     pincy,offset pincym     ; inc y routine
        mov     xmult,5                 ; Scale TEK to mono by 640/1024
        mov     xdiv,8                  ;  so that 0-1023 converts to 0-639
        mov     xmax,640-8              ; x-coord of rightmost character
        mov     ymult,10                ; vertical scale for mono is 200/780
        mov     ydiv,39
        mov     ybot,200                ; bottom of screen is y = 200 for Bios
        jmp     tekin13                 ; Uses TEXT mode, for safety

                                        ; test for CGA
tekin11:mov     graph_mode,cga          ; set CGA high resolution graphics
        mov     segscn,segcga           ; CGA screen segment
        jmp     tekin13

                                        ; Set Graphics mode
tekin13:cmp     graph_mode,hercules     ; Hercules?
        jne     tekin14                 ; ne = no
        call    hgraf                   ; set Herc graphics mode, clear regen
        jmp     short tekin16           ; restore screen
tekin14:mov     ah,0                    ; set screen mode
        mov     al,graph_mode           ;  to this screen mode
        cmp     tekgraf,3               ; user wants "VGA" modes (640x480)?
        jne     tekin14a                ; ne = no
        cmp     al,monoega              ; yes, allow high resolution stuff?
        jb      tekin14a                ; b = no
        cmp     al,ega                  ; ditto
        ja      tekin14a                ; a = no
        add     al,2                    ; use modes 17(b/w) and 18(10)(color)
        mov     ybot,479                ; text screen bottom is 479 on VGA
        mov     ymult,48
tekin14a:cmp    gpage,0                 ; only page 0 available?
        je      tekin15                 ; e = yes, and watch for Bios errors
        cmp     inited,0                ; first time through?
        je      tekin15                 ; e = yes, clear the page of old junk
        or      al,80h                  ; save regen buffer (save area too)
tekin15:int     screen                  ; Bios Set Mode.

tekin16:mov     tekflg,1                ; starting Tek sub mode
        cmp     inited,0                ; inited yet?
        jne     tekin19                 ; ne = yes, restore screen
        mov     ttstate,offset tektxt   ; do displayable text
        mov     prestate,offset tektxt  ; set a previous state of text
        mov     inited,1                ; say we have initialized
        mov     al,gfcol
        mov     tfcol,al                ; remember current coloring
        mov     al,gbcol
        mov     tbcol,al
        call    tekcls                  ; clear screen, for ega coloring
        jmp     short tekin20
tekin19:call    tekrest                 ; restore old graphics screen
        mov     al,tfcol                ; and coloring
        mov     gfcol,al
        mov     al,tbcol
        mov     gbcol,al
tekin20:mov     ax,flow                 ; get flow control word
        xchg    ah,al                   ; get xon into al
        cmp     al,0                    ; able to send xon?
        je      tekin21                 ; e = no
        call    outmodem                ; tell host xon
tekin21:clc                             ; clear carry for success
        jmp     short tekin23
tekin22:stc                             ; set carry for failure
tekin23:pop     es
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
tekini  ENDP

TEKRINT proc    near                    ; Tek reinitialization entry point
        mov     inited,0                ; do complete reinitialization
        jmp     tekini
TEKRINT endp

;Terminal emulation. Enter with received character in AL.

TEKEMU PROC     NEAR                    ; main emulator
        cmp     tekflg,0                ; Tek mode active yet? (msz call)
        jne     tektt1                  ; ne = yes
        call    tekini                  ; init now
        mov     ttstate,offset tektxt   ; initial state
        mov     prestate,offset tektxt  ; set a previous state of text
        jnc     tektt1                  ; nc = succeeded
        ret                             ; else failed to init, just return
tektt1: and     al,7fh                  ; force Tek chars to be 7 bits
        cmp     al,0                    ; NUL char?
        je      tekign                  ; e = yes, ignore it before logging
        push    ax
        call    getflgs                 ; get msy yflags into al
        mov     yflags,al
        test    al,capt                 ; capturing output?
        pop     ax
        jz      tektt4                  ; z = no, forget this part
        push    ax                      ; save char
        call    cptchr                  ; give it captured character
        pop     ax                      ; restore character and keep going
tektt4: test    yflags,trnctl           ; debug? if so use tty mode
        jz      tektt5                  ; z = no
        cmp     al,DEL                  ; DEL char?
        jne     tektt4a                 ; ne = no
        mov     al,5eh                  ; make DEL a caret query mark
        call    outscrn
        mov     al,3fh                  ; the query mark
        call    outscrn
        jmp     short tekign
tektt4a:cmp     al,' '                  ; control char?
        jae     tektt4b                 ; ne = no
        push    ax
        mov     al,5eh                  ; caret
        call    outscrn
        pop     ax
        add     al,'A'-1                ; make char printable
tektt4b:call    outscrn

tekign: ret                             ; Ignore this character

tektt5: call    tkscan                  ; scan for "ESC [ ? 3 8 l" exit code
tektt5a:cmp     al,0                    ; null char response?
        je      tekign                  ; e = yes, ignore the character
        cmp     al,' '                  ; control code?
        jb      tektt6                  ; b = yes, decode
        jmp     ttstate                 ; no, do current state
                                        ; Control characters:
tektt6: cmp     al,GS                   ; Line plot command?
        jne     tektt7                  ; ne = no
        mov     visible,0               ; Next move is invisible
        and     status,not txtmode      ; set status report byte
        mov     ttstate,offset tekline  ; expect coordinates next
        jmp     tektt12
tektt7: cmp     al,RS                   ; Incremental dot command?
        jne     tektt8                  ; ne = no
        and     status,not txtmode      ; set status report
        mov     ttstate,offset tekrlin  ; expect pen command next
        jmp     tektt12
tektt8: cmp     al,FS                   ; Point plot command?
        jne     tektt9                  ; ne = no
        mov     visible,0               ; next move is invisible
        and     status,not txtmode      ; set status report byte
        mov     ttstate,offset tekpnt
        jmp     tektt12
tektt9: cmp     al,US                   ; assert text mode? [bjh]
        jne     tektt10                 ; ne = no
        or      status,txtmode          ; set status report byte
        mov     ttstate,offset tektxt   ; Go to TEKTXT next time
        mov     bypass,0                ; reset bypass condition
        jmp     tektt12
tektt10:cmp     al,ESCAPE               ; Escape?
        jne     tektt11                 ; ne = no
        or      status,txtmode          ; set status report byte
        cmp     ttstate,offset tekesc   ; already in escape state?
        je      tektt14                 ; e = yes, nest no further
        push    ttstate                 ; current state
        pop     prestate                ; save here as previous state
        mov     ttstate,offset tekesc   ; next state parses escapes
        ret
tektt11:cmp     al,CAN                  ; Control X? (exits Tek sub mode)
        jne     tektt13                 ; ne = no, stay in current state
        cmp     ttstate,offset tekesc   ; ESC Control-X?
        je      tektt13                 ; yes, parse it in tekesc code
        mov     ttstate,offset tektxt   ; back to text mode
        test    flags.vtflg,tttek       ; main Tek emulator?
        jnz     tektt12                 ; nz = yes, ignore the ^X
        call    tekend                  ; else exit sub mode
        mov     tekflg,0                ; clear Tek sub mode flag
tektt12:mov     prestate,offset tektxt  ; make previous state text
tektt14:ret
tektt13:jmp     ttstate                 ; let someone else worry about this
TEKEMU  ENDP

; End TEK emulation, recover previous screen

TEKEND  PROC    NEAR
        cmp     tekflg,0                ; Tek sub mode active?
        jne     teknd0                  ; ne = yes
        ret                             ; else return as is.
teknd0: call    teksave                 ; save graphics screen to page 1
        cmp     graph_mode,hercules     ; Hercules?
        jne     teknd1                  ; ne = no
        call    htext                   ; yes then set up Hercules text mode
teknd1: mov     ah,0                    ; set video mode
        mov     al,curmode              ; restore previous screen mode
        int     screen                  ; revert to text screen
        call    restscr                 ; restore text screen
        mov     dx,cursor               ; saved cursor position
        mov     bh,0                    ; page 0
        mov     ah,2                    ; set cursor
        int     screen
        ret
TEKEND  ENDP

; State machine active while Tek is active. Senses ESC [ ? 3 8 l to exit
; Tek mode and return to either non-sub mode terminal or to a VT102.
; Plays back unmatched escape sequences. Enter with character in al.

tkscan  proc    near
        and     al,7fh                  ; strip high bit
        cmp     al,byte ptr tkoff       ; start of Tek Off sequence?
        jne     tkscn1                  ; ne = no
        call    tkscn4                  ; playback previously matched chars
        mov     tkcnt,1                 ; count matched chars (one now)
        mov     tkoffs,al               ; save full character, with high bit
        mov     al,0                    ; our temporary response
        jmp     short tkscnx            ;  and exit

tkscn1: push    bx                      ; check for char in Tek Off sequence
        mov     bx,tkcnt                ; number of chars matched in Tek Off
        mov     tkoffs[bx],al           ; save this char
        cmp     al,byte ptr tkoff[bx]   ; match expected char in sequence?
        pop     bx
        jne     tkscn3                  ; ne = no, play back partial match
        inc     tkcnt                   ; count new match
        mov     al,0                    ; our temporary response
        cmp     tkcnt,tkofflen          ; matched all char in sequence?
        jne     tkscnx                  ; ne = not yet, wait for more
        mov     tkcnt,0                 ; clear counter
        cmp     flags.vtflg,tttek       ; are we a full Tek terminal now?
        jne     tkscn2                  ; ne = no, a submode
        call    vtans52                 ; toggle terminal type, in msyibm
tkscn2: mov     al,CAN                  ; simulate arrival of Control-X
        jmp     short tkscnx            ;  all done

tkscn3: call    tkscn4                  ; playback previously matched chars
        mov     tkcnt,0                 ; reset to no match and exit
tkscnx: ret                             ; common exit

                                        ; local worker procedure
tkscn4: push    ax                      ; save break char (in al)
        push    cx                      ; playback partial sequence to screen
        mov     cx,tkcnt                ; number of chars matched before break
        jcxz    tkscn4b                 ; z = none
        push    si
        mov     si,offset tkoffs        ; string to be played back
tkscn4a:cld
        lodsb                           ; get a char into al
        push    cx
        push    si                      ; save these around tektt5a work
        call    tektt5a                 ; use it
        pop     si
        pop     cx
        loop    tkscn4a                 ; do all that came in previously
        pop     si
tkscn4b:pop     cx
        pop     ax                      ; recover break char
        ret
tkscan  endp


TEKTXT  proc    near                    ; Dispatch on text characters
        cmp     al,DEL                  ; RUBOUT?
        jne     tektx1                  ; ne = no
        mov     al,bs                   ; make BS
        jmp     short tektx7
tektx1: cmp     al,CR                   ; carriage return (^M)?
        je      tektx9                  ; e = yes
tektx2: cmp     al,LF                   ; line feed (^J)?
        je      tektx9                  ; e = yes
tektx3: cmp     al,FF                   ; form feed (^L)?
        jne     tektx4                  ; ne = no
        call    tekcls                  ; clear the screen
        jmp     short tektx8
tektx4: cmp     al,VT                   ; vertical tab (^K)?
        je      tektx7
        cmp     al,bell                 ; bell (^G)?
        jne     tektx5                  ; ne = no
        call    beep
        mov     bypass,0                ; clear GIN mode bypass condition
        jmp     short tektx8
tektx5: cmp     al,tab                  ; horizontal tab (^I)?
        je      tektx7                  ; e = yes
tektx6: cmp     al,BS                   ; backspace (^H)?
        je      tektx7                  ; e = yes
        cmp     al,' '                  ; control char?
        jb      tektx8                  ; b = yes, ignore it
tektx7: cmp     bypass,0                ; bypass mode off?
        jne     tektx8                  ; ne = no, it's on so skip display
        call    OUTSCRN                 ; output character to the screen
tektx8: ret
tektx9: mov     bypass,0                ; clear GIN mode bypass condition
        jmp     short tektx7
TEKTXT  endp

; Process escape sequences. Callable from msz terminal emulator.
; Enter with received character in AL. Escape sequences are generally
; treated as interruptions to the current plotting/text command. Screen
; clearing is the exception by causing a general emulator reset.
TEKESC  PROC    NEAR
        mov     bypass,0                ; clear GIN mode bypass condition
        mov     ttstate,offset tekesc   ; in case get here from msz file
        cmp     tekflg,0                ; Tek mode active yet? (msz call)
        jne     tekesc1                 ; ne = yes
        call    tekini                  ; init now
        mov     prestate,offset tektxt  ; set a previous state of text
        jnc     tekesc1                 ; nc = succeeded
        ret                             ; else failed to init, just return

tekesc1:cmp     al,'Z'                  ; ESC-Z Identify?
        jne     tekesc2                 ; ne = no
        call    SENDID                  ; Send terminal identification
        jmp     tekescx

tekesc2:cmp     al,FF                   ; ESC-FF Clear screen?
        jne     tekesc3                 ; ne = no
        call    tekcls                  ; Clear screen
        mov     prestate,offset tektxt  ; make previous state text mode
        jmp     tekescx                 ; Return to text mode after ESC-FF

tekesc3:cmp     al,ESCZ                 ; ESC-^Z Enter GIN mode?
        jne     tekesc4                 ; ne = no
        cmp     graph_mode,mono         ; Monochrome text mode?
        je      tekesc3a                ; e = yes, no crosshairs in text mode
        mov     bypass,1                ; turn on GIN mode bypass conditon
        call    CROSHAIR                ; Activate the cross-hairs
        jmp     tekescx
tekesc3a:call   beep                    ; tell the user we are unhappy
        jmp     tekescx                 ; and ignore the command

tekesc4:cmp     al,ENQ                  ; ESC-^E Enquiry for cursor position?
        jne     tekesc5                 ; ne = no
        mov     bypass,1                ; set bypass mode
        call    SENDSTAT                ; send status
        jmp     tekescx

tekesc5:cmp     al,CAN                  ; ESC Control-X?
        jne     tekesc6                 ; ne = no
        mov     bypass,1                ; set bypass condition
        jmp     tekescx

tekesc6:cmp     al,3fh                  ; query mark? (ESC ? means DEL)
        jne     tekesc7                 ; ne = no
        mov     al,DEL                  ; replace with DEL code
        jmp     tekescx                 ; and process it as if received.

tekesc7:cmp     al,accent               ; accent grave, line pattern series?
        jb      tekesc8                 ; b = no
        cmp     al,65h                  ; lowercase e?
        ja      tekescx                 ; a = beyond line pattern series
        push    bx
        mov     bl,al
        sub     bl,accent               ; remove bias
        and     bl,7                    ; eight patterns, roll over excess
        mov     bh,0
        shl     bx,1                    ; make this a word index
        mov     bx,linetab[bx]          ; get line pattern word
        mov     linepat,bx              ; save in active word
        pop     bx                      ; return to previous mode

tekesc8:cmp     al,5bh                  ; right square bracket?
        jne     tekescx                 ; ne = no
        jmp     tekcol                  ; start coloring scan

tekescx:push    ax
        mov     ax,prestate             ; get previous state
        mov     ttstate,ax              ; restore it
        or      ax,ax                   ; test for none
        pop     ax
        jz      go2text                 ; z = none, use text mode
        clc
        ret                             ; resume previous state

go2text:mov     ttstate,offset tektxt   ; Go to TEKTXT next time
        mov     lastc,0                 ; clear last drawing coordinate flag
        or      status,txtmode          ; set text mode in status byte
        clc
        ret
TEKESC  ENDP
; Parse ESC [ Pn ; Pn m
; where Pn = 30-37 foreground color, 40-47 background color, ANSI standard
TEKCOL  proc    near
        mov     word ptr lastd,0        ; clear parsing flags used below
        mov     ttstate,offset tekco1   ; resume parsing below
        clc
        ret
tekco1: cmp     lastd,'3'               ; units digit in 30 series?
        jne     tekco2                  ; ne = no
        inc     lastd+1                 ; count argument
        sub     al,'0'                  ; ascii to binary
        cmp     al,7                    ; numeric?
        jbe     tekco1a                 ; be = yes
        jmp     tekco10                 ; a = no, error
tekco1a:push    bx
        mov     bl,al
        mov     bh,0
        mov     al,byte ptr colortb[bx] ; reverse coloring
        pop     bx
        and     tfcol,not (7)           ; retain intensity bit
        or      tfcol,al                ; remember foreground color
        mov     lastd,0                 ; clear parsing flag
        ret

tekco2: cmp     lastd,'4'               ; units digit in 40 series?
        jne     tekco4                  ; ne = no
        inc     lastd+1                 ; count argument
        sub     al,'0'
        cmp     al,7                    ; numeric?
        ja      tekco10                 ; a = no, error
        push    bx
        mov     bl,al
        mov     bh,0
        mov     al,byte ptr colortb[bx] ; reverse coloring
        pop     bx
        mov     tbcol,al                ; remember background color
        mov     lastd,0                 ; clear parsing flag
        ret

tekco4: cmp     lastd,0                 ; looking for tens digit?
        jne     tekco10                 ; ne = yes, error
        cmp     al,';'                  ; separator?
        jne     tekco5                  ; ne = no
        ret                             ; ignore it

tekco5: cmp     al,'0'                  ; remove intensity, set b/w?
        jne     tekco6                  ; ne = no
        mov     tfcol,7                 ; regular white
        mov     tbcol,0                 ;  on black
        inc     lastd+1                 ; count argument
        ret

tekco6: cmp     al,'1'                  ; intensity bit?
        jne     tekco7                  ; ne = no
        and     tfcol,not (8)
        or      tfcol,8                 ; set foreground intensity
        inc     lastd+1                 ; count argument
        ret

tekco7: cmp     al,'m'                  ; end of sequence
        je      tekco8                  ; e = yes
        cmp     al,'3'
        jb      tekco10                 ; b = not allowed tens digit
        cmp     al,'4'
        ja      tekco10                 ; a = not allowed tens digit
        mov     lastd,al                ; remember tens digit
        inc     lastd+1                 ; count argument
        ret

tekco8: cmp     lastd+1,0               ; number of ansi arguments, zero?
        ja      tekco9                  ; a = no, got some
        mov     tbcol,0                 ; none is same as 0, set b/w
        mov     tfcol,7
tekco9: mov     al,tbcol                ; success, store coloring
        mov     gbcol,al                ; set background color
        mov     al,tfcol
        mov     gfcol,al                ; set foreground color
tekco10:mov     word ptr lastd,0        ; clear argument and number of args
        call    fixcolor                ; do special ega corrections
        mov     al,gfcol                ; update these in case error
        mov     tfcol,al
        mov     al,gbcol
        mov     tbcol,al
        jmp     tekescx                 ; finish escape state
TEKCOL  endp

; Revise screen color codes for ega boards with mono displays and limited
; memory.
fixcolor proc   near
        cmp     graph_mode,ega          ; one of these ega modes?
        je      fixcol0                 ; e = yes
        cmp     graph_mode,colorega
        je      fixcol0
        cmp     graph_mode,monoega
        je      fixcol0
        ret                             ; else ignore color corrections
fixcol0:mov     ah,gfcol
        mov     al,gbcol
        cmp     graph_mode,monoega      ; monochrome display?
        jne     fixcol3                 ; ne = no
        test    al,7                    ; bright backgound?
        jnz     fixcol1                 ; nz = yes
        mov     ah,1                    ; normal foreground
        test    gfcol,8                 ; intensity on?
        jz      fixcol1                 ; z = no
        mov     ah,5                    ; say bright foreground
fixcol1:test    al,7                    ; black backgound?
        jz      fixcol2                 ; z = yes
        mov     al,1                    ; regular video
fixcol2:cmp     ah,al                   ; same color in both?
        jne     fixcol3                 ; ne = no
        mov     ah,1                    ; make foreground regular
        mov     al,0                    ;  and background black
fixcol3:mov     gfcol,ah
        mov     gbcol,al
        cmp     gpage,0                 ; minimal memory (64KB mono and ega)?
        ja      fixcol4                 ; a = no, enough, else strange mapping
        mov     al,gfcol                ; fix coloring to map planes C0 to C1
        and     al,5                    ; and C2 to C3 (as 0, 3, 0Ch, or 0Fh)
        mov     ah,al                   ; make a copy
        shl     ah,1                    ; duplicate planes C0, C2 in C1, C3
        or      al,ah                   ; merge the bits
        mov     gfcol,al                ; store proper foreground color
        mov     al,gbcol                ; repeat for background color
        and     al,5
        mov     ah,al
        shl     ah,1
        or      al,ah
        mov     gbcol,al
fixcol4:ret
fixcolor endp

TEKLINE proc    near                    ; GS line drawing
        call    tekxyc                  ; parse coordinates from input bytes
        jnc     teklin1                 ; nc = not done yet
        mov     cl,visible              ; get moveto or drawto variable
        call    tekdraw                 ; move that point
        mov     visible,1               ; say next time we draw
teklin1:ret
TEKLINE endp

TEKPNT  proc    near                    ; FS plot single point
        call    tekxyc                  ; parse coordinates
        jnc     tekpnt1                 ; nc = not done yet
        mov     cl,0                    ; do not draw
        call    tekdraw                 ; move to the point
        mov     ax,si                   ; copy starting point to end point
        mov     bx,di                   ; ax,bx,si,di are in PC coordinates
        mov     cl,1                    ; make plot visible
        call    line                    ; draw the dot
        mov     visible,0               ; return to invisibility
tekpnt1:ret
TEKPNT  endp

; Decode graphics x,y components. Returns carry set to say have all
; components for a line, else carry clear. Understands 4014 lsb extensions.
; Permits embedded escape sequences.
TEKXYC  proc    near
        cmp     al,CR                   ; Exit drawing on CR,LF,RS,US,FS,CAN
        je      tekghx                  ; e = yes, a cr
        cmp     al,LF                   ; these terminate line drawing cmds
        je      tekghx
        cmp     al,FS                   ; <FS>
        je      tekghx
        cmp     al,RS                   ; <RS>
        je      tekghx
        cmp     al,US                   ; <US>
        je      tekghx
        cmp     al,CAN                  ; and <CAN>
        je      tekghx                  ; BUT ignore other control chars
        cmp     al,20h                  ; Control char?
        jb      tekgh0                  ; b = yes, ignore it
        cmp     al,40h
        jb      tekgh2                  ; 20-3F are HIX or HIY
        cmp     al,60h                  ; 40-5F are LOX (causes beam movement)
        jb      tekgh4                  ; 60-7F are LOY
                                        ; Extract low-order 5 bits of Y coord
        mov     ah,tek_loy              ; Copy previous LOY to MSB (4014)
        mov     tek_lsb,ah
        and     al,1Fh                  ; LOY is 5 bits
        mov     tek_loy,al
        cmp     lastc,loy               ; 2nd LOY in a row?
        je      tekgh1                  ; Yes, then LSB is valid
        mov     tek_lsb,0               ; 1st one, clear LSB
tekgh1: mov     lastc,loy               ; LOY seen, expect HIX (instead of HIY)
tekgh0: clc                             ; c clear = not completed yet
        ret
tekghx: jmp     go2text

                        ; Extract high-order 5 bits (X or Y, depending on lastc)
tekgh2: and     ax,1Fh                  ; Just 5 bits
        mov     cl,5
        shl     ax,cl                   ; Shift over 5 bits
        cmp     lastc,loy               ; was last coordinate a low-y?
        je      tekgh3                  ; e = yes, parse hix
        mov     tek_hiy,ax              ; this byte has HIY
        mov     lastc,hiy
        clc
        ret
tekgh3: mov     tek_hix,ax              ; This byte has HIX
        mov     lastc,hix
        clc
        ret
tekgh4: and     al,1Fh                  ; Just 5 bits
        mov     tek_lox,al
        mov     lastc,lox
        mov     ax,tek_hix              ; Combine HIX*32
        or      al,tek_lox              ;  with LOX
        mov     bx,tek_hiy              ; Same for Y
        or      bl,tek_loy
        stc                             ; set c to say completed operation
        ret
TEKXYC  endp

TEKRLIN proc    near                    ; RS relative line drawing
        cmp     al,' '                  ; Pen up command?
        jne     tekrli1                 ; ne = no, try pen down
        mov     visible,0               ; do invisible movements
        jmp     short tekrli2           ; do the command
tekrli1:cmp     al,'P'                  ; pen down command?
        jne     tekrli3                 ; ne = no, return to text mode
        mov     visible,1               ; set visible moves

tekrli2:mov     ax,x_coord              ; PC x coordinate of pen
        mov     bx,y_coord              ;    y coordinate
        call    pctotek                 ; get current pen position in Tek coor
        mov     cl,0                    ; invisible, moveto
        call    tekdraw                 ; move that point, set oldx and oldy
        mov     ttstate,offset tekinc   ; next get incremental movement cmds
        ret

tekrli3:mov     visible,0               ; bad char, reset visibility
        push    prestate
        pop     ttstate                 ; restore previous state
        jmp     tektt5                  ; deal with the break char
TEKRLIN endp
                                        ; interpret RS inc plot command byte
TEKINC  proc    near                    ; get movement character and do cmd
        cmp     al,'A'                  ; move right?
        jne     tekinc1                 ; ne = no
        inc     oldx                    ; adjust beam position
        jmp     short tekinc9
tekinc1:cmp     al,'E'                  ; move right and up?
        jne     tekinc2                 ; ne = no
        inc     oldx
        inc     oldy
        jmp     short tekinc9
tekinc2:cmp     al,'D'                  ; move up?
        jne     tekinc3                 ; ne = no
        inc     oldy
        jmp     short tekinc9
tekinc3:cmp     al,'F'                  ; move left and up?
        jne     tekinc4                 ; ne = no
        dec     oldx
        inc     oldy
        jmp     short tekinc9
tekinc4:cmp     al,'B'                  ; move left?
        jne     tekinc5                 ; ne = no
        dec     oldx
        jmp     short tekinc9
tekinc5:cmp     al,'J'                  ; move left and down?
        jne     tekinc6                 ; ne = no
        dec     oldx
        dec     oldy
        jmp     short tekinc9
tekinc6:cmp     al,'H'                  ; move down?
        jne     tekinc7                 ; ne = no
        dec     oldy
        jmp     short tekinc9
tekinc7:cmp     al,'I'                  ; move right and down?
        jne     tekincb                 ; ne = no, bad command
        inc     oldx
        dec     oldy
tekinc9:cmp     oldx,0                  ; too far left?
        jge     tekinc10                ; ge = no
        mov     oldx,0                  ; else stop at the left margin
tekinc10:cmp    oldx,maxtekx-1          ; too far left?
        jle     tekinc11                ; le = no
        mov     oldx,maxtekx-1          ; else stop that the left margin
tekinc11:cmp    oldy,maxteky-1          ; above the top?
        jle     tekinc12                ; le = no
        mov     oldy,maxteky-1          ; else stop at the top
tekinc12:cmp    oldy,0                  ; below the bottom?
        jge     tekinc13                ; ge = no
        mov     oldy,0                  ; else stop at the bottom
tekinc13:mov    ax,oldx                 ; ax is vector x end point
        mov     bx,oldy                 ; bx is vector y end point
        mov     cl,visible
        call    tekdraw                 ; move/draw to that point
        ret
tekincb:push    prestate                ; bad character, exit inc plot mode
        pop     ttstate                 ; new state is previous state
        mov     visible,0
        jmp     tektt5                  ; reparse the bad char
TEKINC  endp


; Routine to trigger the crosshairs, wait for a key to be struck, and send
; the typed char (if printable ascii) plus four Tek encoded x,y position
; coordinates and then a carriage return.
; ax, cx, xcross, ycross operate in PC coordinates.

CROSHAIR PROC NEAR
        push    linepat                 ; save line drawing pattern
        mov     linepat,0ffffh          ; reset line type to solid

        mov     ax,xmax                 ; right margin minus 7 dots
        add     ax,7
        mov     temp,ax                 ; right margin dot
crosha1:call    crosdraw                ; draw the cross-hairs
        call    iseof                   ; is stdin at EOF?
        jc      crosha2                 ; c = yes, exit this mode now
        mov     ah,coninq               ; DOS, quiet read char
        int     dos
        push    ax                      ; save char for later
        call    crosdraw                ; erase cross hairs
        pop     ax
        or      al,al                   ; ascii or scan code returned
        jnz     arrow5                  ; nz = ascii char returned

        call    iseof                   ; is stdin at EOF?
        jc      crosha2                 ; c = yes, exit this mode now
        mov     ah,coninq               ; read scan code
        int     dos
        cmp     al,0                    ; Control-Break?
        jne     crosha3                 ; ne = no, something else
crosha2:pop     linepat                 ; restore line pattern
        ret                             ; exit crosshairs mode

crosha3:cmp     al,homscn               ; is it 'home'?
        jne     arrow1                  ; ne = no, try other keys
        mov     ax,temp                 ; right margin
        shr     ax,1                    ; central position
        mov     xcross,ax               ; save PC coord for crosshair
        mov     ax,ybot                 ; last scan line
        shr     ax,1
        mov     ycross,ax               ; this is the center of the screen
        jmp     crosha1                 ; home the crosshairs

arrow1: cmp     al,lftarr               ; left arrow?
        jne     arrow2                  ; ne = no
        mov     cx,-1                   ; left shift
        jmp     short xkeys
arrow2: cmp     al,rgtarr               ; right arrow?
        jne     arrow3                  ; ne = no
        mov     cx,1                    ; right shift
        jmp     short xkeys
arrow3: cmp     al,uparr                ; up arrow?
        jne     arrow4                  ; ne = no
        mov     cx,-1                   ; up shift
        jmp     short vertkey
arrow4: cmp     al,dnarr                ; down arrow?
        jne     badkey                  ; ne = no, ignore it
        mov     cx,1                    ; down shift
        jmp     short vertkey

badkey: call    beep                    ; tell user we don't understand
        jmp     crosha1                 ; keep going

                                        ; Shifted keys yield ascii keycodes
arrow5: cmp     al,'C' and 1fh          ; Control-C?
        je      crosha2                 ; e = yes, exit crosshairs mode now
        cmp     al,shlftarr             ; shifted left arrow?
        jne     arrow6                  ; ne = no
        mov     cx,-10                  ; big left shift
        jmp     short xkeys
arrow6: cmp     al,shrgtarr             ; shifted right arrow?
        jne     arrow7                  ; ne = no
        mov     cx,10                   ; big right shift
        jmp     short xkeys
arrow7: cmp     al,shuparr              ; shifted up arrow?
        jne     arrow8                  ; ne = no
        mov     cx,-10                  ; big up shift
        jmp     short vertkey
arrow8: cmp     al,shdnarr              ; shifted down arrow?
        jne     charkey                 ; ne = no, send this key as is
        mov     cx,10                   ; big down shift
        jmp     short vertkey

xkeys:  add     cx,xcross               ; add increment
        jns     noxc                    ; gone too far negative?
        mov     cx,0                    ; yes - then make it 0
noxc:   cmp     cx,temp                 ; too far right?
        jb      xdraw9                  ; b = no
        mov     cx,temp                 ; yes - then make it the right
xdraw9: mov     xcross,cx               ; new x value for cross hairs
        jmp     crosha1                 ; and redraw

vertkey:add     cx,ycross               ; adjust cx
        jns     noyc                    ; gone negative?
        mov     cx,0                    ; yes then make 0
noyc:   cmp     cx,ybot                 ; too high?
        jb      yok
        mov     cx,ybot                 ; make it maximum
yok:    mov     ycross,cx               ; save new y crosshair
        jmp     crosha1                 ; and redraw

charkey:call    clrbuf                  ; purge received data to date
        call    outmodem                ; send the break character
        mov     ax,xcross               ; set beam to xcross,ycross
        mov     bx,ycross               ; must convert to Tek coordinates
        call    pctotek                 ; scale from PC screen coord to Tek
        push    ax                      ; save around drawing
        push    bx
        mov     cx,0                    ; just a move
        call    tekdraw                 ; moveto ax,bx in Tek coord
        pop     bx                      ; recover Tek y
        pop     ax                      ; recover Tek x
        call    sendpos                 ; send position report to host
        pop     linepat                 ; recover current line drawing pattern
        mov     ttstate,offset tektxt   ; Go to TEKTXT next time
        mov     lastc,0                 ; clear last drawing coordinate flag
        or      status,txtmode          ; set text mode in status byte
        ret
CROSHAIR ENDP

; CROSDRAW draws cross-hairs by XORing cross with picture.
; xcross and ycross are in PC coordinates.
CROSDRAW PROC   NEAR
        mov     si,xcross               ; move to (xcross, ycross-10)
        mov     di,ycross
        sub     di,10                   ; half the size of the cross
        jns     crosd1                  ; no sign bit means ok
        mov     di,0                    ; else limit to start of screen
crosd1: mov     ax,si                   ; next, draw to (xcross, ycross+10)
        mov     bx,ycross               ; make bottom stroke
        add     bx,10
        cmp     bx,ybot                 ; too large?
        jbe     crosd2                  ; be = no
        mov     bx,ybot                 ; vertical line to (xcross,ybot)
crosd2: mov     cx,0ffh                 ; invert pixels
        call    line                    ; and draw vertical
        sub     si,12                   ; move to (xcross-12, ycross)
        jns     crosd3                  ; no sign means ok
        mov     si,0                    ; else limit to start of line
crosd3: mov     di,ycross
        mov     bx,di
        mov     ax,xcross               ; draw to (xcross+12, ycross)
        add     ax,12
        cmp     ax,temp                 ; temp is right margin, too large?
        jbe     crosd4                  ; be = no, ok
        mov     ax,temp                 ; max x value
crosd4: mov     cx,0ffh                 ; set XOR code
        call    line                    ; draw to (xcross+12, ycross)
        ret
CROSDRAW ENDP

; SENDPOS sends position of cross-hairs to the host.
; ax has Tek X and bx has Tek Y coord of center of crosshair
SENDPOS PROC NEAR
        push    bx                      ; preserve register
        call    sendxy                  ; send x coord
        pop     ax
        call    sendxy                  ; send y coord
        mov     al,cr                   ; follow up with cr
        call    outmodem
        ret
SENDPOS ENDP

; SENDXY sends value of ax as Tek encoded bytes
; ax is in Tek coordinates
SENDXY  PROC    NEAR
        shl     ax,1
        shl     ax,1                    ; move all but lower 5 bits to ah
        shl     ax,1
        shr     al,1
        shr     al,1                    ; move low five bits to low 5 bits
        shr     al,1
        or      ah,20h                  ; make it a printing char as per TEK
        xchg    al,ah                   ; send high 5 bits first
        call    outmodem
        xchg    al,ah                   ; then low five bits
        or      al,20h
        call    outmodem
        xchg    ah,al                   ; al is first sent byte
        ret
SENDXY  ENDP


SENDID  PROC NEAR                       ; Pretend VT100 with graphics option
        mov     bx,IDSEQ                ; Get addr of string
sndid1: mov     al,[bx]                 ; Get char from sequence
        cmp     al,0                    ; End of sequence?
        jz      sndid0                  ; Yes, return
        call    OUTMODEM                ; Send it out the port
        inc     bx
        jmp     sndid1
sndid0: ret
SENDID  ENDP

; SENDSTAT - send status and cursor position to host

SENDSTAT PROC NEAR
        mov     al,STATUS               ; get tek status
        or      al,20h                  ; make it printable
        call    OUTMODEM                ; and send it
        mov     ax,oldx                 ; now send x coordinate (oldx is Tek)
        call    SENDXY
        mov     ax,oldy                 ; and y coordinate (oldy is Tek coord)
        call    SENDXY
        mov     al,cr                   ; end with a cr
        call    OUTMODEM
        ret
SENDSTAT ENDP

; routine to send al to the modem port

OUTMODEM PROC   NEAR
        push    ax
        mov     ah,al
        call    outchr                  ; outchr reads from ah
         nop                            ; ignore errors
         nop
         nop
        pop     ax
        ret
OUTMODEM ENDP

; Convert X and Y from PC coordinates to Tek coordinates. AX = X, BX = Y
; for both input and output.
pctotek proc    near
        mul     xdiv                    ; scale from PC screen coord to Tek
        div     xmult
        xchg    bx,ax                   ; save Tek x coord in bx
        neg     ax                      ; y axis. Turn upside down for Tek
        add     ax,ybot
        mul     ydiv                    ; scale y from PC screen coord to Tek
        div     ymult
        xchg    ax,bx                   ; ax has X, bx has Y in Tek coords
        ret
pctotek endp

; Routine to output character in AL to the screen.

OUTSCRN PROC NEAR                       ; Output one character to the screen
        cmp     bypass,0                ; GIN mode bypass off?
        je      outscp                  ; e = yes
        ret                             ;  else ignore characters
outscp:                                 ; Set Translation Input filter
        cmp     rxtable+256,0           ; translation turned off?
        je      outsct                  ; e = yes, no translation
        push    bx
        mov     bx,offset rxtable       ; address of translate table
        xlatb                           ; new char is in al
        and     al,7fh                  ; retain only lower seven bits
        pop     bx
outsct: mov     si,ybot                 ; get last scan line
        inc     si                      ; number of scan lines
        sub     si,y_coord              ; minus where char bottom needs to go
        jnc     outscc                  ; nc = enough space for char
                                        ; else give "More >" message
        push    ax                      ; save current char
        push    cx
        mov     cx,mormsglen            ; characters in More message
        mov     ax,cx
        shl     ax,1
        shl     ax,1
        shl     ax,1                    ; times 8 bits/character
        neg     ax                      ; (note: leave last char cell empty)
        add     ax,xmax                 ; right justify
        mov     x_coord,ax              ; set starting x dot
        mov     ax,ybot
        mov     y_coord,ax              ; set starting y line
        mov     ccode,1                 ; write in foreground colors
        push    cx
        mov     al,DEL                  ; fill all pixels
outscm1:call    putc                    ; write
        loop    outscm1
        pop     cx
        push    cx
        mov     al,BS                   ; backup to overwrite with More text
outscm2:call    putc
        loop    outscm2
        pop     cx
        mov     ccode,0                 ; main text in background colors
        mov     si,offset moremsg       ; give More message
outsclf:cld
        lodsb                           ; read a byte from string
        call    putc                    ; display the string
        loop    outsclf                 ; repeat for all string chars
        pop     cx
        mov     ccode,1                 ; restore normal foreground coloring
        call    iseof                   ; EOF on redirected stdin?
        jc      outscl3                 ; c = yes, proceed anyway
        mov     ah,coninq               ; read keyboad via DOS
        int     dos                     ; wait for keystroke
        or      al,al                   ; scan code being returned?
        jne     outscl3                 ; ne = no
        mov     ah,coninq               ; clear away scan code too
        int     dos
outscl3:call    tekcls                  ; clear the screen
        pop     ax                      ; recover current character
        cmp     al,lf                   ; just a line feed?
        jne     outscc                  ; ne = no, display it
        ret                             ;  else ignore the line feed

outscc: push    ax
        mov     ax,xmax
        cmp     x_coord,ax              ; beyond right margin?
        jbe     outsc3                  ; be = no
        mov     al,cr                   ; else simulate cr/lf
        call    putc                    ; before displaying current char
        mov     al,lf
        call    putc
outsc3: pop     ax
        call    putc                    ; routine to draw characters
        ret
OUTSCRN ENDP


; TEKCLS routine to clear the screen.
; Entry point tekcls1 clears screen without resetting current point.
TEKCLS  PROC    NEAR
        cmp     tekflg,0                ; Tek sub mode active yet?
        jne     tekcls0                 ; ne = yes
        ret                             ; else ignore this call
tekcls0:mov     x_coord,0               ; starting text coordinates
        mov     y_coord,8
        mov     oldx,0                  ; assumed cursor starting location
        mov     oldy,maxteky            ;  top right corner (Tek coord)
        mov     scalex,0                ; clear last plotted point (PC coord)
        mov     scaley,0
        mov     lastc,0                 ; last parsed x,y coordinate
        mov     visible,0               ; make lines invisible
        mov     linepat,0ffffh          ; reset line pattern to solid
        mov     ccode,1                 ; reset to ordinary writing
        mov     bypass,0                ; clear bypass condition
        mov     ttstate,offset tektxt   ; do displayable text
        push    ax
        mov     ax,xmax                 ; right margin minus 7 dots
        add     ax,7                    ; right most dot
        shr     ax,1                    ; central position
        mov     xcross,ax               ; save PC coord for crosshair
        mov     ax,ybot                 ; last scan line
        shr     ax,1
        mov     ycross,ax               ; this is the center of the screen
        pop     ax

tekcls1:push    ax                      ; save registers
        push    cx
        cmp     graph_mode,hercules     ; Hercules?
        jne     tekcls2                 ; ne = no
        call    hgraf                   ; set Hercules board to Graphics mode
        jmp     tekcls7

tekcls2:mov     di,0                    ; point to start of screen, di=row
        call    psetup                  ; setup graphics routine and es:di
        mov     cx,4000h                ; CGA, 200 lines times 80 bytes worth
        cmp     graph_mode,cga          ; cga?
        je      tekcls3                 ; e = yes
        mov     cx,8000h                ; Olivetti, 400 lines times 80 bytes
        cmp     graph_mode,olivetti     ; AT&T-Olivetti?
        je      tekcls3                 ; e = yes
        cmp     graph_mode,toshiba      ; Toshiba?
        je      tekcls3                 ; e = yes
        cmp     graph_mode,vaxmate      ; VAXmate?
        jne     tekcls4                 ; ne = no
tekcls3:cld                             ; clear screen directly of text stuff
        mov     al,0                    ; color is black
        rep     stosb                   ; clear the bytes
        jmp     short tekcls7

tekcls4:cmp     graph_mode,ega          ; EGA?
        je      tekcls5                 ; e = yes
        cmp     graph_mode,monoega      ; EGA with mono display?
        je      tekcls5                 ; e = yes
        cmp     graph_mode,colorega     ; EGA with medium resolution monitor?
        je      tekcls5                 ; e = yes
        jmp     short tekcls6           ; else use Bios

tekcls5:                                ; EGA clear screen quickly
        mov     ax,0ff08h               ; set all 8 bits to be changed
        call    ega_gc                  ; set bit mask register accordingly
        mov     cx,ybot                 ; last scan line
        inc     cx                      ; number of scan lines
        mov     ax,80                   ; bytes per scan line
        mul     cx
        mov     cx,ax                   ; cx = number of bytes to clear
        mov     al,gbcol                ; select background colour
        cld
        rep     stosb                   ; write backgound color
        jmp     short tekcls7

tekcls6:push    es                      ; clear screen by scrolling up
        call    cmblnk                  ; clear screen, for Environments
        pop     es

tekcls7:mov     si,0                    ; starting x  (in case screen is
        mov     di,0                    ; starting y    cleared by user)
        pop     cx
        pop     ax
        ret
TEKCLS  ENDP

; Routine to draw a line on the screen, using TEKTRONIX coordinates.
; X coordinate in AX, 0=left edge of screen, 1023=right edge of screen.
; Y coordinate in BX, 0=bottom of screen, 779=top of screen.
; CL=0 - invisible move, CL=1 - draw a line, CL=0FFh - invert pixels on line

TEKDRAW PROC NEAR
        mov     si,scalex               ; get old x already scaled
        mov     di,scaley               ; get old y already scaled
        call    scale                   ; scale new end point to PC coords
        cmp     cl,0                    ; invisible drawing?
        je      moveto                  ; z = just move, skip draw part
        call    LINE                    ; draw the line
moveto: mov     x_coord,ax              ; update text coordinates to match
        mov     y_coord,bx              ;  last drawn point
        ret
TEKDRAW ENDP

; Scale TEKTRONIX coordinates to the currently defined screen coordinates
; AX holds X axis, BX holds Y axis. Both are changed from Tektronix coord
; to PC coordinates by this procedure.
SCALE   PROC    NEAR
        push    dx
        push    si
        mov     oldx,ax                 ; save current Tek x for next draw
        mov     oldy,bx                 ; save current Tek y for next draw
        mul     xmult                   ; scale x-coord
        mov     si,xdiv                 ; get the divisor
        shr     si,1                    ; halve it
        add     ax,si                   ; add in - to round to nearest integer
        adc     dx,0
        div     xdiv
        push    ax
        mov     ax,bx
        mul     ymult                   ; scale y-coord
        mov     si,ydiv                 ; get divisor
        shr     si,1                    ; halve it
        add     ax,si                   ; add in - to round to nearest integer
        adc     dx,0
        div     ydiv
        mov     bx,ybot
        sub     bx,ax                   ; Put new Y in right reg
        jns     scale3                  ; ns = not too far
        mov     bx,0
scale3: pop     ax                      ; Put new X in right reg
        mov     scalex,ax               ; save scaled values
        mov     scaley,bx
        pop     si
        pop     dx
        ret
SCALE   ENDP

; LINE  Subroutine to plot a line with endpoints in AX,BX and SI,DI.
;       fast line drawing routine for the IBM PC
;
; Registers at CALL
; -----------------
; SI=Start X coord, all in PC coordinates
; DI=Start Y coord
; AX=End X coord
; BX=End Y coord
; CL=Color code: 1=draw foreground, 0=draw background, 0ffh=invert
; BP= line drawing pattern (is changed here by rotation)
; registers are all unchanged

LINE    PROC    NEAR
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    es
        mov     bp,linepat              ; store active line pattern word in BP
        mov     ccode,cl        ; save color code in ccode for use by plot()
                        ; first get coord to achieve increasing x; deltax >= 0
        sub     ax,si                   ; deltax = x2 - x1
        jge     line1                   ; ge = going to the right, as desired
        neg     ax                      ; make deltax non-negative
        sub     si,ax                   ; swap the x coordinates
        xchg    bx,di                   ; swap the y coordinates too
                                ; second, compute deltay. ax = deltax, si = x1
line1:  sub     bx,di                   ; deltay = y2 - y1
        call    psetup                  ; setup display adapter for plotting
                                        ;  and setup es:di to screen memory
  ; Choose algorithm based on |deltay| < |deltax| (use shallow) else steep.
  ; We arrange matters such that both deltas are non-negative.
        cmp     bx,0                    ; deltay
        jge     line2                   ; ge = non-negative
        neg     linelen
        neg     bx                      ; make non-negative
line2:  cmp     bx,ax                   ; |deltay| versus |deltax|
        jbe     shallow                 ; be = do shallow algorithm
        jmp     steep                   ; else do steep algorithm

        ; shallow algorithm, move along x, di=y1, bx=deltay, si=x1, ax=deltax
shallow:add     bx,bx                   ; bx = 2*deltay
        mov     cx,ax                   ; cx = number of steps (deltax here)
        inc     cx                      ; loop dec's cx before testing
        mov     dx,bx                   ; dx holds error
        sub     dx,ax                   ; error = 2*deltay - deltax
        add     ax,ax                   ; ax = 2*|deltax|
shal1:  call    plotptr                 ; Plot(x,y)
        cmp     dx,0
        jle     shal2                   ; le =   error <= 0
        call    pincy                   ; increment y by one scan line
        sub     dx,ax                   ; error = error - 2*deltax
shal2:  add     dx,bx                   ; error = error + 2*deltay
        inc     si                      ; x = next dot right
        loop    shal1
shal3:  jmp     short plotex

        ; steep algorithm, move along y, di=y1, bx=deltay, si=x1, ax=deltax
steep:  add     ax,ax                   ; ax = 2*deltax
        mov     dx,ax                   ; dx holds error
        sub     dx,bx                   ; error = 2*deltax(bx) - deltay (bx)
        mov     cx,bx                   ; cx = number of steps (deltay here)
        inc     cx                      ; loop dec's cx before testing
        add     bx,bx                   ; bx = 2*|deltay|
stee1:  call    plotptr                 ; Plot(x,y) x = ax, y = di
        cmp     dx,0
        jle     stee2                   ; le  error <= 0
        inc     si                      ; x = next dot right
        sub     dx,bx                   ; error = error - 2*deltay
stee2:  add     dx,ax                   ; error = error + 2*deltax
        call    pincy                   ; increment y
        loop    stee1
stee3:;;;jmp    plotex

plotex: mov     ccode,1                 ; reset to do foreground coloring
        pop     es
        pop     di
        pop     si
        pop     dx                      ; restore the world
        pop     cx
        pop     bx
        pop     ax
        ret
LINE    ENDP

;;;;;;; EGA plot support routines
psetupe proc    near                    ; EGA setup for plotting
        push    ax
        mov     linelen,80              ; for y going down screen by pincy
        mov     ax,segscn               ; set es to screen memory segment
        mov     es,ax
        mov     ax,0205h                ; mode: write mode 2
        call    ega_gc
        mov     ax,0003h                ; assume writing bits directly
        cmp     ccode,0ffh              ; inverting bits?
        jne     psete2                  ; ne = no
        mov     ax,1803h                ; then say XOR the bits
psete2: call    ega_gc                  ; set controller
        mov     ax,80                   ; compute starting point in regen buff
        mul     di
        mov     di,ax                   ; di = di * 80
        pop     ax
        ret
psetupe endp

pincye  proc    near                    ; EGA inc y
        add     di,linelen              ; includes sign of deltay
        ret
pincye  endp

pltega  proc    near            ; EGA plot(x,y). x is in si, y is in di
        rol     bp,1                    ; rotate line pattern
        jnc     pltega1                 ; nc = no bit to be plotted
        push    bx
        push    si
        push    di
        mov     bx,si                   ; want si/8 for bytes along line
        shr     si,1
        shr     si,1
        shr     si,1
        add     di,si                   ; starting point in regen buffer
        and     bx,0007h                ; leave lower 3 bits for bit in byte
        mov     bh,masktab[bx]          ; 0-7 into bit mask in byte, x pos
        mov     bl,ccode                ; get line type code
        call    ega_plt
        pop     di
        pop     si
        pop     bx
pltega1:ret
pltega  endp

;;;;;;;; CGA plot support routines
; The CGA graphics memory mapping in mode 6 (640 by 200) is 8 dots per byte,
; left most dot in the high bit, 80 bytes per scan line, scan line segments
; alternating between 0b800h (even lines 0, 2, ...) and 0ba00h (odd lines).
psetupc proc    near                    ; CGA setup for plotting
        push    ax
        push    cx
        mov     linelen,80              ; 80 bytes per scan line
        mov     cx,segscn
        mov     es,cx
        mov     cx,di                   ; save copy of di, start y line
                                        ; compute starting point in regen buff
        shr     di,1                    ; half the lines in each bank
        mov     ax,80                   ; 80 bytes per line
        mul     di
        mov     di,ax                   ; di = di * 80 / 2
        test    cx,1                    ; even or odd line
        jz      psetc1                  ; z = even
        add     di,2000h                ; offset to odd bank (seg 0ba00h)
psetc1: and     di,3fffh
        pop     cx
        pop     ax
        ret
psetupc endp

pincyc  proc    near                    ; CGA inc y
        cmp     linelen,0               ; increasing or decreasing y?
        jl      pinyc2                  ; l = decreasing
        cmp     di,2000h                ; in upper bank now?
        jb      pinyc1                  ; b = no, in lower bank
        add     di,linelen              ; add a line
pinyc1: add     di,2000h                ; switch banks
        and     di,3fffh                ; roll over address
        ret
pinyc2: cmp     di,2000h                ; in upper bank now?
        jae     pinyc4                  ; ae = yes
        add     di,linelen              ; subtract a line
pinyc4: add     di,2000h                ; switch banks
        and     di,3fffh                ; roll over address
        ret
pincyc  endp

pltcga  proc    near            ; CGA plot(x,y). x is in si, y is in di
        push    bx              ; used for HGA plot also.
        push    si
        push    di
        rol     bp,1                    ; rotate line pattern
        jnc     pltcg3                  ; nc = no bit to be plotted
        mov     bx,si                   ; want si/8 for bytes along line
        shr     si,1
        shr     si,1
        shr     si,1
        add     di,si                   ; starting point in regen buffer
        and     bx,0007h                ; leave lower 3 bits for bit in byte
                                        ; di = offset in regen buffer
        mov     bh,masktab[bx]          ; 0-7 into bit mask in byte. x position
        mov     bl,ccode                ; get line type code
        cmp     bl,1                    ; draw the bit?
        jne     pltcg1                  ; ne = no
        or      es:[di],bh              ; drawn
        jmp     short pltcg3
pltcg1: cmp     bl,0                    ; draw in background (erase)?
        jne     pltcg2                  ; ne = no
        not     bh
        and     es:[di],bh              ; erase the dots
        jmp     short pltcg3
pltcg2: xor     es:[di],bh              ; xor in this color
pltcg3: pop     di
        pop     si
        pop     bx
        ret
pltcga  endp

;;;;;;; HGA plot support routines
; The HGA graphics memory mapping in mode 255 (720 by 348) is 8 dots per byte,
; left most dot in the high bit, 90 bytes per scan line, scan line segments
; sequence as 0b000h, 0b200h, 0b400h, 0b800h for lines 0-3 and repeat 90 bytes
; higher for the rest.
psetuph proc    near                    ; HGA setup for plotting
        push    ax
        push    cx
        mov     linelen,90              ; for y going down screen by incy
        mov     ax,segscn               ; base segment of display memory
        mov     es,ax
        mov     cx,di                   ; save copy of di, start y line
                                        ; compute starting point in regen buff
        shr     di,1                    ; quarter the lines in each bank
        shr     di,1
        mov     ax,90
        mul     di
        mov     di,ax                   ; di = di * 90 / 4
        and     cx,3                    ; compute bank from 2 lsb of line num
        jcxz    pseth2                  ; z means it is in bank 0 (0b000h)
pseth1: add     di,2000h                ; add offset for each bank
        loop    pseth1                  ; do cx times
pseth2: pop     cx
        pop     ax
        ret
psetuph endp

pincyh  proc    near                    ; HGA inc y, step offset of line
        cmp     linelen,0               ; increasing y?
        jg      pinyh2                  ; g = yes
        cmp     di,2000h                ; in lowest for four banks?
        ja      pinyh1                  ; a = no
        add     di,linelen              ; yes, add a line
pinyh1: add     di,6000h                ; move back by adding a lot
        and     di,7fffh                ; roll over address
        ret
pinyh2: cmp     di,6000h                ; in top most bank?
        jb      pinyh4                  ; b = no
        add     di,linelen              ; yes, first add a line
pinyh4: add     di,2000h                ; switch to next bank
        and     di,7fffh                ; roll over address
        ret
pincyh  endp

;;;;;;; AT&T-Olivetti, Toshiba, VAXmate Graphics Adapter plot support routines
; The graphics memory mapping in 640 by 400 mode is 8 dots per byte,
; left most dot in the high bit, 80 bytes per scan line, scan line segments
; sequence as 0b800h, 0ba00h, 0bc00h, 0be00h for lines 0-3 and repeat 80 bytes
; higher for the rest. Use Hercules line incrementing (inc y) and CGA dot
; writing. This is a monographic display.
psetupo proc    near                    ; setup for plotting
        push    ax
        push    cx
        mov     linelen,80              ; for y going down screen by incy
        mov     ax,segscn               ; base segment of display memory
        mov     es,ax
        mov     cx,di                   ; save copy of di, start y line
                                        ; compute starting point in regen buff
        shr     di,1                    ; quarter the lines in each bank
        shr     di,1
        mov     ax,80
        mul     di
        mov     di,ax                   ; di = di * 80 / 4
        and     cx,3                    ; compute bank from 2 lsb of line num
        jcxz    pseto2                  ; z means it is in bank 0 (0b800h)
pseto1: add     di,2000h                ; add offset for each bank
        loop    pseto1                  ; do cx times
pseto2: pop     cx
        pop     ax
        ret
psetupo endp

;;;;;;;; Monochrome, simulate dots with text char
psetupm proc    near
        mov     linelen,1               ; 80 characters but one line
        ret
psetupm endp

pltmon  proc    near                    ; Monochrome dot plot
        mov     x_coord,si              ; put dot at row=di, col=si, PC Coord
        mov     y_coord,di
        push    ax
        mov     al,'+'                  ; our dot character
        call    mputc                   ; display text char
        pop     ax
        ret
pltmon  endp

pincym  proc    near                    ; Monochrome inc y
        add     di,linelen              ; includes sign
        ret
pincym  endp

; GPUTC - a routine to send text characters from font to true graphics boards
; such as EGA, Hercules or CGA. Char is in al. Drawing routine ptr is gcplot.

gputc   proc    near
        cmp     al,' '                  ; control character?
        jae     gputc1                  ; ae = no, display the char
        jmp     putctrl                 ; else handle controls at putctrl
gputc1: push    ax                      ; first save some registers
        push    bx
        push    cx
        push    es
        push    di
        mov     bl,al                   ; now BL has char to be displayed
        and     bl,7fh                  ; no high bits allowed here
                                        ; set board mode
        mov     di,y_coord              ; get current y coord (char bottom)
        sub     di,8                    ; start 8 lines higher
        jnc     gputc2                  ; nc = ok
        mov     di,0                    ; move up to first line
        mov     y_coord,8               ; and reset scan line indicator
gputc2: call    psetup          ; enter with di=line number, sets es:di to
                                ; start of line in display buf and
                                ; sets byte-wide plot mode
        mov     ax,x_coord              ; compute regen buffer byte
        shr     ax,1                    ; want x_coord/8 for bytes along line
        shr     ax,1
        shr     ax,1
        add     di,ax                   ; byte in regen buffer
        xor     bh,bh
        sub     bx,32                   ; characters in font start at 32
        shl     bx,1
        shl     bx,1                    ; 8 bytes per char - hence * 8
        shl     bx,1
        mov     cx,8                    ; 8 bytes (scan lines) to transfer
        call    gcplot                  ; call character plot routine
        call    incx                    ; move to next char position
        pop     di
        pop     es
        pop     cx
        pop     bx
        pop     ax
        ret
gputc   endp

putctrl proc    near                    ; CONTROL CHARS = cursor movement
        push    ax                      ; save character
        cmp     al,FF                   ; formfeed?
        jne     putct0                  ; ne = no
        call    TEKCLS                  ; FF clears the screen
        jmp     putctx
putct0: cmp     al,BS                   ; BS? sends (logical) cursor back one
        jne     putct2                  ; ne = no, try next
        mov     ax,x_coord
        sub     ax,8                    ; so delete 8 dots (move left)
        jns     putct1                  ; ns = non-negative
        mov     ax,0                    ; but not less than 0
putct1: mov     x_coord,ax              ; and replace x coordinate
        mov     al,' '                  ; send a space
        call    putc
        sub     x_coord,8               ; restore cursor
        jmp     putctx
putct2: cmp     al,tab                  ; tabs move forward one char position
        jne     putct4                  ; ne = not a tab
        call    incx                    ; let incx move cursor right one col
        jmp     putctx
putct3: mov     x_coord,ax
        jmp     putctx
putct4: cmp     al,cr                   ; <CR> means go to beginning of line
        jne     putct5
        mov     x_coord,0               ; zero the x coordinate
        jmp     putctx
putct5: cmp     al,lf                   ; <LF> means go down 8 pixels (1 line)
        jne     putct7                  ; ne = not LF
        add     y_coord,8               ; border managed by outscrn and incx
        jmp     putctx
putct7: cmp     al,vt                   ; <VT> move up screen 1 line (8 pixels)
        jne     putctx
        sub     y_coord,8               ; subtract one line (8 pixels)
        jnc     putctx                  ; nc = space left
        mov     y_coord,8               ; else set to top of screen
putctx: pop     ax
        ret
putctrl endp

mputc   proc    near                    ; MONO put char in AL via Bios
        push    ax                      ; updates x_coord,y_coord with
        push    bx                      ; new cursor position
        push    cx
        push    dx
        mov     ah,0                    ; marker for cursor setting not needed
        cmp     al,' '                  ; control code?
        jae     mputc1                  ; ae = no, printable
        call    putctrl                 ; do cursor arithmetic
        mov     ah,1                    ; marker to set cursor but no display

mputc1: push    ax                      ; save char and marker
        mov     cl,3                    ; char cell is 8 x 8 dots
        mov     ax,x_coord              ; get resulting cursor PC positions
        shr     ax,cl
        mov     dl,al                   ; column
        mov     ax,y_coord
        sub     ax,8                    ; minus 8 dots, like other modes
        jnc     mputc2                  ; nc = non-negative
        mov     ax,0                    ; else start at the top
        mov     y_coord,8               ; here too
mputc2: shr     ax,cl
        mov     dh,al                   ; row
        mov     ah,2                    ; set cursor to x_coord,y_coord
        mov     bh,0                    ; page 0
        int     screen
        pop     ax
        cmp     ah,0                    ; write a char in al?
        jne     mputcx                  ; ne = no
        mov     ah,09h                  ; write char at cursor postion
        mov     cx,1                    ; just one char
        mov     bh,0                    ; page 0
        mov     bl,gfcol                ; foreground coloring
        int     screen
        inc     dl                      ; next column
        mov     ah,2                    ; set real cursor ahead of last char
        int     screen
        call    incx                    ; move logical cursor
mputcx: pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
mputc   endp

incx    proc    near                    ; move the logical cursor right
        mov     ax,x_coord              ; shift the (logical) cursor right
        add     ax,8                    ;  one character cell
        mov     x_coord,ax
        cmp     ax,xmax                 ; at end of the line?
        jbe     incx1                   ; b = no
        mov     x_coord,0               ; wrap to next line
        add     y_coord,8               ; next row
        mov     ax,ybot                 ; last scan line
        cmp     ax,y_coord              ; below bottom line?
        jge     incx1                   ; ge = no
        mov     y_coord,ax              ; set to bottom row
        mov     al,lf                   ; simulate a line feed operation
        call    outscrn                 ; invoke More message
incx1:  ret
incx    endp

; EGA Character plot routine. Enter with bx pointing at font array for char
; cx = number of bytes in char font, es:di = screen memory. Worker for gputc.
; ccode: 0=plot in background colors, 1=foreground, 0ffh=xor with screen
gcega   proc    near
gcega1: mov     al,font[bx]             ; EGA byte plot: get bits from font
        push    bx
;;;;;   mov     bh,0ffh                 ; write these bits to clear field
;;;;;   mov     bl,0                    ; in background coloring
;;;;;   call    ega_plt                 ; plot a byte
        mov     bh,al                   ; set bit pattern of character
        mov     bl,ccode                ; plot in back/fore/xor (ccode) colors
        call    ega_plt                 ; byte plot routine for EGA systems
        pop     bx
        inc     bx                      ; next byte of char pattern
        call    pincy                   ; next scan line (linelen is preset)
        loop    gcega1
        ret
gcega   endp

; General Character plot routine. Enter with bx pointing at font array for
; char, cx = number of bytes in char font, es:di = screen memory.
; Worker for gputc.

gcgen   proc    near
gcgen1: mov     al,font[bx]             ; Non-EGA systems: get bits from font
        cmp     ccode,1                 ; write in foreground?
        je      gcgen2                  ; e = yes
        xor     es:[di],al              ; background or xor (same)
        jmp     short gcgen3
;;;;    mov     es:[di],al              ; write desired pattern (no overwrite)
gcgen2: OR      es:[di],al              ; write desired pattern (no overwrite)
gcgen3: inc     bx                      ; point to next byte of char pattern
        call    pincy                   ; next scan line (linelen is preset)
        loop    gcgen1                  ; and repeat until complete
        ret
gcgen   endp

; routines to manipulate ega graphics controller and mode register
; command code in al, value in ah - destroys al and dx

ega_gc  proc    near                    ; ega graphics controller
        mov     dx,3ceh
        out     dx,al                   ; output command code
        inc     dx                      ; dx is now data port
        mov     al,ah                   ; get value to al
        out     dx,al                   ; output value
        ret
ega_gc  endp
ega_md  proc    near                    ; ega mode controller
        mov     dx,3c4h
        out     dx,al                   ; output command code
        inc     dx                      ; dx is now data port
        mov     al,ah                   ; get value to al
        out     dx,al                   ; output value
        ret
ega_md endp

; Plot eight pixels using an EGA board
; Enter with ES:[DI] pointing to screen address of byte,
; bh has pattern of bits to be set, bl has attributes:
;  0 = draw in background color, 1 = draw in foreground color,
;  0ffh = XOR with current dot colors.
; registers preserved

ega_plt proc    near
        push    ax
        push    dx
        mov     al,8                    ; command to set bit mask register
        mov     ah,bh                   ; get bits to be modified (1)
        call    ega_gc                  ; unprotect those bit positions
        mov     ah,gfcol                ; get foreground colour
        cmp     bl,1                    ; draw in foreground?
        je      ega2                    ; ne = no
        mov     ah,gbcol                ; get grahics background colour
        cmp     bl,0ffh                 ; do an XOR?
        jne     ega2                    ; ne = no
        mov     ah,0ffh                 ; XOR, touch all color bits
ega2:   mov     al,es:[di]              ; latch byte
        mov     es:[di],ah              ; set the byte
        pop     dx
        pop     ax
        ret
ega_plt endp

; routine to set Hercules card to graphics mode - both pages are enabled

HGRAF   PROC    NEAR
        push    ax
        push    bx                      ; save used registers
        push    cx
        push    si
        mov     al,grph                 ; graph mode
        lea     si,gtable               ;  requires graphics table
        mov     bx,0
        mov     cx,4000h                ; clear 4000h words
        call    setmd                   ; and set the mode
        pop     si
        pop     cx
        pop     bx
        pop     ax
        ret
HGRAF   ENDP

; set Hercules card to text mode

HTEXT   PROC    NEAR
        push    ax
        push    bx
        push    cx
        push    si
        mov     al,text                 ; text mode
        lea     si,ttable               ; requires text table
        mov     bx,0720h                ; blank value (space, white on black)
        mov     cx,2000                 ; whole screen to clear (80*25)
        call    setmd                   ; set the mode
        pop     si
        pop     cx
        pop     bx
        pop     ax
        ret
HTEXT   ENDP

; Hercules mode set - called from HTEXT and HGRAF

SETMD   PROC    NEAR
        push    dx
        push    ax
        mov     dx,config               ; configuration port
        mov     al,genable              ; allow graphics mode to be set
        out     dx,al
        pop     ax
        push    ax
        push    cx                      ; save count
        mov     dx,cntrl                ; control port
        out     dx,al                   ; set to text or graphics
        mov     dx,index                ; send 12 bytes from table to 6845
        mov     cx,12                   ; number of registers to load
        xor     ah,ah                   ; start with register 0 of 6845
        cld
setmd1: jmp     $+2                     ; small pause for hardware
        mov     al,ah                   ; ah is counter
        out     dx,al                   ; set register
        inc     dx                      ; point to data port
        lodsb                           ; get next byte in table
        jmp     $+2                     ; small pause for hardware
        out     dx,al                   ; and send to 6845
        inc     ah                      ; next register
        dec     dx                      ; point to register port
        loop    setmd1                  ; and continue 'til cx=0
        pop     cx                      ; recover count
        cld
        push    di
        push    es
        mov     ax,segscn               ; start of screen
        mov     es,ax
        xor     di,di
        mov     ax,bx                   ; get blanking character
        rep     stosw                   ; store blanking char in whole screen
        pop     es
        pop     di
        mov     dx,cntrl                ; now to re-enable screen
        pop     ax                      ; get mode
        or      al,scrn_on              ; enable screen
        out     dx,al
        pop     dx
        ret
SETMD   ENDP

teksave proc    near            ; saves graphics screen from page 0 to page 1
        push    si
        push    di
        cmp     gpage,0         ; only graphics page 0 on display board?
        je      teksavx         ; e = yes, no saving possible here
        mov     si,segscn       ; segment (!) of current screen
        cmp     graph_mode,ega
        je      teksav1
        cmp     graph_mode,monoega
        je      teksav1
        cmp     graph_mode,colorega
        je      teksav1
        cmp     graph_mode,hercules
        je      teksav2
        jmp     short teksavx   ; else nothing
teksav1:mov     di,segega+800h  ; EGA page 1 screen segment
        call    egasr           ; call common save/restore code
        jmp     short teksavx
teksav2:mov     di,seghga+800h  ; Hercules page 1 screen segment
        call    hgasr           ; call common save/restore code
teksavx:pop     di
        pop     si
        ret
teksave endp

tekrest proc    near            ; saves graphics screen of page 0 in page 1
        push    si
        push    di
        cmp     gpage,0         ; only graphics page 0 on display board?
        jne     tekres0         ; ne = no, more so work to do here
        call    tekcls1         ;  else clear the screen to color it
        jmp     short tekresx   ;  and exit
tekres0:mov     di,segscn       ; segment (!) of new graphics screen
        cmp     graph_mode,ega
        je      tekres1
        cmp     graph_mode,monoega
        je      tekres1
        cmp     graph_mode,colorega
        je      tekres1
        cmp     graph_mode,hercules
        je      tekres2
        jmp     short tekresx   ; else nothing
tekres1:mov     si,segega+800h  ; segment of EGA page 1
        call    egasr           ; call common save/restore code
        jmp     short tekresx
tekres2:mov     si,seghga+800h  ; segment of Hercules page 1
        call    hgasr           ; call common save/restore code
tekresx:pop     di
        pop     si
        ret
tekrest endp

egasr   proc    near            ; common code for Tek ega save/restore ops
        push    ax
        push    cx
        push    dx
        mov     ax,0f00h        ; enable 4 plane set/resets
        call    ega_gc          ; set controller
        mov     ax,0f01h        ; enable Set/Reset register
        call    ega_gc
        mov     ax,0f02h        ; set color compare register for 4 planes
        call    ega_gc
        mov     ax,0905h        ; set mode reg: write latches, read mode
        call    ega_gc
        mov     ax,0ff02h       ; enable all planes
        call    ega_md
        mov     cx,ybot         ; last scan line
        inc     cx              ; number of scan lines
        mov     ax,80           ; bytes per scan line
        mul     cx
        mov     cx,ax
        push    es              ; save es
        push    ds              ; save ds
        mov     es,di           ; destination, set es to video memory
        mov     ds,si           ; source, set ds to video memory
        xor     si,si           ; clear offset fields
        xor     di,di
        cld
        rep     movsb           ; copy from page [si] to page [di]
        pop     ds              ; recover ds
        pop     es              ; and other registers
        mov     ax,0000h        ; disable 4 plane set/resets
        call    ega_gc          ; set controller
        mov     ax,0001h        ; disable Set/Reset register
        call    ega_gc          ; set controller
        mov     ax,0002h        ; disable color compare register
        call    ega_gc
        mov     ax,1005h        ; set mode reg: write latches, odd/even
        call    ega_gc
        pop     dx
        pop     cx
        pop     ax
        ret
egasr   endp

hgasr   proc    near            ; Hercules save restore screen
        push    cx
        mov     cx,4000h        ; number of words to move
        push    es              ; save es
        push    ds              ; save ds
        mov     es,di           ; destination, set es to video memory
        mov     ds,si           ; source, set ds to video memory
        xor     si,si           ; clear offset fields
        xor     di,di
        cld
        rep     movsw           ; copy from page [si] to page [di]
        pop     ds              ; recover ds
        pop     es              ; and other registers
        pop     cx
        ret
hgasr   endp
code    ends
        end
