Sunday, August 4, 2024

ALPP 01-10 -- Hello, World! (Not Yet on the Beach) -- 6800, 6801, 6809

Hello, World!
(Not Yet on the Beach)
6800, 6801, 6809

(Title Page/Index)

 

We've seen a little about how to work our way sequentially through a list of small integers.

Characters are essentially small integers. In the case of 7-bit US ASCII, they are -- or were -- 7-bit integers, with values ranging from 0 to 127. 

So a string of text is a list of small integers, serially accessed.

At least, it used to be so in the small computer world, back in the 1970s and early '80s. Then ligatures came to the PC world. That alone broke the 7-bit ASCII small integer paradigm. And then along came international character sets and mixed contexts and byte orders and assertions that 16 bits would be enough for all the useful characters of all modern languages together, ...

Don't get me wrong, the Unicode Consortium has been doing lots of useful work cataloging characters and their construction. And Unicode itself makes possible a lot more international communication than has been possible in the past.

But the existence of Unicode makes it clear that it is no longer possible to just consider characters to be small integers and be done with it. Nor can we consider text to be a list of small integers serially accessed and be done with it. Not really true, even when you consider that 32-bit integers are still small integers.

But it never really was true. Ligatures were only part of the problems that broke the paradigm, even before we started trying to take on all the languages of the world.

On the other hand, you could do a lot of useful work under the assumption of a list of small integers. That assumption has driven the success of Google and is at the heart of the "progress" of AI. There is a very large subset of the characters that we use and the text that we generate and use that does follow the paradigms of a sequence of small integers. 

And we still can do a lot of useful work this way, particularly in the limited world of retro computing. We just need to recognize up front that we there are a lot of Unicode characters beyond US ASCII that we won't be able to deal with.

We'll start here using the ASCII set that fits nicely in 7 bits, under the assumption that at least these characters are nice small integers. (And we may not actually get beyond that paradigm in any practical sense, in this primer.)

This subset usage is  made possible by our choice of platforms, although most of the computers that are now considered retro had fairly simple, compact character sets. 

(If we had chosen the Tandy Color Computer for the 6809, we would have a much more limited set of characters defined by the 6847 video display generator, not even true US ASCII.)

Our target for this exercise is to get a string of characters, such as 

  • "On the beach!"
  • "Hello World!" 
  • "My name is Mudd."
  • "Here we are now."

output where we can read it. 

Both the EXORciser and the Atari ST include low-level routines for outputting characters. If you can output characters and if you know how to construct a loop, you can output strings of text, as well.

The EXORciser also has low-level routines for outputting strings. The purpose of this tutorial is not to teach how to use the EXORciser monitor ROM, EXbug --  or the Atari BIOS, either -- but we will take a quick look at one of the string routines in EXbug, both to get a general idea of how to access such routines, and to get a general idea of what the routines were are going to put together for our purposes should do and should not do.

War story -- When I was first investigating Joe H. Allen's EXORsim (6800-only at the time), to see how useful it would be for testing my old fig-Forth source code transcriptions, I went looking for how to get character input and output. Couldn't find much, but I noted Joe's mention of the facts files being used by the simulator to figure out labels for disassembly. So I took a look inside that file.

From my previous experience (and the fig-FORTH source), I thought I should be looking for labels such as "INCH" and "OUTCH". And I found several suspicious labels in the facts file. 

In particular, these labels, part of what appears from the facts file to be a jump table that starts at $F000, caught my attention:

f012 code INBYTEV	Input byte with echo
f015 code INCHV		Input char with echo (strip bit 7)
f018 code OUTCH		Output character

If you disassemble the ROM code at $F000, you'll see that there is, indeed, a series of jump instructions starting at $F000: 

% u f000 
F000: 7E F5 58 COLDSTART  JMP $F558 [COLDBOOT Cold start (main reset)]			* Cold start
F003: 7E F7 89 ADDRPROMPT JMP $F789 [ADDRPRMT Prompt for addresses]			* Prompt for address
F006: 7E FA A7 HEXBIN     JMP $FAA7 [CVTHEX Convert ascii hex to binary]			* Convert hex to binary
F009: 7E F9 C0 CVTUPPER   JMP $F9C0 [CVTUPP Convert upper half to ascii hex]			* Convert upper half to ascii
F00C: 7E F9 C4 CVTLOWER   JMP $F9C4 [CVTLOW Convert lower half to ascii hex]			* Convert lower half to ascii
F00F: 7E FA 65 GETHEX     JMP $FA65 [GETHEX4 Get 4 hex digits from user into x]			* Get 4 hex digits from user into x
F012: 7E FA 8B INBYTEV    JMP $FA8B [INBYTE Input byte with echo unless AECHO is set]			* Input byte with echo
F015: 7E FA A0 INCHV      JMP $FAA0 [INCH Input character]			* Input char with echo (strip bit 7)
F018: 7E F9 DC OUTCH      JMP $F9DC [OUTCH Output character with NULs]			* Output character
F01B: 7E FA 24 OUTHEX1    JMP $FA24 [OUTHEX1 Output byte in hex ,x+]			* Output byte in hex ,x+
F01E: 7E FA 22 OUTHEX2    JMP $FA22 [OUTHEX2 Output 2 bytes in hex ,x+]			* Output 2 bytes in hex ,x++
F021: 7E FA 41 PCRLF      JMP $FA41 [PCRLF Print CR-LF]			* Print CR-LF
F024: 7E FA 33 PDATA      JMP $FA33 [PDATA Print CR-LF then string]			* Print CR-LF then string
F027: 7E FA 35 PDATA1     JMP $FA35 [PDATA1 Print string]			* Print string
F02A: 7E FA 26 PSPC       JMP $FA26 [PSPC Print space]			* Print space

Yes, this is a jump table. It's purpose is to present a reliable set of addresses to key functions.

Let's look at OUTCH, the entry at $F018, as an example. You see that it will jump from there to $F9DC. This seems like unnecessary jumping around, but the extra jump doesn't take much time compared to the character output function itself. 

If the version of the monitor ROM changes, the actual character output routine might move, say, to $F9D8. In that case, the jump at OUTCH will change to jump to $F9D8 instead of $F9DC, but any program that jumps to OUTCH will then jump from there to the right place without having to be updated. 

The ROM jump table provides a way for programs that use the functions inside the ROM to access those functions even when the insides of the ROM change -- as long as the jump table is maintained. (It can be maintained by re-assembling the monitor from the source code, or by patching it by hand. But the former is more reliable.)

Outputting a single character, 6800/6801:

After I disassembled the code that the OUTCH entry jumps to and examined it, I decided to give it a try. I used something like the following bit of code:

XOUTCH	EQU	$F018
*
ENTRY	LDAA	#'H	; the character to ouput
	JSR	XOUTCH	; Call the output routine in monitor ROM
	NOP		; landing pad
	NOP

Yeah. It's really short. Let's use it now.

EQU is a directive to the assembler, to EQUate a label to a value. In this case the label XOUTCH is defined as the OUTCH entry in the EXbug monitor ROM jump table. 

EQU produces no actual object code, only an entry in a symbol table.

The operand to the LDAA is a small integer, in this case the ASCII code for 'H'. You can put a trailing single quote ('H') on it to make it less disconcerting to human readers, but Motorola's assemblers don't care here. It's just a one-byte character, anything after the byte will be ignored.

JSR is Jump to SubRoutine, the general way to call subroutines in the 6800 and the 6801. It pushes the return address (the address of the first following NOP, in this case) on the return address stack pointed to by the stack pointer register, S. When the subroutine is done, it ends with a RTS -- ReTurn from Subroutine -- instruction, which pops the return address back off the stack and puts it back in the program counter, allowing the CPU to proceed with the instruction that follows the JSR.

How did S get to point to valid memory, by the way? The monitor ROM does that for us, but we won't want to depend on that in the future. I know it will be okay this time. But I'll show you how we can set up our own stack as soon as we've watched this tiny program run on the 6800 and 6801.

So, break out an EXORciser 6800 session, as we've done before, and assemble this at $2000. Disassemble it to be sure.

$ ./exor --mon
Load facts file 'facts'
'exbug.bin' loaded.
  EXBUG-1.1 detected
'mdos.dsk' opened for drive 0 (double sided)

OSLOAD...

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

>         0 A=00 B=00 X=0000 SP=00FF ------          0020: B6 E8 00 LDA E800                 

6800 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
% a 2000
2000: XOUTCH	EQU	$F018
2000: *
2000: ENTRY	LDAA	#'H'	; the character to ouput
2002: 	JSR	XOUTCH	; output routine in monitor ROM
2005: 	NOP		; landing pad
2006: 	NOP
2007: 
% u 2000
2000: 86 48               LDA #$48
2002: BD F0 18            JSR $F018 [OUTCH Output character]
2005: 01                  NOP
2006: 01                  NOP
2007: 00                  ???
2008: 00                  ???
...
%

Set a breakpoint at the appropriate place, set tracing on, and, just for fun, step (s 2000) through the first instruction at $2000, to watch it load $48 (ASCII code for 'H') into accumulator A:

% b 2006
Breakpoint set at 2006
% t on
% s 2000

          0 A=00 B=00 X=0000 SP=00FF ------ ENTRY    2000: 86 48    LDA #48   EA=2001 D=48   
>         1 A=48 B=00 X=0000 SP=00FF ------          2002: BD F0 18 JSR F018                 

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

There it is in A.

Continue from there and watch it trace through the ROM routine and return. (Scroll to the right if you can't see what the CPU is doing in the listing.) --

% c

          1 A=48 B=00 X=0000 SP=00FF ------          2002: BD F0 18 JSR F018  EA=F018(XOUTCH) 

          2 A=48 B=00 X=0000 SP=00FD ------ XOUTCH   F018: 7E F9 DC JMP F9DC  EA=F9DC(OUTCH) 
          3 A=48 B=00 X=0000 SP=00FD ------ OUTCH    F9DC: 37       PSHB                     Output character with NULs
          4 A=48 B=00 X=0000 SP=00FC ------          F9DD: F6 FC F4 LDB FCF4  EA=FCF4(ACIA0) D=02 
          5 A=48 B=02 X=0000 SP=00FC ------          F9E0: C5 02    BITB #02  EA=F9E1 D=02   
          6 A=48 B=02 X=0000 SP=00FC ------          F9E2: 27 F9    BEQ F9DD  EA=F9DD        
          7 A=48 B=02 X=0000 SP=00FC ------          F9E4: B7 FC F5 STA FCF5  EA=FCF5(ACIA1) D=48 
          8 A=48 B=02 X=0000 SP=00FC ------          F9E7: 81 0D    CMPA #0D  EA=F9E8 D=0D   
          9 A=48 B=02 X=0000 SP=00FC ------          F9E9: 26 1B    BNE FA06  EA=FA06        
         10 A=48 B=02 X=0000 SP=00FC ------          FA06: 7D FF 02 TST FF02  EA=FF02(NULCTRL) 
         11 A=48 B=02 X=0000 SP=00FC ---Z--          FA09: 2A F9    BPL FA04  EA=FA04        
         12 A=48 B=02 X=0000 SP=00FC ---Z--          FA04: 33       PULB                     
         13 A=48 B=00 X=0000 SP=00FD ---Z--          FA05: 39       RTS                      

         14 A=48 B=00 X=0000 SP=00FF ---Z--          2005: 01       NOP                      

Breakpoint!
H>        15 A=48 B=00 X=0000 SP=00FF ---Z--          2006: 01       NOP                      

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

And we see the H output somewhere in there, in this case, right after the breakpoint is reported.

Just for grins, read through the code that it executed in the ROM routine and see if you can guess what it is doing.

Also just for fun and enlightenment, let's take a look at the memory pointed to by S. Type "m fc" and hit return until we've gone far enough, and then hit Ctrl-C once:

% m fc
00fc 00 
00fd 00 
00fe 20 
00ff 05 
0100 00 
0101 00 
%

You can see the return address, $2005, stored from $00fe to $00ff. Think about what address was in S when, and what that means for a minute.

I'll wait.

Got it, right? 

No?

Okay, I guess it isn't all that obvious. :-*

On the 6800 and 6801, S will be pointing to the next available byte on the stack any time you try to look at it. That's kind of opposite the usual approach in the industry, but it works, with a few caveats which I will return to later.

Again, the 6801 code will be exactly the same. And since no one has at this point attempted to optimize the monitor ROM for the 6801, the monitor ROM code should also be exactly the same, as well. The only difference would be that, if my emulation of the 6801 were accurate, it would run a bit faster. I'm not going to urge you check the code on the 6801 for this one unless you want the practice.  

[JMR202408181632 edit:]

If you do, you'll run into a problem I had forgotten about. If I get around to updating EXORsim6801, the problem should go away, but until then you'll need some workarounds. Don't forget to come back here if you go look at that now.

[JMR202408181632 edit-end.]

Continuing with the war story, now that Joe has added exor09 to his simulator, I have dug into the facts file for the 6809, and it is (currently) exactly the same as the 6800 facts file. Disassembling the ROM code for the 6809 shows that Motorola's engineers have made quite an effort to avoid changing entry points and effects of code, in spite of the 6809 providing much better native approaches for much of it.  

And, looking at the M6809 EXORciser's User's Guide, the jump table and some other entry points are now documented in the User's Guide, where they weren't in the M6800 EXORciser's User's Guide. So we have a more complete description of the routines.

If you download the M6809 EXORciser's User's Guide, you can find descriptions of the supported entry points in section 3-7, beginning on page 3-9, about p. 50 of the PDF I have.

[JMR202410141236 Note: I haven't explored all of the facts files yet, but I have found a parameter definition that should be different -- AECHO. Based on that, we should be a bit wary of assuming too much. I haven't checked much of the variables and parameters. The entry points I've checked so far do match.]

Outputting a single character, 6809:

The 6809 source code will use the 6809 LDA mnemonic, and otherwise will be the same as the 6800:

$ ./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'
% a 2000
2000: XOUTCH	EQU	$F018
2000: *
2000: ENTRY	LDA	#'H'	; the character to ouput
2002: 	JSR	XOUTCH	; output routine in monitor ROM
2005: 	NOP		; landing pad
2006: 	NOP
2007: 
% u 2000
2000: 86 48               LDA #$48
2002: BD F018             JSR $f018
2005: 12                  NOP 
2006: 12                  NOP 
2007: 00 00               NEG $00
2009: 00 00               NEG $00
...
% b 2006 Breakpoint set at 2006 % c 2000 Breakpoint! H 28 A=48 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -----Z-- 2005: 12 NOP > 29 A=48 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -----Z-- 2006: 12 NOP 6809 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
%

Woops! Forgot to turn tracing on. (Deliberately? ;-) But you can see the 'H' output there. 

Turn tracing on and do it again (and scroll to the right in the listing below to watch what the CPU is doing): 

% t on
% c 2000

         29 A=48 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -----Z-- ENTRY      2000: 86 48        LDA #$48                   
         30 A=48 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            2002: BD F018      JSR $f018                  
         31 A=48 B=00 X=0000 Y=0000 U=0000 S=00FD P=00 -------- XOUTCH     F018: 16 0092      LBRA $f0ad                 
         32 A=48 B=00 X=0000 Y=0000 U=0000 S=00FD P=00 --------            F0AD: 7D FF67      TST $ff67   EA=FF67 D=00   
         33 A=48 B=00 X=0000 Y=0000 U=0000 S=00FD P=00 -----Z--            F0B0: 26 76        BNE $f128                  
         34 A=48 B=00 X=0000 Y=0000 U=0000 S=00FD P=00 -----Z--            F0B2: 8D 74        BSR $f128                  
         35 A=48 B=00 X=0000 Y=0000 U=0000 S=00FB P=00 -----Z--            F128: 17 00AC      LBSR $f1d7                 
         36 A=48 B=00 X=0000 Y=0000 U=0000 S=00F9 P=00 -----Z--            F1D7: 34           PSHS B                     
         37 A=48 B=00 X=0000 Y=0000 U=0000 S=00F8 P=00 -----Z--            F1D9: F6 FCF4      LDB $fcf4   EA=FCF4(ACIA0) D=02 
         38 A=48 B=02 X=0000 Y=0000 U=0000 S=00F8 P=00 --------            F1DC: C5 02        BITB #$02                  
         39 A=48 B=02 X=0000 Y=0000 U=0000 S=00F8 P=00 --------            F1DE: 27 F9        BEQ $f1d9                  
         40 A=48 B=02 X=0000 Y=0000 U=0000 S=00F8 P=00 --------            F1E0: B7 FCF5      STA $fcf5   EA=FCF5(ACIA1) D=48 
         41 A=48 B=02 X=0000 Y=0000 U=0000 S=00F8 P=00 --------            F1E3: 35           PULS PC,B                  
         42 A=48 B=00 X=0000 Y=0000 U=0000 S=00FB P=00 --------            F12B: 34           PSHS B,A                   
         43 A=48 B=00 X=0000 Y=0000 U=0000 S=00F9 P=00 --------            F12D: 84 7F        ANDA #$7f                  
         44 A=48 B=00 X=0000 Y=0000 U=0000 S=00F9 P=00 --------            F12F: 81 0D        CMPA #$0d                  
         45 A=48 B=00 X=0000 Y=0000 U=0000 S=00F9 P=00 --------            F131: 26 15        BNE $f148                  
         46 A=48 B=00 X=0000 Y=0000 U=0000 S=00F9 P=00 --------            F148: F6 FF02      LDB $ff02   EA=FF02(NULCTRL) D=00 
         47 A=48 B=00 X=0000 Y=0000 U=0000 S=00F9 P=00 -----Z--            F14B: 7D FF67      TST $ff67   EA=FF67 D=00   
         48 A=48 B=00 X=0000 Y=0000 U=0000 S=00F9 P=00 -----Z--            F14E: 27 ED        BEQ $f13d                  
         49 A=48 B=00 X=0000 Y=0000 U=0000 S=00F9 P=00 -----Z--            F13D: C4 7F        ANDB #$7f                  
         50 A=48 B=00 X=0000 Y=0000 U=0000 S=00F9 P=00 -----Z--            F13F: 5A           DECB                       
         51 A=48 B=FF X=0000 Y=0000 U=0000 S=00F9 P=00 ----N---            F140: 2B 0E        BMI $f150                  
         52 A=48 B=FF X=0000 Y=0000 U=0000 S=00F9 P=00 ----N---            F150: 4F           CLRA                       
         53 A=00 B=FF X=0000 Y=0000 U=0000 S=00F9 P=00 -----Z--            F151: 35           PULS PC,B,A                
         54 A=48 B=00 X=0000 Y=0000 U=0000 S=00FD P=00 -----Z--            F0B4: 7D FF37      TST $ff37   EA=FF37 D=00   
         55 A=48 B=00 X=0000 Y=0000 U=0000 S=00FD P=00 -----Z--            F0B7: 27 DA        BEQ $f093                  
         56 A=48 B=00 X=0000 Y=0000 U=0000 S=00FD P=00 -----Z--            F093: 39           RTS                        
         57 A=48 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -----Z--            2005: 12           NOP                        

Breakpoint!
H>        58 A=48 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -----Z--            2006: 12           NOP                        

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

We can see that the 6809's path through the monitor ROM is different, and contains instructions unique to the 6809. Just for curiosity, let's look at the 6809's stack after the code is run:

% m 00f8
00f8 00 
00f9 48 
00fa 00 
00fb f0 
00fc b4 
00fd 20 
00fe 05 
00ff 00 
0100 00 
0101 00 
% 

We can see that much more information has been stored on the stack during the run for some reason. (More registers to save? I'm guessing that the 6809 is doing a bit more than would be necessary, in order to maintain compatibility with the EXORciser's 6800-based code.)

We should note that the return address to our code has been stored from $00fd to $00fe. The 6809 varies from the 6800 in stack discipline, always pointing to the last byte pushed instead of to the next available byte. We'll discuss this later, as well.

Now let's try outputting a full string of text on the 6800/6801.

As I mentioned earlier, in the M6809 EXORciser's User's Guide, we find a more complete description of the EXbug routines for outputting strings. We don't intend to use these routines extensively, for reasons I'll explain later, but we will take a quick look at them.

XPDATA and XPDAT1 are described on p. 3-35, about p. 56 in the PDF I have. These correspond to PDATA and PDATA1 in Joe's facts file. 

Specific points of usage are that the string should be terminated by EOT (ASCII 4), and that the X index register should point to the beginning of the string when you call the routine.

Providing PDATA as an entry point that outputs a carriage return/line feed in front of a string is a bit of a byte-count-saving technique, which we will ignore, to keep our excursion into the ROMs short. We'll just use the more general PDATA1 and insert CR/LF characters as necessary to separate the strings from the debugger output.

And, as I said, this time we'll provide our own stack. The Reserve Memory Block directive, RMB, is useful for this.

The assembler keeps track of where the next instruction should be allocated in memory. This pointer is sometimes called the "here" counter or pointer, as in "You are here.".

In effect, the RMB directive adds its argument to the assembler's here pointer, without recording any instructions, constants, or data in the gap. 

A linker or loader may later fill the gap in with zeroes or some other constant, or may just leave it with whatever might have been there before, essentially "garbage" data. You can't rely on what might be in there.

You'll note that the stack's label is at the end of the area allocated for the stack, not the beginning. This is because data is pushed down in memory, most recently pushed data going below what was there before. Also, you'll note that , for the 6800, the initial stack pointer is pointing to the first place available to push a byte to. 

Another note, Joe's interactive assembler doesn't support having the assembler calculate the size of the stack, which is why I commented that line out and replaced it with a pre-calculated size.

I've braced the string in CR/LF pairs to make it more visible in the output. "Sekai yo, yai!" is a Latinization of (or, Rōmaji for) 「世界よ、ヤイ!」 -- a rough approximation of "Hey, World, Hello!" in Japanese. Choose your own phrase to put in here. 

Placement of the text after the stack was deliberate, to avoid the text being overwritten in the unlikely occurrence of a stack overflow.

And don't forget that the monitor ROM routines expect the End Of Text character at the end of the string.

XPDAT1	EQU	$F027	; string output, terminated by EOT
EOT	EQU	$04	; $04 is decimal 4
LF	EQU	$0A	; line feed
CR	EQU	$0D	; carriage return
*
NATWID	EQU	2	; 2 bytes in the CPU's natural integer
*
ENTRY	JMP	START
* (EXORsim apparently doesn't want to calculate RMB arguments.)
*	RMB	16*NATWID-1
	RMB	31	; 16 levels of call minus any saved registers, max
STKBAS	RMB	1	; 6800 is post-dec (post-store-decrement) push
SAVES	RMB	2	; a place to keep S so we can be clean
HELLO	FCB	CR,LF	; Put message at beginning of line
	FCB	"SEKAI YO, YAI!"	; Whatever the user wants here.
	FCB	CR,LF,EOT	; Put the debugger's output on a new line.
*
START	STS	SAVES	; Save what the monitor gives us.
	LDS	#STKBAS	; Move to our own stack
	LDX	#HELLO	; point to the string
	JSR	XPDAT1	; output it
DONE	LDS	SAVES	; restore the stack pointer
	NOP
	NOP		; landing pad

Assemble at $2000. Set the breakpoint at the DONE label, just after the return from the call to XPDAT1 this time, but don't turn tracing on. Single step a few instructions to watch S and X get set. Then continue (c) from there to the breakpoint, and single step after the breakpoint to see S restored:

$ ./exor --mon
Load facts file 'facts'
'exbug.bin' loaded.
  EXBUG-1.1 detected
'mdos.dsk' opened for drive 0 (double sided)

OSLOAD...

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

>         0 A=00 B=00 X=0000 SP=00FF ------          0020: B6 E8 00 LDA E800                 

6800 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
% a 2000
2000: XPDAT1	EQU	$F027	; string output, terminated by EOT
2000: EOT	EQU	$04	; $04 is decimal 4
2000: LF	EQU	$0A	; line feed
2000: CR	EQU	$0D	; carriage return
2000: *
2000: NATWID	EQU	2	; 2 bytes in the CPU's natural integer
2000: *
2000: ENTRY	JMP	START
2003: * (EXORsim apparently doesn't want to calculate RMBs.)
2003: *	RMB	16*NATWID-1
2003: 	RMB	31	; 16 levels of call minus any saved registers, max
2022: STKBAS	RMB	1	; 6800 is post-dec (post-store-decrement) push
2023: SAVES	RMB	2	; a place to keep S so we can be clean
2025: HELLO	FCB	CR,LF	; Put message at beginning of line
2027: 	FCB	"SEKAI YO, YAI!"	; Whatever the user wants here.
2035: 	FCB	CR,LF,EOT	; Put the debugger's output on a new line.
2038: *
2038: START	STS	SAVES	; Save what the monitor gives us.
Address at 2001 set to 2038
203b: 	LDS	#STKBAS	; Move to our own stack
203e: 	LDX	#HELLO	; point to the string
2041: 	JSR	XPDAT1	; output it
2044: DONE	LDS	SAVES	; restore the stack pointer
2047: 	NOP
2048: 	NOP		; landing pad
2049: 
% b 2044
Breakpoint set at 2044
% s 2000

          0 A=00 B=00 X=0000 SP=00FF ------ ENTRY    2000: 7E 20 38 JMP 2038  EA=2038(START) 
>         1 A=00 B=00 X=0000 SP=00FF ------ START    2038: BF 20 23 STS 2023                 

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

          1 A=00 B=00 X=0000 SP=00FF ------ START    2038: BF 20 23 STS 2023  EA=2023(SAVES) D=00FF 
>         2 A=00 B=00 X=0000 SP=00FF ------          203B: 8E 20 22 LDS #$2022                

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

          2 A=00 B=00 X=0000 SP=00FF ------          203B: 8E 20 22 LDS #$2022 EA=203C D=2022 
>         3 A=00 B=00 X=0000 SP=2022 ------          203E: CE 20 25 LDX #$2025                

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

          3 A=00 B=00 X=0000 SP=2022 ------          203E: CE 20 25 LDX #$2025 EA=203F D=2025 
>         4 A=00 B=00 X=2025 SP=2022 ------          2041: BD F0 27 JSR F027                 


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


Breakpoint!

SEKAI YO, YAI!
        321 A=04 B=00 X=2037 SP=2020 ---Z--          FA40: 39       RTS                      

>       322 A=04 B=00 X=2037 SP=2022 ---Z-- DONE     2044: BE 20 23 LDS 2023                 

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

        322 A=04 B=00 X=2037 SP=2022 ---Z-- DONE     2044: BE 20 23 LDS 2023  EA=2023(SAVES) D=00FF 
>       323 A=04 B=00 X=2037 SP=00FF ------          2047: 01       NOP                      

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

And we see the string output just after the breakpoint is reported.

While you're looking at the output string, play around a bit. Disassemble the code, look at the stack, check where the stack pointer was saved, that kind of stuff. Take notes of anything interesting. 

You might even try doing it again, setting trace mode on this time. But, if you do, be prepared to wade through a lot of trace output. And be prepared to have to keep a sharp look out for the characters of the string mixed in with the trace.

Again, the 6801 will do just the same as the 6800. Do it for practice, anyway, unless you're really impatient. 

[JMR202408181636 edit:]

But, as I have now noted above, you'll run into a problem I had forgotten about. If I get around to updating EXORsim6801, the problem should go away, but until then you'll need some workarounds. And, again, don't forget to come back here when done, if you go there now.

[JMR202408181636 edit-end.]

And let's try outputting a full string of text on the 6809.

The same 6800 source will actually work on the 6809 as it is. As it turns out, 15 and a half levels minus the bytes of saved registers is still plenty and then some. Give it a try and note how the return address gets saved starting one byte lower on the stack. You were expecting that by now, right?

But let's modify the source for the way the 6809 does the stack, and, while we are at it, use the 6809's PC-relative addressing modes a little:

* 6809 special version
XPDAT1	EQU	$F027	; string output, terminated by EOT
EOT	EQU	$04	; $04 is decimal 4
LF	EQU	$0A	; line feed
CR	EQU	$0D	; carriage return
*
NATWID	EQU	2	; 2 bytes in the CPU's natural integer
*
ENTRY	BRA	START	; Close enough for the short branch.
	RMB	32	; 16 levels of call minus any saved registers, max
*STKBAS	EQU	*	; Didn't want to tie STKBAS to SAVES
* But the interactive assembler doesn't recognize * as the here pointer.
SAVES	RMB	2	; a place to keep S so we can return cleanly
* And the interactive assembler wants EQU arguments to be known when used.
STKBAS	EQU	SAVES 	; 6809 is pre-dec (pre-store-decrement) push
HELLO	FCB	CR,LF	; Put message at beginning of line
	FCB	"SEKAI YO, YAI!"	; Whatever the user wants here.
	FCB	CR,LF,EOT	; Put the debugger's output on a new line.
*
START	STS	SAVES,PCR	; Save what the monitor gives us.
	LEAS	STKBAS,PCR	; Move to our own stack
	LEAX	HELLO,PCR	; point to the string
	LBSR	XPDAT1		; output it
DONE	LDS	SAVES,PCR	; restore the stack pointer
	NOP
	NOP		; landing pad

I'll explain about the asterisk and the here pointer and why we can't use them in this code later. For now, it should be enough to show that the initial stack pointer on the 6809 points 1 address beyond the actual stack allocation area. This is because the 6809 decrements the stack pointer before pushing a byte on the stack.

For a variety of reasons, we will be revisiting these concepts as we go. 

Let's take a look at this code running:

$ ./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'
% a 2000
2000: * 6809 special version
2000: XPDAT1	EQU	$F027	; string output, terminated by EOT
2000: EOT	EQU	$04	; $04 is decimal 4
2000: LF	EQU	$0A	; line feed
2000: CR	EQU	$0D	; carriage return
2000: *
2000: NATWID	EQU	2	; 2 bytes in the CPU's natural integer
2000: *
2000: ENTRY	BRA	START	; Close enough for the short branch.
later = 2001
2002: 	RMB	32	; 16 levels of call minus any saved registers, max
2022: *STKBAS	EQU	*	; Didn't want to tie STKBAS to SAVES
2022: * But the interactive assembler doesn't recognize * as the here pointer.
2022: SAVES	RMB	2	; a place to keep S so we can return cleanly
2024: * And the interactive assembler wants EQU arguments to be known when used.
2024: STKBAS	EQU	SAVES 	; 6809 is pre-dec (pre-store-decrement) push
2024: HELLO	FCB	CR,LF	; Put message at beginning of line
2026: 	FCB	"SEKAI YO, YAI!"	; Whatever the user wants here.
2034: 	FCB	CR,LF,EOT	; Put the debugger's output on a new line.
2037: *
2037: START	STS	SAVES,PCR	; Save what the monitor gives us.
Offset at 2001 set to 35
203b: 	LEAS	STKBAS,PCR	; Move to our own stack
203e: 	LEAX	HELLO,PCR	; point to the string
2041: 	LBSR	XPDAT1		; output it
2044: DONE	LDS	SAVES,PCR	; restore the stack pointer
2048: 	NOP
2049: 	NOP		; landing pad
204a: 
% u 2000
2000: 20 35               BRA $2037
2002: 00 00               NEG $00
...
2020: 00 00               NEG $00
2022: 00 00               NEG $00
2024: 0D 0A               TST $0a
2026: 53                  COMB 
2027: 45                  ???A 
2028: 4B                  ???A 
% u 2037
2037: 10EF 8C E7           STS $2022,PCR
203B: 32 8C E4            LEAS $2022,PCR
203E: 30 8C E3            LEAX $2024,PCR
2041: 17 CFE3             LBSR $f027 [XPDAT1 Print data string (Enter with X)]
2044: 10EE 8C DA           LDS $2022,PCR
2048: 12                  NOP 
2049: 12                  NOP 
204A: 00 00               NEG $00
204C: 00 00               NEG $00
...
% b 2044
Breakpoint set at 2044
% s 2000

          0 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- ENTRY      2000: 20 35        BRA $2037                  
>         1 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- START      2037: 10EF 8C E7   STS $2022,PCR                

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

          1 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- START      2037: 10EF 8C E7   STS $2022,PCR EA=2022(STKBAS) D=00FF 
>         2 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            203B: 32 8C E4     LEAS $2022,PCR                

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

          2 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            203B: 32 8C E4     LEAS $2022,PCR                
>         3 A=00 B=00 X=0000 Y=0000 U=0000 S=2022 P=00 --------            203E: 30 8C E3     LEAX $2024,PCR                

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

          3 A=00 B=00 X=0000 Y=0000 U=0000 S=2022 P=00 --------            203E: 30 8C E3     LEAX $2024,PCR                
>         4 A=00 B=00 X=2024 Y=0000 U=0000 S=2022 P=00 --------            2041: 17 CFE3      LBSR $f027                 

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


Breakpoint!

SEKAI YO, YAI!
        587 A=04 B=00 X=2036 Y=0000 U=0000 S=2020 P=00 -----Z--            F069: 39           RTS                        
>       588 A=04 B=00 X=2036 Y=0000 U=0000 S=2022 P=00 -----Z-- DONE       2044: 10EE 8C DA   LDS $2022,PCR                

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

        588 A=04 B=00 X=2036 Y=0000 U=0000 S=2022 P=00 -----Z-- DONE       2044: 10EE 8C DA   LDS $2022,PCR EA=2022(STKBAS) D=00FF 
>       589 A=04 B=00 X=2036 Y=0000 U=0000 S=00FF P=00 --------            2048: 12           NOP                        

6809 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
% 
  • Copy it, assemble it, 
  • use the disassembler to make sure it assembled correctly; 
  • set the breakpoint, 
  • single-step a few to watch the stack pointer and the index register get set correctly, 
  • continue execution to watch the string get output; 
  • single-step to watch the S stack pointer get restored.

After that, check the stack memory with the (d)ump or (m)emory commands. (Remember, use Ctrl-C to get out of the memory change cycle.)

One more thing to play with -- Try leaving the EOT off the end of the string and see what happens. (I'll talk more about this later.)

This kind of playing is important. So important that I'll dedicate an entire chapter to it every now and then.

And, just for the record, in case you missed it above, we will be building our own string output routines.

This chapter has gotten kind of long, so we'll try to repeat this all on the Atari ST's 68000 in the next chapter

Or you could check out the workarounds I recommend for EXORsim6801 now, and look at the 68000 code after.


(Title Page/Index)

 

No comments:

Post a Comment