;*******************************************************
;                MEMORY MAPPED VIDEO DRIVER
;*******************************************************
;
;           By  Bill Bolton
;               Software Tools
;               P.O. Box 80
;               Newport Beach
;               NSW, 2106
;               AUSTRALIA
;
;****** DATE OF THIS VERSION 15th February, 1980 *******
;
;       Version 1.12
;
MPAGE   EQU     0CC00H          ;VDM-1 MEMORY ADDR.
SCREEN  EQU     0400H           ;VDM-1 MEMORY SIZE = 1K
FINIS   EQU     004FH           ;END OF CP/M SCRATCH RAM
LB      EQU     0FFH            ;MASK  LOW 8 ADDR BITS
SPACE   EQU     20H             ;ASCII SPACE
DELETE  EQU     7FH             ;ASCII DEL
FLAG    EQU     0FFH            ;FLAG SET
OFFSET  EQU     SPACE           ;OFFSET TO GET ABOVE
                               ;CONTROL CODES
HORIZ   EQU     64              ;NO. OF CHARACTERS/LINE
VERT    EQU     16              ;NO. OF LINES/PAGE
;
       ORG     0F000H
;
;
;*******JUMPS MUST REMAIN HERE IN THE SAME ORDER*******
;
;
ENTRY:
       JMP     VIDEO           ;PROCESS A CHARACTER
       JMP     INITIAL         ;INITIALISE RAM
;
;
       DB      'VIDEODRV '
       DB      'VERSION 1.12 '
       DB      'COPYRIGHT (C) '
       DB      '1977/80 BILL BOLTON '
;
;
VIDEO:
       CALL    LIFTCUR         ;GET CURSOR LOCATION
       LDA     ESCFLAG         ;ESC SEQUENCE IN,
       CPI     0               ;PROGRESS ?
       JNZ     ESCAPE          ;YES, GO TO IT
       MOV     A,C             ;GET CHAR INTO A
       ANI     07FH            ;MASK OUT BIT 7 TO MAKE ASCII
       CPI     DELETE          ;IS IT A DELETE ?
       JZ      EXIT            ;YES, IGNORE IT
       CPI     OFFSET          ;ASCII PRINTING CHAR ?
       JP      PRINT           ;YES, PUT ONTO SCREEN
                               ;NO, MUST BE CONTROL CHARACTER
       ;
       ;CONTROL CHARACTER TABLE PROCESSING ROUTINE
       ;
       PUSH    H               ;SAVE CURRENT SCREEN LOC.
       LXI     H,VDUTBLE       ;GET START OF TABLE
       ADD     L               ;ADD LOW ADDR BYTE TO A
       MOV     L,A             ;PUT RESULT BACK INTO L
       MOV     L,M             ;GET VALUE IN MEMORY,
       XTHL                    ;POINTED TO BY HL INTO L,
       RET                     ;PUT ONTO STACK AND JUMP,
                               ;TO IT, ALSO RESTORE
                               ;CURSOR ADDR TO HL
       ;
VDUTBLE:
       DB      EXIT AND LB     ;^@
       DB      EXIT AND LB     ;^A
       DB      EXIT AND LB     ;^B
       DB      EXIT AND LB     ;^C
       DB      EXIT AND LB     ;^D
       DB      EXIT AND LB     ;^E
       DB      EXIT AND LB     ;^F
       DB      PRINT AND LB    ;^G BELL
       DB      BACKSP AND LB   ;^H CURSOR LEFT
       DB      EXIT AND LB     ;^I
       DB      LINEF AND LB    ;^J LINE FEED
       DB      CURSUP AND LB   ;^K CURSOR UP
       DB      EOL AND LB      ;^L CURSOR RIGHT
       DB      CRET AND LB     ;^M CARRIAGE RETURN
       DB      EXIT AND LB     ;^N
       DB      EXIT AND LB     ;^O
       DB      EXIT AND LB     ;^P
       DB      EXIT AND LB     ;^Q
       DB      EXIT AND LB     ;^R
       DB      EXIT AND LB     ;^S
       DB      EXIT AND LB     ;^T
       DB      EXIT AND LB     ;^U
       DB      EXIT AND LB     ;^V
       DB      EXIT AND LB     ;^W
       DB      EXIT AND LB     ;^X
       DB      EXIT AND LB     ;^Y
       DB      FORM AND LB     ;^Z CLEAR SCREEN
       DB      ESCF AND LB     ;^[ ESCAPE
       DB      EXIT AND LB     ;^\
       DB      EXIT AND LB     ;^]
       DB      HOME AND LB     ;^^ HOME CURSOR
       DB      CRLF AND LB     ;^- NEW LINE
       ;
       ;PRINT THE CHARACTER ON THE SCREEN
       ;
PRINT:
       MOV     C,A             ;PUT ASCII CHAR BACK
       LDA     VFL             ;GET REV VIDEO FLAG
       XRA     C               ;XOR WITH CHARACTER
       MOV     M,A             ;PUT RESULT ONTO SCREEN
       ;
       ;E0L CHECKS THE CURSOR POSITION FOR END OF LINE
       ;
EOL:
       LDA     CURPOS          ;GET CURSOR POSITION
       INR     A               ;CURSOR=CURSOR+1
       CPI     HORIZ           ;MAX LINE LENGTH ?
       JC      TABRET          ;NO,STORE NEW CURPOS
CRLF:
       XRA     A               ;RESET A TO LINE START
       STA     CURPOS          ;SAVE CURSOR POSITION
       ;
       ;MOVE DOWN ONE LINE
       ;
LINEF:
       LDA     LINENO          ;GET CURRENT LINE NO.
       CPI     VERT-1          ;AT THE BOTTOM ?
       JNZ     NOSCRL          ;NO, DONT SCROLL UP
       ;
       ;SCROLL UP ONE LINE
       ;
SCROLL:
       LXI     H,MPAGE+HORIZ   ;YES, HL= LINE N+1
       LXI     D,MPAGE         ;DE= LINE N
       LXI     B,(VERT*(HORIZ/2))-(HORIZ/2)    ;CHAR COUNT
SCRL:
       MOV     A,M             ;GET CHAR FROM LINE N+1
       STAX    D               ;PUT IT INTO LINE N
       INX     D               ;BUMP D TO NEXT CHAR POS
       INX     H               ;BUMP H DITTO
       MOV     A,M             ;GET IT
       STAX    D               ;STORE A
       INX     H               ;BUMP H
       INX     D               ;BUMP D
       DCX     B               ;DECR CHAR COUNT EVERY 2 MOVES
       MOV     A,B             ;GET HI BYTE OF COUNT
       ORA     C               ;IS COUNT = 0 ?
       JNZ     SCRL            ;NOT FINISHED, DO IT AGAIN
       LDA     LINENO          ;YES, GET CURRENT LINE NO.
       ;
       ;ERASE BOTTOM LINE (DE POINTS TO START OF LINE)
       ;
EBOTL:
       XCHG                    ;START OF LINE INTO HL
       MVI     B,HORIZ         ;SET UP CHAR COUNT
       CALL    ELOP            ;DO THE ERASE
       DCR     A               ;SET UP TO ENTER NOSCRL
NOSCRL:
       INR     A               ;LINE = LINE + 1
       STA     LINENO          ;SAVE LINE NO.
       JMP     EXIT
       ;
ELOP:
       MVI     M,SPACE         ;SPACE TO OVERWRITE
       INX     H               ;BUMP H TO NEXT CHAR
       DCR     B               ;B=0 IS END OF LINE
       JNZ     ELOP            ;NO, DO IT AGAIN
       RET
       ;
       ;BACKSPACE
       ;
BACKSP:
       LDA     CURPOS          ;GET CURRENT CURSOR
       DCR     A               ;CURSOR=CURSOR-1
       JP      TABRET          ;STORE NEW CURPOS
       JMP     CRET            ;GONE BACK PAST START
                               ;OF LINE SO RESET
       ;
       ;CLEAR THE SCREEN AND HOME CURSOR
       ;
FORM:
       CALL    CLEAR
HOME:
       XRA     A               ;RESET A TO 0
       STA     LINENO          ;LINENO.=0
       STA     VFL             ;VFL=0
       ;
       ;CARRIAGE RETURN
       ;
CRET:
       XRA     A               ;RESET CURSOR TO LINE START
TABRET:
       STA     CURPOS
       ;
       ;RETURN TO THE CALLING ROUTINE
       ;
EXIT:
       CALL    LIFTCUR         ;CURSOR BACK ON SCREEN
       RET                     ;BACK TO CALLING ROUTINE
       ;
       ;MOVE CURSOR UP
       ;
CURSUP:
       LDA     LINENO          ;LINE NUMBER = N
       ANA     A               ;AT TOP (N=0) ?
       JZ      EXIT            ;YES, DO NOTHING
       DCR     A               ;LINENO=LINENO-1
       STA     LINENO          ;SAVE LINE NUMBER
       JMP     EXIT
       ;
       ;CLEAR THE SCREEN
       ;
CLEAR:
       LXI     H,MPAGE         ;GET SCREEN START ADDR.
WRSPC:
       MVI     M,SPACE         ;PUT SPACE ONTO SCREEN
       INX     H               ;NEXT SCREEN LOCATION
       MOV     A,H             ;GET SCREEN POS. IN A
       CPI     (MPAGE+SCREEN)/256 ;END OF SCREEN ?
       JNZ     WRSPC           ;NO, WIPE NEXT SCREEN POS.
       RET
       ;
       ;SET ESCAPE FLAG
       ;
ESCF:
       MVI     A,FLAG          ;GET FLAG INTO A
       STA     ESCFLAG         ;STORE ESC SEQUENCE FLAG
       JMP     EXIT
       ;
       ;CALCULATE MEMORY ADDRESS FROM CURSOR POSITION
       ;
LIFTCUR:
       LXI     H,MPAGE-HORIZ   ;SCREEN START - 1 LINE
       LXI     D,HORIZ         ;CHARACTERS/LINE
       LDA     LINENO          ;GET CURRENT LINE NUMBER
       INR     A               ;SET UP TO ENTER CLOP
CLOP:
       DAD     D               ;HL=CHAR PER LINE * NUMBER
                               ;OF LINES
       DCR     A               ;LINENO.= LINENO.-1
       JNZ     CLOP            ;LINENO.= 0 ?
CFIN:
       MOV     D,A             ;YES, RESET D TO 0
       LDA     CURPOS          ;GET CURSOR POSITION
       MOV     E,A             ;PUT IT INTO E
       DAD     D               ;ADD CHAR POS IN CURRENT
                               ;LINE TO TOTAL IN HL
       ;
       ;REVERSE THE VIDEO
       ;
       MOV     A,M             ;GET SCREEN POS CONTENTS
       XRI     80H             ;COMPLEMENT BIT 8
       MOV     M,A             ;PUT IT BACK ONTO SCREEN
       RET
       ;
       ;
       ;SET UP ALL THE INITIAL ENTRY PARAMETERS ON
       ;POWERING UP. INTENDED TO BE CALLED FROM THE
       ;OPERATING SYSTEM
       ;
       ;
INITIAL:
       MVI     A,0             ;INITIALISE VDM HARDWARE
       OUT     0C8H            ;VDM-1 PORT
       LXI     H,SCRATCH       ;SCRATCHPAD RAM AREA
ILOOP:
       MVI     M,00            ;CLEAR ONE LOCATION
       INX     H               ;NEXT LOCATION
       MOV     A,L             ;GET LOW ADDRES BYTE
       CPI     FINIS           ;AT THE END YET?
       JNZ     ILOOP           ;NO, CLEAR NEXT BYTE
       JMP     FORM            ;CLEAR THE SCREEN AND
                               ;SET UP THE CURSOR
       ;
       ;TEST FOR WHICH CHARACTER IN ESC SEQUENCE
       ;
ESCAPE:
       LDA     ESCFLAG         ;FLAG DOUBLES AS COUNT
       CPI     0FFH            ;1st CHARACTER ?
       JZ      ESCAPE1         ;YES
       CPI     0FEH            ;2nd CHARACTER ?
       JZ      ESCAPE2         ;YES
       CPI     0FDH            ;3rd CHARACTER ?
       JZ      ESCAPE3         ;YES
       JMP     ESCEND          ;NO MATCH SO END SEQ
       ;
       ;PROCESS FIRST CHARACTER OF ESCAPE SEQUENCE
       ;
ESCAPE1:
       MOV     A,C             ;GET CHAR INTO A
       CPI     '='             ;IS IT '=' ?
       JZ      POSIT           ;YES, SET POSFLAG
       CPI     '*'             ;IS IT '*' ?
       JZ      FORM            ;YES, CLEAR SCREEN
       CPI     'G'             ;IS IT 'G' ?
       JZ      ATTRIB          ;YES, VIDEO ATTRIB
       CPI     'T'             ;IS IT 'T'
       JZ      ERASE           ;YES, ERASE TO END OF LINE
       JMP     ESCEND          ;NO MATCH SO END SEQ
       ;
       ;SET FLAG TO INDICATE POSITIONING SEQUENCE
       ;
POSIT:
       MVI     A,FLAG          ;SET UP POSITION FLAG
       STA     POSFLAG         ;STORE IT
DECR:
       LDA     ESCFLAG         ;GET ESC SEQ CHAR COUNT
       DCR     A               ;COUNT = COUNT-1
       STA     ESCFLAG         ;STORE IT
       JMP     EXIT
       ;
       ;SET FLAG TO INDICATE VIDEO ATTRIBUTE SEQUENCE
       ;
ATTRIB:
       MVI     A,FLAG          ;SET UP ATTRIBUTE FLAG
       STA     ATTFLAG         ;STORE IT
       JMP     DECR            ;DECREMENT ESCFLAG
       ;
ERASE:
       MOV     A,L             ;GET LOW BYTE OF SCREEN
       ORI     HORIZ-1         ;SET A TO END OF LINE-1
       INR     A               ;BUMP A TO EOL
       SUB     L               ;SUBTRACT CURSOR LOCATION
                               ;FROM END OF LINE COUNT
                               ;TO GET NUMBER OF CHARS
                               ;TO ERASE
       MOV     B,A             ;PUT INTO B FOR ELOP
       CALL    ELOP            ;DO THE ERASE
       JMP     ESCEND          ;EXIT
       ;
       ;PROCESS 2nd CHARACTER OF ESCAPE SEQUENCE
       ;
ESCAPE2:
       LDA     POSFLAG         ;GET POSITION FLAG
       CPI     FLAG            ;IS IT SET ?
       JZ      NEWLINE         ;YES, SET NEW LINE
       LDA     ATTFLAG         ;GET ATTRIBUTE FLAG
       CPI     FLAG            ;IS IT SET ?
       JZ      ATTRIB2         ;YES, PROCESS IT
       JMP     ESCEND
       ;
       ;SET CURSOR TO NEW LINE POSITION
       ;
NEWLINE:
       MOV     A,C             ;GET CHAR INTO A
       SUI     OFFSET          ;REMOVE OFFSET
       CPI     VERT            ;LARGER THAN NO. LINES ?
       JM      CONT1           ;NO
       MVI     A,VERT-1        ;GET MAX. LINE NUMBER
CONT1:
       STA     LINENO          ;STORE NEW LINE NO.
       JMP     DECR            ;DECREMENT ESC COUNT
       ;
       ;SET VIDEO ATTRIBUTE
       ;
ATTRIB2:
       MOV     A,C             ;GET CHAR INTO A
       CPI     '0'             ;IS IT ATTRIBUTE OFF?
       JZ      NORMAL          ;YES, RESET VIDEO FLAG
       CPI     '4'             ;IS IT REV VIDEO ON?
       JZ      REVERSE         ;YES, REVERSE VIDEO FLAG
       CPI     '6'             ;IS IT BLINK/REV VIDEO ON?
       JZ      REVERSE         ;CANT BLINK BUT CAN REVERSE
       JMP     ESCEND
       ;
       ;VIDEO FLAG ROUTINES
       ;
NORMAL:
       XRA     A               ;RESET VIDEO FLAG
       JMP     VFLSTOR         ;SAVE IT
       ;
REVERSE:
       MVI     A,80H           ;SET VIDEO FLAG
VFLSTOR:
       STA     VFL             ;SAVE IT
       JMP     ESCEND
       ;
       ;PROCESS THIRD CHARACTER OF ESCAPE SEQUENCE
       ;
ESCAPE3:
       LDA     POSFLAG         ;GET POSITION FLAG
       CPI     FLAG            ;IS IT SET ?
       JZ      NEWCHAR         ;YES, SET NEW CHAR POS
       JMP     ESCEND
       ;
       ;SET NEW CURSOR CHARACTER POSITION
       ;
NEWCHAR:
       MOV     A,C             ;GET CHAR INTO A
       SUI     OFFSET          ;REMOVE OFFSET
       CPI     HORIZ           ;LARGER THAN LINE LENGTH ?
       JM      CONT2           ;NO
       MVI     A,HORIZ-1       ;GET MAX. CHAR COUNT
CONT2:
       STA     CURPOS          ;SAVE NEW CHAR POS
       JMP     ESCEND
       ;
       ;EXIT FROM ESCAPE SEQUENCE
       ;
ESCEND:
       XRA     A               ;RESET A TO 0
       STA     ESCFLAG         ;RESET FLAGS
       STA     POSFLAG
       STA     ATTFLAG
       JMP     EXIT
       ;
       ;CURSOR STORAGE LOCATIONS
       ;
       ORG     0040H           ;Or some other RAM scratch area
;
SCRATCH:DS      6               ;THIS AREA MUST BE RAM
CURPOS: EQU     SCRATCH         ;POSITION ON LINE
LINENO: EQU     SCRATCH+1       ;LINE ON SCREEN
VFL:    EQU     SCRATCH+2       ;REVERSE VIDEO FLAG
ESCFLAG:EQU     SCRATCH+3       ;ESCAPE SEQUENCE FLAG
POSFLAG:EQU     SCRATCH+4       ;CURSOR POSITIONING FLAG
ATTFLAG:EQU     SCRATCH+5       ;VIDEO ATTRIBUTE FLAG
       ;
       END     ENTRY