Friday, October 11, 2024

ALPP 03-07 -- Hexadecimal Output on the 6809, without General Divide (Register Parameters)

Hexadecimal Output on the 6809,
without General Divide
(Register Parameters)

(Title Page/Index)

 

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.


(Title Page/Index)

 

 

 

 

 

 

No comments:

Post a Comment