Hexadecimal Output on the 6809,
without General Divide
(Register Parameters)
We've done
the 6801 version of simple hexadecimal output. It's time for the 6809 version.
If you've skipped the previous two chapters and this code doesn't make sense,
go back and look at
the 6800 version
for the basic math.
Anyway, don't forget to compare the code here with the previous two. Most
(all?) of the changes from the 6800/6801 are addressing and addressing
related, since we don't have to thrash the X register to get to parameters and
such.
* simple 8-bit hexadecimal output for 6809
* using parameter stack,
* with test frame
* Joel Matthew Rees, October 2024
*
OPT 6809
INCLUDE rt_rig01_6809.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
STD ,--U
LBSR OUTC
RTS
*
* Output an 8-bit byte in hexadecimal,
* byte as a 16-bit parameter on PSP.
OUTHX8 LDB 1,U ; get the byte
LSRB
LSRB
LSRB
LSRB
BSR OUTH4
LDB 1,U
BSR OUTH4
LEAU NATWID,U
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 LEAX HEADLN,PCR
PSHU X
LBSR OUTS
LDD #$5A ; byte to output
PSHU D
LBSR OUTB8
LEAX CHEX,PCR
PSHU X
LBSR OUTS
LDD #$5A ; byte to output
PSHU D
LBSR OUTHX8
LBSR OUTNWLN
RTS
*
END ENTRY
And here's the framework rigging with the 6809 bit output routines, again,
slightly optimized (;-):
* A simple run-time framework inclusion for 6809
* 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
*
*
* Essential monitor ROM routines
XOUTCH EQU $F018
*
NATWID EQU 2 ; 2 bytes in the CPU's natural integer
*
*
ORG $2000 ; MDOS says this is a good place for usr stuff
SETDP $20 ; some other assemblers
* SETDP $2000 ; EXORsim
LOCBAS EQU * ; here pointer, local static base starts here.
ENTRY JMP START
NOP
SSAVE RMB 2 ; a place to keep S so we can return clean
DPSAVE RMB 2 ; a place to keep DP so we can return clean
* room for something
* Not much here
*
SETDP 0 ; Not yet set up
ORG $2100 ; Give the DP room.
RMB 2 ; a little bumper space
SSTKLIM RMB 32 ; 16 levels of call, max
* ; 6809 is pre-dec (pre-store-decrement) push
SSTKBAS 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 TFR DP,A
CLRB
TFR D,Y ; save old DP base for a moment
LEAX LOCBAS,PCR ; Set up new DP base
TFR X,D
TFR A,DP ; Now we can access DP variables correctly.
SETDP $20 ; some other assemblers
* SETDP $2000 ; EXORsim
STY <DPSAVE ; technically only need to save high byte
LEAU PSTKBAS,PCR ; Set up the parameter stack
PULS X ; get return address
STS <SSAVE ; Save what the monitor gave us.
LEAS SSTKBAS,PCR ; Move to our own stack
JMP ,X ; return via X
*
* Output the 8-bit binary (base two) number on the stack.
* For consistency, we are passing the byte in the low-order byte
* of a 16-bit word.
OUTB8 LDB #8 ; 8 bits
OUTB8L LSL 1,U ; Get the leftmost bit of the lower byte.
BCS OUTB81
OUTB80 LDA #'0
BRA OUTB8B
OUTB81 LDA #'1
OUTB8B JSR OUTCV
DECB
BNE OUTB8L ; loop if not Zero
LEAU NATWID,U ; drop parameter bytes
RTS
*
OUTNWLN LDA #CR ; driver level code to output a new line
BSR OUTCV
LDA #LF
BSR OUTCV
RTS
*
* No need for PPOPD, PPUSHD, PPOPX, or PPUSHX in 6809 code.
*
* We can handle the parameter stack directly.
* Preserves B.
OUTC LDA 1,U ; Get the character in A where XOUTCH wants it.
LEAU NATWID,U ; Got it in A, now drop it from the stack.
BSR OUTCV ; output via monitor ROM
RTS
*
OUTCV JMP XOUTCH ; driver code for outputting a character
*
* And we can handle the parameter stack directly here, too.
OUTS PULU X ; get the string pointer
OUTSL LDA ,X+ ; get the byte, update the pointer
BEQ OUTDN ; if NUL, leave
BSR OUTCV ; use the same call OUTC uses.
BRA OUTSL ; next character
OUTDN RTS
*
*
******************************
* intermediate-level library:
*
* We often will not need these, but we'll go ahead and define them:
*
* input parameters:
* 16-bit left, right
* output parameter:
* 16-bit sum
ADD16 LDD NATWID,U ; left
ADDD ,U++ ; right
STD ,U ; sum (N, Z, & C flags should be correct)
RTS
* Flags: Specifically,
* N and Z get set correctly by the final store double;
* C should make it through manipulating X and storing D.
* V gets cleared.
*
* input parameters:
* 16-bit left, right
* output parameter:
* 16-bit difference
SUB16 LDD NATWID,U ; left
SUBD ,U++ ; right
STD ,U ; difference (N, Z, & C flags should be correct)
RTS
* Flags: Specifically,
* N and Z get set correctly by the final store double;
* C should make it through manipulating X and storing D.
* V gets cleared.
*
*
************************************
* Start run-time, call program.
* Expects program to define PGSTRT:
*
START LBSR INITRT
*
LBSR PGSTRT
*
DONE LDS <SSAVE ; restore the monitor stack pointer
LDD <DPSAVE ; restore the monitor DP
TFR A,DP
SETDP 0 ; For lack of a better way to set it.
NOP ; remember to set a breakpoint here!
NOP ; landing pad
NOP
NOP
JMP [$FFFE] ; alternatively, jmp through reset vector
*
* 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 with lwasm:
lwasm --format=srec -o outhx8_6809.s19 --map=outhx8_6809.map --list=outhx8_6809.list -s outhx8_6809.asm
Remember that you'll need to return to straight EXORsim, but invoke exor09 instead of exor:
./exor09 --mon
Remember to remove the S0 and S5 lines from the srec binary (.s19) file before copying and pasting the object in at the (l)oad command.
And I would encourage you again to try moving the hexadecimal output code for the 6809 to the rigging yourself. As I say, I'll show you my work later, but give it a try.
When you're done with that, let's talk about how I have two entry points for the character output function.
We're using the parameter stack as a standard place to pass parameters, mostly. Outside this file, we're going to use the parameter stack to pass parameters to the character output function, so we're going to use that entry point, OUTC.
Inside this file, at least in nearby functions where we can scroll down or up a bit and see what's happening, we can use the other entry point, OUTCV, that takes the character to be output from the accumulator. That allows us to eliminate a push and a pop.
Even in this file, if we get too far away, we want to use the primary entry point. Otherwise, we might not notice what needs to change if OUTCV or the code it calls changes.
Also, we've taken the liberty of assuming that B will be preserved here, where in functions that are not so close we probably do not want to do that -- at least, not without some promises.
We need to talk more about register allocation, but it will be easier to talk about that in the hexadecimal routines for the 68000.
No comments:
Post a Comment