~chotrin/go6502-cookbook

0ffe4521dc4ee29cc3634b2e5c7896cc18bfff0f — chotrin 4 years ago f6d7961
Implemented LL_SEEK, LL_GET, LL_SET, and LL_DELETE!
2 files changed, 271 insertions(+), 65 deletions(-)

M 00_arithmetic_program/program.asm
M 01_linked_list/program.asm
M 00_arithmetic_program/program.asm => 00_arithmetic_program/program.asm +10 -28
@@ 1,18 1,16 @@
; go6502-cookbook/00_arithmetic_program/program.asm
; contains basic math operations, such as addition, subtraction,
; multiplication, and division.
; ---------------------
; go6502 Initialization
; ---------------------

;-[ go6502 initialization. ]--------------------------------------------------
        .ARCH   65c02           ; select CMOS 65c02 chip.
        .ORG    $1000           ; origin address for machine code.

; -------
; Exports
; -------
;-[ go6502 symbol exports. ]--------------------------------------------------
        .EX     START
        .EX     DATA
        .EX     END

        ; arithmetic functions.
        .EX     ADDITION_8_BIT
        .EX     ADDITION_16_BIT
        .EX     SUBTRACTION_16_BIT


@@ 21,11 19,8 @@
        .EX     DIVISION_16_BY_8_BIT
        .EX     DIVIDE_16_BIT_BY_10
        .EX     DIVISION_8_BY_8_BIT
        .EX     END_LOOP

; ----------------
; Memory Addresses
; ----------------
;-[ memory addresses. ]-------------------------------------------------------
; These addresses are on the zero page, which requires only one byte to
; address. More info about that here:
; https://en.wikipedia.org/wiki/Zero_page


@@ 35,9 30,7 @@ RESULT          = $20
TEMP            = $30
REMAINDER       = $10

; -------
; Program
; -------
;-[ the program. ]------------------------------------------------------------
START:
        CLD                     ; clear decimal bit.



@@ 59,14 52,10 @@ START:
        JSR     DIVIDE_16_BIT_BY_10
        JSR     DIVISION_8_BY_8_BIT

; that's all, folks!
END_LOOP:
        JMP     END_LOOP        ; infinite loop so the PC doesn't fall off.

; -----------
; Subroutines
; -----------
; infinite loop so the PC doesn't fall off.
END:    JMP     END

;-[ subroutines. ]------------------------------------------------------------
; simple 8-bit addition.
; operands are loaded from OPERAND_1 and OPERAND_2 addresses.
; result is stored as at RESULT address.


@@ 351,10 340,3 @@ DIV_L2: ROL     OPERAND_1
        BNE     DIV_L1
        RTS

; ----
; Data
; ----
DATA:
        .ALIGN  16              ; align next addr on 16-byte boundary.

END

M 01_linked_list/program.asm => 01_linked_list/program.asm +261 -37
@@ 35,60 35,52 @@
; deleted element won't be re-used unless a new list is formed or the list
; shrinks and regrows over that element's data.

; ---------------------
; go6502 Initialization
; ---------------------
;-[ go6502 initialization. ]--------------------------------------------------
        .ARCH   65c02           ; select CMOS 65c02 chip.
        .ORG    $1000           ; origin address for machine code.

; -------
; Exports
; -------
;-[ go6502 symbol exports. ]--------------------------------------------------
        .EX     START
        .EX     EXAMPLE_1
        .EX     EXAMPLE_2
        .EX     BP              ; breakpoint ("b add bp")

        ; linked list functions.
        .EX     LL_INIT
        .EX     LL_CLEAR
        .EX     LL_PUSH_BACK
        .EX     LL_POP_BACK
        .EX     LL_POP_FRONT
        .EX     LL_IS_EMPTY
        .EX     LL_GET
        .EX     LL_ERASE
        .EX     LL_NEXT
        .EX     LL_SEEK
        .EX     LL_GET
        .EX     LL_SET
        .EX     LL_DELETE


; ----------------
; Memory Addresses
; ----------------
;-[ memory addresses. ]-------------------------------------------------------
; offsets for internal linked list use.
LL_START_ADDR   = $00           ; points to the start of the list or 0.
LL_END_ADDR     = $01           ; points to the end of the list or 0.
LL_NODE_SIZE    = $03           ; total size of a node.

; parameters and return values for linked list subroutine calls.
LL_HEAP_ADDR    = $00
LL_HEAP_ADDR    = $00           ; the start address of a 255-byte heap.
LL_NODE_PTR     = $02           ; an iteration pointer.
OPERAND_1       = $10
RESULT          = $20
TEMP            = $30

; -------
; Program
; -------
;-[ the program. ]------------------------------------------------------------
START:
        ; clear decimal bit.
        CLD
        ; store the value $ff at addr $200.
        LDA     #$ff
        STA     $200
        ; store the value $d0 at addr $300.
        LDA     #$d0
        STA     $300

        ; in this example, we're going to push addresses $200 and $300
        ; onto the end of the linked list. we'll also pop them back off.
        ; you can watch the RESULT address in go6502 to see the effects
        ; as you "step over" the LL_POP_FRONT operations.

; in this example, we're going to push addresses $200 and $300
; onto the end of the linked list. we'll also pop them back off.
; you can watch the RESULT address in go6502 to see the effects
; as you "step over" the LL_POP_BACK operations.
EXAMPLE_1:
        ; first, initialize the linked list at LL_HEAP_ADDR $100.
        LDA     #$00
        STA     LL_HEAP_ADDR


@@ 114,34 106,82 @@ START:

        ; pop elements from the end of the linked list.
        ; popped data addresses go into RESULT.
BP:     JSR     LL_POP_BACK
        JSR     LL_POP_BACK
        JSR     LL_POP_BACK

        ; check if the linked list is empty.
        ; skip to END if it is.
        ; skip to EXAMPLE_2 if it is.
        JSR     LL_IS_EMPTY
        LDA     RESULT
        BEQ     END
        BEQ     EXAMPLE_2

        ; this shouldn't be executed.
        JSR     LL_POP_BACK

; in this example, we're going to push addresses $200, $300, $400, and $500
; onto the end of a linked list. then we'll iterate through the list.
; we'll also seek to a specified index, get the element, and set it.
EXAMPLE_2:
        ; clear the list, push addresses $200, $300, $400, and $500 onto it.
        JSR     LL_CLEAR
        LDA     #$00
        STA     OPERAND_1
        LDA     #$2
        STA     OPERAND_1+1
_EXAMPLE_2_PUSH_ELEMENT:
        JSR     LL_PUSH_BACK
        INC     OPERAND_1+1
        LDA     OPERAND_1+1
        CMP     #$6
        BNE     _EXAMPLE_2_PUSH_ELEMENT

_EXAMPLE_2_ITERATE:
        ; iterate through the linked list.
        ; RESULT will be set to $200, $300, $400, and then $500.
        JSR     LL_NEXT
        LDA     LL_NODE_PTR
        BEQ     _EXAMPLE_2_SEEK
        JSR     LL_GET
        JMP     _EXAMPLE_2_ITERATE

_EXAMPLE_2_SEEK:
        ; seek to the element at index 2, then fetch it to RESULT ($400).
        LDA     #2
        STA     OPERAND_1
        JSR     LL_SEEK
        JSR     LL_GET

        ; seek to the element at index 1 ($300), then set it to $FFFF.
        LDA     #1
        STA     OPERAND_1
        JSR     LL_SEEK
        LDA     #$ff
        STA     OPERAND_1
        STA     OPERAND_1+1
        JSR     LL_SET

        ; seek to the element at index 2 ($400), then delete it.
        LDA     #2
        STA     OPERAND_1
        JSR     LL_SEEK
BP:     JSR     LL_DELETE

; infinite loop so the PC doesn't fall off.
END:    JMP     END

; -----------
; Subroutines
; -----------

;-[ subroutines. ]------------------------------------------------------------
; initializes or resets the linked list at LL_HEAP_ADDR.
; also sets the LL_NODE_PTR to 0.
LL_INIT:
LL_CLEAR:
        ; push A and Y onto the stack.
        PHA
        TYA
        PHA
        ; set LL_HEAP_ADDR+LL_START_ADDR and LL_HEAP_ADDR+LL_END_ADDR to 0.
        ; set LL_NODE_PTR to 0.
        LDA     #0
        STA     LL_NODE_PTR
        ; set LL_HEAP_ADDR+LL_START_ADDR and LL_HEAP_ADDR+LL_END_ADDR to 0.
        LDY     #LL_START_ADDR
        STA     (LL_HEAP_ADDR),Y
        LDY     #LL_END_ADDR


@@ 355,12 395,196 @@ LL_IS_EMPTY:
        PLA
        RTS

; moves LL_NODE_PTR by iterating through the linked list at LL_HEAP_ADDR.
; if LL_NODE_PTR is 0, LL_NODE_PTR will begin from the first of the list.
; if LL_NODE_PTR is at the end of the list when this is called,
; LL_NODE_PTR will be set to 0.
LL_NEXT:
        ; push A and Y onto the stack.
        PHA
        TYA
        PHA
        ; see where LL_NODE_PTR is pointing to.
        LDA     LL_NODE_PTR
        BEQ     _LL_NEXT_GO_TO_FIRST_ELEM
        ; iterate to the next node.
        LDY     LL_NODE_PTR
        LDA     (LL_HEAP_ADDR),Y
        STA     LL_NODE_PTR
        JMP     _LL_NEXT_RETURN
_LL_NEXT_GO_TO_FIRST_ELEM:
        LDY     #LL_START_ADDR
        LDA     (LL_HEAP_ADDR),Y
        STA     LL_NODE_PTR
_LL_NEXT_RETURN:
        ; pull A and Y from the stack.
        PLA
        TAY
        PLA
        RTS

; sets LL_NODE_PTR to the index specified by OPERAND_1 for the linked list
; at LL_HEAP_ADDR. wraps around if the index exceeds the size of the list.
LL_SEEK:
        ; push A, X, and Y onto the stack.
        PHA
        TXA
        PHA
        TYA
        PHA
        ; reset to the beginning of the linked list.
        LDA     #0
        STA     LL_NODE_PTR
        ; seek to the index specified at OPERAND_1.
        LDX     OPERAND_1
_LL_SEEK_ITERATE:
        JSR     LL_NEXT
        DEX
        BMI     _LL_SEEK_RETURN
        JMP     _LL_SEEK_ITERATE
_LL_SEEK_RETURN:
        ; pull A, X, and Y from the stack.
        PLA
        TAY
        PLA
        TAX
        PLA
        RTS

; saves the current node's LL_DATA_ADDR to RESULT for the linked list at
; LL_HEAP_ADDR. if LL_NODE_PTR is 0, does not modify RESULT.
LL_GET:
        ; push A and Y onto the stack.
        PHA
        TYA
        PHA
        ; see where LL_NODE_PTR is pointing to. return if it's 0.
        LDA     LL_NODE_PTR
        BEQ     _LL_GET_RETURN
        ; copy LL_NODE_PTR's LL_DATA_ADDR to RESULT.
        TAY
        INY
        LDA     (LL_HEAP_ADDR),Y
        STA     RESULT
        INY
        LDA     (LL_HEAP_ADDR),Y
        STA     RESULT+1
_LL_GET_RETURN:
        ; pull A and Y from the stack.
        PLA
        TAY
        PLA
        RTS

LL_ERASE:
; saves OPERAND_1 to the current node's LL_DATA_ADDR for the linked list at
; LL_HEAP_ADDR. if LL_NODE_PTR is 0, does not modify the linked list.
LL_SET:
        ; push A and Y onto the stack.
        PHA
        TYA
        PHA
        ; see where LL_NODE_PTR is pointing to. return if it's 0.
        LDA     LL_NODE_PTR
        BEQ     _LL_SET_RETURN
        ; copy LL_NODE_PTR's LL_DATA_ADDR to RESULT.
        TAY
        INY
        LDA     OPERAND_1
        STA     (LL_HEAP_ADDR),Y
        INY
        LDA     OPERAND_1+1
        STA     (LL_HEAP_ADDR),Y
_LL_SET_RETURN:
        ; pull A and Y from the stack.
        PLA
        TAY
        PLA
        RTS

LL_NEXT:
; deletes the current node at LL_NODE_PTR for the linked list at
; LL_HEAP_ADDR. if LL_NODE_PTR is 0, does not modify the linked list.
LL_DELETE:
        ; push A, TEMP, RESULT, and Y onto the stack.
        PHA
        LDA     TEMP
        PHA
        LDA     RESULT
        PHA
        LDA     RESULT+1
        PHA
        TYA
        PHA
        ; see where LL_NODE_PTR is pointing to. return if it's 0.
        LDA     LL_NODE_PTR
        BEQ     _LL_DELETE_RETURN
        ; if LL_NODE_PTR is the only element, this is an LL_CLEAR.
        LDY     #LL_START_ADDR
        LDA     (LL_HEAP_ADDR),Y
        LDY     #LL_END_ADDR
        CMP     (LL_HEAP_ADDR),Y
        BEQ     _LL_DELETE_CLEAR_LIST
        ; if LL_NODE_PTR is the first element, this is an LL_POP_FRONT.
        LDY     #LL_START_ADDR
        LDA     (LL_HEAP_ADDR),Y
        CMP     LL_NODE_PTR
        BEQ     _LL_DELETE_FIRST_NODE
        ; if LL_NODE_PTR is the last element, this is an LL_POP_BACK.
        LDY     #LL_END_ADDR
        LDA     (LL_HEAP_ADDR),Y
        CMP     LL_NODE_PTR
        BEQ     _LL_DELETE_LAST_NODE
        ; LL_NODE_PTR is in the middle of the list.
        ; store LL_NODE_PTR at TEMP for comparison later.
        LDA     LL_NODE_PTR
        STA     TEMP
        ; iterate through the list to find the previous node.
        LDY     #LL_START_ADDR
_LL_DELETE_CHECK_IS_NEXT_NODE_CURRENT:
        LDA     (LL_HEAP_ADDR),Y
        TAY
        LDA     (LL_HEAP_ADDR),Y
        CMP     TEMP
        BNE     _LL_DELETE_CHECK_IS_NEXT_NODE_CURRENT
        ; set the previous node's (Y's) LL_NEXT_ADDR to LL_NODE_PTR's next.
        ; push X and Y onto the stack.
        TXA
        PHA
        TYA
        PHA
        LDY     TEMP
        LDA     (LL_HEAP_ADDR),Y
        TAX
        PLA
        ; pull Y (the previous node) from the stack.
        TAY
        TXA
        STA     (LL_HEAP_ADDR),Y
        ; pull X from the stack.
        PLA
        TAX
        ; set LL_NODE_PTR to the previous node.
        TYA
        STA     LL_NODE_PTR
        JMP     _LL_DELETE_RETURN
_LL_DELETE_CLEAR_LIST:
        JSR     LL_CLEAR
        JMP     _LL_DELETE_RETURN
_LL_DELETE_FIRST_NODE:
        JSR     LL_POP_FRONT
        JMP     _LL_DELETE_RETURN
_LL_DELETE_LAST_NODE:
        JSR     LL_POP_BACK
        JMP     _LL_DELETE_RETURN
_LL_DELETE_RETURN:
        ; pull A, TEMP, RESULT, and Y from the stack.
        PLA
        TAY
        PLA
        STA     RESULT+1
        PLA
        STA     RESULT
        PLA
        STA     TEMP
        PLA
        RTS