Saturday, October 26, 2024

ALPP 03-14 -- Keyboard Input Routines and Character Code Output on the 68000 (Debug Session -- Dealloc Error)

Keyboard Input Routines
and Character Coded Output
on the 68000
(Debug Session -- Dealloc Error)

(Title Page/Index)

 

So we found one of the bugs in the code our test program to read the keyboard and show the character and the character code in binary and hexadecimal. 

And I told you we should use techniques that I have described to check that stack balance has been maintained.

But inserting test code into code is not just a great way to test code, it's also a great way to insert new bugs, mask old bugs, and increase opportunities to accidentally alter the code.

So we want to figure out what parts of the code we want to look at before we insert the code to look at it with.

Let's start another Hatari session and set some breakpoints. You'll want the assembly output listing from vasm in a text editor window for reference. In my case, I called it "inkey_68K.list" when I did the assembly the last time:

vasmm68k_mot -Ftos -no-opt -o INKEY_68K.PRG -L inkey_68K.list inkey_68K.s

Without either the listing or the source code open to look at, you'll be flying blind. Even with the listing, you'll be flying instrument rules, so to speak.

Break into the debugger and set the TEXT breakpoint, of course. Then (c)ontinue:

----------------------------------------------------------------------
You have entered debug mode. Type c to continue emulation, h for help.

CPU=$e1d7e2, VBL=1366, FrameCycles=128, HBL=0, LineCycles=128, DSP=N/A
00e1d7e2 46c0                     move.w d0,sr
> b pc=TEXT
CPU condition breakpoint 1 with 1 condition(s) added:
	pc = TEXT
> c
Returning to emulation...

Back at the EMUCON console, invoke the program (INKEY_68K.PRG above) and when it tries to enter the TEXT segment code it will take you to the breakpoint. 

Step into the BRA START at ENTRY and take a disassembly from the PC at START:

1. CPU breakpoint condition(s) matched 1 times.
	pc = TEXT
Reading symbols from program '/home/nova/usr/share/hatari/C:/primer/char_io/stepinch/INKEY_68K.PRG' symbol table...
TOS executable, DRI / GST symbol table, reloc=0, program flags: PRIVATE (0x0)
Program section sizes:
  text: 0x350, data: 0x0, bss: 0x0, symtab: 0x32c
Trying to load DRI symbol table at offset 0x36c...
Offsetting BSS/DATA symbols from TEXT section.
Skipping duplicate address & symbol name checks when autoload is enabled.
Loaded 56 symbols (41 TEXT) from '/home/nova/usr/share/hatari/C:/primer/char_io/stepinch/INKEY_68K.PRG'.

CPU=$13d10, VBL=3801, FrameCycles=210184, HBL=206, LineCycles=888, DSP=N/A
00013d10 6000 02a0                bra.w #$02a0 == $00013fb2 (T)
> s

CPU=$13fb2, VBL=3801, FrameCycles=210196, HBL=206, LineCycles=900, DSP=N/A
00013fb2 6100 ff34                bsr.w #$ff34 == $00013ee8
> d
(PC)
START:
00013fb2 6100 ff34                bsr.w #$ff34 == $00013ee8
00013fb6 4e71                     nop 
00013fb8 6100 005a                bsr.w #$005a == $00014014
DONE:
00013fbc 4e71                     nop 
00013fbe 4ced f000 0008           movem.l (a5,$0008) == $00014068,a4-a7
00013fc4 4e71                     nop 
00013fc6 4e71                     nop 
00013fc8 4e71                     nop 
00013fca 4e71                     nop 
00013fcc 4267                     clr.w -(a7) [0000]
00013fce 4e41                     trap #$01
INCHNE:
00013fd0 610a                     bsr.b #$0a == $00013fdc
00013fd2 c0bc 0000 ffff           and.l #$0000ffff,d0
00013fd8 2d00                     move.l d0,-(a6) [00000000]
00013fda 4e75                     rts  == $00000000
INCHV:
00013fdc 3f3c 0002                move.w #$0002,-(a7) [0000]
00013fe0 3f3c 0002                move.w #$0002,-(a7) [0000]
00013fe4 4e4d                     trap #$0d
> 

Get a look at the registers and step through INITRT, watching the stack and run-time initilizations. Show the registers again at return, even if you don't need to see them before that.

Remember that INITRT returns through JMP A0, not RTS:

> 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 00014060   A5 00014060   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 6100 (BSR) ff34 (ILLEGAL) Chip latch 00000000
00013fb2 6100 ff34                bsr.w #$ff34 == $00013ee8
Next PC: 00013fb6
> s

CPU=$13ee8, VBL=3801, FrameCycles=210216, HBL=206, LineCycles=920, DSP=N/A
00013ee8 205f                     movea.l (a7)+ [00013fb6],a0
> s

CPU=$13eea, VBL=3801, FrameCycles=210228, HBL=206, LineCycles=932, DSP=N/A
00013eea 47fa fe24                lea.l (pc,$fe24) == $00013d10,a3
> s

CPU=$13eee, VBL=3801, FrameCycles=210236, HBL=206, LineCycles=940, DSP=N/A
00013eee 48eb f000 0008           movem.l a4-a7,(a3,$0008) == $00013d18
> s

CPU=$13ef4, VBL=3801, FrameCycles=210280, HBL=206, LineCycles=984, DSP=N/A
00013ef4 2a4b                     movea.l a3,a5
> s

CPU=$13ef6, VBL=3801, FrameCycles=210284, HBL=206, LineCycles=988, DSP=N/A
00013ef6 4fed 0148                lea.l (a5,$0148) == $00013e58,a7
> s

CPU=$13efa, VBL=3801, FrameCycles=210292, HBL=206, LineCycles=996, DSP=N/A
00013efa 4ded 01d0                lea.l (a5,$01d0) == $00013ee0,a6
> s

CPU=$13efe, VBL=3801, FrameCycles=210300, HBL=206, LineCycles=1004, DSP=N/A
00013efe 4ed0                     jmp (a0)
> r
  D0 00000000   D1 00000000   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 00013FB6   A1 00000000   A2 00000000   A3 00013D10 
  A4 00014060   A5 00013D10   A6 00013EE0   A7 00013E58 
USP  00013E58 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 4ed0 (JMP) 4286 (CLR) Chip latch 00000000
00013efe 4ed0                     jmp (a0)
Next PC: 00013f00
> 

Step into the main routine, PGSTRT, and, before you step too far, show the registers and get a disassembly from the PC. Take particular note of the parameter stack pointer, A6.

Remember, when you step,  and when you show registers, it shows you the next op-code to perform, not the one just completed:

> s

CPU=$13fb6, VBL=3801, FrameCycles=210308, HBL=206, LineCycles=1012, DSP=N/A
00013fb6 4e71                     nop 
> r
  D0 00000000   D1 00000000   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 00013FB6   A1 00000000   A2 00000000   A3 00013D10 
  A4 00014060   A5 00013D10   A6 00013EE0   A7 00013E58 
USP  00013E58 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 4e71 (NOP) 6100 (BSR) Chip latch 00000000
00013fb6 4e71                     nop 
Next PC: 00013fb8
> s

CPU=$13fb8, VBL=3801, FrameCycles=210312, HBL=207, LineCycles=0, DSP=N/A
00013fb8 6100 005a                bsr.w #$005a == $00014014
> s

CPU=$14014, VBL=3801, FrameCycles=210332, HBL=207, LineCycles=20, DSP=N/A
00014014 41fa ffd4                lea.l (pc,$ffd4) == $00013fea,a0
> s

CPU=$14018, VBL=3801, FrameCycles=210340, HBL=207, LineCycles=28, DSP=N/A
00014018 2d08                     move.l a0,-(a6) [00000000]
> r
  D0 00000000   D1 00000000   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 00013FEA   A1 00000000   A2 00000000   A3 00013D10 
  A4 00014060   A5 00013D10   A6 00013EE0   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 2d08 (MOVE) 6100 (BSR) Chip latch 00000000
00014018 2d08                     move.l a0,-(a6) [00000000]
Next PC: 0001401a
> s

CPU=$1401a, VBL=3801, FrameCycles=210352, HBL=207, LineCycles=40, DSP=N/A
0001401a 6100 ff50                bsr.w #$ff50 == $00013f6c
> d
(PC)
0001401a 6100 ff50                bsr.w #$ff50 == $00013f6c
0001401e 6100 ffb0                bsr.w #$ffb0 == $00013fd0
00014022 2d16                     move.l (a6) [00013fea],-(a6) [00000000]
00014024 41fa ffe2                lea.l (pc,$ffe2) == $00014008,a0
00014028 2d08                     move.l a0,-(a6) [00000000]
0001402a 6100 ff40                bsr.w #$ff40 == $00013f6c
0001402e 6100 ff24                bsr.w #$ff24 == $00013f54
00014032 2d16                     move.l (a6) [00013fea],-(a6) [00000000]
00014034 41fa ffd7                lea.l (pc,$ffd7) == $0001400d,a0
00014038 2d08                     move.l a0,-(a6) [00000000]
0001403a 6100 ff30                bsr.w #$ff30 == $00013f6c
0001403e 6100 fef0                bsr.w #$fef0 == $00013f30
00014042 2d16                     move.l (a6) [00013fea],-(a6) [00000000]
00014044 41fa ffc9                lea.l (pc,$ffc9) == $0001400f,a0
00014048 2d08                     move.l a0,-(a6) [00000000]
0001404a 6100 ff20                bsr.w #$ff20 == $00013f6c
0001404e 6100 feb0                bsr.w #$feb0 == $00013f00
00014052 6100 fef6                bsr.w #$fef6 == $00013f4a
00014056 221e                     move.l (a6)+ [00013fea],d1
00014058 b23c 0051                cmp.b #$51,d1
0001405c 66b6                     bne.b #$b6 == $00014014 (T)
0001405e 4e75                     rts  == $00013fbc
>

Use the listing and the disassembly to work out the addresses for the breakpoints, and set a breakpoint after every call:

> b pc=$1401e
CPU condition breakpoint 2 with 1 condition(s) added:
	pc = $1401e
> b pc=$14022
CPU condition breakpoint 3 with 1 condition(s) added:
	pc = $14022
> b pc=$1402e
CPU condition breakpoint 4 with 1 condition(s) added:
	pc = $1402e
> b pc=$14032
CPU condition breakpoint 5 with 1 condition(s) added:
	pc = $14032
> b pc=$1403e
CPU condition breakpoint 6 with 1 condition(s) added:
	pc = $1403e
> b pc=$14042
CPU condition breakpoint 7 with 1 condition(s) added:
	pc = $14042
> b pc=$1404e
CPU condition breakpoint 8 with 1 condition(s) added:
	pc = $1404e
> b pc=$14052
CPU condition breakpoint 9 with 1 condition(s) added:
	pc = $14052
> b pc=$14056
CPU condition breakpoint 10 with 1 condition(s) added:
	pc = $14056
> 

Show the registers and continue to the first breakpoint, and repeat, watching the stacks, in particular. Check the listing for what should be on the parameter stack before and after each call.

The first call is to OUTS, and the address of the PROMPT string should be on the stack. From $13EE0 at empty stack to $13EDC is four bytes, so that's one address.

> r
  D0 00000000   D1 00000000   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 00013FEA   A1 00000000   A2 00000000   A3 00013D10 
  A4 00014060   A5 00013D10   A6 00013EDC   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) ff50 (ILLEGAL) Chip latch 00000000
0001401a 6100 ff50                bsr.w #$ff50 == $00013f6c
Next PC: 0001401e
> c
Returning to emulation...
2. CPU breakpoint condition(s) matched 1 times.
	pc = $1401e

CPU=$1401e, VBL=3802, FrameCycles=27092, HBL=26, LineCycles=676, DSP=N/A
0001401e 6100 ffb0                bsr.w #$ffb0 == $00013fd0
> r
  D0 00000001   D1 00000000   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 00000000   A1 00002F50   A2 00000000   A3 00014008 
  A4 00014060   A5 00013D10   A6 00013EE0   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=1 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) ffb0 (ILLEGAL) Chip latch 00000000
0001401e 6100 ffb0                bsr.w #$ffb0 == $00013fd0
Next PC: 00014022
> 

When it returns, A6 = $13EE0 shows that the string address has been removed, and the stack is empty again.

Now it calls INCHNE,

> c
Returning to emulation...
3. CPU breakpoint condition(s) matched 1 times.
	pc = $14022

CPU=$14022, VBL=3803, FrameCycles=232088, HBL=228, LineCycles=440, DSP=N/A
00014022 2d16                     move.l (a6) [0000000d],-(a6) [00000000]
> r
  D0 0000000D   D1 00002310   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 00002F50   A1 00002F50   A2 00000000   A3 00014008 
  A4 00014060   A5 00013D10   A6 00013EDC   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 2d16 (MOVE) 41fa (LEA) Chip latch 00000000
00014022 2d16                     move.l (a6) [0000000d],-(a6) [00000000]
Next PC: 00014024
> 

When it returns from INCHNE, it has the character on the stack in a full 4 byte integer ($13EDC). 

I didn't look at the contents of the parameter stack here, but, if you need to, you can use the (m)emory dump command

> m a6 32

to show the top 32 bytes.

It should be noted that the comment "duplicate" in the source code somehow moved two lines below where it should be.

> s

CPU=$14024, VBL=3803, FrameCycles=232108, HBL=228, LineCycles=460, DSP=N/A
00014024 41fa ffe2                lea.l (pc,$ffe2) == $00014008,a0
> s

CPU=$14028, VBL=3803, FrameCycles=232116, HBL=228, LineCycles=468, DSP=N/A
00014028 2d08                     move.l a0,-(a6) [00000000]
> s

CPU=$1402a, VBL=3803, FrameCycles=232128, HBL=228, LineCycles=480, DSP=N/A
0001402a 6100 ff40                bsr.w #$ff40 == $00013f6c
> r
  D0 0000000D   D1 00002310   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 00014008   A1 00002F50   A2 00000000   A3 00014008 
  A4 00014060   A5 00013D10   A6 00013ED4   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) ff40 (ILLEGAL) Chip latch 00000000
0001402a 6100 ff40                bsr.w #$ff40 == $00013f6c
Next PC: 0001402e
> 

At this point, A6 is $13Ed4 -- three integers on stack: the character input, a copy (duplicate) of the character, and a pointer to bit of leader text to demarcate it. And we're going to call OUTS and OUTC, to show the character.

> c
Returning to emulation...
4. CPU breakpoint condition(s) matched 1 times.
	pc = $1402e

CPU=$1402e, VBL=3803, FrameCycles=243532, HBL=239, LineCycles=708, DSP=N/A
0001402e 6100 ff24                bsr.w #$ff24 == $00013f54
> r
  D0 00000001   D1 0007A309   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 000000A0   A1 00002F50   A2 00000000   A3 0001400D 
  A4 00014060   A5 00013D10   A6 00013ED8   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=1 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) ff24 (ILLEGAL) Chip latch 00000000
0001402e 6100 ff24                bsr.w #$ff24 == $00013f54
Next PC: 00014032
> c
Returning to emulation...
5. CPU breakpoint condition(s) matched 1 times.
	pc = $14032

CPU=$14032, VBL=3803, FrameCycles=245588, HBL=241, LineCycles=732, DSP=N/A
00014032 2d16                     move.l (a6) [0000000d],-(a6) [0000000d]
> 

I should have shown the registers again, to show that $A6 was back to $13EDC after the call. But I stepped. It's okay, we can deduce where things were from the next register dump.

> s

CPU=$14034, VBL=3803, FrameCycles=245608, HBL=241, LineCycles=752, DSP=N/A
00014034 41fa ffd7                lea.l (pc,$ffd7) == $0001400d,a0
> s

CPU=$14038, VBL=3803, FrameCycles=245616, HBL=241, LineCycles=760, DSP=N/A
00014038 2d08                     move.l a0,-(a6) [00014008]
> s

CPU=$1403a, VBL=3803, FrameCycles=245628, HBL=241, LineCycles=772, DSP=N/A
0001403a 6100 ff30                bsr.w #$ff30 == $00013f6c
> r
  D0 00000001   D1 00000000   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 0000000D 
  A0 0001400D   A1 00002F50   A2 00000000   A3 0001400D 
  A4 00014060   A5 00013D10   A6 00013ED4   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) ff30 (ILLEGAL) Chip latch 00000000
0001403a 6100 ff30                bsr.w #$ff30 == $00013f6c
Next PC: 0001403e
> 

Before the call to put the colon before the character code in binary out, the character, a copy, and the string address on stack -- $13ED4.

> c
Returning to emulation...
6. CPU breakpoint condition(s) matched 1 times.
	pc = $1403e

CPU=$1403e, VBL=3803, FrameCycles=248512, HBL=244, LineCycles=608, DSP=N/A
0001403e 6100 fef0                bsr.w #$fef0 == $00013f30
> r
  D0 00000001   D1 0007A304   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 000000A0   A1 00002F50   A2 00000000   A3 0001400F 
  A4 00014060   A5 00013D10   A6 00013ED8   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=1 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) fef0 (ILLEGAL) Chip latch 00000000
0001403e 6100 fef0                bsr.w #$fef0 == $00013f30
Next PC: 00014042
> 

Between the call to put the colon out and the call to put the binary character code out. The character and a copy on the stack -- $13ED8.

Next we let it call OUTB8:

> c
Returning to emulation...
7. CPU breakpoint condition(s) matched 1 times.
	pc = $14042

CPU=$14042, VBL=3804, FrameCycles=7176, HBL=7, LineCycles=64, DSP=N/A
00014042 2d16                     move.l (a6) [0001400d],-(a6) [00000000]
> r
  D0 00000001   D1 0007A314   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000031 
  A0 000000A0   A1 00002F50   A2 00000000   A3 0001400F 
  A4 00014060   A5 00013D10   A6 00013ED4   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=1 V=0 C=0 IMASK=3 STP=0
Prefetch 2d16 (MOVE) 41fa (LEA) Chip latch 00000000
00014042 2d16                     move.l (a6) [0001400d],-(a6) [00000000]
Next PC: 00014044
> 

After the binary output, A6 is $13ED4. It should be back to just the character on the stack, $13EDC.

But let's trace through and see what else we can see.

> s

CPU=$14044, VBL=3804, FrameCycles=7196, HBL=7, LineCycles=84, DSP=N/A
00014044 41fa ffc9                lea.l (pc,$ffc9) == $0001400f,a0
> s

CPU=$14048, VBL=3804, FrameCycles=7204, HBL=7, LineCycles=92, DSP=N/A
00014048 2d08                     move.l a0,-(a6) [00000000]
> s

CPU=$1404a, VBL=3804, FrameCycles=7216, HBL=7, LineCycles=104, DSP=N/A
0001404a 6100 ff20                bsr.w #$ff20 == $00013f6c
> r
  D0 00000001   D1 0007A314   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000031 
  A0 0001400F   A1 00002F50   A2 00000000   A3 0001400F 
  A4 00014060   A5 00013D10   A6 00013ECC   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) ff20 (ILLEGAL) Chip latch 00000000
0001404a 6100 ff20                bsr.w #$ff20 == $00013f6c
Next PC: 0001404e
> c
Returning to emulation...
8. CPU breakpoint condition(s) matched 1 times.
	pc = $1404e

CPU=$1404e, VBL=3804, FrameCycles=15812, HBL=15, LineCycles=572, DSP=N/A
0001404e 6100 feb0                bsr.w #$feb0 == $00013f00
> r
  D0 00000001   D1 0007A319   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000000   D7 00000000 
  A0 000000A0   A1 00002F50   A2 00000000   A3 00014013 
  A4 00014060   A5 00013D10   A6 00013ED0   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=1 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) feb0 (ILLEGAL) Chip latch 00000000
0001404e 6100 feb0                bsr.w #$feb0 == $00013f00
Next PC: 00014052
> c
Returning to emulation...
9. CPU breakpoint condition(s) matched 1 times.
	pc = $14052

CPU=$14052, VBL=3804, FrameCycles=21640, HBL=21, LineCycles=304, DSP=N/A
00014052 6100 fef6                bsr.w #$fef6 == $00013f4a
> r
  D0 00000001   D1 0007A31D   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000044   D7 00000044 
  A0 000000A0   A1 00002F50   A2 00000000   A3 00014013 
  A4 00014060   A5 00013D10   A6 00013ED4   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) fef6 (ILLEGAL) Chip latch 00000000
00014052 6100 fef6                bsr.w #$fef6 == $00013f4a
Next PC: 00014056
> c
Returning to emulation...
10. CPU breakpoint condition(s) matched 1 times.
	pc = $14056

CPU=$14056, VBL=3804, FrameCycles=27624, HBL=27, LineCycles=192, DSP=N/A
00014056 221e                     move.l (a6)+ [0001400d],d1
> r
  D0 00000001   D1 00000000   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000044   D7 0000000A 
  A0 00000000   A1 00002F50   A2 00000000   A3 00014013 
  A4 00014060   A5 00013D10   A6 00013ED4   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 221e (MOVE) b23c (CMP) Chip latch 00000000
00014056 221e                     move.l (a6)+ [0001400d],d1
Next PC: 00014058
> s

CPU=$14058, VBL=3804, FrameCycles=27636, HBL=27, LineCycles=204, DSP=N/A
00014058 b23c 0051                cmp.b #$51,d1
> s

CPU=$1405c, VBL=3804, FrameCycles=27644, HBL=27, LineCycles=212, DSP=N/A
0001405c 66b6                     bne.b #$b6 == $00014014 (T)
> r
  D0 00000001   D1 0001400D   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000044   D7 0000000A 
  A0 00000000   A1 00002F50   A2 00000000   A3 00014013 
  A4 00014060   A5 00013D10   A6 00013ED8   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=1 Z=0 V=0 C=1 IMASK=3 STP=0
Prefetch 66b6 (Bcc) 4e75 (RTS) Chip latch 00000000
0001405c 66b6                     bne.b #$b6 == $00014014 (T)
Next PC: 0001405e
> s

CPU=$14014, VBL=3804, FrameCycles=27656, HBL=27, LineCycles=224, DSP=N/A
00014014 41fa ffd4                lea.l (pc,$ffd4) == $00013fea,a0
> s

CPU=$14018, VBL=3804, FrameCycles=27664, HBL=27, LineCycles=232, DSP=N/A
00014018 2d08                     move.l a0,-(a6) [0001400d]
> s

CPU=$1401a, VBL=3804, FrameCycles=27676, HBL=27, LineCycles=244, DSP=N/A
0001401a 6100 ff50                bsr.w #$ff50 == $00013f6c
> r
  D0 00000001   D1 0001400D   D2 00000000   D3 00000000 
  D4 00000000   D5 00000000   D6 00000044   D7 0000000A 
  A0 00013FEA   A1 00002F50   A2 00000000   A3 00014013 
  A4 00014060   A5 00013D10   A6 00013ED4   A7 00013E54 
USP  00013E54 ISP  00007E64 
T=00 S=0 M=0 X=0 N=0 Z=0 V=0 C=0 IMASK=3 STP=0
Prefetch 6100 (BSR) ff50 (ILLEGAL) Chip latch 00000000
0001401a 6100 ff50                bsr.w #$ff50 == $00013f6c
Next PC: 0001401e
> 

And as we watched it through, OUTHX8 and OUTNWLN did what was expected on the stack, leaving the stack with the 8 extra bytes OUTB8 left us with still on the stack for the beginning of the next go through.

Which explains why A6 was expanding until it walked on the return stack, and why, when the CPU tried to return to something that was not the return address, we ended up trying to execute who knows what, with A7 set to who knows what.

So, we could look at OUTB8 in rt_rig03_68K.s and see that, yes, indeed, we do  seem to be subtracting 4 from A6 on exit instead of adding 4 to drop the parameter and the temporary variable.

Maybe I got confused about which way the stack shrinks when I decided to use ADDQ/SUBQ?

* Output the 8-bit number on the stack in binary (base two).
* For consistency, we are passing the byte in the lowest-order byte
* of a 32-bit word.
* Uses D6, D7.
OUTB8	MOVE.L	(A6),D6	; shift on memory is 16-bit, use register
	MOVE.W	#8,(A6)	; 8 bits to output, borrow parameter high word.
OUTB8L	LSL.B	#1,D6	; Get the leftmost bit of the lowest byte.
	BCS.S	OUTB81
OUTB80	MOVEQ.L	#'0',D7
	BRA.S	OUTB8D
OUTB81	MOVEQ.L	#'1',D7
OUTB8D	BSR.S	OUTCV
	SUBQ.W	#1,(A6)
	BNE.S	OUTB8L	; loop if not Zero
	SUBQ.L	#NATWID,A6	; drop parameter character
	RTS

And subtracting 4 instead of adding 4 would, indeed, leave 8 bytes too many on exit.

But wouldn't OUT8HX then output trash on the stack, probably something that was not the character code at all?

Clear the breakpoints and let it run and see if it does.

 

Oh. That's what's happening. And it won't even respond to Q for quit. The only way out is to crash it. Ouch.

Are we convinced?

Then why put the balance checks in? Just for practice?

Yeah. Practice is good.

If we do a quick search through the code, we aren't using D3 through D5 anywhere at this point. We only need to put stack checks in the main routine and in the OUTB8 routine, so two markers should be sufficient. No need for nesting markers, either.

If we use D3 to mark the stack in the PGSTRT and D4 to mark the stack in OUTB8, we don't need to declare variables in memory, and that will help us limit the impact of the debugging code we insert.

For inserting the code, we could talk about conditional assembly, but we really need to start simple, so let's just put marker comments around the code we insert.

Here's what we'll do with OUTB8, with the bug still in place: 

* Output the 8-bit number on the stack in binary (base two).
* For consistency, we are passing the byte in the lowest-order byte
* of a 32-bit word.
* Uses D6, D7.
OUTB8	MOVE.L	(A6),D6	; shift on memory is 16-bit, use register
******
* DEBUG 1 input parameter, no output parameters
	LEA	NATWID(A6),A0	; this is what A6 should be when we leave.
	MOVE.L	A0,D4
* END DEBUG
******
	MOVE.W	#8,(A6)	; 8 bits to output, borrow parameter high word.
OUTB8L	LSL.B	#1,D6	; Get the leftmost bit of the lowest byte.
	BCS.S	OUTB81
OUTB80	MOVEQ.L	#'0',D7
	BRA.S	OUTB8D
OUTB81	MOVEQ.L	#'1',D7
OUTB8D	BSR.S	OUTCV
	SUBQ.W	#1,(A6)
	BNE.S	OUTB8L	; loop if not Zero
	SUBQ.L	#NATWID,A6	; drop parameter character
******
* DEBUG
	CMP.L	D4,A6
	BNE.W	ERROR
* END DEBUG
******
	RTS
And here's what we'll do with PGSTRT:
	EVEN
PGSTRT	LEA	PROMPT(PC),A0
******
* DEBUG 1 input parameter, no output parameters
	MOVE.L	A6,D3	; this is what A6 should be when we leave.
* END DEBUG
******
	MOVE.L	A0,-(A6)
	BSR.W	OUTS
	BSR.W	INCHNE	; Hold off echo
	MOVE.L	(A6),-(A6)	; duplicate
	LEA	KEYCOL(PC),A0
	MOVE.L	A0,-(A6)
	BSR.W	OUTS
	BSR.W	OUTC	; output character
	MOVE.L	(A6),-(A6)	; duplicate
	LEA	COLBIN(PC),A0
	MOVE.L	A0,-(A6)
	BSR.W	OUTS
	BSR.W	OUTB8
	MOVE.L	(A6),-(A6)	; duplicate
	LEA	COLHEX(PC),A0
	MOVE.L	A0,-(A6)
	BSR.W	OUTS
	BSR.W	OUTHX8
	BSR.W	OUTNWLN
	MOVE.L	(A6)+,D1	; balance stack
	CMP.B	#ASCQ,D1
	BNE.S	PGSTRT
******
* DEBUG
	CMP.L	D3,A6
	BNE.W	ERROR
* END DEBUG
******
	RTS

You should be able to just copy the debug lines and paste them into place. Don't forget the comment lines so you know what to remove.

Add a NOP immediately after DONE, with the label ERROR:

START	BSR.W	INITRT
	NOP		; place to set breakpoint
*
	BSR.W	PGSTRT
*
DONE	NOP		; place to set breakpoint
ERROR	NOP		; place to go on errors
	MOVEM.L	A4SAVE-LOCBAS(A5),A4-A7	; restore the monitor's A4-A7

Assemble it and start an Hatari session, breaking out and setting a breakpoint at TEXT, as usual:

$ vasmm68k_mot -Ftos -no-opt -o INKEY_68K.PRG -L inkey_68K.list inkey_68K.s
vasm 1.9f (c) in 2002-2023 Volker Barthelmann
vasm M68k/CPU32/ColdFire cpu backend 2.6c (c) 2002-2023 Frank Wille
vasm motorola syntax module 3.18 (c) 2002-2023 Frank Wille
vasm tos output module 2.3 (c) 2009-2016,2020,2021,2023 Frank Wille

text(acrx2):	         870 bytes
nova@she:~/usr/share/hatari/C:/primer/char_io/stepinch$ hatari
INFO : Hatari v2.4.0-devel (Dec 18 2021), compiled on:  Dec 18 2021, 11:41:51
INFO : Inserted disk '/home/nova/usr/share/hatari/fig68kwrk.st' to drive A:.
INFO : Inserted disk '/home/nova/usr/share/hatari/stuff.st' to drive B:.
MMU emulation requires 68030/040/060 and it is not JIT compatible.
INFO : Mounting IDE hard drive image /home/nova/work/emu/hatari/hd80mb.image
INFO : GEMDOS HDD emulation, C: <-> /home/nova/usr/share/hatari/C:.
WARN : GEMDOS HD drive C: (may) override ACSI/SCSI/IDE image partitions!
MMU emulation requires 68030/040/060 and it is not JIT compatible.
WARN : Bus Error reading at address $ffffa200, PC=$e00ce2 addr_e3=e00ce2 op_e3=4a10
WARN : No GEMDOS dir '/home/nova/usr/share/hatari/C:/AUTO'

----------------------------------------------------------------------
You have entered debug mode. Type c to continue emulation, h for help.

CPU=$e1d7e2, VBL=1395, FrameCycles=128, HBL=0, LineCycles=128, DSP=N/A
00e1d7e2 46c0                     move.w d0,sr
> b pc=TEXT
CPU condition breakpoint 1 with 1 condition(s) added:
	pc = TEXT
> c
Returning to emulation...

When you run INKEY_68K.PRG (or pretty much anything not built-in) from the EmuTOS console it will break. Step once to the START label and disassemble from the PC:

1. CPU breakpoint condition(s) matched 1 times.
	pc = TEXT
Reading symbols from program '/home/nova/usr/share/hatari/C:/primer/char_io/stepinch/INKEY_68K.PRG' symbol table...
TOS executable, DRI / GST symbol table, reloc=0, program flags: PRIVATE (0x0)
Program section sizes:
  text: 0x366, data: 0x0, bss: 0x0, symtab: 0x33a
Trying to load DRI symbol table at offset 0x382...
Offsetting BSS/DATA symbols from TEXT section.
Skipping duplicate address & symbol name checks when autoload is enabled.
Loaded 57 symbols (42 TEXT) from '/home/nova/usr/share/hatari/C:/primer/char_io/stepinch/INKEY_68K.PRG'.

CPU=$13d10, VBL=102109, FrameCycles=166200, HBL=163, LineCycles=592, DSP=N/A
00013d10 6000 02ac                bra.w #$02ac == $00013fbe (T)
> s

CPU=$13fbe, VBL=102109, FrameCycles=166212, HBL=163, LineCycles=604, DSP=N/A
00013fbe 6100 ff28                bsr.w #$ff28 == $00013ee8
> d
(PC)
START:
00013fbe 6100 ff28                bsr.w #$ff28 == $00013ee8
00013fc2 4e71                     nop 
00013fc4 6100 005c                bsr.w #$005c == $00014022
DONE:
00013fc8 4e71                     nop 
ERROR:
00013fca 4e71                     nop 
00013fcc 4ced f000 0008           movem.l (a5,$0008) == $0001407e,a4-a7
00013fd2 4e71                     nop 
00013fd4 4e71                     nop 
00013fd6 4e71                     nop 
00013fd8 4e71                     nop 
00013fda 4267                     clr.w -(a7) [0000]
00013fdc 4e41                     trap #$01
INCHNE:
00013fde 610a                     bsr.b #$0a == $00013fea
00013fe0 c0bc 0000 ffff           and.l #$0000ffff,d0
00013fe6 2d00                     move.l d0,-(a6) [00000000]
00013fe8 4e75                     rts  == $00000000
INCHV:
00013fea 3f3c 0002                move.w #$0002,-(a7) [0000]
> 

Set two breakpoints, one at the label DONE and one at ERROR (the NOP immediately after). Then continue:

> b pc=$13fc8
CPU condition breakpoint 2 with 1 condition(s) added:
	pc = $13fc8
> b pc=$13fca
CPU condition breakpoint 3 with 1 condition(s) added:
	pc = $13fca
> c
Returning to emulation...

Back in the EmuTOS console, it will be waiting for you to hit a key. The first key you hit, it should break, and the console should not respond. Return to the debugger and check which breakpoint it took. It should be the one at the ERROR label:

3. CPU breakpoint condition(s) matched 1 times.
	pc = $13fca

CPU=$13fca, VBL=140869, FrameCycles=94972, HBL=93, LineCycles=484, DSP=N/A
00013fca 4e71                     nop 
> 

In this case, you can see it was, at $13FCA, which is where we set the second breakpoint. Well, where I set it.

We probably should have set up two ERROR labels, one for OUTB8 to jump to, and one for the main routine, PGSTRT, to jump to. But you can check the return stack for clues:

> r
  D0 00000001   D1 0007A31D   D2 00000000   D3 00013EE0 
  D4 00013EDC   D5 00000000   D6 00000000   D7 00000030 
  A0 000000A0   A1 00002F50   A2 00000000   A3 0001401D 
  A4 00014076   A5 00013D10   A6 00013ED4   A7 00013E50 
USP  00013E50 ISP  00007E64 
T=00 S=0 M=0 X=0 N=1 Z=0 V=0 C=1 IMASK=3 STP=0
Prefetch 4e71 (NOP) 4ced (MVMEL) Chip latch 00000000
00013fca 4e71                     nop 
Next PC: 00013fcc
> m A7 32
00013E50: 00 01 40 52 00 01 3f c8 00 00 00 00 00 00 00 00   ..@R..?.........
00013E60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
>

If i had stepped through the initializations and done a register dump with the stacks set up, we would have an idea what A7 should be.

Those addresses one the return stack should be return addresses, pointing at code:

> d $14052
00014052 2d16                     move.l (a6) [0001401b],-(a6) [00000000]
00014054 41fa ffc7                lea.l (pc,$ffc7) == $0001401d,a0
00014058 2d08                     move.l a0,-(a6) [00000000]
0001405a 6100 ff1c                bsr.w #$ff1c == $00013f78
0001405e 6100 fea0                bsr.w #$fea0 == $00013f00
00014062 6100 fef2                bsr.w #$fef2 == $00013f56
00014066 221e                     move.l (a6)+ [0001401b],d1
00014068 b23c 0051                cmp.b #$51,d1
0001406c 66b4                     bne.b #$b4 == $00014022 (T)
0001406e bdc3                     cmpa.l d3,a6
00014070 6600 ff58                bne.w #$ff58 == $00013fca (T)
00014074 4e75                     rts  == $00014052
00014076 0000 0000                or.b #$00,d0
...
> d $13fc8
DONE:
00013fc8 4e71                     nop 
(PC)
ERROR:
00013fca 4e71                     nop 
00013fcc 4ced f000 0008           movem.l (a5,$0008) == $00013d18,a4-a7
00013fd2 4e71                     nop 
00013fd4 4e71                     nop 
00013fd6 4e71                     nop 
00013fd8 4e71                     nop 
00013fda 4267                     clr.w -(a7) [3f48]
00013fdc 4e41                     trap #$01
INCHNE:
00013fde 610a                     bsr.b #$0a == $00013fea
00013fe0 c0bc 0000 ffff           and.l #$0000ffff,d0
00013fe6 2d00                     move.l d0,-(a6) [00000000]
00013fe8 4e75                     rts  == $00014052
INCHV:
00013fea 3f3c 0002                move.w #$0002,-(a7) [3f48]
00013fee 3f3c 0002                move.w #$0002,-(a7) [3f48]
00013ff2 4e4d                     trap #$0d
00013ff4 588f                     addaq.l #$04,a7
00013ff6 4e75                     rts  == $00014052
> 

That pretty much proves that the jump to ERROR was from OUTB8. 

And we can also look at what's on the parameter stack:

> m a6 32
00013ED4: 00 01 40 1b 00 00 00 6a 00 00 00 6a 00 00 00 00   ..@....j...j....
00013EE4: 00 00 00 00 20 5f 47 fa fe 24 48 eb f0 00 00 08   .... _G..$H.....
> 

We can see two copies of the character of the key I hit, "j".

I wonder what's at $1401b?

> d $1401b
COLBIN:
0001401b 3a00                     move.w d0,d5
COLHEX:
0001401d 3a20                     move.w -(a0) [079c],d5
0001401f 2400                     move.l d0,d2
...

COLBIN and COLHEX. Okay,

> m $1401b 32
0001401B: 3a 00 3a 20 24 00 00 41 fa ff d4 26 0e 2d 08 61   :.: $..A...&.-.a
0001402B: 00 ff 4c 61 00 ff ae 2d 16 41 fa ff e0 2d 08 61   ..La...-.A...-.a
> m $14000 32
00014000: 6e 79 20 6b 65 79 2c 20 51 20 74 6f 20 71 75 69   ny key, Q to qui
00014010: 74 2e 20 0d 0a 00 4b 45 59 3a 00 3a 00 3a 20 24   t. ...KEY:.:.: $
> 

So that's the address of the colon that goes before the binary output, leftover from the call in PGSTRT. Dead on. 

Let's fix the code in OUTB8 and run it with the stack checks in place. I've told you what to delete, and you know what to fix. The question is whether to use

	ADDQ	#NATWID,A6

or

	LEA	NATWID,(A6),A6

Which I will leave up to you, along with actually stepping through the code and proving that it doesn't ERROR out. 

Don't assume that it will work as advertised. Go ahead and take the fifteen minutes or so to try it. Practice is essential. And you may think of something else interesting to try while you're at it.

I've been planning next to build a post-fix (RPN) integer calculator that only does addition and subtraction, in binary, octal, or hexadecimal. We know enough to do this, and, when we have it working, we can add multiplication and division and more interesting stuff.


(Title Page/Index)

 

 

 

 

No comments:

Post a Comment