;C02 stack module assembly language subroutines
;Requires external zero page words DSTPTR, SRCPTR, STKPTR, 
;words STKBGN, STKEND, and bytes TEMP0, TEMP1, TEMP2 (header)
;and external routines RESRXY, SAVRXY, SETDST, SETSRC (stddef),  
;MEMCPL (memory), and STRLEN (string) 

;Implementation Notes:
;Stack starts at STKBGN and builds upward to STKEND
;Each entry on the stack consists of the entry data followed 
;by a single byte containing the length of the entry data

        SUBROUTINE STACK

;Get Stack Pointer
;Affects: N,Z
;Returns: X = Stack Pointer LSB
;         Y = Stack Pointer MSB
.GETPTR LDX STKPTR      ;Load Stack Pointer LSB
        LDY STKPTR+1    ;Load Stack Pointer MSB
        RTS

;Save Stack Pointer 
;Sets: TEMP1,TEMP2 = STKPTR
;Affects: N,Z
;Returns: X = Stack Pointer LSB
;         Y = Stack Pointer MSB
.SAVPTR JSR .GETPTR     ;Get Stack Pointer
        JMP SAVRXY      ;Save in TEMP1, TEMP2 

;.STKSRC() - Set Source Pointer to Stack Pointer
;Sets: SRCPTR = STKPTR
;Affects: X,N,Z
;Returns: Y = 0
.STKSRC JSR .GETPTR     ;Get Stack Pointer Address
        JMP SETSRC      ;Set Source/ an/d Return

;.STKDST() - Set Destination Pointer to Stack Pointer
;Uses: STKPTR = Stack Pointer
;Sets: DSTPTR = Destination Array Pointer
;Affects: N,Z
;Returns: X = Stack Pointer LSB
;         Y = Stack Pointer MSB
.STKDST JSR .GETPTR     ;Get Stack Pointer Address
        JMP SETDST      ;Set Destination and Return

;stkrst() - Reset Stack Pointer to Start Address
;Uses: STKBGN = Stack Start Address
;Sets: STKPTR = Stack Pointer
;Affects: Z, N
;Returns: X = Stack Pointer LSB
;         Y = Stack Pointer MSB
STKRST: LDX STKBGN      ;Load X with Stack Start LSB
        LDY STKBGN+1    ;Load X with Stack Start MSB
        JMP .SETPTR     ;Store in Stack Pointer 

;.RESPTR() - Restore Stack Pointer 
;Uses: TEMP1.TEMP2 = Stack Pointer
;Sets: STKPTR = Stack Pointer
;Affects: N,Z
;Returns: X = Stack Pointer LSB
;         Y = Stack Pointer MSB
.RESPTR JSR RESRXY      ;Get TEMP1, TEMP2
                        ;and fall into .SETPTR

;Set STKPTR to Y,X
.SETPTR STX STKPTR      ;Store X in Stack Pointer LSB
        STY STKPTR+1    ;Store Y in Stack Pointer MSB
        RTS             ;Exit Routine

;stkpsh(n, &m) - Push n Bytes of m onto Stack
;Args: A = Number of Bytes to Append
;      Y,X = Address of Source Array
;Uses: STKEND = Stack End Address
;Sets: DSTPTR = Pointer to Stack Entry
;      SRCPTR = Pointer to Source Array
;      TEMP0 = Number of Bytes to Append
;Updates: STKPTR = Stack Pointer
;Returns: A=Number of Bytes Pushed
;           0 = Error: Stack Underflow
STKPSH: JSR SETSRC      ;Set Source Address
.STKPSH STA TEMP0       ;Save Number of Bytes
        JSR .STKDST     ;Set Destination to Stack Pointer
        JSR .ALLOC      ;Allocate Space on Stack
        JMP MEMCPL      ;Copy Bytes and Return

;Add A to STKPTR
.ADDPTR CLC 
.ADCPTR ADC STKPTR      ;Add to Stack Pointer LSB
        STA STKPTR      ;and Save
        LDA STKPTR+1    ;Add Carry
        ADC #0          ;to Stack Pointer MSB
        STA STKPTR+1    ;and Save
        RTS

;Allocate Space on Stack
;Args: A=Number of Bytes to Allocate
;Updates: STKPTR = Stack Pointer
;Affects: C,N,Z
;Returns: A = Number of Bytes Allocated
;             0 if Error: Pointer Overflow or Length 0 
.ALLOC  JSR .SAVPTR     ;Save Stack Pointer
        LDA TEMP0       ;If No Bytes
        BEQ .POPRET     ;  Abort Calling Routine
        JSR .ADDPTR     ;Add to Stack Pointer
        LDA STKEND+1    ;If Stack End MSB
        CMP STKPTR+1    ;  < Stack Pointer MSB
        BCC .ABORT      ;  Abort Calling Routine
        BNE .ALSKIP     ;Else if Not Equal
        LDA STKEND      ;and Stack End LSB
        CMP STKPTR      ;  < Stack Pointer LSB
        BCC .ABORT      ;  Abort Calling Routine
.ALSKIP LDA TEMP0       ;Get Number of Bytes from X Register
        LDY #0          ;Initialize Index
        STA (STKPTR),Y  ;Store after Allocated Area
                        ;and fall into .INCPTR

;Increment Stack Pointer
.INCPTR INC STKPTR      ;Increment LSB 
        BNE .RETURN     ;If Zero
        INC STKPTR+1    ;  Increment MSB
        RTS

;STKPTR to STKBGN
;Returns: C=1,Z=1 if STKPTR = STKBGN 
;         C=1,Z=0 if STKPTR > STKBGN
;         C=0,Z=0 if STKPTR < STKBGN
.CMPPTR LDA STKPTR+1    ;Compare MSB
        CMP STKBGN+1
        BCC .RETURN     ;If Pointer<Start Return C=0, Z=1
        BNE .RETURN     ;If Pointer>Start Return C=1. Z=0
        LDA STKPTR      ;If MSB Equal
        CMP STKBGN      ;Compare LSB
        RTS

;.DECPTR(n) - Decrement Stack Pointer
;Sets: STKPTR = Stack Pointer Address
;Affects: X,Z,N
.DECPTR LDX STKPTR      ;If LSB
        BNE .DECLSB     ;  is Zero
        DEC STKPTR+1    ;  Decrement MSB
.DECLSB DEC STKPTR      ;  Decrement LSB
        RTS

;stksiz() - Get Stack Size in Bytes
;Uses: STKPTR = Stack Pointer
;Sets: STKBGN = Stack Start Address
;Returns: A = $FF if Stack Size is Non-Zero
;             $00 if Stack is Empty
;         Y,X = Stack Size (MSB, LSB)
STKSIZ: LDA STKPTR      ;Subtract 
        SEC             ;Start Address LSB
        SBC STKBGN      ;from Pointer LSB
        TAX             ;and Copy to X
        LDA STKPTR+1    ;Subtract Start MSB
        SBC STKBGN+1      ;from Pointer MSB
        TAY             ;and Copy to Y
        BCC .RETZRO     ;Return FALSE if <=0
        BNE .RETTRU     ;Return TRUE if MSB <> 0
        TXA             ;Else
        BEQ .RETURN     ;Return FALSE if LSB = 0 
.RETTRU LDA #$FF        ;Else Return TRUE
.RETURN RTS

;stkdrp(&m) - Drop Top Entry from Stack
;Note: Aborts Calling Routine if Error
;Uses: STKBGN = Stack Start Address
;Sets: TEMP1, TEMP2 = Original Stack Pointer
;Updates: STKPTR = Stack Pointer
;Affects: Y,C,N,Z
;Returns: A=Number of Bytes in Entry
;           0=Error: Stack Underflow
;         Y=0
STKDRP: JSR .SAVPTR     ;Save Stack Pointer
.STKDRP JSR .CMPPTR     ;If Stack Pointer
        BCC .ABORT      ;  <= Stack Start
        BEQ .ABORT      ;  Return 0
        JSR .DECPTR     ;Decrement Stack Pointer
        LDY #0          ;Initialize Index
        LDA (STKPTR),Y  ;Get Number of Bytes in Entry
                        ;and fall into .SUBPTR

;Subtract A from STKPTR
.SUBPTR STA TEMP0       ;Save Number in TEMP0
        LDA STKPTR
        SEC             ;Subtract A from LSB
        SBC TEMP0
        STA STKPTR
        LDA STKPTR+1    ;Subtract 0 from LSB
        SBC #0          ;For Borrow
        STA STKPTR+1
        JSR .CMPPTR     ;If Stack Pointer < Start 
        BCC .ABORT      ;  Restore Pointer and Return 0
        LDA TEMP0       ;Retrieve Number of Bytes
        RTS
.ABORT  JSR .RESPTR     ;Restore Stack Pointer
.POPRET PLA             ;Drop Return Address from Stack
        PLA             ;  Aborting Calling Routine
.RETZRO LDA #0          ;Set A to 0
        RTS

;stkpop(&m) - Pop Top Entry off Stack
;Args: Y,X = Address of Source Array
;Uses: STKBGN = Stack Start Address
;Updates: STKPTR = Stack Pointer
;Affects: C,N,Z
;Returns: A,Y = Number of Bytes in Entry
;           0 = Error: Stack Underflow
STKPOP: JSR SETDST      ;Set Destination Address
        JSR STKDRP      ;Deallocate Stack Entry
        JSR .STKSRC     ;Set Source Address to Stack Pointer
        JMP MEMCPL      ;Copy Bytes and Return

;stkstr(n, &m) - Push String onto Stack
;Args: Y,X = Address of Source Array
STKSTR: JSR STRLEN      ;Get Length of String
        BEQ .PSHSRC     ;If Length > 0
        BMI .PSHSRC     ;  and < 128
        INY             ;  Increment Length to
        TYA             ;  include null terminator
.PSHSRC LDY #0          ;Clear Y Index
        JMP .STKPSH     ;Push Bytes onto Stack 

;stktop(&m) - Copy Top Entry off Stack
;Args: Y,X = Address of Source Array
;Uses: STKBGN = Stack Start Address
;Affects: C,N,Z
;Returns: A,Y = Number of Bytes in Entry
;           0 = Error: Stack Underflow
STKTOP: JSR STKPOP      ;Pop Top Entry Off Stack
        BEQ .RETURN     ;If Underflow, Return 0
        JMP .RESPTR     ;Else Restore Stack Pointer and Return

;stkdup(&m) - Duplicate Top Entry on Stack
;Uses: STKBGN = Stack Start Address
;      STKEND = Stack End Address
;Updates: STKPTR = Stack Pointer
;Affects: C,N,Z
;Returns: A,Y = Number of Bytes in Entry
;           0 = Error: Stack Overflow or Underflow
STKDUP: JSR STKDRP      ;Deallocate Top Entry
.STKDUP JSR .STKSRC     ;Set Source Pointer to Stack Pointer
        JSR .RESPTR     ;Restore Stack Pointer
        JMP .PSHSRC     ;Push Top Entry onto Stack

;stkovr(&m) - Duplicate Second Entry on Stack
;Uses: STKBGN = Stack Start Address
;      STKEND = Stack End Address
;Updates: STKPTR = Stack Pointer
;Affects: C,N,Z
;Returns: A,Y = Number of Bytes in Entry
;           0 = Error: Stack Overflow or Underflow
STKOVR: JSR STKDRP      ;Deallocate Top Entry
        JSR .STKDRP     ;Deallocate Second Entry
        JMP .STKDUP     ;Restore Pointer and Duplicate

;stkswp() - Swap Top Entry with Entry Below it
;Uses: STKBGN = Stack Start Address
;      STKEND = Stack End Address
;Destroys: DSTPTR,TEMP0,TEMP0,TEMP1,TEMP2,TEMP3
;Affects: C,N,Z
;Returns: A,Y = Number of Bytes in Entry
;           0 = Error: Stack Overflow or Underflow
STKSWP: JSR STKOVR      ;Duplicate Second Entry
        BEQ .RETURN     ;Exit if Error
        JSR .STKDRP     ;Deallocate Duplicate
        STA TEMP3       ;Save Size of Duplicate
        JSR .SAVPTR     ;Save Pointer to Duplicate
        JSR .STKDRP     ;Deallocate Top Entry
        JSR .STKSRC     ;Set Source to Top Entry
        STA DSTPTR      ;Save Top Entry Size
        JSR .STKDRP     ;Deallocate Second Entry
        LDA DSTPTR      ;Retrieve Top Entry Size
        JSR .COPY       ;Copy Top Entry Down
        JSR RESSRC      ;Set Duplicate as Source
        LDA TEMP3       ;Get Duplicate Length
.COPY   STA TEMP0       ;Save Number of Bytes
        JSR .STKDST     ;Set Destination to Stack Pointer
        LDY #0          ;Clear Index Register
        JSR MEMCPL      ;Copy Bytes and Return
        STA (DSTPTR),Y  ;Store Number of Bytes after Entry
        SEC             ;Add Number of Bytes plus 1  
        JSR .ADCPTR     ;to Stack Pointer
        TYA             ;Return Number of Bytes
        RTS

        ENDSUBROUTINE