4

I am working on some disassembly from an NEC 78K0R. Most functions have parameters passed in registers, but there are some functions that have compile time defined parameters passed using a method I have not seen before.

Execution of instructions is from flash.

The function, 0x4c4, is called as follows:

027d9        fdc404      CALL            !4C4H
027dc        02          ?               ?
027dd        75          MOV             D,A

The return address is set to 0x27dc and put into the stack.

The function itself is as follows:

// Entry point from call
004c4        c1          PUSH            AX
004c5        c3          PUSH            BC
004c6        c5          PUSH            DE
004c7        c7          PUSH            HL

// Get the stack pointer into HL
004c8        aef8        MOVW            AX,SP
004ca        16          MOVW            HL,AX
004cb        c7          PUSH            HL

// Preserve the ES (extended segment) register used for addressing
004cc        8efd        MOV             A,ES
004ce        c1          PUSH            AX

// Retreive SP + 0x0A - this is the ES part of the return address
004cf        8c0a        MOV             A,[HL+0AH] 
004d1        9efd        MOV             ES,A

// Retreive SP + 0x8 - this is the lower two bytes of return address
004d3        ac08        MOVW            AX,[HL+8H] 
004d5        16          MOVW            HL,AX

// Take the contents of memory from the return address into A
004d6        118b        MOV             A,ES:[HL]

// Not relevant to question
004d8        74          MOV             E,A
004d9        c0          POP             AX
004da        9efd        MOV             ES,A
004dc        5500        MOV             D,#0H

// HL is the original return address - shift up one to skip to instruction and store back into stack
004de        a7          INCW            HL
004df        17          MOVW            AX,HL
004e0        c6          POP             HL
004e1        bc08        MOVW            [HL+8H],AX

So, in short - the parameter 02 is stored in the flash memory in the next address. This is retrieved by using the return address from the stack, and then the return address in the stack in incremented by one so that we get to the next real instruction.

The function shifts about the contents of the stack, but I do not think this is material to the problem.

I've not seen this method used before - it's rather curious. I can't work out why it would be done - that's a lot of instructions required, as compared to doing:

MOV A,#2
CALL !4c4H
<use A as parameter>

Has anyone got any insights to why this has been done like this?

Edit

As requested, the entire function:

004c4        c1          PUSH            AX
004c5        c3          PUSH            BC
004c6        c5          PUSH            DE
004c7        c7          PUSH            HL
    004c8        aef8        MOVW            AX,SP
    004ca        16          MOVW            HL,AX
    004cb        c7          PUSH            HL
        004cc        8efd        MOV             A,ES
        004ce        c1          PUSH            AX
            004cf        8c0a        MOV             A,[HL+0AH] 
            004d1        9efd        MOV             ES,A
            004d3        ac08        MOVW            AX,[HL+8H] 
            004d5        16          MOVW            HL,AX
            004d6        118b        MOV             A,ES:[HL]
            004d8        74          MOV             E,A
        004d9        c0          POP             AX
        004da        9efd        MOV             ES,A
        004dc        5500        MOV             D,#0H
        004de        a7          INCW            HL
        004df        17          MOVW            AX,HL
    004e0        c6          POP             HL
    004e1        bc08        MOVW            [HL+8H],AX
    004e3        17          MOVW            AX,HL
    004e4        25          SUBW            AX,DE
    004e5        bef8        MOVW            SP,AX
    004e7        c5          PUSH            DE
        004e8        14          MOVW            DE,AX
        004e9        320c00      MOVW            BC,#0CH
        004ec        8b          MOV             A,[HL]
        004ed        99          MOV             [DE],A
        004ee        a7          INCW            HL
        004ef        a5          INCW            DE
        004f0        b3          DECW            BC
        004f1        6171        XOR             A,A
        004f3        616a        OR              A,C
        004f5        616b        OR              A,B
        004f7        dff3        BNZ             $4ECH
    004f9        c2          POP             BC
    004fa        aef8        MOVW            AX,SP
    004fc        040c00      ADDW            AX,#0CH
    004ff        14          MOVW            DE,AX
    00500        3620fe      MOVW            HL,#0FE20H
    00503        fd6104      CALL            !461H
00506        c6          POP             HL
00507        c4          POP             DE
00508        c2          POP             BC
00509        c0          POP             AX
0050a        d7          RET      

Sub 461 called from above:

            00461        c1          PUSH            AX
            00462        c3          PUSH            BC
        00463        61dd        PUSH            PSW
            00465        17          MOVW            AX,HL
            00466        25          SUBW            AX,DE
            00467        de18        BNC             $481H
            00469        37          XCHW            AX,HL
            0046a        03          ADDW            AX,BC
            0046b        37          XCHW            AX,HL
            0046c        35          XCHW            AX,DE
            0046d        03          ADDW            AX,BC
            0046e        35          XCHW            AX,DE
            0046f        61cd        POP             PSW
            00471        b7          DECW            HL
            00472        b5          DECW            DE
            00473        8b          MOV             A,[HL]
            00474        99          MOV             [DE],A
            00475        b3          DECW            BC
            00476        6171        XOR             A,A
            00478        616a        OR              A,C
            0047a        616b        OR              A,B
            0047c        dff3        BNZ             $471H
        0047e        ee1000      BR              $!491H
        00481        61cd        POP             PSW
        00483        c5          PUSH            DE 
        00484        c7          PUSH            HL
            00485        8b          MOV             A,[HL]
            00486        99          MOV             [DE],A
            00487        a7          INCW            HL
            00488        a5          INCW            DE
            00489        b3          DECW            BC
            0048a        13          MOVW            AX,BC
            0048b        6168        OR              A,X
            0048d        dff6        BNZ             $485H
        0048f        c6          POP             HL
        00490        c4          POP             DE
    00491        c2          POP             BC
    00492        c0          POP             AX
    00493        d7          RET  

And also the pre/post of the call:

024fe        c5          PUSH            DE
024ff        fdc404      CALL            !4C4H
02502        06          
02503        9d24        MOV             0FFE24H,A
02505        33          XCHW            AX,BC
02506        bd20        MOVW            0FFE20H,AX

1 Answer 1

3

This approach (function call followed by data) is very often used to implement switches/table jumps, especially on platforms with limited flash space (so they prefer to not inline switch code but use a helper routine). I've seen it done on ARM, 8051 and some other CPUs. Since the switch data (values and offsets) is not going to change, it makes sense to put it into the code flash space.

EDIT: thanks for the full listing. So apparently that value is used to decrement SP, then 12 bytes of saved registers are copied there, then something else happens in sub 461H.

At a guess, this sets up the caller's function frame (allocates space for local vars). As to why it's a code byte and not a direct argument, I have only one theory: to save flash space because only one byte is necessary as opposed to the MOV (at least 2 bytes) or MOV + PUSH (at least 3 bytes) instructions.

I'd suggest finding out which compiler was used for this code (Renesas? KPIT?) and searching for this function in runtime libraries. The symbols should help here.

EDIT2: sub 461 seems to be memmove(DE, HL, BC) with copy direction detection. So it's copying N bytes from FE20 into the newly allocated stack space. Weird stuff.

12
  • Ok - I can understand it if we are looking at jump table as it will be a significant number of parameters. However, this is just a single parameter, a single byte. The microcontroller has 128KB of flash - not huge but large for the type of device. Sep 13, 2013 at 11:20
  • 1
    Are you sure it's indeed a single byte and not just your disassembler skipping one byte and restarting disassembly from next valid instruction? Maybe paste the rest of the function to see what it does.
    – Igor Skochinsky
    Sep 13, 2013 at 11:49
  • I'm running it in a simulator and also on the hardware with debug and seeing it return to the normal return address + 1, as well as my interpretation saying that. Code added above. Sep 13, 2013 at 14:17
  • Double checked - if using the interactive simulator or hardware debug, as soon as it returns to a incorrectly diassembled instruction, it re-aligns to correct itself. It's certainly what it is doing, just can't work out why. Sep 13, 2013 at 17:01
  • 1
    on the Apple II, this technique saved on symbol space during compilation, by avoiding the need to declare any labels for the data that were copied. Perhaps something similar here? Sep 13, 2013 at 18:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.