描述:演示简化的
printf
功能(printg
)的程序,它是硬编码的,并且没有任何播放。我不知道为什么。
ORG $8000
START:
; Push parameters onto the stack (right-to-left)
MOVE.L #300, -(SP) ; Push the sixth number
MOVE.L #11, -(SP) ; Push the fifth number
MOVE.L #134212, -(SP) ; Push the fourth number
MOVE.L #92, -(SP) ; Push the third number
MOVE.L #51, -(SP) ; Push the second number
MOVE.L #23, -(SP) ; Push the first number
; Push the address of the format string onto the stack
LEA FMT, A0 ; Load the address of FMT into A0
MOVE.L A0, -(SP) ; Push the address onto the stack
; Call printg subroutine
BSR printg
; Clean up the stack
ADDA.L #28, SP ; Remove all six 32-bit numbers and the format string address
; Rest of the program or exit
SIMHALT ; Stop simulation
printg:
; Save registers that will be used
MOVE.L A0, -(SP) ; Save A0
MOVE.L D0, -(SP) ; Save D0
MOVE.L D1, -(SP) ; Save D1
; Call LENGTH to get the number of integers to print
MOVEA.L 8(SP), A0 ; Move the address of the format string into A0
BSR LENGTH ; Call LENGTH, result will be in D0
MOVE.B D0, D1 ; Store the number of integers in D1
; Calculate the starting address of the integers on the stack
ADDA.L #4, SP ; Adjust stack pointer to the first integer
PRINT_LOOP:
; Check if we have processed all format characters
DBF D1, END_PRINT ; Decrement D1 and branch if D1 is -1
; Get the next format character
MOVE.B (A0)+, D0 ; Move the next format character into D0
; Determine the base for the DISPLAY subroutine
CMP.B #'B', D0
BEQ DISPLAY_BINARY
CMP.B #'0', D0
BEQ DISPLAY_OCTAL
CMP.B #'D', D0
BEQ DISPLAY_DECIMAL
CMP.B #'H', D0
BEQ DISPLAY_HEXADECIMAL
BRA PRINT_LOOP ; If the character is not B, O, D, or H, skip it
DISPLAY_BINARY:
MOVE.B #2, -(SP) ; Push base 2 for binary
BRA CALL_DISPLAY
DISPLAY_OCTAL:
MOVE.B #8, -(SP) ; Push base 8 for octal
BRA CALL_DISPLAY
DISPLAY_DECIMAL:
MOVE.B #10, -(SP) ; Push base 10 for decimal
BRA CALL_DISPLAY
DISPLAY_HEXADECIMAL:
MOVE.B #16, -(SP) ; Push base 16 for hexadecimal
CALL_DISPLAY:
; Calculate the offset for the current integer
MOVE.L D1, D2 ; Copy the index to D2
LSL.L #2, D2 ; Multiply the index by 4 to get the byte offset
LEA (SP), A1 ; Load the address of the first integer into A1
ADDA.L D2, A1 ; Add the offset to the address
MOVE.L (A1), D1 ; Move the integer at the calculated offset into D1
MOVE.L D2, -(SP) ; Push the base onto the stack
MOVE.L D1, -(SP) ; Push the integer onto the stack
BSR DISPLAY ; Call DISPLAY subroutine
ADDA.L #8, SP ; Clean up the stack (base + integer)
BRA PRINT_LOOP ; Process the next character
END_PRINT:
; Restore the original stack pointer position
SUBA.L #4, SP ; Adjust stack pointer back to the format string address
; Restore registers
MOVE.L (SP)+, D1 ; Restore D1
MOVE.L (SP)+, D0 ; Restore D0
MOVE.L (SP)+, A0 ; Restore A0
RTS ; Return from subroutine
LENGTH:
MOVE.L A0, -(SP) ; Save A0 on the stack
CLR.B D0 ; Initialize the length counter to 0
LENGTH_LOOP:
MOVE.B (A0)+, D1 ; Load the next character from the format string
BEQ LENGTH_DONE ; If the character is null (0), we are done
ADDQ.B #1, D0 ; Increment the length counter
BRA LENGTH_LOOP ; Loop back to process the next character
LENGTH_DONE:
MOVE.L (SP)+, A0 ; Restore A0 from the stack
RTS ; Return from subroutine
DISPLAY:
; Save registers that will be used by TRAP #15
MOVE.L D0, -(SP) ; Save D0 on the stack
MOVE.L D1, -(SP) ; Save D1 on the stack
; Pop the base and the number off the stack
MOVE.L (SP)+, D2 ; Pop the base off the stack into D2
MOVE.L (SP)+, D1 ; Pop the integer value off the stack into D1
; Ensure D2 only contains the base in the lower byte
ANDI.L #$FF, D2 ; Clear upper bytes of D2, leaving only the base
; Set up for TRAP #15 to display the number
MOVE.L D1, D1 ; Number to display
MOVE.L D2, D2 ; Base
MOVE.W #3, D0 ; Task number for TRAP #15
TRAP #15 ; Execute the trap to display the number
; Restore registers after TRAP #15
MOVE.L (SP)+, D1 ; Restore D1
MOVE.L (SP)+, D0 ; Restore D0
RTS ; Return from subroutine
ORG $9000
FMT DC.B 'B','D','O','O','H','B',0 ; Format string for printg
END START
我期望硬编码输出显示在输出上。
到达 printg 子例程后,您的堆栈具有:
300 ; the sixth number
11 ; the fifth number
134212 ; the fourth number
92 ; the third number
51 ; the second number
23 ; the first number
FMT ; the address of FMT
? ; the return address
然后在堆栈上保留一些寄存器:
300 ; the sixth number
11 ; the fifth number
134212 ; the fourth number
92 ; the third number
51 ; the second number
23 ; the first number this is at 20(SP)
FMT ; the address of FMT this is at 16(SP)
? ; the return address
? ; preserved A0
? ; preserved D0
? ; preserved D1 this is at (SP)
第一个错误位于
MOVEA.L 8(SP), A0
,您确实应该从堆栈检索格式字符串的地址。在偏移量 +8 处,您存储了 A0 的内容,该内容碰巧包含该地址,因此此时您实际上并没有失败。然而,正确的使用说明是MOVEA.L 16(SP), A0
。
LENGTH子例程最好不要将D0视为一个字节。最好将其视为 long,这样一旦将其全尺寸复制到 D1 中,依赖于位 0 到 15 的
DBF D1, END_PRINT
指令就可以正常工作。
在 DISPLAY_BINARY 中推送这些基值 {2,8,10,16} 时,不要使用
.B
。始终保持堆栈指针均匀(并使用 .L 来清晰地显示堆栈布局),因此请使用 MOVE.L #2, -(SP)
等。
栈上整数起始地址的计算完全是假的!指示
ADDA.L #4, SP ; Adjust stack pointer to the first integer
不好。只需删除它。并且不要忘记进一步删除其恢复指令SUBA.L #4, SP ; Adjust stack pointer back to the format string address
。300 ; the sixth number
11 ; the fifth number
134212 ; the fourth number
92 ; the third number
51 ; the second number
23 ; the first number this is at 24(SP)
FMT ; the address of FMT
? ; the return address
? ; preserved A0
? ; preserved D0
? ; preserved D1
? ; the base value
CALL_DISPLAY处的代码变为:
CALL_DISPLAY:
; Calculate the offset for the current integer
MOVE.L D1, D2 ; Copy the index to D2
LSL.L #2, D2 ; Multiply the index by 4 to get the byte offset
LEA 24(SP), A1 ; Load the address of the first integer into A1
ADDA.L D2, A1 ; Add the offset to the address
此代码片段第一次运行时,D1 包含值 5。乘以 4,这将为已经指向第一个数字 (23) 的 A1 添加 20。最终结果是第六个数字 (300) 是程序要显示的第一个数字。不知道是不是你想要的...
您使用堆栈上的 2 个长参数调用 DISPLAY 子例程,并且立即将 D0 和 D1 寄存器保留在堆栈上:
? ; the base this is at 16(SP) (*)
? ; the integer this is at 12(SP)
? ; the return address
? ; preserved D0
? ; preserved D1
代码:
MOVE.L (SP)+, D2 ; Pop the base off the stack into D2 MOVE.L (SP)+, D1 ; Pop the integer value off the stack into D1
不会起作用。您将弹出您刚刚推动保存的价值观!使用
MOVE.L 16(SP), D2
和 MOVE.L 12(SP), D1
分别检索基数和整数。
(*) 最后提示:您的程序将基值两次推送到堆栈(相邻)。