; FPGA_VGA.Z80 This is a simple stand alone demo TTY terminal version to work with the V2 FPGA S100 Board ; using a VGA module. ; The display can be 80 characters wide, 40 lines deep. I find its best to limit the height to ; 38 lines high for reasonable VGA displays. This is done below. Adjust if needed. ; Note there is a "quark" in the VHD code characters in that the lines are numbered 1,2,3...79,0 ; This slightly complicates the cursor positioning code. Normally lines are far shorter than ; this but things like scrooling at the very bottom line at position 80 will not work. ; ; Assemble and SLR's Z80ASM Assembler (Can also use the Cromemco Assembler) ; Use:- Z80ASM SBC-MON FH ; ; To assemble under windows... ; Load Altair.EXE in Windows CMD box ; do cpm3 ; I: ; I:>Submit FPGA_VGA ; ; FPGA_VGA.HEX is written back to the same windows folder that the PC file "altair.exe" is in. ; ; Recent History... ; ; V1.0 6/15/2019 Initial code ; V1.1 6/22/2019 Incoporate code directly from FPGA Z80 SBC ROM ; ; FALSE EQU 0 TRUE EQU NOT FALSE ; ; ; ; ORG 100H VGA_BASE EQU 08000H ;The VGA text will reside from 8000H to 8FFFH in this demo VGA_BASE_END EQU 08C7FH ;Last character at bottom RHS TOTAL_VGA_LINES EQU 38 VGA_CURSOR_X EQU 068H ;Port to set VGA video cursor VGA_CURSOR_Y EQU 069H VGA_COLOR EQU 0C2H ;Port to set text color VGA_BASE EQU 08000H ;The VGA text will reside from E000H to EC7FH VGA_BASE_END EQU 08C7FH ;Last character at bottom RHS TOTAL_VGA_LINES EQU 38 ;26H RAM_POINTER EQU VGA_BASE+0F0EH ;Will store VGA RAM Pointer at very top or its RAM. LOCAL_X EQU VGA_BASE+0F0DH LOCAL_Y EQU VGA_BASE+0F0CH MONITOR EQU 0F000H ;Z80 Monitor SCROLL EQU 01H ;Set scroll direction UP. BELL EQU 07H SPACE EQU 20H TAB EQU 09H ;TAB ACROSS (8 SPACES FOR SD-BOARD) CR EQU 0DH LF EQU 0AH FF EQU 0CH QUIT EQU 11H ;Turns off any screen enhancements (flashing, underline etc). NO_ENH EQU 17H ;Turns off whatever is on FAST EQU 10H ;High speed scroll ESC EQU 1BH DELETE EQU 7FH BACKS EQU 08H CLEAR EQU 1AH ;TO CLEAR SCREEN RST7 EQU 38H ;RST 7 (LOCATION FOR TRAP) NN EQU 0H ;[I] INITIAL VALUE SOH EQU 1 ;For XModem etc. EOT EQU 4 ACK EQU 6 NAK EQU 15H ; ; ;-------------- S100Computers PROPELLER CONSOLE_IO (OR SD SYSTEMS VIDIO BOARD) FOR CONSOLE INPUT & OUTPUT S100_CONSOL_STATUS EQU 0H ;Note will utilize this board if IOBYTE bits 0 & 1 are ZERO (or bit 5 is 1). S100_CONSOL_IN EQU 1H S100_CONSOL_OUT EQU 1H START: LD SP,STACK LD HL,S100_SIGNON ;Print Signon on S100 Bus Propeller Console IO Board CALL PRINT_STRING ;-------THIS IS THE START ON THE MAIN FPGA_VGA LOOP-------------------------------- INIT: CALL VGA_CLEAR_SCREEN LD HL,VGA_SIGNON CALL VGA_PRINT_STRING MAIN_LOOP: CALL CI ;Store character in C CP A,ESC JP Z,TO_MONITOR LD C,A CALL VGA_CO ;Now to VGA display (characters in RAM at 8000H) JP MAIN_LOOP ;--------------------------------------VGA VIDEO ROUTINES --------------------------------------- VGA_CO: PUSH BC ;Only A is changed PUSH DE PUSH HL LD A,C ;Character output to VGA Display ; CALL CO ;To S100 Bus Console (For Diagnostic display only) LD A,C CP A,CR ;Is it a CR, will convert to CR/LF JP Z,DO_CR CP A,LF ;Is it a LF, Skip for now JP Z,DO_LF CP A,FF ;Is it a FF (0CH,^L), if so clear screen JP Z,DO_FF CP A,BACKS ;Back Space JP Z,DO_BS CP A,TAB ;Is it a TAB, skip for now JP Z,DO_TAB CP A,BELL ;Is it a BELL, skip for now JP Z,DO_BELL CP A,1FH ;Only real characters JR NC,NORMAL_CHAR JP VGA_CO_DONE ;SKIP anything else NORMAL_CHAR: ;All other (real) ASCII characters LD HL,(RAM_POINTER) ;RAM POINTER to [HL] & UPDATE RAM POINTER X,Y LD (HL),C ;Drop down character INC HL ;Move to next RAM position LD (RAM_POINTER),HL LD A,(LOCAL_X) ;Get current RAM X position INC A ;Point to next position CP A,50H ;Is it past EOL, LOCAL X cursor goes 0,2,3...4FH (80) JR NZ,NOT_EOL ;LOCAL X goes 0,2,3...4F LD A,(LOCAL_Y) ;At EOL, LOCAL Y goes 0,2,3...26H (36) CP A,TOTAL_VGA_LINES ;Are we on the bottom line JP Z,BOTTOM_LINE LD A,0 ;Not Bottom line, so MOVE to next line LD (LOCAL_X),A LD A,(LOCAL_Y) INC A LD (LOCAL_Y),A JR DONE_NORMAL_CHARACTER NOT_EOL: LD (LOCAL_X),A JR DONE_NORMAL_CHAR BOTTOM_LINE: ;Special case situation for bottom line (38) CALL VGA_SCROOL LD A,(LOCAL_X) ;Get current RAM X position CP A,4FH ;Is it the last character on last line JP Z,LAST_CHAR ;If so special case LD A,0 ;Not last char on last line so just move everything up one line LD (LOCAL_X),A JR DONE_NORMAL_CHAR LAST_CHAR: LD A,0 ;Move to start of line LD (LOCAL_X),A LD HL,VGA_BASE + (TOTAL_VGA_LINES * 50H) ;Point to bottom LHS corner LD (RAM_POINTER),HL JR DONE_NORMAL_CHAR DONE_NORMAL_CHAR: LD A,(LOCAL_X) INC A ;Cursor X goes 1,2,3...4F,0! CP A,50H JR NZ,NOT_50 LD A,0 NOT_50: OUT (VGA_CURSOR_X),A LD A,(LOCAL_Y) ;Cursor Y goes 0,1,2,3...4F OUT (VGA_CURSOR_Y),A VGA_CO_DONE: POP HL POP DE POP BC LD A,C RET DO_CR: LD A,(LOCAL_X) LD HL,(RAM_POINTER) DO_CR1: OR A,A JR Z,DONE_DO_CR DEC A DEC HL JR DO_CR1 DONE_DO_CR: LD (RAM_POINTER),HL LD (LOCAL_X),A JR DONE_NORMAL_CHAR DO_LF: LD A,(LOCAL_Y) LD HL,(RAM_POINTER) LD DE,50H ;Add 80 character positions CP A,TOTAL_VGA_LINES ;My VGA Monitor works best with no more than 38 lines JR Z,LF_SCROOL ADD HL,DE LD (RAM_POINTER),HL INC A LD (LOCAL_Y),A JR DONE_NORMAL_CHAR LF_SCROOL: CALL VGA_SCROOL JR DONE_NORMAL_CHAR DO_TAB: LD A,(LOCAL_X) ;Expand out tabs so line numbering is 1,2,3,4.....80 CP A,4FH JR NZ,DO_TAB1 ;Not at end of line, expand tabs LD C,CR ;At end of line, do CR/FL CALL VGA_CO LD C,LF ;At end of line, do CR/FL CALL VGA_CO JP DONE_NORMAL_CHAR DO_TAB1: LD D,A ;Store for below INC A AND A,00000111B ;Max 8 spaces for tabs LD B,A LD A,8 SUB A,B LD B,A ;1 to 8 spaces in loop below DO_8: LD C,SPACE ;Print a space CALL VGA_CO DJNZ DO_8 JP DONE_NORMAL_CHAR DO_BS: LD A,(LOCAL_X) ;Get current RAM X position OR A,A JP Z,VGA_CO_DONE ;NO BS for first character, just return LD HL,(RAM_POINTER) ;RAM POINTER to [HL] & BACKUP RAM POINTER X,Y DEC HL LD (RAM_POINTER),HL LD A,SPACE LD (HL),A LD A,(LOCAL_X) ;Get current RAM X position DEC A LD (LOCAL_X),A IN A,(VGA_CURSOR_X) DEC A OUT (VGA_CURSOR_X),A JP VGA_CO_DONE DO_FF: CALL VGA_CLEAR_SCREEN ; Clear VGA Screen, put cursor at Top LH corner JP DONE_NORMAL_CHAR DO_DEL: LD HL,(RAM_POINTER) ;RAM POINTER to [HL] LD A,SPACE LD (HL),A JP VGA_CO_DONE DO_BELL:POP DE ;Skip for now POP HL POP BC LD A,C RET VGA_SCROOL: LD DE,VGA_BASE ;Start from E000H LD HL,VGA_BASE+50H LD BC,0BE0H + 50H ;Total number of bytes to move for 38 lines + one blank line LDIR ;Z80 block move ((DE++)<-(HL++), BC-- RET VGA_CLEAR_SCREEN: ; Clear VGA Screen, put cursor ate Top LH corner LD HL,VGA_BASE LD DE,(VGA_BASE_END + 51H )- VGA_BASE ;<--- Do NOT clear all the way up to EF00FH because CLEAR1: LD A,SPACE ; this is where pointers used here are stored. LD (HL),A ;Note we clear an extra line for screen scrolls INC HL DEC DE LD A,E CP A,0 JR NZ,CLEAR1 ; Continue until DE = 0 LD A,D CP A,0 JR NZ,CLEAR1 XOR A,A LD (LOCAL_X),A ;0, RAM X offset LD (LOCAL_Y),A ;0, RAM Y offset OUT (VGA_CURSOR_Y),A ;0, Cursor Y at top RH corner. Cursor Y goes:- 0,1,2,3,...80 INC A OUT (VGA_CURSOR_X),A ;1, Cursor X goes:- 1,2,3,...79,0 LD HL,VGA_BASE LD (RAM_POINTER),HL RET ;----------------------------------- SUPPORT ROUTINES --------------------------- ;Print a string in [HL] up to '$' PRINT_STRING: push bc push de PSTRX: ld a,(hl) cp '$' jp z,DONEP ld c,A call CO inc hl jp PSTRX DONEP: pop de pop bc ret VGA_PRINT_STRING: push bc push de VPSTRX: ld a,(hl) cp '$' jp z,VDONEP ld c,A call VGA_CO inc hl jp VPSTRX VDONEP: pop de pop bc ret ;SEND TO CONSOL CR/LF CRLF: PUSH AF PUSH BC LD C,CR CALL CO LD C,LF CALL CO POP BC POP AF RET VGA_CRLF: PUSH AF PUSH BC LD C,CR CALL VGA_CO LD C,LF CALL CO POP BC POP AF RET TO_MONITOR: LD HL,TO_MON_MSG CALL VGA_PRINT_STRING JP MONITOR ;<<<<<<<<<<<<<<<<<<<<<< MAIN CONSOL OUTPUT ROUTINE >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ; CO: IN A,(S100_CONSOL_STATUS) ;PROPELLER VIDIO BOARD PORT AND 4H JR Z,CO ;Not yet ready, try both outputs LD A,C OUT (S100_CONSOL_OUT),A RET CSTS: IN A,(S100_CONSOL_STATUS) AND 02H JP Z,NO_CSTS ;Zero if nothing GOT_CSTS: XOR A DEC A ;RETURN WITH 0FFH IN [A] IF SOMETHING RET NO_CSTS: XOR A RET ;RETURN WITH 0 IN A IF NOTHING THERE CI: IN A,(S100_CONSOL_STATUS) ;NEED CONSTAT TO CLEAN UP SHIFT KEYS ETC AND 02H JR Z,CI ;Wait until something there IN A,(S100_CONSOL_IN) AND 7FH RET ;------------------------------------------------------------------------------------------------- S100_SIGNON: DB CR,LF,LF,'FPGA BOARD VGA DEMO (www.S100Computers.COM J.Monahan, 6/15/2019)',CR,LF,'$' VGA_SIGNON: DB 'FPGA BOARD VGA DEMO (www.S100Computers.COM J.Monahan, 6/15/2019)',CR,LF DB 'Remember CR and LF are seperate characters. (For LF use ^J). ESC to abort.',CR,LF,LF,'$' TO_MON_MSG DB CR,LF,LF,'Returning to Z80 Monitor at F000H',CR,LF,'$' DS 100H ;Space for stack STACK EQU $ X_POSITION DB 1 ;Store for cursor position Y_POSITION DB 0 RAM_POSITION DW 8000H ;Store next position for character placement