;====================================================================== ; VGA DRIVER FOR RBC PROJECT ; ; WRITTEN BY: WAYNE WARTHEN -- 5/29/2017 ;====================================================================== ; ; TODO: ; ;====================================================================== ; VGA DRIVER - CONSTANTS ;====================================================================== ; VGA_BASE .EQU $E0 ; VGA_KBDDATA .EQU VGA_BASE + $00 ; KBD CTLR DATA PORT VGA_KBDST .EQU VGA_BASE + $01 ; KBD CTLR STATUS/CMD PORT VGA_REG .EQU VGA_BASE + $02 ; SELECT CRTC REGISTER VGA_DATA .EQU VGA_BASE + $03 ; READ/WRITE CRTC DATA VGA_CFG .EQU VGA_BASE + $04 ; VGA3 BOARD CFG REGISTER VGA_HI .EQU VGA_BASE + $05 ; BOARD RAM HI ADDRESS VGA_LO .EQU VGA_BASE + $06 ; BOARD RAM LO ADDRESS VGA_DAT .EQU VGA_BASE + $07 ; BOARD RAM BYTE R/W ; VGA_NOBL .EQU 00000000B ; NO BLINK VGA_NOCU .EQU 00100000B ; NO CURSOR VGA_BFAS .EQU 01000000B ; BLINK AT X16 RATE VGA_BSLO .EQU 01100000B ; BLINK AT X32 RATE ; VGA_BLOK .EQU 0 ; BLOCK CURSOR VGA_ULIN .EQU 1 ; UNDERLINE CURSOR ; VGA_CSTY .EQU VGA_BLOK ; DEFAULT CURSOR STYLE VGA_BLNK .EQU VGA_NOBL ; DEFAULT BLINK RATE VGA_9BIT .EQU $0101 ; 9 BIT MSK-CFG VGA_8BIT .EQU $0000 ; 8 BIT MSK-CFG VGA_NICE .EQU FALSE ; TRUE = SLOW BUT PRETTY ; #IF (VGASIZ=V80X25) VGA_ROWS .EQU 25 VGA_COLS .EQU 80 VGA_SCANL .EQU 16 VGA_89BIT .EQU VGA_8BIT #DEFINE USEFONT8X16 #DEFINE VGA_FONT FONT8X16 #ENDIF #IF (VGASIZ=V80X30) VGA_ROWS .EQU 30 VGA_COLS .EQU 80 VGA_SCANL .EQU 16 VGA_89BIT .EQU VGA_8BIT #DEFINE USEFONT8X16 #DEFINE VGA_FONT FONT8X16 #ENDIF #IF (VGASIZ=V80X43) VGA_ROWS .EQU 43 VGA_COLS .EQU 80 VGA_SCANL .EQU 11 VGA_89BIT .EQU VGA_8BIT #DEFINE USEFONT8X11 #DEFINE VGA_FONT FONT8X11 #ENDIF #IF (VGASIZ=V80X60) VGA_ROWS .EQU 60 VGA_COLS .EQU 80 VGA_SCANL .EQU 8 VGA_89BIT .EQU VGA_8BIT #DEFINE USEFONT8X8 #DEFINE VGA_FONT FONT8X8 #ENDIF ; #IF VGA_CSTY=VGA_BLOK VGA_R10 .EQU (VGA_BLNK + $00) VGA_R11 .EQU VGA_SCANL-1 #ENDIF ; #IF VGA_CSTY=VGA_ULIN VGA_R10 .EQU (VGA_BLNK + VGA_SCANL-1) VGA_R11 .EQU VGA_SCANL-1 #ENDIF ; #DEFINE DEFREGS REGS_VGA ; TERMENABLE .SET TRUE ; INCLUDE TERMINAL PSEUDODEVICE DRIVER ; ; DRIVER UTILIZES THE MULTIPLE DISPLAY WINDOW FEATURE OF THE CRTC TO ACCOMPLISH ; FULL SCREEN SCROLLING WITHOUT THE NEED TO MOVE DISPLAY RAM BYTES. ; ; SCREEN 1 IMPLICITLY STARTS AT PHYSICAL ROW 0 ; SCREEN 1 RAM ADDRESS POINTER POINTS TO SCREEN OFFSET (R12/R13) ; SCREEN 2 ROW DEFINES WHERE BUFFER BYTE 0 WILL BE DISPLAYED (R18) ; SCREEN 2 RAM ADDRESS IS ALWAYS ZERO (R19/R20) ; ;====================================================================== ; VGA DRIVER - INITIALIZATION ;====================================================================== ; VGA_INIT: LD IY,VGA_IDAT ; POINTER TO INSTANCE DATA ; CALL NEWLINE ; FORMATTING PRTS("VGA: IO=0x$") LD A,VGA_REG CALL PRTHEXBYTE CALL VGA_PROBE ; CHECK FOR HW PRESENCE JR Z,VGA_INIT1 ; CONTINUE IF HW PRESENT ; ; HARDWARE NOT PRESENT PRTS(" NOT PRESENT$") OR $FF ; SIGNAL FAILURE RET ; VGA_INIT1: ; DISPLAY CONSOLE DIMENSIONS LD A,VGA_COLS CALL PC_SPACE CALL PRTDECB LD A,'X' CALL COUT LD A,VGA_ROWS CALL PRTDECB PRTS(" TEXT$") ; HARDWARE INITIALIZATION CALL VGA_CRTINIT ; SETUP THE VGA CHIP REGISTERS CALL VGA_LOADFONT ; LOAD FONT DATA FROM ROM TO VGA STORAGE CALL VGA_VDARES CALL KBD_INIT ; INITIALIZE KEYBOARD DRIVER ; ADD OURSELVES TO VDA DISPATCH TABLE LD BC,VGA_FNTBL ; BC := FUNCTION TABLE ADDRESS LD DE,VGA_IDAT ; DE := VGA INSTANCE DATA PTR CALL VDA_ADDENT ; ADD ENTRY, A := UNIT ASSIGNED ; INITIALIZE EMULATION LD C,A ; C := ASSIGNED VIDEO DEVICE NUM LD DE,VGA_FNTBL ; DE := FUNCTION TABLE ADDRESS LD HL,VGA_IDAT ; HL := VGA INSTANCE DATA PTR CALL TERM_ATTACH ; DO IT XOR A ; SIGNAL SUCCESS RET ; ;====================================================================== ; VGA DRIVER - VIDEO DISPLAY ADAPTER (VDA) FUNCTIONS ;====================================================================== ; VGA_FNTBL: .DW VGA_VDAINI .DW VGA_VDAQRY .DW VGA_VDARES .DW VGA_VDADEV .DW VGA_VDASCS .DW VGA_VDASCP .DW VGA_VDASAT .DW VGA_VDASCO .DW VGA_VDAWRC .DW VGA_VDAFIL .DW VGA_VDACPY .DW VGA_VDASCR .DW KBD_STAT .DW KBD_FLUSH .DW KBD_READ .DW VGA_VDARDC #IF (($ - VGA_FNTBL) != (VDA_FNCNT * 2)) .ECHO "*** INVALID VGA FUNCTION TABLE ***\n" !!!!! #ENDIF VGA_VDAINI: ; RESET VDA ; CURRENTLY IGNORES VIDEO MODE AND BITMAP DATA CALL VGA_VDARES ; RESET VDA XOR A ; SIGNAL SUCCESS RET VGA_VDAQRY: LD C,$00 ; MODE ZERO IS ALL WE KNOW LD D,VGA_ROWS ; ROWS LD E,VGA_COLS ; COLS LD HL,0 ; EXTRACTION OF CURRENT BITMAP DATA NOT SUPPORTED YET XOR A ; SIGNAL SUCCESS RET VGA_VDARES: LD A,$07 ; ATTRIBUTE IS STANDARD WHITE ON BLACK LD (VGA_ATTR),A ; SAVE IT XOR A ; ZERO (REVERSE, UNDERLINE, BLINK) LD (VGA_RUB),A ; SAVE IT LD DE,0 ; ROW = 0, COL = 0 CALL VGA_XY ; SEND CURSOR TO TOP LEFT LD A,' ' ; BLANK THE SCREEN LD DE,VGA_ROWS*VGA_COLS ; FILL ENTIRE BUFFER CALL VGA_FILL ; DO IT LD DE,0 ; ROW = 0, COL = 0 CALL VGA_XY ; SEND CURSOR TO TOP LEFT LD HL,$0404 | VGA_89BIT; SET VIDEO ENABLE BIT CALL VGA_SETCFG ; DO IT XOR A RET VGA_VDADEV: LD D,VDADEV_VGA ; D := DEVICE TYPE LD E,0 ; E := PHYSICAL UNIT IS ALWAYS ZERO LD H,0 ; H := 0, DRIVER HAS NO MODES LD L,VGA_BASE ; L := BASE I/O ADDRESS XOR A ; SIGNAL SUCCESS RET VGA_VDASCS: CALL SYSCHK ; NOT IMPLEMENTED (YET) LD A,ERR_NOTIMPL OR A RET VGA_VDASCP: CALL VGA_XY ; SET CURSOR POSITION XOR A ; SIGNAL SUCCESS RET VGA_VDASAT: ; INCOMING IS: -----RUB (R=REVERSE, U=UNDERLINE, B=BLINK) ; ; JUST SAVE THE VALUE AND FALL THROUGH. ONLY REVERSE IS ; SUPPORTED WHICH IS IMPLEMENTED BELOW. LD A,E LD (VGA_RUB),A ; SAVE IT JR VGA_VDASCO2 ; IMPLEMENT SETTING VGA_VDASCO: ; INCOMING IS: IBGRIBGR (I=INTENSITY, B=BLUE, G=GREEN, R=RED) ; TRANSFORM TO: -RGBIRGB (DISCARD INTENSITY BIT IN HIGH NIBBLE) ; ; A := INVERTED E, SO A IS RGBIRGBI (F/B) LD B,8 ; DO 8 BITS VGA_VDASCO1: RRC E ; LOW BIT OF E ROTATED RIGHT INTO CF RLA ; CF ROTATED LEFT INTO LOW BIT OF A DJNZ VGA_VDASCO1 ; DO FOR ALL 8 BITS ; LS A X 3 TO SWAP F/B BITS, SO A IS IRGBIRGB (B/F) RLCA RLCA RLCA ; MASK FOR RELEVANT BITS, SO A IS 0R0B0R0B AND %01010101 ; SAVE A IN C AND SET A = E LD C,A LD A,E ; MASK FOR RELEVANT BITS, SO A IS 00G0I0G0 AND %00101010 ; COMBINE WITH SAVED OR E ; SAVE NEW ATTR VALUE LD (VGA_ATTR),A ; AND SAVE THE RESULT VGA_VDASCO2: ; CHECK FOR REVERSE VIDEO LD A,(VGA_RUB) ; GET RUB SETTING BIT 2,A ; REVERSE IS BIT 2 JR Z,VGA_VDASCO3 ; DONE IF REVERSE VID NOT SET ; IMPLEMENT REVERSE VIDEO LD A,(VGA_ATTR) ; GET ATTRIBUTE PUSH AF ; SAVE IT AND %00001000 ; ISOLATE INTENSITY BIT LD E,A ; SAVE IN E POP AF ; GOT ATTR BACK RLCA ; SWAP FG/BG COLORS RLCA RLCA RLCA AND %01110111 ; REMOVE HIGH BITS OR E ; COMBINE WITH PREVIOUS INTENSITY BIT LD (VGA_ATTR),A ; SAVE NEW VALUE VGA_VDASCO3: XOR A ; SIGNAL SUCCESS RET VGA_VDAWRC: LD A,E ; CHARACTER TO WRITE GOES IN A CALL VGA_PUTCHAR ; PUT IT ON THE SCREEN XOR A ; SIGNAL SUCCESS RET VGA_VDAFIL: LD A,E ; FILL CHARACTER GOES IN A EX DE,HL ; FILL LENGTH GOES IN DE CALL VGA_FILL ; DO THE FILL XOR A ; SIGNAL SUCCESS RET VGA_VDACPY: ; LENGTH IN HL, SOURCE ROW/COL IN DE, DEST IS VGA_POS ; BLKCPY USES: HL=SOURCE, DE=DEST, BC=COUNT PUSH HL ; SAVE LENGTH CALL VGA_XY2IDX ; ROW/COL IN DE -> SOURCE ADR IN HL POP BC ; RECOVER LENGTH IN BC LD DE,(VGA_POS) ; PUT DEST IN DE JP VGA_BLKCPY ; DO A BLOCK COPY VGA_VDASCR: LD A,E ; LOAD E INTO A OR A ; SET FLAGS RET Z ; IF ZERO, WE ARE DONE PUSH DE ; SAVE E JP M,VGA_VDASCR1 ; E IS NEGATIVE, REVERSE SCROLL CALL VGA_SCROLL ; SCROLL FORWARD ONE LINE POP DE ; RECOVER E DEC E ; DECREMENT IT JR VGA_VDASCR ; LOOP VGA_VDASCR1: CALL VGA_RSCROLL ; SCROLL REVERSE ONE LINE POP DE ; RECOVER E INC E ; INCREMENT IT JR VGA_VDASCR ; LOOP ;---------------------------------------------------------------------- ; READ VALUE AT CURRENT VDU BUFFER POSITION ; RETURN E = CHARACTER, B = COLOUR, C = ATTRIBUTES ;---------------------------------------------------------------------- VGA_VDARDC: OR $FF ; UNSUPPORTED FUNCTION RET ; ;====================================================================== ; VGA DRIVER - PRIVATE DRIVER FUNCTIONS ;====================================================================== ; ;---------------------------------------------------------------------- ; SET BOARD CONFIGURATON REGISTER ; MASK IN H, VALUE IN L ;---------------------------------------------------------------------- ; VGA_SETCFG: PUSH AF ; PRESERVE AF LD A,H ; MASK IN ACCUM CPL ; INVERT IT LD H,A ; BACK TO H LD A,(VGA_CFGV) ; GET CURRENT CONFIG VALUE AND H ; RESET ALL TARGET BITS OR L ; SET TARGET BITS LD (VGA_CFGV),A ; SAVE NEW VALUE OUT (VGA_CFG),A ; AND WRITE IT TO REGISTER POP AF ; RESTORE AF RET ; ;---------------------------------------------------------------------- ; UPDATE CRTC REGISTERS ; VGA_REGWR WRITES VALUE IN A TO VDU REGISTER SPECIFIED IN C ;---------------------------------------------------------------------- ; VGA_REGWR: PUSH AF ; SAVE VALUE TO WRITE LD A,C ; SET A TO VGA REGISTER TO SELECT OUT (VGA_REG),A ; WRITE IT TO SELECT THE REGISTER POP AF ; RESTORE VALUE TO WRITE OUT (VGA_DATA),A ; WRITE IT RET ; VGA_REGWRX: LD A,H ; SETUP MSB TO WRITE CALL VGA_REGWR ; DO IT INC C ; NEXT VDU REGISTER LD A,L ; SETUP LSB TO WRITE JR VGA_REGWR ; DO IT & RETURN ; ;---------------------------------------------------------------------- ; READ CRTC REGISTERS ; VGA_REGRD READS VDU REGISTER SPECIFIED IN C AND RETURNS VALUE IN A ;---------------------------------------------------------------------- ; VGA_REGRD: LD A,C ; SET A TO VGA REGISTER TO SELECT OUT (VGA_REG),A ; WRITE IT TO SELECT THE REGISTER IN A,(VGA_DATA) ; READ IT RET ; VGA_REGRDX: CALL VGA_REGRD ; GET VALUE FROM REGISTER IN C LD H,A ; SAVE IN H INC C ; BUMP TO NEXT REGISTER OF PAIR CALL VGA_REGRD ; READ THE VALUE LD L,A ; SAVE IT IN L RET ; ;---------------------------------------------------------------------- ; WRITE VIDEO RAM ; VGA_MEMWR WRITES VALUE IN A TO ADDRESS IN DE ; VGA_MEMWRX WRITES VALUE IN HL TO ADDRESS IN DE ;---------------------------------------------------------------------- ; VGA_MEMWR: LD C,VGA_HI OUT (C),D INC C OUT (C),E INC C OUT (C),A RET ; VGA_MEMWRX: LD C,VGA_HI OUT (C),D INC C OUT (C),E INC C OUT (C),H INC E DEC C OUT (C),E INC C OUT (C),L DEC E RET ; ;---------------------------------------------------------------------- ; READ VIDEO RAM ; VGA_MEMRD READS VALUE IN DE TO A ; VGA_MEMRDX READS VALUE IN DE TO HL ;---------------------------------------------------------------------- ; VGA_MEMRD: LD C,VGA_HI OUT (C),D INC C OUT (C),E INC C IN A,(C) RET ; VGA_MEMRDX: LD C,VGA_HI OUT (C),D INC C OUT (C),E INC C IN H,(C) INC E DEC C OUT (C),E INC C IN L,(C) DEC E RET ; ;---------------------------------------------------------------------- ; WAIT FOR VERTICAL RETRACE ACTIVE ;---------------------------------------------------------------------- ; VGA_WAITSB: LD A,31 ; CRTC REG 31 IS STATUS REG OUT (VGA_REG),A ; SETUP TO ACCESS IT VGA_WAITSB1: IN A,(VGA_DATA) ; GET STATUS BIT 1,A ; TEST SB BIT (RETRACE ACTIVE) RET NZ ; RETURN IF ACTIVE JR VGA_WAITSB1 ; LOOP ; ;---------------------------------------------------------------------- ; PROBE FOR VGA HARDWARE ;---------------------------------------------------------------------- ; ; ON RETURN, ZF SET INDICATES HARDWARE FOUND ; VGA_PROBE: LD DE,0 ; POINT TO FIRST BYTE OF VRAM LD A,$A5 ; INITIAL TEST VALUE LD B,A ; SAVE IN B CALL VGA_MEMWR ; WRITE IT INC E ; NEXT BYTE OF VRAM CPL ; INVERT TEST VALUE CALL VGA_MEMWR ; WRITE IT DEC E ; BACK TO FIRST BYTE OF VRAM CALL VGA_MEMRD ; READ IT CP B ; CHECK FOR TEST VALUE RET NZ ; RETURN NZ IF FAILURE INC E ; SECOND VRAM BYTE CALL VGA_MEMRD ; READ IT CPL ; INVERT IT CP B ; CHECK FOR INVERTED TEST VALUE RET ; RETURN WITH ZF SET BASED ON CP ; ;---------------------------------------------------------------------- ; CRTC DISPLAY CONTROLLER CHIP INITIALIZATION ;---------------------------------------------------------------------- ; VGA_CRTINIT: LD HL,$FF00 | VGA_89BIT ; INITIAL CFG BITS CALL VGA_SETCFG ; DO IT CALL VGA_RES ; RESET CRTC (ALL REGS TO ZERO) LD HL,DEFREGS ; HL = POINTER TO TABLE OF REG VALUES VGA_CRTINIT1: LD A,(HL) ; FIRST BYTE IS REG ADR LD C,A ; PUT IN C FOR LATER INC A ; TEST FOR END MARKER ($FF) RET Z ; IF EQUAL, DONE INC HL ; NEXT BYTE LD A,(HL) ; SECOND BYTE IS REG VAL INC HL ; HL TO NEXT ENTRY CALL VGA_REGWR ; WRITE REGISTER VALUE JR VGA_CRTINIT1 ; LOOP ; VGA_RES: LD C,0 ; START WITH REG ZERO LD B,40 ; CLEAR 40 REGISTERS VGA_RES1: XOR A ; VALUE IS ZERO CALL VGA_REGWR ; SET VALUE INC C ; NEXT REGISTER DJNZ VGA_RES1 ; LOOP TILL DONE RET ; DONE ; VGA_CRTCDUMP: LD C,0 ; START WITH REG ZERO LD B,40 ; CLEAR 40 REGISTERS VGA_CRTCDUMP1: CALL VGA_REGRD ; SET VALUE CALL PRTHEXBYTE CALL PC_SPACE INC C ; NEXT REGISTER DJNZ VGA_CRTCDUMP1 ; LOOP TILL DONE RET ; DONE ; ;---------------------------------------------------------------------- ; LOAD FONT DATA ;---------------------------------------------------------------------- ; VGA_LOADFONT: LD HL,$7000 | VGA_89BIT ; CLEAR FONT PAGE NUM CALL VGA_SETCFG #IF USELZSA2 LD (VGA_STACK),SP ; SAVE STACK LD HL,(VGA_STACK) ; AND SHIFT IT LD DE,$2000 ; DOWN 4KB TO OR A ; CREATE A SBC HL,DE ; DECOMPRESSION BUFFER LD SP,HL ; HL POINTS TO BUFFER EX DE,HL ; START OF STACK BUFFER PUSH DE ; SAVE IT LD HL,VGA_FONT ; START OF FONT DATA CALL DLZSA2 ; DECOMPRESS TO DE POP HL ; RECALL STACK BUFFER POSITION #ELSE LD HL,VGA_FONT ; START OF FONT DATA #ENDIF LD DE,$7000 ; PAGE 7 OF VIDEO RAM VGA_LOADFONT1: LD B,VGA_SCANL ; # BYTES FOR EACH CHAR VGA_LOADFONT2: LD A,(HL) ; GET NEXT BYTE CALL VGA_MEMWR ; MEM(DE) := A INC HL ; NEXT FONT BYTE INC DE ; NEXT MEM BYTE DJNZ VGA_LOADFONT2 LD BC,16-VGA_SCANL ; MOVE TO NEXT EX DE,HL ; 16 BYTE ADD HL,BC ; CHARACTER EX DE,HL LD A,D CP $80 ; CHECK FOR END JR NZ,VGA_LOADFONT1 ; LOOP LD HL,$7070 | VGA_89BIT ; SET FONT PAGE NUM TO 7 CALL VGA_SETCFG #IF USELZSA2 LD HL,(VGA_STACK) ; ERASE DECOMPRESS BUFFER LD SP,HL ; BY RESTORING THE STACK RET ; DONE VGA_STACK .DW 0 #ELSE RET #ENDIF ; ;---------------------------------------------------------------------- ; SET CURSOR POSITION TO ROW IN D AND COLUMN IN E ;---------------------------------------------------------------------- ; VGA_XY: CALL VGA_XY2IDX ; CONVERT ROW/COL TO BUF IDX LD (VGA_POS),HL ; SAVE THE RESULT (DISPLAY POSITION) LD C,14 ; CURSOR POSITION REGISTER PAIR JP VGA_REGWRX ; DO IT AND RETURN ; ;---------------------------------------------------------------------- ; CONVERT XY COORDINATES IN DE INTO LINEAR INDEX IN HL ; D=ROW, E=COL ;---------------------------------------------------------------------- ; VGA_XY2IDX: LD A,E ; SAVE COLUMN NUMBER IN A LD H,D ; SET H TO ROW NUMBER LD E,VGA_COLS ; SET E TO ROW LENGTH CALL MULT8 ; MULTIPLY TO GET ROW OFFSET LD E,A ; GET COLUMN BACK ADD HL,DE ; ADD IT IN LD DE,(VGA_OFF) ; SCREEN OFFSET ADD HL,DE ; ADJUST ; PUSH HL ; SAVE IT LD DE,VGA_ROWS * VGA_COLS ; DE := BUF SIZE OR A ; CLEAR CARRY SBC HL,DE ; SUBTRACT FROM HL JR C,VGA_XY2IDX1 ; BYPASS IF NO WRAP POP DE ; THROW AWAY TOS RET ; DONE VGA_XY2IDX1: POP HL ; NO WRAP, RESTORE RET ; RETURN ; ;---------------------------------------------------------------------- ; WRITE VALUE IN A TO CURRENT VDU BUFFER POSITION, ADVANCE CURSOR ;---------------------------------------------------------------------- ; VGA_PUTCHAR: ; SETUP DE WITH BUFFER ADDRESS LD DE,(VGA_POS) ; GET CURRENT POSITION SLA E ; MULTIPLY BY 2 RL D ; ... 2 BYTES PER CHAR ; SETUP CHAR/ATTR IN HL LD H,A ; CHARACTER LD A,(VGA_ATTR) ; ATTRIBUTE LD L,A ; ... TO L ; WRITE CHAR & ATTR #IF (VGA_NICE) CALL VGA_WAITSB ; WAIT FOR RETRACE #ENDIF CALL VGA_MEMWRX ; UPDATE CURRENT POSITION LD HL,(VGA_POS) ; GET CURSOR POSITION INC HL ; INCREMENT ; PUSH HL ; SAVE IT LD DE,VGA_ROWS * VGA_COLS ; DE := BUF SIZE OR A ; CLEAR CARRY SBC HL,DE ; SUBTRACT FROM HL JR C,VGA_PUTCHAR1 ; BYPASS IF NO WRAP POP DE ; THROW AWAY TOS JR VGA_PUTCHAR2 ; CONTINUE VGA_PUTCHAR1: POP HL ; NO WRAP, RESTORE VGA_PUTCHAR2: LD (VGA_POS),HL ; SAVE NEW POSITION LD C,14 ; CURSOR POSITION REGISTER PAIR JP VGA_REGWRX ; DO IT AND RETURN ; ;---------------------------------------------------------------------- ; FILL AREA IN BUFFER WITH SPECIFIED CHARACTER AND CURRENT COLOR/ATTRIBUTE ; STARTING AT THE CURRENT FRAME BUFFER POSITION ; A: FILL CHARACTER ; DE: NUMBER OF CHARACTERS TO FILL ;---------------------------------------------------------------------- ; VGA_FILL: LD B,A ; CACHE FILL CHAR IN B ; SETUP HL WITH INITIAL BUFFER ADDRESS LD HL,(VGA_POS) ; GET CURRENT POSITION SLA L ; MULTIPLY BY 2 RL H ; ... 2 BYTES PER CHAR VGA_FILL1: ; FILL ONE POSITION (CHAR & ATTR) LD C,VGA_HI ; C := VGA ADR HI OUT (C),H ; SET HI ADDR INC C ; C := VGA ADR LO OUT (C),L ; SET LO ADDR INC C ; POINT TO DATA REG OUT (C),B ; OUTPUT FILL CHAR INC L ; BUMP ADDR (ONLY NEED TO DO LOW BYTE) DEC C ; C := VGA ADDR LO OUT (C),L ; UDPATE LO ADDR INC C ; POINT TO DATA REG #IF (VGA_NICE) CALL VGA_WAITSB ; WAIT FOR RETRACE #ENDIF LD A,(VGA_ATTR) ; GET CUR ATTR OUT (C),A ; OUTPUT ATTR ; CHECK COUNT DEC DE ; DECREMENT COUNT LD A,D ; TEST FOR OR E ; ... ZERO RET Z ; DONE IF SO ; BUMP BUFFER ADDRESS WITH POSSIBLE WRAP INC HL ; NEXT POSITION LD A,0 + (VGA_ROWS * VGA_COLS * 2) & $FF CP L ; TEST LOW BYTE JR NZ,VGA_FILL1 ; IF NOT EQ, NO WRAP, LOOP LD A,0 + ((VGA_ROWS * VGA_COLS * 2) >> 8) & $FF CP H ; TEST HI BYTE JR NZ,VGA_FILL1 ; IF NOT EQ, NO WRAP, LOOP LD HL,0 ; WRAP! JR VGA_FILL1 ; AND LOOP ; ;---------------------------------------------------------------------- ; SCROLL ENTIRE SCREEN FORWARD BY ONE LINE (CURSOR POSITION UNCHANGED) ;---------------------------------------------------------------------- ; VGA_SCROLL: ; CLEAR TOP LINE WHICH IS ABOUT TO BECOME NEW LINE ; AT BOTTOM OF SCREEN LD DE,(VGA_POS) ; GET CURRENT POS PUSH DE ; SAVE IT LD DE,(VGA_OFF) ; TOP OF SCREEN IS OFFSET VALUE LD (VGA_POS),DE ; SET POS LD DE,VGA_COLS ; CLEAR ONE ROW LD A,' ' ; WITH BLANKS CALL VGA_FILL ; DO IT POP DE ; GET ORIG POS VALUE BACK LD (VGA_POS),DE ; AND SAVE IT ; ; OFF += ROWLEN, IF OFF >= BUFSIZ, ADR := 0 LD HL,(VGA_OFF) ; CURRENT SCREEN OFFSET LD A,VGA_COLS ; ROW LENGTH CALL ADDHLA ; BUMP TO NEXT ROW PUSH HL ; SAVE IT LD DE,VGA_ROWS * VGA_COLS ; DE := BUF SIZE OR A ; CLEAR CARRY SBC HL,DE ; SUBTRACT FROM HL JR C,VGA_SCROLL1 LD HL,0 ; WRAP AROUND TO 0 POP DE ; THROW AWAY TOS JR VGA_SCROLL2 ; CONTINUE VGA_SCROLL1: POP HL ; NO WRAP, RESTORE VGA_SCROLL2: LD (VGA_OFF),HL ; SAVE IT CALL VGA_WAITSB LD C,12 ; SCREEN 1 ADDRESS CALL VGA_REGWRX ; COMMIT ; ; S2ROW--, IF S2ROW < 0, THEN S2ROW := MAXROW LD A,(VGA_S2ROW) ; CURRENT S2 ROW OR A ; = 0? JR Z,VGA_SCROLL3 ; IF 0, WRAP DEC A ; DECREMENT JR VGA_SCROLL4 ; AND CONTINUE VGA_SCROLL3: LD A,VGA_ROWS - 1 ; WRAP BACK TO MAX ROW VGA_SCROLL4: LD (VGA_S2ROW),A ; SAVE IT DEC A ; ADJUST LD C,18 ; S2 ROW REG CALL VGA_REGWR ; COMMIT ; ; POS += ROWLEN; IF POS >= BUFSIZ, POS -= BUFSIZ LD HL,(VGA_POS) ; CURRENT POSITION LD A,VGA_COLS ; ROW LENGTH CALL ADDHLA ; BUMP TO NEXT ROW PUSH HL ; SAVE IT LD DE,VGA_ROWS * VGA_COLS ; DE := BUF SIZE OR A ; CLEAR CARRY SBC HL,DE ; SUBTRACT FROM HL JR C,VGA_SCROLL5 ; BYPASS IF NO WRAP POP DE ; THROW AWAY TOS JR VGA_SCROLL6 ; CONTINUE VGA_SCROLL5: POP HL ; NO WRAP, RESTORE VGA_SCROLL6: LD (VGA_POS),HL ; SAVE IT LD C,14 ; CURSOR 1 POS REG CALL VGA_REGWRX ; COMMIT RET ; ;---------------------------------------------------------------------- ; REVERSE SCROLL ENTIRE SCREEN BY ONE LINE (CURSOR POSITION UNCHANGED) ;---------------------------------------------------------------------- ; VGA_RSCROLL: ; OFF -= ROWLEN, IF OFF < 0, OFF := MAXROW ((ROWS - 1) * COLS) LD HL,(VGA_OFF) ; CURRENT SCREEN OFFSET LD DE,VGA_COLS ; SUBTRACT ONE ROW SBC HL,DE ; DO IT JR NC,VGA_RSCROLL1 ; IF NOT NEGATIVE, CONTINUE LD HL,0 + ((VGA_ROWS - 1) * VGA_COLS) VGA_RSCROLL1: LD (VGA_OFF),HL ; SAVE IT CALL VGA_WAITSB ; WAIT FOR RETRACE LD C,12 ; SCREEN 1 ADDRESS CALL VGA_REGWRX ; COMMIT ; ; S2ROW++, IF S2ROW >= ROWS, THEN S2ROW := 0 LD A,(VGA_S2ROW) ; CURRENT S2 ROW INC A ; BUMP TO NEXT ROW CP VGA_ROWS ; COMPARE TO ROWS JR C,VGA_RSCROLL2 ; IF NOT >= ROWS, CONTINUE XOR A ; SET TO ZERO VGA_RSCROLL2: LD (VGA_S2ROW),A ; SAVE IT DEC A ; ADJUST LD C,18 ; S2 ROW REG CALL VGA_REGWR ; COMMIT ; ; POS -= ROWLEN; IF POS < 0, POS += BUFSIZ LD HL,(VGA_POS) ; CURRENT SCREEN OFFSET LD DE,VGA_COLS ; SUBTRACT ONE ROW OR A ; CLEAR CARRY SBC HL,DE ; DO IT JR NC,VGA_RSCROLL3 ; IF NOT NEGATIVE, CONTINUE LD DE,VGA_ROWS * VGA_COLS ; DE := BUF SIZE ADD HL,DE ; ADD TO HL VGA_RSCROLL3: LD (VGA_POS),HL ; SAVE IT LD C,14 ; CURSOR 1 POS REG CALL VGA_REGWRX ; COMMIT ; ; CLEAR TOP LINE JUST EXPOSED LD DE,(VGA_POS) ; GET CURRENT POS PUSH DE ; SAVE IT LD DE,(VGA_OFF) ; TOP OF SCREEN IS OFFSET VALUE LD (VGA_POS),DE ; SET POS LD DE,VGA_COLS ; CLEAR ONE ROW LD A,' ' ; WITH BLANKS CALL VGA_FILL ; DO IT POP DE ; GET ORIG POS VALUE BACK LD (VGA_POS),DE ; AND SAVE IT ; RET ; ;---------------------------------------------------------------------- ; BLOCK COPY BC BYTES FROM HL TO DE ;---------------------------------------------------------------------- ; VGA_BLKCPY: ; DOUBLE BC TO ACCOUNT FOR 2 BYTE ENTRIES (CHAR & ATTR) SLA C RL B PUSH BC ; COUNT ==> TOS ; ADJUST HL & DE FOR SCREEN OFFSET/WRAP CALL VGA_BLKCPY4 ; DO HL EX DE,HL ; SWAP CALL VGA_BLKCPY4 ; DO OTHER EX DE,HL ; SWAP BACK VGA_BLKCPY1: #IF (VGA_NICE) CALL VGA_WAITSB ; WAIT FOR RETRACE #ENDIF ; GET NEXT SOURCE BYTE LD C,VGA_HI ; C := VGA_HI OUT (C),H ; VGA_HI := SOURCE HI (H) INC C ; C := VGA_LO OUT (C),L ; VGA_LO := SOURCE LO (L) INC C ; C := VGA_DATA IN A,(C) ; A := (HL) ; COPY TO DESTINATION LD C,VGA_HI ; C := VGA_HI OUT (C),D ; VGA_HI := SOURCE HI (H) INC C ; C := VGA_LO OUT (C),E ; VGA_LO := SOURCE LO (L) INC C ; C := VGA_DATA OUT (C),A ; (DE) := A ; BUMP SOURCE ADDRESS WITH POSSIBLE WRAP INC HL ; NEXT POSITION LD A,0 + (VGA_ROWS * VGA_COLS * 2) & $FF CP L ; TEST LOW BYTE JR NZ,VGA_BLKCPY2 ; IF NOT EQ, NO WRAP, CONTINUE LD A,0 + ((VGA_ROWS * VGA_COLS * 2) >> 8) & $FF CP H ; TEST HI BYTE JR NZ,VGA_BLKCPY2 ; IF NOT EQ, NO WRAP, CONTINUE LD HL,0 ; WRAP! VGA_BLKCPY2: ; BUMP DEST ADDRESS WITH POSSIBLE WRAP INC DE ; NEXT POSITION LD A,0 + (VGA_ROWS * VGA_COLS * 2) & $FF CP E ; TEST LOW BYTE JR NZ,VGA_BLKCPY3 ; IF NOT EQ, NO WRAP, CONTINUE LD A,0 + ((VGA_ROWS * VGA_COLS * 2) >> 8) & $FF CP D ; TEST HI BYTE JR NZ,VGA_BLKCPY3 ; IF NOT EQ, NO WRAP, CONTINUE LD DE,0 ; WRAP! VGA_BLKCPY3: ; DECREMENT BYTE COUNT AND CHECK FOR COMPLETION EX (SP),HL ; GET COUNT, SAVE HL DEC HL ; DECREMENT LD A,H ; TEST FOR CP L ; ... ZERO EX (SP),HL ; COUNT BACK TO TOS, RESTORE HL JR NZ,VGA_BLKCPY1 ; LOOP IF NOT ZERO POP BC ; CLEAN UP STACK RET ; DONE ; VGA_BLKCPY4: ; SUBROUTINE TO ADJUST FOR SCREEN OFFSET/WRAP PUSH DE ; SAVE DE LD DE,(VGA_OFF) ; CUR SCRN OFFSET ;ADD HL,DE ; ADJUST FOR OFFSET SLA L ; MULTIPLY BY 2 RL H ; ... FOR TWO BYTES PER ENTRY PUSH HL ; SAVE IT LD DE,VGA_ROWS * VGA_COLS * 2 ; DE := BUF SIZE OR A ; CLEAR CARRY SBC HL,DE ; SUBTRACT FROM HL JR C,VGA_BLKCPY4A ; BYPASS IF NO WRAP POP DE ; THROW AWAY TOS JR VGA_BLKCPY4B ; CONTINUE VGA_BLKCPY4A: POP HL ; NO WRAP, RESTORE VGA_BLKCPY4B: POP DE ; RESTORE DE RET ; ;================================================================================================== ; VGA DRIVER - DATA ;================================================================================================== ; VGA_ATTR .DB 0 ; CURRENT COLOR VGA_POS .DW 0 ; CURRENT DISPLAY POSITION VGA_OFF .DW 0 ; SCREEN START OFFSET INTO CRTC RAM VGA_S2ROW .DB 0 ; CURRENT S2 ROW VGA_CFGV .DB 0 ; CURRENT BOARD CONFIG VALUE VGA_RUB .DB 0 ; REVERSE/UNDERLINE/BLINK (-----RUB) ; ; ATTRIBUTE ENCODING: ; BIT 7: ALT FONT ; BIT 6: BG REG ; BIT 5: BG GREEN ; BIT 4: BG BLUE ; BIT 3: FG INTENSITY ; BIT 2: FG RED ; BIT 1: FG GREEN ; BIT 0: FG BLUE ; #IF (VGASIZ=V80X25) ;=============================================================================== ; 80x25x8 70hz REGISTER VALUES ;=============================================================================== ; REGS_VGA: .DB 0,100 - 1 ; HORZ TOT - 1 .DB 1,VGA_COLS ; HORZ DISP .DB 2,VGA_COLS + 2 ; HORZ DISP + HORZ FP .DB 3,(2 << 4) | (12 & $0F) ; VERT SW, HORZ SW .DB 4,28 - 1 ; VERT TOT - 1 .DB 5,1 ; VERT TOT ADJ .DB 6,VGA_ROWS ; VERT DISP .DB 7,VGA_ROWS + 0 ; VERT DISP + VERT FP ROWS .DB 9,VGA_SCANL - 1 ; CHAR HEIGHT - 1 .DB 10,VGA_R10 ; CURSOR START & CURSOR BLINK .DB 11,VGA_R11 ; CURSOR END .DB 12,($0000 >> 8) & $FF ; SCRN 1 START (HI) .DB 13,($0000 & $FF) ; SCRN 1 START (LO) .DB 18,-1 ; S2 ROW - 1 .DB 27,12 ; VERT SYNC POS ADJ .DB 30,$01 | $08 ; CTL 1, 2 WINDOWS & ENABLE R27 VSYNC FINE ADJ .DB $FF ; END MARKER #ENDIF #IF (VGASIZ=V80X30) ;=============================================================================== ; 80x30x8 60hz REGISTER VALUES ;=============================================================================== ; REGS_VGA: .DB 0,100 - 1 ; HORZ TOT - 1 .DB 1,VGA_COLS ; HORZ DISP .DB 2,VGA_COLS + 2 ; HORZ DISP + HORZ FP .DB 3,44 ; VERT SW, HORZ SW .DB 4,33 - 1 ; VERT TOT - 1 .DB 5,13 ; VERT TOT ADJ .DB 6,VGA_ROWS ; VERT DISP .DB 7,VGA_ROWS + 0 ; VERT DISP + VERT FP ROWS .DB 9,VGA_SCANL - 1 ; CHAR HEIGHT - 1 .DB 10,VGA_R10 ; CURSOR START & CURSOR BLINK .DB 11,VGA_R11 ; CURSOR END .DB 12,0 ; SCRN 1 START (HI) .DB 13,0 ; SCRN 1 START (LO) .DB 18,-1 ; S2 ROW - 1 .DB 27,0 ; VERT SYNC POS ADJ .DB 30,$01 | $08 ; CTL 1, 2 WINDOWS & ENABLE R27 VSYNC FINE ADJ .DB $FF ; END MARKER #ENDIF #IF (VGASIZ=V80X43) ;=============================================================================== ; 80x43x8 60hz REGISTER VALUES ;=============================================================================== ; REGS_VGA: .DB 0,100 - 1 ; HORZ TOT - 1 .DB 1,VGA_COLS ; HORZ DISP .DB 2,VGA_COLS + 2 ; HORZ DISP + HORZ FP .DB 3,44 ; VERT SW, HORZ SW .DB 4,47 - 1 ; VERT TOT - 1 .DB 5,8 ; VERT TOT ADJ .DB 6,VGA_ROWS ; VERT DISP .DB 7,VGA_ROWS + 0 ; VERT DISP + VERT FP ROWS .DB 9,VGA_SCANL - 1 ; CHAR HEIGHT - 1 .DB 10,VGA_R10 ; CURSOR START & CURSOR BLINK .DB 11,VGA_R11 ; CURSOR END .DB 12,0 ; SCRN 1 START (HI) .DB 13,0 ; SCRN 1 START (LO) .DB 18,-1 ; S2 ROW - 1 .DB 27,0 ; VERT SYNC POS ADJ .DB 30,$01 | $08 ; CTL 1, 2 WINDOWS & ENABLE R27 VSYNC FINE ADJ .DB $FF ; END MARKER #ENDIF #IF (VGASIZ=V80X60) ;=============================================================================== ; 80x60X8 60hz REGISTER VALUES ;=============================================================================== ; REGS_VGA: .DB 0,100 - 1 ; HORZ TOT - 1 .DB 1,VGA_COLS ; HORZ DISP .DB 2,VGA_COLS + 2 ; HORZ DISP + HORZ FP .DB 3,44 ; VERT SW, HORZ SW .DB 4,66 - 1 ; VERT TOT - 1 .DB 5,0 ; VERT TOT ADJ .DB 6,VGA_ROWS ; VERT DISP .DB 7,VGA_ROWS + 0 ; VERT DISP + VERT FP ROWS .DB 9,VGA_SCANL - 1 ; CHAR HEIGHT - 1 .DB 10,VGA_R10 ; CURSOR START & CURSOR BLINK .DB 11,VGA_R11 ; CURSOR END .DB 12,0 ; SCRN 1 START (HI) .DB 13,0 ; SCRN 1 START (LO) .DB 18,-1 ; S2 ROW - 1 .DB 27,0 ; VERT SYNC POS ADJ .DB 30,$01 | $08 ; CTL 1, 2 WINDOWS & ENABLE R27 VSYNC FINE ADJ .DB $FF ; END MARKER #ENDIF ;================================================================================================== ; VGA DRIVER - INSTANCE DATA ;================================================================================================== ; VGA_IDAT: .DB VGA_KBDST .DB VGA_KBDDATA