From the docs: "In single-chip modes the MCU has no external bus. All memory accesses and program fetches are internal". As we are in a single-chip mode (selected by the MODA+MODB tied to GND), we only care about internal memory. Yeah, we already know that the only external memory connected to our MCU is the EEPROM (M25P20) and this is via SPI, no bus involved.
Let's take a look at the entry-point code. IDA was so kind to create a sub called "_Vreset" for us:
Code: Select all
ROM:D1C2 ; public _Vreset
ROM:D1C2 _Vreset: ; DATA XREF: USER_VEC:FFFE↓o
ROM:D1C2 lds #$3FFF
ROM:D1C5 movb #$FD, RPAGE ; RAM Page Index Register
ROM:D1CA movb #$20, byte_12 ; ' '
ROM:D1CF movb #$FF, PPAGE ; Program Page Index Register
ROM:D1D4 ldx #$CDED
ROM:D1D7 ldy 2,x+
ROM:D1D9
ROM:D1D9 loc_D1D9: ; CODE XREF: _Vreset+25↓j
ROM:D1D9 ; _Vreset+39↓j ...
ROM:D1D9 ldaa 5,x+
ROM:D1DB beq loc_D214
ROM:D1DD bpl loc_D1E1
ROM:D1DF leax 2,x
ROM:D1E1
ROM:D1E1 loc_D1E1: ; CODE XREF: _Vreset+1B↑j
ROM:D1E1 bita #$60 ; '`'
ROM:D1E3 bne loc_D1E9
ROM:D1E5 ldy -2,x
ROM:D1E7 bra loc_D1D9
ROM:D1E9 ; ---------------------------------------------------------------------------
ROM:D1E9
ROM:D1E9 loc_D1E9: ; CODE XREF: _Vreset+21↑j
ROM:D1E9 pshx
ROM:D1EA tsta
ROM:D1EB bmi loc_D1FD
ROM:D1ED tfr y, d
ROM:D1EF subd -2,x
ROM:D1F1 ldx -4,x
ROM:D1F3
ROM:D1F3 loc_D1F3: ; CODE XREF: _Vreset+35↓j
ROM:D1F3 movb 1,y+, 1,x+
ROM:D1F7 ibne d, loc_D1F3
ROM:D1FA pulx
ROM:D1FB bra loc_D1D9
ROM:D1FD ; ---------------------------------------------------------------------------
ROM:D1FD
ROM:D1FD loc_D1FD: ; CODE XREF: _Vreset+29↑j
ROM:D1FD ldd -2,x
ROM:D1FF pshd
ROM:D200 movb -5,x, GPAGE ; Global Page Index Register
ROM:D205 ldx -4,x
ROM:D207
ROM:D207 loc_D207: ; CODE XREF: _Vreset+4C↓j
ROM:D207 ldab 1,y+
ROM:D209 gstab 1,x+
ROM:D20C cpy 0,sp
ROM:D20E bcs loc_D207
ROM:D210 pulx
ROM:D211 pulx
ROM:D212 bra loc_D1D9
ROM:D214 ; ---------------------------------------------------------------------------
ROM:D214
ROM:D214 loc_D214: ; CODE XREF: _Vreset+19↑j
ROM:D214 ldx #$2002
ROM:D217 clrb
ROM:D218 bra loc_D21C
ROM:D21A ; ---------------------------------------------------------------------------
ROM:D21A
ROM:D21A loc_D21A: ; CODE XREF: _Vreset+5D↓j
ROM:D21A std 2,x+
ROM:D21C
ROM:D21C loc_D21C: ; CODE XREF: _Vreset+56↑j
ROM:D21C cpx #$2002
ROM:D21F bcs loc_D21A
ROM:D221 movb #$7F, GPAGE ; Global Page Index Register
ROM:D226 jsr sub_EF89
ROM:D229
ROM:D229 loc_D229: ; CODE XREF: _Vreset:loc_D229↓j
ROM:D229 bra loc_D229
ROM:D229 ; End of function _Vreset
The Mnemonic "LDS" set's the stack-pointer (SP) to 0x3FFF. The stack is used for data and return-addresses, e.g. when a sub-function is called within the code, the CPU will change the program-counter (PC) to that new address. But before, the addess of the next command is placed (pushed) onto the stack. This way a "RET" command (return from subfunction) can simply pick the address from the stack an set PC to it. So for the nature of stacks, the address denotes the highest possible memory location and the stack grows downwards in address range. Usually there is no lower limit for the stack, so the program itself needs to take care that it does not overlap into other regions. Also such stacks are located in RAM.
In our case, surprise surprise, the SP is set to the highest possible RAM address 0x3FFF (see memory map above).
Next it set's the RAM-PAGE register, which is located at 0x0016 with the immediate value 0xFD. This register holds which part of the 32KB RAM is mapped into the 4KB memory window (page) in the address range 0x1000 - 0x1FFF.
Think of the RPAGE number as an index to choose which part (page) of the available 32KB RAM should be shown (mapped) in the local address space of the CPU.
Again docs help: "The reset value of 0xFD ensures that there is a linear RAM space available between addresses 0x1000 and 0x3FFF out of reset
The fixed 4K page from 0x2000–0x2FFF of RAM is equivalent to page 254 (page number 0xFE).
The fixed 4K page from 0x3000–0x3FFF of RAM is equivalent to page 255 (page number 0xFF)."
Now we have three 4KB RAM pages linear available from 0x1000 up to 0x3FFF, which is 12KB of RAM. Usually the RAM is unitialized, so it can have 0x00 but also any other, arbitrary content out of reset.
The next command is a bit creepy, as it writes the value 0x20 to a reserved resgister located at 0x0012. The docs don't tell anything about it, maybe it's custom?
After that, the Flash page (PPAGE register at 0x0030) is set to 0xFF, which selects the highest Flash location to be shown in the 16KB window from 0x8000-0xBFFF. Page index 0xFD is fixed shown from 0x4000-0x7FFF (below the page window) and 0xFF at 0xC000-0xFEFF (above page window). This is remarkable, as it means that flash content from 0xFF0000 is mirrored at 0xC000 and 0x8000. I head expected page index 0xFE here, as it would give a linear space from flash at 0x4000 up to 0xFFEF... but that may be changed later.
Flash pages can we switched by code writing into the PPAGE register directly, or by some JMP or CALL commands, which can carry a desired page with the target address. Those commands push the current page-index onto the stack and the corresponding return commands restore them later.
"ldx #$CDED"
loads register X (a 16 Bit register) with immediate value 0xCDED. (beware that the hash-symbol "#" in front of a number denote to load the value itself, otherwise it would load the content of a memory location)
"ldy 2,x+"
The adressing mode here is a classical indexing access, like done with pointers in structs of C. The meaning ist to load the two byte content found at memory location where register X is pointing to into register Y. After that operation, the address in register X is incremented by 2, so it points to the next byte following.
The name "loc_D1D9" denotes, that some other parts of the firmware jumps back here.
"ldaa 5,x+"
Load accumulator register "A" (the upper half of register "D") with value found at memory location X is pointing to. X will be incremented by 5 after the operation.
"beq loc_D214"
Jumps to address "loc_D214" if Zero-flag is set. This was done by the former operation if the loaded value of A is "0".
Using the debugger
The USBDM suite brings also a debugger /commandline-interpreter. (usbdmscript.exe). It works like the Segger one. Start it, choose chip-type and interface, connect to device (without any power applied!) and go...
Code: Select all
% settarget hcs12x
USBDM DLL Version = 4.12.1.262
BDM List:
0 - USBDM-JS16-SWD_SER-0001 : USBDM HCS08,HCS12,CFV1,ARM-SWD BDM
Found 1 devices
:setTarget HCS12
% openbdm
Opening USBDM-JS16-SWD_SER-0001
BDM Version = HW=97, SW=4C
% connect
:connect
BDM status => Ackn, Speed-sync, Vpp-Off, Vdd-External, RSTO=1, No Reset, CFVx-running
Speed = 2009 kHz (3823 ticks, sync=63.7 us)
Ackn, Speed-sync, Vpp-Off, Vdd-External, RSTO=1, No Reset, CFVx-running
%
Now we can simply step each instruction and show it's side effects on memory and registers!
![Smile :-)](./images/smilies/icon_e_smile.gif)
Use "regs" command to show the current register status:
Code: Select all
% regs
regs
PC =524D, D = 0, X = 0, Y = 0, SP = 0,
PC = 0x524D : 0x0402 0x0201
Here you see that registers are empty after reset and PC is set to 0x524D for whatever reason. But we don't care, as we will set the PC to a value we got from the reset vector table 0xD1C2 (start of code) using the "wpc" command:
Code: Select all
% wpc 0xD1C2
:wpc 0xD1C2
% regs
regs
PC =D1C2, D = 0, X = 0, Y = 0, SP = 0,
PC = 0xD1C2 : 0xCF3F 0xFF18
Now we can comfortably single-step each instruction:
Code: Select all
% step
:step, from PC = 0x0000D1C2 : 0xCF3F 0xFF18, to PC = 0x0000D1C5 : 0x180B 0xFD00,
% regs
regs
PC =D1C5, D = 0, X = 0, Y = 0, SP =3FFF,
PC = 0xD1C5 : 0x180B 0xFD00
see as SP was set to 0x3FFF
Code: Select all
% step
:step, from PC = 0x0000D1C5 : 0x180B 0xFD00, to PC = 0x0000D1CA : 0x180B 0x2000,
% regs
regs
PC =D1CA, D = 0, X = 0, Y = 0, SP =3FFF,
PC = 0xD1CA : 0x180B 0x2000
% step
:step, from PC = 0x0000D1CA : 0x180B 0x2000, to PC = 0x0000D1CF : 0x180B 0xFF00,
% regs
regs
PC =D1CF, D = 0, X = 0, Y = 0, SP =3FFF,
PC = 0xD1CF : 0x180B 0xFF00
% step
:step, from PC = 0x0000D1CF : 0x180B 0xFF00, to PC = 0x0000D1D4 : 0xCECD 0xEDED,
% regs
regs
PC =D1D4, D = 0, X = 0, Y = 0, SP =3FFF,
PC = 0xD1D4 : 0xCECD 0xEDED
% step
:step, from PC = 0x0000D1D4 : 0xCECD 0xEDED, to PC = 0x0000D1D7 : 0xED31 0xA634,
% regs
regs
PC =D1D7, D = 0, X =CDED, Y = 0, SP =3FFF,
PC = 0xD1D7 : 0xED31 0xA634
here, register "X" was loaded with 0xCDED
Code: Select all
% step
:step, from PC = 0x0000D1D7 : 0xED31 0xA634, to PC = 0x0000D1D9 : 0xA634 0x2737,
% regs
regs
PC =D1D9, D = 0, X =CDEF, Y =CDF7, SP =3FFF,
PC = 0xD1D9 : 0xA634 0x2737
% step
:step, from PC = 0x0000D1D9 : 0xA634 0x2737, to PC = 0x0000D1DB : 0x2737 0x2A02,
% regs
regs
PC =D1DB, D =E400, X =CDF4, Y =CDF7, SP =3FFF,
PC = 0xD1DB : 0x2737 0x2A02
% step
:step, from PC = 0x0000D1DB : 0x2737 0x2A02, to PC = 0x0000D1DD : 0x2A02 0x1A02,
% regs
regs
PC =D1DD, D =E400, X =CDF4, Y =CDF7, SP =3FFF,
PC = 0xD1DD : 0x2A02 0x1A02
% step
:step, from PC = 0x0000D1DD : 0x2A02 0x1A02, to PC = 0x0000D1DF : 0x1A02 0x8560,
% regs
regs
PC =D1DF, D =E400, X =CDF4, Y =CDF7, SP =3FFF,
PC = 0xD1DF : 0x1A02 0x8560
% step
:step, from PC = 0x0000D1DF : 0x1A02 0x8560, to PC = 0x0000D1E1 : 0x8560 0x2604,
% regs
regs
PC =D1E1, D =E400, X =CDF6, Y =CDF7, SP =3FFF,
PC = 0xD1E1 : 0x8560 0x2604
% step
:step, from PC = 0x0000D1E1 : 0x8560 0x2604, to PC = 0x0000D1E3 : 0x2604 0xED1E,
% regs
regs
PC =D1E3, D =E400, X =CDF6, Y =CDF7, SP =3FFF,
PC = 0xD1E3 : 0x2604 0xED1E
% step
:step, from PC = 0x0000D1E3 : 0x2604 0xED1E, to PC = 0x0000D1E9 : 0x3497 0x2B10,
% regs
regs
PC =D1E9, D =E400, X =CDF6, Y =CDF7, SP =3FFF,
PC = 0xD1E9 : 0x3497 0x2B10
% step
:step, from PC = 0x0000D1E9 : 0x3497 0x2B10, to PC = 0x0000D1EA : 0x972B 0x10B7,
% regs
regs
PC =D1EA, D =E400, X =CDF6, Y =CDF7, SP =3FFD,
PC = 0xD1EA : 0x972B 0x10B7
% step
:step, from PC = 0x0000D1EA : 0x972B 0x10B7, to PC = 0x0000D1EB : 0x2B10 0xB764,
% regs
regs
PC =D1EB, D =E400, X =CDF6, Y =CDF7, SP =3FFD,
PC = 0xD1EB : 0x2B10 0xB764
% step
:step, from PC = 0x0000D1EB : 0x2B10 0xB764, to PC = 0x0000D1FD : 0xEC1E 0x3B18,
% regs
regs
PC =D1FD, D =E400, X =CDF6, Y =CDF7, SP =3FFD,
PC = 0xD1FD : 0xEC1E 0x3B18
%