211 lines
4.8 KiB
NASM
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
|