Global Constants on the 6800, 6801, 6809, and 68000
So, now we have working environments set up for the 6800, 6809, 6801, and 68000, and have taken a look at summing up a few small integer constants buried in the code on each of these processors.
Do you think having a list of numbers buried in the code might be a little clumsy? Just a little bit?
It is. Usually.
There are several ways to separate the numbers from the code that adds them.
Let's take a look at one here, a way which we will call using global
constants.
There are three related topics we will introduce here, in the process.
- labels,
- lists in memory
- absolute addressing
And, while we're at it, you might want to think about object code size and
execution time. We haven't taken up all the tools necessary to think about
such things, but you might want to, anyway.
First, lets get the constants out of the code.
In Motorola's 680X assemblers, the usual way to define a constant byte-sized integer is to use the FCB directive. You may think FCB stands for File Control Block, but in this context it means Form Constant Byte:
FCB 8
We do not use "FCB #8" for two reasons. Well, three. One is that this is no longer immediate addressing. The other is that FCB is not an instruction for the microprocessor. It's a directive to the assembler. And the other other reason is that the assembler will complain if you do. Because of the first two reasons. And I promise this will make more sense later.
But it's not enough to have that small constant out there in memory somewhere. We need some way for the code to reference it. So we will give it a label, maybe call it BYTE8 (because it sounds cool or something):
BYTE8 FCB 8
You might remember the label START at the beginning of the code in our previous examples? It's basically the same thing in a slightly different context.
This is quite literally just a label, a name by which a particular address in
memory can be referenced.
Okay, here's the entire summing code for the 6800, using the separate constants:
ENTRY JMP START
*
BYTE8 FCB 8
BYTE5 FCB 5
BYTE2 FCB 2
BYTE7 FCB 7
BYTE4 FCB 4
*
START LDAB BYTE8
ADDB BYTE5
ADDB BYTE2
ADDB BYTE7
ADDB BYTE4
NOP
DONE NOP
Those asterisks at the start of those two lines are Motorola's way to show
that the entire line is a comment. In this case, it's to make a deliberate
empty line in the source code, to separate code from constants. It has zero
effect in the object code.
That's a JuMP at label ENTRY. It jumps over the constants.
Is it necessary?
Good question.
:-)
Get a running EXORsim debugging session:
$ ./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'
%
Start an assembly at address $1000 and paste it in. Remember, right-click-paste to paste into the shell, because the shell thinks Ctrl-V means something else:
% a 1000
1000: ENTRY JMP START
1003: *
1003: BYTE8 FCB 8
1004: BYTE5 FCB 5
1005: BYTE2 FCB 2
1006: BYTE7 FCB 7
1007: BYTE4 FCB 4
1008: *
1008: START LDAB BYTE8
Address at 1001 set to 1008
100b: ADDB BYTE5
100e: ADDB BYTE2
1011: ADDB BYTE7
1014: ADDB BYTE4
1017: NOP
1018: DONE NOP
1019:
Disassemble it to see how it went:
% u 1000
1000: 7E 10 08 JMP $1008
1003: 08 INX
1004: 05 ???
1005: 02 ???
1006: 07 TPA
1007: 04 ???
1008: F6 10 03 LDB $1003
100B: FB 10 04 ADDB $1004
100E: FB 10 05 ADDB $1005
1011: FB 10 06 ADDB $1006
1014: FB 10 07 ADDB $1007
1017: 01 NOP
1018: 01 NOP
1019: 00 ???
101A: 00 ???
...
Ackkkkkk! Where are the constants?!?!?!?!? And what is this INX and TPA and ????
If you don't jump over the constants, the processor will see that $08 as the
op-code for the INcrement X instruction, and the $07 as the Transfer Processor
status to A instruction. So that's what the disassembler shows you. And 5, 2,
and 4 are not defined instructions, so the disassembler prints question marks.
And the processor, who knows what it would do?
(Interested geeks have researched this question. You can look it up if you are so inclined, but most undefined op-codes aren't all that interesting on the 6800.)
So. Maybe we do need that JMP instruction. Yes, we do. Sort of.
Now, since we are using the interactive debugger, we could just use the step or continue debugger command to tell it to start at address $1008. In fact, we could. We know that.
But how does an operating system know that? How does someone who has neither the source code nor a debugger know what address to start at? I mean, without the source, here's all you have:
% d 1000
1000: 7E 10 08 08 05 02 07 04 F6 10 03 FB 10 04 FB 10 ~...............
1010: 05 FB 10 06 FB 10 07 01 01 00 00 00 00 00 00 00 ................
1020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
...
So, on the one hand, no the JMP is not exactly necessary. On the other hand, it is a useful convention. We will use it, at least until we have something better.
So, set a breakpoint, turn trace on, and watch it do its stuff:
% b 1018
Breakpoint set at 1018
% t on
% c 1000
9 A=00 B=1A X=0000 SP=00FF ------ ENTRY 1000: 7E 10 08 JMP 1008 EA=1008(START)
10 A=00 B=1A X=0000 SP=00FF ------ START 1008: F6 10 03 LDB 1003 EA=1003(BYTE8) D=08
11 A=00 B=08 X=0000 SP=00FF ------ 100B: FB 10 04 ADDB 1004 EA=1004(BYTE5) D=05
12 A=00 B=0D X=0000 SP=00FF ------ 100E: FB 10 05 ADDB 1005 EA=1005(BYTE2) D=02
13 A=00 B=0F X=0000 SP=00FF ------ 1011: FB 10 06 ADDB 1006 EA=1006(BYTE7) D=07
14 A=00 B=16 X=0000 SP=00FF H----- 1014: FB 10 07 ADDB 1007 EA=1007(BYTE4) D=04
15 A=00 B=1A X=0000 SP=00FF ------ 1017: 01 NOP
Breakpoint!
> 16 A=00 B=1A X=0000 SP=00FF ------ DONE 1018: 01 NOP
6800 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
%
And we can see it adding those up in the B accumulator.
The 6801 version will be pretty much exactly the same, but go ahead and change directories to where you built my 6801-enabled version of EXORsim and run a session to prove it to yourself:
$ ./exor --6801 --mon
Load facts file 'facts'
'exbug.bin' loaded.
'mdos.dsk' opened for drive 0 (single sided)
Hit Ctrl-C for simulator command line. Starting simulation...
> 0 A=00 B=00 X=0000 SP=FF8A ------ OSLOAD E800: 8E FF 8A LDS #$FF8A Load OS
Type 'help'
%
% a 1000
1000: ENTRY JMP START
1003: *
1003: BYTE8 FCB 8
Huh?: BYTE5 FCB 5
% 05: BYTE2 FCB 2
% 06: BYTE7 FCB 7
% 07: BYTE4 FCB 4
% 08: *
% 08: START LDAB BYTE8
% dress at 1001 set to 1008
% 0b: ADDB BYTE5
% 0e: ADDB BYTE2
% 11: ADDB BYTE7
% 14: ADDB BYTE4
% 17: NOP
% 18: DONE NOP
% 19:
% u 1000
% 00: 7E 10 08 JMP $1008
% 03: 08 INX
% 04: 05 LSLD
% 05: 02 ???
% 06: 07 TPA
% 07: 04 LSRD
% 08: F6 10 03 LDB $1003
% 0B: FB 10 04 ADDB $1004
% 0E: FB 10 05 ADDB $1005
% 11: FB 10 06 ADDB $1006
% 14: FB 10 07 ADDB $1007
% 17: 01 NOP
% 18: 01 NOP
% 19: 00 ???
% 1A: 00 ???
...
And so forth.
One thing you'll notice is that, on the 6801, op-codes $05 and $04 are
defined, whereas they are not on the 6800.
But we aren't using those, and even if you had a real 6801, even then, the timing would be the same.
You may be wondering why I switched to using B to accumulate the sums here. As I mentioned in the chapter on getting started on the 6801, conventions on which accumulator was less significant switched between the 6800 and the 6809, and the 6801 follows the 6809 byte order in the D register instructions. But it follows the hard-wired 6800 stacking order during interrupts, to avoid too many surprises when re-using code from the 6800.
From here on out, we'll also be following the 6809 conventions on byte order
where we can, not that you'll really notice it very often.
Now, let's see what happens on the 6809. In the first line of the code, change the LDAB to LDB:
ENTRY JMP START
...
The rest is the same source code.
Change back to Joe Allen's updated EXORsim and run a 6809 session. Paste in
the code, making sure the LDAB is changed to LDB:
$ ./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 1000
1000: ENTRY JMP START
later = 1001
1003: *
1003: BYTE8 FCB 8
1004: BYTE5 FCB 5
1005: BYTE2 FCB 2
1006: BYTE7 FCB 7
1007: BYTE4 FCB 4
1008: *
1008: START LDB BYTE8
Address at 1001 set to 1008
100b: ADDB BYTE5
100e: ADDB BYTE2
1011: ADDB BYTE7
1014: ADDB BYTE4
1017: NOP
1018: DONE NOP
1019:
% u 1000
1000: 7E 1008 JMP $1008
1003: 08 05 ASL $05
1005: 02 07 ??? $07
1007: 04 F6 LSR $f6
1009: 1003 FB COM $fb
100C: 1004 FB LSR $fb
100F: 1005 FB ??? $fb
1012: 1006 FB ROR $fb
1015: 1007 12 ASR $12
1018: 12 NOP
1019: 00 00 NEG $00
101B: 00 00 NEG $00
...
YIKES!
Oh. The op-code map is a little different on the 6809, and our small constant
integers are actually two-byte op-codes instead of one-byte -- which throws
the disassembly off at address $1007.
Easy. We can note that the JMP at ENTRY is to address $1008, and disassemble from there:
% u 1008
1008: F6 1003 LDB $1003
100B: FB 1004 ADDB $1004
100E: FB 1005 ADDB $1005
1011: FB 1006 ADDB $1006
1014: FB 1007 ADDB $1007
1017: 12 NOP
1018: 12 NOP
1019: 00 00 NEG $00
101B: 00 00 NEG $00
...
Okay, the op-codes are a little bit different in places, but that doesn't hurt
us.
% b 1018
Breakpoint set at 1018
% t on
% c 1000
0 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- ENTRY 1000: 7E 1008 JMP $1008
1 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- START 1008: F6 1003 LDB $1003 EA=1003(BYTE8) D=08
2 A=00 B=08 X=0000 Y=0000 U=0000 S=00FF P=00 -------- 100B: FB 1004 ADDB $1004 EA=1004(BYTE5) D=05
3 A=00 B=0D X=0000 Y=0000 U=0000 S=00FF P=00 -------- 100E: FB 1005 ADDB $1005 EA=1005(BYTE2) D=02
4 A=00 B=0F X=0000 Y=0000 U=0000 S=00FF P=00 -------- 1011: FB 1006 ADDB $1006 EA=1006(BYTE7) D=07
5 A=00 B=16 X=0000 Y=0000 U=0000 S=00FF P=00 --H----- 1014: FB 1007 ADDB $1007 EA=1007(BYTE4) D=04
6 A=00 B=1A X=0000 Y=0000 U=0000 S=00FF P=00 -------- 1017: 12 NOP
Breakpoint!
> 7 A=00 B=1A X=0000 Y=0000 U=0000 S=00FF P=00 -------- DONE 1018: 12 NOP
6809 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
%
The 6809 version works exactly the same.
So we should be able to move on to the 68000.
Except.
The 6809
has
something
called PC-relative addressing.
OHHHHHHHH HORRORS!!!!
8-o
;->
Take a look at the disassembly in the code above. Where immediate mode addressing buried the actual constants in the code, you'll see the addresses of the constants buried in the code.
LDB BYTE8
becomes
F6 1003
if you start the assembly at $1000.
But if you start the assembly at $1100, BYTE8 will become a label for $1108,
and the object code will assemble as "FB 1103".
Which means that, once you have assembled the code, you can't just copy the code somewhere else to run it. You have to do something called relocation, where you dig through every address in the object code and adjust it for where it want it to end up.
Relocation is not as hard as it sounds. Sort-of. (Gurgle. Cough. Sigh.) Once you know what assumptions you can make. Which ...
We don't want to go there just yet.
PC-relative code can be moved without relocation math. Which is why it is called position-independent, or, sometimes, "relocatable". Erk.
We'll talk about this more later, but right now I think we should take a quick
advance look at it.
Here's how PC relative code for the global constants looks for the code above:
ENTRY JMP START
*
BYTE8 FCB 8
BYTE5 FCB 5
BYTE2 FCB 2
BYTE7 FCB 7
BYTE4 FCB 4
*
START LDB BYTE8,PCR ; address relative to PC
ADDB BYTE5,PCR
ADDB BYTE2,PCR
ADDB BYTE7,PCR
ADDB BYTE4,PCR
NOP
DONE NOP
Quit your EXORsim 6809 session and start a fresh one, and paste the above code
in.
$ ./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 1000
1000: ENTRY JMP START
later = 1001
1003: *
1003: BYTE8 FCB 8
1004: BYTE5 FCB 5
1005: BYTE2 FCB 2
1006: BYTE7 FCB 7
1007: BYTE4 FCB 4
1008: *
1008: START LDB BYTE8,PCR
Address at 1001 set to 1008
100b: ADDB BYTE5,PCR
100e: ADDB BYTE2,PCR
1011: ADDB BYTE7,PCR
1014: ADDB BYTE4,PCR
1017: NOP
1018: DONE NOP
1019:
% u 1000
1000: 7E 1008 JMP $1008
1003: 08 05 ASL $05
1005: 02 07 ??? $07
1007: 04 E6 LSR $e6
1009: 8C F8EB CMPX #$f8eb
100C: 8C F6EB CMPX #$f6eb
100F: 8C F4EB CMPX #$f4eb
1012: 8C F2EB CMPX #$f2eb
1015: 8C F012 CMPX #$f012
1018: 12 NOP
1019: 00 00 NEG $00
101B: 00 00 NEG $00
...
% u 1008
1008: E6 8C F8 LDB $1003,PCR
100B: EB 8C F6 ADDB $1004,PCR
100E: EB 8C F4 ADDB $1005,PCR
1011: EB 8C F2 ADDB $1006,PCR
1014: EB 8C F0 ADDB $1007,PCR
1017: 12 NOP
1018: 12 NOP
1019: 00 00 NEG $00
101B: 00 00 NEG $00
...
% b 1018
Breakpoint set at 1018
% t on
% c 1000
0 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- ENTRY 1000: 7E 1008 JMP $1008
1 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- START 1008: E6 8C F8 LDB $1003,PCR EA=1003(BYTE8) D=08
2 A=00 B=08 X=0000 Y=0000 U=0000 S=00FF P=00 -------- 100B: EB 8C F6 ADDB $1004,PCR EA=1004(BYTE5) D=05
3 A=00 B=0D X=0000 Y=0000 U=0000 S=00FF P=00 -------- 100E: EB 8C F4 ADDB $1005,PCR EA=1005(BYTE2) D=02
4 A=00 B=0F X=0000 Y=0000 U=0000 S=00FF P=00 -------- 1011: EB 8C F2 ADDB $1006,PCR EA=1006(BYTE7) D=07
5 A=00 B=16 X=0000 Y=0000 U=0000 S=00FF P=00 --H----- 1014: EB 8C F0 ADDB $1007,PCR EA=1007(BYTE4) D=04
6 A=00 B=1A X=0000 Y=0000 U=0000 S=00FF P=00 -------- 1017: 12 NOP
Breakpoint!
> 7 A=00 B=1A X=0000 Y=0000 U=0000 S=00FF P=00 -------- DONE 1018: 12 NOP
6809 Monitor: Ctrl-C to exit, 'c' to continue, or type 'help'
% q
Bye
And, even though we only see the offsets in the code, the processor found the
constants and was able to add them up.
We'll try more interesting things with this idea later. For now, file it away,
and let's move on to the 68000 version of this. The code for absolute
addressing first:
OPT LIST,SYMTAB ; Options we want for the stand-alone assembler.
MACHINE MC68000 ; because there are a lot the assembler can do.
OPT DEBUG ; We want the debugger to know our labels.
OUTPUT
***********************************************************************
* EVEN ; byte data doesn't have to be aligned.
ENTRY JMP START
*
BYTE8 DC.B 8
BYTE5 DC.B 5
BYTE2 DC.B 2
BYTE7 DC.B 7
BYTE4 DC.B 4
*
EVEN ; But 68K code does have to be even aligned.
START MOVE.B BYTE8,D1
ADD.B BYTE5,D1
ADD.B BYTE2,D1
ADD.B BYTE7,D1
ADD.B BYTE4,D1
NOP
DONE NOP
* One way to return to the OS or other calling program
clr.w -(sp) ; there should be enough room on the caller's stack
trap #1 quick exit
Note that, instead of
Form Constant Byte (FCB),
the 68000 uses
Define Constant Byte (DC.B).
Also notice that I've added a little formal quitting code at the end. And
that's another something to talk about later.
I'm saving the source code as sumconst.s under in the working directory I made under the
Hatari C drive emulation directory. In the above example, that would be
~/asmwork/share/hatari/C/primer/sumconst.s
And I'm assembling it with
$ vasmm68k_mot -Ftos -o SUMCONST.PRG -L sumconst.lst sumconst.s
Here's the listing it produces:
Sections:
00: "text" (0-24)
Source: "sumconst.s"
1: OPT LIST,SYMTAB ; Options we want for the stand-alone assembler.
2: MACHINE MC68000 ; because there are a lot the assembler can do.
3: OPT DEBUG ; We want the debugger to know our labels.
4: OUTPUT
5: ***********************************************************************
6: * EVEN ; byte data doesn't have to be aligned.
00:00000000 6006 7: ENTRY JMP START
8: *
00:00000002 08 9: BYTE8 DC.B 8
00:00000003 05 10: BYTE5 DC.B 5
00:00000004 02 11: BYTE2 DC.B 2
00:00000005 07 12: BYTE7 DC.B 7
00:00000006 04 13: BYTE4 DC.B 4
14: *
15: EVEN ; But 68K code does have to be even aligned.
00:00000008 123AFFF8 16: START MOVE.B BYTE8,D1
00:0000000C D23AFFF5 17: ADD.B BYTE5,D1
00:00000010 D23AFFF2 18: ADD.B BYTE2,D1
00:00000014 D23AFFEF 19: ADD.B BYTE7,D1
00:00000018 D23AFFEC 20: ADD.B BYTE4,D1
00:0000001C 4E71 21: NOP
00:0000001E 4E71 22: DONE NOP
23: * One way to return to the OS or other calling program
00:00000020 4267 24: clr.w -(sp) ; there should be enough room on the caller's stack
00:00000022 4E41 25: trap #1 quick exit
If you know how to read the op-codes, you'll notice that the assembler seems
to be doing something you're not expecting. Let's check.
Get Hatari running, Ctrl-Z to escape GEM and get a TOS (CP/M) shell, Alt-Pause to enter the debugger, use the mouse to bring up the host shell you started Hatari in, and set the breakpoint (b) to break when the CPU jumps to the TEXT segment, then do a continue (c) to return to the TOS (CP/M) shell:
CPU condition breakpoint 1 with 1 condition(s) added:
pc = TEXT
-> Break only once, and delete breakpoint afterwards.
> c
Returning to emulation...
Invoke the assembled code in the TOS shell:
C:\primer>SUMCONST.PRG
Use the mouse to get back to the host shell where the debugger has taken the break and is waiting for you. Disassemble (d):
> d
(PC)
ENTRY:
00013d10 6006 bra.b #$06 == $00013d18 (T)
BYTE8:
00013d12 0805 0207 btst.l #$0207,d5
BYTE4:
00013d16 0400 123a sub.b #$3a,d0
00013d1a fff8 illegal
00013d1c d23a fff5 add.b (pc,$fff5) == $00013d13 [05],d1
00013d20 d23a fff2 add.b (pc,$fff2) == $00013d14 [02],d1
00013d24 d23a ffef add.b (pc,$ffef) == $00013d15 [07],d1
00013d28 d23a ffec add.b (pc,$ffec) == $00013d16 [04],d1
00013d2c 4e71 nop
DONE:
00013d2e 4e71 nop
00013d30 4267 clr.w -(a7) [0000]
00013d32 4e41 trap #$01
00013d34 0000 0000 or.b #$00,d0
00013d38 0000 0000 or.b #$00,d0
...
Didn't remember a BRAnch instruction?
My neither. That was supposed to be a JuMP.
It's getting confused trying to disassemble constants as code, so look at the
target for the branch ($00013d18
) and disassemble
from there. Except you forgot that the Hatari debugger expects decimal unless
you tell it otherwise. So you try again, typing it in as proper hexadecimal:
> d 13d18
Extra characters in decimal based number '13d18'!
Invalid address value '13d18'!
> d $13d18
START:
00013d18 123a fff8 move.b (pc,$fff8) == $00013d12 [08],d1
00013d1c d23a fff5 add.b (pc,$fff5) == $00013d13 [05],d1
00013d20 d23a fff2 add.b (pc,$fff2) == $00013d14 [02],d1
00013d24 d23a ffef add.b (pc,$ffef) == $00013d15 [07],d1
00013d28 d23a ffec add.b (pc,$ffec) == $00013d16 [04],d1
00013d2c 4e71 nop
DONE:
00013d2e 4e71 nop
00013d30 4267 clr.w -(a7) [0000]
00013d32 4e41 trap #$01
00013d34 0000 0000 or.b #$00,d0
00013d38 0000 0000 or.b #$00,d0
...
>
Twilight Zone ? (Cue creepy music.)
LoL. Optimization. It's using the PC relative modes because they take fewer bytes. We wanted to do that later, but we wanted to do that explicitly. Not yet. Not here.
This is one of the reasons I was hesitant to use vasm.
Sigh. Well, there are
switches to turn optimizations off. Not really a problem.
I'd go ahead and step through the optimized code, but we want to do that in a few more minutes, with explicit code. So, for now, let's (q)uit out of the debugger and Hatari and try again:
$ vasmm68k_mot -Ftos -no-opt -o SUMCONST.PRG -L sumconst.lst sumconst.s
Here's the listing it produces with the no-opts switch:
Sections:
00: "text" (0-32)
Source: "sumconst.s"
1: OPT LIST,SYMTAB ; Options we want for the stand-alone assembler.
2: MACHINE MC68000 ; because there are a lot the assembler can do.
3: OPT DEBUG ; We want the debugger to know our labels.
4: OUTPUT
5: ***********************************************************************
6: * EVEN ; byte data doesn't have to be aligned.
00:00000000 4EF90000000C 7: ENTRY JMP START
8: *
00:00000006 08 9: BYTE8 DC.B 8
00:00000007 05 10: BYTE5 DC.B 5
00:00000008 02 11: BYTE2 DC.B 2
00:00000009 07 12: BYTE7 DC.B 7
00:0000000A 04 13: BYTE4 DC.B 4
14: *
15: EVEN ; But 68K code does have to be even aligned.
00:0000000C 123900000006 16: START MOVE.B BYTE8,D1
00:00000012 D23900000007 17: ADD.B BYTE5,D1
00:00000018 D23900000008 18: ADD.B BYTE2,D1
00:0000001E D23900000009 19: ADD.B BYTE7,D1
00:00000024 D2390000000A 20: ADD.B BYTE4,D1
00:0000002A 4E71 21: NOP
00:0000002C 4E71 22: DONE NOP
23: * One way to return to the OS or other calling program
00:0000002E 4267 24: clr.w -(sp) ; there should be enough room on the caller's stack
00:00000030 4E41 25: trap #1 quick exit
26:
27:
28:
Symbols by name:
BYTE2 00:00000008
BYTE4 00:0000000A
BYTE5 00:00000007
BYTE7 00:00000009
BYTE8 00:00000006
DONE 00:0000002C
ENTRY 00:00000000
START 00:0000000C
Symbols by value:
00000000 ENTRY
00000006 BYTE8
00000007 BYTE5
00000008 BYTE2
00000009 BYTE7
0000000A BYTE4
0000000C START
0000002C DONE
That looks much better. Let's get it into the debugger.
Again,
- Start Hatari.
- When the GUI comes up, use Ctrl-Z to break out of GEM into the TOS command line shell.
-
Change to the primer directory:
C:\>CD PRIMER
C:\primer> - Use Alt-PAUSE to break out of TOS shell into the debugger.
-
Set a PC breakpoint at the beginning of the TEXT segment:
>b pc = TEXT :once
- (c)ontinue back to TOS shell.
-
Invoke the executable created by the assembler:
C:\primer>SUMCONST.PRG
Then we can
- (d)isassemble the code (from START this time)
- (m)emory dump (notice I'm using the count argument this time)
-
and alternate
- (r)egister dump
- (s)tep
- until we hit the landing pad
as follows:
----------------------------------------------------------------------
You have entered debug mode. Type c to continue emulation, h for help.
CPU=$e1d7e2, VBL=1385, FrameCycles=128, HBL=0, LineCycles=128, DSP=N/A
00e1d7e2 46c0 move.w d0,sr
> b pc = TEXT :once
CPU condition breakpoint 1 with 1 condition(s) added:
pc = TEXT
-> Break only once, and delete breakpoint afterwards.
> c
Returning to emulation...
1. CPU breakpoint condition(s) matched 1 times.
pc = TEXT :once
Removed CPU breakpoint 1:
pc = TEXT :once
Reading symbols from program '/home/nova/usr/share/hatari/C:/primer/SUMCONST.PRG' symbol table...
TOS executable, DRI / GST symbol table, reloc=0, program flags: PRIVATE (0x0)
Program section sizes:
text: 0x32, data: 0x0, bss: 0x0, symtab: 0x70
Trying to load DRI symbol table at offset 0x4e...
Offsetting BSS/DATA symbols from TEXT section.
Skipping duplicate address & symbol name checks when autoload is enabled.
Loaded 8 symbols (8 TEXT) from '/home/nova/usr/share/hatari/C:/primer/SUMCONST.PRG'.
CPU=$13d10, VBL=2479, FrameCycles=63376, HBL=62, LineCycles=384, DSP=N/A
00013d10 4ef9 0001 3d1c jmp $00013d1c
> d $13d1c
START:
00013d1c 1239 0001 3d16 move.b $00013d16 [08],d1
00013d22 d239 0001 3d17 add.b $00013d17 [05],d1
00013d28 d239 0001 3d18 add.b $00013d18 [02],d1
00013d2e d239 0001 3d19 add.b $00013d19 [07],d1
00013d34 d239 0001 3d1a add.b $00013d1a [04],d1
00013d3a 4e71 nop
DONE:
00013d3c 4e71 nop
00013d3e 4267 clr.w -(a7) [0000]
00013d40 4e41 trap #$01
00013d42 0000 0000 or.b #$00,d0
00013d46 0000 0000 or.b #$00,d0
...
> m $13d10 64
00013D10: 4e f9 00 01 3d 1c 08 05 02 07 04 00 12 39 00 01 N...=........9..
00013D20: 3d 16 d2 39 00 01 3d 17 d2 39 00 01 3d 18 d2 39 =..9..=..9..=..9
00013D30: 00 01 3d 19 d2 39 00 01 3d 1a 4e 71 4e 71 42 67 ..=..9..=.NqNqBg
00013D40: 4e 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 NA..............
> r
D0 00000000 D1 00000000 D2 00000000 D3 00000000
D4 00000000 D5 00000000 D6 00000000 D7 00000000
A0 00000000 A1 00000000 A2 00000000 A3 00000000
A4 00013D42 A5 00013D42 A6 00077FC6 A7 00077FF8
USP 00077FF8 ISP 00007E64
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 4ef9 (JMP) 0001 (OR) Chip latch 00000000
00013d10 4ef9 0001 3d1c jmp $00013d1c
Next PC: 00013d16
> s
CPU=$13d1c, VBL=2479, FrameCycles=63388, HBL=62, LineCycles=396, DSP=N/A
00013d1c 1239 0001 3d16 move.b $00013d16 [08],d1
> r
D0 00000000 D1 00000000 D2 00000000 D3 00000000
D4 00000000 D5 00000000 D6 00000000 D7 00000000
A0 00000000 A1 00000000 A2 00000000 A3 00000000
A4 00013D42 A5 00013D42 A6 00077FC6 A7 00077FF8
USP 00077FF8 ISP 00007E64
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 1239 (MOVE) 0001 (OR) Chip latch 00000000
00013d1c 1239 0001 3d16 move.b $00013d16 [08],d1
Next PC: 00013d22
> s
CPU=$13d22, VBL=2479, FrameCycles=63404, HBL=62, LineCycles=412, DSP=N/A
00013d22 d239 0001 3d17 add.b $00013d17 [05],d1
> r
D0 00000000 D1 00000008 D2 00000000 D3 00000000
D4 00000000 D5 00000000 D6 00000000 D7 00000000
A0 00000000 A1 00000000 A2 00000000 A3 00000000
A4 00013D42 A5 00013D42 A6 00077FC6 A7 00077FF8
USP 00077FF8 ISP 00007E64
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch d239 (ADD) 0001 (OR) Chip latch 00000000
00013d22 d239 0001 3d17 add.b $00013d17 [05],d1
Next PC: 00013d28
> s
CPU=$13d28, VBL=2479, FrameCycles=63420, HBL=62, LineCycles=428, DSP=N/A
00013d28 d239 0001 3d18 add.b $00013d18 [02],d1
> r
D0 00000000 D1 0000000D D2 00000000 D3 00000000
D4 00000000 D5 00000000 D6 00000000 D7 00000000
A0 00000000 A1 00000000 A2 00000000 A3 00000000
A4 00013D42 A5 00013D42 A6 00077FC6 A7 00077FF8
USP 00077FF8 ISP 00007E64
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch d239 (ADD) 0001 (OR) Chip latch 00000000
00013d28 d239 0001 3d18 add.b $00013d18 [02],d1
Next PC: 00013d2e
> s
CPU=$13d2e, VBL=2479, FrameCycles=63436, HBL=62, LineCycles=444, DSP=N/A
00013d2e d239 0001 3d19 add.b $00013d19 [07],d1
> r
D0 00000000 D1 0000000F D2 00000000 D3 00000000
D4 00000000 D5 00000000 D6 00000000 D7 00000000
A0 00000000 A1 00000000 A2 00000000 A3 00000000
A4 00013D42 A5 00013D42 A6 00077FC6 A7 00077FF8
USP 00077FF8 ISP 00007E64
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch d239 (ADD) 0001 (OR) Chip latch 00000000
00013d2e d239 0001 3d19 add.b $00013d19 [07],d1
Next PC: 00013d34
> s
CPU=$13d34, VBL=2479, FrameCycles=63452, HBL=62, LineCycles=460, DSP=N/A
00013d34 d239 0001 3d1a add.b $00013d1a [04],d1
> r
D0 00000000 D1 00000016 D2 00000000 D3 00000000
D4 00000000 D5 00000000 D6 00000000 D7 00000000
A0 00000000 A1 00000000 A2 00000000 A3 00000000
A4 00013D42 A5 00013D42 A6 00077FC6 A7 00077FF8
USP 00077FF8 ISP 00007E64
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch d239 (ADD) 0001 (OR) Chip latch 00000000
00013d34 d239 0001 3d1a add.b $00013d1a [04],d1
Next PC: 00013d3a
> s
CPU=$13d3a, VBL=2479, FrameCycles=63468, HBL=62, LineCycles=476, DSP=N/A
00013d3a 4e71 nop
At this point, we can either (q)uit the whole Hatari session, or, since I have
added that extra code that allows us to return to what called us, (c) and go
back to the TOS shell.
Why did I put the sum in D1 this time instead of D0? Good question. If we make
a really fine point of it, I could have kept the sum in D7, instead. LoL. We
have lots of registers in the 68000, and which one might correspond to B is
just a matter of convention -- although there is stack order, and, in the
68020, there are 64-bit results that are put in register pairs, so ...
But let's look at what happens when we (explicitly this time) use PC relative addressing. Here's the code:
OPT LIST,SYMTAB ; Options we want for the stand-alone assembler.
MACHINE MC68000 ; because there are a lot the assembler can do.
OPT DEBUG ; We want labels for debugging.
OUTPUT
***********************************************************************
* EVEN ; byte data doesn't have to be aligned.
ENTRY JMP START
*
BYTE8 DC.B 8
BYTE5 DC.B 5
BYTE2 DC.B 2
BYTE7 DC.B 7
BYTE4 DC.B 4
*
EVEN ; But 68K code does have to be even aligned.
START MOVE.B BYTE8(PC),D1
ADD.B BYTE5(PC),D1
ADD.B BYTE2(PC),D1
ADD.B BYTE7(PC),D1
ADD.B BYTE4(PC),D1
NOP
DONE NOP
* One way to return to the OS or other calling program
clr.w -(sp) ; there should be enough room on the caller's stack
trap #1 quick exit
and the listing file:
Sections:
00: "text" (0-28)
Source: "sumconpc.s"
1: OPT LIST,SYMTAB ; Options we want for the stand-alone assembler.
2: MACHINE MC68000 ; because there are a lot the assembler can do.
3: OPT DEBUG ; We want labels for debugging.
4: OUTPUT
5: ***********************************************************************
6: * EVEN ; byte data doesn't have to be aligned.
00:00000000 4EF90000000C 7: ENTRY JMP START
8: *
00:00000006 08 9: BYTE8 DC.B 8
00:00000007 05 10: BYTE5 DC.B 5
00:00000008 02 11: BYTE2 DC.B 2
00:00000009 07 12: BYTE7 DC.B 7
00:0000000A 04 13: BYTE4 DC.B 4
14: *
15: EVEN ; But 68K code does have to be even aligned.
00:0000000C 123AFFF8 16: START MOVE.B BYTE8(PC),D1
00:00000010 D23AFFF5 17: ADD.B BYTE5(PC),D1
00:00000014 D23AFFF2 18: ADD.B BYTE2(PC),D1
00:00000018 D23AFFEF 19: ADD.B BYTE7(PC),D1
00:0000001C D23AFFEC 20: ADD.B BYTE4(PC),D1
00:00000020 4E71 21: NOP
00:00000022 4E71 22: DONE NOP
23: * One way to return to the OS or other calling program
00:00000024 4267 24: clr.w -(sp) ; there should be enough room on the caller's stack
00:00000026 4E41 25: trap #1 quick exit
And I'm going to let you step through it yourself instead of showing you what it looks like here. It's basically just a different way to get at the same data, like on the 6809, but with 32-bit addresses and 16-bit offsets to deal with. And we'll actually talk about that all later.
This has been another really long one. As we say in Japan, 「ご苦労様。」 (Go-kurō-sama.) You've worked hard, even if you just read through and didn't take time to enjoy plugging the code in to watch it run.
Wait a minute. If you just read through and didn't step or trace through the simulations, you've really missed out on all the fun. Go back. Watch the code execute. Play with things just a little to see if you can make anything interesting happen.
And now we can really say, “ご苦労様。”
The next thing we need to do is take a small detour to make sure you have tools to deal with all the hexadecimal math that has your eyes glazing over.
No comments:
Post a Comment