        Name msgibm
; File MSGIBM.ASM
; Tektronix emulator for use with MS Kermit/IBM.
; Edit history:
; Last edit 1 Jan 1988
; 1 Jan 1988 version 2.30
; 31 Dec 1987 change name from msvibm to msgibm for final release. [jrd]
; 29 Dec 1987 Add ESC [ ? 3 8 l  as exit Tek mode command, from VT340's.[jrd]
; 26 Dec 1987 Add test to absorb echo of crosshairs report. [jrd]
; 22 Dec 1987 Revise parsing rules to make an escape sequence be a temporary
;  interruption to the current command (except Clear Screen seq). [jrd]
; Add Control-C and Control-Break as non-reporting exits from GIN mode. [jrd]
; 21 Dec 1987 Add AT&T 6300, Olivetti M24 presence tests and run code. [jrd]
; 16 Dec 1987 Correct screen coloring for 64KB mono/med res color egas. [jrd]
; 4 Dec 1987 Add quirks for Environments, such as TopView, Windows. [jrd]
; 3 Dec 1987 Let 128KB EGA boards save screens. [jrd]
; 30 Nov 1987 Add relative plotting, thanks to help from Bob Parks. [jrd]
; 24 Nov 1987 Add dashed line patterns. [jrd]
; 21 Nov 1987 Add full color background. [jrd]
; 15 Nov 1987 Do screen clears manually because a Bios mode-set keeps
;  interrupts off long enough to miss serial port characters. Make crosshairs
;  smaller. [jrd]
; 8 Nov 1987 Modularize line drawing using Bresneham's algorithm, use pointers
;  to action routines for different board types. Add screen save/restore.
;  Do display board presence tests. Add FS as point plot introducer. Allow
;  for virtual screens when operating under Environments (Windows, etc). [jrd]
; 1 Nov 1987 Heavy rewrite to integrate code into regular MS Kermit/IBM
;  material. [jrd]
;==============================================================================
; Original version for TI Pro computers by
; 12-Dec-84  Joe Smith, CSM Computing Center, Golden CO 80401
; Converted to IBM PCs by Brian Holley, Cambridge Univ.
; Upgraded and integrated into MS Kermit 2.30 by Joe Doupnik, Utah State Univ.
;
;                  Description of Tektronix commands
;
; ESCAPE-CONTROL-E (ENQ) requests a status report
; ESCAPE-FORMFEED erases the screen.
; ESCAPE-CONTROL-Z turns on the crosshairs (not on 4006 or 4025)
; ESCAPE [ ? 3 8 l  exits Tek mode and returns to host text terminal type
;  (VT102 if none defined yet). This is an extension from DEC VT340's.
; CONTROL-] (GS) turns on plot mode, the first move will be with beam off.
; CONTROL-^ (RS) turns on incremental plot mode. RS space means move pen up
;  RS P means move pen down, following letters:A, E, D, F, B, J, H, I mean
;  move right, right and up, up, left and up, left, left and down, down, and
;  right and down, respectively. Ex: RS <space> J J J  means move three Tek
;  positions left and down with the pen up (invisibly).
; CONTROL-UNDERLINE (US) turns off plot mode, as does CR (for all but 4025).
; CONTROL-X switches from TEKTRONIX sub mode to NORMAL alpha mode but is
;  ignored if we are emulating a full Tek terminal rather than a sub mode
;  of DEC or Heath.
; FF erases screen.
; ESCAPE letter, where letter is accent grave (`), a-e sets the line drawing
;   pattern until reset to solid lines (same as escape accent) by command or
;   a terminal reset.
; where
;       ENQ = Control E
;       ESC = Control [ (left square bracket)
;       FF = Control L
;       FS = Control \ (backslash)
;       GS = Control ] (right square bracket)
;       RS = Control ^ (caret)
;       US = Control _ (underscore)
;
; The plot commands are characters which specify the absolute position to move
; the beam.  All moves except the one immediately after the GS character
; (Control-]) are with a visible trace.
;
; For 4010-like devices - The positions are from 0 to 1023 for both X and Y,
; although only 0 to 780 are visible for Y due to screen geometry.  The screen
; is 10.23 by 7.80 inches, and coordinates are sent as 1 to 4 characters.
;
; For 4014-like devices - The positions are from 0 to 4096, but each movement
; is a multiple of 4 positions unless the high-resolution LSBXY are sent.  This
; makes it compatible with the 4010 in that a full sized plot fills the screen.
;
; HIX,HIY = High-order 5 bits of position
; LOX,LOY = Middle-order 5 bits of position
; LSBXY   = Low-order 2 bits of X + low-order 2 bits of Y (4014 mode)
;
; Hi Y    Lo Y    Hi X    LSBXY   Characters sent (Lo-X always sent)
; ----    ----    ----    -----   ----------------------------------
; Same    Same    Same    Same                           Lo-X
; Same    Same    Same    Diff          LSB, Lo-Y,       Lo-X   4014
; Same    Same    Diff    Same               Lo-Y, Hi-X, Lo-X
; Same    Same    Diff    Diff          LSB, Lo-Y, Hi-X, Lo-X   4014
; Same    Diff    Same    Same               Lo-Y,       Lo-X
; Same    Diff    Same    Diff          LSB, Lo-Y,       Lo-X   4014
; Same    Diff    Diff    Same               Lo-Y, Hi-X, Lo-X
; Same    Diff    Diff    Diff          LSB, Lo-Y, Hi-X, Lo-X   4014
; Diff    Same    Same    Same    Hi-Y,                  Lo-X
; Diff    Same    Same    Diff    Hi-Y, LSB, Lo-Y,       Lo-X   4014
; Diff    Same    Diff    Same    Hi-Y,      Lo-Y, Hi-X, Lo-X
; Diff    Same    Diff    Diff    Hi-Y, LSB, Lo-Y, Hi-X, Lo-X   4014
; Diff    Diff    Same    Same    Hi-Y,      Lo-Y,       Lo-X
; Diff    Diff    Same    Diff    Hi-Y, LSB, Lo-Y,       Lo-X   4014
; Diff    Diff    Diff    Same    Hi-y,      Lo-Y, Hi-X, Lo-X
; Diff    Diff    Diff    Diff    Hi-y, LSB, Lo-Y, Hi-X, Lo-X   4014
; Offset for byte:                20h   60h  60h   20h   40h
;
; Note that LO-Y must be sent if HI-X has changed so that the TEKTRONIX knows
; the HI-X byte (in the range of 20h-3fh) is HI-X and not HI-Y.  LO-Y must
; also be sent if LSBXY has changed, so that the 4010 will ignore LSBXY and
; accept LO-Y.  The LSBXY byte is 60h + MARGIN*10h + LSBY*4 + LSBX. (MARGIN=0)
;
;==============================================================================
;
; External variable tekflg and calls to tekini, tekemu, tekesc, tekcls:
; Byte TEKFLG is non-zero when the Tek emulator is active; it is set by the
; startup code in tekini and is maintained in this file. Internal variable
; inited remembers if we have a graphics screen saved, etc.
; TEKINI must be called when entering the emulator to establish the graphics
; screen mode and to calculate the screen dimensions.
; TEKESC is called from say mszibm.asm to invoke Tek emulation when the
; external procedures have detected an Escape Control-L sequence. An implicit
; initialization is done if necessary.
; TEKEMU is the normal entry point to pass a received character to the emulator.
; It too will do an implicit initialization, if required.
; TEKCLS clears the graphics screen, but only if the emulator is active.
; The emulator remains active during Connect mode Help, Status, and other
; interrupts which do not change the terminal type.
; =============================================================================
;
; - ctrl-g (BEL) from line gives beep in tek mode
; - characters better placed in relation to current beam position
;       in EGA and Hercules modes
;   characters OR-ed into place rather than overwriting
; - add SET TERMINAL GRAPHICS NONE -    this ignores the start-up ESC-FF
;                                       for Tektronix
; - add MonoEGA mode for monochrome graphics on an EGA
;
; version 2.29/t3
;  changes - stop control characters from echoing to screen in TEKEMU
;          - show version on name in MSSDEF.H
; version 2.29b
; changes with this version:  - faster line drawing, using direct write to
;                               memory rather than BIOS calls
;                             - support for Hercules, Olivetti, EGA
;                             - minor bug fixes

; adapted to IBM PC June 1987 by        Brian Holley,
;                                       Faculty of Economics and Politics
;                                       University of Cambridge, England
;                                       Email: BJH6@UK.AC.CAM.PHX

        public  tekemu,tekini,tekend    ; Terminal emulation routines
        public  tekcls, tekesc, tekflg  ; used by msz file

        include mssdef.h

ENQ     equ     05h                     ; ^E ENQ for TEK enquiries
CAN     equ     18h                     ; ^X to return to ANSI mode
ESCZ    equ     1Ah                     ; SUB, ESC-^Z triggers crosshairs
VT      equ     0bh                     ; ^K go up one line
FS      equ     1ch                     ; ^\ for point plot mode
GS      equ     1Dh                     ; ^] draw line (1st move is invisible)
RS      equ     1Eh                     ; ^^ for incremental line plot mode
US      equ     1Fh                     ; ^_ (underscore) returns to text mode
accent  equ     60h                     ; accent grave

txtmode equ     4                       ; text mode for TEKTRONIX status
maxtekx equ     1024                    ; horizontal and
maxteky equ     780                     ; vertical resolution of TEK 4010

screen  equ     10h                     ; bios screen call

uparr   equ     72                      ; DOS scan codes for arrow keys
dnarr   equ     80
lftarr  equ     75
rgtarr  equ     77
homscn  equ     71                      ; DOS home screen scan code
shuparr equ     '8'                     ; ascii codes for shifted arrows
shdnarr equ     '2'
shlftarr equ    '4'
shrgtarr equ    '6'

                                        ; Graph_mode for different systems:
cga     equ     6                       ; highest resolution mode for CGA
mono    equ     7                       ; real monochrome display adapter
colorega equ    14                      ; Low-res mode, color EGA
monoega equ     15                      ; mono ega needs mode 15
ega     equ     16                      ; Hi-res mode - EGA
olivetti equ    72                      ; Olivetti's Hi-res - 50 lines text
hercules equ    255                     ; pseudo mode for Hercules graphics
; Note: IBM VGA mode 18, 640 by 480, can be used by setting "ega" above
; to 18 and modify ybot to be 479 and ymult to be 48 at label tekin5.
; The code will scale everything appropriately for the new screen size, but
; there will be insufficient memory to retain the entire graphics image.
; Do this at your own risk, please, until PS/2 Tech Refs are available. [jrd]

segega  equ     0a000h                  ; segments of display memory, EGA
segcga  equ     0b800h                  ; CGA, AT&T/Olivetti
seghga  equ     0b000h                  ; HGA
segmono equ     0b000h                  ; Monochrome

                                        ; Hercules equates:
index   equ     03b4h                   ; 6845 index register
cntrl   equ     03b8h                   ; Display mode control port
hstatus equ     03bah                   ; status port
scrn_on equ     8                       ; bit to turn screen on
grph    equ     2                       ; graphics mode
text    equ     20h                     ; text mode
config  equ     03bfh                   ; configuration port
genable equ     1                       ; enable graphics, page 0 only

hiy     equ     1                       ; codes for Tek graphics components
loy     equ     2
hix     equ     4
lox     equ     3

datas   segment public 'datas'
        extrn   flags:byte, portval:word, rxtable:byte, vtemu:byte
        extrn   tv_mode:byte

xmult   dw      ?                       ; scaling factor for x is
xdiv    dw      ?                       ;     xmult/xdiv
ymult   dw      ?                       ; scaling factor for y is
ydiv    dw      ?                       ;     ymult/ydiv
xmax    dw      ?                       ;
ybot    dw      ?                       ;

; required for Hercules screen handling

gtable  db      35h,2dh,2eh,07h         ; bytes for 6845 controller
        db      5bh,02h,57h,57h         ; - graphics mode
        db      02h,03h,0,0

ttable  db      61h,50h,52h,0fh         ; bytes for 6845 controller
        db      19h,06h,19h,19h         ; - text mode
        db      02h,0dh,0bh,0ch

attlogo db      'OLIVETTI'              ; Olivetti M24, AT&T 6300 rom id
attlen  equ     $-attlogo               ; length

ttstate dw      tektxt                  ; state machine control pointer
prestate dw     0                       ; previous state, across interruptions
visible db      0                       ; 0 to move, 1 to draw a line
tek_hiy dw      0                       ; Y coordinate in Tektronix mode
tek_loy db      0
tek_hix dw      0                       ; X coordinate in Tektronix mode
tek_lox db      0
tek_lsb db      0                       ; Low-order 2 bits of X + low Y
                                        ;       (4014 mode)
status  db      0
lastc   db      0                       ; last x/y coord fragment seen
masktab db      80h,40h,20h,10h,8,4,2,1 ; quicker than calculations!
                                        ; dashed line patterns
linetab dw      0ffffh                  ; ESC accent    11111111 11111111
        dw      0aaaah                  ; ESC a         10101010 10101010
        dw      0f0f0h                  ; ESC b         11110000 11110000
        dw      0fafah                  ; ESC c         11111010 11111010
        dw      0ffcch                  ; ESC d         11111111 11001100
        dw      0fc92h                  ; ESC e         11111100 10010010

linepat dw      0ffffh                  ; active line pattern, from above

;End of init data
IDSEQ   dw      tekem                   ; address of response to terminal
CTLTAB  dw      0                       ; .. inquiry
tekem   db      'IBM_TEK'               ; .. and the response
        db      escape,'/Z',0
x_coord dw      0                       ; Tek text char X coordinate
y_coord dw      8                       ; Tek text char Y coordinate
xcross  dw      0                       ; cross hairs to start at centre
ycross  dw      0
oldx    dw      0                       ; Tek coordinates of last point
oldy    dw      767                     ;  initially top left
scalex  dw      0                       ; PC coord for scaled x value
scaley  dw      0                       ;  for scaled y value
curmode db      0                       ; screen mode before graphics
                                ; local variables for LINE plotting routine
graph_mode db   0                       ; graphics video mode, default is none
cursor  dw      0                       ; saved text cursor
inited  db      0                       ; non-zero if inited (retains page)
tekflg  db      0                       ; Tek mode active flag
yflags  db      0                       ; flags byte from msy
flow    dw      0                       ; flow control word
gpage   db      0                       ; display adapter graphics page
gfcol   db      15                      ; graphics foreground colour
gbcol   db      0                       ; graphics background color
moremsg db      ' More >'
mormsglen equ   $-moremsg               ; length of message
ccode   db      0                       ; temp for holding plot color code
linelen dw      0                       ; offset increment between scan lines
putc    dw      mputc                   ; ptr to plot a character routine
psetup  dw      psetupm                 ; ptr to plot setup routine
pfin    dw      pfinm                   ; ptr to plot cleanup routine
pincy   dw      pincym                  ; ptr to inc y routine
plotptr dw      pltmon                  ; ptr to dot plot routine
segscn  dw      0b800h                  ; actual screen segment to use
; ANSI Escape sequence to turn off Media Copy (Print Controller Off)
tkoff   db      escape,'[?38l'          ; Exit Tek mode escape sequence
tkofflen equ    $-tkoff                 ; length of sequence
tkoffs  db      6 dup (0)               ; received chars in rcv'd sequence
tkcnt   dw      0                       ; counter of matched char in tkoffs
repbuf  db      6 dup (0)               ; crosshairs report buf
repcnt  db      0                       ; number of untouched chars in repbuf
temp    dw      0

; 8*8 font for Hercules, CGA, and EGA TEK mode
; - allows 43 lines, and 80 (90 for Hercules) chars per line.
; all printing (?) characters from <space> to <del> - two characters per line
; 8 bits per scan line, given top line first, 8 scan lines.
font    db      0,0,0,0,0,0,0,0,               18h,18h,18h,18h,18h,0,18h,0
        db      6ch,6ch,6ch,0,0,0,0,0,         36h,36h,7fh,36h,7fh,36h,36h,0
        db      0ch,3fh,68h,3eh,0bh,7eh,18h,0, 60h,66h,0ch,18h,30h,66h,06h,0
        db      38h,6ch,6ch,38h,6dh,66h,3bh,0, 0ch,18h,30h,0,0,0,0,0
        db      0ch,18h,30h,30h,30h,18h,0ch,0, 30h,18h,0ch,0ch,0ch,18h,30h,0
        db      0,18h,7eh,3ch,7eh,18h,0,0,     0,18h,18h,7eh,18h,18h,0,0
        db      0,0,0,0,0,18h,18h,30h,         0,0,0,7eh,0,0,0,0
        db      0,0,0,0,0,18h,18h,0,           0,06h,0ch,18h,30h,60h,0,0
        db      3ch,66h,6eh,7eh,76h,66h,3ch,0, 18h,38h,18h,18h,18h,18h,7eh,0
        db      3ch,66h,06h,0ch,18h,30h,7eh,0, 3ch,66h,06h,1ch,06h,66h,3ch,0
        db      0ch,1ch,3ch,6ch,7eh,0ch,0ch,0, 7eh,60h,7ch,06h,06h,66h,3ch,0
        db      1ch,30h,60h,7ch,66h,66h,3ch,0, 7eh,06h,0ch,18h,30h,30h,30h,0
        db      3ch,66h,66h,3ch,66h,66h,3ch,0, 3ch,66h,66h,3eh,06h,0ch,38h,0
        db      0,0,18h,18h,0,18h,18h,0,       0,0,18h,18h,0,18h,18h,30h
        db      0ch,18h,30h,60h,30h,18h,0ch,   0,0,0,7eh,0,7eh,0,0,0
        db      30h,18h,0ch,06h,0ch,18h,30h,   0,3ch,66h,0ch,18h,18h,0,18h,0
        db      3ch,66h,6eh,6ah,6eh,60h,3ch,   0,3ch,66h,66h,7eh,66h,66h,66h,0
        db      7ch,66h,66h,7ch,66h,66h,7ch,   0,3ch,66h,60h,60h,60h,66h,3ch,0
        db      78h,6ch,66h,66h,66h,6ch,78h,   0,7eh,60h,60h,7ch,60h,60h,7eh,0
        db      7eh,60h,60h,7ch,60h,60h,60h,   0,3ch,66h,60h,6eh,66h,66h,3ch,0
        db      66h,66h,66h,7eh,66h,66h,66h,   0,7eh,18h,18h,18h,18h,18h,7eh,0
        db      3eh,0ch,0ch,0ch,0ch,6ch,38h,   0,66h,6ch,78h,70h,78h,6ch,66h,0
        db      60h,60h,60h,60h,60h,60h,7eh,   0,63h,77h,7fh,6bh,6bh,63h,63h,0
        db      66h,66h,76h,7eh,6eh,66h,66h,   0,3ch,66h,66h,66h,66h,66h,3ch,0
        db      7ch,66h,66h,7ch,60h,60h,60h,   0,3ch,66h,66h,66h,6ah,6ch,36h,0
        db      7ch,66h,66h,7ch,6ch,66h,66h,   0,3ch,66h,60h,3ch,06h,66h,3ch,0
        db      7eh,18h,18h,18h,18h,18h,18h,   0,66h,66h,66h,66h,66h,66h,3ch,0
        db      66h,66h,66h,66h,66h,3ch,18h,   0,63h,63h,6bh,6bh,7fh,77h,63h,0
        db      66h,66h,3ch,18h,3ch,66h,66h,   0,66h,66h,66h,3ch,18h,18h,18h,0
        db      7eh,06h,0ch,18h,30h,60h,7eh,   0,7ch,60h,60h,60h,60h,60h,7ch,0
        db      0,60h,30h,18h,0ch,06h,0,0,     3eh,06h,06h,06h,06h,06h,3eh,0
        db      18h,3ch,66h,42h,0,0,0,0,       0,0,0,0,0,0,0,0ffh
        db      30h,18h,0ch,0,0,0,0,0,         0,0,3ch,06h,3eh,66h,3eh,0
        db      60h,60h,7ch,66h,66h,66h,7ch,0, 0,0,3ch,66h,60h,66h,3ch,0
        db      06h,06h,3eh,66h,66h,66h,3eh,0, 0,0,3ch,66h,7eh,60h,3ch,0
        db      0eh,18h,18h,3ch,18h,18h,18h,0, 0,0,3eh,66h,66h,3eh,06h,3ch
        db      60h,60h,7ch,66h,66h,66h,66h,0, 18h,0,38h,18h,18h,18h,3ch,0
        db      18h,0,38h,18h,18h,18h,18h,70h, 60h,60h,66h,6ch,78h,6ch,66h,0
        db      38h,18h,18h,18h,18h,18h,3ch,0, 0,0,76h,7fh,6bh,6bh,63h,0
        db      0,0,7ch,66h,66h,66h,66h,0,     0,0,3ch,66h,66h,66h,3ch,0
        db      0,0,7ch,66h,66h,7ch,60h,60h,0, 0,3eh,66h,66h,3eh,06h,07h
        db      0,0,6ch,76h,60h,60h,60h,0,     0,0,3eh,60h,3ch,06h,7ch,0
        db      30h,30h,7ch,30h,30h,30h,1ch,0, 0,0,66h,66h,66h,66h,3eh,0
        db      0,0,66h,66h,66h,3ch,18h,0,     0,0,63h,6bh,6bh,7fh,36h,0
        db      0,0,66h,3ch,18h,3ch,66h,0,     0,0,66h,66h,66h,3eh,06h,3ch
        db      0,0,7eh,0ch,18h,30h,7eh,0,     0ch,18h,18h,70h,18h,18h,0ch,0
        db      18h,18h,18h,0,18h,18h,18h,0,   30h,18h,18h,0eh,18h,18h,30h,0
        db      31h,6bh,46h,0,0,0,0,0,         8 dup (0ffh)
datas   ends

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

        assume  cs:code, ds:datas, es:nothing

; Initialise TEK mode by setting high resolution screen, etc

;The IBM PC CGA card has (639,199) as the coordinate of the lower-right corner.
;Calculate endpoint X=(5/8)*(HIX*32+LOX), Y=199-(10/39)*(HIY*32+LOY)

;The Olivetti has (639,399) as the coordinate of the lower-right corner.
;Calculate endpoint X=(5/8)*(HIX*32+LOX), Y=399-(20/39)*(HIY*32+LOY)

;The Hercules card has (719,347) as the coordinate of the lower-right corner.
;Calculate endpoint X=(45/64)*(HIX*32+LOX), Y=347-(29/65)*(HIY*32+LOY)

; The EGA card has (639,349) as the coordinates of the lower-right corner
;Calculate endpoint X=(45/64)*(HIX*32+LOX), Y=349-(35/78)*(HIY*32+LOY)

tekini  PROC NEAR
        push    ax                      ; do presence tests. [jrd]
        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     psetup,offset psetupc   ; CGA plot setup routine
        mov     plotptr,offset pltcga   ; CGA dot plot routine
        mov     pfin,offset pfinc       ; CGA cleanup 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
                                        ; test for EGA
        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     pfin,offset pfine       ; cleanup routine
        mov     pincy,offset pincye     ; inc y routine
        mov     putc,offset gputc       ; character display routine
        mov     ah,gfcol
        mov     al,gbcol
        cmp     graph_mode,monoega      ; monochrome display?
        jne     tekin5d                 ; ne = no
        test    al,7                    ; bright backgound?
        jnz     tekin5b                 ; nz = yes
        mov     ah,1                    ; normal foreground
        test    gfcol,8                 ; intensity on?
        jz      tekin5b                 ; z = no
        mov     ah,5                    ; say bright foreground
tekin5b:test    al,7                    ; black backgound?
        jz      tekin5c                 ; z = yes
        mov     al,1                    ; regular video
tekin5c:cmp     ah,al                   ; same color in both?
        jne     tekin5d                 ; ne = no
        mov     ah,1                    ; make foreground regular
        mov     al,0                    ;  and background black
tekin5d:mov     gfcol,ah
        mov     gbcol,al
        cmp     gpage,0                 ; minimal memory (64KB mono and ega)?
        ja      tekin5e                 ; 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
tekin5e:jmp     tekin13                 ; end of ega tests.

tekin7: mov     ax,0fc00h               ; Olivetti/AT&T 6300, check rom id
        mov     es,ax                   ; look in 0fc00:050h for logo
        mov     di,50h                  ; 'OLIVETTI' logo should be here
        mov     si,offset ATTLOGO       ; master string
        mov     cx,attlen               ; length of it
        cld
        repe    cmpsb                   ; do a match
        jne     tekin7a                 ; ne = mismatch, check other adapters
        mov     graph_mode,olivetti     ; Olivetti
        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 for Olivetti
        mov     plotptr,offset pltcga   ; cga dot plot routine
        mov     pfin,offset pfinc       ; cleanup routine
        mov     pincy,offset pincyh     ; inc y routine (Herc style addresses)
        mov     putc,offset gputc       ; character display routine
        mov     ybot,399                ; bottom of screen is y = 399
        mov     ymult,20                ; Olivetti vertical scale = 400/780
        mov     ydiv,39                 ; same as cga setup
        jmp     tekin13

tekin7a: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     psetup,offset psetuph   ; plot setup routine to use
        mov     plotptr,offset plthga   ; use hga dot plot routine
        mov     pfin,offset pfinh       ; cleanup routine
        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
                                        ; test for 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     pfin,offset pfinm       ; cleanup 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     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
        call    tekcls                  ; clear screen, for ega coloring
        jmp     short tekin20
tekin19:call    tekrest                 ; restore old graphics screen
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

;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
        cmp     al,0                    ; null char response?
        je      tekign                  ; e = yes, ignore the character
                                        ; check for echo of crosshair report
        cmp     repcnt,0                ; any chars need matching in report?
        je      tektt5a                 ; e = no
        push    bx                      ; yes
        mov     bx,6                    ; number of bytes in crosshairs rpt
        sub     bl,repcnt               ; number of chars needing matching
        cmp     al,repbuf[bx]           ; received same as current rpt char?
        pop     bx
        jne     tektt5a                 ; ne = mismatch, stop echo test
        dec     repcnt                  ; say one more matched
        clc                             ; absorb without comment
        ret                             ; stay in this state
tektt5a:mov     repcnt,0                ; clear report count
        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
        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
        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 printer
        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>
tektx1: cmp     al,CR                   ; ^M Carriage return?
        je      tektx7                  ; e = yes
tektx2: cmp     al,LF                   ; ^J LineFeed?
        je      tektx7                  ; e = yes
tektx3: cmp     al,FF                   ; ^L Formfeed?
        jne     tektx4                  ; ne = no
        call    tekcls                  ; clear the screen
        jmp     short tektx8
tektx4: cmp     al,VT                   ; ^K vertical tab?
        je      tektx7
        cmp     al,bell                 ; ^G bell on line?
        jne     tektx5                  ; ne = no
        call    beep
        jmp     short tektx8
tektx5: cmp     al,Tab                  ; Tab?
        jne     tektx6                  ; ne = no
        mov     al,' '                  ; make it a space
tektx6: cmp     al,BS                   ; ^H backspace?
        je      tektx7                  ; e = yes
        cmp     al,' '                  ; control char?
        jb      tektx8                  ; b = yes, ignore it
tektx7: call    OUTSCRN                 ; output character to the screen
tektx8: ret
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     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
        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
        call    SENDSTAT                ; send status
        jmp     tekescx

tekesc5:cmp     al,accent               ; accent grave, line pattern series?
        jb      tekescx                 ; b = no
        cmp     al,65h                  ; lowercase e?
        ja      tekescx                 ; a = beyond line pattern series
        push    bx
        mov     bl,al
        sub     bl,accent               ; remove bias
        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

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

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      go2text                 ; e = yes, a cr
        cmp     al,LF                   ; these terminate line drawing cmds
        je      go2text
        cmp     al,FS                   ; <FS>
        je      go2text
        cmp     al,RS                   ; <RS>
        je      go2text
        cmp     al,US                   ; <US>
        je      go2text
        cmp     al,CAN                  ; and <CAN>
        je      go2text                 ; 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
                      ; 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, by, xcross, ycross operate in PC coordinates.

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

crosha0:mov     ax,xmax                 ; right margin minus 7 dots
        add     ax,7
        mov     temp,ax                 ; right margin 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
crosha1:call    crosdraw                ; draw the cross-hairs
        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

        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'?
        je      crosha0                 ; e = yes
        jmp     short arrow1            ; else try the arrow keys

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,-64                  ; big left shift
        jmp     short xkeys
arrow6: cmp     al,shrgtarr             ; shifted right arrow?
        jne     arrow7                  ; ne = no
        mov     cx,64                   ; big right shift
        jmp     short xkeys
arrow7: cmp     al,shuparr              ; shifted up arrow?
        jne     arrow8                  ; ne = no
        mov     cx,-32                  ; big up shift
        jmp     short vertkey
arrow8: cmp     al,shdnarr              ; shifted down arrow?
        jne     charkey                 ; ne = no, send this key as is
        mov     cx,32                   ; 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
        mov     repcnt,6                ; set length of report string
        mov     repbuf,al               ; store first report character
        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
        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-12)
        mov     di,ycross
        sub     di,12                   ; 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+12)
        mov     bx,ycross               ; make bottom stroke
        add     bx,12
        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
        mov     word ptr repbuf+1,ax    ; first word
        pop     ax
        call    sendxy                  ; send y coord
        mov     word ptr repbuf+3,ax    ; second word
        mov     al,cr                   ; follow up with cr
        mov     repbuf+5,al             ; last report character
        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
                                        ; 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
        mov     ax,mormsglen            ; characters in More message
        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     ah,gfcol                ; get screen coloring
        mov     al,gbcol
        push    ax                      ; save it
        xchg    ah,al                   ; interchange colors for message
        mov     gfcol,ah
        mov     gbcol,al
        mov     si,offset moremsg       ; give More message
        push    cx
        mov     cx,mormsglen
outsclf:cld
        lodsb                           ; read a byte from string
        call    putc                    ; display the string
        loop    outsclf                 ; repeat for all string chars
        pop     cx
        pop     ax                      ; restore normal video
        mov     gfcol,ah
        mov     gbcol,al
        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     ttstate,offset tektxt   ; do displayable text

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?
        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     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: call    pfin                    ; cleanup boards after plotting
        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

pfine   proc    near                    ; EGA end of plot board cleanup
        mov     ax,0003h                ; restore write bits directly mode
        call    ega_gc                  ; set controller
        ret
pfine   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

pfinc   proc    near                    ; CGA end of plot board cleanup
        ret
pfinc   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

plthga  proc    near                    ; HGA plot(x,y). x is in si, y is in di.
        jmp     pltcga                  ; use CGA routine for plotting
plthga  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

pfinh   proc    near                    ; HGA end of plot board cleanup
        ret
pfinh   endp

;;;;;;;;;;;;;;; AT&T-Olivetti Graphics Adapter plot support routines
; The OGA graphics memory mapping in mode 48h (640 by 400) 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                    ; OGA 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,'+'
        call    mputc                   ; display text char
        pop     ax
        ret
pltmon  endp

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

pfinm   proc    near                    ; Monochrome end of plot cleanup
        ret
pfinm   endp

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

gputc   proc    near
        cmp     al,' '                  ; control character?
        jae     gputc1                  ; ae = no, display the char
        jmp     putctrl                 ; else handle controls at putctrl
gputc1: push    bx                      ; first save some registers
        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
        cmp     graph_mode,cga          ; CGA. medium color display and board?
        je      gputc4                  ; e = yes
        cmp     graph_mode,hercules     ; Hercules?
        je      gputc4                  ; e = yes
        cmp     graph_mode,olivetti     ; AT&T-Olivetti?
        je      gputc4                  ; e = yes

gputc3: 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,1                    ; plot in foreground colors this time
        call    ega_plt                 ; byte plot routine for EGA systems
        pop     bx
        inc     bx                      ; next byte of char pattern
        call    pincy                   ; next scan line
        loop    gputc3
        jmp     short gputx

gputc4: mov     al,font[bx]             ; Non-EGA systems: get bits from font
        mov     es:[di],al              ; write desired pattern (no overwrite)
        inc     bx                      ; point to next byte of char pattern
        call    pincy                   ; inc to next line (linelen is preset)
        loop    gputc4                  ; and repeat 'til complete
gputx:  call    incx                    ; move to next char position
        pop     di
        pop     es
        pop     cx
        pop     bx
        ret
gputc   endp

putctrl proc    near                    ; CONTROL CHARS = cursor movement
        cmp     al,FF                   ; formfeed?
        jne     putct0                  ; ne = no
        call    TEKCLS                  ; FF clears the screen
        ret
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
        ret
putct2: cmp     al,tab                  ; tabs move forward one char position
        jne     putct4                  ; ne = not a tab
        mov     ax,x_coord
        add     ax,8                    ; so add 8
        cmp     ax,xmax                 ; zero if off edge of screen
        jbe     putct3
        mov     x_coord,0
        mov     al,lf
        jmp     short putct5            ; process our new line feed
putct3: mov     x_coord,ax
        ret
putct4: cmp     al,cr                   ; <CR> means go to beginning of line
        jne     putct5
        mov     x_coord,0               ; zero the x coordinate
        ret
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
        ret
putct7: cmp     al,vt                   ; <VT> move up screen 1 line (8 pixels)
        jne     putctx
        sub     y_coord,8               ; - so subtract 8
        jnc     putctx                  ; nc = space left
        mov     y_coord,8               ; else set to top of screen
putctx: 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

; 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 - only page 0 is
; enabled and used

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

