diff options
| author | Miquel Sabaté Solà <msabate@suse.com> | 2023-12-21 00:02:51 +0100 |
|---|---|---|
| committer | Miquel Sabaté Solà <msabate@suse.com> | 2023-12-21 18:41:41 +0100 |
| commit | 9e22faffffa20aa256a05468157404006e2e4d1e (patch) | |
| tree | 04266bf24d93a5a11452c27c10dbca16e3a3a8d9 /list.s | |
| download | list.nes-9e22faffffa20aa256a05468157404006e2e4d1e.tar.gz list.nes-9e22faffffa20aa256a05468157404006e2e4d1e.zip | |
Initial commit
Implemented the List scope which brings some handy macros and functions
to manipulate big lists on the NES.
Signed-off-by: Miquel Sabaté Solà <msabate@suse.com>
Diffstat (limited to 'list.s')
| -rw-r--r-- | list.s | 179 |
1 files changed, 179 insertions, 0 deletions
@@ -0,0 +1,179 @@ +;;; +;; list.nes - a small library to maintain big lists on the NES. +;; +;; Copyright (C) Miquel Sabaté Solà <mikisabate@gmail.com> +;; +;; This library is free software: you can redistribute it and/or modify +;; it under the terms of the GNU Lesser General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This library is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public License +;; along with this library. If not, see <https://www.gnu.org/licenses/>. + +.p02 + +;; Initialize a new list which starts at the given 16-bit address. +.macro LIST_INIT address + lda #.lobyte(address) + sta List::ptr + sta List::last + lda #.hibyte(address) + sta List::ptr + 1 + sta List::last + 1 +.endmacro + +;; For an already initialized list, reset the `List::ptr` variable so the list +;; can be iterated with functions like `List::get`. +.macro LIST_IT_FROM address + lda #.lobyte(address) + sta List::ptr + lda #.hibyte(address) + sta List::ptr + 1 +.endmacro + +;; Move the `List::ptr` variable so to point to the next element. +.macro LIST_NEXT + clc + lda #1 + adc List::ptr + sta List::ptr + lda #0 + adc List::ptr + 1 + sta List::ptr + 1 +.endmacro + +;;; +;; List provides the scope for the variables and subroutines that have been +;; defined in order to maintain and manipulate big lists on the NES. +;; +;; This library can also work for smaller list but there are other more +;; performant ways to achieve the same thing. Thus, use this library only if you +;; need to have a list that may store more than 255 bytes and indexing in the +;; usual ways might be a problem otherwise. +;; +;; For more information on the usage and the API check the documentation +;; (README.md file on the https://github.com/mssola/list.nes repository). +.scope List + ;; NOTE (important): this library needs 4 bytes ($60-$63) to store + ;; information of this list. These are two 16-bit pointers which are used + ;; for the subroutines being defined here. If you have a clash with these + ;; memory addresses, do feel free to change them, but remember that the code + ;; assumes that both pointers are 16-bit (they don't need to be contiguous). + ptr = $60 + last = $62 + + ;; Set the value of `a` into the list and advance one position without + ;; growing the list. + ;; + ;; Use this function instead of `List::push` if you have already set + ;; `List::ptr` as desired (e.g. with `LIST_IT_FROM`) and you just want to + ;; set a specific value to this address. That is, the list has already been + ;; defined somewhere else and you are just modifying some position. + ;; + ;; If the list pointer is already at the end and the operation is not + ;; possible, then `y` is set to $FF, otherwise `y` will be set to 0. + ;; + ;; NOTE: registers modified: `a` and `y`. + .proc set + tay + + ;; Check if `List::ptr` >= `List::last` (16-bit comparison). If this is + ;; the case, then we are actually done, otherwise we can proceed to set + ;; the value as desired. + lda List::ptr + 1 + cmp List::last + 1 + bcc @do + bne @done + lda List::ptr + cmp List::last + bcc @do + @done: + ;; We were actually done: set `y` to `$FF` to denote "end of list". + ldy #$FF + lda #0 + rts + @do: + tya + ldy #0 + sta (List::ptr), y + + LIST_NEXT + + rts + .endproc + + ;; Push the value of `a` after the last position of the list and grow one + ;; more byte. + ;; + ;; Note that this subroutine assumes that we are already at the last item of + ;; the list. Hence, if you reset `List::ptr` (e.g. with `LIST_IT_FROM`) and + ;; then call this subroutine, you will also move the `List::last` pointer + ;; accordingly and thus you might have (accidentally) shrinked the list. If + ;; this is not a behavior that you want (you just want to set a value at a + ;; specific location), then take a look at `List::set` instead. + ;; + ;; NOTE: registers modified: `a` and `y`. + ;; NOTE: this subroutine might set the carry flag. + .proc push + ldy #0 + sta (List::ptr), y + + clc + lda #1 + adc List::ptr + sta List::ptr + sta List::last + lda #0 + adc List::ptr + 1 + sta List::ptr + 1 + sta List::last + 1 + rts + .endproc + + ;; Get the contents of the current position of the list pointer and advance + ;; it. + ;; + ;; The byte will be stored into the 'a' register. If the list pointer is + ;; already at the end and won't fetch relevant data, then `y` is set to $FF, + ;; otherwise `y` will be set to 0. + ;; + ;; NOTE: registers modified: 'a' and 'y'. + ;; NOTE: this subroutine might set the carry flag. + .proc get + ;; Check if `List::ptr` >= `List::last` (16-bit comparison). If this is + ;; the case, then we are actually done, otherwise we can proceed to + ;; fetch the value as desired. + lda List::ptr + 1 + cmp List::last + 1 + bcc @do + bne @done + lda List::ptr + cmp List::last + bcc @do + @done: + ;; We were actually done: set `y` to `$FF` to denote "end of list" and + ;; zero out the returned value. + ldy #$FF + lda #0 + rts + @do: + ;; Fetch the value and push it into the stack since `LIST_NEXT` will + ;; actually mess with the `a` register. + ldy #0 + lda (List::ptr), y + pha + + LIST_NEXT + + ;; Get the value back from the stack so to set the proper return value. + pla + + rts + .endproc +.endscope |
