.. MicroVIP operating system .. modified from the VIP hex listing .. and using information from VIPER Vol 1, Issue 3 .. by J.W. Wentworth's Interpretation of VIP .. D. Hunter 09/03/17 .. .. NOTE: the subroutines on page #8100 are not moved from the original VIP so that .. the OS is still compatible with Chip-8. .. .. Enter the VIP operating system by holding down the 'C' key when entering RUN .. A 4 digit hex address / file number is entered followed by a command key .. e.g. 0123 .. There are four commands possible: .. Memory Read = A .. Memory Write = 0 (enter the byte to write) .. Flash Read = B (address = file number [0000-01FF]) .. Flash Write = F (address = file number [0000-01FF]) .. .. The registers R3-RF are stored in a table on start up .. #0FB3-#0FBF <- R3.0-RF.0 .. #0FC3-#0FCF <- R3.1-RF.1 .. .. Execute a RESET (RUN OFF, RUN ON) to exit a command and start again .. .. Memory Map .. 0000 - 0FFF RAM (4K) .. 8000 - 81FF ROM1 (512) [MicroVIP ROM] .. 8200 - 83FF ROM2 (512) [UT4 Monitor] .. 8400 - 9FFF ROM3 (7K) [Tiny BASIC] .. A000 - BFFF .. C000 - C3FF Auxiliary RAM (1K) .. D000 - FFFF .. .. I/O Port Map [] = future option .. # OUT IN .. 1 video off video on .. 2 keypad [ASCII Keyboard] .. 3 output port input port .. 4 [I2C out] [I2C in] .. 5 control reg [UART status] .. 6 [UART txd] [UART rxd] .. 7 SPI out SPI in .. .. .. control register (PORT 5) .. bit 0,1: Q mux 00 = tone (VIP) .. 01 = serial (UT4) .. 10 = GPIO out strobe .. 11 = Flash SPI CS .. bit 2: Tone Disable 0 = tone enabled 1 = tone disabled .. bit 3: UART pins 0 = Q/EF2 1 = UART (future) .. bit 4: Flash WP 0 = WP off 1 = WP on .. bit 5: .. bit 6: .. bit 7: .. .. Q .. FLASH CS (inverted) .. LED / SPEAKER TONE ON .. EF PINS .. VIDEO FRAME EF1 .. UT4 SERIAL EF2 .. KEYPAD EF3 .. (OPEN) EF4 STKTOP: EQU #AF .. top of stack is below register memory LINE27: EQU 216 .. display starts at line 27, position 0 .. for 5 rows (27,28,29,30,31) .. so the characters are at the bottom .. of the screen .. 27 * 8 = 216 = #D8 NXTCHR: EQU 217 .. line 27, position 1 .. Serial Flash commands CMDWR: EQU #02 CMDRD: EQU #03 CMDSTA: EQU #05 CMDWEN: EQU #06 CMDERA: EQU #20 .. REGISTER USAGE .. R0 = DMA (video memory) .. R1 = INTERRUPT .. R2 = STACK POINTER .. R3 = PC .. R4 = DISPLAY CONTROL SUBROUTINE (#81DD) .. R5 = 5 ROW DISPLAY SUBROUTINE (#81C6) .. R6 = POINTER TO ADDRESSED BYTE ENTERED BY OPERATOR .. R7 = GET BYTE FROM KEYPAD SUBROUTINE (#81BA) .. FLASH READ/WRITE: SERIAL FLASH PAGE ADDRESS (upper 16 bits of address) .. R8 = R8.1 IS A TIMER FOR CHIP 8 PROGRAMS, TESTED EVERY TV SCREEN .. R8.0 TIMER TESTED AND DECREMENTED EVERY TV SCREEN (60 HZ) .. A TONE IS ON (Q SET) WHEN TIMER > 0 .. R8.0 TIMER ALSO CONTROLS DEBOUNCE FOR THE KEYBOARD .. R9 = SPECIAL VALUE USED BY THE RANDOM NUMBER GENERATOR IN CHIP 8, .. INCREMENTED ONCE PER TV SCREEN .. RA = DATA POINTER USED IN 5 ROW HEX DIGIT DISPLAY -POINTS TO CHARACTER DATA .. RB = RB.1 STORES DISPLAY PAGE UPPER ADDRESS FOR USE DURING THE INTERRUPT ROUTINE .. RB.0 USED DURING THE INTERRUPT ROUTINE TO DECREMENT R8.1 W/O CHANGING DF .. RC = KEYBOARD SCAN SUBROUTINE (#8195) .. RD = DESTINATION POINTER TO BE DISPLAYED ON DISPLAY PAGE .. RE = RE.1 STORES CONTROL DIGIT (A, 0, B OR F) FROM USER .. RE.0 STORES THE MOST SIGNIFICANT DIGIT FOR THE KEYBOARD INPUT .. RF = RF.1 .. RF.0 IS USED FOR KEYBOARD SCANNING AND A COUNTER FOR THE 5 ROW DISPLAY ORG #8000 COLD: LDI A.1(MON) .. jump to MON from boot vector PHI R2 LDI A.0(MON) PLO R2 SEX R2 .. 2 -> X SEP R2 .. 2 -> P MON: OUT 5 .. reset control register for VIP mode ,#00 OUT 2 .. set keyboard scan for switch 'C' ,#0C .. Set the top of RAM at the 4K boundary (max size for a VIP) .. a memory test is not needed since the memory size is fixed by the FPGA MEMCK: LDI #FF .. set for top of memory, #0FFF PLO R1 LDI #0F PHI R1 DONE: B3 CLRDSP .. start if KEY 'C' pressed GHI R0 .. else, start user program at #0000 PLO R0 SEX R0 SEP R0 .. Erase the display page from 0FFF to 0FB0 CLRDSP: SEX R1 CLEAR: LDI #00 STXD GLO R1 XRI STKTOP .. clear register table to memory top BNZ CLEAR .. (#0FB0 to #0FFF) .. copy registers to a register table .. by creating op codes on the fly .. #0FB3-#0FBF <- R3.0-RF.0 .. #0FC3-#0FCF <- R3.1-RF.1 REGDMP: LDI #D2 .. SEP R2 op code STXD LDI #9F .. GHI RF op code STR R1 GLO R1 PLO R0 GHI R1 PHI R0 .. R0 <- R1 LDI #CF PLO R1 .. R1 <- #0FCF (top of register table) REGRD: SEP R0 STXD DEC R0 DEC R0 LDA R0 SMI #01 DEC R0 STR R0 XRI #82 .. stop after R3.0 is stored (GLO R2 opcode) BNZ REGRD .. Initialize the registers to start the operating system GHI R2 PHI R3 LDI A.0(MAIN) PLO R3 SEP R3 .. go to MAIN MAIN: GHI R0 PHI R2 .. R2.1 = top of memory (stack) PHI RB .. RB.1 = top of memory (video) PHI RD .. RD.1 = top of memory (display mem) LDI #81 .. point subroutines to page 2 of ROM PHI R1 .. #81 -> R1.1 PHI R4 .. #81 -> R4.1 PHI R5 .. #81 -> R5.1 PHI R7 .. #81 -> R7.1 PHI RA .. #81 -> RA.1 PHI RC .. #81 -> RC.1 LDI A.0(INT) PLO R1 .. set interrupt pointer LDI STKTOP PLO R2 .. set stack pointer LDI A.0(DISPD) PLO R4 .. R4 = display data LDI A.0(DISPW) PLO R5 .. R5 = write character to display memory LDI A.0(GETB) .. RA = get 2 hex digits into a byte PLO R7 LDI A.0(DBNCE) .. RC = keyboard debounce initially .. = keyboard scan after first call PLO RC SEX R2 .. set R2 as stack pointer INP 1 .. turn display on .. Enter address SEP RC .. call keypad debounce for the 'C' key SEP R7 .. get address upper byte SEP R7 .. routine must be called 3 times SEP R7 .. because R7 returns to main PHI R6 .. put high byte in R6 SEP R7 .. get address lower byte SEP R7 SEP R7 PLO R6 .. R6 = address / file number entered by user SEP R4 .. display address .. get user command SEP RC .. keyboard scan PHI RE .. store control value (key) in RE.1 BZ MWR .. Memory write? XRI #0A BZ MRD .. Memory read? .. Flash Read/Write command? DEC R2 OUT 1 .. turn off the display so Q can be used for chip select GHI R6 PHI R7 .. copy file number into R7 GLO R6 PLO R7 .. copy file number into R7 .. shift R7 left by 4 to create a flash page address LDI #04 PLO RE SETPG: GLO R7 SHL PLO R7 GHI R7 SHLC PHI R7 .. R7 = R7 << 1 DEC RE GLO RE BNZ SETPG SEX R3 OUT 5 .. connect Q to Flash CS ,#03 SEX R2 LDI #00 .. set address pointer (R6) to start of RAM PLO R6 PHI R6 .. put 24 bit flash address on stack DEC R2 STXD .. page address (7-0) = #00 GLO R7 STXD .. page address (15-8) GHI R7 STXD .. page address (23-16) GHI RE .. get control value XRI #0B .. FLASH READ? BZ FRD GHI RE XRI #0F BZ FWR .. FLASH WRITE? .. can put other commands here BR $ .. no, invalid command, go into endless loop .. SERIAL FLASH ROUTINES .. for Winbond W25Q128 Serial Flash (16MB) .. the serial flash replaces the cassette tape interface .. the first 2MB of the serial flash is reserved for the VIP OS (000000-1FFFFF) .. up to 512 memory (4K) images can be saved in the serial flash (0000-01FF) .. Flash Write Routine .. erase a 4K sector, then write entire 4K memory image to it .. R2 = stack pointer .. R6 = memory address .. R9 = sector page address (0000-1FF0) .. The serial flash can only write one page (256 bytes) at a time .. So, 16 page writes are needed to write a complete 4K sector FWR: LDI CMDERA STR R2 .. save erase sector command on stack LDI A.0(WRENA) PLO RC SEP RC .. send write enable for erase sector LDI A.0(SNDCMD) PLO RC SEP RC .. send command REQ .. deassert chip select WRTPG: DEC R2 .. back up stack pointer LDI #00 STXD .. page address (7-0) is always 0 GLO R7 .. page address (15-8) STXD GHI R7 .. page address (23-16) STXD LDI CMDWR STR R2 .. write page command LDI A.0(FWAIT) PLO RC SEP RC .. wait for flash to be ready LDI A.0(WRENA) PLO RC SEP RC .. send write enable for page write LDI A.0(SNDCMD) PLO RC SEP RC .. send write page command .. send page (256 bytes) of data SEX R6 .. point X to memory WRLOOP: OUT 7 .. send it out, inc R6 GLO R6 .. on next page? BNZ WRLOOP .. no, keep going REQ .. yes, deassert chip select SEX R2 .. restore X INC R7 .. go to next page of flash GLO R7 ANI #0F .. was last page of sector sent (R7 == #xxx0)? BNZ WRTPG .. no, continue FDONE: INP 1 .. turn video back on DEC R6 .. get last byte SEP R4 .. and display it BR $ .. endless loop .. Flash Read Routine .. read a 4K sector and store in memory .. R2 = stack pointer .. R6 = memory address FRD: LDI CMDRD STR R2 .. read command LDI A.0(SNDCMD) PLO RC SEP RC .. send read page command DEC R2 LDI #00 STR R2 RDLOOP: OUT 7 .. send zero byte to read in data INP 7 .. get SPI byte read and put in memory STR R6 INC R6 DEC R2 .. restore pointer GHI R6 XRI #10 .. at top of memory (#1000)? BNZ RDLOOP .. no, continue REQ .. yes, deassert chip select BR FDONE .. display last byte and spin .. MEMORY ROUTINES .. Memory read .. displays the next byte with a press of any key MRD: SEP RC .. get any key from keyboard INC R6 .. go to next address SEP R4 .. display byte BR MRD .. Memory Write MWR: SEP R7 .. get byte to write SEP R7 SEP R7 STR R6 .. store in the address SEP R4 .. display byte INC R6 .. next address BR MWR .. end of main routine .. filler ,#00 ,#00 ,#00 ,#00 ,#00 ,#00 ORG #8100 .. CHARACTER MAP LOOK UP TABLE .. offsets to the character data CH0: ,A.0(DIG0) CH1: ,A.0(DIG1) CH2: ,A.0(DIG2) CH3: ,A.0(DIG3) CH4: ,A.0(DIG4) CH5: ,A.0(DIG5) CH6: ,A.0(DIG6) CH7: ,A.0(DIG7) CH8: ,A.0(DIG8) CH9: ,A.0(DIG9) CHA: ,A.0(DIGA) CHB: ,A.0(DIGB) CHC: ,A.0(DIGC) CHD: ,A.0(DIGD) CHE: ,A.0(DIGE) CHF: ,A.0(DIGF) .. CHARACTER MAP .. each character is 5 bytes .. in a 8w x 5h matrix .. the characters are 4w by 5h, left justified DIGE: ,#F0 .. ****.... ,#80 .. *....... DIGF: ,#F0 .. ****.... ,#80 .. *....... DIGC: ,#F0 .. ****.... ,#80 .. *....... ,#80 .. *....... ,#80 .. *....... DIGB: ,#F0 .. ****.... ,#50 .. .*.*.... ,#70 .. .***.... ,#50 .. .*.*.... DIGD: ,#F0 .. ****.... ,#50 .. .*.*.... ,#50 .. .*.*.... ,#50 .. .*.*.... DIG5: ,#F0 .. ****.... ,#80 .. *....... DIG2: ,#F0 .. ****.... ,#10 .. ...*.... DIG6: ,#F0 .. ****.... ,#80 .. *....... DIG8: ,#F0 .. ****.... ,#90 .. *..*.... DIG9: ,#F0 .. ****.... ,#90 .. * *.... DIG3: ,#F0 .. ****.... ,#10 .. ...*.... ,#F0 .. ****.... ,#10 .. ...*.... DIGA: ,#F0 .. ****.... ,#90 .. *..*.... DIG0: ,#F0 .. ****.... ,#90 .. *..*.... ,#90 .. *..*.... ,#90 .. *..*.... DIG7: ,#F0 .. ****.... ,#10 .. ...*.... ,#10 .. ...*.... ,#10 .. ...*.... ,#10 .. ...*.... DIG1: ,#60 .. .**..... ,#20 .. ..*..... ,#20 .. ..*..... ,#20 .. ..*..... ,#70 .. .***.... DIG4: ,#A0 .. *.*..... ,#A0 .. *.*..... ,#F0 .. ****.... ,#20 .. ..*..... ,#20 .. ..*..... .. END OF CHARACTER MAP .. video interrupt (64 x 32 format) INTX: REQ .. tone off LDA R2 .. get D from stack RET .. exit and re-enable interrupt INT: DEC R2 SAV .. T-> STACK DEC R2 STR R2 .. D-> STACK .. note DF is not changed by this routine NOP .. 3 cycle instruction to synchronize INC R9 .. increment random number value LDI #00 PLO R0 GHI RB PHI R0 .. reset R0 (DMA pointer) to video memory SEX R2 .. NOP SEX R2 .. NOP DISP: GLO R0 .. LINE START ADDR -> D SEX R2 SEX R2 DEC R0 .. reset R0.1 if pass page PLO R0 .. line start addr -> R0.0 SEX R2 .. nop DEC R0 .. reset R0.1 if pass page PLO R0 .. line start addr -> R0.0 SEX R2 .. nop DEC R0 .. reset R0.1 if pass page PLO R0 .. line start addr -> R0.0 BN1 DISP .. loops 32 times .. decrement timers every video frame (~16ms) .. R8.1 is a regular timer .. R8.0 is the tone timer TIMER: GHI R8 BZ TIMX PLO RB DEC RB GLO RB PHI R8 .. dec timer.1 without changing DF TIMX: GLO R8 BZ INTX .. exit with tone off if timer = 0 SEQ .. turn tone on if timer > 0 DEC R8 .. decrement tone timer BR INTX+1 .. exit with tone on .. wait for serial flash to be ready by continuous reads of the status register SEP R3 FWAIT: SEX RC .. point to ROM SEQ .. assert chip select OUT 7 .. send read status command ,CMDSTA FWLOOP: OUT 7 .. write dummy value ,#00 INP 7 .. get status byte, write to ROM (RX) ignored SHR BDF FWLOOP .. wait for busy bit to clear REQ .. deassert chip select SEX R2 .. restore X register BR FWAIT-1 .. send write enable command SEP R3 WRENA: SEX RC .. point to ROM SEQ .. assert chip select OUT 7 .. send write enable command ,CMDWEN REQ .. deassert chip select SEX R2 .. restore X register BR WRENA-1 .. assert chip select, then send command and address SEP R3 SNDCMD: SEQ .. assert chip select OUT 7 .. send command byte OUT 7 .. send address 23-16 OUT 7 .. send address 15-8 OUT 7 .. send address 7-0 BR SNDCMD-1 .. filler ,#00 ,#00 ,#00 ,#00 ,#00 ,#00 .. keyboard scan SEP R3 KSCN: SEX R2 .. this subroutine uses the stack GHI RC PLO RF .. #81 -> RF.0 KSCN1: DEC RF .. next key value DEC R2 .. move stack down because OUT increments it GLO RF STR R2 .. store RF.0 on stack OUT 2 .. set keypad latch value, INC R2 SEX R2 SEX R2 .. pause BN3 KSCN1 .. alternative entry point to just debounce DBNCE: LDI #04 PLO R8 .. set R8.0 to debounce timer (~66ms) GLO R8 BNZ $-1 .. wait for timeout DBNCE1: LDI #04 PLO R8 .. additional timing operation B3 DBNCE1 .. wait for key GLO R8 .. pause BQ $-3 .. if Q = 1, R8.0 timer is still running GLO RF .. get scan value ANI #0F .. mask off lower byte STR R2 .. store on the stack BR KSCN-1 .. exit .. filler ,#00 ,#00 ,#00 ,#00 .. assemble two keyboard entries into a byte .. note: this needs to be called 3 times to get through it completely .. because the keyboard scan keeps returning to main .. SEP R3 GETB: SEP RC .. get first digit (end 3rd SEP R7) SHL .. (end 1st SEP R7) SHL SHL SHL PLO RE .. put it in the high nibble SEP RC .. get second digit GLO RE .. (end 2nd SEP R7) OR .. combine the digits BR GETB-1 .. and return with digits in D .. write digit character to the display memory SEP R4 DISPW: PLO RA .. put digit to display in RA.0 LDN RA .. offset into character map PLO RA .. get first byte of character data LDI #05 PLO RF .. set row counter for character data STRDM: LDA RA .. get byte STR RD .. save in display memory GLO RD ADI #08 PLO RD .. offset to next display line (64 bits = 8 bytes) DEC RF GLO RF BNZ STRDM .. all 5 rows? GLO RD ADI NXTCHR .. sets RD to 1 byte to the right PLO RD .. so characters do not overlap BR DISPW-1 .. subroutine to display data address and contents on screen SEP R3 DISPD: DEC R2 .. dec stack pointer LDN R6 STXD .. store byte on stack addressed by R6 GLO R6 STXD .. store lower byte of address GHI R6 STR R2 .. store upper byte of address LDI #06 PLO RE .. set special control byte LDI LINE27 .. point to location for address display PLO RD .. in RD DSPBY: LDN R2 .. get upper byte SHR SHR SHR SHR .. get upper nibble SEP R5 .. display the digit LDA R2 ANI #0F .. get lower nibble SEP R5 .. display the digit GLO RE SHR PLO RE .. shift over control byte BZ DISPD-1 .. exit if all digits written BNF DSPBY .. DF=0 after writing high address byte .. else, provide space between the address and data INC RD INC RD BR DSPBY .. next byte ,#01 .. filler and check byte END