Files

211 lines
4.8 KiB
NASM

; Tests MVDS, which copies a Data Pointer into the Stack Pointer.
;
; This is the dangerous one. Moving the Stack Pointer abandons everything below the new
; position: return addresses, saved registers, interrupt frames, all of it. Nothing is
; unwound, because moving the Stack does not move what is on it.
;
; It is here for one job, and that job is what this program acts out. A system that runs
; other programs and takes the machine back afterwards cannot do so without it. A program
; that gives up part way through leaves whatever it pushed behind, and nothing is ever
; going to return and tidy it away. Without MVDS the Stack only ever moves downward, a
; little further with every program run, and a shell cannot outlive many of them.
;
; The other half of the story is that moving the Stack does not destroy what was on it.
; A routine that puts the Stack Pointer back where it found it returns perfectly normally,
; because the frame was only stepped away from. Both halves are acted out here, and the
; nesting case with them: two routines that each borrow a Stack, one inside the other,
; each keeping its own saved Stack Pointer on the Stack it borrowed. A fixed location in
; Data Memory would fail exactly there, and would fail silently.
;
; Both sequences are the ones the Programming Manual prints under "The Stack Pointer,
; Set By Hand", so if either example ever stops working this test says so.
;
; Correct output is:
; stack reclaim test
; at start: FFFF
; spent: FFF7
; reclaimed: FFFF
; still good: 33
; borrowed: 7 and 9
; back home: FFFF
#Include console.asm
#Program
start:
SETD.0 Banner
CALL printString
CALL newLine
; Where the Stack is before anything has touched it. Read first, so that no call has
; had a chance to move it.
MVSD.0
SETD.1 SavedStack
STD.0.1
SETD.0 StartLabel
CALL printString
SETD.0 SavedStack
CALL printWordHex
CALL newLine
; Spend some Stack and never give it back. This is not a subroutine, because a
; subroutine that pushed without popping would take its own return address with it.
; It stands in for a program that stops half way through.
INIA 0x11
PSHA PSHA PSHA PSHA
PSHA PSHA PSHA PSHA
MVSD.0
SETD.1 Spent
STD.0.1
SETD.0 SpentLabel
CALL printString
SETD.0 Spent
CALL printWordHex
CALL newLine
; Take it back. Nothing pops those eight bytes: they are simply no longer on the Stack,
; which is the whole of what reclaiming means.
SETD.1 SavedStack
LDD.0.1
MVDS.0
MVSD.0
SETD.1 Reclaimed
STD.0.1
SETD.0 ReclaimedLabel
CALL printString
SETD.0 Reclaimed
CALL printWordHex
CALL newLine
; A Stack Pointer in the right place is not proof that the Stack works. Push something
; through it and get it back, so that the reclaimed Stack is shown to be usable and not
; merely well positioned.
SETD.0 GoodLabel
CALL printString
INIA 0d33
PSHA
RSTA
POPA
CALL printByteDecimal
CALL newLine
; ---- Moving the Stack and coming back from it ----
; borrower runs on a Stack of its own and returns with a plain RET, which only works
; because putting the Stack Pointer back leaves the frame exactly as CALL left it.
CALL borrower
SETD.0 BorrowedLabel
CALL printString
SETD.0 OuterResult
LDA.0
CALL printByteDecimal
SETD.0 AndText
CALL printString
SETD.0 InnerResult
LDA.0
CALL printByteDecimal
CALL newLine
; The Stack has to be back where it started, or the nesting quietly lost track of it.
MVSD.0
SETD.1 Reclaimed
STD.0.1
SETD.0 HomeLabel
CALL printString
SETD.0 Reclaimed
CALL printWordHex
CALL newLine
HALT
; Borrows a Stack, works on it, calls something that does the same thing again, then puts
; the Stack back and returns. The old Stack Pointer goes onto the borrowed Stack rather
; than into a fixed place in memory, which is what makes the nesting safe: the inner
; routine keeps its own copy on its own Stack and cannot tread on this one.
borrower:
MVSD.3
SETD.0 OuterTop
MVDS.0
PSHD.3
INIA 0d7
PSHA
RSTA
POPA
SETD.1 OuterResult
STA.1
CALL innerBorrower
POPD.3
MVDS.3
RET
; The same again, one level down and on a Stack of its own.
innerBorrower:
MVSD.3
SETD.0 InnerTop
MVDS.0
PSHD.3
INIA 0d9
PSHA
RSTA
POPA
SETD.1 InnerResult
STA.1
POPD.3
MVDS.3
RET
#Data
Banner:
"stack reclaim test"
StartLabel:
"at start: "
SpentLabel:
"spent: "
ReclaimedLabel:
"reclaimed: "
GoodLabel:
"still good: "
BorrowedLabel:
"borrowed: "
AndText:
" and "
HomeLabel:
"back home: "
SavedStack:
0x00 0x00
Spent:
0x00 0x00
Reclaimed:
0x00 0x00
OuterResult:
0x00
InnerResult:
0x00
; Two places a Stack can live, sixty four bytes each. The label is on the last byte of
; each, because a Stack grows downward from wherever it is set.
OuterStack:
#Reserve 0d63
OuterTop:
0x00
InnerStack:
#Reserve 0d63
InnerTop:
0x00
#Vectors
Boot start