Tuesday, October 8, 2024

ALPP 03-03 -- Binary Output on the 6809, Left-to-right; Framework-by-include (LWTools)

Binary Output on the 6809,
Left-to-right;
Framework-by-include
(LWTools)

(Title Page/Index)

 

We've done binary output on the 6801, so let's do it on the 6809. Sure it's getting monotonous, but that's what it takes to understand the differences between the processors. More to the point, I don't know any other way to explain the discipline I'm using, except in steps.

The binary output routines in their less-optimized form don't make a lot of obvious use of the 6809's unique features; at least, they don't stand out until you consider how things are being done directly by the 6809's native instructions rather than subroutines:
* simple 8-bit binary output for 6809
* using parameter stack,
* with test frame
* Joel Matthew Rees, October 2024
*
	OPT	6809
	INCLUDE	rt_rig6809.asm
****************
* Program code:
*
* Output a 0
OUT0	LDB	#'0
OUT01	PSHU	D
	LBSR	OUTC
	RTS
*
* Output a 1 
OUT1	LDB	#'1
	BRA	OUT01
* Rob code, shave a couple of bytes, waste a few cycles.
*
* 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
	STB	0,U	; Borrow the upper byte of the parameter.
OUTB8L	LSL	1,U	; Get the leftmost bit of the lower byte.
	BCS	OUTB81
OUTB80	BSR	OUT0
	BRA	OUTB8D
OUTB81	BSR	OUT1
OUTB8D	DEC	,U
	BNE	OUTB8L	; loop if not Zero
	LEAU	2,U	; drop parameter bytes
	RTS
*
HEADLN	FCB	CR,LF	; Put message at beginning of line
	FCC	"Outputting $5A in binary:"	; 
	FCB	CR,LF,NUL	; Put the binary output on a new line
*
*
*
*
PGSTRT	LEAX	HEADLN,PCR
	PSHU	X
	LBSR	OUTS
	LDD	#$5A	; byte to output
	PSHU	D
	LBSR	OUTB8
	LBSR	OUTNWLN
	RTS
*
	END	ENTRY

You'll want to save that as something like

outb8_6809.asm

You'll note that the inclusion directive is now INCLUDE, not EXP. We won't be using asm68c on the 6809 source. I never got that far, and Lost Wizard does a really good job with LWTools, anyway. 

(William Astle kept OPT as a synonym for PRAGMA, to help compatibility with other assemblers.)

As was somewhat the case with the 6801, the framework rigging makes much more clear use of the 6809's features:

* A simple run-time framework inclusion for 6809
* providing parameter stack and local base
* Version 00.00.00
* 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	31	; 16 levels of call, max
*			; 6809 is pre-dec (decrement-before-store) 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
*
*
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	2,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	2,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	2,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.

Save this in the same directory as

rt_rig6809.asm

since that's what the inclusion directive says it is.

Since asm68c won't assemble that, we'll need something else. I recommend William Astle's very professional LWTools, as I mentioned above, found here:

http://www.lwtools.ca/

Instructions to build it are on that page, and there are links to the manual as well.

Building it is straightforward, and it supports standard techniques for changing the install directory. I was able to tell it to install the executables to my user-local executables directory without fuss.

When you get through the install with no errors, you should be able to give it the command

$ lwasm --help

and it should list out various ways to call it. 

You can use the command line

$ lwasm outb8_6809.asm

to assemble the code, and the object code, if there were no errors in copying, will be saved as 

a.out

in a format that EXORsim doesn't recognize. And you won't get a listing. To get s1-s9 (srec) object code and listing to the terminal, use

$ lwasm --format=srec -o outb8_6809.s19 --list outb8_6809.asm

I find it useful to put the symbol table in the listing with the "-s" option and save the listing to a file, so I use the command line

$ lwasm --format=srec -o outb8_6809.s19 --list=outb8_6809.list -s outb8_6809.asm

If you get a clean assembly, you can open the object file 

outb8_6809.s19

in a text editor and select ...

Okay, this is a little tricky. EXORsim doesn't accept the S0 header record (first line) or the S5 count record (second to last line), so we have to delete those lines before we can select all of the rest to paste it in. 

No big deal, right?

Again, you may have to hit return again at the end, to bring it back to the % prompt:

$ ./exor09 --mon
Load facts file 'facts09'
'exbug09.bin' loaded.
  EXBUG09-2.1 detected
'mdos09.dsk' opened for drive 0 (double sided)

OSLOAD...

Hit Ctrl-C for simulator command line.  Starting simulation...

>         0 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            0020: 86 10        LDA #$10                   

6809 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
% l 
S10720007E21AF1278
S10D21661FB85F1F02308DFE911FA9
S1132170101F8B109F06338CEB351010DF04328C4C
S1132180A06E84860D8D0C860A8D0839A6413342D3
S11321908D01397EF0183710A68027048DF520F8BC
S11321A039EC42E3C1EDC439EC42A3C1EDC43917A3
S11321B0FFB417004E10DE04DC061F8B121212123D
S11321C06E9FFFFEC630360617FFC139C63120F6B2
S11321D0C608E740684125048DEA20028DEE6AC4F2
S11321E026F23342390D0A4F757470757474696E32
S11321F0672024354120696E2062696E6172793AE4
S11322000D0A00308CDF361017FF8BCC005A3606CF
S10A221017FFBD17FF6D3934
S9032000DC
PC set to 2000
181 bytes loaded
No checksum errors.
% 

 Refer to the listing file, look around, step through a bit, set a breakpoint, and run it:

% u 21AF
21AF: 17 FFB4             LBSR $2166
21B2: 17 004E             LBSR $2203
21B5: 10DE 04             LDS $04
21B8: DC 06               LDD $06
21BA: 1F 8B               TFR A,DP
21BC: 12                  NOP 
21BD: 12                  NOP 
21BE: 12                  NOP 
21BF: 12                  NOP 
21C0: 6E 9F FFFE           JMP [$fffe]
21C4: C6 30               LDB #$30
21C6: 36                  PSHU B,A
21C8: 17 FFC1             LBSR $218c
21CB: 39                  RTS 
21CC: C6 31               LDB #$31
21CE: 20 F6               BRA $21c6
21D0: C6 08               LDB #$08
21D2: E7 40               STB 0,U
21D4: 68 41               ASL 1,U
21D6: 25 04               BCS $21dc
21D8: 8D EA               BSR $21c4
21DA: 20 02               BRA $21de
% b 21BD
Breakpoint set at 21BD
% s 2000

          0 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            2000: 7E 21AF      JMP $21af                  
>         1 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            21AF: 17 FFB4      LBSR $2166                 

6809 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
% r
PC=21AF A=00 B=00 X=0000 Y=0000 U=0000 SP=FF DP=00 CC=00
% c 


Breakpoint!

Outputting $5A in binary:
01011010
       1323 A=00 B=00 X=2203 Y=0000 U=2164 S=00FF P=00 -----Z--            21BC: 12           NOP                        
>      1324 A=00 B=00 X=2203 Y=0000 U=2164 S=00FF P=00 -----Z--            21BD: 12           NOP                        

6809 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
% 

The same optimization that we made for the 6800 and 6801 will work here. except you have to edit LDAA to LDA, etc., remove the references to PSP and trade them for ,U, etc. I should leave that for you to figure out, but, maybe not:

* 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
	STB	,U	; Borrow the upper byte of the parameter.
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
	DEC	,U
	BNE	OUTB8L	; loop if not Zero
	LEAU	2,U	; drop parameter bytes
	RTS

Doesn't having U to reference the parameter stack and make references to parameters (and variables) clear without having to dodge the return address and look through stack frames make it so much easier to see what you're doing?

I'm getting excited again. Sorry.

Stack frames. I guess I'll have to show you how those work, but not yet.

The 68000 code should be just different enough that we don't fall asleep getting there. 

(Title Page/Index)

 

 

 

 

No comments:

Post a Comment