assembly Motorola 68000汇编代码:未显示在Easy68k模拟器应用程序中

yshpjwxd  于 2023-11-19  发布在  其他
关注(0)|答案(1)|浏览(141)

说明:程序演示了一个简化的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

字符串
我希望硬编码输出显示在输出上。

bogh5gae

bogh5gae1#

一切都是关于堆栈的

一旦到达 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 子例程最好不要将D 0视为字节,最好将其视为一个long,这样一旦将其完整复制到D1中,依赖于位0到15的DBF D1, END_PRINT指令就可以正确工作。
  • 当在 * BLOG_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_CALL * 处的代码变为:

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)是您的程序将显示的第一个数字。不知道这是否是您想要的...

  • 你用栈上的两个长参数调用 * 子例程,并立即在栈上保留D 0和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), D2MOVE.L 12(SP), D1分别检索基数和整数。
(*)最后一个提示:你的程序将基值两次推入堆栈(相邻)。

相关问题