我正在遵循兰迪·海德的书“使用6502汇编语言”中的A教程。在第14章第7节中,他写了"jsr incrtn“,问题是他没有创建一个名为incrtn的子例程,这里是完整的代码,
PRTSTR:
STA ASAVE
STY YSAVE
PLA ;GET RETURN ADDRESS FROM
STA RTNADR ;THE 6502 STACK
PLA
STA RTNADR+$1
;
JSR INCRTN ;INCREMEOT THE RETURN ADDRESS
LDY #$0
LDA (RTNADR),Y ;GET L.O. ADDRESS OF STRING
STA ZPAGE
INY
LDA (RTNADR),Y ;GET H.O.ADDRESS OF STRING
STA ZPAGE+$1
;
JSR INCRTN ;MOVE RTNADR PAST THE ADDRESS
JSR INCRTN ;BYTES
;
;
; AT THIS POINT, ZPAGE POINTS TO THE STRING WHICH
; IS SUPPOSED TO BE OUTPUT
;
DEY ;RESET Y REG TO ZERO
LDA (ZPAGE),Y ;GET THE LENGTH OF THE STRING
STA LENGTH ;AND STORE IT IN "LENGTH"
PRTS1 INY ;MOVE TO THE NEXT CHARACTER
CPY LENGTH ;ARE WE THROUGH YET?
BEQ PRTS2
;
LDA (ZPAGE),Y ;GET THIS CHARACTER
JSR COUT ;AND OUTPUT
JMP PRTS1 ;MOVE TO NEXT CHAR AND REPEAT
;
PRTS2 LDA ASAVE ;RESTORE THE REGISTERS
LDY YSAVE
JMP (RTNADR) ;SIMULATE AN RTS
;
;
;
ASAVE EPZ $0 ;ZERO PAGE WORKSPACE
YSAVE EPZ ASAVE+$1
ZPAGE EPZ YSAVE+$1
RTNADR EPZ ZPAGE+$2
COUT EQU $FDED ;COUT ROUTINE
END有谁可以帮我?
更新,如果有人想知道如何以更简单的方式打印文本,这里有一些有效的代码
LDX #$0
LOOP INX
LDA STRING,X
JSR $FDF0
CPX STRING
BLT LOOP
RTS
STRING STR "hello world"
END发布于 2019-12-14 03:59:23
在处理器具有堆栈或足够的寄存器之前,参数通常通过“内联”参数传递,即在JSR或call类型的指令之后传递参数,方法是将值或指针直接放在call指令之后,要求被调用者在返回以恢复调用者时跳过它们。
正如你所知道的,调用指令捕获一个返回地址(以某种方式,在某个地方:可能在堆栈或子例程的代码内存中!),该返回地址可用于获取放置在调用指令之后的参数。*返回地址递增以获得连续的参数,完成后,需要再递增一次(递增1或2)以返回调用者的实际代码,而不是返回内联参数,这些参数当然是数据,而不是代码。
对于这个PRTSTR,海德使用的样式就是这个内联参数机制。
下面是此PRTSTR的用法
...
STRING STR "HELLO THERE"
...
START
JSR PRTSTR # call to print string
ADR STRING # pointer parameter passed "inline" within code,
# this is data, to be used and then skipped over by print string
... # actual return location to resume code in START考虑到6502同时具有调用堆栈和寄存器,使用内联机制进行调用有点倒退,但肯定已经完成了。(内联参数传递机制早于调用堆栈和大量寄存器,就像我们今天在现代处理器中所拥有的那样。)
在堆栈但寄存器很少的情况下,一些具有大量参数的调用可以将参数放到堆栈上,而另一些调用则通过传入可用的寄存器来完成。
在纯汇编代码中,您可以为每个函数创建自定义调用约定。只有C和其他高级语言的出现才会产生对规范、常规和可预测的调用约定的要求,这些约定可以在编译器中编码。
我必须承认我不理解三条JSR INCRTN指令中的第一条。(我理解后两条指令-它们跳过指向字符串的指针(JSR后面的内联参数),由于指针是两个字节,地址需要递增2个字节-但这三条指令中的第一条在我看来是错误的。)* 6502上没有ADR操作码,所以我不得不假设它是一个汇编伪运算码,形成了一个指向标签的2字节指针。
我的假设在某种程度上得到了海德在14-6中的确认,其中说:
JSR SASIGN ;STRING ASSIGNMENT
ADR DEST ;DEST = SOURCE
ADR SOURCE上面是一个7字节的序列(即1个用于JSR操作码,2个用于SASIGN (JSR的操作数),2个分别用于ADR DEST和ADR SOURCE)。
因此,当代码执行JSR INCRTN时,RTNADR和RTNADR + 1处的16位变量需要加1。
我相信,通过更直接地使用索引寄存器作为指针,而不是在索引寄存器Y中使用内存间接和整数,可以大大改善这个代码序列。
此外,由于我无法理解第一个JSR INCRTN,并且第二个被调用两次,因此它也可以简单地递增2,而不是递增1,两次。
发布于 2020-01-27 05:13:38
这个INCRTN子例程应该看起来像这样:
INCRTN:
INC RTNADR ; Add 1 to RTNADR
BNE INCRTNEX
INC RTNADR+$01 ; If the pointer passes a page boundary,
INCRTNEX: ; advance to the next page.
RTS这将在RTNADR对象上执行16位增量。
另一种需要在A中输入值,但更灵活的替代版本是使用以下内容:
ADD2RTN:
CLC
ADC RTNADR
STA RTNADR
BCC ADD2RTNX
INC RTNADR+$01
ADD2RTNX:
RTS这会将一个8位值(在A中提供)添加到16位RTNADR对象。
我提供的第一段代码完全符合您给出的示例,而第二段代码的性能要快得多,需要添加超过两个字节的代码。
在您给出的代码中,您可以替换加载字符串地址并推进返回地址的代码,如下所示:
LDY #$01
LDA (RTNADR),Y
STA ZPAGE
INY
LDA (RTNADR),Y
STA ZPAGE+$1
LDA #03
JSR ADD2RTN
;
LDY #0 ; Manually reset Y to 0 instead of using DEY twice to save time它的执行速度比原始代码快几个周期(JSR和RTS各需要6个周期,而INC使用5个周期,因为RTNADR在0页上),并且不会添加任何代码。在处理6502时,节省的每个字节和执行周期都会提高性能。
https://stackoverflow.com/questions/59326514
复制相似问题