;====================================================================== ; VDU DRIVER FOR SBC PROJECT ; ; ORIGINALLY WRITTEN BY: ANDREW LYNCH ; REVISED/ENHANCED BY DAN WERNER -- 11/7/2009 ; ROMWBW ADAPTATION BY: WAYNE WARTHEN -- 11/9/2012 ;====================================================================== ; ; TODO: ; - ADD REMAINING REGISTERS TO INIT ; - TRY 25 ROW MODE? ; - IMPLEMENT CONSTANTS FOR SCREEN DIMENSIONS ; - IMPLEMENT SET CURSOR STYLE (VDASCS) FUNCTION ; - IMPLEMENT ALTERNATE DISPLAY MODES? ; ;====================================================================== ; VDU DRIVER - CONSTANTS ;====================================================================== ; VDU_BASE .EQU $F0 ; VDU_RAMRD .EQU VDU_BASE + $00 ; READ VDU VDU_RAMWR .EQU VDU_BASE + $01 ; WRITE VDU VDU_STAT .EQU VDU_BASE + $02 ; VDU STATUS/REGISTER VDU_REG .EQU VDU_BASE + $02 ; VDU STATUS/REGISTER VDU_DATA .EQU VDU_BASE + $03 ; VDU DATA REGISTER VDU_PPIA .EQU VDU_BASE + $04 ; PPI PORT A VDU_PPIB .EQU VDU_BASE + $05 ; PPI PORT B VDU_PPIC .EQU VDU_BASE + $06 ; PPI PORT C VDU_PPIX .EQU VDU_BASE + $07 ; PPI CONTROL PORT ; TERMENABLE .SET TRUE ; INCLUDE TERMINAL PSEUDODEVICE DRIVER ; ;====================================================================== ; VDU DRIVER - INITIALIZATION ;====================================================================== ; VDU_INIT: LD IY,VDU_IDAT ; POINTER TO INSTANCE DATA ; CALL NEWLINE ; FORMATTING PRTS("VDU: IO=0x$") LD A,VDU_RAMRD CALL PRTHEXBYTE CALL VDU_PROBE ; CHECK FOR HW EXISTENCE JR Z,VDU_INIT1 ; CONTINUE IF HW PRESENT ; ; HARDWARE NOT PRESENT PRTS(" NOT PRESENT$") OR $FF ; SIGNAL FAILURE RET ; VDU_INIT1: CALL VDU_CRTINIT ; INIT SY6845 VDU CHIP CALL VDU_VDARES CALL PPK_INIT ; INITIALIZE KEYBOARD DRIVER ; ; ADD OURSELVES TO VDA DISPATCH TABLE LD BC,VDU_FNTBL ; BC := FUNCTION TABLE ADDRESS LD DE,VDU_IDAT ; DE := VDU INSTANCE DATA PTR CALL VDA_ADDENT ; ADD ENTRY, A := UNIT ASSIGNED ; ; INITIALIZE EMULATION LD C,A ; ASSIGNED VIDEO UNIT IN C LD DE,VDU_FNTBL ; DE := FUNCTION TABLE ADDRESS LD HL,VDU_IDAT ; HL := VDU INSTANCE DATA PTR CALL TERM_ATTACH ; DO IT XOR A ; SIGNAL SUCCESS RET ; ;====================================================================== ; VDU DRIVER - VIDEO DISPLAY ADAPTER (VDA) FUNCTIONS ;====================================================================== ; VDU_FNTBL: .DW VDU_VDAINI .DW VDU_VDAQRY .DW VDU_VDARES .DW VDU_VDADEV .DW VDU_VDASCS .DW VDU_VDASCP .DW VDU_VDASAT .DW VDU_VDASCO .DW VDU_VDAWRC .DW VDU_VDAFIL .DW VDU_VDACPY .DW VDU_VDASCR .DW PPK_STAT .DW PPK_FLUSH .DW PPK_READ #IF (($ - VDU_FNTBL) != (VDA_FNCNT * 2)) .ECHO "*** INVALID VDU FUNCTION TABLE ***\n" #ENDIF VDU_VDAINI: ; RESET VDA ; CURRENTLY IGNORES VIDEO MODE AND BITMAP DATA CALL VDU_VDARES ; RESET VDA XOR A ; SIGNAL SUCCESS RET VDU_VDAQRY: LD C,$00 ; MODE ZERO IS ALL WE KNOW LD DE,$1850 ; 24 ROWS ($18), 80 COLS ($50) LD HL,0 ; EXTRACTION OF CURRENT BITMAP DATA NOT SUPPORTED XOR A ; SIGNAL SUCCESS RET VDU_VDARES: LD DE,0 LD (VDU_OFFSET),DE CALL VDU_XY LD A,' ' LD DE,1024*16 CALL VDU_FILL XOR A RET VDU_VDADEV: LD D,VDADEV_VDU ; D := DEVICE TYPE LD E,0 ; E := PHYSICAL UNIT IS ALWAYS ZERO XOR A ; SIGNAL SUCCESS RET VDU_VDASCS: CALL PANIC ; NOT IMPLEMENTED (YET) VDU_VDASCP: CALL VDU_XY XOR A RET VDU_VDASAT: XOR A RET VDU_VDASCO: XOR A RET VDU_VDAWRC: LD A,E CALL VDU_PUTCHAR XOR A RET VDU_VDAFIL: LD A,E ; FILL CHARACTER GOES IN A EX DE,HL ; FILL LENGTH GOES IN DE CALL VDU_FILL ; DO THE FILL XOR A ; SIGNAL SUCCESS RET VDU_VDACPY: ; LENGTH IN HL, SOURCE ROW/COL IN DE, DEST IS VDU_POS PUSH HL ; SAVE LENGTH CALL VDU_XY2IDX ; ROW/COL IN DE -> SOURCE ADR IN HL POP BC ; RECOVER LENGTH IN BC LD DE,(VDU_POS) ; PUT DEST IN DE JP VDU_BLKCPY ; DO A BLOCK COPY RET VDU_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,VDU_VDASCR1 ; E IS NEGATIVE, REVERSE SCROLL CALL VDU_SCROLL ; SCROLL FORWARD ONE LINE POP DE ; RECOVER E DEC E ; DECREMENT IT JR VDU_VDASCR ; LOOP VDU_VDASCR1: CALL VDU_RSCROLL ; SCROLL REVERSE ONE LINE POP DE ; RECOVER E INC E ; INCREMENT IT JR VDU_VDASCR ; LOOP ; ;====================================================================== ; VDU DRIVER - PRIVATE DRIVER FUNCTIONS ;====================================================================== ; ;---------------------------------------------------------------------- ; WAIT FOR VDU TO BE READY FOR A DATA READ/WRITE ;---------------------------------------------------------------------- ; VDU_WAITRDY: IN A,(VDU_STAT) ; READ STATUS OR A ; SET FLAGS RET M ; IF BIT 7 SET, THEN READY! JR VDU_WAITRDY ; KEEP CHECKING ; ;---------------------------------------------------------------------- ; UPDATE SY6845 REGISTERS ; VDU_WRREG WRITES VALUE IN A TO VDU REGISTER SPECIFIED IN C ; VDU_WRREGX WRITES VALUE IN DE TO VDU REGISTER PAIR IN C, C+1 ;---------------------------------------------------------------------- ; VDU_WRREG: PUSH AF ; SAVE VALUE TO WRITE LD A,C ; SET A TO VDU REGISTER TO SELECT OUT (VDU_REG),A ; WRITE IT TO SELECT THE REGISTER POP AF ; RECOVER VALUE TO WRITE OUT (VDU_DATA),A ; WRITE IT RET ; VDU_WRREGX: LD A,H ; SETUP MSB TO WRITE CALL VDU_WRREG ; DO IT INC C ; NEXT VDU REGISTER LD A,L ; SETUP LSB TO WRITE JR VDU_WRREG ; DO IT & RETURN ; ;---------------------------------------------------------------------- ; READ SY6845 REGISTERS ; VDU_RDREG READS VDU REGISTER SPECIFIED IN C AND RETURNS VALUE IN A ; VDU_RDREGX READS VDU REGISTER PAIR SPECIFIED BY C, C+1 ; AND RETURNS VALUE IN HL ;---------------------------------------------------------------------- ; VDU_RDREG: LD A,C ; SET A TO VDU REGISTER TO SELECT OUT (VDU_REG),A ; WRITE IT TO SELECT THE REGISTER IN A,(VDU_DATA) ; READ IT RET ; VDU_RDREGX: CALL VDU_RDREG ; GET VALUE FROM REGISTER IN C LD H,A ; SAVE IN H INC C ; BUMP TO NEXT REGISTER OF PAIR CALL VDU_RDREG ; READ THE VALUE LD L,A ; SAVE IT IN L RET ; ;---------------------------------------------------------------------- ; PROBE FOR VDU HARDWARE ;---------------------------------------------------------------------- ; ; ON RETURN, ZF SET INDICATES HARDWARE FOUND ; VDU_PROBE: ; WRITE TEST PATTERN $A5 $5A TO VRAM ADDRESS POINTER LD HL,$A55A ; POINT TO FIRST BYTE OF VRAM LD C,14 ; ADDRESS REGISTER PAIR CALL VDU_WRREGX ; UPDATE VRAM ADDRESS POINTER LD C,14 ; ADDRESS REGISTER PAIR CALL VDU_RDREGX ; READ IT BACK ; TOP TWO BITS ARE ZEROED IN COMPARE BECAUSE THE CRTC ; STORES ONLY A 14 BIT VALUE FOR REGISTER PAIR 14/15 LD A,$A5 & $3F ; FIRST BYTE TEST VALUE CP H ; COMPARE RET NZ ; ABORT IF NOT EQUAL LD A,$5A ; SECOND BYTE TEST VALUE CP L ; COMPARE RET ; RETURN WITH COMPARE RESULTS ; ;---------------------------------------------------------------------- ; SY6845 DISPLAY CONTROLLER CHIP INITIALIZATION ;---------------------------------------------------------------------- ; VDU_CRTINIT: LD C,0 ; START WITH REGISTER 0 LD B,16 ; INIT 16 REGISTERS LD HL,VDU_INIT6845 ; HL = POINTER TO THE DEFAULT VALUES VDU_CRTINIT1: LD A,(HL) ; GET VALUE CALL VDU_WRREG ; WRITE IT INC HL ; POINT TO NEXT VALUE INC C ; POINT TO NEXT REGISTER DJNZ VDU_CRTINIT1 ; LOOP RET ; ;---------------------------------------------------------------------- ; CONVERT XY COORDINATES IN DE INTO LINEAR INDEX IN HL ; D=ROW, E=COL ;---------------------------------------------------------------------- ; VDU_XY2IDX: LD A,E ; SAVE COLUMN NUMBER IN A LD H,D ; SET H TO ROW NUMBER LD E,80 ; SET E TO ROW LENGTH CALL MULT8 ; MULTIPLY TO GET ROW OFFSET LD E,A ; GET COLUMN BACK ADD HL,DE ; ADD IT IN RET ; RETURN ; ;---------------------------------------------------------------------- ; SET CURSOR POSITION TO ROW IN D AND COLUMN IN E ;---------------------------------------------------------------------- ; VDU_XY: LD A,E ; SAVE COLUMN NUMBER IN A LD H,D ; SET H TO ROW NUMBER LD E,80 ; 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 (VDU_POS),HL ; SAVE THE RESULT (DISPLAY POSITION) LD DE,(VDU_OFFSET) ; NOW GET THE BUFFER OFFSET ADD HL,DE ; AND ADD THAT IN LD C,14 ; CURSOR POSITION REGISTER PAIR JP VDU_WRREGX ; DO IT AND RETURN ; ;---------------------------------------------------------------------- ; WRITE VALULE IN A TO CURRENT VDU BUFFER POSTION, ADVANCE CURSOR ;---------------------------------------------------------------------- ; VDU_PUTCHAR: LD B,A ; SAVE THE CHARACTER ; SET BUFFER WRITE POSITION LD HL,(VDU_OFFSET) LD DE,(VDU_POS) ADD HL,DE INC DE ; INC LD (VDU_POS),DE ; SAVE NEW SCREEN POSITION LD C,18 ; UPDATE ADDRESS REGISTER PAIR CALL VDU_WRREGX ; DO IT INC HL ; NEW CURSOR POSITION LD C,14 ; CURSOR POSITION REGISTER PAIR CALL VDU_WRREGX ; DO IT LD A,31 ; PREP VDU FOR DATA R/W OUT (VDU_REG),A CALL VDU_WAITRDY ; WAIT FOR VDU TO BE READY LD A,B OUT (VDU_RAMWR),A ; OUTPUT CHAR TO VDU RET ; ;---------------------------------------------------------------------- ; 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 ;---------------------------------------------------------------------- ; VDU_FILL: LD B,A ; SAVE THE FILL CHARACTER ; SET FILL START POSITION PUSH DE LD HL,(VDU_OFFSET) LD DE,(VDU_POS) ADD HL,DE LD C,18 CALL VDU_WRREGX POP DE ; FILL LOOP LD A,31 ; PREP VDU FOR DATA R/W OUT (VDU_REG),A VDU_FILL1: LD A,D ; CHECK NUMBER OF FILL CHARS LEFT OR E RET Z ; ALL DONE, RETURN CALL VDU_WAITRDY ; WAIT FOR VDU TO BE READY LD A,B OUT (VDU_RAMWR),A ; OUTPUT CHAR TO VDU DEC DE ; DECREMENT COUNT JR VDU_FILL1 ; LOOP ; ;---------------------------------------------------------------------- ; COPY A BLOCK (UP TO 255 BYTES) FROM HL TO FRAME BUFFER POSITION ; BC: NUMBER OF BYTES TO COPY ; HL: SOURCE POSITION ; DE: DESTINATION POSITION ;---------------------------------------------------------------------- ; VDU_BLKCPY: ; SETUP TO COPY FROM VDU SOURCE TO WORK BUFFER PUSH DE ; SAVE VDU DESTINATION ADR PUSH HL ; SAVE VDU SOURCE ADDRESS LD HL,(VDU_OFFSET) ; GET THE CURRENT OFFSET POP DE ; RECOVER SOURCE ADDRESS ADD HL,DE ; HL HAS TRUE SOURCE ADR OF VIDEO BUF W/ OFFSET LD DE,VDU_BUF ; POINT DE TO WORK BUFFER PUSH BC ; SAVE COPY LENGTH FOR LATER LD B,C ; NOW USE B FOR LENGTH (MAX COPY IS 255) LD C,18 ; SET SOURCE ADDRESS IN VDU (HL) CALL VDU_WRREGX ; DO IT LD A,31 ; PREP VDU FOR DATA R/W OUT (VDU_REG),A ; DO IT LD HL,VDU_BUF ; HL POINTS TO WORK BUFFER ; LD C,VDU_RAMWR ; LOAD C WITH VDU WRITE REGISTER VDU_BLKCPY1: ; VIDEO RAM -> BUFFER COPY LOOP CALL VDU_WAITRDY ; WAIT FOR VDU ;;;;;;;;;;;;;;;;; ; INI IS NOT WORKING FOR ME, GARBAGE DATA READS, NO IDEA WHY ; INI ; READ BYTE, DEC B, INC HL ; IN A,(VDU_DATA) ; BOGUS READ TO INCREMENT VDU RAM ADDRESS!!! ; JR NZ,VDU_BLKCPY1 ; LOOP TILL DONE ;;;;;;;;;;;;;;;;; IN A,(VDU_RAMRD) ; READ DATA BYTE LD (HL),A ; SAVE IN BUFFER INC HL ; BUMP SOURCE ADDRESS IN A,(VDU_DATA) ; BOGUS READ TO INCREMENT VDU RAM ADDRESS!!! DJNZ VDU_BLKCPY1 ; LOOP TILL DONE ; SETUP TO COPY FROM WORK BUFFER TO VDU DEST POP BC ; RECOVER THE COPY LENGTH LD HL,(VDU_OFFSET) ; GET THE CURRENT VDU OFFSET POP DE ; RECOVER THE DEST ADDRESS ADD HL,DE ; HL HAS TRUE DEST ADR OF VIDEO BUF W/ OFFSET LD B,C ; NOW USE B FOR LENGTH (MAX COPY IS 255) LD C,18 ; SET DEST ADDRESS IN VDU (HL) CALL VDU_WRREGX ; DO IT LD A,31 ; PREP VDU FOR DATA R/W OUT (VDU_REG),A ; DO IT LD HL,VDU_BUF ; HL POINTS TO WORK BUFFER LD C,VDU_RAMWR ; LOAD C WITH VDU WRITE REGISTER VDU_BLKCPY2: ; BUFFER -> VIDEO RAM COPY LOOP CALL VDU_WAITRDY ; WAIT FOR VDU OUTI ; WRITE BYTE, DEC B, INC HL JR NZ,VDU_BLKCPY2 ; LOOP TILL DONE ; RET ; RETURN ; ;---------------------------------------------------------------------- ; SCROLL ENTIRE SCREEN FORWARD BY ONE LINE (CURSOR POSITION UNCHANGED) ;---------------------------------------------------------------------- ; VDU_SCROLL: ; SCROLL FORWARD BY ADDING ONE ROW TO DISPLAY START ADDRESS LD HL,(VDU_OFFSET) LD DE,80 ADD HL,DE LD (VDU_OFFSET),HL LD C,12 CALL VDU_WRREGX ; FILL EXPOSED LINE LD HL,(VDU_POS) PUSH HL LD HL,23*80 LD (VDU_POS),HL LD DE,80 LD A,' ' CALL VDU_FILL POP HL LD (VDU_POS),HL ; ADJUST CURSOR POSITION AND RETURN LD HL,(VDU_OFFSET) LD DE,(VDU_POS) ADD HL,DE LD C,14 JP VDU_WRREGX ; ;---------------------------------------------------------------------- ; REVERSE SCROLL ENTIRE SCREEN BY ONE LINE (CURSOR POSITION UNCHANGED) ;---------------------------------------------------------------------- ; VDU_RSCROLL: ; SCROLL BACKWARD BY SUBTRACTING ONE ROW FROM DISPLAY START ADDRESS LD HL,(VDU_OFFSET) LD DE,-80 ADD HL,DE LD (VDU_OFFSET),HL LD C,12 CALL VDU_WRREGX ; FILL EXPOSED LINE LD HL,(VDU_POS) PUSH HL LD HL,0 LD (VDU_POS),HL LD DE,80 LD A,' ' CALL VDU_FILL POP HL LD (VDU_POS),HL ; ADJUST CURSOR POSITION AND RETURN LD HL,(VDU_OFFSET) LD DE,(VDU_POS) ADD HL,DE LD C,14 JP VDU_WRREGX ; ;================================================================================================== ; VDU DRIVER - DATA ;================================================================================================== ; VDU_POS .DW 0 ; CURRENT DISPLAY POSITION VDU_OFFSET .DW 0 ; CURRENT DISPLAY POSITION VDU_BUF .FILL 256,0 ; COPY BUFFER ; ;================================================================================================== ; VDU DRIVER - SY6845 REGISTER INITIALIZATION ;================================================================================================== ; VDU_INIT6845: ; DB 07FH, 50H, 60H, 7CH, 19H, 1FH, 19H, 1AH, 78H, 09H, 60H, 09H, 00H, 00H, 00H, 00H ; ; CCIR 625/50 VERSION (USED IN MOST OF THE WORLD) ; JUMPER K1 2-3, K2 1-2 FOR 2MHz CHAR CLOCK .DB 07FH ; R0 TOTAL NUMBER OF HORIZONTAL CHARACTERS (DETERMINES HSYNC) .DB 050H ; R1 NUMBER OF HORIZONTAL CHARACTERS DISPLAYED (80 COLUMNS) .DB 060H ; R2 HORIZONTAL SYNC POSITION .DB 00CH ; R3 SYNC WIDTHS .DB 01EH ; R4 VERTICAL TOTAL (TOTAL CHARS IN A FRAME -1) .DB 002H ; R5 VERTICAL TOTAL ADJUST ( .DB 018H ; R6 VERTICAL DISPLAYED (24 ROWS) .DB 01AH ; R7 VERTICAL SYNC .DB 078H ; R8 MODE B7=0 TRANSPARENT UPDATE DURING BLANKING ; B6=1 PIN 34 IS UPDATE STROBE ; B5=1 DELAY CURSOR 1 CHARACTER ; B4=1 DELAY DISPLAY ENABLE 1 CHARACTER ; B3=1 TRANSPARENT MEMORY ADDRESSING ; B2=0 RAM STRAIGHT BINARY ADDRESSING ; B1,B0=0 NON-INTERLACE .DB 009H ; R9 SCAN LINE (LINES PER CHAR AND SPACING -1) .DB 060H ; R10 CURSOR START RASTER .DB 009H ; R11 CURSOR END RASTER .DB 00H ; R12 START ADDRESS HI .DB 00H ; R13 START ADDRESS LO .DB 00H ; R14 CURSOR ADDRESS HI .DB 00H ; R15 CURSOR ADDRESS LO ; ; THE CCIR 625/50 TELEVISION STANDARD HAS 625 LINES INTERLACED AT 50 FIELDS PER SECOND. THIS WORKS ; OUT AS 50 FIELDS OF 312.5 LINES PER SECOND NON-INTERLACED AS USED HERE. ; HORIZONTAL LINE WIDTH IS 64uS. FOR A 2 MHz CHARACTER CLOCK (R0+1)/2000000 = 64uS ; NEAREST NUMBER OF LINES IS 312 = (R4+1) * (R9+1) + R5. ; 15625 / 312 = 50.08 FIELDS PER SECOND (NEAR ENOUGH-DGG) ; ; ;================================================================================================== ; VDU DRIVER - INSTANCE DATA ;================================================================================================== ; VDU_IDAT: .DB VDU_PPIA .DB VDU_PPIB .DB VDU_PPIC .DB VDU_PPIX