; PMON - PISC Monitor ; Monitor Program for the PISC 1.0c ; Stephen Arnold 22-03-2020 ; ;---------------------- EQUATES ----------------------------------------------- ; ; PISC v1.0c Memory Map - Note: All memory slots have an entire 16k block ; of address space. ; ; MEM Slot 0 = $0000 - $3FFF ; MEM Slot 1 = $4000 - $7FFF ; MEM Slot 2 = $8000 - $BFFF ; MEM Slot 3 = $C000 - $FFFF ; ; PISC Register Use - The PISC has 8 x Registers R0..R7 ; ; R0 = General Purpose - Typical use as input to (and possible output from) a function ; R1 = General Purpose - Typical use as output from function if we do not wish to destroy R0 ; R2 = General Purpose ; R3 = General Purpose ; ; R4 = PISC ASM Working - Used by PISC Assembeller as a tempory working varible. Use with care! ; R5 = Stack Pointer - Used by CALL, RETurn, POP and PUSH instructions ; R6 = Interrupt Vector - May be used if interrupts are disabled ; R7 = Program Counter - Post incremented after each hardware encoded fetch instruction ; ; NULL EQU $0000 ;Set NULL value FALSE EQU $0000 ;Set FALSE value TRUE EQU $FFFF ;Set TRUE value ; BUFFER1 EQU $FD00 ;Start of RAM used for keyboard buffer. 32 words 64 characters BUFFER2 EQU $FD20 ;Start of RAM used for keyboard buffer. 64 words 128 characters ; VAR1 EQU $FD60 ;Actual binary variable data. VAR2 EQU $FD61 VAR3 EQU $FD62 VAR4 EQU $FD63 ; VAR1TXT EQU $FD64 ;Text string for each variable extracted VAR2TXT EQU $FD65 ;from command line. VAR3TXT EQU $FD66 VAR4TXT EQU $FD67 ; VCOUNT EQU $FD68 ;Varible count VFLAG EQU $FD69 ;General purpose Flag variable ; DPTR EQU $FD70 ;Dump routine pointer. Used to track last memory dump location ; CKSUM EQU $FD71 ;16 bit CRC for Hex file transfer routine XDATA EQU $FD72 ;Hex Tranfer Data Count (Number of Words to recieve) XADDR EQU $FD73 ;Hex Tranfer Memory Address XTYPE EQU $FD74 ;Hex Tranfer Record Type ; VAR5 EQU $FD75 ;General purpose short term storage variable ; RINGB EQU $FE40 ;Start Address for SIO Ring Buffer. ;64 x Words. AND with $3F. Ends $FE9F RBWPTR EQU $FE80 ;Ring Buffer Write Pointer RBRPTR EQU $FE81 ;Ring Buffer Read Pointer RBOVFL EQU $FE82 ;Ring Buffer Overflow (High = $0F for Over Flow flag, Low = Char) ; ; 2651 Serial UART Program Bitmap Data ; SIOMR1 EQU %01001110 ;Mode Register 1 = 01 1-Stop, 0 Odd-Parity, 0 Parity-Off, 11 8-Data, 10 16xBaud-Clk SIOMR2 EQU %00111110 ;Mode Register 2 = 00 Not Used, 11 Internal-TX&RX-Clk,1110 9600 Baud SIOCR EQU %00100111 ;Command Register = 00 Normal Operation, 1 RTS-On, 0 Reset-Err, 0 Force-Brk, 1 RX-EN, 1 DTR-On, 1 TX-EN ; ; RAM Addresses for 2651 Serial UART Port locations ; SIODAT EQU $FE83 ;Data Port SIOSTA EQU $FE84 ;Status Port, SYN/DLE Registers - Read=Status, 3 x Write=SYN1/SYN2/DLE SIOMOD EQU $FE85 ;Mode Port 1&2 - 2 x Read/Write. SIOCMD EQU $FE86 ;Command Port ; MDAT0 EQU $FEA6 ;Memory Banking Slot Config Data 0 MDAT1 EQU $FEA7 ;Memory Banking Slot Config Data 1 MDAT2 EQU $FEA8 ;Memory Banking Slot Config Data 2 MDAT3 EQU $FEA9 ;Memory Banking Slot Config Data 3 ; BKADDR EQU $FEAA ;Breakpoint Address BKINST EQU $FEAB ;Original Breakpoint Instruction (x1) BKSAVE EQU $FEAC ;Breakpoint Registers R7 to R0 (8 words) BKFLAG EQU $FEB4 ;Breakpoint Flags (1 word) ; IRQVEC EQU $FEB5 ;Start of IRQ Routine Vector Table x 8 words ; ; ;---------------------- MONITOR CODE STARTS HERE ------------------------------ ; PMAIN ORG $0000 ;Start of ROM memory for PISC LOAD $8000 ;Build Hex output file for loading to $8000 JMP COLD ;Jump past jump vector and flags tables ;to COLD boot location. Address $0000 ; JMP WARM ;Jump past jump vector and flags tables ;to WARM boot location. Address $0003 ; JMP BREAK ;Known fixed address for entry into Breakpoint ;Routine. Address $0006 ; ;---------------------- BIOS CALL STARTS HERE --------------------------------- ; BIOS calls sould enter with R0 containing optional data ; and R1 containing the BIOS function number starting at 0 ; Address of BIOS fixed at $0009 and called with jump to ; this location. ; *** NOTE: BIOS Calls destroy R4! ; BIOS LD R4,JPTBL ;Load base address of Jump Table into R4 ADD R4,R1 ;Add function number to Jump Table Offset RD R4,R7 ;Read function Address into R7 ;Causes a JMP to the function address ;Functions RET will return to BIOS caller ; ;---------------------- FLAGS DATA TABLE -------------------------------------- ; ; Flags Data Table ; Used for restoring Flags EQ & CY after IRQ routine ; ;Fixme: Bug! DW $0000,$0000 is not valid. Location counter only set +1 ; FLAGTBL DW $0000 DW $0000 ; EQ = 0, CY = 0 ; DW $FFFF DW $0000 ; EQ = 0, CY = 1 ; DW $0000 DW $FFFF ; EQ = 1, CY = 0 ; DW $FFFF DW $FFFF ; EQ = 1, CY = 1 ; ;---------------------- COLD BOOT STARTS HERE --------------------------------- ; ; The following Bank Memory setup always boots from Slot 0 ORG $0000 ; to the Monitor BIOS. Slot 0 is normally mapped to ROM Bank #1 (2nd ROM Bank) ; Unless the last key pressed on the keypad was an asterix '*' ; In which case it will boot from ROM Bank #0 (1st ROM Bank) ; This arrangement makes it possible to have a development BIOS in Bank #1 ; and a fail-safe BIOS in Bank #0 ; COLD IRQ 0 ; Disable IRQ (Should already be off due to reset) ; LD R2,$FF10 ;Clear 7-Seg Display to 0000 0000 ZERO R0 ;Set R0 to zero WRI R2,R0 ;Hex digit #1 0000 00## to zero. Value from R0-L WRI R2,R0 ;Hex digit #2 0000 ##00 to zero. Value from R0-H WRI R2,R0 ;Hex digit #3 00## 0000 to zero. Value from R0-L WRI R2,R0 ;Hex digit #4 ##00 0000 to zero. Value from R0-H WR R2 ;Clear 8 x LED's. Value from R0-L ; MEM $100 ; Turn Memory Banking Off - Probably not required after reboot but... MEM $01 ; Memory Slot 0 -> Bank 1 (ROM 2nd Bank) MOV R1,R4 ; Save Memory Slot Config 0 in R1. R4 set instr above. RD $FF10,R0 ; Read last key pressed on keypad CMP R0,$FFEC ; FFEC = '*' key JNE :CONT ; Skip next two instructions if '*' pressed MEM $00 ; Memory Slot 0 -> Bank 0 (ROM 1st Bank) MOV R1,R4 ; Save Memory Slot Config 0 in R1 (again, overwrite) :CONT MEM $19 ; Memory Slot 1 -> Bank 9 (RAM 2nd Bank) MEM $2A ; Memory Slot 2 -> Bank 10 (RAM 3rd Bank) MEM $3B ; Memory Slot 3 -> Bank 11 (RAM 4th Bank) MEM $300 ; Slots defined - Now turn Memory Banking On ; ;**** WARNING! Nothing in the BIOS from $0000 to here should be changed! ; As once we have turned on Bank Switching (above) the next instruction could ; flick from being executed in Bank 0 (Boot/Failsafe) to Bank 1. ; The Program Counter remains unchanged so code execution will ; continue in Bank 1 (non-failsafe) from the next instruction. ; WR MDAT0,R1 ;Save Slot 0 config JMP COLDNXT ;Jump past BIOS jump vector table and ISR routines ;and continue the COLD Boot below ; ;---------------------- BIOS CALLS JUMP VECTOR TABLE ------------------------------------- ; BIOS Calls Jump Vector Table ; JPTBL DW SIOTX ;Fn= 0 TX serial Char via SIO DW SIORX ;Fn= 1 RX serial Char via SIO DW NEWLN ;Fn= 2 Output a CR+LF New Line sequence DW PRINT ;Fn= 3 Print routine DW INPUT ;Fn= 4 Input routine DW DSPNUM ;Fn= 5 Display 16 bit unsigned number DW DSPSGN ;Fn= 6 Display 16 bit signed number DW DSPHEX ;Fn= 7 Display 16 bit value as 4 digit hex number DW EXPSTR ;Fn= 8 Expand 8 bit ASCII packed string to a string of words DW WRD2HEX ;Fn= 9 Convert word in R0 to ASCII R2 -> Hex string DW DEC2BN ;Fn=10 Convert ASCII decimal string to 16 bit binary DW INKEY ;Fn=11 Check and return char in Read Buffer, Null if none DW $0000 ; ;---------------------- IRQ SERVICE ROUTINES STARTS HERE --------------------------------- ; ISR ;Switch assembeller to using R6 for Program Counter ; ;---------------------- PROCESS IRQ REQUEST ---------------------------------------------- ; IRQ Service Routines *MUST* keep the Stack balanced ; *MUST NOT* alter the contents of R7, R6 or R5 ; IRQREQ RD $FF00,R0 ;Read slot IO base address for IRQ generating card AND R0,$00FF ;We only want the lower 8 bits SHR R0 ;Move upper nibble to lower nibble SHR R0 ; NB: Five instructions could be removed from ISR SHR R0 ; if the IRQ hardware returned $0002 SHR R0 ; instead of $FF20 LD R1,IRQVEC ;Load R1 with the addr of the IRQ Vector Table ADD R1,R0 ;Add the computed IRQ card offset into table to R1 RD R1,R0 ;Read IRQ vector for card into R0 ; ;Fixme: Bug! CALL function hard coded to 4 words. But CALL R0 with no prior load is only ; two words. ; ; Second bug(?): R7 not being transformed to R6 inside ISR-EISR pseudo ops ; ; CALL R0 ;Call IRQ handler PUSH R6 ;Simulate CALL instruction for now MOV R6,R0 ; ;---------------------- RETURN FROM IRQ SERVICE ROUTINE ---------------------------------- ; Start by restoring Flags. POP R0 ;Flags are now in R0 bits 1&0 as EQ&CY SHL R0 ;R0 = R0 * 2 LD R1,FLAGTBL ;R1 -> Start of Flag Table ADD R1,R0 ;Add offset into table equal to flag settings RDI R1,R2 ;Read 1st control variable into R2 RD R1,R3 ;Read 2nd control variable into R3 CMP R2,$FFFF ;Is R2 equal to $FFFF? Used to store EQ flag ; ADC R2,R3 EQ SF ;Add with Carry. CY-IN=EQ, Store Flags ;Flags now restored to previous values ;ADC is F=A+B+CY where ADD is F=A+B+(CY=0) ; ; Next is only for Diags. Write Flags to 7-Seg LED #3 ; ZERO R0 ;Clear contents R0 ; SLC R0 EQ ;Shift Left with Carry. F=R0+R0+(EQ status in bit 0) ; SLC R0 CY ;Shift Left with Carry. F=R0+R0+(CY status in bit 0) ; WR $FF12,R0 ;Display Flags Value ; ; Now Restore Saved Registers from Stack ; POP R0 POP R1 POP R2 POP R3 POP R4 ; IRQC ;Clear IRQ latch. Returns from IRQ routine. Fetch now from PC in R7 ;R6 now -> IRQJMP for the start of the next IRQ call ; ;Start of IRQ request location. IRQ Vector in R6 should point here! ; IRQJMP ;PUSH R7 ;Start by saving machine state, all registers and flags to stack ;PUSH R6 ;But not R7, R6 or R5 ;PUSH R5 PUSH R4 ;We must save R4 before its contents get destroyed by the next jump instruction PUSH R3 PUSH R2 PUSH R1 PUSH R0 ; ; Now save the two Flags EQ and CY. ; ZERO R0 ;Clear contents R0 SLC R0 EQ ;Shift Left with Carry. F=R0+R0+(EQ status in bit 0) SLC R0 CY ;Shift Left with Carry. F=R0+R0+(CY status in bit 0) PUSH R0 ;Save R0 now containing flags in bits 1&0 as EQ&CY ; ; Next is only for Diags. Write Flags to 7-Seg LED #4 ; *** WARNING! Can't do this here as we may be using R7 as PC ; If we are then the auto-added instruction ; ld r4,$FF13 ; post increments R6 not R7 and then the machine ; tries to execute data $FF13 as an opcode. ; ; SWP R0 ;Flags need to be in R0-H ; WR $FF13,R0 ;Display Flags Value ; ; Machine state including all registers and flags now saved. ; But did we arrive here by a Breakpoint Jump rather than ; an actual interrupt? ; ; *** WARNING! We can't use LD explicitly or by assembeller expansion ; of something like "MOV R7,BREAK" at this point. As we ; do not know if this code is being executed by R7 or R6 ; acting as the program counter. The LD operand makes ; explicit use of one or the other! Dependant on the ; status of the assembeller ISR flag (Pseudo Ops ISR & EISR). ; At this point we are inside the ISR code so R6 is hard coded ; into the LD instruction. But if the machine jumpped into this location ; via a Breakpoint rather than entering because of an ; Interrupt we will be using R7. ; ; So we have to arrive at the Breakpoint vector address without ; using a LD (Load) operand. ; ZERO R0 INC R0 INC R0 INC R0 SHL R0 ;R0 should now be 6 = BIOS Breakpoint Vector ; MOV R4,R7 ;Testing if we are using R7 for the Program Counter? CMP R4,R7 ;This will NOT be equal if we are! MOV R7,R0 IFN EQ ;This will jump to Break if using R7 as PC ; ; We must be using R6 as PC. So continue to the actual IRQ Request handler ; JMP IRQREQ ;Jump to start of IRQ Request Service Routine ; ;---------------------- NOIRQ - Invalid IRQ Request --------------------------- ; An unsupported/unexpected card raised an IRQ service request ; NOIRQ RD $FF00,R0 ;Read card address into R0 (again) LD R1,$FF10 ;Port Addr of 7-Seg Display WRI R1,R0 ;Display R0-L on digit #1 WRI R1,R0 ;Display R0-H on digit #2 LD R0,$E001 ;Error 001 = Invalid IRQ WRI R1,R0 ;Display R0-H on digit #3 WRI R1,R0 ;Display R0-H on digit #4 HLT ;Halt CPU with R0 on the Addr Bus ; ;---------------------- IRQ7SG - IRQ 7-Seg LED Display Board ------------------ ; Slot $FF10 7 Segment LED display PCB and Keypad. ; IRQ7SG RD $FF10,R0 ;Read keypad key into R0 WR $FF10,R0 ;Write key value to digit 0 RET ; ;---------------------- RINGWR - Ring Buffer Write Routine IRQ Slot #6 -------- ; Slot $FF60 contains 2651 Serial Card ; RINGWR LD R2,RBWPTR ;Ring Buffer Write Pointer RDI R2,R1 ;R1 -> Write Head of ring buffer RDD R2,R3 ;R3 -> Read Tail of ring buffer INC R1 ;Move Write Head forward one word AND R1,$3F ;Constrain Buffer to 64 words cyclic ADD R1,RINGB ;Add back the Ring Buffer base address CMP R1,R3 ;Does the Head = Tail? JEQ :OVRFL ;Yes? Buffer Full. Overflow Char lost! RD R2,R3 ;Get back current Write Head Ptr in R3 CALL :GETCHR ;Get Char from Serial Port into R0 JEQ :EXIT ;Was char Ctrl-C? Yes, then Exit WR R3,R0 ;Write char to ring buffer. NB: This will store 00## WR R2,R1 ;Write new Write Head Pointer to RBWPTR JMP :EXIT ; ; Buffer Over Flow Logic. ; Start by turning 'Off' RTS which disables CTS at PC end and stops ; further characters from arriving at the serial port. ; :OVRFL RD SIOCMD,R0 ;SIO Command Port RD R0,R1 ;Read current Command Register settings AND R1,%11011111 ;RTS Mask for bit 5 WR R0,R1 ;Write Command Register - Turn OFF RTS ; CALL :GETCHR ;Get Char from Serial Port into R0 JEQ :QUIT ;Was char Ctrl-C? Yes, then Quit LD R2,RBOVFL ;Address of Overflow Flag-H/Char-L data OR R0,$0F00 ;OR Char with high byte OverFlow flag WR R2,R0 ;Save Char and Flag to OverFlow word JMP :EXIT ;We're done return via exit ; :GETCHR PUSH R1 ;Save R1 for later RD SIODAT,R1 ;2651 Data Register RD R1,R0 ;RX Char in R0. LD R1,$00FF ;Mask for low byte only AND R0,R1 ;Only keep R0-low CMP R0,$03 ;Is char ETX (aka Ctrl-C)? MOV R7,$0003 IF EQ ;Yes? Then set R7 to Jump to Warm Boot Vector ;.. after ISR completes (using R6 at the moment) POP R1 ;Restore R1 RET ;Return to caller ; :QUIT RD SIOCMD,R0 ;Turn on RTS again as Ctrl-C not saved to buffer RD R0,R1 ;Read current Command Register settings OR R1,%00100000 ;RTS Mask for bit 5 WR R0,R1 ;Write Command Register - Turn ON RTS :EXIT RET ; ;---------------------- IRQ SERVICE ROUTINES ENDS HERE ------------------------ EISR ;End Interrupt Service Routine ;Switch Assembeller back to R7 for Program Counter ; ;---------------------- BREAKPOINT ROUTINE ------------------------------------ ; ; Arriving here from start of the IRQ Service Routine the stack will look ; like this:- ; ; ;TOS location at time of breakpoint ; R4 ; R3 ; R2 ; R1 ; R0 ; R5 -> Flags (Top Of Stack Now). ; Free (New location to store next PUSH/CALL data to) ; ;Start by saving all Registers and Flags to the Breakpoint Save area. ;Most values have been pushed to the stack by the ISR routine ;before we arrived here (see above). ; BREAK LD R4,BKFLAG ;Bottom of Register & Flags Save area (R7 -> R0, Flags) POP R0 ;R0 now contains flags value WRD R4,R0 ;Save flags POP R0 ;R0 now contains breakpoint R0 value WRD R4,R0 ;Save R0 POP R0 ;R0 now contains breakpoint R1 value WRD R4,R0 ;Save R1 POP R0 ;R0 now contains breakpoint R2 value WRD R4,R0 ;Save R2 POP R0 ;R0 now contains breakpoint R3 value WRD R4,R0 ;Save R3 POP R0 ;R0 now contains breakpoint R4 value WRD R4,R0 ;Save R4 MOV R0,R5 ;Place current R5 value into R0 WRD R4,R0 ;Save R5. Corrected to time of breakpoint WRD R4,R6 ;Save R6. Not changed directly by ISR code MOV R1,R4 ;Save current R4 to R1 as pisc-asm ; needs R4 for internal loads ; ; Now sort out correct value for R7 ; RD BKADDR,R0 ;Retrieve actual breakpoint address WR R1,R0 ;Save R7 address used to trigger breakpoint ; ; NB: A value of $0000 for BKADDR would indicate that no breakpoint was set ; so we should NOT be here executing this code! ; ;** NB: I don't believe this code block is needed as the actual BKPOINT ; does exactly this at the start of that routine ; ; CMP R0,$0000 ;Is the breakpoint address zero? ; JNE :CONT ;Continue as it is not zero ; LD R2,BRKERR ;Invalid Breakpoint Address ; CALL PRINT ; HLT ;Halt CPU ; ; Breakpoint Save Table values are now all correct. Jump into Monitor ; at the BKPOINT routine ; ; Reset breakpoint instruction back into its original location ; and clear the breakpoint address (flag) ; LD R1,BKADDR ;R1 -> Mem loc. for Breakpoint address RD R1,R2 ;R2 -> Mem loc. for Original opcode WRI R1,$0000 ;Breakpoint now address erased/cleared RD R1,R0 ;Read original opcode WR R2,R0 ;Original opcode restored ZERO R1 ;Set variable count to "0" for BKPOINT routine WR VCOUNT,R1 ;Write variable count CALL BKPOINT ;Enter monitor by call starting at Breakpoint routine ; ; Now exiting Breakpoint with Kontinue. Start by restoring Flags. ; do restore everything and carry on. ; LD R4,BKFLAG ;Saved registers and flags table RD R4,R0 ;Flags are now in R0 SHL R0 ;R0 = R0 * 2 LD R1,FLAGTBL ;R1 -> Start of Flag Table ADD R1,R0 ;Add offset into table equal to flag settings RDI R1,R2 ;Read 1st control variable into R2 RD R1,R3 ;Read 2nd control variable into R3 CMP R2,$FFFF ;Is R2 equal to $FFFF? Used to store EQ flag ; ADC R2,R3 EQ SF ;Add with Carry. CY-IN=EQ, Store Flags ;Flags now restored to previous values ;ADC is F=A+B+CY where ADD is F=A+B+(CY=0) ; ; Now restore Registers R7 to R0 ; LD R4,BKSAVE ;Saved registers and flags table RD R4,R0 ;R7 now in R0 PUSH R0 ;Save R7 to stack. ZERO R0 ;Clear R0 to $0000 WRI R4,R0 ;Write $0000 to R7 in Save Area. Clears Breakpoint Flag RDI R4,R6 ;R6 as IRQ should not change. Unless not using IRQ? INC R4 ;Skip past Stack Pointer. RDI R4,R3 ;R3 now contains value for R4 at breakpoint PUSH R3 ;Place the R4 value on the stack RDI R4,R3 ;Restore values for R3 RDI R4,R2 ;R2 RDI R4,R1 ;R1 RD R4,R0 ;and R0 POP R4 ;Get correct value of R4 back again POP R7 ;Restore R7 value from Stack ;Execution will now jump to instruction ;at original R7 breakpoint address. ;Stack R5 balanced back to correct ;value at time of breakpoint. ; ;---------------------- COLD BOOT CONTINUES HERE ------------------------------ ; ; Only now do we have RAM memory up high to store data ; and establish a stack ; COLDNXT LD R5,$FEC0 ;R5 used as Stack pointer ;Start of Stack set just below top of RAM ;Stack size 64 words = $40 ;So start starts at $FF00-$40 = $FEC0 ;** Stack grows UP! ** ; ; Set RAM location values for 2651 Serial UART Ports - COM1 ; LD R0,SIODAT ;R0 -> Addr holding SIODAT (Start of SIO Port Data Structure) WRI R0,$FF60 ;Data Port WRI R0,$FF62 ;Status Port, SYN/DLE Registers - Read=Status, 3 x Write=SYN1/SYN2/DLE WRI R0,$FF64 ;Mode Port 1&2 - 2 x Read/Write. WRI R0,$FF66 ;Command Port ; LD R2,$FF10 ;7-Seg Display digit #1 LD R0,$B1 ;Display B1 Hex WRI R2,R0 ;Hex digit #1 0000 00## to 01. Value from R0-L CALL SIOINIT ;Initialize the 2651 Serial UART LD R2,$FF10 ;7-Seg Display digit #1 LD R0,$B2 ;Display B2 Hex WRI R2,R0 ;Hex digit #1 0000 00## to 01. Value from R0-L ; ; Next we must setup the SIO Ring Buffer Head and Tail Pointers ; Because as soon as we enable IRQ at the end of this next section the ; IRQ SIO Service Routine could start trying to use these pointers! ; LD R1,RBWPTR ; R1 -> Ring Buffer Write Pointer WRI R1,RINGB ; Buffer Head points to start of ring WR R1,R4 ; Buffer Tail points to start of ring. R4 Set instr above. ; ; Setup for Interrupt Service Requests (IRQ) and then Enable ; LD R6,IRQJMP ;Init R6 with IRQ jump location LD R0,IRQVEC ;Load R0 with start of IRQ vector table ; WRI R0,NOIRQ ;IRQ Service routine for card slot 0 $FF00 WRI R0,IRQ7SG ;IRQ Service routine for card slot 1 $FF10 WRI R0,NOIRQ ;IRQ Service routine for card slot 2 $FF20 WRI R0,NOIRQ ;IRQ Service routine for card slot 3 $FF30 WRI R0,NOIRQ ;IRQ Service routine for card slot 4 $FF40 WRI R0,NOIRQ ;IRQ Service routine for card slot 5 $FF50 WRI R0,RINGWR ;IRQ Service routine for card slot 6 $FF60 WRI R0,NOIRQ ;IRQ Service routine for card slot 7 $FF70 WRI R0,NOIRQ ;IRQ Service routine for card slot 8 $FF80 ; RD MDAT0,R1 ;Read Slot 0 config data into R1 CMP R1,$00 ;Are we running FailSafe BIOS? JEQ :SKIP ;Failsafe BIOS - Do NOT enable IRQ ; IRQ 1 ;Normal BIOS setup all done. Enable IRQ ; :SKIP LD R0,BKADDR ;Clear key breakpoint values WRI R0,$0000 ;BKADDR - Breakpoint address WRI R0,$0000 ;BKINST - Original instrauction at breakpoint WRI R0,$0000 ;BKSAVE - Top of Register save area -> R7 ; Also used as flag. ; Non-Zero = Breakpoint Triggered. ; ;Note: R7 in save area should always be equal to BKADDR. You cannot set ; memory location $0000 as a valid breakpoint address. ; As this indicates no breakpoint has been set. ; By default this location is a ROM address in any case. ; ;---------------- WARM BOOT/MAIN PROGRAM STARTS HERE -------------------------- ; WARM LD R2,$FF10 ;7-Seg Display digit #1 LD R0,$B3 ;Display B3 Hex WRI R2,R0 ;Hex digit #1 0000 00## to 01. Value from R0-L LD R5,$FEC0 ;Reset R5 Stack pointer to TOS on Warm Boot CALL NEWLN ;Start by printing new line CR+LF LD R2,MSG1 ;Signon version message CALL PRINT ZERO R0 ;Set R0 to zero WR DPTR,R0 ;Init DPTR to Zero WR CKSUM,R0 ;Init Checksum to Zero RD MDAT0,R0 ;Read config data for memory slot 0 CMP R0,$0000 ;Are we running fail-safe BIOS from ROM Bank 0? JNE START ;No? Just jump to start LD R2,MSG3 ;Print Failsafe message CALL PRINT ; ; START LD R0,'>' ;Display prompt symbol CALL SIOTX ; ; CALL RTSON ;Turn ON RTS to host to start serial data LD R2,BUFFER1 ;Set R2 to 8 bit string buffer LD R3,BUFFER2 ;Set R3 to 16 bit string buffer CALL INPUT ;Input a line of text ; ; CALL RTSOFF ;Turn OFF RTS to host to halt serial data CALL NEWLN ;Print newline ; RD R2,R0 ;Read command field into R0 CMP R0,$3A3A ;Hex for '::' Hex file start code JEQ HEXFILE ;RX Hex File line CALL TOUPPER ;Convert packed string to all upper case ASCII CALL EXPSTR ;Expand 8 bit char string to 16 bit buffer CALL PKDSTR ;Pack 16 bit into 8 bit string with field delimiters word aligned ;CALL PRINT ;Print 8 bit buffer now with '$' field delimitors ;CALL NEWLN CALL SETVARS ;Set pointer values for VAR1TXT..VAR4TXT ; ;CALL PRTVARS ;Print variables - Diagnostics only ; ; Next logic fills four variables at locations VAR1..VAR4 ; VAR1 will contain two ASCII character command. Could single char with null i.e. hh00 ; VAR2..4 will be converted from hex string to 16 bit binary value ; LD R1,VAR1TXT ;Pointer to VAR1TXT string location LD R3,VAR1 ;Addr in memory where we wish to store VAR1 RD R1,R2 RD R2,R0 ;Read first word from location in VAR?TXT PTR into R0 WR R3,R0 ;Write R0 unmodified to first variable. This is the two char command INC R1 ;Point to next variable text VAR?TXT INC R3 ;Point to next variable bit value VAR? ; :LOOP1 RD R1,R2 ;Read VAR?TXT pointer contents into R2 CALL STR2BN ;Convert string at location R2 into 16 bit binary WR R3,R0 ;R0 should now contain the binary value. Write it to mem variable INC R1 ;Point to next variable text VAR?TXT INC R3 ;Point to next variable bit value VAR? CMP $FD64,R3 ;Have we now filled all four variables? ;Fixme: Bug! VAR4+1 emits ld r4,$fd63 and then another ; ld r4,fd64. Probably mucks up Location Counter. JNE :LOOP1 ;..No? Then loop again ; ;------------------------ TEST PRINT VARIABLES ------------------------------ ; ; LD R3,VAR1 ;Reset R3 for test printing to.. ; ;Addr in memory where VAR1 is stored ;:LOOP2 RD R3,R0 ;Read VAR? straight back in for testing ; CALL WRD2HEX ;Convert this value to 4 digit HExidecimal string ; LD R0,'*' ;Print a separator ; CALL SIOTX ; CALL PRINT ;Print value in buffer ; LD R0,'*' ;Print a separator ; CALL SIOTX ; CALL NEWLN ;Print newline ; INC R3 ;Point to next variable bit value VAR? ; CMP $FD64,R3 ;Have we displayed all four variables? ; ;Fixme: Bug! VAR4+1 emits ld r4,$fd63 and then another ; ; ld r4,fd64. Probably mucks up Location Counter. ; JNE :LOOP2 ;..No? Then loop again ; ; RD VCOUNT,R0 ;Read count value into R0 for the next routine ; CALL WRD2HEX ;Convert this value to 4 digit Hexidecimal string ; LD R0,'#' ;Print a separator ; CALL SIOTX ; CALL PRINT ;Print value in buffer ; LD R0,'#' ;Print a separator ; CALL SIOTX ; CALL NEWLN ; ;------------------------------------------------------------------------------ ; RD VCOUNT,R0 ;Read current value of VCOUNT CMP R0,$0000 ;Nothing entered on command line? JEQ START ;..Then just go back to the start again ; RD VAR1,R0 ;Read command value into R0 LD R1,$DF00 ;Only want the first char and in UPPER case AND R1,R0 SWP R1 ;R1 now holds 00hh where hh is the command letter ; CMP R1,'D' ;Dump command? JEQ DUMP ;..Yes then goto Dump routine CMP R1,'W' ;Write command? JEQ WRITE ;..Yes then goto Write routine CMP R1,'R' ;Read command? JEQ READ ;..Yes then goto Read routine CMP R1,'E' ;Enter command? JEQ ENTER ;..Yes then goto Enter routine CMP R1,'F' ;Fill command? JEQ FILL ;..Yes then goto Fill routine CMP R1,'C' ;Copy command? JEQ COPY ;..Yes then goto Copy routine CMP R1,'J' ;Jump command? JEQ JUMP ;..Yes then goto Jump routine CMP R1,'M' ;Memory Banking command? JEQ MEMBNK ;..Yes then goto Memory Bank routine CMP R1,'B' ;Breakpoint command? JEQ BKPOINT ;..Yes then goto Breakpoint routine CMP R1,'K' ;Breakpoint Kontinue command? JEQ BKPCONT ;..Yes then goto Breakpoint Kontinue routine CMP R1,'S' ;Breakpoint Step command? JEQ BKPSTEP ;..Yes then goto Breakpoint Step routine ; LD R2,MSGHLP ;Non valid command. Display help message CALL PRINT JMP START ;Repeat main program forever ; ;-------------------------- Hex File Receive ---------------------------------- ; ; We hack serial comms here to slow things down by Turning OFF RTS at start ; and back on again at the end of line processing. ; A more general "fix" in Serial IRQ ISR routine is needed! ; HEXFILE INC R2 ;Move R2 past the '::' Start Code CALL HEX2WRD ;Convert first 4 hex digits to 16 bit word WR XDATA,R0 ;Save returned value to Data word count CALL ADD2CRC ;Add R0 to cumulative CRC value CALL HEX2WRD ;Convert next 4 hex digits to 16 bit word WR XADDR,R0 ;Save returned value to load memory Address CALL ADD2CRC ;Add R0 to cumulative CRC value CALL HEX2WRD ;Convert next 4 hex digits to 16 bit word WR XTYPE,R0 ;Save returned value to record Type MOV R1,R0 ;Grab copy of R0 before ADD2CRC changes it CALL ADD2CRC ;Add R0 to cumulative CRC value CMP R1,$0010 ;Is this a data record? JEQ :RXHEX ;..Yes? Then jump to Hex Data CMP R1,$0011 ;Is this a End Of File record? JEQ :RXEOF ;Process EOF record ; LD R2,XERROR ;Bin file record type unknown CALL PRINT ;Display error JMP :EXIT ;Exit this routine ; ;Process EOF Record ; :RXEOF CALL HEX2WRD ;This should be the checksum CALL ADD2CRC ;Add R0 to cumulative CRC value LD R2,XTRANS ;Display "Transfer" message CALL PRINT LD R2,XFAIL ;Setup for "Failed" message LD R1,XOK ;R1 now holds address of XOK message CMP R0,$0000 ;Valid record? Store EQ flag MOV R2,R1 IF EQ ;Setup R2 for "Success" message if Checksum zero CALL PRINT ;Print transfer result ZERO R0 ;Reset Checksum for next time WR CKSUM,R0 ;Init Checksum to Zero JMP :EXIT ;Exit this routine ; :RXHEX RD XDATA,R1 ;Load number of data words into R1 DEC R1 SF ;Offset by one as test below aborts at -1 JEQ :HEXERR ;Number of bytes set to zero. Jump to error LD R3,BUFFER2 ;Use BUFFER2 for output memory data :LOOP CALL HEX2WRD ;Convert next 4 hex digits to 16 bit word WRI R3,R0 ;Save output to BUFFER2 CALL ADD2CRC ;Add R0 to cumulative CRC value DEC R1 SF ;Decrement data word count and Store Flags JNE :LOOP ;Keep going until all data processed ; CALL HEX2WRD ;Next word should be checksum returned in R0 CALL ADD2CRC ;Add R0 to cumulative CRC value CMP R0,$0000 ;Checksum should always be zero if successful! JNE :HEXERR ;..We have a transfer error ; ;CRC Pass - Successful data transfer - Now write data to memory ; RD XDATA,R1 ;Load number of data words into R1 DEC R1 ;Offset by one as test below aborts at -1 LD R3,BUFFER2 ;Point R3 to start of BUFFER2 again RD XADDR,R2 ;Point R2 to memory location to load data :LOOP2 RDI R3,R0 ;Read R3 -> source data into R0 WRI R2,R0 ;Write data to R2 -> Memory DEC R1 SF ;Decrement data word count and Store Flags JNE :LOOP2 ;Keep going until all data processed JMP :EXIT ;Exit this routine ; :HEXERR WR $FF10,R0 ;Display R0 WR $FF11,R0 LD R0,$E002 ;Error 2 WR $FF12,R0 WR $FF13,R0 LD R2,CRCERR ;Display CRC Error message CALL PRINT CALL RTSON ;Turn ON RTS to host to start serial data :LOOPY JMP :LOOPY ;Hang here. Ctrl-C warm boot may get out? ; :EXIT JMP START ;Jump to start again ; ;------------------------------------------------------------------------------ DUMP LD R2,MSGDU CALL PRINT RD DPTR,R1 ;R1 now equal to last dump location MOV R3,R1 ;R3 end location now the same ADD R3,$0080 ;R3 now + $0080 above start RD VCOUNT,R0 ;R0 now equal to number of parameters CMP R0,4 ;Were four (or more) parameters supplied? JNE :NEXT1 ;..No? keep going DEC R0 ;We don't need 4 so ignore the last ; :NEXT1 CMP R0,3 ;Three parameters? JNE :NEXT2 ;..No? Jump next test RD VAR2,R1 ;Start addr in R1 RD VAR3,R3 ;End addr in R3 INC R3 ;Adjust +1 because display loop uses postincrement JMP :DISP ; :NEXT2 CMP R0,2 ;Were two parameters passed? JNE :DISP ;..No? We'll run with defaults RD VAR2,R1 ;Start addr supplied we'll use it MOV R3,R1 ;No end addr so we'll use start ADD R3,$0080 ;..with $0080 hex added as end addr ; :DISP PUSH R1 ;Save the line start mem location for later MOV R0,R1 ;R0 now equal to current memory location CALL WRD2HEX ;Convert this value to 4 digit Hexidecimal string CALL PRINT ;Print value in buffer LD R0,':' ;Print a separator CALL SIOTX LD R0,$20 CALL SIOTX ZERO R0 ;VFLAG used to track 8 values (0..7) to display per line WR VFLAG,R0 ;Ran out of Registers. ; :LOOP RDI R1,R0 ;Word to display in R0 CALL WRD2HEX ;Convert this value to 4 digit Hexidecimal string CALL PRINT ;Print value in buffer LD R0,$20 ;Print a space separator CALL SIOTX CMP R3,R1 ;Reached the end? JEQ :EXIT ;..Yes? Then exit dump RD VFLAG,R0 ;Retrieve number of values displayed this line INC R0 ;Increase by one WR VFLAG,R0 ;Update the vflag variable CMP R0,8 ;Have we displayed 8 values? JNE :LOOP ;..No? Then loop again for another value POP R2 ;Retrieve the start location of this line CALL PRINT16 ;Now display the ASCII version of the data CALL NEWLN ;Terminate the line with CR+LF JMP :DISP ;Now start a new line ; :EXIT POP R2 ;Retrieve the start location of this line CALL PRINT16 ;Now display the last line of ASCII data CALL NEWLN ;Terminate the line with CR+LF WR DPTR,R1 ;DPTR now equal to last dump Addr+1 JMP START ; ;------------------------------------------------------------------------------ WRITE LD R2,MSGWR CALL PRINT RD VAR2,R0 ;Addr in R0 RD VAR3,R1 ;Data in R1 WR R0,R1 ;Write data to address CALL WRD2HEX ;Convert this value to 4 digit Hexidecimal string CALL PRINT ;Print value in buffer LD R0,':' ;Print a separator CALL SIOTX LD R0,$20 CALL SIOTX MOV R0,R1 ;Copy Data value to R0 CALL WRD2HEX ;Convert this value to 4 digit Hexidecimal string CALL PRINT ;Print value in buffer CALL NEWLN JMP START ; ;------------------------------------------------------------------------------ READ LD R2,MSGRD CALL PRINT RD VAR2,R0 ;Addr in R0 PUSH R0 ;Save for later CALL WRD2HEX ;Convert this value to 4 digit Hex string CALL PRINT ;Print value in buffer LD R0,':' ;Print a separator CALL SIOTX LD R0,$20 CALL SIOTX POP R1 ;Restore Addr to read RD R1,R0 ;Data read into R0 CALL WRD2HEX ;Convert this value to 4 digit Hex string CALL PRINT ;Print value in buffer CALL NEWLN JMP START ; ;------------------------------------------------------------------------------ ENTER LD R2,MSGEN CALL PRINT RD VAR2,R0 ;Addr in R0 :LOOP PUSH R0 ;Save for later CALL WRD2HEX ;Convert this value to 4 digit Hex string CALL PRINT ;Print value in buffer LD R0,':' ;Print a separator CALL SIOTX LD R0,$20 CALL SIOTX POP R1 ;Restore Addr to read RD R1,R0 ;Data read into R0 CALL WRD2HEX ;Convert this value to 4 digit Hex string CALL PRINT ;Print value in buffer LD R0,$20 CALL SIOTX LD R2,BUFFER1 ;Set R2 to 8 bit string buffer LD R3,BUFFER2 ;Set R3 to 16 bit string buffer CALL INPUT ;Input a line of text CALL NEWLN ;Print newline RD R2,R0 ;Read command field into R0 SWP R0 ;Only interested in first character AND R0,$00FF ;Mask for Low Byte Only CMP R0,$0000 ;Nothing entered? JEQ :SKIP ;Then skip to the next word CMP R0,'.' ;Period entered? JEQ START ;Yes? We're done. Jump to monitor Start loop RD R2,R0 ;Some other value. Read data into R0 again CALL STR2BN ;Convert string at location R2 into 16 bit binary WR R1,R0 ;R0 should now contain the binary value. Write it to memory :SKIP INC R1 ;Point to next word location MOV R0,R1 ;Save it back to R0 for next cycle JMP :LOOP ;Go do it again all over again ; ;------------------------------------------------------------------------------ FILL LD R2,MSGFL CALL PRINT RD VCOUNT,R0 ;Check that we have all four parameters CMP R0,4 ;Four parameters supplied? JNE ERROR ;We have the wrong number of parameters RD VAR2,R1 ;Start Addr in R1 RD VAR3,R2 ;Stop Addr in R2 RD VAR4,R3 ;Fill Data in R3 INC R2 ;Because we post increment bump end addr up one :LOOP WRI R1,R3 ;Write data to dest at R1 CMP R1,R2 ;Have we reach the end? JEQ START ;..Yes? The we are done JMP :LOOP ;Loop until complete ; ;------------------------------------------------------------------------------ COPY LD R2,MSGCP CALL PRINT RD VCOUNT,R0 ;Check that we have all four parameters CMP R0,4 JNE ERROR ;We have the wrong number of parameters RD VAR2,R1 ;Source Addr in R1 RD VAR3,R2 ;Dest Addr in R2 RD VAR4,R3 ;Length in R3 DEC R3 ;Loop terminates at -1 so we adjust by one :LOOP RDI R1,R0 ;Read source data into R0 WRI R2,R0 ;Write data to dest at R2 DEC R3 SF ;Decrement the word count and store flags JEQ START ;We are done JMP :LOOP ;Loop until complete ; ;------------------------------------------------------------------------------ JUMP LD R2,MSGJP CALL PRINT RD VCOUNT,R0 ;Check that we have exactly two parameters CMP R0,2 JNE ERROR ;We have the wrong number of parameters RD VAR2,R1 ;Target jump Addr in R1 PUSH R7 ;Set this up as if it were a CALL MOV R7,R1 ;Update R7 aka PC to new Addr - Do it! JMP START ;On the off chance we return then back to start ; ;------------------------------------------------------------------------------ MEMBNK LD R2,MSGMBK CALL PRINT RD VCOUNT,R0 ;Check that we have exactly two parameters CMP R0,2 JNE ERROR ;We have the wrong number of parameters RD VAR2,R1 ;Variable 2 now in R1 MEM R1 ;Use VAR2 to set Slot Memory Banking ; Example VAR2 = $19 would be Memory Slot 1 -> Bank 9 (RAM 2nd Bank) JMP START ; ;------------------------------------------------------------------------------ BKPOINT RD VCOUNT,R0 ;Check that we have exactly two parameters CMP R0,2 JNE :CONT1 ;We're not setting a breakpoint. Carry on. LD R2,BKADDR ;R2 -> Stored Breakpoint Address RD VAR2,R1 ;New Breakpoint Addr to set in R1 WRI R2,R1 ;Breakpoint Addr Stored RD R1,R0 ;Read original opcode WRI R2,R0 ;Save original opcode ; WR R2,$0000 ;Clear R7 data value at top of BKSAVE area ; this is used as a flag to track trigger ; of the breakpoint LD R2,$3E9A ;Replace opcode with 'MOV R7,R6' WR R1,R2 ;Write Break Opcode to Breakpoint Addr JMP START ;Back to monitor ; :CONT1 LD R2,BKADDR ;Read breakpoint set address RDI R2,R0 ; saved in Register save area INC R2 ;Skip over original opcode word RD R2,R1 ;Read triggered address from R7 in save area MOV R2,R1 ;Save R1 into R2 OR R2,R0 ;Combine both values with OR DEC R2 SF ;Was R2=Zero? Now equal to -1 two's complement if it was. JNE :CONT2 ;We have have either a set or triggered breakpoint LD R2,BRKERR ;Invalid Breakpoint Address CALL PRINT JMP START ;Stay in monitor ; ; NB: A value of $0000 for BKSAVE would indicate that no breakpoint has been triggered ; :CONT2 OR R0,R1 ;Not sure if Break Set or Triggered. So just OR them. PUSH R0 ;Save break address in R0 for later LD R2,MSGBRK ;Display Break message and location CALL PRINT CALL WRD2HEX ;Convert R0 to 4 digit Hexidecimal string CALL PRINT ;Print value in buffer LD R0,$3A20 ;Set R0 = ": " CALL PRTWRD POP R2 ;Get back break address into R2 RD R2,R0 ;Read current opcode at addr R2 into R0 CALL WRD2HEX ;Convert R0 to 4 digit Hexidecimal string CALL PRINT ;Print value in buffer CALL NEWLN ;Print new line CR+LF combo CMP R1,$0000 ;Breakpoint Triggered? i.e. Saved R7 address non-zero? JEQ START ;No! Jump back to monitor. ; LD R2,MSGBKH ;Load Breakpoint Header Message CALL PRINT LD R1,7 ;0-7 Registers LD R3,BKSAVE ;Display breakpoint saved registers and flags :LOOP3 LD R0,'R' ;Display 'R' lead-in for Register number CALL SIOTX LD R0,$30 ;ASCII '0' ADD R0,R1 ;Adjust R0 for Register number CALL SIOTX ;Print register number LD R0,'=' ;Print an '=' sign CALL SIOTX RDI R3,R0 CALL WRD2HEX ;Convert this value to 4 digit Hexidecimal string CALL PRINT ;Print value in buffer LD R0,$20 ;Print a space separator character. Fixme: ' ' returns Null CALL SIOTX DEC R1 SF ;Decrement R1 and store flags JCY :LOOP3 CALL NEWLN LD R2,MSGBK2 ;Flags message CALL PRINT RD R3,R0 CALL WRD2HEX ;Convert this value to 4 digit Hexidecimal string CALL PRINT ;Print value in buffer CALL NEWLN JMP START ;Jump back to START of monitor ;------------------------------------------------------------------------------ BKPCONT LD R2,BKSAVE ;Retrieve actual breakpoint address from RD R2,R0 ;save area R7 at break now in R0 ; ; NB: A value of $0000 for BKSAVE would indicate that no breakpoint event ; was triggered. ; ZERO R1 ;Set R1 to zero CMP R0,R1 ;Is the triggered breakpoint address zero? JNE :CONT ;No? Then Continue back to suspended program LD R2,BKADDR ;Retrieve current set breakpoint address RD R2,R0 CMP R0,R1 ;Is the set breakpoint address zero? JEQ :EXIT ;Yes? No breakpoint set. Display error and exit ; ; Flow to here means Breakpoint set but not triggered. So just clear it ; Reset current breakpoint instruction back into its original location ; and clear the breakpoint address (flag) ; LD R1,BKADDR ;R1 -> Mem loc. for Breakpoint address RD R1,R2 ;R2 -> Mem loc. for Original opcode WRI R1,$0000 ;Breakpoint now address erased/cleared RD R1,R0 ;Read original opcode WR R2,R0 ;Original opcode restored JMP START ;Stay in monitor ; ; :CONT RET ;Exit Monitor by Return to breakpoint caller ; :EXIT LD R2,BRKERR ;No Breakpoint address currently set CALL PRINT JMP START ;Stay in monitor ; ;------------------------------------------------------------------------------ BKPSTEP LD R2,BKSAVE ;Retrieve actual breakpoint address from RD R2,R1 ;Save area R7 at break now in R0 ; ; NB: A value of $0000 for BKSAVE would indicate that no breakpoint event ; was triggered. ; ZERO R0 ;Set R0 to zero CMP R1,R0 ;Is the triggered breakpoint address zero? JEQ :CONT ;Continue to error as no breakpoint triggered RD R1,R0 ;R0 now contains opcode at breakpoint AND R0,%1111100011111111 ;Mask for the LD (load) opcode CMP R0,%1011100010000000 ;LD opcode ZERO R0 ;R0 now zero MOV R0,$0001 IF EQ ;R0 now '1' only if this is a LD opcode ADD R1,R0 ;Add R0 to R1. Increments by one if LD command INC R1 ;Point to next executable memory opcode word LD R2,BKADDR ;R2 -> Stored Breakpoint Address WRI R2,R1 ;Breakpoint Addr Stored RD R1,R0 ;Read original opcode WRI R2,R0 ;Save original opcode LD R2,$3E9A ;Replace opcode with 'MOV R7,R6' WR R1,R2 ;Write Break Opcode to Breakpoint Addr :EXIT RET ;Step out of Monitor by return to breakpoint caller ; should now step to the next instruction ; :CONT LD R2,BRKERR ;No Breakpoint address currently set CALL PRINT JMP START ;Stay in monitor ; ; ;------------------------------------------------------------------------------ ERROR LD R2,MSGERR ;Display error message CALL PRINT JMP START ; ;============================================================================== ;Name : ADD2CRC ; Build 16 bit CRC (Cyclic Redundancy Check) Value ; Entry: R0 = 16 bit value to add to Checksum ; Exit: CKSUM Memory address updated with new checksum ; R0 = Current checksum value ; ; Destroyed: R0 as return value ;============================================================================== ADD2CRC PUSH R1 ;Save R1 RD CKSUM,R1 ;Read current checksum value ADD R0,R1 ;Add current to new R0 value WR CKSUM,R0 ;Write R0 value back to memory POP R1 ;Restore R1 RET ; ;============================================================================== ;Name : HEX2WRD ; Convert four Hexidecimal characters to a 16 bit binary word ; Entry: R2 -> two consecutive words containing 4 x Hex chars ; Exit: R0 contains 16 bit value ; R2 -> next unread word in buffer ; (NB: R2 would normally return as R2+2 but may be less ; as it will halt at any Null encountered) ; ; Destroyed: R0 ; ; *** NB: This code nearly identical to the STR2BN function. ; STR2BN converts arbitary length hex strings to binary ; value max size 16 bits. While HEW2WRD is fixed at four hex digits ; and does not check validity of the input hex chars. ; Functions HEX2WRD and STR2BN should be refactored combining ; common elements. ;============================================================================== ; HEX2WRD PUSH R1 PUSH R3 ; ZERO R3 ;Used to hold binary return value RD R2,R0 ;Read first word CALL :CBYTE ;Convert first word to high byte RD R2,R0 ;Read second word CALL :CBYTE ;Convert second word to low byte ; MOV R0,R3 ;Move return value back into R0 POP R3 POP R1 RET ;Return to caller ; :CBYTE SHL R3 ;Move value up ready for next char SHL R3 ;First time through loop this is a total SHL R3 ;..waste of time as R3 = Null SHL R3 ;..But it keeps the code simpler! SWP R0 ;Place high nibble at R0-L LD R1,$00FF ;Test if Null string termination reached AND R1,R0 DEC R1 SF JEQ :EXIT CALL A2HEX ;Convert high character OR R3,R1 ;Store R1 value into low nibble R3 ; SHL R3 ;Move value up ready for next char SHL R3 ;First time through loop this is a total SHL R3 ;..waste of time as R3 = Null SHL R3 ;..But it keeps the code simpler! SWP R0 ;Switch low nibble back into R0-L LD R1,$00FF ;Test if Null string termination reached AND R1,R0 DEC R1 SF JEQ :EXIT CALL A2HEX ;We have a valid character OR R3,R1 ;Store R1 value into low nibble R3 INC R2 ;Increment R2 to point at next ASCII hex byte :EXIT RET ;Return to HEX2WRD call ; ;============================================================================== ;Name : STR2BN ; Convert string to 16 bit word binary value ; Entry: R2 -> Buffer containing hexidecimal string ; Exit: R0 contains 16 bit value ; ; ; Destroyed: ;============================================================================== ; STR2BN PUSH R1 PUSH R2 PUSH R3 ; ZERO R3 ;Used to hold binary return value :LOOP RDI R2,R0 SWP R0 LD R1,$00FF AND R1,R0 DEC R1 SF JEQ :EXIT SHL R3 ;Move value up ready for next char SHL R3 ;First time through loop this is a total SHL R3 ;..waste of time as R3 = Null SHL R3 ;..But it keeps the code simpler! CALL A2HEX ;We have a valid character OR R3,R1 ;Store R1 value into low nibble R3 ; SWP R0 LD R1,$00FF AND R1,R0 DEC R1 SF JEQ :EXIT SHL R3 ;Move value up ready for next char SHL R3 ;First time through loop this is a total SHL R3 ;..waste of time as R3 = Null SHL R3 ;..But it keeps the code simpler! CALL A2HEX ;We have a valid character OR R3,R1 ;Store R1 value into low nibble R3 JMP :LOOP ; :EXIT MOV R0,R3 ;Move return value back into R0 POP R3 POP R2 POP R1 RET ; ;============================================================================== ;Name : WRD2HEX ; Convert 16 bit word to four Hexidecimal characters ; Entry: R0 contains 16 bit value ; Exit: R2 -> Buffer containing 3 x words with 4 x Hex chars + Null ; Termination ; ; ; Destroyed: R2 ;============================================================================== ; WRD2HEX PUSH R0 ;Save R0 for later SWP R0 ;Process high byte first LD R2,BUFFER1 ;Set R2 -> BUFFER1 CALL BN2HEX ;Convert high byte in R0-L to 2 hex digits WRI R2,R0 ;Hex digits returned in R0 store in buffer POP R0 ;Restore R0 to original value CALL BN2HEX ;Now process low byte in R0-L WRI R2,R0 ;Store low byte Hex digits in buffer WRI R2,$0000 ;Make sure buffer string is Null terminated LD R2,BUFFER1 ;Point to start of buffer again R2 -> BUFFER1 RET ; ;============================================================================== ;Name : PRTVARS ; Print four variables VAR1TXT..VAR2TXT ; Entry: ; Exit: ; ; Destroyed: ;============================================================================== ; PRTVARS PUSH R0 ;Save stack PUSH R1 PUSH R2 PUSH R3 ; LD R3,VAR1TXT ;R3 now the Address of VAR1TXT ; :LOOP RDI R3,R2 ;Read contents of VAR?TXT pointer into R2 LD R0,'>' CALL SIOTX CALL PRINT LD R0,'<' CALL SIOTX CALL NEWLN CMP R3,$FD68 ;All four variables printed? ;Fixme: Bug This should be VAR4TXT+1 ; but PISCASM fails to add +1 here! JNE :LOOP ;..No? Then loop again ; POP R3 ;Restore stack POP R2 POP R1 POP R0 RET ; ;============================================================================== ;Name : BN2HEX ; 8 bit Binary to ASCII hex ; Entry: R0-Low = 8 Bits of binary data ; Exit: R1 = Two digit ASCII hex representation of binary data ; R1-High is upper 4 bit nibble in ASCII hex ; R1-Low is lower 4 bit nibble in ASCII hex ;============================================================================== ; BN2HEX ;Convert High Nibble PUSH R1 PUSH R0 ;Save original binary value ; AND R0,$00F0 ;Get high nibble SHR R0 ;Move high nibble to low nibble (Bitwise Shift-Right) SHR R0 SHR R0 SHR R0 CALL NIB2ASC ;Convert high nibble to ASCII MOV R1,R0 ;Save high nibble char in R1-L SWP R1 ;Move high nibble char in R1-L to R1-H ; ;Convert Low Nibble POP R0 ;Restore original R0 binary value AND R0,$000F ;Get low nibble CALL NIB2ASC ;Convert low nibble to ASCII OR R0,R1 ;Return both ASCII chars in R0 ; POP R1 ;Restore stack RET ; ;============================================================================== ;Name: NIB2ASC ; Convert a hexadecimal Nibble (Lower 4 bits R0-L 0..F digit) to ASCII ; Entry: R0 = Binary data in lower 4 bits nibble ; Exit: R0 = ASCII Character representation of binary nibble ; ; Destroyed: ;============================================================================== ; NIB2ASC SUB 9,R0 SF ;Is R0 > 9 ? ;..Subtract R0 from 9. ;..9 -> R4, R4 destroyed by result, don't care. ;NB: Subtraction actually 1's complement addition. ; So Carry means borrow -> No underflow ; No Carry -> Underflow JCY :CONT ;..No? Then jump past the addition ADD R0,7 ;..Yes? Then add 7 so after adding '0' the ;..character will be in range 'A'..'F' ; :CONT ADD R0,'0' ;Add ASCII '0' to make it a character RET ; ;============================================================================== ;Name : HEX2BN ; Two digit hex ASCII characters to 8 bit Binary ; Entry: R0 = 16 bit 2 hex digit ASCII characters ('00'..'FF') ; Exit: R1-low = 8 bit binary data value (0-255) ; ; Destroyed: ;============================================================================== ; HEX2BN PUSH R2 ;Save R2 to stack ; ZERO R1 ;Make sure R1 starts as at zero SWP R0 ;Switch high char to R0-low CALL A2HEX ;Convert R0-low char to hexadecimal SHL R1 ;Shift returned value to upper 4 bits SHL R1 SHL R1 SHL R1 MOV R2,R1 ;Save high hex digit value in R2 SWP R0 ;Switch low char back to R0-low CALL A2HEX ;Convert it to hexadecimal OR R1,R2 ;OR in the saved high hex digit value ; POP R2 ;Restore stack RET ; ;============================================================================== ;Name : A2HEX ; Convert single ASCII Hex character to a 8 bit Hex binary value ; Entry: R0-low = ASCII Hex Digit (0-F) ; Exit: R1-low = 4 bit binary data value (low nibble) ; ; Destroyed: ;============================================================================== ; A2HEX PUSH R0 ;Save R0 to stack AND R0,$00FF ;Mask for low byte only SUB R0,'0' ;Subtract ASCII offset SUB 9,R0 SF ;Is R1 > 9 ? ;.. Subtract R0 from 9. 9->R4 working register ;.. R4 then destroyed by result but we don't care. ;NB: Subtraction actually 1's complement addition. ; So Carry means borrow -> No underflow ; No Carry -> Underflow JCY A2HEX1 ;..Yes? Jump if carry set. ;..Meaning R0 (low nibble) <= 9 ; SUB R0,7 ;..else subtract offset for letters A2HEX1 MOV R1,R0 ;Copy return result into R1 POP R0 ;Restore original R0 RET ; ;============================================================================== ;Name : TOUPPER ; Convert 8-bit packed ASCII string to all upper case. ; ; Entry: R2 -> First char of Null terminated String 8 bit packed source ; ; Exit: R2 -> Same first word of Null terminated String 8 bit packed ; after conversion of all lower case to upper case ; ; Destroyed: None ;============================================================================== ; TOUPPER PUSH R0 ;Save registers to stack PUSH R1 PUSH R2 ; :LOOP RD R2,R0 ;Get two packed chars into R0 CMP R0,$0000 ;Have we reach the end? JEQ :EXIT ;Yes? Then exit MOV R1,R0 ;Save two chars in R1 AND R1,$4040 ;Mask for upper case bytes only SHR R1 ;Shift bits in mask one place right NOT R1 ;Invert R1 to create upper case mask AND R0,R1 ;Turn off upper case WRI R2,R0 ;Write characters back again JMP :LOOP ;Keep going to next two characters ; :EXIT POP R2 ;Restore modified registers POP R1 POP R0 RET ;============================================================================== ;Name : EXPSTR ; Expand string from 8 bit packed format to 16 bit per character ; format. ; Entry: R2 -> First char of Null terminated String 8 bit packed source ; R3 -> First char of String buffer for 16 bit destination ; NB: R3 Must be twice the size of R2 to prevent buffer overflow ; ; Exit: R3 contains 16 unpacked version of R2 null terminated ; ; Destroyed: ;============================================================================== ; EXPSTR PUSH R0 ;PUSH stack here PUSH R1 PUSH R2 PUSH R3 ; ;LD R2,BUFFER1 ;LD R3,BUFFER2 ; :LOOP RDI R2,R0 ;Process high byte SWP R0 LD R1,$00FF AND R1,R0 CMP R1,$0000 ;Null end of string detected? JEQ :EXIT ;Yes? Then exit WRI R3,R1 ;Write high byte to output $00hh ; SWP R0 ;Swap back to low byte LD R1,$00FF AND R1,R0 CMP R1,$0000 ;Null end of string detected? JEQ :EXIT ;Yes? Then exit WRI R3,R1 JMP :LOOP ; :EXIT WRI R3,$0000 ;Terminate string POP R3 ;POP stack here POP R2 POP R1 POP R0 RET ; ;============================================================================== ;Name : PKDSTR ; Pack 16 bit sting to 8 bit and Null delimit fields ; Entry: R3 -> First char of String buffer for 16 bit input format $00hh ; High bits always null ; R2 -> First char of destination 8 bit Null terminated String ; NB: R3 Should be twice the size of R2 ; Exit: R2 contains 8 packed string with fields separated by one or ; more ASCII Unit Separator bytes (US) $1F ; ; Destroyed: ;============================================================================== ; PKDSTR PUSH R0 ;PUSH stack here PUSH R1 PUSH R2 PUSH R3 ; ZERO R1 ;Clear R1. Used to hold two chars to be written :LOOP RDI R3,R0 ;Read 16 bit char into R0 = $00hh CMP R0,$0000 ;Have we reached the end of the string? JEQ :EXIT ;Yes? Then exit CMP R0,$0020 ;Is char a [SPACE]? ; MOV R0,$001F IF EQ ;Yes? Then change [SPACE] to a US $1F MOV R0,$0024 IF EQ ;Yes? Temp change to '$' while debugging OR R1,R0 ;Always place new char in R1-low PUSH R0 ;Save R0 to stack - Ran out of registers LD R0,$FF00 ;Mask for high char AND R0,R1 ;R0 now has $hh00 CMP R0,$0000 ;Is the high char still free? POP R0 ;Restore R0 from stack JNE :WRITE ;No? Two chars filled go and write to buffer SWP R1 ;Yes? Then move char from low to high byte CMP R0,$0024 ;Was the char changed to US $1F? JNE :LOOP ;No? Then go and get the next char ; :WRITE CMP R1,$2400 ;Is the high byte a unit separator? ; CMP R1,$1F00 ;Is the high byte a unit separator? MOV R1,$2424 IF EQ ;Yes? Then make both bytes $1F WRI R2,R1 ZERO R1 JMP :LOOP :EXIT WRI R2,R1 ;Make sure output string is Null terminated ;R1 could be $0000 or $hh00 ;..Ok either way. ; POP R3 ;POP stack here POP R2 POP R1 POP R0 RET ; ;============================================================================== ;Name : SETVARS ; Set variable pointers routine ; Read 8 bit packed string buffer and set next variable pointer to ; word location one cell after a Null is detected. ; Entry: R2 -> First char of 8 bit Unit Separated (US) $1F and Null ; terminated String being the output of PKDSTR ; Exit: All four VAR?TXT pointers set (is sufficient fields provided) ; VCOUNT is set to the number of fields provided. ; ; Destroyed: ;============================================================================== SETVARS PUSH R0 ;PUSH stack here PUSH R1 PUSH R2 PUSH R3 ; ZERO R0 ;Set VCOUNT to zero WR VCOUNT,R0 ; ; Now set ALL four VAR?TXT pointers to start of BUFFER1 ; ; LD R2,BUFFER1 ;This should not be required! ; LD R3,VAR1TXT ;Pointer R3 set to address of first variable WRI R3,R2 ;VAR1TXT will allways be the start of buffer WRI R3,R2 ;VAR2TXT initially points to start of buffer WRI R3,R2 ;VAR2TXT initially points to start of buffer WRI R3,R2 ;VAR2TXT initially points to start of buffer LD R3,VAR2TXT ;Pointer R3 set to address of second variable ; RD R2,R1 ;Read first two chars CMP R1,$0000 ;Null string? JEQ :EXIT ;..Yes? Then nothing to do so exit INC R0 ;Not null? Then VCOUNT now equal to "One" WR VCOUNT,R0 ;Write updated VCOUNT ; :LOOP RDI R2,R0 ;Read two chars from 8 bit buffer into R0 SWP R0 ;Swap R0 to process R1-High byte LD R1,$00FF AND R1,R0 CMP R1,$0000 ;Have we reached the end of string Null? JEQ :EXIT CMP R1,$0024 ;Have we found a field Unit Separator? JNE :CONT1 ;No? Carry on and process Low Byte LD R1,$FF00 ;Yes? Mask for high byte AND R0,R1 ;Retain R0-High char but Null terminate R0-Low SWP R0 ;Swap bytes back to correct order JMP :SETPTR ; :CONT1 SWP R0 ;Swap R0 again to process R1-Low byte LD R1,$00FF AND R1,R0 CMP R1,$0000 ;Have we reached the end of string Null? JEQ :EXIT CMP R1,$0024 ;Have we found a field Unit Separator? JNE :LOOP ;No? Loop again. Go get next two chars LD R1,$FF00 ;Yes? Mask for upper byte AND R0,R1 ;Retain R0-High char Null terminate R0-Low ; :SETPTR DEC R2 ;Move 8 bit buffer pointer back to original word WRI R2,R0 ;Write back R0 containing the $1F to $00 conversion ;This could result in $001F in 8 bit buffer ;Is this a problem?? RD VCOUNT,R0 ;Read current value of VCOUNT CMP R0,$04 ;All four pointers already filled? JEQ :LOOP ;..Yes? Then loop. Keep processing string to end. INC R0 ;No? Then increment the variable count WR VCOUNT,R0 ;Write the new VCOUNT value WRI R3,R2 ;Set next VAR?TXT pointer JMP :LOOP ;Keep processing all fields even in excess of the ;four max. As we need to null terminate each field. ;Required for field #4 if additional fields supplied ;Routine will exit at the null string termination ; :EXIT POP R3 ;POP stack here POP R2 POP R1 POP R0 RET ; ; ;============================================================================== ; Print String Routine ; Entry: R2 -> First char of 8 bit packed Null terminated String ;============================================================================== ; PRINT PUSH R0 PUSH R1 PUSH R2 PUSH R3 ; ; LD R6,PIO ;For testing only display chars on PIO ; LD R0,$0000 ; WR R6,R0 ;Clear LEDs from previous display ; :LOOP RDI R2,R0 SWP R0 LD R1,$00FF AND R1,R0 DEC R1 SF JEQ :EXIT ; WR R6,R0 ;Display char on PIO LEDs CALL SIOTX ;TX High Byte char in R0 ; SWP R0 LD R1,$00FF AND R1,R0 DEC R1 SF JEQ :EXIT ; WR R6,R0 ;Display char on PIO LEDs CALL SIOTX ;TX Low Byte char in R0 ; JMP :LOOP ; :EXIT POP R3 POP R2 POP R1 POP R0 RET ; ;============================================================================== ; Print 16 String Routine - ignores Nulls displays exactly 16 bytes ; Entry: R2 -> First char of 8 bit packed Null terminated String ;============================================================================== ; PRINT16 PUSH R0 PUSH R1 PUSH R2 PUSH R3 ; LD R3,7 ;Used to count the number of words displayed ; :LOOP RDI R2,R1 SWP R1 LD R0,$00FF AND R0,R1 CALL :DOT ;Convert R0-Low to '.' if non ASCII CALL SIOTX ;TX High Byte char in R0 ; SWP R1 LD R0,$00FF AND R0,R1 CALL :DOT ;Convert R0-Low to '.' if non ASCII CALL SIOTX ;TX Low Byte char in R0 ; DEC R3 SF ;Decrement display count by one JEQ :EXIT ;Have we reached zero? Yes, then exit JMP :LOOP ; ;Convert R0-Low to '.' if non ASCII :DOT SUB $1F,R0 SF ;Is R0 < $1F ? ;.. Subtract R0 from $1F. $1F->R4 working register ;.. R4 destroyed by result but we don't care. ;NB: Subtraction actually 1's complement addition. ; So Carry means borrow -> No underflow ; No Carry -> Underflow MOV R0,'.' IF CY ;.. Yes? Then change R0 to a dot '.' SUB $7E,R0 SF ;Is R0 > $7E ? ;.. Subtract R0 from $7E. $7E->R4 working register ;.. R4 destroyed by result but we don't care. ;NB: Subtraction actually 1's complement addition. ; So Carry means borrow -> No underflow ; No Carry -> Underflow MOV R0,'.' IFN CY ;.. Yes? Then change R0 to a dot '.' RET ; :EXIT POP R3 POP R2 POP R1 POP R0 RET ; ;============================================================================== ;Name : INPUT ;Desc : Input String Routine ;Entry: ;Exit : R2 -> Start of chars typed into BUFFER1 null terminated ; Termination could be ##$00,$0000 or $####,$0000 ; where ## is valid char data but either way the last word ; in the buffer is set to $0000. ;============================================================================== ; INPUT PUSH R0 PUSH R1 PUSH R3 ; LD R2,BUFFER1 ;Load R1 with the addr of BUFFER1 ; :LOOP ;CALL SIORX ;Get character from serial port into R0-Low CALL RINGRD ;Get character from Ring Buffer into R0-Low CALL SIOTX ;Display typed character to terminal SWP R0 ;Byte returned now in R0-high byte MOV R1,R0 ;Char now in R1 high byte ZERO R3 ;Set R3 to $0000 CMP R0,R3 ;Was character returned a null? JEQ :EXIT ;Y? Then call write character to buffer CMP R0,$0800 ;Is the char a Backspace? JEQ :BKSP0 ;Yes jump to backspace routine CMP R2,$FD1E ;Is pointer R2 at second last word in buffer? ;Fixme: Bug This should be BUFFER1+31 ; but PISCASM fails to add +31 here! JEQ :EXIT ;Y? End of buffer reached so bail out ; :LOWCHR ;CALL SIORX ;Get character from serial port into R0-Low CALL RINGRD ;Get character from Ring Buffer into R0-Low CALL SIOTX ;Display typed character to terminal OR R1,R0 ;Place this char in R1-low byte keeping R1-high ZERO R3 ;Set R3 to $0000 CMP R0,R3 ;Was character returned a null? JEQ :EXIT ;Y? Then call write character to buffer ;We have two chars in R1 now write them to buffer CMP R0,$08 ;Is the char a Backspace? JEQ :BKSP1 ;Yes then start again on this word WRI R2,R1 ;Write R1 into R2->memory addr, post INC R2 JMP :LOOP ;Go get another two characters ; :BKSP0 CMP R2,BUFFER1 ;Is R2 -> to start of buffer? JEQ :LOOP ;Yes? Nothing to do jump to loop DEC R2 ;Move R2 buffer pointer back one position RD R2,R1 ;Get last two chars back again AND R1,$FF00 ;Erase low character LD R0,$2008 ;Print + CALL PRTWRD JMP :LOWCHR ;Go process low char again ; :BKSP1 LD R0,$2008 ;Print + CALL PRTWRD JMP :LOOP ;Start this word over again ; :EXIT WRI R2,R1 ;Write R1 into R2->memory addr, post INC R2 ZERO R1 ;Set R1 to $0000 WR R2,R1 ;Make sure the buffer is terminated with $0000 LD R2,BUFFER1 ;R2 now -> Start of null terminated input string POP R3 POP R1 POP R0 RET ; ;============================================================================== ;Name : NEWLN ; Print CR LF to serial port for a new line ; Entry: None ; ; Destroyed: None ;============================================================================== NEWLN PUSH R0 ;Save current R0 LD R0,$0A0D ;CR+LF in hex CALL PRTWRD ;Print it POP R0 ;Restore R0 RET ; ;============================================================================== ;Name : PRTWRD ; Print 16 Bit Word as two characters ASCII ; Entry: R0 = Binary data to print ; ; Destroyed: None ;============================================================================== PRTWRD SWP R0 ;Swap high-byte into low-byte CALL SIOTX SWP R0 ;Swap back again CALL SIOTX RET ; ;============================================================================== ;Name : INKEY - Read and return character from Ring Buffer if available ; return Null if none available. ; ;Desc : Read character from SIO Ring Buffer if one exists. Return Null ; : Otherwise. Does NOT wait for character. Returns Null if none available ; : Calls RINGRD to collect character if after checking one exists in ; : buffer ;Entry: ;Exit : R0-Low = Single character from SIO Ring Buffer ; : CR char ASCII 13 returned as NULL ASCII 0 ;============================================================================== ; INKEY PUSH R1 PUSH R2 PUSH R3 ; ZERO R0 ;Make sure R0 is Null LD R2,RBWPTR ;Ring Buffer Write Pointer RDI R2,R1 ;R1 -> Write Head of ring buffer RD R2,R3 ;R3 -> Read Tail of ring buffer CMP R3,R1 ;Does the Tail = Head? JEQ :EXIT ;Yes? Buffer empty. Just return. CALL RINGRD ;Character available call Ring Buffer Read ; :EXIT POP R3 ;Clean up and exit POP R2 POP R1 RET ; ;============================================================================== ;Name : RINGRD - Waits for a character and returns it ; ;Desc : Read character from SIO Ring Buffer ;Entry: ;Exit : R0-Low = Single character from SIO Ring Buffer ; : CR char ASCII 13 returned as NULL ASCII 0 ;============================================================================== ; RINGRD PUSH R1 PUSH R2 PUSH R3 ; LD R2,RBWPTR ;Ring Buffer Write Pointer RDI R2,R1 ;R1 -> Write Head of ring buffer RD R2,R3 ;R3 -> Read Tail of ring buffer CMP R3,R1 ;Does the Tail = Head? JNE :CONT ;No? Buffer not empty. Carry on. LD R1,RBOVFL ;Check for any Ring Buffer overflow RD R1,R0 ;Overflow Flag and Char (if any) in R0 AND R0,$FF00 ;Mask for Flag in high byte only CMP R0,$0F00 ;Overflow marked? JNE :WAIT ;No? No Overflow condition. Go wait for Char. RD R1,R0 ;Get Mask and Char back in R0 again WR R1,$0000 ;Clear both Mask and Char for next time CALL :CKCHR ;Check Char for CALL RTSON ;Turn on CTS to PC so Chars can flow again JMP :EXIT ;All Done. Jump to exit. Char in R0. ; :WAIT LD R2,RBWPTR ;Ring Buffer Write Pointer RDI R2,R1 ;R1 -> Write Head of ring buffer RD R2,R3 ;R3 -> Read Tail of ring buffer CMP R3,R1 ;Does the Tail = Head? JEQ :WAIT ;Loop around until Char arrives ;Warning this could hang forever! ; :CONT RD R3,R0 ;Ring Buffer Tail Char now in R0. CALL :CKCHR ;Check Char for INC R3 ;Move Read Tail forward one word AND R3,$3F ;Constrain Buffer to 64 words cyclic ADD R3,RINGB ;Add back the Ring Buffer base address WR R2,R3 ;Write new Read Tail Pointer to RBRPTR JMP :EXIT ;All Done. Jump to exit. Char in R0. ; :CKCHR LD R1,$00FF ;Mask for low byte only AND R0,R1 ;Only keep R0-low CMP R0,$0D ;Is char a CR (ASCII 13) key? MOV R0,$00 IF EQ ;Yes? Then change it to a NULL RET ; ; We should test if character is visible here before we echo ; But for the moment we'll just display it regardless ; :EXIT ;CALL SIOTX ;Display typed character to terminal POP R3 ;Clean up and exit POP R2 POP R1 RET ; ;============================================================================== ;Name : SIORX ;Desc : Get character from serial port ;Entry: ;Exit : R0-Low = Single character from serial port ; : CR char ASCII 13 returned as NULL ASCII 0 ;============================================================================== ; SIORX PUSH R1 PUSH R2 PUSH R3 ; RD SIOSTA,R1 ;Check Status Register LD R2,%00000010 ;Mask for RxRDY :LOOP RD R1,R3 ;Read Status AND R3,R2 ;Only RX ready status remains CMP R3,R2 ;Are they equal? JNE :LOOP ;No? Then RX char not available. Loop until ready ;Warning! This could hang forever ; RD SIODAT,R1 ;2651 Data Register RD R1,R0 ;RX Char in R0 LD R1,$00FF ;Mask for low byte only AND R0,R1 ;Only keep R0-low CMP R0,$0D ;Is char a CR (ASCII 13) key? MOV R0,$00 IF EQ ;Yes? Then change it to a NULL ; ; We should test if character is visible here before we echo ; But for the moment we'll just display it regardless ; CALL SIOTX ;Display typed character to terminal ; POP R3 ;Clean up and exit POP R2 POP R1 RET ; ;============================================================================== ;Name : SIOTX ; Serial 2651 TX Character Routine ; Entry: R0 = Char to TX ;============================================================================== ; SIOTX PUSH R1 PUSH R2 PUSH R3 ; RD SIOSTA,R1 ;Check Status Register LD R2,%00000001 ;Mask for TX Register holding buffer :LOOP RD R1,R3 ;Read Status AND R3,R2 ;Only TX buffer status remains CMP R3,R2 ;Are they equal? JNE :LOOP ;No? Then TX is busy. Loop until ready ;Warning! This could hang forever ; RD SIODAT,R1 ;2651 Data Register WR R1,R0 ;TX Char ; POP R3 ;We're done. Clean up stack POP R2 ;and Return POP R1 RET ; ;============================================================================== ;Name : SIOINIT ; Initialize the 2651 Serial UART ; Entry: None ; Exit: None ; ; Destroyed: R0, R1 ;============================================================================== ; SIOINIT RD SIOCMD,R0 ;Need to start by reading the Command Register RD R0,R1 ;to reset the internal 2651 state. ;Should really stop the TX & RX at this point RD SIOMOD,R0 LD R1,SIOMR1 WR R0,R1 ;Write Mode Register 1 ; LD R1,SIOMR2 WR R0,R1 ;Write Mode Register 2 ; RD SIOCMD,R0 LD R1,SIOCR WR R0,R1 ;Write Command Register ; ; 2651 should now be good to go! RET ; ;============================================================================== ;Name : RTSOFF ; Set Request To Send (RTS) used for hardware handshake with PC to OFF. ; This will disable the Clear To Send signal to the PC halting data ; transfer. ; ; Entry: None ; Exit: None ; ; Destroyed: R0, R1 ;============================================================================== RTSOFF PUSH R0 ;Save registers PUSH R1 ; RD SIOCMD,R0 RD R0,R1 ;Read current Command Register settings AND R1,%11011111 ;RTS Mask for bit 5 WR R0,R1 ;Write Command Register - Turn OFF RTS ; POP R1 ;Clean up stack and exit POP R0 RET ; ;============================================================================== ;Name : RTSON ; Set Request To Send (RTS) used for hardware handshake with PC to ON. ; This will enable the Clear To Send signal to the PC starting data ; transfer again. ; ; Entry: None ; Exit: None ; ; Destroyed: None ;============================================================================== RTSON PUSH R0 ;Save registers PUSH R1 ; RD SIOCMD,R0 RD R0,R1 ;Read current Command Register settings OR R1,%00100000 ;RTS Mask for bit 5 WR R0,R1 ;Write Command Register - Turn ON RTS ; POP R1 ;Clean up stack and exit POP R0 RET ; ;============================================================================== ;Name : Copyright Message ; Displays a copyright message. ; This is really just for testing the Jump command. ; Entry: None ; Exit: None ; ; Destroyed: R0, R1 ;============================================================================== ; PUSH R2 LD R2,MSG2 CALL PRINT POP R2 RET ; ;============================================================================== ;Name : DSPSGN & DSPCUR ; Display 16 bit signed number in ASCII ; Entry: R0 = 16 bit value to display ; Exit: ; ; Destroyed: R0 ;============================================================================== ; DSPSGN PUSH R1 ; LD R1,%1000000000000000 AND R1,R0 CMP R1,$0000 JEQ :DISP ;Positive number so just display it ; PUSH R0 ;Save R0 MOV R0,'-' ;Display negative sign LD R1,0 ;Function 0 = SIOTX routine CALL BIOS ;Call into the PISC BIOS POP R0 ;Restore R0 ; NOT R0 ;Convert number to positive INC R0 ;by two's complement inversion, plus 1 ; :DISP CALL DSPNUM ; POP R1 RET ; ;============================================================================== ;Name : DSPNUM ; Display 16 bit unsigned number in ASCII ; Entry: R0 = 16 bit value to display ; Exit: ; ; Destroyed: R0 ;============================================================================== DSPNUM PUSH R1 PUSH R2 PUSH R3 ; ZERO R3 CMP R0,R3 ;Is the value to display zero? JNE :START ;No? Then start display routine LD R0,'0' ;Yes? Then just display a zero CALL SIOTX ;Display zero JMP :QUIT ;Jump to quit ; :START MOV R2,R0 LD R1,-10000 CALL :NUM1 LD R1,-1000 CALL :NUM1 LD R1,-100 CALL :NUM1 LD R1,-10 CALL :NUM1 LD R1,-1 CALL :NUM1 ; :QUIT POP R3 POP R2 POP R1 RET ; :NUM1 LD R0,-1 :NUM2 INC R0 ADD R2,R1 SF JCY :NUM2 SUB R2,R1 ADD R3,R0 CMP R3,$0000 JEQ :EXIT ADD R0,$30 LD R1,0 ;Function 0 = SIOTX routine CALL BIOS ;Call into the PISC BIOS :EXIT RET ; ;============================================================================== ;Name : DSPHEX ; Display 16 bit value as 4 digit Hexidecimal number ; Entry: R0 = 16 bit value to display ; Exit: ; ; Destroyed: ;============================================================================== ; DSPHEX PUSH R2 ;Save R2 CALL WRD2HEX ;Convert R0 value to 4 digit Hexidecimal string CALL PRINT ;Print value in buffer POP R2 ;Restore R2 RET ; ; ;============================================================================== ;Name : DEC2BN ; Convert ASCII string to a 16 bit signed number ; Entry: R2 -> packed ASCII string null terminated ; Exit: R0 = 16 bit returned value ; ; Destroyed: ;============================================================================== DEC2BN PUSH R1 ;Save registers PUSH R3 ; LD R1,8 ;Fn=8 Expand 8 bit ASCII packed string to a string of words LD R3,BUFFER2 ;Address to use for expanded buffer CALL BIOS ;Call into the PISC BIOS MOV R2,R3 ;Move R3 -> 16 bit unpacked string of words into R2 ; ZERO R0 ;Used to store return value ZERO R1 ;Set R1 flag to $0000, used to track sign of value ; RD R2,R3 ;First character word in R3 ;Check for zero here? CMP R3,'-' ;Is it a negative sign? JNE :PLUS ;No? Then it's positive HIGH R1 ;Set R1 flag to $FFFF, this is a negative number JMP :CNVERT ;Start conversion ; :PLUS CMP R3,'+' ;Is the first char a positive sign? JNE :CHKDIG ;No? The start conversion with 1st digit ; :CNVERT INC R2 ;Move to next digit for conversion RD R2,R3 ;Get next character word CMP R3,$0000 ;Have we reached the end? JEQ :SIGN ;Yes? then deal with the sign :CHKDIG SUB R3,'0' SF ;Delete ASCII offset JNC :ERROR ;Error if < 0 (not a digit) CMP R3,9+1 JCY :ERROR ;Error if > 9 (not a digit) ; ; The word character is a digit so save it ; SHL R0 ;R0 now *2 PUSH R0 ;Save *2 value SHL R0 ;R0 now *4 SHL R0 ;R0 now *8 POP R4 ;Get the *2 back into R4 ADD R0,R4 ;R0 is now *10 ADD R0,R3 ;Now add the new digit value JMP :CNVERT ;Continue conversion with next char ; :ERROR ZERO R0 ;Clear Ro back to Zero SNE ;Set NEQ flag to indicate error JMP :EXIT ;Jump to exit ; :SIGN TST R1 ;Was the sign negative? JNE :EXIT ;No? Then exit ZERO R4 ;Negative sign.. SUB R4,R0 ;So subtract from zero MOV R0,R4 ;Move corrected negative value back to R0 SEQ ;Set EQ flag to indicate successful conversion ; :EXIT POP R3 ;Restore registers POP R1 RET ; ;============================================================================== ; Program Data ;============================================================================== ; MSG1 DB $0A,$0D,"PISC Monitor v1.0 b8",$0A,$0D,$00 MSG2 DB "Copyright 2019 Stephen Arnold",$0A,$0D,$00 MSG3 DB "*** FAILSAFE BOOT ***",$0A,$0D,$00 MSGERR DB "Syntax Error.",$0A,$0D,$00 IRQERR DB $0A,$0D,"Unhandled IRQ from: ",$00 IRQM1 DB $0A,$0D,"2651 IRQ Char = ",$00 MSGDU DB "Dump ",$0A,$0D,$00 MSGRD DB "Read ",$0A,$0D,$00 MSGEN DB "Enter",$0A,$0D,$00 MSGWR DB "Write",$0A,$0D,$00 MSGFL DB "Fill ",$0A,$0D,$00 MSGCP DB "Copy ",$0A,$0D,$00 MSGJP DB "Jump ", MSGMBK DB "Mem Bank ",$0A,$0D,$00 MSGHEX DB "Hex Transfer",$0A,$0D,$00 CRCERR DB "CRC Error",$0A,$0D,$00 XERROR DB "Record Error",$0A,$0D,$00 XTRANS DB "Transfer ",$00 XOK DB "Successful",$0A,$0D,$00 XFAIL DB "Failed",$0A,$0D,$00 MSGBRK DB "Break @",$00 MSGBK2 DB "Flags ",$00 MSGBKH DB $0A,$0D,"Register Values:',$0A,$0D,$00 BRKERR DB "No breakpoint set.",$0A,$0D,$00 MSGHLP DB $0A,$0D,"Available Commands: ",$0A,$0D DB "(D)ump (W)rite (R)ead ",$0A,$0D DB "(E)nter (F)ill (C)opy ",$0A,$0D DB "(J)ump (M)emory ",$0A,$0D DB "(B)reakpoint (K)ontinue (S)tep ",$0A,$0D DB $0A,$0D,$00 ; ; END ;Assembeller should stop parsing here. ;So you should not see this line in listing output!