指令的执行是从flash中执行的。
函数0x4c4的调用如下:
027d9 fdc404 CALL !4C4H
027dc 02 ? ?
027dd 75 MOV D,A
返回地址设置为0x27dc并放入堆栈中。
函数本身如下:
// 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
因此,简而言之-参数02存储在闪存的下一个地址中。这是通过使用堆栈中的返回地址来检索的,然后将堆栈中的返回地址加1,以便我们进入下一条实数指令。
该函数将在堆栈,但我认为这对问题不重要。
我之前从未见过使用此方法-这很好奇。我不知道为什么会这样做-与这样做相比,这需要大量说明:
MOV A,#2
CALL !4c4H
<use A as parameter>
有谁对为什么这样做有任何见解
编辑
按要求完成整个功能:
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 ECH
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
从461调用以上:
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 1H
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 1H
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 5H
0048f c6 POP HL
00490 c4 POP DE
00491 c2 POP BC
00492 c0 POP AX
00493 d7 RET
以及通话的前/后:
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 楼
这种方法(函数调用后跟数据)通常用于实现开关/表跳转,尤其是在闪存空间有限的平台上(因此,他们宁愿不内联开关代码,而使用帮助程序)。我已经看到它在ARM,8051和其他一些CPU上完成了。由于开关数据(值和偏移量)不会改变,因此将其放入代码闪存空间是有意义的。编辑:感谢完整列表。因此很显然,该值用于减少SP,然后将12个字节的已保存寄存器复制到那里,然后在
sub 461H
中发生其他情况。猜测,这将设置调用方的功能框架(为本地变量)。至于为什么它是一个代码字节而不是直接参数,我只有一个理论:节省闪存空间,因为与
MOV
(至少2个字节)或MOV + PUSH
(至少3个字节)指令相比,只需要一个字节。 我建议找出用于此代码的编译器(瑞萨(Renesas)?KPIT?),然后在运行时库中搜索此函数。这些符号应在此处提供帮助。
EDIT2:sub 461似乎是带有复制方向检测的
memmove(DE, HL, BC)
。因此,它将N个字节从FE20复制到新分配的堆栈空间中。奇怪的东西。
评论
好的-如果我们查看跳转表,我会理解的,因为它将是很多参数。但是,这只是一个参数,一个字节。该微控制器具有128KB的闪存-虽然不大,但对于设备类型而言却很大。
– Cybergibbons
2013年9月13日在11:20
您确定它确实是一个字节,而不仅仅是您的反汇编程序跳过一个字节并从下一条有效指令重新开始反汇编吗?也许粘贴其余的功能以查看其功能。
–伊戈尔·斯科钦斯基♦
2013年9月13日在11:49
我正在模拟器中运行它,还在带有调试功能的硬件上运行它,看到它返回到正常的返回地址+ 1,以及我的解释。上面添加了代码。
– Cybergibbons
2013年9月13日14:17在
仔细检查-如果使用交互式模拟器或硬件调试,则返回错误组装的指令后,它将重新对齐以更正自身。这肯定是它正在做的事情,只是无法弄清楚原因。
– Cybergibbons
2013年9月13日在17:01
在Apple II上,此技术避免了为复制的数据声明任何标签,从而在编译期间节省了符号空间。也许这里有些相似?
–彼得·弗里
2013年9月13日在18:42