URL
https://opencores.org/ocsvn/raptor64/raptor64/trunk
Subversion Repositories raptor64
[/] [raptor64/] [trunk/] [software/] [sample code/] [bootrom.s] - Rev 46
Compare with Previous | Blame | View Log
; ============================================================================ ; __ ; \\__/ o\ (C) 2012-2013 Robert Finch, Stratford ; \ __ / All rights reserved. ; \/_// robfinch<remove>@opencores.org ; || ; ; ; This source file is free software: you can redistribute it and/or modify ; it under the terms of the GNU Lesser General Public License as published ; by the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; This source file is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see <http://www.gnu.org/licenses/>. ; ; ============================================================================ ; CR EQU 0x0D ;ASCII equates LF EQU 0x0A TAB EQU 0x09 CTRLC EQU 0x03 CTRLH EQU 0x08 CTRLI EQU 0x09 CTRLJ EQU 0x0A CTRLK EQU 0x0B CTRLM EQU 0x0D CTRLS EQU 0x13 CTRLX EQU 0x18 XON EQU 0x11 XOFF EQU 0x13 EX_IRQ EQU 449 DATA_PRESENT EQU 0x01 ; there is data preset at the serial port bc_uart3 XMIT_NOT_FULL EQU 0x20 BUFLEN EQU 80 ; length of keyboard input buffer ; Initial stack tops for contexts ; Each context gets 1k from the special 16k startup stack memory ; STACKTOP0 EQU 0xFFFF_FFFF_FFFE_FFF8 STACKTOP1 EQU 0xFFFF_FFFF_FFFE_FBF8 STACKTOP2 EQU 0xFFFF_FFFF_FFFE_F7F8 STACKTOP3 EQU 0xFFFF_FFFF_FFFE_F3F8 STACKTOP4 EQU 0xFFFF_FFFF_FFFE_EFF8 STACKTOP5 EQU 0xFFFF_FFFF_FFFE_EBF8 STACKTOP6 EQU 0xFFFF_FFFF_FFFE_E7F8 STACKTOP7 EQU 0xFFFF_FFFF_FFFE_E3F8 STACKTOP8 EQU 0xFFFF_FFFF_FFFE_DFF8 STACKTOP9 EQU 0xFFFF_FFFF_FFFE_DBF8 STACKTOP10 EQU 0xFFFF_FFFF_FFFE_D7F8 STACKTOP11 EQU 0xFFFF_FFFF_FFFE_D3F8 STACKTOP12 EQU 0xFFFF_FFFF_FFFE_CFF8 STACKTOP13 EQU 0xFFFF_FFFF_FFFE_CBF8 STACKTOP14 EQU 0xFFFF_FFFF_FFFE_C7F8 STACKTOP15 EQU 0xFFFF_FFFF_FFFE_C3F8 ; BOOT ROM routines TCBSize EQU 0x200 ; 512 bytes per TCB TCBBase EQU 0x00000001_00000000 ; TCB pages TCBr1 EQU 0x00 TCBr2 EQU 0x08 TCBr3 EQU 0x10 TCBr4 EQU 0x18 TCBr5 EQU 0x20 TCBr6 EQU 0x28 TCBr7 EQU 0x30 TCBr8 EQU 0x38 TCBr9 EQU 0x40 TCBr10 EQU 0x48 TCBr11 EQU 0x50 TCBr12 EQU 0x58 TCBr13 EQU 0x60 TCBr14 EQU 0x68 TCBr15 EQU 0x70 TCBr16 EQU 0x78 TCBr17 EQU 0x80 TCBr18 EQU 0x88 TCBr19 EQU 0x90 TCBr20 EQU 0x98 TCBr21 EQU 0xA0 TCBr22 EQU 0xA8 TCBr23 EQU 0xB0 TCBr24 EQU 0xB8 TCBr25 EQU 0xC0 TCBr26 EQU 0xC8 TCBr27 EQU 0xD0 TCBr28 EQU 0xD8 TCBr29 EQU 0xE0 TCBr30 EQU 0xE8 TCBr31 EQU 0xF0 SCREENGATE EQU 0x00 KEYBDGATE EQU 0x01 VIDEOGATE EQU 0x02 CARDGATE EQU 0x03 warmStart EQU 0x1020 usrJmp EQU 0x1028 TickIRQAddr EQU 0x1030 TaskBlock EQU 0x1038 Milliseconds EQU 0x1400 Lastloc EQU 0x1408 CharColor EQU 0x1410 ScreenColor EQU 0x1414 CursorRow EQU 0x1417 CursorCol EQU 0x1418 CursorFlash EQU 0x141A KeybdEcho EQU 0x141C KeybdBuffer EQU 0x1440 KeybdHead EQU 0x1450 KeybdTail EQU 0x1451 sp_save EQU 0x1460 lr_save EQU 0x1468 r1_save EQU 0x1470 r2_save EQU 0x1478 r26_save EQU 0x1480 Score EQU 0x1500 Manpos EQU 0x1508 MissileActive EQU 0x1510 MissileX EQU 0x1512 MissileY EQU 0x1514 InvadersRow1 EQU 0x1520 InvadersRow2 EQU 0x1530 InvadersRow3 EQU 0x1540 InvadersRow4 EQU 0x1550 InvadersRow5 EQU 0x1560 InvadersColpos EQU 0x1570 InvadersRowpos EQU 0x1571 Uart_rxfifo EQU 0x1600 Uart_rxhead EQU 0x1800 Uart_rxtail EQU 0x1802 Uart_ms EQU 0x1808 Uart_rxrts EQU 0x1809 Uart_rxdtr EQU 0x180A Uart_rxxon EQU 0x180B Uart_rxflow EQU 0x180C Uart_fon EQU 0x180E Uart_foff EQU 0x1810 Uart_txrts EQU 0x1812 Uart_txdtr EQU 0x1813 Uart_txxon EQU 0x1814 Uart_txxonoff EQU 0x1815 TaskList EQU 0x2000 ReadyList1 EQU 0x2000 ReadyList2 EQU 0x2020 ReadyList3 EQU 0x2040 ReadyList4 EQU 0x2060 ReadyList5 EQU 0x2080 ReadyNdx1 EQU 0x20A0 ReadyNdx2 EQU 0x20A1 ReadyNdx3 EQU 0x20A2 ReadyNdx4 EQU 0x20A3 ReadyNdx5 EQU 0x20A4 RunningTCB EQU 0x20A6 NextToRunTCB EQU 0x20A8 r1save EQU 0x20B0 r2save EQU 0x20B8 AXCstart EQU 0x20C0 ; Context startup address table ; ctx0start EQU 0x20D0 ctx1start EQU 0x20D8 ctx2start EQU 0x20E0 ctx3start EQU 0x20E8 ctx4start EQU 0x20F0 ctx5start EQU 0x20F8 ctx6start EQU 0x2100 ctx7start EQU 0x2108 ctx8start EQU 0x2110 ctx9start EQU 0x2118 ctx10start EQU 0x2120 ctx11start EQU 0x2128 ctx12start EQU 0x2130 ctx13start EQU 0x2138 ctx14start EQU 0x2140 ctx15start EQU 0x2148 sp_saves EQU 0x2200 sp_saves_end EQU 0x2280 p100IRQvec EQU 0x3000 keybdIRQvec EQU 0x3008 serialIRQvec EQU 0x3010 rasterIRQvec EQU 0x3018 startSector EQU 0x30F8 BPB EQU 0x3100 TEXTSCR EQU 0xD0_0000 COLORSCR EQU 0xD1_0000 TEXTREG EQU 0xDA_0000 TEXT_COLS EQU 0x0 TEXT_ROWS EQU 0x2 TEXT_CURPOS EQU 0x16 KEYBD EQU 0xDC_0000 KEYBDCLR EQU 0xDC_0002 UART EQU 0xDC_0A00 UART_LS EQU 0xDC_0A01 UART_MS EQU 0xDC_0A02 UART_IS EQU 0xDC_0A03 UART_IE EQU 0xDC_0A04 UART_MC EQU 0xDC_0A06 DATETIME EQU 0xDC_0400 SPIMASTER EQU 0xDC_0500 SPI_MASTER_VERSION_REG EQU 0x00 SPI_MASTER_CONTROL_REG EQU 0x01 SPI_TRANS_TYPE_REG EQU 0x02 SPI_TRANS_CTRL_REG EQU 0x03 SPI_TRANS_STATUS_REG EQU 0x04 SPI_TRANS_ERROR_REG EQU 0x05 SPI_DIRECT_ACCESS_DATA_REG EQU 0x06 SPI_SD_ADDR_7_0_REG EQU 0x07 SPI_SD_ADDR_15_8_REG EQU 0x08 SPI_SD_ADDR_23_16_REG EQU 0x09 SPI_SD_ADDR_31_24_REG EQU 0x0a SPI_RX_FIFO_DATA_REG EQU 0x10 SPI_RX_FIFO_DATA_COUNT_MSB EQU 0x12 SPI_RX_FIFO_DATA_COUNT_LSB EQU 0x13 SPI_RX_FIFO_CTRL_REG EQU 0x14 SPI_TX_FIFO_DATA_REG EQU 0x20 SPI_TX_FIFO_CTRL_REG EQU 0x24 SPI_INIT_SD EQU 0x01 SPI_TRANS_START EQU 0x01 SPI_TRANS_BUSY EQU 0x01 SPI_INIT_NO_ERROR EQU 0x00 SPI_READ_NO_ERROR EQU 0x00 RW_READ_SD_BLOCK EQU 0x02 RW_WRITE_SD_BLOCK EQU 0x03 PIC EQU 0xDC_0FF0 PIC_IE EQU 0xDC_0FF2 PSG EQU 0xD5_0000 PSGFREQ0 EQU 0xD5_0000 PSGPW0 EQU 0xD5_0002 PSGCTRL0 EQU 0xD5_0004 PSGADSR0 EQU 0xD5_0006 SPRRAM EQU 0xD8_0000 AC97 EQU 0xDC_1000 TMP EQU 0xDC_0300 LED EQU 0xDC_0600 ETHMAC EQU 0xDC_2000 CONFIGREC EQU 0xDC_FFFF MIIMODER EQU 0x28 MIIADDRESS EQU 0x30 GACCEL EQU 0xDA_E000 RASTERIRQ EQU 0xDA_0100 BOOT_STACK EQU 0xFFFF_FFFF_FFFE_FFF8 SPRITEREGS EQU 0xDA_D000 BITMAPSCR EQU 0x00000001_00200000 BOOTJMP EQU 0x100800204 txempty EQU 0x40 rxfull EQU 0x01 ; ; Internal variables follow: ; bss org 0x1048 txtWidth db 0 ; BIOS var =56 txtHeight db 0 ; BIOS var =31 cursx db 0 ; cursor x position cursy db 0 ; cursor y position pos dh 0 ; text screen position charToPrint dc 0 fgColor db 0 bkColor db 0 cursFlash db 0 ; flash the cursor ? lineLinkTbl fill.b 47,0 ; screen line link table typef db 0 ; variable / expression type align 8 OSSP dw 1 ; OS value of sp CURRNT dw 1 ; Current line pointer STKGOS dw 1 ; Saves stack pointer in 'GOSUB' STKINP dw 1 ; Saves stack pointer during 'INPUT' LOPVAR dw 1 ; 'FOR' loop save area LOPINC dw 1 ; increment LOPLMT dw 1 ; limit LOPLN dw 1 ; line number LOPPT dw 1 ; text pointer TXTUNF dw 1 ; points to unfilled text area VARBGN dw 1 ; points to variable area IVARBGN dw 1 ; points to integer variable area SVARBGN dw 1 ; points to string variable area FVARBGN dw 1 ; points to float variable area STKBOT dw 1 ; holds lower limit for stack growth NUMWKA fill.b 24,0 ; numeric work area BUFFER fill.b BUFLEN,0x00 ; Keyboard input buffer bss org 0x1_00600000 TXT equ 0x1_00600000 ; Beginning of program area ; org 0x070 ; iret ; nop ; nop ; nop ; nop ; nop ; nop ; nop ; code org 0xFFFF_FFFF_FFFF_B000 ; jump table ; jmp SerialGetChar jmp SerialPutChar jmp SetKeyboardEcho jmp KeybdCheckForKey jmp KeybdGetChar jmp DisplayChar jmp DisplayString jmp DisplayNum jmp CalcScreenLoc jmp ClearScreen jmp DisplayWord start: ; lea MSGRAM,a1 ; jsr DisplayString ColdStart: icache_off ; turn on the ICache dcache_off ; turn on the DCache ; Make sure semaphores are available by closing the gates. ; We don't know what power up state is. cmgi #KEYBDGATE cmgi #VIDEOGATE ; Initialize the context startup address table with NULL xor r1,r1,r1 sw r1,ctx0start sw r1,ctx1start sw r1,ctx2start sw r1,ctx3start sw r1,ctx4start sw r1,ctx5start sw r1,ctx6start sw r1,ctx7start sw r1,ctx8start sw r1,ctx9start sw r1,ctx10start sw r1,ctx11start sw r1,ctx12start sw r1,ctx13start sw r1,ctx14start sw r1,ctx15start ; Initialize the context schedule with all contexts treated equally ; There are only 16 contexts, but 256 schedule slots. Each context is ; given 16 slots distributed evenly throughout the execution pattern ; table. ; xor r1,r1,r1 ; r1 = 0 ict1: mtep r1,r1 ; only the low order four bits of r1 will move to the pattern table addui r1,r1,#1 cmpi r2,r1,#255 bne r2,r0,ict1 ; Point the interrupt return address register of the context to the ; context startup code. The context will start up when an interrupt return ; occurs. ; ; We cannot use a loop for this. Fortunately there's only 16 contexts. ; lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP0 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP1 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP2 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP3 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP4 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP5 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP6 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP7 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP8 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP9 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP10 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP11 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP12 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP13 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP14 iepp lea r25,ctxstart mtspr IPC,r25 lea r30,STACKTOP15 iepp ; Ensure that context zero is the active context ; ctxstart3: mfspr r1,AXC beq r1,r0,ctxstart2 iepp bra ctxstart3 ctxstart2: sb r1,AXCstart ; save off the startup context which should be context zero ; Entry point for context startup ; ; Avoid repeating all the system initialization when a context starts up by testing whether ; or not the context is the starting context. ; ctxstart: mfspr r1,AXC lbu r2,AXCstart bne r1,r2,ctxstart1 ; ; set system vectors ; TBA defaults to zero on reset ; setlo r3,#0 setlo r2,#511 lea r1,nmirout csj5: sw r1,[r3] addui r3,r3,#8 loop r2,csj5 lea r1,VideoSC ; Video BIOS vector sw r1,0xCD0 lea r1,SCCARDSC ; SD Card BIOS vector sw r1,0xCE8 lea r1,RTCSC ; Real time clock vector sw r1,0xD00 lea r1,KeybdSC ; keyboard BIOS vector sw r1,0xD08 lea r1,irqrout sw r1,0xE08 ; set IRQ vector lea r1,ui_irout sw r1,0xF78 ; set unimplemented instruction vector lea r1,dberr_rout sw r1,0xFE0 ; set Bus error vector lea r1,iberr_rout sw r1,0xFE8 ; set Bus error vector lea r1,nmirout sw r1,0xFF0 ; set NMI vector ; set system interrupt hook vectors lea r1,KeybdIRQ sw r1,keybdIRQvec lea r1,Pulse100 sw r1,p100IRQvec lea r1,SerialIRQ sw r1,serialIRQvec lea r1,RasterIRQfn sw r1,rasterIRQvec ;------------------------------- ; Initialize I/O devices ;------------------------------- inbu r1,CONFIGREC bfext r1,r1,#4,#4 beq r1,r0,skip5 call tmp_init skip5: inbu r1,CONFIGREC bfext r1,r1,#5,#5 beq r1,r0,skip4 call SerialInit skip4: call KeybdInit call PICInit call SetupRasterIRQ cli ; enable interrupts ; call HelloWorld setlo r3,#0xCE ; blue on blue sc r3,ScreenColor sc r3,CharColor lc r3,0x1414 setlo r3,#32 sc r3,0x1416 ; we do a store, then a load through the dcache lc r2,0x1416 ; beq r2,r3,dcokay dcache_off ; data cache failed dcokay: sc r0,NextToRunTCB sc r0,RunningTCB lw r1,#2 ; get rid of startup keyboard glitchs by trying to get a character syscall #417 lw r1,#2 ; get rid of startup keyboard glitchs by trying to get a character syscall #417 ; wait for screen to be available call ClearScreen call ClearBmpScreen ; Test whether or not the sprite controller is present. Skip ; Initialization if it isn't. inb r1,CONFIGREC bfext r1,r1,#0,#0 beq r1,r0,skip1 call RandomizeSprram skip1: sb r0,CursorRow sb r0,CursorCol lw r1,#1 sb r1,CursorFlash lea r1,MSGSTART call DisplayStringCRLF ; Test whether or not sound generator is present ; skip initialization and beep if not present inb r1,CONFIGREC bfext r1,r1,#2,#2 beq r1,r0,skip2 call SetupAC97 ; and Beep lw r1,#4 outb r1,LED call Beep skip2: lea r1,context1disp ; start a display sw r1,ctx1start ; Startup Ethernet access ? ; inb r1,CONFIGREC bfext r1,r1,#1,#1 beq r1,r0,skip3 lea r1,eth_main sw r1,ctx2start skip3: lea r1,RandomLines sw r1,ctx3start call spi_init bne r1,r0,skip_spi_read call spi_read_boot call loadBootFile skip_spi_read: jmp Monitor j4: jmp Monitor bra j4 ; The contexts wait for a context startup address to be placed in the ; startup table. Once an address is in the table, a call to the context ; code will be made. The default is a NULL pointer, which ; causes the context to loop around back to here while waiting for a ; code to run. ; ctxstart1: lea r1,ctx0start ; r1 = context start table base mfspr r2,AXC ; r2 = index into start table lw r1,[r1+r2*8] ; r1 = context start address beq r1,r0,ctx12 jal lr,[r1] ; perform a call to the context code ; We might as well move to the next context, since there's nothing ; to do. This can be accomplished by tirggering a IRQ interrupt. ; We can't just increment the excution pattern pointer, because that ; would only switch the register set and not the program counter. ; An interrupt saves the program counter, and restores it from the ; IPC context register. ; ctx12: sei ; causes a priv violation. don't allow interrupts during syscall nop ; wait for sei to take effect nop nop syscall #EX_IRQ bra ctxstart1 ; call ramtest context1disp: ; once we've started, clear the start vector so that the context ; isn't continuously restarted. ; sw r0,ctx1start lea r3,TEXTSCR lw r1,#'V' lw r2,#330 lw r4,#47 call AsciiToScreen ctx11: inch r1,[r3+r2] addui r1,r1,#1 outc r1,[r3+r2] addui r2,r2,#168 loop r4,ctx11 bra context1disp ;----------------------------------------- ; Hello World! ;----------------------------------------- HelloWorld: subui r30,r30,#24 sw r1,[sp] sw r2,8[sp] sw lr,16[sp] lea r2,MSG j3: lb r1,[r2] beq r1,r0,j2 call SerialPutChar addui r2,r2,#1 bra j3 j2: sw lr,16[sp] sw r2,8[sp] sw r1,[sp] ret #24 align 16 MSG: db "Hello World!",0 MSGSTART: db "Raptor64 system starting....",0 align 16 ;---------------------------------------------------------- ; Initialize programmable interrupt controller (PIC) ; 0 = nmi (parity error) ; 1 = keyboard reset ; 2 = 1000Hz pulse (context switcher) ; 3 = 100Hz pulse (cursor flash) ; 4 = ethmac ; 8 = uart ; 13 = raster interrupt ; 15 = keyboard char ;---------------------------------------------------------- PICInit: lea r1,PICret sw r1,TickIRQAddr ; enable: raster irq, setlo r1,#0x800F ; enable nmi,kbd_rst,and kbd_irq ; A10F enable serial IRQ outc r1,PIC_IE PICret: ret ;============================================================================== ; Serial port ;============================================================================== ;----------------------------------------- ; Initialize the serial port ;----------------------------------------- ; SerialInit: sc r0,Uart_rxhead ; reset buffer indexes sc r0,Uart_rxtail setlo r1,#0x1f0 sc r1,Uart_foff ; set threshold for XOFF setlo r1,#0x010 sc r1,Uart_fon ; set threshold for XON setlo r1,#1 outb r1,UART_IE ; enable receive interrupt only sb r0,Uart_rxrts ; no RTS/CTS signals available sb r0,Uart_txrts ; no RTS/CTS signals available sb r0,Uart_txdtr ; no DTR signals available sb r0,Uart_rxdtr ; no DTR signals available setlo r1,#1 sb r1,Uart_txxon ; for now ret ;--------------------------------------------------------------------------------- ; Get character directly from serial port. Blocks until a character is available. ;--------------------------------------------------------------------------------- ; SerialGetCharDirect: sgc1: inb r1,UART_LS ; uart status andi r1,r1,#rxfull ; is there a char available ? beq r1,r0,sgc1 inb r1,UART ret ;------------------------------------------------ ; Check for a character at the serial port ; returns r1 = 1 if char available, 0 otherwise ;------------------------------------------------ ; SerialCheckForCharDirect: inb r1,UART_LS ; uart status andi r1,r1,#rxfull ; is there a char available ? sne r1,r1,r0 ret ;----------------------------------------- ; Put character to serial port ; r1 = char to put ;----------------------------------------- ; SerialPutChar: subui sp,sp,#32 sw r2,[sp] sw r3,8[sp] sw r4,16[sp] sw r5,24[sp] inb r2,UART_MC ori r2,r2,#3 ; assert DTR / RTS outb r2,UART_MC lb r2,Uart_txrts beq r2,r0,spcb1 lw r4,Milliseconds setlo r3,#100 ; delay count (1 s) spcb3: inb r2,UART_MS andi r2,r2,#10 ; is CTS asserted ? bne r2,r0,spcb1 lw r5,Milliseconds beq r4,r5,spcb3 mov r4,r5 loop r3,spcb3 bra spcabort spcb1: lb r2,Uart_txdtr beq r2,r0,spcb2 lw r4,Milliseconds setlo r3,#100 ; delay count spcb4: inb r2,UART_MS andi r2,r2,#20 ; is DSR asserted ? bne r2,r0,spcb2 lw r5,Milliseconds beq r4,r5,spcb4 mov r4,r5 loop r3,spcb4 bra spcabort spcb2: lb r2,Uart_txxon beq r2,r0,spcb5 spcb6: lb r2,Uart_txxonoff beq r2,r0,spcb5 inb r4,UART_MS andi r4,r4,#0x80 ; DCD ? bne r4,r0,spcb6 spcb5: lw r4,Milliseconds setlo r3,#100 ; wait up to 1s spcb8: inb r2,UART_LS andi r2,r2,#0x20 ; tx not full ? bne r2,r0,spcb7 lw r5,Milliseconds beq r4,r5,spcb8 mov r4,r5 loop r3,spcb8 bra spcabort spcb7: outb r1,UART spcabort: lw r2,[sp] lw r3,8[sp] lw r4,16[sp] lw r5,24[sp] ret #32 ;------------------------------------------------- ; Compute number of characters in recieve buffer. ; r4 = number of chars ;------------------------------------------------- CharsInRxBuf: lc r4,Uart_rxhead lc r2,Uart_rxtail subu r4,r4,r2 bgt r4,r0,cirxb1 setlo r4,#0x200 addu r4,r4,r2 lc r2,Uart_rxhead subu r4,r4,r2 cirxb1: ret ;---------------------------------------------- ; Get character from rx fifo ; If the fifo is empty enough then send an XON ;---------------------------------------------- ; SerialGetChar: subui sp,sp,#32 sw r2,[sp] sw r3,8[sp] sw r4,16[sp] sw lr,24[sp] lc r3,Uart_rxhead lc r2,Uart_rxtail beq r2,r3,sgcfifo1 ; is there a char available ? lea r3,Uart_rxfifo lb r1,[r2+r3] ; get the char from the fifo into r1 addui r2,r2,#1 ; increment the fifo pointer andi r2,r2,#0x1ff sc r2,Uart_rxtail lb r2,Uart_rxflow ; using flow control ? beq r2,r0,sgcfifo2 lc r3,Uart_fon ; enough space in Rx buffer ? call CharsInRxBuf bgt r4,r3,sgcfifo2 sb r0,Uart_rxflow ; flow off lb r4,Uart_rxrts beq r4,r0,sgcfifo3 inb r4,UART_MC ; set rts bit in MC ori r4,r4,#2 outb r4,UART_MC sgcfifo3: lb r4,Uart_rxdtr beq r4,r0,sgcfifo4 inb r4,UART_MC ; set DTR ori r4,r4,#1 outb r4,UART_MC sgcfifo4: lb r4,Uart_rxxon beq r4,r0,sgcfifo5 setlo r4,#XON outb r4,UART sgcfifo5: sgcfifo2: ; return with char in r1 lw r2,[sp] lw r3,8[sp] lw r4,16[sp] lw lr,24[sp] ret #32 sgcfifo1: setlo r1,#-1 ; no char available lw r2,[sp] lw r3,8[sp] lw r4,16[sp] lw lr,24[sp] ret #32 ;----------------------------------------- ; Serial port IRQ ;----------------------------------------- ; SerialIRQ: subui sp,sp,#40 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw r4,24[sp] sw lr,32[sp] inb r1,UART_IS ; get interrupt status bge r1,r0,sirq1 andi r1,r1,#0x7f ; switch on interrupt type beqi r1,#4,srxirq beqi r1,#0xC,stxirq beqi r1,#0x10,smsirq sirq1: lw r1,[sp] lw r2,8[sp] lw r3,16[sp] lw r4,24[sp] lw lr,32[sp] ret #40 ; Get the modem status and record it smsirq: inb r1,UART_MS sb r1,Uart_ms bra sirq1 stxirq: bra sirq1 ; Get a character from the uart and store it in the rx fifo srxirq: srxirq1: inb r1,UART ; get the char (clears interrupt) lb r2,Uart_txxon beq r2,r0,srxirq3 bnei r1,#XOFF,srxirq2 setlo r1,#1 sb r1,Uart_txxonoff bra srxirq5 srxirq2: bnei r1,#XON,srxirq3 sb r0,Uart_txxonoff bra srxirq5 srxirq3: sb r0,Uart_txxonoff lc r2,Uart_rxhead lea r3,Uart_rxfifo sb r1,[r3+r2] ; store in buffer addui r2,r2,#1 andi r2,r2,#0x1ff sc r2,Uart_rxhead srxirq5: inb r1,UART_LS ; check for another ready character andi r1,r1,#rxfull bne r1,r0,srxirq1 lb r1,Uart_rxflow ; are we using flow controls? bne r1,r0,srxirq8 call CharsInRxBuf lc r1,Uart_foff blt r4,r1,srxirq8 setlo r1,#1 sb r1,Uart_rxflow lb r1,Uart_rxrts beq r1,r0,srxirq6 inb r1,UART_MC andi r1,r1,#0xFD ; turn off RTS outb r1,UART_MC srxirq6: lb r1,Uart_rxdtr beq r1,r0,srxirq7 inb r1,UART_MC andi r1,r1,#0xFE ; turn off DTR outb r1,UART_MC srxirq7: lb r1,Uart_rxxon beq r1,r0,srxirq8 setlo r1,#XOFF outb r1,UART srxirq8: bra sirq1 ;============================================================================== ; Video BIOS ; Video interrupt #410 ; ; Function in R1 ; 0x02 = Set Cursor Position r2 = row, r3 = col ; 0x03 = Get Cursor position returns r1 = row, r2 = col ; 0x06 = Scroll screen up ; 0x09 = Display character+attribute, r2=char, r3=attrib, r4=#times ; 0x0A = Display character, r2 = char, r3 = # times ; 0x0C = Display Pixel r2 = x, r3 = y, r4 = color ; 0x0D = Get pixel r2 = x, r3 = y ; 0x14 = Display String r2 = pointer to string ; 0x15 = Display number r2 = number, r3 = # digits ; 0x16 = Display String + CRLF r2 = pointer to string ; 0x17 = Display Word r2 as hex = word ; 0x18 = Display Half word as hex r2 = half word ; 0x19 = Display Charr char in hex r2 = char ; 0x1A = Display Byte in hex r2 = byte ;============================================================================== ; VideoSC: mfspr r26,AXC ; get context shlui r26,r26,#3 ; *8 sw sp,sp_saves[r26] ; save sp in save area shlui r26,r26,#8 ; 2k for stack mov sp,r26 addui sp,sp,#0x100008000 ; base stacks address subui sp,sp,#8 sw lr,[sp] Video1: omgi lr,#VIDEOGATE bne lr,r0,Video1 beqi r1,#0x02,Video_x02 beqi r1,#0x03,Video_x03 beqi r1,#0x06,Video_x06 beqi r1,#0x09,Video_x09 beqi r1,#0x0A,Video_x0A beqi r1,#0x0C,Video_x0C beqi r1,#0x0C,Video_x0D beqi r1,#0x14,Video_x14 beqi r1,#0x15,Video_x15 beqi r1,#0x16,Video_x16 beqi r1,#0x17,Video_x17 beqi r1,#0x1A,Video_x1A bra VideoRet Video_x02: sb r2,CursorRow sb r3,CursorCol call CalcScreenLoc bra VideoRet Video_x03: lbu r1,CursorRow lbu r2,CursorCol bra VideoRet Video_x06: call ScrollUp bra VideoRet Video_x09: sc r3,CharColor mov r1,r2 Video_x09a: call DisplayChar loop r4,Video_x09a bra VideoRet Video_x0A: mov r1,r2 Video_x0Aa: call DisplayChar loop r3,Video_x0Aa bra VideoRet Video_x0C: sh r2,GACCEL+8 ; x0 sh r3,GACCEL+12 ; y0 sh r4,GACCEL+0 ; color lw r1,#1 sh r1,GACCEL+60 ; DRAW PIXEL command bra VideoRet Video_x0D: sh r2,GACCEL+8 ; x0 sh r3,GACCEL+12 ; y0 lw r1,#8 sh r1,GACCEL+60 ; GET PIXEL command nop ; let command start nop nop vxd1: lhu r1,GACCEL+56 ; wait for state = IDLE bne r1,r0,vxd1 lhu r1,GACCEL+52 bra VideoRet Video_x14: mov r1,r2 call DisplayString bra VideoRet Video_x15: mov r1,r2 mov r2,r3 call DisplayNum bra VideoRet Video_x16: mov r1,r2 call DisplayStringCRLF bra VideoRet Video_x17: mov r1,r2 call DisplayWord bra VideoRet Video_x1A: mov r1,r2 call DisplayByte bra VideoRet VideoRet: cmgi #VIDEOGATE lw lr,[sp] mfspr r26,AXC ; get context shlui r26,r26,#3 ; *8 lw sp,sp_saves[r26] ; get back the stack eret ;============================================================================== ; BIOS interrupt #413 ; 0x00 initialize ; 0x01 read sector r2 = sector #, r3 = pointer to buffer ; 0x02 write sector ;============================================================================== ; SDCARDSC: mfspr r26,AXC ; get context shlui r26,r26,#3 ; *8 sw sp,sp_saves[r26] ; save sp in save area shlui r26,r26,#8 ; 2k for stack mov sp,r26 addui sp,sp,#0x100008000 ; base stacks address subui sp,sp,#8 sw lr,[sp] SDC_1: omgi lr,#CARDGATE bne lr,r0,SDC_1 beqi r1,#0,SDC_x00 beqi r1,#1,SDC_x01 beqi r1,#2,SDC_x02 bra SDCRet SDC_x00: call spi_init bra SDCRet SDC_x01: mov r1,r2 mov r2,r3 call spi_read_sector bra SDCRet SDC_x02: SDCRet: cmgi #CARDGATE lw lr,[sp] mfspr r26,AXC ; get context shlui r26,r26,#3 ; *8 lw sp,sp_saves[r26] ; get back the stack eret ;============================================================================== ; Real time clock BIOS ; BIOS interrupt #416 ; ; Function ; 0x00 = get system tick ; 0x01 = get date/time ; 0x02 = set date/time ;============================================================================== ; RTCSC: mfspr r26,AXC ; get context shlui r26,r26,#3 ; *8 sw sp,sp_saves[r26] ; save sp in save area shlui r26,r26,#8 ; 2k for stack mov sp,r26 addui sp,sp,#0x100008000 ; base stacks address subui sp,sp,#8 sw lr,[sp] ; beqi r1,#0x00,RTC_x00 beqi r1,#0x01,RTC_x01 RTC_x00: mfspr r1,TICK bra RTCRet RTC_x01: outw r0,DATETIME+24 ; trigger a snapshot nop inw r1,DATETIME ; get the snapshotted date and time bra RTCRet RTCRet: lw lr,[sp] mfspr r26,AXC ; get context shlui r26,r26,#3 ; *8 lw sp,sp_saves[r26] ; get back the stack eret ;============================================================================== ; Keyboard BIOS ; BIOS interrupt #417 ; ; Function in R1 ; 0x00 = initialize keyboard ; 0x01 = set keyboard echo ; 0x02 = get keyboard character from buffer ; 0x03 = check for key available in buffer ; 0x04 = check for key directly at keyboard port ; 0x05 = get keyboard character directly from keyboard port (blocks) ;============================================================================== ; KeybdSC: mfspr r26,AXC ; get context shlui r26,r26,#3 ; *8 sw sp,sp_saves[r26] ; save sp in save area shlui r26,r26,#8 ; 2k for stack mov sp,r26 addui sp,sp,#0x100008000 ; base stacks address subui sp,sp,#8 sw lr,[sp] kbdsc5: omgi lr,#KEYBDGATE bne lr,r0,kbdsc5 beqi r1,#0,kbd_x00 beqi r1,#1,kbd_x01 beqi r1,#2,kbd_x02 beqi r1,#3,kbd_x03 beqi r1,#4,kbd_x04 beqi r1,#5,kbd_x05 bra kbdscRet kbd_x00: call KeybdInit bra kbdscRet kbd_x01: mov r1,r2 call SetKeyboardEcho bra kbdscRet kbd_x02: call KeybdGetChar bra kbdscRet kbd_x03: call KeybdCheckForKey bra kbdscRet kbd_x04: call KeybdCheckForKeyDirect bra kbdscRet kbd_x05: call KeybdGetCharDirect bra kbdscRet kbdscRet: cmgi #KEYBDGATE lw lr,[sp] mfspr r26,AXC ; get context shlui r26,r26,#3 ; *8 lw sp,sp_saves[r26] ; get back the stack eret ;------------------------------------------------------------------------------ ; Initialize keyboard ;------------------------------------------------------------------------------ KeybdInit: sb r0,KeybdHead sb r0,KeybdTail setlo r1,#1 ; turn on keyboard echo sb r1,KeybdEcho ret ;------------------------------------------------------------------------------ ; Normal keyboard interrupt, the lowest priority interrupt in the system. ; Grab the character from the keyboard device and store it in a buffer. ;------------------------------------------------------------------------------ ; KeybdIRQ: subui sp,sp,#8 sw r2,[sp] lbu r1,KeybdHead andi r1,r1,#0x0f ; r1 = index into buffer KeybdIRQa: inch r2,KEYBD ; get keyboard character outc r0,KEYBD+2 ; clear keyboard strobe (turns off the IRQ) sb r2,KeybdBuffer[r1] ; store character in buffer addui r1,r1,#1 ; increment head index andi r1,r1,#0x0f sb r1,KeybdHead KeybdIRQb: lbu r2,KeybdTail ; check to see if we've collided bne r1,r2,KeybdIRQc ; with the tail addui r2,r2,#1 ; if so, increment the tail index andi r2,r2,#0x0f ; the oldest character will be lost sb r2,KeybdTail KeybdIRQc: lw r2,[sp] ret #8 ;------------------------------------------------------------------------------ ; r1 0=echo off, non-zero = echo on ;------------------------------------------------------------------------------ SetKeyboardEcho: sb r1,KeybdEcho ret ;----------------------------------------- ; Get character from keyboard buffer ;----------------------------------------- KeybdGetChar: subui sp,sp,#16 sw r2,[sp] sw lr,8[sp] lbu r2,KeybdTail lbu r1,KeybdHead beq r1,r2,nochar lbu r1,KeybdBuffer[r2] addui r2,r2,#1 andi r2,r2,#0x0f sb r2,KeybdTail lb r2,KeybdEcho beq r2,r0,kgc3 bnei r1,#CR,kgc2 call CRLF ; convert CR keystroke into CRLF bra kgc3 kgc2: call DisplayChar bra kgc3 nochar: setlo r1,#-1 kgc3: lw lr,8[sp] lw r2,[sp] ret #16 ;------------------------------------------------------------------------------ ; Check if there is a keyboard character available in the keyboard buffer. ;------------------------------------------------------------------------------ ; KeybdCheckForKey: lbu r1,KeybdTail lbu r2,KeybdHead sne r1,r1,r2 ret ;------------------------------------------------------------------------------ ; Check if there is a keyboard character available. If so return true (1) ; otherwise return false (0) in r1. ;------------------------------------------------------------------------------ ; KeybdCheckForKeyDirect: inch r1,KEYBD slt r1,r1,r0 ret ;------------------------------------------------------------------------------ ; Get character directly from keyboard. This routine blocks until a key is ; available. ;------------------------------------------------------------------------------ ; KeybdGetCharDirect: subui sp,sp,#16 sw r2,[sp] sw lr,8[sp] setlo r2,KEYBD kgc1: inch r1,KEYBD bge r1,r0,kgc1 outc r0,KEYBD+2 ; clear keyboard strobe andi r1,r1,#0xff ; remove strobe bit lb r2,KeybdEcho ; is keyboard echo on ? beq r2,r0,gk1 bnei r1,#'\r',gk2 ; convert CR keystroke into CRLF call CRLF bra gk1 gk2: call DisplayChar gk1: lw r2,[sp] lw lr,8[sp] ret #16 ;============================================================================== ;============================================================================== tmp_init: ; wait for the rst1626 to go low lw r2,#10000000 ; retry for up to several seconds tmp_init4: beq r2,r0,tmp_init5 subui r2,r2,#1 inch r1,TMP+2 ; read the status reg blt r1,r0,tmp_init4 tmp_init5: lw r1,#0x51 ; Start temperature conversion outc r1,TMP ; wait a bit for the trigger to take effect lw r1,#2500 tmp_init1: loop r1,tmp_init1 ; wait for the rst1626 to go low lw r2,#10000000 ; retry for up to several seconds tmp_init2: beq r2,r0,tmp_init3 subui r2,r2,#1 inch r1,TMP+2 ; read the status reg blt r1,r0,tmp_init2 tmp_init3: ret tmp_read: subui sp,sp,#24 sw lr,[sp] sw r1,8[sp] sw r2,16[sp] lw r1,#25000000 ; wait about 1 second or so tmp_read1: loop r1,tmp_read1 lw r1,#0xAC ; issue read temperature conversion outc r1,TMP ; wait a bit for the trigger to take effect lw r1,#2500 tmp_read3: loop r1,tmp_read3 ; wait for the rst1626 to go low lw r2,#10000000 tmp_read2: inch r1,TMP+2 ; read the status reg beq r2,r0,tmp_read4 subui r2,r2,#1 blt r1,r0,tmp_read2 tmp_read4: inch r1,TMP+2 ; read the temperature lw r2,#5 ; five digits call DisplayNum lw lr,[sp] lw r1,8[sp] lw r2,16[sp] ret #24 ;============================================================================== ;============================================================================== ;------------------------------------------------------------------------------ ; 100 Hz interrupt ; - takes care of "flashing" the cursor ;------------------------------------------------------------------------------ ; Pulse100: subui sp,sp,#8 sw lr,[sp] lea r2,TEXTSCR inch r1,334[r2] addui r1,r1,#1 outc r1,334[r2] ; call DisplayDatetime call SelectNextToRunTCB call SwitchTask outb r0,0xDCFFFC ; clear interrupt ; lw r1,TickIRQAddr ; jal r31,[r1] ; lw r1,Milliseconds ; andi r1,r1,#0x0f ; bnei r1,#5,p1001 ; call FlashCursor p1001: lw lr,[sp] ret #8 ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ SelectNextToRunTCB: sc r0,NextToRunTCB ret ;------------------------------------------------------------------------------ ; Switch from the RunningTCB to the NextToRunTCB ;------------------------------------------------------------------------------ SwitchTask: sw r1,r1save sw r2,r2save lcu r1,NextToRunTCB lcu r2,RunningTCB bne r1,r2,swtsk1 ; are we already running this TCB ? lw r1,r1save lw r2,r2save ret swtsk1: andi r2,r2,#0x1ff ; max 512 TCB's mului r2,r2,#TCBSize addui r2,r2,#TCBBase lw r1,r1save ; get back r1 sw r1,TCBr1[r2] lw r1,r2save ; get back r2 sw r1,TCBr2[r2] sw r3,TCBr3[r2] sw r4,TCBr4[r2] sw r5,TCBr5[r2] sw r6,TCBr6[r2] sw r7,TCBr7[r2] sw r8,TCBr8[r2] sw r9,TCBr9[r2] sw r10,TCBr10[r2] sw r11,TCBr11[r2] sw r12,TCBr12[r2] sw r13,TCBr13[r2] sw r14,TCBr14[r2] sw r15,TCBr15[r2] sw r16,TCBr16[r2] sw r17,TCBr17[r2] sw r18,TCBr18[r2] sw r19,TCBr19[r2] sw r20,TCBr20[r2] sw r21,TCBr21[r2] sw r22,TCBr22[r2] sw r23,TCBr23[r2] sw r24,TCBr24[r2] sw r25,TCBr25[r2] sw r26,TCBr26[r2] sw r27,TCBr27[r2] sw r28,TCBr28[r2] sw r29,TCBr29[r2] sw r30,TCBr30[r2] sw r31,TCBr31[r2] lcu r2,NextToRunTCB sc r2,RunningTCB mului r2,r2,#TCBSize addui r2,r2,#TCBBase lw r1,TCBr1[r2] lw r3,TCBr3[r2] lw r4,TCBr4[r2] lw r5,TCBr5[r2] lw r6,TCBr6[r2] lw r7,TCBr7[r2] lw r8,TCBr8[r2] lw r9,TCBr9[r2] lw r10,TCBr10[r2] lw r11,TCBr11[r2] lw r12,TCBr12[r2] lw r13,TCBr13[r2] lw r14,TCBr14[r2] lw r15,TCBr15[r2] lw r16,TCBr16[r2] lw r17,TCBr17[r2] lw r18,TCBr18[r2] lw r19,TCBr19[r2] lw r20,TCBr20[r2] lw r21,TCBr21[r2] lw r22,TCBr22[r2] lw r23,TCBr23[r2] lw r24,TCBr24[r2] lw r25,TCBr25[r2] lw r26,TCBr26[r2] lw r27,TCBr27[r2] lw r28,TCBr28[r2] lw r29,TCBr29[r2] lw r30,TCBr30[r2] lw r31,TCBr31[r2] lw r2,TCBr2[r2] ret ;------------------------------------------------------------------------------ ; Flash Cursor ;------------------------------------------------------------------------------ ; FlashCursor: subui sp,sp,#32 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw lr,24[sp] call CalcScreenLoc addui r1,r1,#0x10000 lb r2,CursorFlash beq r2,r0,flshcrsr2 ; causes screen colors to flip around inch r2,[r1] addui r2,r2,#1 outc r2,[r1] flshcrsr3: lw r2,Lastloc beq r1,r2,flshcrsr1 ; restore the screen colors of the previous cursor location lc r3,ScreenColor outc r3,[r2] sw r1,Lastloc flshcrsr1: lw r1,[sp] lw r2,8[sp] lw r3,16[sp] lw lr,24[sp] ret #32 flshcrsr2: lc r3,ScreenColor outc r3,[r1] bra flshcrsr3 CursorOff: lw r1,#0xA0 outc r1,TEXTREG+16 ; turn off cursor ret CursorOn: lw r1,#0xE0 outc r1,TEXTREG+16 ; turn on cursor ret ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ ClearBmpScreen: subui sp,sp,#24 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] lw r2,#1364*768 shrui r2,r2,#3 ; r2 = # words to clear lea r1,0x2929292929292929 ; r1 = color for eight pixels lea r3,BITMAPSCR ; r3 = screen address csj4: sw r1,[r3] ; store pixel data addui r3,r3,#8 ; advance screen address by eight loop r2,csj4 ; decrement pixel count and loop back lw r1,[sp] lw r2,8[sp] lw r3,16[sp] ret #24 ;------------------------------------------------------------------------------ ; Clear the screen and the screen color memory ; We clear the screen to give a visual indication that the system ; is working at all. ;------------------------------------------------------------------------------ ; ClearScreen: subui sp,sp,#40 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw r4,24[sp] sw lr,32[sp] lea r3,TEXTREG inch r1,TEXT_COLS[r3] ; calc number to clear inch r2,TEXT_ROWS[r3] mulu r2,r1,r2 ; r2 = # chars to clear setlo r1,#32 ; space char lc r4,ScreenColor call AsciiToScreen lea r3,TEXTSCR ; text screen address csj4: outc r1,[r3] outc r4,0x10000[r3] ; color screen is 0x10000 higher addui r3,r3,#2 loop r2,csj4 lw lr,32[sp] lw r4,24[sp] lw r3,16[sp] lw r2,8[sp] lw r1,[sp] ret #40 ;------------------------------------------------------------------------------ ; Scroll text on the screen upwards ;------------------------------------------------------------------------------ ; ScrollUp: subui sp,sp,#40 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw r4,24[sp] sw lr,32[sp] lea r3,TEXTREG inch r1,TEXT_COLS[r3] ; r1 = # text columns inch r2,TEXT_ROWS[r3] mulu r2,r1,r2 ; calc number of chars to scroll subu r2,r2,r1 ; one less row lea r3,TEXTSCR scrup1: inch r4,[r3+r1*2] ; indexed addressing example outc r4,[r3] addui r3,r3,#2 loop r2,scrup1 lea r3,TEXTREG inch r1,TEXT_ROWS[r3] subui r1,r1,#1 call BlankLine lw r1,[sp] lw r2,8[sp] lw r3,16[sp] lw r4,24[sp] lw lr,32[sp] ret #40 ;------------------------------------------------------------------------------ ; Blank out a line on the display ; line number to blank is in r1 ;------------------------------------------------------------------------------ ; BlankLine: subui sp,sp,#24 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] lea r3,TEXTREG ; r3 = text register address inch r2,TEXT_COLS[r3] ; r2 = # chars to blank out mulu r3,r2,r1 shli r3,r3,#1 addui r3,r3,#TEXTSCR ; r3 = screen address setlo r1,#' ' blnkln1: outc r1,[r3] addui r3,r3,#2 loop r2,blnkln1 lw r1,[sp] lw r2,8[sp] lw r3,16[sp] ret #24 ;------------------------------------------------------------------------------ ; Convert ASCII character to screen display character. ;------------------------------------------------------------------------------ ; AsciiToScreen: andi r1,r1,#0x00ff bltui r1,#'A',atoscr1 bleui r1,#'Z',atoscr1 bgtui r1,#'z',atoscr1 bltui r1,#'a',atoscr1 subui r1,r1,#0x60 atoscr1: ori r1,r1,#0x100 ret ;------------------------------------------------------------------------------ ; Convert screen character to ascii character ;------------------------------------------------------------------------------ ; ScreenToAscii: andi r1,r1,#0xff bgtui r1,#26,stasc1 addui r1,r1,#60 stasc1: ret ;------------------------------------------------------------------------------ ; Calculate screen memory location from CursorRow,CursorCol. ; Also refreshes the cursor location. ; Destroys r1,r2,r3 ; r1 = screen location ;------------------------------------------------------------------------------ ; CalcScreenLoc: lbu r1,CursorRow andi r1,r1,#0x7f lea r3,TEXTREG inch r2,TEXT_COLS[r3] mulu r2,r2,r1 lbu r1,CursorCol andi r1,r1,#0x7f addu r2,r2,r1 outc r2,TEXT_CURPOS[r3] shlui r2,r2,#1 addui r1,r2,#TEXTSCR ; r1 = screen location ret ;------------------------------------------------------------------------------ ; Display a character on the screen ; d1.b = char to display ;------------------------------------------------------------------------------ ; DisplayChar: bnei r1,#'\r',dccr ; carriage return ? subui sp,sp,#32 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw lr,24[sp] sb r0,CursorCol ; just set cursor column to zero on a CR bra dcx7 dccr: ; beqi r1,#CTRLK,dccr1 bnei r1,#0x91,dcx6 ; cursor right ? dccr1: subui sp,sp,#32 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw lr,24[sp] lbu r2,CursorCol beqi r2,#56,dcx7 addui r2,r2,#1 sb r2,CursorCol dcx7: call CalcScreenLoc lw lr,24[sp] lw r3,16[sp] lw r2,8[sp] lw r1,[sp] ret #32 dcx6: ; beqi r1,#CTRLI,dccu1 bnei r1,#0x90,dcx8 ; cursor up ? dccu1: subui sp,sp,#32 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw lr,24[sp] lbu r2,CursorRow beqi r2,#0,dcx7 subui r2,r2,#1 sb r2,CursorRow bra dcx7 dcx8: ; beqi r1,#CTRLJ,dccl1 bnei r1,#0x93,dcx9 ; cursor left ? dccl1: subui sp,sp,#32 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw lr,24[sp] lbu r2,CursorCol beqi r2,#0,dcx7 subui r2,r2,#1 sb r2,CursorCol bra dcx7 dcx9: ; beqi r1,#CTRLM,dccd1 bnei r1,#0x92,dcx10 ; cursor down ? dccd1: subui sp,sp,#32 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw lr,24[sp] lbu r2,CursorRow beqi r2,#30,dcx7 addui r2,r2,#1 sb r2,CursorRow bra dcx7 dcx10: bnei r1,#0x94,dcx11 ; cursor home ? subui sp,sp,#32 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw lr,24[sp] lbu r2,CursorCol beq r2,r0,dcx12 sb r0,CursorCol bra dcx7 dcx12: sb r0,CursorRow bra dcx7 dcx11: subui sp,sp,#48 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw r4,24[sp] sw r5,32[sp] sw lr,40[sp] bnei r1,#0x99,dcx13 ; delete ? call CalcScreenLoc mov r3,r1 ; r3 = screen location lbu r1,CursorCol ; r1 = cursor column bra dcx5 dcx13: bnei r1,#CTRLH,dcx3 ; backspace ? lbu r2,CursorCol beq r2,r0,dcx4 subui r2,r2,#1 sb r2,CursorCol call CalcScreenLoc ; a0 = screen location mov r3,r1 ; r3 = screen location lbu r1,CursorCol dcx5: inch r2,2[r3] outc r2,[r3] addui r3,r3,#2 addui r1,r1,#1 lea r4,TEXTREG inch r5,TEXT_COLS[r4] bltu r1,r5,dcx5 setlo r1,#' ' call AsciiToScreen outc r1,-2[r3] bra dcx4 dcx3: beqi r1,#'\n',dclf ; linefeed ? mov r4,r1 ; save r1 in r4 call CalcScreenLoc ; r1 = screen location mov r3,r1 ; r3 = screen location mov r1,r4 ; restore r1 call AsciiToScreen ; convert ascii char to screen char outc r1,[r3] lc r1,CharColor outc r1,0x10000[r3] call IncCursorPos bra dcx4 dclf: call IncCursorRow dcx4: lw lr,40[sp] lw r5,32[sp] lw r4,24[sp] lw r3,16[sp] lw r2,8[sp] lw r1,[sp] ret #48 ;------------------------------------------------------------------------------ ; Increment the cursor position, scroll the screen if needed. ;------------------------------------------------------------------------------ ; IncCursorPos: subui sp,sp,#32 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw lr,24[sp] lbu r1,CursorCol addui r1,r1,#1 sb r1,CursorCol inch r2,TEXTREG+TEXT_COLS bleu r1,r2,icc1 sb r0,CursorCol ; column = 0 bra icr1 IncCursorRow: subui sp,sp,#32 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw lr,24[sp] icr1: lbu r1,CursorRow addui r1,r1,#1 sb r1,CursorRow inch r2,TEXTREG+TEXT_ROWS bleu r1,r2,icc1 subui r2,r2,#1 ; backup the cursor row, we are scrolling up sb r2,CursorRow call ScrollUp icc1: call CalcScreenLoc lw lr,24[sp] lw r3,16[sp] lw r2,8[sp] lw r1,[sp] ret #32 ;------------------------------------------------------------------------------ ; Display a string on the screen. ;------------------------------------------------------------------------------ ; DisplayString: subui sp,sp,#24 sw r1,[sp] sw r2,8[sp] sw lr,16[sp] mov r2,r1 ; r2 = pointer to string dspj1: lbu r1,[r2] ; move string char into r1 addui r2,r2,#1 ; increment pointer beq r1,r0,dsret ; is it end of string ? call DisplayChar ; display character bra dspj1 ; go back for next character dsret: lw lr,16[sp] lw r2,8[sp] lw r1,[sp] ret #24 DisplayStringCRLF: subui r30,r30,#8 sw r31,[r30] call DisplayString lw r31,[r30] addui r30,r30,#8 CRLF: subui sp,sp,#16 sw r1,[sp] sw lr,8[sp] setlo r1,#'\r' call DisplayChar setlo r1,#'\n' call DisplayChar lw lr,8[sp] lw r1,[sp] ret #16 ; Call the Tiny BASIC routine to display a number ; DisplayNum: jmp PRTNUM ;------------------------------------------------------------------------------ ; Display nybble in r1 ;------------------------------------------------------------------------------ ; DisplayNybble: subui sp,sp,#16 sw r1,[sp] sw lr,8[sp] andi r1,r1,#0x0F addui r1,r1,#'0' bleui r1,#'9',dispnyb1 addui r1,r1,#7 dispnyb1: call DisplayChar lw lr,8[sp] lw r1,[sp] ret #16 ;------------------------------------------------------------------------------ ; Display the byte in r1 ;------------------------------------------------------------------------------ ; DisplayByte: subui sp,sp,#16 sw r1,[sp] sw lr,8[sp] rori r1,r1,#4 call DisplayNybble roli r1,r1,#4 call DisplayNybble lw lr,8[sp] lw r1,[sp] ret #16 ;------------------------------------------------------------------------------ ; Display the char in r1 ;------------------------------------------------------------------------------ ; DisplayCharr: subui sp,sp,#16 sw r1,[sp] sw lr,8[sp] rori r1,r1,#8 call DisplayByte roli r1,r1,#8 call DisplayByte lw lr,8[sp] lw r1,[sp] ret #16 ;------------------------------------------------------------------------------ ; Display the half-word in r1 ;------------------------------------------------------------------------------ ; DisplayHalf: subui sp,sp,#16 sw r1,[sp] sw lr,8[sp] rori r1,r1,#16 call DisplayCharr roli r1,r1,#16 call DisplayCharr lw lr,8[sp] lw r1,[sp] ret #16 ;------------------------------------------------------------------------------ ; Display the 64 bit word in r1 ;------------------------------------------------------------------------------ ; DisplayWord: subui sp,sp,#24 sw r1,[sp] sw r3,8[sp] sw lr,16[sp] setlo r3,#7 dspwd1: roli r1,r1,#8 call DisplayByte loop r3,dspwd1 lw lr,16[sp] lw r3,8[sp] lw r1,[sp] ret #24 ;------------------------------------------------------------------------------ ; Display memory pointed to by r2. ; destroys r1,r3 ;------------------------------------------------------------------------------ ; DisplayMemB: subui sp,sp,#24 sw r1,[sp] sw r3,8[sp] sw lr,16[sp] setlo r1,#':' call DisplayChar mov r1,r2 call DisplayWord setlo r3,#7 dspmem1: setlo r1,#' ' call DisplayChar lbu r1,[r2] call DisplayByte addui r2,r2,#1 loop r3,dspmem1 call CRLF lw lr,16[sp] lw r3,8[sp] lw r1,[sp] ret #24 DisplayMemC: subui sp,sp,#24 sw r1,[sp] sw r3,8[sp] sw lr,16[sp] setlo r1,#':' call DisplayChar mov r1,r2 call DisplayWord setlo r3,#3 dspmemc1: setlo r1,#' ' call DisplayChar lcu r1,[r2] call DisplayCharr addui r2,r2,#2 loop r3,dspmemc1 call CRLF lw lr,16[sp] lw r3,8[sp] lw r1,[sp] ret #24 DisplayMemW: subui sp,sp,#24 sw r1,[sp] sw lr,16[sp] setlo r1,#':' call DisplayChar mov r1,r2 call DisplayWord setlo r1,#' ' call DisplayChar lw r1,[r2] call DisplayWord addui r2,r2,#8 call CRLF lw lr,16[sp] lw r1,[sp] ret #24 ;------------------------------------------------------------------------------ ; Converts binary number in r1 into BCD number in r2 and r1. ;------------------------------------------------------------------------------ ; BinToBCD: subui sp,sp,#48 sw r3,[sp] sw r4,8[sp] sw r5,16[sp] sw r6,24[sp] sw r7,32[sp] sw r8,40[sp] setlo r2,#10 setlo r8,#19 ; number of digits to produce - 1 bta1: modu r3,r1,r2 shli r3,r3,#60 ; shift result to uppermost bits shli r7,r5,#60 ; copy low order nybble of r5 to r4 topmost nybble shrui r4,r4,#4 or r4,r4,r7 shrui r5,r5,#4 or r5,r5,r3 ; copy new bcd digit into uppermost bits of r5 divui r1,r1,r2 ; r1=r1/10 loop r8,bta1 shrui r4,r4,#48 ; right align number in register shli r6,r5,#16 or r4,r4,r6 ; copy bits into r4 shrui r5,r5,#48 mov r1,r4 mov r2,r5 lw r3,[sp] lw r4,8[sp] lw r5,16[sp] lw r6,24[sp] lw r7,32[sp] lw r8,40[sp] ret #48 ;------------------------------------------------------------------------------ ; Converts BCD number in r1 into Ascii number in r2 and r1. ;------------------------------------------------------------------------------ ; BCDToAscii: subui sp,sp,#32 sw r3,[sp] sw r4,8[sp] sw r5,16[sp] sw r8,24[sp] setlo r8,#15 bta2: andi r2,r1,#0x0F ori r2,r2,#0x30 shli r2,r2,#56 shrui r4,r4,#8 shli r5,r3,#56 or r4,r4,r5 shrui r3,r3,#8 or r3,r3,r2 shrui r1,r1,#4 loop r8,bta2 mov r1,r4 mov r2,r3 lw r3,[sp] lw r4,8[sp] lw r5,16[sp] lw r8,24[sp] ret #32 ;------------------------------------------------------------------------------ ; Convert a binary number into a 20 character ascii string. ; r1 = number to convert ; r2 = address of string buffer ;------------------------------------------------------------------------------ ; BinToStr: subui sp,sp,#56 sw r3,[sp] sw r7,8[sp] sw r8,16[sp] sw r9,24[sp] sw r10,32[sp] sw r11,40[sp] sw lr,48[sp] mov r11,r2 call BinToBCD mov r10,r2 ; save off r2 call BCDToAscii setlo r9,#1 btos3: setlo r8,#7 btos1: shli r7,r9,#3 addui r7,r7,r8 addui r7,r7,#4 andi r3,r1,#0xff sb r3,[r7+r11] shrui r1,r1,#8 loop r8,btos1 mov r1,r2 loop r9,btos3 ; the last four digits mov r1,r10 ; get back r2 call BCDToAscii setlo r8,#3 btos2: andi r3,r1,#0xff sb r3,[r8+r11] shrui r1,r1,#8 loop r8,btos2 sb r0,20[r11] ; null terminate lw r3,[sp] lw r7,8[sp] lw r8,16[sp] lw r9,24[sp] lw r10,32[sp] lw r11,40[sp] lw lr,48[sp] ret #56 ;============================================================================== ; System Monitor Program ;============================================================================== ; Monitor: lea sp,STACKTOP0 ; top of stack; reset the stack pointer sb r0,KeybdEcho ; turn off keyboard echo PromptLn: call CRLF setlo r1,#'$' call DisplayChar ; Get characters until a CR is keyed ; Prompt3: ; lw r1,#2 ; get keyboard character ; syscall #417 call KeybdGetChar beqi r1,#-1,Prompt3 ; wait for a character beqi r1,#CR,Prompt1 call DisplayChar bra Prompt3 ; Process the screen line that the CR was keyed on ; Prompt1: sb r0,CursorCol ; go back to the start of the line call CalcScreenLoc ; r1 = screen memory location mov r3,r1 inch r1,[r3] addui r3,r3,#2 call ScreenToAscii bnei r1,#'$',Prompt2 ; skip over '$' prompt character inch r1,[r3] addui r3,r3,#2 call ScreenToAscii ; Dispatch based on command character ; Prompt2: beqi r1,#':',Editmem ; $: - edit memory beqi r1,#'D',Dumpmem ; $D - dump memory beqi r1,#'F',Fillmem ; $F - fill memory Prompt7: bnei r1,#'B',Prompt4 ; $B - start tiny basic jmp CSTART Prompt4: beqi r1,#'J',ExecuteCode ; $J - execute code bnei r1,#'L',Prompt9 ; $L - load S19 file jmp LoadSector Prompt9: bnei r1,#'?',Prompt10 ; $? - display help lea r1,HelpMsg call DisplayString jmp Monitor Prompt10: beqi r1,#'C',TestCLS ; $C - clear screen bnei r1,#'R',Prompt12 jmp RandomLinesCall Prompt12: bnei r1,#'I',Prompt13 jmp Invaders Prompt13: bnei r1,#'P',Prompt14 jmp Piano Prompt14: bnei r1,#'T',Prompt15 call tmp_read Prompt15: jmp Monitor RandomLinesCall: call RandomLines jmp Monitor TestCLS: inch r1,[r3] addui r3,r3,#2 call ScreenToAscii bnei r1,#'L',Monitor inch r1,[r3] addui r3,r3,#2 call ScreenToAscii bnei r1,#'S',Monitor call ClearScreen sb r0,CursorCol sb r0,CursorRow call CalcScreenLoc jmp Monitor HelpMsg: db "? = Display help",CR,LF db "CLS = clear screen",CR,LF db ": = Edit memory bytes",CR,LF db "L = Load S19 file",CR,LF db "D[B|C|H|W] = Dump memory",CR,LF db "F[B|C|H|W] = Fill memory",CR,LF db "B = start tiny basic",CR,LF db "J = Jump to code",CR,LF db "I = Invaders",CR,LF db "R = Random lines",CR,LF db "T = get temperature",CR,LF db "P = Piano",CR,LF,0 align 4 ;------------------------------------------------------------------------------ ; Ignore blanks in the input ; r3 = text pointer ; r1 destroyed ;------------------------------------------------------------------------------ ; ignBlanks: subui sp,sp,#8 sw r31,[sp] ignBlanks1: inch r1,[r3] addui r3,r3,#2 call ScreenToAscii beqi r1,#' ',ignBlanks1 subui r3,r3,#2 lw r31,[sp] ret #8 ;------------------------------------------------------------------------------ ; Edit memory byte(s). ;------------------------------------------------------------------------------ ; EditMem: call ignBlanks call GetHexNumber or r5,r1,r0 setlo r4,#7 edtmem1: call ignBlanks call GetHexNumber sb r1,[r5] addui r5,r5,#1 loop r4,edtmem1 jmp Monitor ;------------------------------------------------------------------------------ ; Execute code at the specified address. ;------------------------------------------------------------------------------ ; ExecuteCode: call ignBlanks call GetHexNumber jal r31,[r1] jmp Monitor LoadSector: call ignBlanks call GetHexNumber lw r2,#0x3800 call spi_read_sector jmp Monitor ;------------------------------------------------------------------------------ ; Do a memory dump of the requested location. ;------------------------------------------------------------------------------ ; DumpMem: inch r1,[r3] addui r3,r3,#2 call ScreenToAscii mov r6,r1 ; r6 = fill type character call ignBlanks call GetHexNumber ; get start address of dump mov r2,r1 call ignBlanks call GetHexNumber ; get number of bytes to dump shrui r1,r1,#3 ; 1/8 as many dump rows bnei r1,#0,Dumpmem2 lw r1,#1 ; dump at least one row Dumpmem2: call CRLF beqi r6,#'W',DumpmemW ; beqi r6,#'H',DumpmemH beqi r6,#'C',DumpmemC DumpmemB: call DisplayMemB loop r1,DumpmemB jmp Monitor DumpmemC: call DisplayMemC loop r1,DumpmemC jmp Monitor DumpmemW: call DisplayMemW loop r1,DumpmemW jmp Monitor ; call DisplayMem ; call DisplayMem ; call DisplayMem ; call DisplayMem ; call DisplayMem ; call DisplayMem ; call DisplayMem bra Monitor Fillmem: inch r1,[r3] addui r3,r3,#2 call ScreenToAscii mov r6,r1 ; r6 = fill type character call ignBlanks call GetHexNumber ; get start address of dump mov r2,r1 call ignBlanks call GetHexNumber ; get number of bytes to fill mov r5,r1 call ignBlanks call GetHexNumber ; get the fill byte beqi r6,#'C',FillmemC beqi r6,#'H',FillmemH beqi r6,#'W',FillmemW FillmemB: sb r1,[r2] addui r2,r2,#1 loop r5,FillmemB jmp Monitor FillmemC: sc r1,[r2] addui r2,r2,#2 loop r5,FillmemC jmp Monitor FillmemH: sh r1,[r2] addui r2,r2,#4 loop r5,FillmemH jmp Monitor FillmemW: sw r1,[r2] addui r2,r2,#8 loop r5,FillmemW jmp Monitor ;------------------------------------------------------------------------------ ; Get a hexidecimal number. Maximum of sixteen digits. ; R3 = text pointer (updated) ; R1 = hex number ;------------------------------------------------------------------------------ ; GetHexNumber: subui sp,sp,#24 sw r2,[sp] sw r4,8[sp] sw lr,16[sp] setlo r2,#0 setlo r4,#15 gthxn2: inch r1,[r3] addui r3,r3,#2 call ScreenToAscii call AsciiToHexNybble beqi r1,#-1,gthxn1 shli r2,r2,#4 andi r1,r1,#0x0f or r2,r2,r1 loop r4,gthxn2 gthxn1: mov r1,r2 lw lr,16[sp] lw r4,8[sp] lw r2,[sp] ret #24 ;------------------------------------------------------------------------------ ; Convert ASCII character in the range '0' to '9', 'a' to 'f' or 'A' to 'F' ; to a hex nybble. ;------------------------------------------------------------------------------ ; AsciiToHexNybble: bltui r1,#'0',gthx3 bgtui r1,#'9',gthx5 subui r1,r1,#'0' ret gthx5: bltui r1,#'A',gthx3 bgtui r1,#'F',gthx6 subui r1,r1,#'A' addui r1,r1,#10 ret gthx6: bltui r1,#'a',gthx3 bgtui r1,#'f',gthx3 subui r1,r1,#'a' addui r1,r1,#10 ret gthx3: setlo r1,#-1 ; not a hex number ret ;============================================================================== ; Load an S19 format file ;============================================================================== ; LoadS19: bra ProcessRec NextRec: call sGetChar bne r1,#LF,NextRec ProcessRec: call sGetChar beqi r1,#26,Monitor ; CTRL-Z ? bnei r1,#'S',NextRec call sGetChar blt r1,#'0',NextRec bgt r1,#'9',NextRec or r4,r1,r0 ; r4 = record type call sGetChar call AsciiToHexNybble or r2,r1,r0 call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 ; r2 = byte count or r3,r2,r1 ; r3 = byte count beqi r4,#'0',NextRec ; manufacturer ID record, ignore beqi r4,#'1',ProcessS1 beqi r4,#'2',ProcessS2 beqi r4,#'3',ProcessS3 beqi r4,#'5',NextRec ; record count record, ignore beqi r4,#'7',ProcessS7 beqi r4,#'8',ProcessS8 beqi r4,#'9',ProcessS9 bra NextRec pcssxa: andi r3,r3,#0xff subui r3,r3,#1 ; one less for loop pcss1a: call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 sb r2,[r5] addui r5,r5,#1 loop r3,pcss1a ; Get the checksum byte call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 bra NextRec ProcessS1: call S19Get16BitAddress bra pcssxa ProcessS2: call S19Get24BitAddress bra pcssxa ProcessS3: call S19Get32BitAddress bra pcssxa ProcessS7: call S19Get32BitAddress sw r5,S19StartAddress bra Monitor ProcessS8: call S19Get24BitAddress sw r5,S19StartAddress bra Monitor ProcessS9: call S19Get16BitAddress sw r5,S19StartAddress jmp Monitor S19Get16BitAddress: subui sp,sp,#8 sw r31,[sp] call sGetChar call AsciiToHexNybble or r2,r1,r0 bra S1932b S19Get24BitAddress: subui sp,sp,#8 sw r31,[sp] call sGetChar call AsciiToHexNybble or r2,r1,r0 bra S1932a S19Get32BitAddress: subui sp,sp,#8 sw r31,[sp] call sGetChar call AsciiToHexNybble or r2,r1,r0 call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r1,r2 call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 S1932a: call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 S1932b: call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 call sGetChar call AsciiToHexNybble shli r2,r2,#4 or r2,r2,r1 xor r4,r4,r4 or r5,r2,r0 lw r31,[sp] addui sp,sp,#8 ret ;------------------------------------------------------------------------------ ; Get a character from auxillary input, checking the keyboard status for a ; CTRL-C ;------------------------------------------------------------------------------ ; sGetChar: subui sp,sp,#8 sw r31,[sp] sgc2: call KeybdCheckForKey beq r1,r0,sgc1 call KeybdGetchar beqi r1,#CRTLC,Monitor sgc1: call AUXIN ble r1,r0,sgc2 lw r31,[sp] ret #8 ;-------------------------------------------------------------------------- ; Draw random lines on the bitmap screen. ;-------------------------------------------------------------------------- ; RandomLines: subui sp,sp,#24 sw r1,[sp] sw r3,8[sp] sw lr,16[sp] sw r0,ctx3start ; prevent restarting context over and over again rl5: gran mfspr r1,rand ; select a random color outh r1,GACCEL rl1: ; random X0 gran mfspr r1,rand lw r3,#1364 modu r1,r1,r3 outh r1,GACCEL+8 rl2: ; random X1 gran mfspr r1,rand lw r3,#1364 modu r1,r1,r3 outh r1,GACCEL+16 rl3: ; random Y0 gran mfspr r1,rand lw r3,#768 modu r1,r1,r3 outh r1,GACCEL+12 rl4: ; random Y1 gran mfspr r1,rand lw r3,#768 modu r1,r1,r3 outh r1,GACCEL+20 setlo r1,#2 ; draw line command outh r1,GACCEL+60 rl8: ; call KeybdGetChar ; beqi r1,#CTRLC,rl7 inch r1,GACCEL+56 ; ensure controller is in IDLE state bne r1,r0,rl8 bra rl5 rl7: lw lr,16[sp] lw r3,8[sp] lw r1,[sp] ret #24 ;-------------------------------------------------------------------------- ; Initialize sprite image caches with random data. ;-------------------------------------------------------------------------- RandomizeSprram: lea r2,SPRRAM setlo r4,#14335 ; number of chars to initialize rsr1: gran mfspr r1,rand outc r1,[r2] addui r2,r2,#2 loop r4,rsr1 ret ;-------------------------------------------------------------------------- ; Setup the AC97/LM4550 audio controller. Check keyboard for a CTRL-C ; interrupt which may be necessary if the audio controller isn't ; responding. ;-------------------------------------------------------------------------- ; SetupAC97: subui sp,sp,#16 sw r1,[sp] sw lr,8[sp] sac974: outc r0,AC97+0x26 ; trigger a read of register 26 (status reg) sac971: ; wait for status to register 0xF (all ready) call KeybdGetChar ; see if we needed to CTRL-C beqi r1,#CTRLC,sac973 inch r1,AC97+0x68 ; wait for dirty bit to clear bne r1,r0,sac971 inch r1,AC97+0x26 ; check status at reg h26, wait for andi r1,r1,#0x0F ; analogue to be ready bnei r1,#0x0F,sac974 sac973: outc r0,AC97+2 ; master volume, 0db attenuation, mute off outc r0,AC97+4 ; headphone volume, 0db attenuation, mute off outc r0,AC97+0x18 ; PCM gain (mixer) mute off, no attenuation outc r0,AC97+0x0A ; mute PC beep setlo r1,#0x8000 ; bypass 3D sound outc r1,AC97+0x20 sac972: call KeybdGetChar beqi r1,#CTRLC,sac975 inch r1,AC97+0x68 ; wait for dirty bits to clear bne r1,r0,sac972 ; wait a while for the settings to take effect sac975: lw lr,8[sp] lw r1,[sp] ret #16 ;-------------------------------------------------------------------------- ; Sound a 800 Hz beep ;-------------------------------------------------------------------------- ; Beep: subui sp,sp,#16 sw r1,[sp] sw lr,8[sp] setlo r1,#8 outb r1,LED ori r1,r0,#15 ; master volume to max outc r1,PSG+128 ori r1,r0,#13422 ; 800Hz outc r1,PSGFREQ0 setlo r1,#9 outb r1,LED ; decay (16.384 ms)2 ; attack (8.192 ms)1 ; release (1.024 s)A ; sustain level C setlo r1,#0xCA12 outc r1,PSGADSR0 ori r1,r0,#0x1104 ; gate, output enable, triangle waveform outc r1,PSGCTRL0 ori r1,r0,#2500000 ; delay about 1s beep1: loop r1,beep1 setlo r1,#13 outb r1,LED ori r1,r0,#0x0104 ; gate off, output enable, triangle waveform outc r1,PSGCTRL0 ori r1,r0,#2500000 ; delay about 1s beep2: loop r1,beep2 setlo r1,#16 outb r1,LED ori r1,r0,#0x0000 ; gate off, output enable off, no waveform outc r1,PSGCTRL0 lw lr,8[sp] lw r1,[sp] ret #16 ;-------------------------------------------------------------------------- ;-------------------------------------------------------------------------- ; Piano: ori r1,r0,#15 ; master volume to max outc r1,PSG+128 playnt: call KeybdGetChar beqi r1,#CTRLC,Monitor beqi r1,#'a',playnt1a beqi r1,#'b',playnt1b beqi r1,#'c',playnt1c beqi r1,#'d',playnt1d beqi r1,#'e',playnt1e beqi r1,#'f',playnt1f beqi r1,#'g',playnt1g bra playnt playnt1a: setlo r1,#7217 call Tone bra playnt playnt1b: setlo r1,#8101 call Tone bra playnt playnt1c: setlo r1,#4291 call Tone bra playnt playnt1d: setlo r1,#4817 call Tone bra playnt playnt1e: setlo r1,#5407 call Tone bra playnt playnt1f: setlo r1,#5728 call Tone bra playnt playnt1g: setlo r1,#6430 call Tone bra playnt Tone: subui sp,sp,#16 sw r1,[sp] sw lr,8[sp] outc r1,PSGFREQ0 ; decay (16.384 ms)2 ; attack (8.192 ms)1 ; release (1.024 s)A ; sustain level C setlo r1,#0xCA12 outc r1,PSGADSR0 ori r1,r0,#0x1104 ; gate, output enable, triangle waveform outc r1,PSGCTRL0 ori r1,r0,#250000 ; delay about 10ms tone1: loop r1,tone1 ori r1,r0,#0x0104 ; gate off, output enable, triangle waveform outc r1,PSGCTRL0 ori r1,r0,#250000 ; delay about 10ms tone2: loop r1,tone2 ori r1,r0,#0x0000 ; gate off, output enable off, no waveform outc r1,PSGCTRL0 lw lr,8[sp] lw r1,[sp] ret #16 ;============================================================================== ;============================================================================== SetupRasterIRQ: subui sp,sp,#8 sw r1,[sp] setlo r1,#200 outc r1,RASTERIRQ setlo r1,#240 outc r1,RASTERIRQ+2 setlo r1,#280 outc r1,RASTERIRQ+4 setlo r1,#320 outc r1,RASTERIRQ+6 setlo r1,#360 outc r1,RASTERIRQ+8 lw r1,[sp] ret #8 RasterIRQfn: inch r1,RASTERIRQ+30 ; get the raster compare register # (clears IRQ) beqi r1,#1,rirq1 beqi r1,#2,rirq2 beqi r1,#3,rirq3 beqi r1,#4,rirq4 beqi r1,#5,rirq5 beqi r1,#6,rirq6 beqi r1,#7,rirq7 beqi r1,#8,rirq8 ret rirq1: rirq2: rirq3: rirq4: rirq5: rirq6: rirq7: rirq8: mului r1,r1,#40 addui r1,r1,#204 outc r1,SPRITEREGS+2 outc r1,SPRITEREGS+18 outc r1,SPRITEREGS+34 outc r1,SPRITEREGS+50 outc r1,SPRITEREGS+66 outc r1,SPRITEREGS+82 outc r1,SPRITEREGS+98 outc r1,SPRITEREGS+114 ret ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ DisplayDatetime: subui sp,sp,#48 sw r1,[sp] sw r2,8[sp] sw r3,16[sp] sw r4,24[sp] sw r5,32[sp] sw lr,24[sp] call CursorOff lw r1,#3 ; get cursor position syscall #410 mov r4,r1 ; r4 = row mov r5,r2 ; r5 = col lw r1,#2 ; set cursor position lw r2,#46 ; move cursor down to last display line lw r3,#64 syscall #410 lw r1,#1 ; get the snapshotted date and time syscall #416 call DisplayWord ; display on screen lw r1,#2 ; restore cursor position mov r2,r4 ; r2 = row mov r3,r5 ; r3 = col syscall #410 call CursorOn lw lr,24[sp] lw r3,16[sp] lw r2,8[sp] lw r1,[sp] lw r4,24[sp] lw r5,32[sp] ret #48 ;============================================================================== ;============================================================================== InitializeGame: subui sp,sp,#16 sm [sp],r3/lr setlo r3,#320 sc r3,Manpos sc r0,Score sb r0,MissileActive sc r0,MissileX sc r0,MissileY lm [sp],r3/lr ret #16 DrawScore: subui sp,sp,#24 sm [sp],r1/r3/lr setlo r3,#1 sb r3,CursorRow setlo r3,#40 sb r3,CursorCol lb r1,Score call DisplayByte lb r1,Score+1 call DisplayByte lm [sp],r1/r3/lr ret #24 DrawMissile: subui sp,sp,#16 sm [sp],r1/lr lc r1,MissileY bleu r1,#2,MissileOff lc r1,MissileX shrui r1,r1,#3 sb r1,CursorCol lc r1,MissileY sb r1,CursorRow subui r1,r1,#1 sc r1,MissileY setlo r1,#'^' call DisplayChar lb r1,CursorCol subui r1,r1,#1 sb r1,CursorCol lb r1,CursorRow subui r1,r1,#1 sb r1,CursorRow setlo r1,#' ' call DisplayChar lm [sp],r1/lr ret #16 MissileOff: sb r0,MissileActive lc r1,MissileX shrui r1,r1,#3 sb r1,CursorCol lc r1,MissileY sb r1,CursorRow setlo r1,#' ' call DisplayChar lm [sp],r1/lr ret #16 DrawMan: subui sp,sp,#24 sm [sp],r1/r3/lr setlo r3,#46 sb r3,CursorRow lc r3,Manpos shrui r3,r3,#3 sb r3,CursorCol setlo r1,#' ' call DisplayChar setlo r1,#'#' call DisplayChar setlo r1,#'A' call DisplayChar setlo r1,#'#' call DisplayChar setlo r1,#' ' call DisplayChar lm [sp],r1/r3/lr ret #24 DrawInvader: lw r3,InvaderPos lw r1,#233 sc r1,[r3] lw r1,#242 sc r1,1[r3] lw r1,#223 sc r1,2[r3] ret DrawInvaders: subui sp,sp,#40 sm [sp],r1/r2/r3/r4/lr lc r1,InvadersRow1 lc r4,InvadersColpos andi r2,r1,#1 beq r2,r0,dinv1 lb r3,InvadersRowpos sb r3,CursorRow sb r4,CursorCol setlo r1,#' ' call DisplayByte setlo r1,#'#' call DisplayByte setlo r1,#'#' call DisplayByte setlo r1,#'#' call DisplayByte setlo r1,#' ' call DisplayByte lb r1,CursorRow addui r1,r1,#1 sb r1,CursorRow lb r1,CursorCol subui r1,r1,#5 setlo r1,#' ' call DisplayByte setlo r1,#'X' call DisplayByte setlo r1,#' ' call DisplayByte setlo r1,#'X' call DisplayByte setlo r1,#' ' call DisplayByte dinv1: lm [sp],r1/r2/r3/r4/lr ret #40 DrawBombs: ret Invaders: subui sp,#240 sm [sp],r1/r2/r3/r4/lr call InitializeGame InvadersLoop: call DrawScore call DrawInvaders call DrawBombs call DrawMissile call DrawMan TestMoveMan: call KeybdGetChar beqi r1,#'k',MoveManRight beqi r1,#'j',MoveManLeft beqi r1,#' ',FireMissile bra Invaders1 MoveManRight: lc r2,Manpos bgtu r2,#640,Invaders1 addui r2,r2,#8 sc r2,Manpos bra Invaders1 MoveManLeft: lc r2,Manpos ble r2,r0,Invaders1 subui r2,r2,#8 sc r2,Manpos bra Invaders1 FireMissile: lb r2,MissileActive bne r2,r0,Invaders1 setlo r2,#1 sb r2,MissileActive lc r2,Manpos sc r2,MissileX setlo r2,#46 sc r2,MissileY bra Invaders1 Invaders1: beqi r1,#CTRLC,InvadersEnd bra InvadersLoop InvadersEnd: lm [sp],r1/r2/r3/r4/lr addui sp,sp,#240 bra Monitor ;============================================================================== ;============================================================================== ; ; Initialize the SD card ; Returns ; r = 0 if successful, 1 otherwise ; spi_init: subui sp,sp,#24 sw lr,[sp] sw r2,8[sp] sw r3,16[sp] lea r3,SPIMASTER lw r1,#SPI_INIT_SD outb r1,SPI_TRANS_TYPE_REG[r3] lw r1,#SPI_TRANS_START outb r1,SPI_TRANS_CTRL_REG[r3] nop spi_init1: inb r1,SPI_TRANS_STATUS_REG[r3] mov r2,r1 ; note: some time needs to be wasted mov r1,r2 ; between status reads. beqi r1,#SPI_TRANS_BUSY,spi_init1 inb r1,SPI_TRANS_ERROR_REG[r3] bfext r1,r1,#1,#0 bne r1,#SPI_INIT_NO_ERROR,spi_error lea r1,spi_init_ok_msg call DisplayString xor r1,r1,r1 bra spi_init_exit spi_error: call DisplayByte lea r1,spi_init_error_msg call DisplayString lw r1,#1 spi_init_exit: lw lr,[sp] lw r2,8[sp] lw r3,16[sp] ret #24 ; SPI read sector ; ; r1= sector number to read ; r2= address to place read data ; Returns: ; r1 = 0 if successful ; spi_read_sector: subui sp,sp,#40 sw lr,[sp] sw r5,8[sp] sw r2,16[sp] sw r3,24[sp] sw r4,32[sp] lea r3,SPIMASTER ; spi master wants a byte address, so we multiply the sector number ; by 512. shlui r1,r1,#9 outb r1,SPI_SD_ADDR_7_0_REG[r3] shrui r1,r1,#8 outb r1,SPI_SD_ADDR_15_8_REG[r3] shrui r1,r1,#8 outb r1,SPI_SD_ADDR_23_16_REG[r3] shrui r1,r1,#8 outb r1,SPI_SD_ADDR_31_24_REG[r3] ; Force the reciever fifo to be empty, in case a prior error leaves it ; in an unknown state. lw r1,#1 outb r1,SPI_RX_FIFO_CTRL_REG[r3] lw r1,#RW_READ_SD_BLOCK outb r1,SPI_TRANS_TYPE_REG[r3] lw r1,#SPI_TRANS_START outb r1,SPI_TRANS_CTRL_REG[r3] nop spi_read_sect1: inb r1,SPI_TRANS_STATUS_REG[r3] mov r4,r1 ; just a delay between consecutive status reg reads mov r1,r4 beqi r1,#SPI_TRANS_BUSY,spi_read_sect1 inb r1,SPI_TRANS_ERROR_REG[r3] bfext r1,r1,#3,#2 bnei r1,#SPI_READ_NO_ERROR,spi_read_error lw r4,#512 ; read 512 bytes from fifo spi_read_sect2: inb r1,SPI_RX_FIFO_DATA_REG[r3] sb r1,[r2] addui r2,r2,#1 loop r4,spi_read_sect2 xor r1,r1,r1 bra spi_read_ret spi_read_error: call DisplayByte lea r1,spi_read_error_msg call DisplayString lw r1,#1 spi_read_ret: lw lr,[sp] lw r5,8[sp] lw r2,16[sp] lw r3,24[sp] lw r4,32[sp] ret #40 ; Read the boot sector from the disk. ; Must find it first by looking for the signature bytes 'EB' and '55AA'. ; spi_read_boot: subui sp,sp,#32 sw lr,[sp] sw r2,8[sp] sw r3,16[sp] sw r5,24[sp] sw r0,startSector ; default starting sector lw r3,#500 ;1934720 ; number of sectors to read (up to 1GB) lw r5,#0 ; r5 = starting address spi_read_boot1: mov r1,r5 ; r1 = sector number lw r2,#8 ; eight digits sb r0,CursorCol call DisplayNum ; Display the sector number being checked mov r1,r5 ; r1 = sector number lw r2,#0x100800000 ; r2 = target address call spi_read_sector ; The following displays the contents of the sector ; lw r1,#0x10 ; lw r2,#0x3800 ;spi_read_boot5: ; call DisplayMemB ; loop r1,spi_read_boot5 addui r5,r5,#1 ; move to next sector lbu r1,0x100800000 cmpui r2,r1,#0xEB beq r2,r0,spi_read_boot2 spi_read_boot3: loop r3,spi_read_boot1 lw r1,#1 ; r1 = 1 for error bra spi_read_boot4 spi_read_boot2: lea r1,msgFoundEB call DisplayString lbu r1,0x1008001FE ; check for 0x55AA signature bnei r1,#0x55,spi_read_boot3 lbu r1,0x1008001FF bnei r1,#0xAA,spi_read_boot3 subui r1,r5,#1 sw r1,startSector xor r1,r1,r1 ; r1 = 0, for okay status spi_read_boot4: lw lr,[sp] lw r2,8[sp] lw r3,16[sp] lw r5,24[sp] ret #32 msgFoundEB: db "Found EB code.",CR,LF,0 .align 4 ; Load the FAT tables into memory ; loadFAT: subui sp,sp,#8 sw lr,[sp] lcu r3,0x100800016 ; sectors per FAT lbu r2,0x100800010 ; number of FATs mulu r3,r3,r2 ; offset lea r2,0x100800200 ; where to place FAT lcu r5,0x10080000E ; r5 = # reserved sectors before FAT lw r6,startSector addu r5,r5,r6 loadFAT1: mov r1,r5 ; r1 = sector # call spi_read_sector addui r5,r5,#1 addui r2,r2,#512 ; advance 512 bytes loop r3,loadFAT1 lw lr,[sp] ret #8 ; Load the root directory from disk ; r2 = where to place root directory in memory ; loadRootDirectory: lcu r3,0x100800016 ; sectors per FAT lbu r4,0x100800010 ; number of FATs mulu r3,r3,r4 ; offset lcu r4,0x10080000E ; r2 = # reserved sectors before FAT addu r3,r3,r4 ; r3 = root directory sector number lw r6,startSector addu r5,r3,r6 ; r5 = root directory sector number ; we have to use two byte loads here because the number is at an unaligned data address lbu r7,0x100800011 ; r7 <= number of root directory entries lbu r8,0x100800012 shlui r8,r8,#8 or r7,r7,r8 mov r8,r7 ; r8 = number of root directory entries shlui r7,r7,#5 ; r7 *=32 = size of root directory table (bytes) shrui r7,r7,#9 ; r7 /= 512 = number of sectors in root directory mov r3,r7 loadRootDir1: mov r1,r5 call spi_read_sector addui r5,r5,#1 addui r2,r2,#512 loop r3,loadRootDir1 loadBootFile: ; For now we cheat and just go directly to sector 512. bra loadBootFileTmp lcu r3,0x100800016 ; sectors per FAT lbu r2,0x100800010 ; number of FATs mulu r3,r3,r2 ; offset lcu r2,0x10080000E ; r2 = # reserved sectors before FAT addu r3,r3,r2 ; r3 = root directory sector number ; we have to use two byte loads here because the number is at an unaligned data address lbu r7,0x100800011 ; r7 <= number of root directory entries lbu r8,0x100800012 shlui r8,r8,#8 or r7,r7,r8 mov r8,r7 ; r8 = number of root directory entries shlui r7,r7,#5 ; r7 *=32 = size of root directory table (bytes) shrui r7,r7,#9 ; r7 /= 512 = number of sectors in root directory ; now we need to fetch the sectors of the root directory and put them somewhere in ; memory ; loadBootFile4: lw r1,[r3] ; get filename cmpui r1,r1,#0x454C4946544F4F42 ; "BOOTFILE" beq r1,r0,loadBootFile5 loadBootFile3: addui r3,r3,#32 ; move to next directory entry loop r7,loadBootFile4 ; boot file not found ; here we found the file in the directory ; loadBootFile5: lcu r2,0x1a[r3] ; get starting cluster lcu r7,0x100800011 ; r7 = number of root directory entries shlui r7,r7,#5 ; r7 *=32 = size of root directory table (bytes) shrui r7,r7,#9 ; r7 /= 512 = number of sectors in root directory loadBootFileTmp: ; We load the number of sectors per cluster, then load a single cluster of the file. ; This is 16kib lbu r3,0x10080000D ; sectors per cluster lea r2,0x100800200 ; where to place FAT in memory lw r5,startSector ; r5=start sector of disk addui r5,r5,#512 ; r5= sector 512 loadBootFile1: mov r1,r5 ; r1=sector to read call spi_read_sector addui r5,r5,#1 ; r5 = next sector addui r2,r2,#512 loop r3,loadBootFile1 lhu r1,0x100800200 ; make sure it's bootable bnei r1,#0x544F4F42,loadBootFile2 lw r1,#0x16 lea r1,msgJumpingToBoot call DisplayString lw r1,#0x100800204 jal lr,[r1] jmp Monitor loadBootFile2: lea r1,msgNotBootable call DisplayString jmp Monitor msgJumpingToBoot: db "Jumping to boot",0 msgNotBootable: db "SD card not bootable.",0 spi_init_ok_msg: db "SD card initialized okay.",0 spi_init_error_msg: db ": error occurred initializing the SD card.",0 spi_boot_error_msg: db "SD card boot error",0 spi_read_error_msg: db "SD card read error",0 .align 4 ;============================================================================== ; Ethernet ;============================================================================== my_MAC1 EQU 0x00 my_MAC2 EQU 0xFF my_MAC3 EQU 0xEE my_MAC4 EQU 0xF0 my_MAC5 EQU 0xDA my_MAC6 EQU 0x42 .bss eth_unique_id dw 0 .code ; Initialize the ethmac controller. ; Supply a MAC address, set MD clock ; eth_init: lea r3,ETHMAC lw r1,#0x64 ; 100 sh r1,MIIMODER[r3] lw r1,#7 ; PHY address sh r1,MIIADDRESS[r3] lw r1,#0xEEF0DA42 sh r1,0x40[r3] ; MAC0 lw r1,#0x00FF sh r1,0x44[r3] ; MAC1 ret ; Request a packet and display on screen ; r1 = address where to put packet ; eth_request_packet: subui sp,sp,#24 sw r3,[sp] sw r2,8[sp] sw r4,16[sp] lea r3,ETHMAC lw r2,#4 ; clear rx interrupt sh r2,4[r3] sh r1,0x604[r3] ; storage address lw r2,#0xe000 ; enable interrupt sh r2,0x600[r3] eth1: nop inh r2,4[r3] bfext r2,r2,#2,#2 ; get bit #2 beq r2,r0,eth1 inh r2,0x600[r3] ; get from descriptor shrui r2,r2,#16 lw r3,#0 lea r4,TEXTSCR+7560 ; second last line of screen eth20: lbu r2,[r1+r3] ; get byte sc r2,[r4+r3*2] ; store to screen addui r3,r3,#1 cmpui r2,r3,#83 bne r2,r0,eth20 lw r3,[sp] lw r2,8[sp] lw r4,16[sp] ret #24 ; r1 = packet address ; eth_interpret_packet: subui sp,sp,#16 sw r3,[sp] sw r2,8[sp] lbu r2,12[r1] lbu r3,13[r1] bnei r2,#8,eth2 ; 0x806 ? bnei r3,#6,eth2 lw r1,#2 ; return r1 = 2 for ARP eth5: lw r3,[sp] lw r2,8[sp] ret #16 eth2: bnei r2,#8,eth3 ; 0x800 ? bnei r3,#0,eth3 lbu r2,23[r1] bnei r2,#1,eth4 lw r1,#1 bra eth5 ; return 1 ICMP eth4: bnei r2,#0x11,eth6 lw r1,#3 ; return 3 for UDP bra eth5 eth6: bnei r2,#6,eth7 lw r1,#4 ; return 4 for TCP bra eth5 eth7: eth3: xor r1,r1,r1 ; return zero for unknown lw r3,[sp] lw r2,8[sp] ret #16 ; r1 = address of packet to send ; r2 = packet length ; eth_send_packet: subui sp,sp,#16 sw r3,[sp] sw r4,8[sp] lea r3,ETHMAC ; wait for tx buffer to be clear eth8: inh r4,0x400[r3] bfext r4,r4,#15,#15 beqi r4,#1,eth8 lw r4,#1 ; clear tx interrupt sh r4,4[r3] ; set address sh r1,0x404[r3] ; set the packet length field and enable interrupts shlui r2,r2,#16 ori r2,r2,#0xF000 sh r2,0x400[r3] lw r4,8[sp] lw r3,[sp] ret #16 ; Only for IP type packets (not ARP) ; r1 = rx buffer address ; r2 = swap flag ; Returns: ; r1 = data start index ; eth_build_packet: subui sp,sp,#64 sw r3,[sp] sw r4,8[sp] sw r5,16[sp] sw r6,24[sp] sw r7,32[sp] sw r8,40[sp] sw r9,48[sp] sw r10,56[sp] lbu r3,6[r1] lbu r4,7[r1] lbu r5,8[r1] lbu r6,9[r1] lbu r7,10[r1] lbu r8,11[r1] ; write to destination header sb r3,[r1] sb r4,1[r1] sb r5,2[r1] sb r6,3[r1] sb r7,4[r1] sb r8,5[r1] ; write to source header lw r3,#my_MAC1 sb r3,6[r1] lw r3,#my_MAC2 sb r3,7[r1] lw r3,#my_MAC3 sb r3,8[r1] lw r3,#my_MAC4 sb r3,9[r1] lw r3,#my_MAC5 sb r3,10[r1] lw r3,#my_MAC6 sb r3,11[r1] bnei r2,#1,eth16 // if (swap) lbu r3,26[r1] lbu r4,27[r1] lbu r5,28[r1] lbu r6,29[r1] ; read destination lbu r7,30[r1] lbu r8,31[r1] lbu r9,32[r1] lbu r10,33[r1] ; write to sender sb r7,26[r1] sb r8,27[r1] sb r9,28[r1] sb r10,29[r1] ; write destination sb r3,30[r1] sb r4,31[r1] sb r5,32[r1] sb r6,33[r1] eth16: lw r3,eth_unique_id addui r3,r3,#1 sw r3,eth_unique_id sb r3,19[r1] shrui r3,r3,#8 sb r3,18[r1] lbu r3,14[r1] andi r3,r3,#0xF shlui r3,r3,#2 ; *4 addui r1,r3,#14 ; return datastart in r1 lw r3,[sp] lw r4,8[sp] lw r5,16[sp] lw r6,24[sp] lw r7,32[sp] lw r8,40[sp] lw r9,48[sp] lw r10,56[sp] ret #64 ; Compute IPv4 checksum of header ; r1 = packet address ; r2 = data start ; eth_checksum: subui sp,sp,#24 sw r3,[sp] sw r4,8[sp] sw r5,16[sp] ; set checksum to zero sb r0,24[r1] sb r0,25[r1] xor r3,r3,r3 ; r3 = sum = zero lw r4,#14 eth15: mov r5,r2 subui r5,r5,#1 ; r5 = datastart - 1 bge r4,r5,eth14 lbu r5,[r1+r4] ; shi = [rx_addr+i] lbu r6,1[r1+r4] ; slo = [rx_addr+i+1] shlui r5,r5,#8 or r5,r5,r6 ; shilo addu r3,r3,r5 ; sum = sum + shilo addui r4,r4,#2 ; i = i + 2 bra eth15 eth14: mov r5,r3 ; r5 = sum andi r3,r3,#0xffff shrui r5,r5,#16 addu r3,r3,r5 com r3,r3 sb r3,25[r1] ; low byte shrui r3,r3,#8 sb r3,24[r1] ; high byte sw r3,[sp] sw r4,8[sp] sw r5,16[sp] ret #24 ; r1 = packet address ; returns r1 = 1 if this IP ; eth_verifyIP: subui sp,sp,#32 sw r2,[sp] sw r3,8[sp] sw r4,16[sp] sw r5,24[sp] lbu r2,30[r1] lbu r3,31[r1] lbu r4,32[r1] lbu r5,33[r1] ; Check for general broadcast bnei r2,#0xFF,eth11 bnei r3,#0xFF,eth11 bnei r4,#0xFF,eth11 bnei r5,#0xFF,eth11 eth12: lw r1,#1 eth13: lw r2,[sp] lw r3,8[sp] lw r4,16[sp] lw r5,24[sp] ret #32 eth11: mov r1,r2 shlui r1,r1,#8 or r1,r1,r3 shlui r1,r1,#8 or r1,r1,r4 shlui r1,r1,#8 or r1,r1,r5 beqi r1,#0xC0A8012A,eth12 xor r1,r1,r1 bra eth13 eth_main: call eth_init eth_loop: xor r1,r1,r1 lw r1,#0x1_00000000 ; memory address zero call eth_request_packet call eth_interpret_packet ; r1 = packet type bnei r1,#1,eth10 mov r2,r1 ; save off r1, r2 = packet type lw r1,#0x1_00000000 ; memory address zero call eth_verifyIP mov r3,r1 mov r1,r2 ; r1 = packet type again bnei r3,#1,eth10 lw r1,#0x1_00000000 ; memory address zero lw r2,#1 call eth_build_packet mov r3,r1 ; r3 = icmpstart lw r1,#0x1_00000000 ; memory address zero sb r0,[r1+r3] ; [rx_addr+icmpstart] = 0 lbu r2,17[r1] addui r2,r2,#14 ; r2 = len mov r6,r2 ; r6 = len lbu r4,2[r1+r3] ; shi lbu r5,3[r1+r3] ; slo shlui r4,r4,#8 or r4,r4,r5 ; sum = {shi,slo}; com r4,r4 ; sum = ~sum subui r4,r4,#0x800 ; sum = sum - 0x800 com r4,r4 ; sum = ~sum sb r4,3[r1+r3] shrui r4,r4,#8 sb r4,2[r1+r3] mov r2,r3 call eth_checksum lw r1,#0x1_00000000 ; memory address zero mov r2,r6 call eth_send_packet jmp eth_loop eth10: ; r2 = rx_addr bnei r1,#2,eth_loop ; Do we have ARP ? ; xor r2,r2,r2 ; memory address zero lw r2,#1_00000000 ; get the opcode lbu r13,21[r2] bnei r13,#1,eth_loop ; ARP request ; get destination IP address lbu r9,38[r2] lbu r10,39[r2] lbu r11,40[r2] lbu r12,41[r2] ; set r15 = destination IP mov r15,r9 shlui r15,r15,#8 or r15,r15,r10 shlui r15,r15,#8 or r15,r15,r11 shlui r15,r15,#8 or r15,r15,r12 ; Is it our IP ? bnei r15,#0xC0A8012A,eth_loop; //192.168.1.42 ; get source IP address lbu r5,28[r2] lbu r6,29[r2] lbu r7,30[r2] lbu r8,31[r2] ; set r14 = source IP mov r14,r5 shlui r14,r14,#8 or r14,r14,r6 shlui r14,r14,#8 or r14,r14,r7 shlui r14,r14,#8 or r14,r14,r8 ; Get the source MAC address lbu r16,22[r2] lbu r17,23[r2] lbu r18,24[r2] lbu r19,25[r2] lbu r20,26[r2] lbu r21,27[r2] ; write to destination header sb r16,[r2] sb r17,1[r2] sb r18,2[r2] sb r19,3[r2] sb r20,4[r2] sb r21,5[r2] ; and write to ARP destination sb r16,32[r2] sb r17,33[r2] sb r18,34[r2] sb r19,35[r2] sb r20,36[r2] sb r21,37[r2] ; write to source header ; stbc #0x00,6[r2] ; stbc #0xFF,7[r2] ; stbc #0xEE,8[r2] ; stbc #0xF0,9[r2] ; stbc #0xDA,10[r2] ; stbc #0x42,11[r2] sb r0,6[r2] lw r1,#0xFF sb r1,7[r2] lw r1,#0xEE sb r1,8[r2] lw r1,#0xF0 sb r1,9[r2] lw r1,#0xDA sb r1,10[r2] lw r1,#0x42 sb r1,11[r2] ; write to ARP source ; stbc #0x00,22[r2] ; stbc #0xFF,23[r2] ; stbc #0xEE,24[r2] ; stbc #0xF0,25[r2] ; stbc #0xDA,26[r2] ; stbc #0x42,27[r2] sb r0,22[r2] lw r1,#0xFF sb r1,23[r2] lw r1,#0xEE sb r1,24[r2] lw r1,#0xF0 sb r1,25[r2] lw r1,#0xDA sb r1,26[r2] lw r1,#0x42 sb r1,27[r2] ; swap sender / destination IP ; write sender sb r9,28[r2] sb r10,29[r2] sb r11,30[r2] sb r12,31[r2] ; write destination sb r5,38[r2] sb r6,39[r2] sb r7,40[r2] sb r8,41[r2] ; change request to reply ; stbc #2,21[r2] lw r1,#2 sb r1,21[r2] mov r1,r2 ; r1 = packet address lw r2,#0x2A ; r2 = packet length call eth_send_packet jmp eth_loop ;============================================================================== ;============================================================================== ;****************************************************************; ; ; ; Tiny BASIC for the Raptor64 ; ; ; ; Derived from a 68000 derivative of Palo Alto Tiny BASIC as ; ; published in the May 1976 issue of Dr. Dobb's Journal. ; ; Adapted to the 68000 by: ; ; Gordon brndly ; ; 12147 - 51 Street ; ; Edmonton AB T5W 3G8 ; ; Canada ; ; (updated mailing address for 1996) ; ; ; ; Adapted to the Raptor64 by: ; ; Robert Finch ; ; Ontario, Canada ; ; robfinch<remove>@opencores.org ; ;****************************************************************; ; Copyright (C) 2012 by Robert Finch. This program may be ; ; freely distributed for personal use only. All commercial ; ; rights are reserved. ; ;****************************************************************; ; ; Register Usage ; r8 = text pointer (global usage) ; r3,r4 = inputs parameters to subroutines ; r2 = return value ; ;* Vers. 1.0 1984/7/17 - Original version by Gordon brndly ;* 1.1 1984/12/9 - Addition of '0x' print term by Marvin Lipford ;* 1.2 1985/4/9 - Bug fix in multiply routine by Rick Murray ; ; Standard jump table. You can change these addresses if you are ; customizing this interpreter for a different environment. ; GOSTART: jmp CSTART ; Cold Start entry point GOWARM: jmp WSTART ; Warm Start entry point GOOUT: jmp OUTC ; Jump to character-out routine GOIN: jmp INC ;Jump to character-in routine GOAUXO: jmp AUXOUT ; Jump to auxiliary-out routine GOAUXI: jmp AUXIN ; Jump to auxiliary-in routine GOBYE: jmp BYEBYE ; Jump to monitor, DOS, etc. ; ; Modifiable system constants: ; align 8 TXTBGN dw 0x000000001_00600000 ;TXT ;beginning of program memory ENDMEM dw 0x000000001_07FFFFF8 ; end of available memory ; ; The main interpreter starts here: ; ; Usage ; r1 = temp ; r8 = text buffer pointer ; r12 = end of text in text buffer ; align 16 CSTART: ; First save off the link register and OS sp value subui sp,sp,#8 sw lr,[sp] sw sp,OSSP lw sp,ENDMEM ; initialize stack pointer subui sp,sp,#8 sw lr,[sp] ; save off return address sb r0,CursorRow ; set screen output sb r0,CursorCol sb r0,CursorFlash sh r0,pos lw r2,#0x10000020 ; black chars, yellow background ; sh r2,charToPrint call ClearScreen lea r1,msgInit ; tell who we are ; call PRMESGAUX lea r1,msgInit ; tell who we are call PRMESG lw r1,TXTBGN ; init. end-of-program pointer sw r1,TXTUNF lw r1,ENDMEM ; get address of end of memory subui r1,r1,#4096 ; reserve 4K for the stack sw r1,STKBOT subui r1,r1,#16384 ; 1000 vars sw r1,VARBGN call clearVars ; clear the variable area lw r1,VARBGN ; calculate number of bytes free lw r3,TXTUNF subu r1,r1,r3 setlo r2,#0 call PRTNUM lea r1,msgBytesFree call PRMESG WSTART: sw r0,LOPVAR ; initialize internal variables sw r0,STKGOS sw r0,CURRNT ; current line number pointer = 0 lw sp,ENDMEM ; init S.P. again, just in case lea r1,msgReady ; display "Ready" call PRMESG ST3: setlo r1,#'>' ; Prompt with a '>' and call GETLN ; read a line. call TOUPBUF ; convert to upper case mov r12,r8 ; save pointer to end of line lea r8,BUFFER ; point to the beginning of line call TSTNUM ; is there a number there? call IGNBLK ; skip trailing blanks ; does line no. exist? (or nonzero?) beq r1,r0,DIRECT ; if not, it's a direct statement bleu r1,#0xFFFF,ST2 ; see if line no. is <= 16 bits lea r1,msgLineRange ; if not, we've overflowed bra ERROR ST2: ; ugliness - store a character at potentially an ; odd address (unaligned). mov r2,r1 ; r2 = line number sb r2,-2[r8] shrui r2,r2,#8 sb r2,-1[r8] ; store the binary line no. subui r8,r8,#2 call FNDLN ; find this line in save area mov r13,r9 ; save possible line pointer beq r1,r0,ST4 ; if not found, insert ; here we found the line, so we're replacing the line ; in the text area ; first step - delete the line setlo r1,#0 call FNDNXT ; find the next line (into r9) bne r1,r0,ST7 beq r9,r0,ST6 ; no more lines ST7: mov r1,r9 ; r1 = pointer to next line mov r2,r13 ; pointer to line to be deleted lw r3,TXTUNF ; points to top of save area call MVUP ; move up to delete sw r2,TXTUNF ; update the end pointer ; we moved the lines of text after the line being ; deleted down, so the pointer to the next line ; needs to be reset mov r9,r13 bra ST4 ; here there were no more lines, so just move the ; end of text pointer down ST6: sw r13,TXTUNF mov r9,r13 ST4: ; here we're inserting because the line wasn't found ; or it was deleted from the text area mov r1,r12 ; calculate the length of new line sub r1,r1,r8 blei r1,#3,ST3 ; is it just a line no. & CR? if so, it was just a delete lw r11,TXTUNF ; compute new end of text mov r10,r11 ; r10 = old TXTUNF add r11,r11,r1 ; r11 = new top of TXTUNF (r1=line length) lw r1,VARBGN ; see if there's enough room bltu r11,r1,ST5 lea r1,msgTooBig ; if not, say so jmp ERROR ; open a space in the text area ST5: sw r11,TXTUNF ; if so, store new end position mov r1,r10 ; points to old end of text mov r2,r11 ; points to new end of text mov r3,r9 ; points to start of line after insert line call MVDOWN ; move things out of the way ; copy line into text space mov r1,r8 ; set up to do the insertion; move from buffer mov r2,r13 ; to vacated space mov r3,r12 ; until end of buffer call MVUP ; do it bra ST3 ; go back and get another line ;****************************************************************** ; ; *** Tables *** DIRECT *** EXEC *** ; ; This section of the code tests a string against a table. When ; a match is found, control is transferred to the section of ; code according to the table. ; ; At 'EXEC', r8 should point to the string, r9 should point to ; the character table, and r10 should point to the execution ; table. At 'DIRECT', r8 should point to the string, r9 and ; r10 will be set up to point to TAB1 and TAB1_1, which are ; the tables of all direct and statement commands. ; ; A '.' in the string will terminate the test and the partial ; match will be considered as a match, e.g. 'P.', 'PR.','PRI.', ; 'PRIN.', or 'PRINT' will all match 'PRINT'. ; ; There are two tables: the character table and the execution ; table. The character table consists of any number of text items. ; Each item is a string of characters with the last character's ; high bit set to one. The execution table holds a 32-bit ; execution addresses that correspond to each entry in the ; character table. ; ; The end of the character table is a 0 byte which corresponds ; to the default routine in the execution table, which is ; executed if none of the other table items are matched. ; ; Character-matching tables: TAB1: db "LIS",'T'+0x80 ; Direct commands db "LOA",'D'+0x80 db "NE",'W'+0x80 db "RU",'N'+0x80 db "SAV",'E'+0x80 TAB2: db "NEX",'T'+0x80 ; Direct / statement db "LE",'T'+0x80 db "I",'F'+0x80 db "GOT",'O'+0x80 db "GOSU",'B'+0x80 db "RETUR",'N'+0x80 db "RE",'M'+0x80 db "FO",'R'+0x80 db "INPU",'T'+0x80 db "PRIN",'T'+0x80 db "POKE",'C'+0x80 db "POKE",'H'+0x80 db "POKE",'W'+0x80 db "POK",'E'+0x80 db "STO",'P'+0x80 db "BY",'E'+0x80 db "SY",'S'+0x80 db "CL",'S'+0x80 db "CL",'R'+0x80 db "RDC",'F'+0x80 db 0 TAB4: db "PEEK",'C'+0x80 ;Functions db "PEEK",'H'+0x80 ;Functions db "PEEK",'W'+0x80 ;Functions db "PEE",'K'+0x80 ;Functions db "RN",'D'+0x80 db "AB",'S'+0x80 db "SIZ",'E'+0x80 db "US",'R'+0x80 db 0 TAB5: db "T",'O'+0x80 ;"TO" in "FOR" db 0 TAB6: db "STE",'P'+0x80 ;"STEP" in "FOR" db 0 TAB8: db '>','='+0x80 ;Relational operators db '<','>'+0x80 db '>'+0x80 db '='+0x80 db '<','='+0x80 db '<'+0x80 db 0 TAB9: db "AN",'D'+0x80 db 0 TAB10: db "O",'R'+0x80 db 0 .align 8 ;* Execution address tables: TAB1_1: dw LISTX ;Direct commands dw LOAD dw NEW dw RUN dw SAVE TAB2_1: dw NEXT ; Direct / statement dw LET dw IF dw GOTO dw GOSUB dw RETURN dw IF2 ; REM dw FOR dw INPUT dw PRINT dw POKEC dw POKEH dw POKEW dw POKE dw STOP dw GOBYE dw SYSX dw _cls dw _clr dw _rdcf dw DEFLT TAB4_1: dw PEEKC dw PEEKH dw PEEKW dw PEEK ;Functions dw RND dw ABS dw SIZEX dw USRX dw XP40 TAB5_1 dw FR1 ;"TO" in "FOR" dw QWHAT TAB6_1 dw FR2 ;"STEP" in "FOR" dw FR3 TAB8_1 dw XP11 ;>= Relational operators dw XP12 ;<> dw XP13 ;> dw XP15 ;= dw XP14 ;<= dw XP16 ;< dw XP17 TAB9_1 dw XP_AND dw XP_ANDX TAB10_1 dw XP_OR dw XP_ORX .align 4 ;* ; r3 = match flag (trashed) ; r9 = text table ; r10 = exec table ; r11 = trashed DIRECT: lea r9,TAB1 lea r10,TAB1_1 EXEC: mov r11,lr ; save link reg call IGNBLK ; ignore leading blanks mov lr,r11 ; restore link reg mov r11,r8 ; save the pointer setlo r3,#0 ; clear match flag EXLP: lbu r1,[r8] ; get the program character addui r8,r8,#1 lbu r2,[r9] ; get the table character bne r2,r0,EXNGO ; If end of table, mov r8,r11 ; restore the text pointer and... bra EXGO ; execute the default. EXNGO: beq r1,r3,EXGO ; Else check for period... if so, execute andi r2,r2,#0x7f ; ignore the table's high bit beq r2,r1,EXMAT; is there a match? addui r10,r10,#8 ;if not, try the next entry mov r8,r11 ; reset the program pointer setlo r3,#0 ; sorry, no match EX1: addui r9,r9,#1 lb r1,-1[r9] ; get to the end of the entry bgt r1,r0,EX1 bra EXLP ; back for more matching EXMAT: setlo r3,#'.' ; we've got a match so far addui r9,r9,#1 lb r1,-1[r9] ; end of table entry? bgt r1,r0,EXLP ; if not, go back for more EXGO: lw r11,[r10] ; execute the appropriate routine jal r0,[r11] ; lb r1,[r8] ; get token from text space ; bpl ; and r1,#0x7f ; shl r1,#2 ; * 4 - word offset ; add r1,r1,#TAB1_1 ; lw r1,[r1] ; jmp [r1] ;****************************************************************** ; ; What follows is the code to execute direct and statement ; commands. Control is transferred to these points via the command ; table lookup code of 'DIRECT' and 'EXEC' in the last section. ; After the command is executed, control is transferred to other ; sections as follows: ; ; For 'LISTX', 'NEW', and 'STOP': go back to the warm start point. ; For 'RUN': go execute the first stored line if any; else go ; back to the warm start point. ; For 'GOTO' and 'GOSUB': go execute the target line. ; For 'RETURN' and 'NEXT'; go back to saved return line. ; For all others: if 'CURRNT' is 0, go to warm start; else go ; execute next command. (This is done in 'FINISH'.) ; ;****************************************************************** ; ; *** NEW *** STOP *** RUN (& friends) *** GOTO *** ; ; 'NEW<CR>' sets TXTUNF to point to TXTBGN ; ; 'STOP<CR>' goes back to WSTART ; ; 'RUN<CR>' finds the first stored line, stores its address ; in CURRNT, and starts executing it. Note that only those ; commands in TAB2 are legal for a stored program. ; ; There are 3 more entries in 'RUN': ; 'RUNNXL' finds next line, stores it's address and executes it. ; 'RUNTSL' stores the address of this line and executes it. ; 'RUNSML' continues the execution on same line. ; ; 'GOTO expr<CR>' evaluates the expression, finds the target ; line, and jumps to 'RUNTSL' to do it. ; NEW: call ENDCHK lw r1,TXTBGN sw r1,TXTUNF ; set the end pointer call clearVars STOP: call ENDCHK bra WSTART ; WSTART will reset the stack RUN: call ENDCHK lw r8,TXTBGN ; set pointer to beginning sw r8,CURRNT call clearVars RUNNXL: ; RUN <next line> lw r1,CURRNT ; executing a program? beq r1,r0,WSTART ; if not, we've finished a direct stat. setlo r1,#0 ; else find the next line number mov r9,r8 call FNDLNP ; search for the next line bne r1,r0,RUNTSL bne r9,r0,RUNTSL bra WSTART ; if we've fallen off the end, stop RUNTSL: ; RUN <this line> sw r9,CURRNT ; set CURRNT to point to the line no. lea r8,2[r9] ; set the text pointer to RUNSML: ; RUN <same line> call CHKIO ; see if a control-C was pressed lea r9,TAB2 ; find command in TAB2 lea r10,TAB2_1 bra EXEC ; and execute it GOTO: call OREXPR ;evaluate the following expression mov r5,r1 call ENDCHK ;must find end of line mov r1,r5 call FNDLN ; find the target line bne r1,r0,RUNTSL ; go do it lea r1,msgBadGotoGosub bra ERROR ; no such line no. _clr: call clearVars bra FINISH ; Clear the variable area of memory clearVars: subui sp,sp,#16 sw r6,[sp] sw lr,8[sp] setlo r6,#2048 ; number of words to clear lw r1,VARBGN cv1: sw r0,[r1] add r1,r1,#8 loop r6,cv1 lw lr,8[sp] lw r6,[sp] ret #16 ;****************************************************************** ; LIST ; ; LISTX has two forms: ; 'LIST<CR>' lists all saved lines ; 'LIST #<CR>' starts listing at the line # ; Control-S pauses the listing, control-C stops it. ;****************************************************************** ; LISTX: call TSTNUM ; see if there's a line no. mov r5,r1 call ENDCHK ; if not, we get a zero mov r1,r5 call FNDLN ; find this or next line LS1: bne r1,r0,LS4 beq r9,r0,WSTART ; warm start if we passed the end LS4: mov r1,r9 call PRTLN ; print the line mov r9,r1 ; set pointer for next call CHKIO ; check for listing halt request beq r1,r0,LS3 bnei r1,#CTRLS,LS3 ; pause the listing? LS2: call CHKIO ; if so, wait for another keypress beq r1,r0,LS2 LS3: setlo r1,#0 call FNDLNP ; find the next line bra LS1 ;****************************************************************** ; PRINT command is 'PRINT ....:' or 'PRINT ....<CR>' ; where '....' is a list of expressions, formats, back-arrows, ; and strings. These items a separated by commas. ; ; A format is a pound sign followed by a number. It controls ; the number of spaces the value of an expression is going to ; be printed in. It stays effective for the rest of the print ; command unless changed by another format. If no format is ; specified, 11 positions will be used. ; ; A string is quoted in a pair of single- or double-quotes. ; ; An underline (back-arrow) means generate a <CR> without a <LF> ; ; A <CR LF> is generated after the entire list has been printed ; or if the list is empty. If the list ends with a semicolon, ; however, no <CR LF> is generated. ;****************************************************************** ; PRINT: lw r5,#11 ; D4 = number of print spaces setlo r3,#':' lea r4,PR2 call TSTC ; if null list and ":" call CRLF ; give CR-LF and continue bra RUNSML ; execution on the same line PR2: setlo r3,#CR lea r4,PR0 call TSTC ;if null list and <CR> call CRLF ;also give CR-LF and bra RUNNXL ;execute the next line PR0: setlo r3,#'#' lea r4,PR1 call TSTC ;else is it a format? call OREXPR ; yes, evaluate expression lw r5,r1 ; and save it as print width bra PR3 ; look for more to print PR1: setlo r3,#'$' lea r4,PR4 call TSTC ; is character expression? (MRL) call OREXPR ; yep. Evaluate expression (MRL) call GOOUT ; print low byte (MRL) bra PR3 ;look for more. (MRL) PR4: call QTSTG ; is it a string? ; the following branch must occupy only two bytes! bra PR8 ; if not, must be an expression PR3: setlo r3,#',' lea r4,PR6 call TSTC ; if ",", go find next call FIN ;in the list. bra PR0 PR6: call CRLF ;list ends here bra FINISH PR8: call OREXPR ; evaluate the expression lw r2,r5 ; set the width call PRTNUM ; print its value bra PR3 ; more to print? FINISH: call FIN ; Check end of command jmp QWHAT ; print "What?" if wrong ;******************************************************************* ; ; *** GOSUB *** & RETURN *** ; ; 'GOSUB expr:' or 'GOSUB expr<CR>' is like the 'GOTO' command, ; except that the current text pointer, stack pointer, etc. are ; saved so that execution can be continued after the subroutine ; 'RETURN's. In order that 'GOSUB' can be nested (and even ; recursive), the save area must be stacked. The stack pointer ; is saved in 'STKGOS'. The old 'STKGOS' is saved on the stack. ; If we are in the main routine, 'STKGOS' is zero (this was done ; in the initialization section of the interpreter), but we still ; save it as a flag for no further 'RETURN's. ;****************************************************************** ; GOSUB: call PUSHA ; save the current 'FOR' parameters call OREXPR ; get line number call FNDLN ; find the target line bne r1,r0,gosub1 lea r1,msgBadGotoGosub bra ERROR ; if not there, say "How?" gosub1: sub sp,sp,#24 sw r8,[sp] ; save text pointer lw r1,CURRNT sw r1,8[sp] ; found it, save old 'CURRNT'... lw r1,STKGOS sw r1,16[sp] ; and 'STKGOS' sw r0,LOPVAR ; load new values sw sp,STKGOS bra RUNTSL ;****************************************************************** ; 'RETURN<CR>' undoes everything that 'GOSUB' did, and thus ; returns the execution to the command after the most recent ; 'GOSUB'. If 'STKGOS' is zero, it indicates that we never had ; a 'GOSUB' and is thus an error. ;****************************************************************** ; RETURN: call ENDCHK ; there should be just a <CR> lw r1,STKGOS ; get old stack pointer bne r1,r0,return1 lea r1,msgRetWoGosub bra ERROR ; if zero, it doesn't exist return1: mov sp,r1 ; else restore it lw r1,16[sp] sw r1,STKGOS ; and the old 'STKGOS' lw r1,8[sp] sw r1,CURRNT ; and the old 'CURRNT' lw r8,[sp] ; and the old text pointer add sp,sp,#24 call POPA ;and the old 'FOR' parameters bra FINISH ;and we are back home ;****************************************************************** ; *** FOR *** & NEXT *** ; ; 'FOR' has two forms: ; 'FOR var=exp1 TO exp2 STEP exp1' and 'FOR var=exp1 TO exp2' ; The second form means the same thing as the first form with a ; STEP of positive 1. The interpreter will find the variable 'var' ; and set its value to the current value of 'exp1'. It also ; evaluates 'exp2' and 'exp1' and saves all these together with ; the text pointer, etc. in the 'FOR' save area, which consists of ; 'LOPVAR', 'LOPINC', 'LOPLMT', 'LOPLN', and 'LOPPT'. If there is ; already something in the save area (indicated by a non-zero ; 'LOPVAR'), then the old save area is saved on the stack before ; the new values are stored. The interpreter will then dig in the ; stack and find out if this same variable was used in another ; currently active 'FOR' loop. If that is the case, then the old ; 'FOR' loop is deactivated. (i.e. purged from the stack) ;****************************************************************** ; FOR: call PUSHA ; save the old 'FOR' save area call SETVAL ; set the control variable sw r1,LOPVAR ; save its address lea r9,TAB5 lea r10,TAB5_1; use 'EXEC' to test for 'TO' jmp EXEC FR1: call OREXPR ; evaluate the limit sw r1,LOPLMT ; save that lea r9,TAB6 lea r10,TAB6_1 ; use 'EXEC' to test for the word 'STEP jmp EXEC FR2: call OREXPR ; found it, get the step value bra FR4 FR3: setlo r1,#1 ; not found, step defaults to 1 FR4: sw r1,LOPINC ; save that too FR5: lw r2,CURRNT sw r2,LOPLN ; save address of current line number sw r8,LOPPT ; and text pointer lw r3,sp ; dig into the stack to find 'LOPVAR' lw r6,LOPVAR bra FR7 FR6: addui r3,r3,#40 ; look at next stack frame FR7: lw r2,[r3] ; is it zero? beq r2,r0,FR8 ; if so, we're done bne r2,r6,FR6 ; same as current LOPVAR? nope, look some more lw r1,r3 ; Else remove 5 long words from... addui r2,r3,#40 ; inside the stack. lw r3,sp call MVDOWN add sp,sp,#40 ; set the SP 5 long words up FR8: bra FINISH ; and continue execution ;****************************************************************** ; 'NEXT var' serves as the logical (not necessarily physical) end ; of the 'FOR' loop. The control variable 'var' is checked with ; the 'LOPVAR'. If they are not the same, the interpreter digs in ; the stack to find the right one and purges all those that didn't ; match. Either way, it then adds the 'STEP' to that variable and ; checks the result with against the limit value. If it is within ; the limit, control loops back to the command following the ; 'FOR'. If it's outside the limit, the save area is purged and ; execution continues. ;****************************************************************** ; NEXT: setlo r1,#0 ; don't allocate it call TSTV ; get address of variable bne r1,r0,NX4 lea r1,msgNextVar bra ERROR ; if no variable, say "What?" NX4: mov r9,r1 ; save variable's address NX0: lw r1,LOPVAR ; If 'LOPVAR' is zero, we never... bne r1,r0,NX5 ; had a FOR loop lea r1,msgNextFor bra ERROR NX5: beq r1,r9,NX2 ; else we check them OK, they agree call POPA ; nope, let's see the next frame bra NX0 NX2: lw r1,[r9] ; get control variable's value lw r2,LOPINC addu r1,r1,r2 ; add in loop increment ; BVS.L QHOW say "How?" for 32-bit overflow sw r1,[r9] ; save control variable's new value lw r3,LOPLMT ; get loop's limit value bgt r2,r0,NX1 ; check loop increment, branch if loop increment is positive blt r1,r3,NXPurge ; test against limit bra NX3 NX1: bgt r1,r3,NXPurge NX3: lw r8,LOPLN ; Within limit, go back to the... sw r8,CURRNT lw r8,LOPPT ; saved 'CURRNT' and text pointer. bra FINISH NXPurge: call POPA ; purge this loop bra FINISH ;****************************************************************** ; *** REM *** IF *** INPUT *** LET (& DEFLT) *** ; ; 'REM' can be followed by anything and is ignored by the ; interpreter. ; ;REM ; br IF2 ; skip the rest of the line ; 'IF' is followed by an expression, as a condition and one or ; more commands (including other 'IF's) separated by colons. ; Note that the word 'THEN' is not used. The interpreter evaluates ; the expression. If it is non-zero, execution continues. If it ; is zero, the commands that follow are ignored and execution ; continues on the next line. ;****************************************************************** ; IF: call OREXPR ; evaluate the expression IF1: bne r1,r0,RUNSML ; is it zero? if not, continue IF2: mov r9,r8 ; set lookup pointer setlo r1,#0 ; find line #0 (impossible) call FNDSKP ; if so, skip the rest of the line bgt r1,r0,WSTART ; if no next line, do a warm start IF3: bra RUNTSL ; run the next line ;****************************************************************** ; INPUT is called first and establishes a stack frame INPERR: lw sp,STKINP ; restore the old stack pointer lw r8,16[sp] sw r8,CURRNT ; and old 'CURRNT' lw r8,8[sp] ; and old text pointer addui sp,sp,#40 ; fall through will subtract 40 ; 'INPUT' is like the 'PRINT' command, and is followed by a list ; of items. If the item is a string in single or double quotes, ; or is an underline (back arrow), it has the same effect as in ; 'PRINT'. If an item is a variable, this variable name is ; printed out followed by a colon, then the interpreter waits for ; an expression to be typed in. The variable is then set to the ; value of this expression. If the variable is preceeded by a ; string (again in single or double quotes), the string will be ; displayed followed by a colon. The interpreter the waits for an ; expression to be entered and sets the variable equal to the ; expression's value. If the input expression is invalid, the ; interpreter will print "What?", "How?", or "Sorry" and reprint ; the prompt and redo the input. The execution will not terminate ; unless you press control-C. This is handled in 'INPERR'. ; INPUT: subui sp,sp,#40 ; allocate stack frame sw r5,32[sp] IP6: sw r8,[sp] ; save in case of error call QTSTG ; is next item a string? bra IP2 ; nope - this branch must take only two bytes setlo r1,#1 ; allocate var call TSTV ; yes, but is it followed by a variable? beq r1,r0,IP4 ; if not, brnch mov r10,r1 ; put away the variable's address bra IP3 ; if so, input to variable IP2: sw r8,8[sp] ; save for 'PRTSTG' setlo r1,#1 call TSTV ; must be a variable now bne r1,r0,IP7 lea r1,msgInputVar bra ERROR ; "What?" it isn't? IP7: mov r10,r1 ; put away the variable's address lb r5,[r8] ; get ready for 'PRTSTG' by null terminating sb r0,[r8] lw r1,8[sp] ; get back text pointer call PRTSTG ; print string as prompt sb r5,[r8] ; un-null terminate IP3 sw r8,8[sp] ; save in case of error lw r1,CURRNT sw r1,16[sp] ; also save 'CURRNT' setlo r1,#-1 sw r1,CURRNT ; flag that we are in INPUT sw sp,STKINP ; save the stack pointer too sw r10,24[sp] ; save the variable address setlo r1,#':' ; print a colon first call GETLN ; then get an input line lea r8,BUFFER ; point to the buffer call OREXPR ; evaluate the input lw r10,24[sp] ; restore the variable address sw r1,[r10] ; save value in variable lw r1,16[sp] ; restore old 'CURRNT' sw r1,CURRNT lw r8,8[sp] ; and the old text pointer IP4: setlo r3,#',' lea r4,IP5 ; is the next thing a comma? call TSTC bra IP6 ; yes, more items IP5: lw r5,32[sp] add sp,sp,#40 ; clean up the stack jmp FINISH DEFLT: lb r1,[r8] beq r1,#CR,FINISH ; empty line is OK else it is 'LET' ;****************************************************************** ; 'LET' is followed by a list of items separated by commas. ; Each item consists of a variable, an equals sign, and an ; expression. The interpreter evaluates the expression and sets ; the variable to that value. The interpreter will also handle ; 'LET' commands without the word 'LET'. This is done by 'DEFLT'. ;****************************************************************** ; LET: call SETVAL ; do the assignment setlo r3,#',' lea r4,FINISH call TSTC ; check for more 'LET' items bra LET LT1: bra FINISH ; until we are finished. ;****************************************************************** ; *** LOAD *** & SAVE *** ; ; These two commands transfer a program to/from an auxiliary ; device such as a cassette, another computer, etc. The program ; is converted to an easily-stored format: each line starts with ; a colon, the line no. as 4 hex digits, and the rest of the line. ; At the end, a line starting with an '@' sign is sent. This ; format can be read back with a minimum of processing time by ; the Butterfly. ;****************************************************************** ; LOAD lw r8,TXTBGN ; set pointer to start of prog. area setlo r1,#CR ; For a CP/M host, tell it we're ready... call GOAUXO ; by sending a CR to finish PIP command. LOD1: call GOAUXI ; look for start of line ble r1,r0,LOD1 beq r1,#'@',LODEND ; end of program? beq r1,#0x1A,LODEND ; or EOF marker bne r1,#':',LOD1 ; if not, is it start of line? if not, wait for it call GCHAR ; get line number sb r1,[r8] ; store it shrui r1,r1,#8 sb r1,1[r8] addui r8,r8,#2 LOD2: call GOAUXI ; get another text char. ble r1,r0,LOD2 sb r1,[r8] addui r8,r8,#1 ; store it bne r1,#CR,LOD2 ; is it the end of the line? if not, go back for more bra LOD1 ; if so, start a new line LODEND: sw r8,TXTUNF ; set end-of program pointer bra WSTART ; back to direct mode ; get character from input (16 bit value) GCHAR: subui sp,sp,#24 sw r5,[sp] sw r6,8[sp] sw lr,16[sp] setlo r6,#3 ; repeat four times setlo r5,#0 GCHAR1: call GOAUXI ; get a char ble r1,r0,GCHAR1 call asciiToHex shli r5,r5,#4 or r5,r5,r1 loop r6,GCHAR1 mov r1,r5 lw lr,16[sp] lw r6,8[sp] lw r5,[sp] ret #24 ; convert an ascii char to hex code ; input ; r1 = char to convert asciiToHex: blei r1,#'9',a2h1 ; less than '9' subui r1,r1,#7 ; shift 'A' to '9'+1 a2h1: subui r1,r1,#'0' ; andi r1,r1,#15 ; make sure a nybble ret SAVE: lw r8,TXTBGN ;set pointer to start of prog. area lw r9,TXTUNF ;set pointer to end of prog. area SAVE1: call AUXOCRLF ; send out a CR & LF (CP/M likes this) bgeu r8,r9,SAVEND ; are we finished? setlo r1,#':' ; if not, start a line call GOAUXO lbu r1,[r8] ; get line number lbu r2,1[r8] shli r2,r2,#8 or r1,r1,r2 addui r8,r8,#2 call PWORD ; output line number as 4-digit hex SAVE2: lb r1,[r8] ; get a text char. addui r8,r8,#1 beqi r1,#CR,SAVE1 ; is it the end of the line? if so, send CR & LF and start new line call GOAUXO ; send it out bra SAVE2 ; go back for more text SAVEND: setlo r1,#'@' ; send end-of-program indicator call GOAUXO call AUXOCRLF ; followed by a CR & LF setlo r1,#0x1A ; and a control-Z to end the CP/M file call GOAUXO bra WSTART ; then go do a warm start ; output a CR LF sequence to auxillary output ; Registers Affected ; r3 = LF AUXOCRLF: subui sp,sp,#8 sw lr,[sp] setlo r1,#CR call GOAUXO setlo r1,#LF call GOAUXO lw lr,[sp] ret #8 ; output a word in hex format ; tricky because of the need to reverse the order of the chars PWORD: sub sp,sp,#16 sw lr,[sp] sw r5,8[sp] lea r5,NUMWKA+15 mov r4,r1 ; r4 = value pword1: mov r1,r4 ; r1 = value shrui r4,r4,#4 ; shift over to next nybble call toAsciiHex ; convert LS nybble to ascii hex sb r1,[r5] ; save in work area subui r5,r5,#1 cmpui r1,r5,#NUMWKA bge r1,r0,pword1 pword2: addui r5,r5,#1 lb r1,[r5] ; get char to output call GOAUXO ; send it cmpui r1,r5,#NUMWKA+15 blt r1,r0,pword2 lw r5,8[sp] lw lr,[sp] ret #16 ; convert nybble in r2 to ascii hex char2 ; r2 = character to convert toAsciiHex: andi r1,r1,#15 ; make sure it's a nybble blti r1,#10,tah1 ; > 10 ? addi r1,r1,#7 ; bump it up to the letter 'A' tah1: addui r1,r1,#'0' ; bump up to ascii '0' ret ;****************************************************************** ; *** POKE *** & SYSX *** ; ; 'POKE expr1,expr2' stores the byte from 'expr2' into the memory ; address specified by 'expr1'. ; ; 'SYSX expr' jumps to the machine language subroutine whose ; starting address is specified by 'expr'. The subroutine can use ; all registers but must leave the stack the way it found it. ; The subroutine returns to the interpreter by executing an RET. ;****************************************************************** ; POKE: subui sp,sp,#8 call OREXPR ; get the memory address setlo r3,#',' lea r4,PKER ; it must be followed by a comma call TSTC ; it must be followed by a comma sw r1,[sp] ; save the address call OREXPR ; get the byte to be POKE'd lw r2,[sp] ; get the address back sb r1,[r2] ; store the byte in memory addui sp,sp,#8 bra FINISH PKER: lea r1,msgComma bra ERROR ; if no comma, say "What?" POKEC: subui sp,sp,#8 call OREXPR ; get the memory address setlo r3,#',' lea r4,PKER ; it must be followed by a comma call TSTC ; it must be followed by a comma sw r1,[sp] ; save the address call OREXPR ; get the byte to be POKE'd lw r2,[sp] ; get the address back sc r1,[r2] ; store the char in memory addui sp,sp,#8 jmp FINISH POKEH: subui sp,sp,#8 call OREXPR ; get the memory address setlo r3,#',' lea r4,PKER ; it must be followed by a comma call TSTC sw r1,[sp] ; save the address call OREXPR ; get the byte to be POKE'd lw r2,[sp] ; get the address back sh r1,[r2] ; store the word in memory addui sp,sp,#8 jmp FINISH POKEW: subui sp,sp,#8 call OREXPR ; get the memory address setlo r3,#',' lea r4,PKER ; it must be followed by a comma call TSTC sw r1,[sp] ; save the address call OREXPR ; get the word to be POKE'd lw r2,[sp] ; get the address back sw r1,[r2] ; store the word in memory addui sp,sp,#8 jmp FINISH SYSX: subui sp,sp,#8 call OREXPR ; get the subroutine's address bne r1,r0,sysx1 ; make sure we got a valid address lea r1,msgSYSBad bra ERROR sysx1: sw r8,[sp] ; save the text pointer jal r31,[r1] ; jump to the subroutine lw r8,[sp] ; restore the text pointer addui sp,sp,#8 bra FINISH ;****************************************************************** ; *** EXPR *** ; ; 'EXPR' evaluates arithmetical or logical expressions. ; <OREXPR>::= <ANDEXPR> OR <ANDEXPR> ... ; <ANDEXPR>::=<EXPR> AND <EXPR> ... ; <EXPR>::=<EXPR2> ; <EXPR2><rel.op.><EXPR2> ; where <rel.op.> is one of the operators in TAB8 and the result ; of these operations is 1 if true and 0 if false. ; <EXPR2>::=(+ or -)<EXPR3>(+ or -)<EXPR3>(... ; where () are optional and (... are optional repeats. ; <EXPR3>::=<EXPR4>( <* or /><EXPR4> )(... ; <EXPR4>::=<variable> ; <function> ; (<EXPR>) ; <EXPR> is recursive so that the variable '@' can have an <EXPR> ; as an index, functions can have an <EXPR> as arguments, and ; <EXPR4> can be an <EXPR> in parenthesis. ; ; <OREXPR>::=<ANDEXPR> OR <ANDEXPR> ... ; OREXPR: subui sp,sp,#16 sw lr,[sp] call ANDEXPR ; get first <ANDEXPR> XP_OR1: sw r1,4[sp] ; save <ANDEXPR> value lea r9,TAB10 ; look up a logical operator lea r10,TAB10_1 jmp EXEC ; go do it XP_OR: call ANDEXPR lw r2,8[sp] or r1,r1,r2 bra XP_OR1 XP_ORX: lw r1,8[sp] lw lr,[sp] ret #16 ; <ANDEXPR>::=<EXPR> AND <EXPR> ... ; ANDEXPR: subui sp,sp,#16 sw lr,[sp] call EXPR ; get first <EXPR> XP_AND1: sw r1,8[sp] ; save <EXPR> value lea r9,TAB9 ; look up a logical operator lea r10,TAB9_1 jmp EXEC ; go do it XP_AND: call EXPR lw r2,8[sp] and r1,r1,r2 bra XP_AND1 XP_ANDX: lw r1,8[sp] lw lr,[sp] ret #16 ; Determine if the character is a digit ; Parameters ; r1 = char to test ; Returns ; r1 = 1 if digit, otherwise 0 ; isDigit: blt r1,#'0',isDigitFalse bgt r1,#'9',isDigitFalse setlo r1,#1 ret isDigitFalse: setlo r1,#0 ret ; Determine if the character is a alphabetic ; Parameters ; r1 = char to test ; Returns ; r1 = 1 if alpha, otherwise 0 ; isAlpha: blt r1,#'A',isAlphaFalse ble r1,#'Z',isAlphaTrue blt r1,#'a',isAlphaFalse bgt r1,#'z',isAlphaFalse isAlphaTrue: setlo r1,#1 ret isAlphaFalse: setlo r1,#0 ret ; Determine if the character is a alphanumeric ; Parameters ; r1 = char to test ; Returns ; r1 = 1 if alpha, otherwise 0 ; isAlnum: subui sp,sp,#8 sw lr,[sp] or r2,r1,r0 ; save test char call isDigit bne r1,r0,isDigitx ; if it is a digit or r1,r2,r0 ; get back test char call isAlpha isDigitx: lw lr,[sp] ret #8 EXPR: subui sp,sp,#16 sw lr,[sp] call EXPR2 sw r1,8[sp] ; save <EXPR2> value lea r9,TAB8 ; look up a relational operator lea r10,TAB8_1 jmp EXEC ; go do it XP11: lw r1,8[sp] call XP18 ; is it ">="? bge r2,r1,XPRT1 ; no, return r2=1 bra XPRT0 ; else return r2=0 XP12: lw r1,8[sp] call XP18 ; is it "<>"? bne r2,r1,XPRT1 ; no, return r2=1 bra XPRT0 ; else return r2=0 XP13: lw r1,8[sp] call XP18 ; is it ">"? bgt r2,r1,XPRT1 ; no, return r2=1 bra XPRT0 ; else return r2=0 XP14: lw r1,8[sp] call XP18 ; is it "<="? ble r2,r1,XPRT1 ; no, return r2=1 bra XPRT0 ; else return r2=0 XP15: lw r1,8[sp] call XP18 ; is it "="? beq r2,r1,XPRT1 ; if not, return r2=1 bra XPRT0 ; else return r2=0 XP16: lw r1,8[sp] call XP18 ; is it "<"? blt r2,r1,XPRT1 ; if not, return r2=1 bra XPRT0 ; else return r2=0 XPRT0: lw lr,[sp] setlo r1,#0 ; return r1=0 (false) ret #16 XPRT1: lw lr,[sp] setlo r1,#1 ; return r1=1 (true) ret #16 XP17: ; it's not a rel. operator lw r1,8[sp] ; return r2=<EXPR2> lw lr,[sp] ret #16 XP18: subui sp,sp,#16 sw lr,[sp] sw r1,8[sp] call EXPR2 ; do a second <EXPR2> lw r2,8[sp] lw lr,[sp] ret #16 ; <EXPR2>::=(+ or -)<EXPR3>(+ or -)<EXPR3>(... EXPR2: subui sp,sp,#16 sw lr,[sp] setlo r3,#'-' lea r4,XP21 call TSTC ; negative sign? setlo r1,#0 ; yes, fake '0-' sw r0,8[sp] bra XP26 XP21: setlo r3,#'+' lea r4,XP22 call TSTC ; positive sign? ignore it XP22: call EXPR3 ; first <EXPR3> XP23: sw r1,8[sp] ; yes, save the value setlo r3,#'+' lea r4,XP25 call TSTC ; add? call EXPR3 ; get the second <EXPR3> XP24: lw r2,8[sp] add r1,r1,r2 ; add it to the first <EXPR3> ; BVS.L QHOW brnch if there's an overflow bra XP23 ; else go back for more operations XP25: setlo r3,#'-' lea r4,XP45 call TSTC ; subtract? XP26: call EXPR3 ; get second <EXPR3> neg r1,r1 ; change its sign bra XP24 ; and do an addition XP45: lw r1,8[sp] lw lr,[sp] ret #16 ; <EXPR3>::=<EXPR4>( <* or /><EXPR4> )(... EXPR3: subui sp,sp,#16 sw lr,[sp] call EXPR4 ; get first <EXPR4> XP31: sw r1,8[sp] ; yes, save that first result setlo r3,#'*' lea r4,XP34 call TSTC ; multiply? call EXPR4 ; get second <EXPR4> lw r2,8[sp] muls r1,r1,r2 ; multiply the two bra XP31 ; then look for more terms XP34: setlo r3,#'/' lea r4,XP47 call TSTC ; divide? call EXPR4 ; get second <EXPR4> or r2,r1,r0 lw r1,8[sp] divs r1,r1,r2 ; do the division bra XP31 ; go back for any more terms XP47: lw r1,8[sp] lw lr,[sp] ret #16 ; Functions are called through EXPR4 ; <EXPR4>::=<variable> ; <function> ; (<EXPR>) EXPR4: subui sp,sp,#24 sw lr,[sp] lea r9,TAB4 ; find possible function lea r10,TAB4_1 jmp EXEC ; branch to function which does subsequent ret for EXPR4 XP40: ; we get here if it wasn't a function setlo r1,#0 call TSTV beq r1,r0,XP41 ; nor a variable lw r1,[r1] ; if a variable, return its value in r1 lw lr,[sp] ret #24 XP41: call TSTNUM ; or is it a number? bne r2,r0,XP46 ; (if not, # of digits will be zero) if so, return it in r1 call PARN ; check for (EXPR) XP46: lw lr,[sp] ret #24 ; Check for a parenthesized expression PARN: subui sp,sp,#8 sw lr,[sp] setlo r3,#'(' lea r4,XP43 call TSTC ; else look for ( OREXPR ) call OREXPR setlo r3,#')' lea r4,XP43 call TSTC XP42: lw lr,[sp] ret #8 XP43: lea r1,msgWhat bra ERROR ; ===== Test for a valid variable name. Returns Z=1 if not ; found, else returns Z=0 and the address of the ; variable in r1. ; Parameters ; r1 = 1 = allocate if not found ; Returns ; r1 = address of variable, zero if not found TSTV: subui sp,sp,#24 sw lr,[sp] sw r5,8[sp] or r5,r1,r0 ; allocate flag call IGNBLK lbu r1,[r8] ; look at the program text blt r1,#'@',tstv_notfound ; C=1: not a variable bne r1,#'@',TV1 ; brnch if not "@" array addui r8,r8,#1 ; If it is, it should be call PARN ; followed by (EXPR) as its index. shli r1,r1,#3 ; BCS.L QHOW say "How?" if index is too big subui sp,sp,#24 sw r1,8[sp] ; save the index sw lr,[sp] call SIZEX ; get amount of free memory lw lr,[sp] lw r2,8[sp] ; get back the index bltu r2,r1,TV2 ; see if there's enough memory jmp QSORRY ; if not, say "Sorry" TV2: lea r1,VARBGN ; put address of array element... subu r1,r1,r2 ; into r1 (neg. offset is used) bra TSTVRT TV1: call getVarName ; get variable name beq r1,r0,TSTVRT ; if not, return r1=0 mov r2,r5 call findVar ; find or allocate TSTVRT: lw r5,8[sp] lw lr,[sp] ret #24 ; r1<>0 (found) tstv_notfound: lw r5,8[sp] lw lr,[sp] setlo r1,#0 ; r1=0 if not found ret #24 ; Returns ; r1 = 6 character variable name + type ; getVarName: subui sp,sp,#24 sw lr,[sp] sw r5,16[sp] lb r1,[r8] ; get first character sw r1,8[sp] ; save off current name call isAlpha beq r1,r0,gvn1 setlo r5,#5 ; loop six more times ; check for second/third character gvn4: addui r8,r8,#1 lb r1,[r8] ; do we have another char ? call isAlnum beq r1,r0,gvn2 ; nope lw r1,8[sp] ; get varname shli r1,r1,#8 lb r2,[r8] or r1,r1,r2 ; add in new char sw r1,8[sp] ; save off name again loop r5,gvn4 ; now ignore extra variable name characters gvn6: addui r8,r8,#1 lb r1,[r8] call isAlnum bne r1,r0,gvn6 ; keep looping as long as we have identifier chars ; check for a variable type gvn2: lb r1,[r8] beq r1,#'%',gvn3 beq r1,#'$',gvn3 setlo r1,#0 subui r8,r8,#1 ; insert variable type indicator and return gvn3: addui r8,r8,#1 lw r2,8[sp] shli r2,r2,#8 or r1,r1,r2 ; add in variable type lw lr,[sp] lw r5,16[sp] ret #24 ; return Z = 0, r1 = varname ; not a variable name gvn1: lw lr,[sp] lw r5,16[sp] setlo r1,#0 ; return Z = 1 if not a varname ret #24 ; Find variable ; r1 = varname ; r2 = allocate flag ; Returns ; r1 = variable address, Z =0 if found / allocated, Z=1 if not found findVar: subui sp,sp,#16 sw lr,[sp] sw r7,8[sp] lw r3,VARBGN fv4: lw r7,[r3] ; get varname / type beq r7,r0,fv3 ; no more vars ? beq r1,r7,fv1 ; match ? add r3,r3,#8 ; move to next var lw r7,STKBOT blt r3,r7,fv4 ; loop back to look at next var ; variable not found ; no more memory setlo r1,#<msgVarSpace sethi r1,#>msgVarSpace bra ERROR ; lw lr,[sp] ; lw r7,4[sp] ; add sp,sp,#8 ; lw r1,#0 ; ret ; variable not found ; allocate new ? fv3: beq r2,r0,fv2 sw r1,[r3] ; save varname / type ; found variable ; return address fv1: addui r1,r3,#8 lw lr,[sp] lw r7,8[sp] ret #16 ; Z = 0, r1 = address ; didn't find var and not allocating fv2: lw lr,[sp] lw r7,8[sp] addui sp,sp,#16 ; Z = 0, r1 = address setlo r1,#0 ; Z = 1, r1 = 0 ret ; ===== Multiplies the 32 bit values in r1 and r2, returning ; the 32 bit result in r1. ; ; ===== Divide the 32 bit value in r2 by the 32 bit value in r3. ; Returns the 32 bit quotient in r1, remainder in r2 ; ; r2 = a ; r3 = b ; r6 = remainder ; r7 = iteration count ; r8 = sign ; ; q = a / b ; a = r1 ; b = r2 ; q = r2 ; ===== The PEEK function returns the byte stored at the address ; contained in the following expression. ; PEEK: call PARN ; get the memory address lbu r1,[r1] ; get the addressed byte lw lr,[sp] ; and return it ret #24 ; ===== The PEEK function returns the byte stored at the address ; contained in the following expression. ; PEEKC: call PARN ; get the memory address andi r1,r1,#-2 ; align to char address lcu r1,[r1] ; get the addressed char lw lr,[sp] ; and return it ret #24 ; ===== The PEEK function returns the byte stored at the address ; contained in the following expression. ; PEEKH: call PARN ; get the memory address andi r1,r1,#-4 ; align to half-word address lhu r1,[r1] ; get the addressed char lw lr,[sp] ; and return it ret #24 ; ===== The PEEK function returns the byte stored at the address ; contained in the following expression. ; PEEKW: call PARN ; get the memory address andi r1,r1,#-8 ; align to word address lw r1,[r1] ; get the addressed word lw lr,[sp] ; and return it ret #24 ; user function call ; call the user function with argument in r1 USRX: call PARN ; get expression value sw r8,8[sp] ; save the text pointer lw r2,usrJmp ; get usr vector jal r31,[r2] ; jump to the subroutine lw r8,8[sp] ; restore the text pointer lw lr,[sp] ret #24 ; ===== The RND function returns a random number from 1 to ; the value of the following expression in D0. ; RND: call PARN ; get the upper limit beq r1,r0,rnd2 ; it must be positive and non-zero blt r1,r0,rnd1 lw r2,r1 gran ; generate a random number mfspr r1,rand ; get the number call modu4 ; RND(n)=MOD(number,n)+1 addui r1,r1,#1 lw lr,[sp] ret #24 rnd1: lea r1,msgRNDBad bra ERROR rnd2: gran mfspr r1,rand lw lr,[sp] ret #24 ; r = a mod b ; a = r1 ; b = r2 ; r = r6 modu4: subui sp,sp,#32 sw r3,[sp] sw r5,8[sp] sw r6,16[sp] sw r7,24[sp] lw r7,#63 ; n = 64 xor r5,r5,r5 ; w = 0 xor r6,r6,r6 ; r = 0 mod2: roli r1,r1,#1 ; a <<= 1 andi r3,r1,#1 shli r6,r6,#1 ; r <<= 1 or r6,r6,r3 andi r1,r1,#-2 bgtu r2,r6,mod1 ; b < r ? subu r6,r6,r2 ; r -= b mod1: loop r7,mod2 ; n-- mov r1,r6 lw r3,[sp] lw r5,8[sp] lw r6,16[sp] lw r7,24[sp] ret #32 ; ===== The ABS function returns an absolute value in r2. ; ABS: call PARN ; get the following expr.'s value abs r1,r1 lw lr,[sp] ret #24 ; ===== The SGN function returns the sign in r1. +1,0, or -1 ; SGN: call PARN ; get the following expr.'s value sgn r1,r1 lw lr,[sp] ret #24 ; ===== The SIZE function returns the size of free memory in r1. ; SIZEX: lw r1,VARBGN ; get the number of free bytes... lw r2,TXTUNF ; between 'TXTUNF' and 'VARBGN' subu r1,r1,r2 lw lr,[sp] ret #24 ; return the number in r2 ;****************************************************************** ; ; *** SETVAL *** FIN *** ENDCHK *** ERROR (& friends) *** ; ; 'SETVAL' expects a variable, followed by an equal sign and then ; an expression. It evaluates the expression and sets the variable ; to that value. ; ; 'FIN' checks the end of a command. If it ended with ":", ; execution continues. If it ended with a CR, it finds the ; the next line and continues from there. ; ; 'ENDCHK' checks if a command is ended with a CR. This is ; required in certain commands, such as GOTO, RETURN, STOP, etc. ; ; 'ERROR' prints the string pointed to by r1. It then prints the ; line pointed to by CURRNT with a "?" inserted at where the ; old text pointer (should be on top of the stack) points to. ; Execution of Tiny BASIC is stopped and a warm start is done. ; If CURRNT is zero (indicating a direct command), the direct ; command is not printed. If CURRNT is -1 (indicating ; 'INPUT' command in progress), the input line is not printed ; and execution is not terminated but continues at 'INPERR'. ; ; Related to 'ERROR' are the following: ; 'QWHAT' saves text pointer on stack and gets "What?" message. ; 'AWHAT' just gets the "What?" message and jumps to 'ERROR'. ; 'QSORRY' and 'ASORRY' do the same kind of thing. ; 'QHOW' and 'AHOW' also do this for "How?". ; ; returns ; r2 = variable's address ; SETVAL: subui sp,sp,#16 sw lr,[sp] setlo r1,#1 ; allocate var call TSTV ; variable name? bne r1,r0,sv2 lea r1,msgVar bra ERROR sv2: sw r1,8[sp] ; save the variable's address setlo r3,#'=' lea r4,SV1 call TSTC ; get past the "=" sign call OREXPR ; evaluate the expression lw r2,8[sp] ; get back the variable's address sw r1,[r2] ; and save value in the variable lw r1,r2 ; return r1 = variable address lw lr,[sp] ret #16 SV1: bra QWHAT ; if no "=" sign FIN: subui sp,sp,#8 sw lr,[sp] setlo r3,#':' lea r4,FI1 call TSTC ; *** FIN *** addui sp,sp,#8 ; if ":", discard return address bra RUNSML ; continue on the same line FI1: setlo r3,#CR lea r4,FI2 call TSTC ; not ":", is it a CR? lw lr,[sp] ; else return to the caller addui sp,sp,#8 ; yes, purge return address bra RUNNXL ; execute the next line FI2: lw lr,[sp] ; else return to the caller ret #8 ; Check that there is nothing else on the line ; Registers Affected ; r1 ; ENDCHK: subui sp,sp,#8 sw lr,[sp] call IGNBLK lb r1,[r8] beq r1,#CR,ec1 ; does it end with a CR? setlo r1,#<msgExtraChars sethi r1,#>msgExtraChars jmp ERROR ec1: lw lr,[sp] ret #8 TOOBIG: lea r1,msgTooBig bra ERROR QSORRY: lea r1,SRYMSG bra ERROR QWHAT: lea r1,msgWhat ERROR: call PRMESG ; display the error message lw r1,CURRNT ; get the current line number beq r1,r0,WSTART ; if zero, do a warm start beq r1,#-1,INPERR ; is the line no. pointer = -1? if so, redo input lb r5,[r8] ; save the char. pointed to sb r0,[r8] ; put a zero where the error is lw r1,CURRNT ; point to start of current line call PRTLN ; display the line in error up to the 0 or r6,r1,r0 ; save off end pointer sb r5,[r8] ; restore the character setlo r1,#'?' ; display a "?" call GOOUT setlo r2,#0 ; stop char = 0 subui r1,r6,#1 ; point back to the error char. call PRTSTG ; display the rest of the line jmp WSTART ; and do a warm start ;****************************************************************** ; ; *** GETLN *** FNDLN (& friends) *** ; ; 'GETLN' reads in input line into 'BUFFER'. It first prompts with ; the character in r3 (given by the caller), then it fills the ; buffer and echos. It ignores LF's but still echos ; them back. Control-H is used to delete the last character ; entered (if there is one), and control-X is used to delete the ; whole line and start over again. CR signals the end of a line, ; and causes 'GETLN' to return. ; ; GETLN: subui sp,sp,#16 sw lr,[sp] sw r5,8[sp] call GOOUT ; display the prompt setlo r1,#1 ; turn on cursor flash sb r1,cursFlash setlo r1,#' ' ; and a space call GOOUT setlo r8,#<BUFFER ; r8 is the buffer pointer sethi r8,#>BUFFER GL1: call CHKIO ; check keyboard beq r1,r0,GL1 ; wait for a char. to come in beq r1,#CTRLH,GL3 ; delete last character? if so beq r1,#CTRLX,GL4 ; delete the whole line? beq r1,#CR,GL2 ; accept a CR bltu r1,#' ',GL1 ; if other control char., discard it GL2: sb r1,[r8] ; save the char. add r8,r8,#1 call GOOUT ; echo the char back out lb r1,-1[r8] ; get char back (GOOUT destroys r1) beq r1,#CR,GL7 ; if it's a CR, end the line cmpui r1,r8,#BUFFER+BUFLEN-1 ; any more room? blt r1,r0,GL1 ; yes: get some more, else delete last char. GL3: setlo r1,#CTRLH ; delete a char. if possible call GOOUT setlo r1,#' ' call GOOUT cmpui r1,r8,#BUFFER ; any char.'s left? ble r1,r0,GL1 ; if not setlo r1,#CTRLH ; if so, finish the BS-space-BS sequence call GOOUT sub r8,r8,#1 ; decrement the text pointer bra GL1 ; back for more GL4: or r1,r8,r0 ; delete the whole line subui r5,r1,#BUFFER ; figure out how many backspaces we need beq r5,r0,GL6 ; if none needed, brnch GL5: setlo r1,#CTRLH ; and display BS-space-BS sequences call GOOUT setlo r1,#' ' call GOOUT setlo r1,#CTRLH call GOOUT loop r5,GL5 GL6: lea r8,BUFFER ; reinitialize the text pointer bra GL1 ; and go back for more GL7: setlo r1,#0 ; turn off cursor flash sb r1,cursFlash setlo r1,#LF ; echo a LF for the CR call GOOUT lw lr,[sp] lw r5,8[sp] ret #16 ; 'FNDLN' finds a line with a given line no. (in r1) in the ; text save area. r9 is used as the text pointer. If the line ; is found, r9 will point to the beginning of that line ; (i.e. the high byte of the line no.), and flags are Z. ; If that line is not there and a line with a higher line no. ; is found, r9 points there and flags are NC & NZ. If we reached ; the end of the text save area and cannot find the line, flags ; are C & NZ. ; Z=1 if line found ; N=1 if end of text save area ; Z=0 & N=0 if higher line found ; r0 = 1 <= line is found ; r9 = pointer to line ; r0 = 0 <= line is not found ; r9 = zero, if end of text area ; r9 = otherwise higher line number ; ; 'FNDLN' will initialize r9 to the beginning of the text save ; area to start the search. Some other entries of this routine ; will not initialize r9 and do the search. ; 'FNDLNP' will start with r9 and search for the line no. ; 'FNDNXT' will bump r9 by 2, find a CR and then start search. ; 'FNDSKP' uses r9 to find a CR, and then starts the search. ; return Z=1 if line is found, r9 = pointer to line ; ; Parameters ; r1 = line number to find ; FNDLN: bleui r1,#0xFFFF,fl1 ; line no. must be < 65535 lea r1,msgLineRange bra ERROR fl1: lw r9,TXTBGN ; init. the text save pointer FNDLNP: lw r10,TXTUNF ; check if we passed the end subui r10,r10,#1 bgtu r9,r10,FNDRET1 ; if so, return with r9=0,r1=0 lbu r3,[r9] ; get low order byte of line number lbu r2,1[r9] ; get high order byte shli r2,r2,#8 or r2,r2,r3 ; build whole line number bgtu r1,r2,FNDNXT ; is this the line we want? no, not there yet beq r1,r2,FNDRET2 FNDRET: xor r1,r1,r1 ; line not found, but r9=next line pointer ret ; return the cond. codes FNDRET1: xor r9,r9,r9 ; no higher line xor r1,r1,r1 ; line not found ret FNDRET2: setlo r1,#1 ; line found ret FNDNXT: addui r9,r9,#2 ; find the next line FNDSKP: lbu r2,[r9] addui r9,r9,#1 bnei r2,#CR,FNDSKP ; try to find a CR, keep looking bra FNDLNP ; check if end of text ;****************************************************************** ; 'MVUP' moves a block up from where r1 points to where r2 points ; until r1=r3 ; MVUP1: lb r4,[r1] sb r4,[r2] add r1,r1,#1 add r2,r2,#1 MVUP: bne r1,r3,MVUP1 MVRET: ret ; 'MVDOWN' moves a block down from where r1 points to where r2 ; points until r1=r3 ; MVDOWN1: sub r1,r1,#1 sub r2,r2,#1 lb r4,[r1] sb r4,[r2] MVDOWN: bne r1,r3,MVDOWN1 ret ; 'POPA' restores the 'FOR' loop variable save area from the stack ; ; 'PUSHA' stacks for 'FOR' loop variable save area onto the stack ; ; Note: a single zero word is stored on the stack in the ; case that no FOR loops need to be saved. This needs to be ; done because PUSHA / POPA is called all the time. POPA: lw r1,[sp] ; restore LOPVAR, but zero means no more sw r1,LOPVAR beq r1,r0,PP1 lw r1,32[sp] ; if not zero, restore the rest sw r1,LOPPT lw r1,24[sp] sw r1,LOPLN lw r1,16[sp] sw r1,LOPLMT lw r1,8[sp] sw r1,LOPINC ret #40 PP1: ret #8 PUSHA: lw r1,STKBOT ; Are we running out of stack room? addui r1,r1,#40 ; we might need this many bytes bltu sp,r1,QSORRY ; out of stack space lw r1,LOPVAR ; save loop variables beq r1,r0,PU1 ; if LOPVAR is zero, that's all subui sp,sp,#40 sw r1,[sp] lw r1,LOPPT sw r1,32[sp] ; else save all the others lw r1,LOPLN sw r1,24[sp] lw r1,LOPLMT sw r1,16[sp] lw r1,LOPINC sw r1,8[sp] ret PU1: subui sp,sp,#8 sw r1,[sp] ret ;****************************************************************** ; ; *** PRTSTG *** QTSTG *** PRTNUM *** PRTLN *** ; ; 'PRTSTG' prints a string pointed to by r3. It stops printing ; and returns to the caller when either a CR is printed or when ; the next byte is the same as what was passed in r4 by the ; caller. ; ; 'QTSTG' looks for an underline (back-arrow on some systems), ; single-quote, or double-quote. If none of these are found, returns ; to the caller. If underline, outputs a CR without a LF. If single ; or double quote, prints the quoted string and demands a matching ; end quote. After the printing, the next i-word of the caller is ; skipped over (usually a branch instruction). ; ; 'PRTNUM' prints the 32 bit number in r3, leading blanks are added if ; needed to pad the number of spaces to the number in r4. ; However, if the number of digits is larger than the no. in ; r4, all digits are printed anyway. Negative sign is also ; printed and counted in, positive sign is not. ; ; 'PRTLN' prints the saved text line pointed to by r3 ; with line no. and all. ; ; r1 = pointer to string ; r2 = stop character ; return r1 = pointer to end of line + 1 PRTSTG: subui sp,sp,#32 sw r5,[sp] sw r5,8[sp] sw r7,16[sp] sw lr,24[sp] mov r5,r1 ; r5 = pointer mov r6,r2 ; r6 = stop char PS1: lbu r7,[r5] ; get a text character addui r5,r5,#1 beq r7,r6,PRTRET ; same as stop character? if so, return mov r1,r7 call GOOUT ; display the char. bnei r7,#CR,PS1 ; is it a C.R.? no, go back for more setlo r1,#LF ; yes, add a L.F. call GOOUT PRTRET: mov r2,r7 ; return r2 = stop char mov r1,r5 ; return r1 = line pointer lw lr,24[sp] lw r7,16[sp] lw r5,8[sp] lw r5,[sp] ret #32 ; then return QTSTG: subui sp,sp,#8 sw lr,[sp] setlo r3,#'"' lea r4,QT3 call TSTC ; *** QTSTG *** setlo r2,#'"' ; it is a " QT1: or r1,r8,r0 call PRTSTG ; print until another lw r8,r1 bne r2,#LF,QT2 ; was last one a CR? addui sp,sp,#8 bra RUNNXL ; if so, run next line QT3: setlo r3,#'''' lea r4,QT4 call TSTC ; is it a single quote? setlo r2,#'''' ; if so, do same as above bra QT1 QT4: setlo r3,#'_' lea r4,QT5 call TSTC ; is it an underline? setlo r1,#CR ; if so, output a CR without LF call GOOUT QT2: lw lr,[sp] addui sp,sp,#8 jal r0,4[lr] ; skip over next i-word when returning QT5: ; not " ' or _ lw lr,[sp] ret #8 ; Output a CR LF sequence ; prCRLF: subui sp,sp,#8 sw lr,[sp] setlo r1,#CR call GOOUT setlo r1,#LF call GOOUT lw lr,[sp] ret #8 ; r1 = number to print ; r2 = number of digits ; Register Usage ; r5 = number of padding spaces PRTNUM: subui sp,sp,#40 sw r3,[sp] sw r5,8[sp] sw r6,16[sp] sw r7,24[sp] sw lr,32[sp] lea r7,NUMWKA ; r7 = pointer to numeric work area mov r6,r1 ; save number for later mov r5,r2 ; r5 = min number of chars bgt r1,r0,PN2 ; is it negative? if not neg r1,r1 ; else make it positive subui r5,r5,#1 ; one less for width count PN2: lw r3,#10 PN1: modu r2,r1,r3 ; r2 = r1 mod 10 divui r1,r1,#10 ; r1 /= 10 divide by 10 addui r2,r2,#'0' ; convert remainder to ascii sb r2,[r7] ; and store in buffer addui r7,r7,#1 subui r5,r5,#1 ; decrement width bne r1,r0,PN1 PN6: ble r5,r0,PN4 ; test pad count, skip padding if not needed PN3: setlo r1,#' ' ; display the required leading spaces call GOOUT loop r5,PN3 PN4: bge r6,r0,PN5 ; is number negative? setlo r1,#'-' ; if so, display the sign call GOOUT PN5: subui r7,r7,#1 lb r1,[r7] ; now unstack the digits and display call GOOUT cmpui r1,r7,#NUMWKA bgtu r1,r0,PN5 PNRET: lw lr,32[sp] lw r7,24[sp] lw r6,16[sp] lw r5,8[sp] lw r3,[sp] ret #40 ; r1 = number to print ; r2 = number of digits PRTHEXNUM: subui sp,sp,#40 sw r5,[sp] sw r6,8[sp] sw r7,16[sp] sw r8,24[sp] sw lr,32[sp] lea r7,NUMWKA ; r7 = pointer to numeric work area or r6,r1,r0 ; save number for later setlo r5,#20 ; r5 = min number of chars mov r4,r1 bgt r4,r0,PHN1 ; is it negative? if not neg r4,r4 ; else make it positive subui r5,r5,#1 ; one less for width count setlo r8,#20 ; maximum of 10 digits PHN1: mov r1,r4 andi r1,r1,#15 blt r1,#10,PHN7 addui r1,r1,#'A'-10 bra PHN8 PHN7: add r1,r1,#'0' ; convert remainder to ascii PHN8: sb r1,[r7] ; and store in buffer addui r7,r7,#1 subui r5,r5,#1 ; decrement width shrui r4,r4,#4 beq r4,r0,PHN6 ; is it zero yet ? loop r8,PHN1 ; safety PHN6: ; test pad count ble r5,r0,PHN4 ; skip padding if not needed PHN3: setlo r1,#' ' ; display the required leading spaces call GOOUT loop r5,PHN3 PHN4: bgt r6,r0,PHN5 ; is number negative? setlo r1,#'-' ; if so, display the sign call GOOUT PHN5: subui r7,r7,#1 lb r1,[r7] ; now unstack the digits and display call GOOUT cmpui r1,r7,#NUMWKA bgt r1,r0,PHN5 PHNRET: lw lr,32[sp] lw r8,24[sp] lw r7,16[sp] lw r6,8[sp] lw r5,[sp] ret #40 ; r1 = pointer to line ; returns r1 = pointer to end of line + 1 PRTLN: subui sp,sp,#16 sw r5,[sp] sw lr,8[sp] addi r5,r1,#2 lbu r1,-2[r5] ; get the binary line number lbu r2,-1[r5] shli r2,r2,#8 or r1,r1,r2 setlo r2,#0 ; display a 0 or more digit line no. call PRTNUM setlo r1,#' ' ; followed by a blank call GOOUT setlo r2,#0 ; stop char. is a zero or r1,r5,r0 call PRTSTG ; display the rest of the line lw lr,8[sp] lw r5,[sp] ret #16 ; ===== Test text byte following the call to this subroutine. If it ; equals the byte pointed to by r8, return to the code following ; the call. If they are not equal, brnch to the point ; indicated in r4. ; ; Registers Affected ; r3,r8 ; Returns ; r8 = updated text pointer ; TSTC subui sp,sp,#16 sw lr,[sp] sw r1,8[sp] call IGNBLK ; ignore leading blanks lb r1,[r8] beq r3,r1,TC1 ; is it = to what r8 points to? if so lw r1,8[sp] lw lr,[sp] addui sp,sp,#16 jal r0,[r4] ; jump to the routine TC1: addui r8,r8,#1 ; if equal, bump text pointer lw r1,8[sp] lw lr,[sp] ret #16 ; ===== See if the text pointed to by r8 is a number. If so, ; return the number in r2 and the number of digits in r3, ; else return zero in r2 and r3. ; Registers Affected ; r1,r2,r3,r4 ; Returns ; r1 = number ; r2 = number of digits in number ; r8 = updated text pointer ; TSTNUM: subui sp,sp,#8 sw lr,[sp] call IGNBLK ; skip over blanks setlo r1,#0 ; initialize return parameters setlo r2,#0 TN1: lb r3,[r8] bltui r3,#'0',TSNMRET ; is it less than zero? bgtui r3,#'9',TSNMRET ; is it greater than nine? lw r4,#0x07FFFFFF_FFFFFFFF bleu r1,r4,TN2 ; see if there's room for new digit lea r1,msgNumTooBig bra ERROR ; if not, we've overflowd TN2: mului r1,r1,#10 ; quickly multiply result by 10 addui r8,r8,#1 ; adjust text pointer andi r3,r3,#0x0F ; add in the new digit addu r1,r1,r3 addui r2,r2,#1 ; increment the no. of digits bra TN1 TSNMRET: lw lr,[sp] ret #8 ;===== Skip over blanks in the text pointed to by r8. ; ; Registers Affected: ; r8 ; Returns ; r8 = pointer updateded past any spaces or tabs ; IGNBLK: subui sp,sp,#8 sw r1,[sp] IGB2: lb r1,[r8] ; get char beqi r1,#' ',IGB1 ; see if it's a space bnei r1,#'\t',IGBRET ; or a tab IGB1: addui r8,r8,#1 ; increment the text pointer bra IGB2 IGBRET: lw r1,[sp] ret #8 ; ===== Convert the line of text in the input buffer to upper ; case (except for stuff between quotes). ; ; Registers Affected ; r1,r3 ; Returns ; r8 = pointing to end of text in buffer ; TOUPBUF: subui sp,sp,#8 sw lr,[sp] lea r8,BUFFER ; set up text pointer setlo r3,#0 ; clear quote flag TOUPB1: lb r1,[r8] ; get the next text char. addui r8,r8,#1 beqi r1,#CR,TOUPBRT ; is it end of line? beqi r1,#'"',DOQUO ; a double quote? beqi r1,#'''',DOQUO ; or a single quote? bne r3,r0,TOUPB1 ; inside quotes? call toUpper ; convert to upper case sb r1,-1[r8] ; store it bra TOUPB1 ; and go back for more DOQUO: bne r3,r0,DOQUO1; are we inside quotes? mov r3,r1 ; if not, toggle inside-quotes flag bra TOUPB1 DOQUO1: bne r3,r1,TOUPB1 ; make sure we're ending proper quote setlo r3,#0 ; else clear quote flag bra TOUPB1 TOUPBRT: lw lr,[sp] ret #8 ; ===== Convert the character in r1 to upper case ; toUpper blti r1,#'a',TOUPRET ; is it < 'a'? bgti r1,#'z',TOUPRET ; or > 'z'? subui r1,r1,#32 ; if not, make it upper case TOUPRET ret ; 'CHKIO' checks the input. If there's no input, it will return ; to the caller with the r1=0. If there is input, the input byte is in r1. ; However, if a control-C is read, 'CHKIO' will warm-start BASIC and will ; not return to the caller. ; CHKIO: subui sp,sp,#8 ; save link reg sw lr,[sp] call GOIN ; get input if possible beqi r1,#-1,CHKRET2 ; if Zero, no input bnei r1,#CTRLC,CHKRET ; is it control-C? jmp WSTART ; if so, do a warm start CHKRET2: xor r1,r1,r1 CHKRET: lw lr,[sp] ;r1=0 ret #8 ; ===== Display a CR-LF sequence ; CRLF: setlo r1,CLMSG ; ===== Display a zero-ended string pointed to by register r1 ; Registers Affected ; r1,r2,r4 ; PRMESG: subui sp,sp,#16 sw r5,[sp] sw lr,8[sp] mov r5,r1 ; r5 = pointer to message PRMESG1: addui r5,r5,#1 lbu r1,-1[r5] ; get the char. beq r1,r0,PRMRET call GOOUT ;else display it trashes r4 bra PRMESG1 PRMRET: mov r1,r5 lw lr,8[sp] lw r5,[sp] ret #16 ; ===== Display a zero-ended string pointed to by register r1 ; Registers Affected ; r1,r2,r3 ; PRMESGAUX: subui sp,sp,#16 sw r5,[sp] sw lr,8[sp] mov r5,r1 ; r3 = pointer PRMESGA1: addui r5,r5,#1 lb r1,-1[r5] ; get the char. beq r1,r0,PRMRETA call GOAUXO ;else display it bra PRMESGA1 PRMRETA: mov r1,r5 lw lr,8[sp] lw r5,[sp] ret #16 ;***************************************************** ; The following routines are the only ones that need * ; to be changed for a different I/O environment. * ;***************************************************** ; ===== Output character to the console (Port 1) from register r1 ; (Preserves all registers.) ; OUTC: jmp DisplayChar ; ===== Input a character from the console into register R1 (or ; return Zero status if there's no character available). ; INC: jmp KeybdGetChar ;* ;* ===== Input a character from the host into register r1 (or ;* return Zero status if there's no character available). ;* AUXIN: call SerialGetChar beqi r1,#-1,AXIRET_ZERO andi r1,r1,#0x7f ;zero out the high bit AXIRET: ret AXIRET_ZERO: xor r1,r1,r1 ret ; ===== Output character to the host (Port 2) from register r1 ; (Preserves all registers.) ; AUXOUT jmp SerialPutChar ; call boot rom routine _cls call clearScreen bra FINISH _wait10 ret _getATAStatus ret _waitCFNotBusy ret _rdcf br FINISH rdcf6 br ERROR ; ===== Return to the resident monitor, operating system, etc. ; BYEBYE: lw sp,OSSP lw lr,[sp] ret #8 ; MOVE.B #228,D7 return to Tutor ; TRAP #14 msgInit db CR,LF,"Raptor64 Tiny BASIC v1.0",CR,LF,"(C) 2013 Robert Finch",CR,LF,LF,0 OKMSG db CR,LF,"OK",CR,LF,0 msgWhat db "What?",CR,LF,0 SRYMSG db "Sorry." CLMSG db CR,LF,0 msgReadError db "Compact FLASH read error",CR,LF,0 msgNumTooBig db "Number is too big",CR,LF,0 msgDivZero db "Division by zero",CR,LF,0 msgVarSpace db "Out of variable space",CR,LF,0 msgBytesFree db " bytes free",CR,LF,0 msgReady db CR,LF,"Ready",CR,LF,0 msgComma db "Expecting a comma",CR,LF,0 msgLineRange db "Line number too big",CR,LF,0 msgVar db "Expecting a variable",CR,LF,0 msgRNDBad db "RND bad parameter",CR,LF,0 msgSYSBad db "SYS bad address",CR,LF,0 msgInputVar db "INPUT expecting a variable",CR,LF,0 msgNextFor db "NEXT without FOR",CR,LF,0 msgNextVar db "NEXT expecting a defined variable",CR,LF,0 msgBadGotoGosub db "GOTO/GOSUB bad line number",CR,LF,0 msgRetWoGosub db "RETURN without GOSUB",CR,LF,0 msgTooBig db "Program is too big",CR,LF,0 msgExtraChars db "Extra characters on line ignored",CR,LF,0 align 8 LSTROM equ * ; end of possible ROM area ; END ;* ;* ===== Return to the resident monitor, operating system, etc. ;* BYEBYE: jmp Monitor ; MOVE.B #228,D7 ;return to Tutor ; TRAP #14 ;============================================================================== ; Checkerboard RAM tester ;============================================================================== ; code align 16 ramtest: or r8,r0,r0 ; r8 = 0 ori r1,r0,#0xAAAA5555AAAA5555 ; checkerboard pattern ramtest2: sw r1,[r8] ; save the checkerboard to memory lw r2,[r8] ; read it back cmp r3,r1,r2 ; is it the same ? bne r3,r0,ramtest1 addui r8,r8,#8 ; increment RAM pointer cmpi r3,r8,#0x0000_0000_0400_0000 blt r3,r0,ramtest2 ramtest1: or r10,r8,r0 ; r10 = max ram address ; readback the checkerboard pattern or r8,r0,r0 ; r8 = 0 ramtest4: lw r2,[r8] cmpi r3,r2,#0xAAAA5555AAAA5555 bne r3,r0,ramtest3 addi r8,r8,#8 cmpi r3,r8,#0x0000_0000_0100_0000 blt r3,r0,ramtest4 ramtest3: bne r8,r10,ramtest8 ; check for equal maximum address ; perform ramtest again with inverted checkerboard or r8,r0,r0 ; r8 = 0 ori r1,r0,#0x5555AAAA5555AAAA ramtest5: sw r1,[r8] lw r2,[r8] cmp r3,r1,r2 bne r3,r0,ramtest6 addi r8,r8,#8 cmpi r3,r8,#0x0000_0000_0100_0000 blt r3,r0,ramtest5 ramtest6: or r11,r8,r0 ; r11 = max ram address ; readback checkerboard or r8,r0,r0 ramtest7: lw r2,[r8] cmpi r3,r2,#0x5555AAAA5555AAAA bne r3,r0,ramtest8 addi r8,r8,#8 cmpi r3,r8,#0x0000_0000_0100_0000 blt r3,r0,ramtest7 ramtest8: beq r8,r11,ramtest9 min r8,r8,r11 ramtest9: beq r8,r10,ramtest10 min r8,r8,r10 ramtest10: sw r8,0x00000400 ;memend ret ;------------------------------------------- ;------------------------------------------- ; iberr_rout: lea r1,msgiberr call DisplayString mfspr r1,EPC call DisplayWord wait jmp start dberr_rout: lw sp,#0x100200100 lea r1,msgdberr call DisplayString mfspr r1,ERRADR call DisplayWord lea r1,msgEPC call DisplayString mfspr r1,EPC call DisplayWord call CRLF lw r2,#31 dberr1: mtspr PCHI,r2 nop nop nop mfspr r1,PCHISTORIC call DisplayWord call CRLF loop r2,dberr1 wait jmp start .align 16 msgdberr: db "Data bus error at: ",0 msgEPC: db " EPC: ",0 msgiberr: db "Err fetching instruction at: ",0 .align 4 ;------------------------------------------------------------------------------ ; IRQ routine ; ; Interrupts are automatically disabled at the time of the interrupt in order ; to prevent nested interrupts from occuring. Interrupts are re-enabled by ; the IRET instruction at the end of the interrupt routine. If the interrupt ; turns out to not match a hardware interrupt, then a software context ; switching interrupt is assumed. ; ; This routine uses it's own private interrupt stack; the stack of the ; interrupted context is not used at all. A couple of working registers are ; saved off not on the stack. We can get away with this because nested ; interrupts are not allowed. ;------------------------------------------------------------------------------ ; irqrout: sw sp,sp_save ; use our own private stack for interrupt processing sw lr,lr_save ; so, save off the sp and working registers sw r26,r26_save sw r1,r1_save sw r2,r2_save lw sp,#0x1_00001000 ; the second two kbytes inch r1,PIC ; r1= which IRQ line is active ; Dispatch fork, in order of required timeliness beqi r1,#2,irq1000Hz beqi r1,#3,irq100Hz beqi r1,#8,irqSerial beqi r1,#13,irqRaster beqi r1,#15,irqKeybd beqi r1,#1,irqColdStart ; CTRL-ALT-DEL interrupt ; Here, none of the hardware interrupts were active so ; assume software context switch interrupt ; lw sp,sp_save lw lr,lr_save lw r26,r26_save lw r1,r1_save lw r2,r2_save iepp iret ; 1000 Hz interrupt ; This IRQ must be fast, so it's placed inline. It's also the first ; IRQ checked for in the interrupt dispatch. ; Increments the millisecond counter, and switches to the next context ; irq1000Hz: outb r0,0xDCFFFD ; acknowledge interrupt lw r1,Milliseconds ; increment milliseconds count addui r1,r1,#1 sw r1,Milliseconds lw sp,sp_save lw lr,lr_save lw r26,r26_save lw r1,r1_save lw r2,r2_save iepp ; move to the next context iret ; return to the next context ; 100 Hz interrupt ; This IRQ could have some work to do, including flashing a cursor. So ; we call a subroutine. ; irq100Hz: lw r1,p100IRQvec ; jal lr,[r1] call Pulse100 irqret: lw sp,sp_save lw lr,lr_save lw r26,r26_save lw r1,r1_save lw r2,r2_save iret irqSerial: lw r1,serialIRQvec jal lr,[r1] bra irqret irqRaster: lw r1,rasterIRQvec ; jal lr,[r1] call RasterIRQfn bra irqret irqKeybd: lw r1,keybdIRQvec call KeybdIRQ ; jal lr,[r1] bra irqret irqColdStart: jmp ColdStart ;------------------------------------------------------------------------------ ; NMI routine ; ; The NMI line is tied to the parity error signal. But also any non-initialized ; interrupts get sent here. ;------------------------------------------------------------------------------ ; nmirout: sw sp,sp_save sw r1,r1_save sw r26,r26_save lw sp,#0x100001000 outb r0,0xDCFFFE ; acknowledge interrupt lea r1,msgPerr call DisplayString mfspr r1,IPC call DisplayWord call CRLF lw sp,sp_save lw r1,r1_save lw r26,r26_save iret msgPerr: db "Parity error at: ",0 ;------------------------------------------- ; Unimplemented instructions end up here ;------------------------------------------- .align 4 ui_irout: subui sp,sp,#8 sw r1,[sp] lea r1,msgUnimp call DisplayString mfspr r1,IPC call DisplayWord call CRLF lw r1,[sp] addui sp,sp,#8 ; hang the context ui_irout1: bra ui_irout1 iret msgUnimp: db "Unimplemented instruction at: ",0 ;------------------------------------------- ; Handle miss on Data TLB ;------------------------------------------- .align 4 DTLBHandler: sw r1,0xFFFF_FFFF_FFFF_0000 sw r2,0xFFFF_FFFF_FFFF_0008 dh1: omgi r1,#0 ; try open mutex gate #0 (TLB protector) bne r1,r0,dh1 ; spinlock if gate is closed mfspr r1,PTA ; get the page table address mfspr r2,BadVAddr ; get the bad virtual address mtspr TLBVirtPage,r2 ; which virtual address to update shrui r2,r2,#13 ; turn va into index addu r1,r1,r2 lw r2,[r1] ; get the physical address from the table and r2,r2,#FFFF_FFFF_FFFF_E000 ; mask off lower bits mtspr TLBPhysPage0,r2 ; lw r2,8[r1] ; get the physical address from the table and r2,r2,#FFFF_FFFF_FFFF_E000 ; mask off lower bits mtspr TLBPhysPage1,r2 ; tlbwr ; update a random entry in the TLB cmgi #0 ; close the mutex gate lw r1,0xFFFF_FFFF_FFFF_0000 lw r2,0xFFFF_FFFF_FFFF_0008 iret .align 32 org 0xFFFF_FFFF_FFFF_FFB0 jmp DTLBHandler nop nop org 0xFFFF_FFFF_FFFF_FFC0 jmp DTLBHandler nop nop org 0xFFFF_FFFF_FFFF_FFE0 dw 0 ; dw 0 ; ; RST vector org 0xFFFF_FFFF_FFFF_FFF0 jmp start ; ROM checksum goes here org 0xFFFF_FFFF_FFFF_FFF8 dw 0