Hexadecimal Output on the 6801,
without General Divide
In the last chapter, I discussed the theory behind hexadecimal output and implemented it for you on the 6800.
There's very little to change from the 6800 code, and I can't think of anything to discuss, so I'll just leave code here for you to paste in and test.
Here's the hexadecimal output code and test routine:
* simple 8-bit hexadecimal output for 6801
* using parameter stack,
* with test frame
* Joel Matthew Rees, October 2024
*
OPT 6801
EXP rt_rig01_6801.asm
****************
* Program code:
*
ASC0 EQU '0 ; Some assemblers won't handle 'c constants well.
ASC9 EQU '9
ASCA EQU 'A
ASCXGAP EQU ASCA-ASC9-1 ; Gap between '9' and 'A' for hexadecimal
*
* Mask off and convert the nybble in B to ASCII numeric,
* including hexadecimals
OUTH4 ANDB #$0F ; mask it off
ADDB #ASC0 ; Add the ASCII for '0'
CMPB #ASC9 ; Greater than '9'?
BLS OUTH4D ; no, output as is.
ADDB #ASCXGAP ; Adjust it to 'A' - 'F'
OUTH4D CLRA
JSR PPSHD
JSR OUTC
RTS
*
* Output an 8-bit byte in hexadecimal,
* byte as a 16-bit parameter on PSP.
OUTHX8 LDX PSP
LDAB 1,X ; get the byte
LSRB
LSRB
LSRB
LSRB
BSR OUTH4
LDX PSP
LDAB 1,X
BSR OUTH4
LDX PSP
INX
INX
STX PSP
RTS
*
HEADLN FCB CR,LF ; Put message at beginning of line
FCC "Outputting $5A in binary and hex: " ;
FCB CR,LF,NUL ; Put the binary output on a new line
CHEX FCC ": $"
FCB NUL
*
*
*
*
PGSTRT LDX #HEADLN
JSR PPSHX
JSR OUTS
LDD #$5A ; byte to output
JSR PPSHD
JSR OUTB8
LDX #CHEX
JSR PPSHX
JSR OUTS
LDD #$5A ; byte to output
JSR PPSHD
JSR OUTHX8
JSR OUTNWLN
RTS
*
END ENTRY
And here's the framework rigging -- bringing in the 6801 bit output routines, slightly optimized --
Optimized.
You may have missed it in the 6800 code while were were focused on shifting bits, but now you might have noticed. And realized I've already been playing a similar game with X. Isn't that cheating?
I don't want to talk about it.
Not here, not in code where the way we're thrashing X gets in the way of seeing what we're doing with X. Let's look at this again in the 6809 code.
* A simple run-time framework inclusion for 6801
* providing parameter stack and local base
* Version 00.00.01
* Joel Matthew Rees, October 2024
*
* Essential control codes
LF EQU $0A ; line feed
CR EQU $0D ; carriage return
NUL EQU 0
*
SCRLIN EQU $40 ; More than a screen full of lines.
*
* Essential monitor ROM routines
XOUTCH EQU $F018
*
NATWID EQU 2 ; 2 bytes in the CPU's natural integer
*
*
ORG $80 ; MDOS and EXbug docs say it should be okay here.
ENTRY JMP START
NOP ; Just want even addressed pointers for no reason.
*
* These are the page zero context variables that must be
* saved and restored on process context switch.
* They must never be accessed except in leaf routines:
PSP RMB 2 ; parameter stack pointer
LBP RMB 2 ; local static variable base pointer
XWORK RMB 2 ; for stashing X during other very short operations
*
SSAVE RMB 2 ; a place to keep S so we can return clean
* End of page zero context variables.
*
*
ORG $2000 ; MDOS says this is a good place for usr stuff
LOCBAS EQU * ; here pointer, local static base starts here.
NOENTRY JMP START
NOP
RMB 64 ; room for something
RMB 2 ; a little bumper space
* Not much here
*
SSTKLIM RMB 31 ; 16 levels of call, max
SSTKBAS RMB 1 ; 6800 is post-dec (post-store-decrement) push
RMB 2 ; a little bumper space
PSTKLIM RMB 64 ; 16 levels of call at two parameters per call
PSTKBAS RMB 2 ; bumper space -- parameter stack is pre-dec
*
*
INITRT LDX #PSTKBAS ; Set up the run-time environment
STX PSP
LDX #LOCBAS
STX LBP
PULX ; pop return address to X
STS SSAVE ; Save what the monitor gave us.
LDS #SSTKBAS ; Move to our own stack
JMP 0,X ; return via X
*
*
*********************
* Low-level library:
*
* Only alters X
PPOPX LDX PSP
LDX 0,X ; get value for X
PSHX ; push it out of the way
LDX PSP
INX
INX
STX PSP
PULX ; and bring it back.
RTS
*
* Trashes A,B;
* X points to X value just pushed -- PSP top of stack -- at end
PPSHX PSHX ; push X out of the way
PULA
PULB ; Falls through:
*
* X points to PSP top of stack at end
PPSHD LDX PSP
DEX
DEX
STX PSP
STD 0,X
RTS
*
* X points to PSP top of stack at end
PPOPD LDX PSP
LDD 0,X
INX
INX
STX PSP
RTS
*
* Keep this around in comments,
* for reference for when we want to referance tables in code.
* Load a constant from the instruction stream into A:B,
* continue execution after the constant.
* This is not self-modifying code, even though it feels like a trick
* and is playing with the return stack and instruction stream
* in ways we wouldn't think we wanted to think we should.
* Call it a "necessary" bit of run-time syntactic sugar.
*
* Use it like this:
* JSR LD16I ; load D immediate
* FDB $1234 ; "immediate" 16-bit value to load
* JSR SOMEWHERE ; or some other executable code.
*
*LD16I TSX ; point to top of return address stack
* LDX 0,X ; point into the instruction stream
* LDAA 0,X ; high byte from instruction stream
* LDAB 1,X ; low byte from instruction stream
* INS ; drop the return address we don't need
* INS
* JMP 2,X ; return to the byte after the constant.
*
* Output the 8-bit number on the stack in binary (base two).
* For consistency, we are passing the byte as the low-order byte
* of a 16-bit word.
OUTB8 LDX PSP ; parameter is at 0,X (low byte at 1,X)
LDAB #8 ; 8 bits (B is preserved in BIOS
OUTB8L LSL 1,X ; Get the leftmost bit.
BCS OUTB81
OUTB80 LDAA #'0
BRA OUTB8B
OUTB81 LDAA #'1
OUTB8B JSR OUTCV
DECB ; count
BNE OUTB8L ; loop if not Zero
INX ; drop parameter bytes
INX
STX PSP
RTS
*
OUTNWLN LDAA #CR ; driver level code to output a new line
BSR OUTCV
LDAA #LF
BSR OUTCV
RTS
*
OUTC JSR PPOPD ; get the character in B
TBA ; put it where XOUTCH wants it.
BSR OUTCV ; output A via monitor ROM
RTS
*
OUTCV JMP XOUTCH ; driver code for outputting a character
*
OUTS JSR PPOPX ; get the string pointer
OUTSL LDAA 0,X ; get the byte out there
BEQ OUTDN ; if NUL, leave
BSR OUTCV ; use the same call OUTC uses.
INX ; point to the next
BRA OUTSL ; next character
OUTDN RTS
*
WAKETRM LDAB #SCRLIN
WAKETML JSR OUTNWLN
DECB
BNE WAKETML
RTS
*
******************************
* intermediate-level library:
*
* input parameters:
* 16-bit left, right
* output parameter:
* 16-bit sum
ADD16 LDX PSP
LDD 2,X ; left
ADDD 0,X ; right
STD 2,X ; sum
INX ; adjust parameter stack
INX
STX PSP
RTS
*
* input parameters:
* 16-bit left, right
* output parameter:
* 16-bit difference
SUB16 LDX PSP
LDD 2,X ; left
SUBD 0,X ; right
STD 2,X ; difference
INX ; adjust parameter stack
INX
STX PSP
RTS
*
*
************************************
* Start run-time, call program.
* Expects program to define PGSTRT:
*
START JSR INITRT
JSR WAKETRM
*
JSR PGSTRT
*
DONE LDS SSAVE ; restore the monitor stack pointer
NOP ; remember to set a breakpoint here!
NOP ; landing pad
NOP
NOP
LDX $FFFE ; alternatively, get reset vector
JMP 0,X ; and reboot through it
*
* Anyway, if running in EXORsim,
* Ctrl-C should bring you back to EXORsim monitor,
* but not necessarily to your program in a runnable state.
Look through the code, compare it with the 6800 code, save both files with
appropriate names, and assemble it as you did the previous with asm68c:
asm68c -l2 outhx8_6801.asm >outhx8_6801.list
Remember that you'll need to use EXORsim6801 instead of straight EXORsim, and invoke it with the --6801 option:.
./exor --6801 --mon
Again, try to move the hexadecimal output to the rigging and test it before you move on to the 6809 code. If you can get it to work, great. If you can't, I'll have it in place when we start working on character input, so you can grab it from there. But do give it a try.
When you're done, let's move on to
the hexadecimal routines for the 6809.
No comments:
Post a Comment