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.
It's all about the stack
Once arrived at the printg subroutine your stack has:
You then preserve some registers on the stack:
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 isMOVEA.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 useMOVE.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 instructionSUBA.L #4, SP ; Adjust stack pointer back to the format string address
further down.Because the stack by now looks like:
The code at CALL_DISPLAY becomes:
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 code:
is not going to work. You will be popping off the values that you just pushed for preservation! Use
MOVE.L 16(SP), D2
andMOVE.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).