Motorola 68000 Assembly code: Not displaying in the Easy68k simulator app

48 views Asked by At

Description: Program to demonstrate a simplified printf function (printg), it is hardcoded, and nothing is playing. I'm not sure why.

        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

I am expecting the hardcoded outputs to display on the output.

1

There are 1 answers

0
Sep Roland On

It's all about the stack

Once arrived at the printg subroutine your stack has:

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

You then preserve some registers on the stack:

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)
  • The first error is in MOVEA.L 8(SP), A0 where you really should retrieve the address of the format string from the stack. At the offset +8 you have stored the contents of A0 that by chance happens to contain that address, so you don't actually fail at this point. The correct instruction to use however is MOVEA.L 16(SP), A0.

  • The LENGTH subroutine would better not treat D0 a byte. Better treat it as a long so that once you have copied it full-size in D1, the DBF D1, END_PRINT instruction that depends on bits 0 to 15 can work correctly.

  • When pushing those base values {2,8,10,16} in DISPLAY_BINARY don't use .B. Always keep the stackpointer even (and use .L for a clean display of the stack layout), so use MOVE.L #2, -(SP), etc.

  • The calculation of the starting address of the integers on the stack is totally bogus! The instruction ADDA.L #4, SP ; Adjust stack pointer to the first integer is not good. Just remove it. And don't forget to also remove its restoration instruction SUBA.L #4, SP ; Adjust stack pointer back to the format string address further down.
    Because the stack by now looks like:

    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
    

    The code at CALL_DISPLAY becomes:

    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
    

    The very first time that this snippet runs, D1 contains the value 5. Multiplied by 4 this will add 20 to A1 that already points at the first number (23). The net result is that the sixth number (300) is the first that your program is going to display. Don't know if that's what you want...

  • You call your DISPLAY subroutine with 2 long arguments on the stack and you immediately preserve the D0 and D1 registers on the stack:

    ?      ; the base           this is at 16(SP)     (*)
    ?      ; the integer        this is at 12(SP)
    ?      ; the return address
    ?      ; preserved D0
    ?      ; preserved D1
    

    The code:

     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
    

    is not going to work. You will be popping off the values that you just pushed for preservation! Use MOVE.L 16(SP), D2 and MOVE.L 12(SP), D1 to retrieve the base and the integer respectively.

(*) Final tip: your program pushes the base value twice to the stack (adjacently).