;====================================================================== ; XOSERA DRIVER FOR RCBUS XOSERA CARD ; ; WRITTEN BY: ROB GOWIN -- 01/19/2025 ;====================================================================== ; ; TODO: ; * LOOK AT FONT STUFF IN MORE DETAIL ; ;====================================================================== ; XOSERA DRIVER - CONSTANTS ;====================================================================== ; ;XOS_BASE .EQU $E0 ; DEVECHO "XOSERA: " DEVECHO "IO=" DEVECHO XOS_BASE DEVECHO "\n" ; #IF (XOSSIZ=V80X30) XOS_ROWS_CONST .EQU 30 XOS_COLS_CONST .EQU 80 #ENDIF #IF (XOSSIZ=V80X60) XOS_ROWS_CONST .EQU 60 XOS_COLS_CONST .EQU 80 #ENDIF ; XOSERA MAIN REGISTER ZXM_SYS_CTRL .EQU XOS_BASE ZXM_INT_CTRL .EQU XOS_BASE+2 ZXM_TIMER .EQU XOS_BASE+4 ZXM_RD_XADDR .EQU XOS_BASE+6 ZXM_WR_XADDR .EQU XOS_BASE+8 ZXM_XDATA .EQU XOS_BASE+10 ZXM_RD_INCR .EQU XOS_BASE+12 ZXM_RD_ADDR .EQU XOS_BASE+14 ZXM_WR_INCR .EQU XOS_BASE+16 ZXM_WR_ADDR .EQU XOS_BASE+18 ZXM_DATA .EQU XOS_BASE+20 ZXM_DATA_2 .EQU XOS_BASE+22 ZXM_PIXEL_X .EQU XOS_BASE+24 ZXM_PIXEL_Y .EQU XOS_BASE+26 ZXM_UART .EQU XOS_BASE+28 ZXM_FEATURE .EQU XOS_BASE+30 ; See: https:;github.com/XarkLabs/Xosera/blob/master/REFERENCE.md ; Xosera XR Memory Regions (size in 16-bit words) XR_CONFIG_REGS .EQU $0000 ; (R/W) 0x0000-0x0007 8 config/ctrl registers XR_PA_REGS .EQU $0010 ; (R/W) 0x0010-0x0017 8 playfield A video registers XR_PB_REGS .EQU $0018 ; (R/W) 0x0018-0x001F 8 playfield B video registers XR_AUDIO_REGS .EQU $0020 ; (-/W) 0x0020-0x002F 16 audio playback registers XR_BLIT_REGS .EQU $0040 ; (-/W) 0x0040-0x0049 10 blitter registers XR_TILE_ADDR .EQU $4000 ; (R/W) 0x4000-0x53FF tile glyph/tile map memory XR_TILE_SIZE .EQU $1400 ; 4096+1024 x 16-bit tile glyph/tile map memory XR_COLOR_ADDR .EQU $8000 ; (R/W) 0x8000-0x81FF 2 x A & B color lookup memory XR_COLOR_SIZE .EQU $0200 ; 2 x 256 x 16-bit words (0xARGB) XR_COLOR_A_ADDR .EQU $8000 ; (R/W) 0x8000-0x80FF A 256 entry color lookup memory XR_COLOR_A_SIZE .EQU $0100 ; 256 x 16-bit words (0xARGB) XR_COLOR_B_ADDR .EQU $8100 ; (R/W) 0x8100-0x81FF B 256 entry color lookup memory XR_COLOR_B_SIZE .EQU $0100 ; 256 x 16-bit words (0xARGB) XR_POINTER_ADDR .EQU $8200 ; (-/W) 0x8200-0x82FF 256 word 32x32 4-bpp pointer image XR_POINTER_SIZE .EQU $0100 ; 256 x 16-bit words (4-bit pixels) XR_COPPER_ADDR .EQU $C000 ; (R/W) 0xC000-0xC5FF copper memory (16-bit words) XR_COPPER_SIZE .EQU $0600 ; 1024+512 x 16-bit copper memory words ; XR Extended Register / Region (accessed via XM_RD_XADDR/XM_WR_XADDR and XM_XDATA) ; Video Config and Copper XR Registers XR_VID_CTRL .EQU $00 ; (R /W) display control and border color index XR_COPP_CTRL .EQU $01 ; (R /W) display synchronized coprocessor control XR_AUD_CTRL .EQU $02 ; (- /-) TODO: audio channel control XR_SCANLINE .EQU $03 ; (R /W) read scanline (incl. offscreen), write signal video interrupt XR_VID_LEFT .EQU $04 ; (R /W) left edge of active display window (typically 0) XR_VID_RIGHT .EQU $05 ; (R /W) right edge of active display window +1 (typically 640 or 848) XR_POINTER_H .EQU $06 ; (- /W) pointer sprite raw H position XR_POINTER_V .EQU $07 ; (- /W) pointer sprite raw V position / pointer color select ; Playfield A Control XR Registers XR_PA_GFX_CTRL .EQU $10 ; (R /W ) playfield A graphics control XR_PA_TILE_CTRL .EQU $11 ; (R /W ) playfield A tile control XR_PA_DISP_ADDR .EQU $12 ; (R /W ) playfield A display VRAM start address XR_PA_LINE_LEN .EQU $13 ; (R /W ) playfield A display line width in words XR_PA_HV_FSCALE .EQU $14 ; (R /W ) playfield A horizontal and vertical fractional scale XR_PA_H_SCROLL .EQU $15 ; (R /W ) playfield A horizontal and vertical fine scroll XR_PA_V_SCROLL .EQU $16 ; (R /W ) playfield A horizontal and vertical fine scroll XR_PA_LINE_ADDR .EQU $17 ; (- /W ) playfield A scanline start address (loaded at start of line) ; Playfield B Control XR Registers XR_PB_GFX_CTRL .EQU $18 ; (R /W ) playfield B graphics control XR_PB_TILE_CTRL .EQU $19 ; (R /W ) playfield B tile control XR_PB_DISP_ADDR .EQU $1A ; (R /W ) playfield B display VRAM start address XR_PB_LINE_LEN .EQU $1B ; (R /W ) playfield B display line width in words XR_PB_HV_FSCALE .EQU $1C ; (R /W ) playfield B horizontal and vertical fractional scale XR_PB_H_SCROLL .EQU $1D ; (R /W ) playfield B horizontal and vertical fine scroll XR_PB_V_SCROLL .EQU $1E ; (R /W ) playfield B horizontal and vertical fine scroll XR_PB_LINE_ADDR .EQU $1F ; (- /W ) playfield B scanline start address (loaded at start of line) ; Default 1-bpp font glyphs in TILE memory (total 0x1400 words) FONT_ST_8x16_ADDR .EQU XR_TILE_ADDR + $0000 FONT_ST_8x16_SIZE .EQU $800 FONT_ST_8x8_ADDR .EQU XR_TILE_ADDR + $0800 FONT_ST_8x8_SIZE .EQU $400 FONT_PC_8x8_ADDR .EQU XR_TILE_ADDR + $0C00 FONT_PC_8x8_SIZE .EQU $400 FONT_HEX_8x8_ADDR .EQU XR_TILE_ADDR + $1000 FONT_HEX_8x8_SIZE .EQU $400 TILE_CTRL_TILE_VRAM_F .EQU $0100 TILE_CTRL_DISP_TILEMEM_F .EQU $0200 ; GET XOSERA MAIN REGISTER REG_NUM VALUE INTO DE. #DEFINE XM_GETW(REG_NUM) \ #defcont \ LD C,REG_NUM #defcont \ IN D,(C) #defcont \ INC C #defcont \ IN E,(C) ; SET XOSERA MAIN REGISTER REG_NUM TO VALUE IN DE. #DEFINE XM_SETW(REG_NUM) \ #defcont \ LD C,REG_NUM #defcont \ OUT (C),D #defcont \ INC C #defcont \ OUT (C),E ; SET LOW BYTE OF XOERA MAIN REGISTER REG_NUM TO VALUE IN A. #DEFINE XM_SETBL(REG_NUM) \ #defcont \ LD C,REG_NUM+1 #defcont \ OUT (C),A SYNC_RETRIES .EQU 250 TERMENABLE .SET TRUE ; INCLUDE TERMINAL PSEUDODEVICE DRIVER ;====================================================================== ; XOSERA DRIVER - INITIALIZATION ;====================================================================== ; XOS_PREINIT: LD IY,XOS_IDAT ; POINTER TO INSTANCE DATA ; JP KBD_PREINIT ; INITIALIZE KEYBOARD RET ; XOS_INIT: LD IY,XOS_IDAT ; POINTER TO INSTANCE DATA ; CALL NEWLINE ; FORMATTING PRTS("XOSERA: IO=0x$") LD A,XOS_BASE CALL PRTHEXBYTE CALL XOS_PROBE ; CHECK FOR HW PRESENCE JR Z,XOS_INIT1 ; CONTINUE IF HW PRESENT ; ; HARDWARE NOT PRESENT PRTS(" NOT PRESENT$") OR $FF RET ; XOS_INIT1: ; RETRIEVE SOME CONFIGURATION INFO FROM XOSERA AND PRINT IT LD A,' ' CALL COUT CALL XOS_SHOW_INFO CALL XOS_VDAINI ; ADD OURSELVES TO VDA DISPATCH TABLE LD BC,XOS_FNTBL ; BC := FUNCTION TABLE ADDRESS LD DE,XOS_IDAT ; DE := XOSERA INSTANCE DATA PTR CALL VDA_ADDENT ; ADD ENTRY, A := UNIT ASSIGNED ; INITIALIZE EMULATION LD C,A ; C := ASSIGNED VIDEO DEVICE NUM LD DE,XOS_FNTBL ; DE := FUNCTION TABLE ADDRESS LD HL,XOS_IDAT ; HL := XOSERA INSTANCE DATA PTR CALL TERM_ATTACH ; DO IT XOR A ; SIGNAL SUCCESS RET ;---------------------------------------------------------------------- ; XOS_SHOW_INFO: ; XOSERA HAS SOME BUILD-TIME INFO STORED IN THE UPPER 128 OF COPPER ; MEMORY. RETRIEVE THAT INFO AND DISPLAY IT. ;---------------------------------------------------------------------- XOS_SHOW_INFO: LD DE,$C580 ; LAST 128 BYTES OF XOSERA COPPER MEM XM_SETW(ZXM_RD_XADDR) XOS_SHOW_INFO1: XM_GETW(ZXM_XDATA) ; GET INFO DATA IN DE LD A,D CP 0 JR Z,XOS_SHOW_INFO2 ; EXIT LOOP IF D == 0 PUSH DE CALL COUT ; PRINT THE CHAR IN D (COPIED TO A) POP DE LD A,E CP 0 JR Z,XOS_SHOW_INFO2 ; EXIT LOOP IF E == 0 CALL COUT ; PRINT THE CHAR IN E (COPIED TO A) JR XOS_SHOW_INFO1 XOS_SHOW_INFO2: RET ;---------------------------------------------------------------------- ; XOS_DETECT: DETECT PRESENCE OF A XOSERA BOARD ; ; READ XOSERA RD_INCR REGISTER, THEN XOR WITH CONSTANT TO CHANGE IT. ; WRITE XOR'D VALUE TO RD_INCR, THEN READ BACK. IF A MATCH, ; XOSERA BOARD IS DETECTED. ; EITHER WAY, RESTORE ORIGINAL RD_INCR THEN RETURN RESULT IN A. ; RESULT IN A: 0 IF VALUES MATCH, -8 (HARDWARE NOT PRESENT) OTHERWISE. ;---------------------------------------------------------------------- XOS_DETECT: XM_GETW(ZXM_RD_INCR) ; DE := XOSERA RD_INCR REGISTER PUSH DE ; SAVE A COPY LD A,D XOR $F5 LD D,A ; XOR RD_INCR MSB WITH $F5 LD A,E XOR $FA LD E,A ; XOR RD_INCR LSB WITH $FA ; DE = TEST_INCR = RD_INCR ^ 0xF5FA; LD H,D LD L,E ; SAVE TEST_INCR TO HL XM_SETW(ZXM_RD_INCR) ; WRITE NEW RD_INCR VALUE LD DE,$0000 XM_GETW(ZXM_RD_INCR) ; READ IT BACK INTO DE LD A,H CP D JR NZ,XOS_DETECT1 ; D & H THE SAME? LD A,L CP E JR NZ,XOS_DETECT1 ; E & L THE SAME? XOR A ; YEP, LOOKS GOOD JP XOS_DETECT2 XOS_DETECT1: LD A,-8 ; READBACK VALUE DID NOT MATCH. SIGNAL HARDWARE NOT PRESENT XOS_DETECT2: ; RESTORE THE ORGINAL VALUE OF RD_INCR POP DE XM_SETW(ZXM_RD_INCR) OR A ; SET FLAGS RET ; ;---------------------------------------------------------------------- ; XOS_PROBE: PROBE FOR XOSERA, TRY UP TO `SYNC_RETRIES` TIMES, WAITING ; 10MS BETWEEN SYNC ATTEMPTS. ; RETURN IN A: 0 IF A XOSERA_BOARD IS DETECTED, OR -8 (HARDWARE NOT ; DETECTED) OTHERWISE. ;---------------------------------------------------------------------- ; XOS_PROBE: LD B,SYNC_RETRIES XOS_PROBE1: CALL XOS_DETECT ; Z FLAG SET IF XOSERA DETECTED, CLEAR IF NOT JR Z,XOS_PROBE2 ; OUR ATTEMPT TO SYNC DID NOT WORK, SO WAIT 10MS TO TRY AGAIN PUSH BC LD DE,625 ; 625 * 16US = 10000US = 10MS CALL VDELAY POP BC DJNZ XOS_PROBE1 LD A,-8 ; FAILED TO SYNC AFTER SEVERAL ATTEMPTS. XOS_PROBE2: ; A WILL BE ZERO OR -8. SET THE Z FLAG ACCORDINGLY. OR A ; SET FLAGS RET ; ;---------------------------------------------------------------------- ; XOS_CLR_TEXT_SCR: CALL FILL TO CLEAR ENTIRE ROWS*COLS TEXT SCREEN. ;---------------------------------------------------------------------- XOS_CLR_TXT_SCR: LD A,(XOS_COLS) ; COMPUTE HOW MANY WORDS TO FILL LD H,A LD A,(XOS_ROWS) LD E,A CALL MULT8 LD B,H LD C,L ; BC := WORD COUNT = ROWS * COLS LD HL,(XOS_POS) PUSH HL ; SAVE CURRENT CURSOR POS LD HL,(XOS_OFF) LD (XOS_POS),HL ; TELL FILL TO START AT 0,0 (POS = XOS_OFF) LD A,$20 ; FILL WITH SPACE (AND CURRENT ATTR) CALL XOS_FILL CALL REVERSE_CURSOR_POS ; BIT OF A HACK POP HL LD HL,(XOS_POS) ; RESTORE ORIGINAL CURSOR POS RET ; ;---------------------------------------------------------------------- ; XOS_WRITE_XREGS: WRITE A GROUP OF XOSERA EXTENDED REGISTERS. ; HL POINTS TO SETUP TABLE, B HAS NUMBER OF WORDS. ; DE HAS FIRST XOSERA XREG ADDRESS ;---------------------------------------------------------------------- ; XOS_WRITE_XREGS: XM_SETW(ZXM_WR_XADDR) XOS_WRITE_XREGS1: LD E,(HL) INC HL LD D,(HL) INC HL XM_SETW(ZXM_XDATA) DEC B JR NZ,XOS_WRITE_XREGS1 RET ; ;---------------------------------------------------------------------- ; XOS_WRITE_PALETTE_B ; WHEN WRITING PALETTE B, WE NEED TO SET THE ALPHA VALUE TO $F ; SO THAT THE GRAPHICS/TEXT OVERLAP WORKS CORRECTLY. EXCEPT THAT ; WE DON'T WANT THIS FOR THE ZERO ENTRY SO HANDLE THAT SEPERATELY. ;---------------------------------------------------------------------- ; XOS_WRITE_PALETTE_B: XM_SETW(ZXM_WR_XADDR) LD E,(HL) INC HL LD D,(HL) INC HL XM_SETW(ZXM_XDATA) DEC B XOS_WRITE_PALETTE_B1: LD E,(HL) INC HL LD D,(HL) INC HL LD A,D OR $F0 ; SET ALPHA VALUE TO $F LD D,A XM_SETW(ZXM_XDATA) DEC B JR NZ,XOS_WRITE_PALETTE_B1 RET ; ;---------------------------------------------------------------------- ; XOS_COPY_FONT_TO_VRAM ; COPY THE 8 X 8 ST FONT IN THE EXTENDED REGISTER AREA TO THE ; UPPER 1K OF VRAM. USED IN 80 X 60 MODE, BUT NOT IN ; 80 X 30 MODE. ;---------------------------------------------------------------------- ; XOS_COPY_FONT_TO_VRAM: ; SEE IF THE FONT HAS ALREADY BEEN COPIED. IF SO VRAM ADDRESS ; $FC04 WILL HAVE THE VALUE $183C. LD DE,$FC04 XM_SETW(ZXM_RD_ADDR) XM_GETW(ZXM_DATA) LD A,D CP $18 JR NZ,XOS_COPY_FONT_TO_VRAM1 LD A,E CP $3C JR NZ,XOS_COPY_FONT_TO_VRAM1 ; FONT HAS ALREADY BEEN COPIED TO VRAM. JUST RETURN RET XOS_COPY_FONT_TO_VRAM1: LD DE,FONT_ST_8x8_ADDR ; READ FROM FONT AREA XM_SETW(ZXM_RD_XADDR) LD DE,$FC00 XM_SETW(ZXM_WR_ADDR) ; WRITE TO VRAM LD DE,$0001 XM_SETW(ZXM_WR_INCR) ; MAKE SURE WR INCR IS NONE LD BC,FONT_ST_8x8_SIZE ; LOOP COUNTER XOS_COPY_FONT_TO_VRAM2: PUSH BC XM_GETW(ZXM_XDATA) XM_SETW(ZXM_DATA) ; CHECK COUNT POP BC DEC BC ; DECREMENT COUNT LD A,B ; TEST FOR OR C ; ... ZERO RET Z ; DONE IF SO JR XOS_COPY_FONT_TO_VRAM2 XOS_HW_SETUP: LD DE,XR_VID_CTRL LD HL,XOS_BASE_INIT LD B,8 CALL XOS_WRITE_XREGS ; INITIALIZE PLAYFIELD A FOR BITMAP GRAPHICS LD DE,XR_PA_REGS LD HL,PF_640x480x4 LD B,7 CALL XOS_WRITE_XREGS ; INITIALIZE PLAYFIELD B FOR 80x30 TEXT MODE LD DE,XR_PB_REGS #IF (XOSSIZ=V80X60) LD HL,PF_80X60_TILED #ELSE LD HL,PF_80X30_TILED #ENDIF LD B,7 CALL XOS_WRITE_XREGS ; INITIALIZE THE PALETTE FOR PLAYFIELD A LD DE,XR_COLOR_A_ADDR LD HL,XOS_PALETTE LD B,16 CALL XOS_WRITE_XREGS ; INITIALIZE THE PALETTE FOR PLAYFIELD B LD DE,XR_COLOR_B_ADDR LD HL,XOS_PALETTE LD B,16 CALL XOS_WRITE_PALETTE_B ; INITIALIZE THE COPPER PROGRAM WE ARE USING TO 'LETTERBOX' ; A 640 X 400 BITMAP ON A 640 X 480 DISPLAY. SEE COPPER NOTES ; WAY BELOW. LD DE,$C100 XM_SETW(ZXM_WR_XADDR) LD DE,(PF_640x480x4) ; DE := PA_GFX_CTRL XM_SETW(ZXM_XDATA) LD A,E OR $80 LD E,A ; DE := PA_GFX_CTRL WITH BLANK BIT SET XM_SETW(ZXM_XDATA) LD DE,XR_COPPER_ADDR LD HL,XOS_COPPER LD B,19 CALL XOS_WRITE_XREGS LD DE,XR_COPP_CTRL ; ENABLE THE COPPER XM_SETW(ZXM_WR_XADDR) LD DE,$8000 XM_SETW(ZXM_XDATA) LD A,$0F ; SET DEFAUL PIXEL MASK XM_SETBL(ZXM_SYS_CTRL) LD DE,$0001 XM_SETW(ZXM_WR_INCR) ; SET DEFAULT READ/WRITE INCREMENTS XM_SETW(ZXM_RD_INCR) RET ; ;====================================================================== ; XOSERA DRIVER - VIDEO DISPLAY ADAPTER (VDA) FUNCTIONS ;====================================================================== ; XOS_FNTBL: .DW XOS_VDAINI .DW XOS_VDAQRY .DW XOS_VDARES .DW XOS_VDADEV .DW XOS_VDASCS .DW XOS_VDASCP .DW XOS_VDASAT .DW XOS_VDASCO .DW XOS_VDAWRC .DW XOS_VDAFIL .DW XOS_VDACPY .DW XOS_VDASCR .DW XKBD_STAT .DW XKBD_FLUSH .DW XKBD_READ .DW XOS_VDARDC #IF (($ - XOS_FNTBL) != (VDA_FNCNT * 2)) .ECHO "*** INVALID VGA FUNCTION TABLE ***\n" !!!!! #ENDIF XOS_VDAINI: LD A,$0F ; BRIGHT WHITE FG, BLACK BG LD (XOS_ATTR),A LD A,0 LD (XOS_RUB),A LD A,80 LD (XOS_COLS),A LD A,XOS_ROWS_CONST LD (XOS_ROWS),A #IF (XOSSIZ=V80X60) LD HL,$4000 #ELSE LD HL,$4800 #ENDIF LD (XOS_OFF),HL LD (XOS_POS),HL ; FONT DATA FOR 80x30 MODE WILL STAY IN TILE MEM, BUT ; FONT DATA FOR 80X60 MODE WILL LIVE IN VRAM. SO WE ; NEED TO COPY THE ST 8X8 FONT TO VRAM. WE MUST DO THIS ; BEFORE CLEARING THE TEXT SCREEN BECAUSE THE SCREEN ; DATA IS STORED WHERE FONT CURRENTLY RESIDES. LD A,$0F XM_SETBL(ZXM_SYS_CTRL) ; SET VRAM WRITE MASK CALL XOS_COPY_FONT_TO_VRAM CALL XOS_CLR_TXT_SCR ; CLEAR THE TEXT SCREEN CALL XOS_HW_SETUP ; CALL XOS_CLR_GRAPHICS_SCR ; CLEAR THE GRAPHICS SCREEN XOR A ; SIGNAL SUCCESS RET XOS_VDAQRY: LD C,$00 ; MODE ZERO IS ALL WE KNOW LD A,(XOS_ROWS) LD D,A LD A,(XOS_COLS) LD E,A LD HL,$0 ; EXTRACTION OF CURRENT BITMAP DATA NOT SUPPORTED YET XOR A ; SIGNAL SUCCESS RET XOS_VDARES: XOR A ; SIGNAL SUCCESS RET XOS_VDADEV: LD C,0 ; C := ATTRIBUTES (UNDEFINED) LD D,VDADEV_XOSERA ; D := DEVICE TYPE LD E,0 ; E := PHYSICAL UNIT IS ALWAYS ZERO LD H,0 ; H := 0, DRIVER HAS NO MODES LD L,XOS_BASE ; L := BASE I/O ADDRESS XOR A ; SIGNAL SUCCESS RET XOS_VDASCS: SYSCHKERR(ERR_NOTIMPL) ; NOT IMPLEMENTED (YET) RET XOS_VDASCP: ; UNREVERSE THE CURRENT CURSOR CALL REVERSE_CURSOR_POS CALL XOS_XY ; SET CURSOR POSITION ; REVERSE VIDEO THE CHARACTER AT THE NEW CURSOR POSITION CALL REVERSE_CURSOR_POS XOR A ; SIGNAL SUCCESS RET XOS_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 (XOS_RUB),A ; SAVE IT JR XOS_VDASCO1 ; IMPLEMENT SETTING XOS_VDASCO: ; WE HANDLE ONLY PER-CHARACTER COLORS (D=0) LD A,D ; GET CHAR/SCREEN SCOPE OR A ; CHARACTER? JR NZ,XOS_VDASCO2 ; IF NOT, JUST RETURN LD A,E LD (XOS_ATTR),A ; SAVE COLOR INFO XOS_VDASCO1: ; CHECK FOR REVERSE VIDEO LD A,(XOS_RUB) ; GET RUB SETTING BIT 2,A ; REVERSE IS BIT 2 JR Z,XOS_VDASCO2 ; DONE IF REVERSE VID NOT SET ; IMPLEMENT REVERSE VIDEO LD A,(XOS_ATTR) ; GET ATTRIBUTE RLCA ; SWAP FG/BG COLORS RLCA RLCA RLCA LD (XOS_ATTR),A ; SAVE NEW VALUE XOS_VDASCO2: XOR A ; SIGNAL SUCCESS RET XOS_VDAWRC: CALL REVERSE_CURSOR_POS LD A,E ; CHARACTER TO WRITE GOES IN A CALL XOS_PUTCHAR ; PUT IT ON THE SCREEN CALL REVERSE_CURSOR_POS XOR A ; SIGNAL SUCCESS RET XOS_VDAFIL: CALL REVERSE_CURSOR_POS LD A,E ; FILL CHARACTER GOES IN A LD B,H LD C,L ; FILL LENGTH GOES IN BC CALL XOS_FILL ; DO THE FILL CALL REVERSE_CURSOR_POS XOR A ; SIGNAL SUCCESS RET XOS_VDACPY: ; LENGTH IN HL, SOURCE ROW/COL IN DE, DEST IS XOS_POS ; BLKCPY USES: HL=SOURCE, DE=DEST, BC=COUNT CALL REVERSE_CURSOR_POS PUSH HL ; SAVE LENGTH CALL XOS_XY2IDX ; ROW/COL IN DE -> SOURCE ADR IN HL PUSH DE LD DE,(XOS_OFF) ; ADD IN VRAM START OFFSET ADD HL,DE POP DE POP BC ; RECOVER LENGTH IN BC LD DE,(XOS_POS) ; PUT DEST IN DE ; LD A,0 ; SOURCE IS MEMORY CALL XOS_BLKCPY ; DO A BLOCK COPY CALL REVERSE_CURSOR_POS XOR A ; SIGNAL SUCCESS RET XOS_VDASCR: CALL REVERSE_CURSOR_POS LD A,E OR A ; SET FLAGS RET Z ; IF ZERO, WE ARE DONE JP M,XOS_VDASCR1 ; E IS NEGATIVE, REVERSE SCROLL CALL XOS_RSCROLL ; SCROLL FORWARD 'E' LINES CALL REVERSE_CURSOR_POS XOR A ; SIGNAL SUCCESS RET XOS_VDASCR1: NEG ; A IS NEGATIVE, BUT NEED IT POSITIVE LD E,A ; LINES TO SCROLL TO E CALL XOS_SCROLL ; SCROLL REVERSE 'E' LINES CALL REVERSE_CURSOR_POS XOR A ; SIGNAL SUCCESS RET XKBD_STAT: XKBD_FLUSH: XKBD_READ: OR $FF ; RETURN FAILURE STATUS RET XOS_VDARDC: XOR A RET ; ;====================================================================== ; XOSERA DRIVER - PRIVATE DRIVER FUNCTIONS ;====================================================================== ; ;---------------------------------------------------------------------- ; SET CURSOR POSITION TO ROW IN D AND COLUMN IN E ;---------------------------------------------------------------------- ; XOS_XY: CALL XOS_XY2IDX ; CONVERT ROW/COL TO BUF IDX LD DE,(XOS_OFF) ADD HL,DE LD (XOS_POS),HL ; SAVE THE RESULT (DISPLAY POSITION) RET ; ;---------------------------------------------------------------------- ; CONVERT XY COORDINATES IN DE INTO LINEAR INDEX IN HL ; D=ROW, E=COL ;---------------------------------------------------------------------- ; XOS_XY2IDX: LD A,E ; SAVE COLUMN NUMBER IN A LD H,D ; SET H TO ROW NUMBER PUSH AF LD A,(XOS_COLS) LD E,A ; SET E TO ROW LENGTH CALL MULT8 ; MULTIPLY TO GET ROW OFFSET POP AF LD E,A ; GET COLUMN BACK ADD HL,DE ; ADD IT IN RET ; ;---------------------------------------------------------------------- ; REVERSE_CURSOR_POS - REVERSE VIDEO THE CHARACTER AT THE CURRENT ; CURSOR POSITION. ;---------------------------------------------------------------------- ; REVERSE_CURSOR_POS: PUSH AF PUSH BC PUSH DE LD DE,(XOS_POS) XM_SETW(ZXM_RD_XADDR) XM_SETW(ZXM_WR_XADDR) XM_GETW(ZXM_XDATA) ; GET THE ATTRIBUTE/CHAR AT CURSOR INTO DE LD A,D RLCA ; SWAP FG/BG COLORS RLCA RLCA RLCA LD D,A XM_SETW(ZXM_XDATA) POP DE POP BC POP AF RET ; ;---------------------------------------------------------------------- ; WRITE VALUE IN A TO CURRENT XOSERA BUFFER POSITION, ADVANCE CURSOR ;---------------------------------------------------------------------- ; XOS_PUTCHAR: ; SETUP DE WITH TILEMEM ADDRESS LD DE,(XOS_POS) XM_SETW(ZXM_WR_XADDR) ; SETUP CHAR/ATTR IN DE LD E,A ; CHARACTER TO E LD A,(XOS_ATTR) ; ATTRIBUTE LD D,A ; ... TO D ; WRITE CHAR & ATTR XM_SETW(ZXM_XDATA) ; UPDATE CURRENT POSITION LD HL,(XOS_POS) ; GET CURSOR POSITION INC HL ; INCREMENT LD (XOS_POS),HL RET ; ;---------------------------------------------------------------------- ; FILL AREA IN BUFFER WITH SPECIFIED CHARACTER AND CURRENT COLOR/ATTRIBUTE ; STARTING AT THE CURRENT FRAME BUFFER POSITION ; A: FILL CHARACTER ; BC: NUMBER OF CHARACTERS TO FILL ;---------------------------------------------------------------------- ; XOS_FILL: LD H,A ; CACHE FILL CHAR IN H ; SETUP DE WITH INITIAL BUFFER ADDRESS LD DE,(XOS_POS) ; GET CURRENT POSITION PUSH BC ; PRESERVE BC (C USED BY MACRO) XM_SETW(ZXM_WR_XADDR) ; SET VRAM WRITE ADDRESS POP BC ; RESTORE BC XOS_FILL1: ; FILL ONE POSITION (ATTR & CHAR) LD E,H ; CHARACTER TO E LD A,(XOS_ATTR) ; ATTRIBUTE LD D,A ; ... TO D PUSH BC ; PRESERVE BC (C USED BY MACRO) XM_SETW(ZXM_XDATA) ; WRITE CHAR & ATTR POP BC ; RESTORE BC ; CHECK COUNT DEC BC ; DECREMENT COUNT LD A,B ; TEST FOR OR C ; ... ZERO JR Z,XOS_FILL2 ; DONE IF SO ; UPDATE CURSOR POSITION PUSH HL LD HL,(XOS_POS) ; GET CURSOR POSITION INC HL ; INCREMENT LD (XOS_POS),HL POP HL JR XOS_FILL1 XOS_FILL2: RET ; ;---------------------------------------------------------------------- ; BLOCK COPY BC BYTES FROM HL (SOURCE) TO DE (DEST). ;---------------------------------------------------------------------- ; XOS_BLKCPY: PUSH BC ; COUNT ==> TOS ; SETUP XOSERA VRAM READ ADDRESS FROM HL PUSH DE LD D,H LD E,L XM_SETW(ZXM_RD_XADDR) ; SETUP XOSERA VRAM WRITE ADDRESS FROM DE POP DE XM_SETW(ZXM_WR_XADDR) XOS_BLKCPY1: ; GET NEXT SOURCE WORD INTO DE XM_GETW(ZXM_XDATA) ; WRITE DE TO DEST WORD XM_SETW(ZXM_XDATA) ; DECREMENT BYTE COUNT AND CHECK FOR COMPLETION EX (SP),HL ; GET COUNT, SAVE HL DEC H ; DECREMENT LD A,H ; TEST FOR CP L ; ... ZERO EX (SP),HL ; COUNT BACK TO TOS, RESTORE HL JR NZ,XOS_BLKCPY1 ; LOOP IF NOT ZERO POP BC ; CLEAN UP STACK RET ; DONE ; ;---------------------------------------------------------------------- ; SCROLL ENTIRE SCREEN FORWARD BY ONE OR MORE LINES (CURSOR POSITION UNCHANGED) ;---------------------------------------------------------------------- ; NUMBER OF LINES TO SCROLL COMES IN E XOS_SCROLL: ; DEST ADDR IS START OF LAST ROW PUSH DE ; SAVE SCROLL LINE COUNT PUSH DE ; SAVE IT TWICE PUSH DE ; SAVE IT THRICE LD A,(XOS_ROWS) DEC A LD H,A ; H := ROWS - 1 LD A,(XOS_COLS) LD E,A CALL MULT8 LD B,H LD C,L LD HL,(XOS_OFF) ADD HL,BC ; HL := DEST TILEMEM ADDRESS ; SOURCE ADDRESS is DEST ADDRESS - (COLS * LINES) POP DE ; RESTORE LINE COUNT IN E PUSH HL ; SAVE DEST ADDRESS SO WE CAN USE H LD A,(XOS_COLS) LD H,A CALL MULT8 LD D,H LD E,L ; DE := COLS * LINES POP HL ; RESTORE DEST ADDR BACK TO HL PUSH HL OR A ; CLEAR CARRY SBC HL,DE ; HL := SOURCE ADDR POP BC ; BC := DEST ADDRESS LD A,(XOS_ROWS) POP DE ; GET SCROLL LINE COUNT IN TO E SUB E LD D,B LD E,C ; RESTORE DE AS DEST ADDR LD B,A ; B := ROWS - LINE COUNT => LOOP COUNTER ; USE BLKCPY TO COPY ONE ROW XOS_SCROLL1: PUSH HL ; HL IS SOURCE ADDR FOR BLOCK COPY PUSH DE ; DE IS DEST ADDR FOR BLOCK COPY PUSH BC ; B IS OUR LOOP COUNTER LD A,(XOS_COLS) ; EXCEPT THAT BLOCK COPY USES BC FOR COPY COUNT LD C,A LD B,0 CALL XOS_BLKCPY POP BC ; RESTORE LOOP COUNTER ; CHECK COUNT DEC B JR Z,XOS_SCROLL2 ; MOVE ON TO FILL WHEN DONE ; DECREMENT SOURCE AND DEST BY ONE ROW POP HL ; HL := DEST ADDRESS LD A,(XOS_COLS) LD E,A LD D,0 OR A ; CLEAR CARRY SBC HL,DE ; HL := UPDATED DEST ADDRESS (DEST ADDR - COLS) POP DE ; DE := SOURCE ADDR PUSH HL ; SAVE UPDATED DEST TEMPORARILY LD H,D LD L,E LD A,(XOS_COLS) LD E,A LD D,0 OR A ; CLEAR CARRY SBC HL,DE ; HL := UPDATED SOURCE ADDRESS POP DE ; DE := UPDATED DEST ADDRESS JR XOS_SCROLL1 ; LOOP AROUND XOS_SCROLL2: POP DE POP HL ; CLEAN UP STACK POP DE ; RESTORE SCROLL LINE COUNT ; NOW FILL THE E LINES AT THE TOP OF THE SCREEN. ; FIRST, SAVE OFF CURRENT CURSOR POSITION, THE SET IT TO 0,0 LD HL,(XOS_POS) PUSH HL LD HL,(XOS_OFF) LD (XOS_POS),HL ; CURSOR POSITION NOW 0,0 ; COMPUTE THE NUMBER OF BYTES FILL WHICH IS COLS * LINES LD A,(XOS_COLS) LD H,A CALL MULT8 LD B,H LD C,L LD A,$20 ; FILL CHAR IS SPACE CALL XOS_FILL POP HL LD (XOS_POS),HL ; RESTORE ORIGINAL CURSOR POSITION RET ; ;---------------------------------------------------------------------- ; REVERSE SCROLL ENTIRE SCREEN BY ONE LINE (CURSOR POSITION UNCHANGED) ;---------------------------------------------------------------------- ; NUMBER OF LINES TO SCROLL COMES IN E XOS_RSCROLL: PUSH DE ; SAVE SCROLL LINE COUNT PUSH DE ; NEED IT OFF THE STACK TWICE PUSH DE ; NEED IT OFF THE STACK THRICE ; DEST ADDR IS ROW=0, COL=0 LD DE,(XOS_OFF) XM_SETW(ZXM_WR_XADDR) ; SET DEST ADDR IN XOSERA POP DE ; RESTORE SCROLL LINE COUNT IN E ; SOURCE ADDR (HL) = XOS_OFF + XOS_COLS * E LD A,(XOS_COLS) LD H,A CALL MULT8 ; HL := XOS_COLS * E LD BC,(XOS_OFF) ADD HL,BC ; HL := SOURCE ADDR := XOS_OFF + XOS_COLS * E LD D,H LD E,L XM_SETW(ZXM_RD_XADDR) ; SET SOURCE ADDR IN XOSERA ; COMPUTE THE COUNT OF WORDS TO MOVE = COLS * (ROWS - LINES) POP DE ; RESTORE SCROLL LINE COUNT IN E LD A,(XOS_ROWS) SUB E LD H,A ; H := ROWS - LINES LD A,(XOS_COLS) LD E,A ; E := COLS CALL MULT8 LD B,H LD C,L ; BC = COUNT := COLS * (ROW - LINES) PUSH BC ; SAVE FOR LATER USE BY FILL XOS_RSCROLL1: PUSH BC ; MACROS BELOW ALTER C, SO SAVE XM_GETW(ZXM_XDATA) ; TRANSFER A WORD FROM TILEMEM SRC TO DEST XM_SETW(ZXM_XDATA) POP BC ; RESTORE LOOP COUNT ; CHECK COUNT DEC BC ; DECREMENT COUNT LD A,B ; TEST FOR OR C ; ... ZERO JR Z,XOS_RSCROLL2 ; MOVE ON TO FILL WHEN DONE JR XOS_RSCROLL1 XOS_RSCROLL2: ; NOW DO THE FILL OF THE EXPOSED LINES AT THE BOTTOM OF THE SCREEN. ; DESTINATION VRAM ADDRESS FOR THIS FILL IS THE COUNT FROM ABOVE PLUS VRAM OFFSET POP BC ; RESTORE COUNT POP DE ; RESTORE LINES IN E LD A,(XOS_COLS) LD H,A PUSH BC ; SAVE COUNT AGAINT CALL MULT8 ; HL := XOS_COLS * LINES LD B,H LD C,L ; BC NOW HAS FILL WORD COUNT POP HL ; RESTORE SCROLL WORD COUNT TO HL LD DE,(XOS_OFF) ADD HL,DE ; ADD IN OFFSET LD D,H LD E,L ; DE NOW HAS DEST VRAM ADDR LD HL,(XOS_POS) ; XOS_FILL USES XOS_POS AS DEST PUSH HL ; SAVE CURRENT CURSOR POSITION LD (XOS_POS),DE LD A,$20 ; USE SPACE TO FILL (USING CURRENT COLOR/ATTR) CALL XOS_FILL POP HL LD (XOS_POS),HL ; RESTORE PREVIOUS CURSOR POSITION XOR A ; SIGNAL SUCCESS RET ; ;================================================================================================== ; XOSERA DRIVER - DATA ;================================================================================================== ; XOS_ATTR: .DB 0 ; CURRENT COLOR XOS_POS: .DW 0 ; CURRENT DISPLAY POSITION XOS_OFF: .DW 0 ; SCREEN START OFFSET INTO XOSERA VRAM XOS_RUB: .DB 0 ; REVERSE/UNDERLINE/BLINK (-----RUB) XOS_COLS: .DB 0 ; NUMBER OF COLUMNS XOS_ROWS: .DB 0 ; NUMBER OF ROWS XOS_BASE_INIT: .DW $0000 ; XR_VID_CTRL => NO PLAYFIELD COLOR SWAP. BORDER CORDER = 0 .DW $0000 ; XR_COPP_CTRL => DISABLE COPPER .DW $0000 ; XR_AUD_CTRL => DISABLE AUDIO .DW $0000 ; XR_SCANLINE => JUST SET TO ZERO .DW $0000 ; XR_VID_LEFT => LEFT EDGE OF ACTIVE DISPLAY => 0 .DW 640 ; XR_VID_RIGHT => RIGHT EDGE OF ACTIVE DISPLAY => 640 .DW $0000 ; XR_POINTER_H => HIDE POINTER, SO SET TO 0 .DW $0000 ; XR_POINTER_V => HIDE POINTER, SO SET TO 0 PF_640x480x4: ; CONFIGURE PLAYFIELD FOR 640x480x4BPP BITMAP GRAPHICS (LETTERBOXED TO 640 X 400) .DW $0050 ; GFX_CTRL .DW $0000 ; TILE_CTRL => IGNORED IN BITMAP MODE .DW $0000 ; DISP_ADDR => START OF VRAM .DW $00A0 ; LINE_LEN => 640 PIXELS WIDE / 4 PIXELS PER WORD = 160d .DW $0000 ; HV_FSCALE => NO FRACTIONAL SCALING .DW $0000 ; H_SCROLL => NO HORIZONTAL FINE SCROLLING .DW $0000 ; V_SCROLL => NO VERTICAL FINE SCROLLING PF_80X30_TILED: ; CONFIGURE PLAYFIELD FOR 80 X 30 TILED MODE ; TILEMAP AND 8x16 FONT DATA BOTH RESIDE IN TILEMEM (AS OPPOSED TO VRAM) .DW $0000 ; GFX_CTRL .DW FONT_ST_8x16_ADDR | TILE_CTRL_DISP_TILEMEM_F | 15; TILE_CTRL => (LAST ONE IS TILE HEIGHT - 1) .DW FONT_ST_8x16_ADDR + FONT_ST_8x16_SIZE ; DISP_ADDR => TILEMAP STATS JUST AFTER FONT DATA IN TILEMEM. .DW $0050 ; LINE_LEN => 80 COLS .DW $0000 ; HV_FSCALE => NO FRACTIONAL SCALING .DW $0000 ; H_SCROLL => NO HORIZONTAL FINE SCROLLING .DW $0000 ; V_SCROLL => NO VERTICAL FINE SCROLLING PF_80X60_TILED: ; CONFIGURE PLAYFIELD FOR 80 X 60 TILED MODE ; TILEMAP WILL RESIDE IN TILEMEM, BUT DATA IS AT $FC00 IN VRAM .DW $0000 ; GFX_CTRL .DW $FC00 | TILE_CTRL_DISP_TILEMEM_F | TILE_CTRL_TILE_VRAM_F | 7; TILE_CTRL => (LAST ONE IS TILE HEIGHT - 1) .DW $4000 ; DISP_ADDR => TILEMAP STARTS AT BEGINNING OF TILEMEM .DW $0050 ; LINE_LEN => 80 COLS .DW $0000 ; HV_FSCALE => NO FRACTIONAL SCALING .DW $0000 ; H_SCROLL => NO HORIZONTAL FINE SCROLLING .DW $0000 ; V_SCROLL => NO VERTICAL FINE SCROLLING XOS_PALETTE: ; SET THE FIRST SIXTEEN ENTRIES OF A XOSERA PALETTE TO MATCH WHAT IS EXPECTED BY ROMWBW. .DW $0000 ; 0: BLACK .DW $0A00 ; 1: RED .DW $00A0 ; 2: GREEN .DW $0A50 ; 3: BROWN .DW $000A ; 4: BLUE .DW $0A0A ; 5: MAGENTA .DW $00AA ; 6: CYAN .DW $0AAA ; 7: WHITE .DW $0555 ; 8: GRAY .DW $0F55 ; 9: LIGHT RED .DW $05F5 ; 10: LIGHT GREEN .DW $0FF5 ; 11: YELLOW .DW $055F ; 12: LIGHT BLUE .DW $0F5F ; 13: LIGHT MAGENTA .DW $05FF ; 14: LIGHT CYAN .DW $0FFF ; 15: BRIGHT WHITE ; XOSERA HAS A BUILT-IN AMIGA-ISH COPROCESSOR. WE USE IT HERE TO LIMIT THE GRAPHICS ; BITMAP DISPLAY TO 600x400. THIS IS REQUIRED BECAUSE WHILE XOSERA OUTPUTS a 640 X 480 ; BY 60 HZ VGA SIGNAL, THERE IS NOT ENOUGH VRAM FOR A FULL 640 X 480 X 4BPP BITMAP. SO WE ; RESTRICT TO 640x400 AND USE SOME COPPER TRICKS TO BLANK THE OTHER 80 LINES OF ; THE DISPLAY. ESSENTIALLY WE ARE 'LETTERBOXING' A 640 X 400 BITMAP IN THE VERTICAL ; CENTER OF A 640 X 480 DISPLAY. NOTE THAT TEXT MODE STILL USES THE ENTIRE 640 X 480 ; DISPLAY. XOS_COPPER: .DW $D101,$0800 ; LDM $C101 .DW $1800,$0010 ; STM XR_PA_GFX_CTRL ; start the frame by blanking the display .DW $2828 ; VPOS #40 ; Wait for line 40 .DW $0800,$0000 ; LDI #0 .DW $1800,$0017 ; STM XR_PA_LINE_ADDR .DW $D100,$0800 ; LDM $C100 ; get value of XR_PA_GFX_CTRL with BLANK bit NOT set .DW $1800,$0010 ; STM XR_PA_GFX_CTRL ; unblank playfield A .DW $D101,$0800 ; LDM $C101 ; get value of XR_PA_GFX_CTRL with BLANK bit set .DW $29B8 ; VPOS #440 ; wait until vertical line 440 .DW $1800,$0010 ; STM XR_PA_GFX_CTRL ; blank playfield A .DW $2BFF ; VPOS #V_EOF ; wait until end of frame ; ;================================================================================================== ; XOSERA DRIVER - INSTANCE DATA ;================================================================================================== ; XOS_IDAT: .DB KBDMODE_NONE .DB 0 .DB 0 XOS_IDAT2: .DB KBDMODE_NONE .DB 0 .DB 0