Commit: df60ac38795a1ba52deda59a25897c59934b7fa4 Parent: 113484927c665468867c945c4e136a1aefcb77dc Author: Vi Grey Date: 2023-08-03 18:04 UTC Summary: Add source code Makefile | 42 +++ neszip.py | 117 ++++++ src/graphics/nametable.nam | Bin 0 -> 1024 bytes src/graphics/palette.pal | 1 + src/graphics/tiles.chr | Bin 0 -> 8192 bytes src/nes-tbas-v1.asm | 1602 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1762 insertions(+) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..867a6c6 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +# Copyright (C) 2018, Vi Grey +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +PKG_NAME := nes-tbas-v1 +CURRENTDIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) +DIRNAME := $(shell basename $(CURRENTDIR)) + +all: + mkdir -p $(CURRENTDIR)/bin; \ + mkdir -p $(CURRENTDIR)/pkg; \ + cd $(CURRENTDIR)..; \ + zip $(CURRENTDIR)pkg/$(PKG_NAME).zip -r $(DIRNAME) -x "$(DIRNAME)/bin/*" -x "$(DIRNAME)/pkg/*" -x "$(DIRNAME)/.git/*"; \ + cd $(CURRENTDIR)src; \ + asm $(PKG_NAME).asm ../bin/$(PKG_NAME).nes; \ + cd ..; \ + ./neszip.py bin/$(PKG_NAME).nes pkg/$(PKG_NAME).zip; \ + rm -rf $(CURRENTDIR)pkg + +clean: + rm -rf $(CURRENTDIR)bin $(CURRENTDIR)pkg diff --git a/neszip.py b/neszip.py new file mode 100755 index 0000000..c551bf9 --- /dev/null +++ b/neszip.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2018, Vi Grey +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +import os.path +import struct +import sys + +VERSION = '0.0.4' +HOSTOS = b'\x03' + + +# Check if help or version flag was passed as an argument +if len(sys.argv) >= 2: + if sys.argv[1] == '--version' or sys.argv[1] == '-v': + print('neszip ' + VERSION) + exit(0) +# Check if there are enough arguments otherwise +elif len(sys.argv) != 3: + print('\x1b[91m2 NES file and ZIP file paths required\x1b[0m') + exit(1) + +# Get nes file and zip file inputs values from arguments +nes_input = sys.argv[1] +zip_input = sys.argv[2] + +# Takes the zip file and modifies its contents to be correctly offset +# And also claims the zip file was made on an Atari ST +def reoffset_zip(n, z, i): + new_z = b'' + z_cursor = 0 + # get last central directory file header signature + last_file_index = z.rfind(b'\x50\x4b\x01\x02') + index_check = True + if last_file_index == -1: + index_check = False + while index_check: + next_file_index = z.index(b'\x50\x4b\x01\x02', z_cursor) + if next_file_index == last_file_index: + index_check = False + new_z += z[z_cursor: next_file_index] + z_cursor = next_file_index + new_z += z[z_cursor: z_cursor + 5] + new_z += HOSTOS + new_z += z[z_cursor + 6: z_cursor + 42] + old_z_offset = struct.unpack('= 32762: + # 32762 gives a 6 byte buffer for end of PRG + i = n.rfind(b'\x00' * len(z), 16, 32778) + if i != -1: + new_nes_contents += n[:i] + z_reoffset = reoffset_zip(n, z, i) + new_nes_contents += z_reoffset + new_nes_contents += n[i + len(z):] + return(new_nes_contents) + else: + print('\x1b[91mNot enough space in NES file for ZIP file\x1b[0m') + exit(1) + else: + print('\x1b[91mInvalid NES file size\x1b[0m') + exit(1) + +# Get contents of NES rom file and zip file +try: + nes_file = open(nes_input, 'rb+') + nes_content = nes_file.read() + zip_file = open(zip_input, 'rb') + zip_content = zip_file.read() + zip_file.close() +except: + print('\x1b[91mUnable to open or read an input file\x1b[0m') + exit(1) + +# Create the new NES file that has the embedded zip file +try: + nes_file.seek(0) + nes_file.write(create_polyglot(nes_content, zip_content)) + nes_file.close() +except: + print('\x1b[91mUnable to write output file\x1b[0m') + exit(1) diff --git a/src/graphics/nametable.nam b/src/graphics/nametable.nam new file mode 100644 index 0000000..e69e8cd Binary files /dev/null and b/src/graphics/nametable.nam differ diff --git a/src/graphics/palette.pal b/src/graphics/palette.pal new file mode 100644 index 0000000..0e619e8 --- /dev/null +++ b/src/graphics/palette.pal @@ -0,0 +1 @@ +-,!1& )-,!1& ) \ No newline at end of file diff --git a/src/graphics/tiles.chr b/src/graphics/tiles.chr new file mode 100644 index 0000000..8adc8be Binary files /dev/null and b/src/graphics/tiles.chr differ diff --git a/src/nes-tbas-v1.asm b/src/nes-tbas-v1.asm new file mode 100644 index 0000000..69d30c2 --- /dev/null +++ b/src/nes-tbas-v1.asm @@ -0,0 +1,1602 @@ +;; Copyright (C) 2018, Vi Grey +;; All rights reserved. +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following conditions +;; are met: +;; +;; 1. Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; 2. Redistributions in binary form must reproduce the above copyright +;; notice, this list of conditions and the following disclaimer in the +;; documentation and/or other materials provided with the distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +;; ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +;; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +;; SUCH DAMAGE. + +;; Please pardon the code quality. I came up with this project 5 days +;; before I had to have it finished. Some planned features had to be +;; cut due to time constraints. These features might be added in a +;; version 2. + +;;;;;;;;;; +;; +;; iNES Header +;; +;;;;;;;;;; + +;;INES header setup + .db "NES", $1A + .db $02 + .db $01 + .db $00 + .db $00 + .db 0, 0, 0, 0, 0, 0, 0, 0 ; pad header to 16 bytes + +;;;;;;;;;; +;; +;; Variables +;; +;;;;;;;;;; + +;;Initialize Variables + +EDIT_MODE = $00 +EXECUTE_MODE = $01 +INPUT_MODE = $02 +BUTTON_A = 1 << 7 +BUTTON_B = 1 << 6 +BUTTON_SELECT = 1 << 5 +BUTTON_START = 1 << 4 +BUTTON_UP = 1 << 3 +BUTTON_DOWN = 1 << 2 +BUTTON_LEFT = 1 << 1 +BUTTON_RIGHT = 1 << 0 +ECELL = $300 +PCELL = $400 +MCELL = $500 +FIFO = $600 +IO_MODE_COUNT = $06 + + enum $00 +addr dsb 2 +bottomlinestart dsb 2 +processmode dsb 1 +ePointer dsb 2 +pPointer dsb 2 +mPointer dsb 2 +fifoPointer dsb 2 +squarebrackets dsb 2 +iomode dsb 1 +inputbyte dsb 8 +inputbytePointer dsb 1 +controller dsb 1 +controllerLastFrame dsb 1 +drawAllowed dsb 1 +tmpNum dsb 3 + ende + + .base $8000 + + +;;;;;;;;;; +;; +;; Reset Management +;; +;;;;;;;;;; + +;; Run upon power on or reset +RESET: + sei ; disable IRQs + cld ; disable decimal mode, meant to make decimal arithmetic "easier" + ldx #$40 + stx $4017 ; disable APU frame IRQ + ldx #$FF + txs ; Set up stack + inx ; X overflows back to 0 + jsr DisableNMI + jsr Blank + stx $4010 ; disable DMC IRQs + ldx #$00 ; reset x back to 0 + ldy #$00 ; reset y back to 0 + + +;;;;;;;;;; +;; +;; Vblank Waits +;; +;;;;;;;;;; + +;; First vblank wait to make sure PPU is ready +vwait1: + lda $2002 ;wait + bpl vwait1 + +;; Second vblank wait, PPU is ready after this +vwait2: + lda $2002 ;wait + bpl vwait2 + + +;;;;;;;;;; +;; +;; Memory Clearing Management +;; +;;;;;;;;;; + +;; Initialize memory and APU +Initialize: + ldx #$00 +InitializeLoop: + ; reset all values of $0000 - $07FF back to 0 + lda #$00 + sta $0000, x + sta $0100, x + sta $0300, x + sta $0400, x + sta $0500, x + sta $0600, x + sta $0700, x + lda #$fe + sta $0200, x + inx + bne InitializeLoop ;repeat until x rolls back to 0 + sta ePointer + sta mPointer + sta fifoPointer + sta iomode +InitializeDone: + lda #$00 ;reset A value + ldx #$00 ;reset X value + ldy #$00 ;reset Y value + + +;;;;;;;;;; +;; +;; Palette/Background Initialization +;; +;;;;;;;;;; + +;; Load initial palette data and background + jsr LoadPalettes + jsr LoadMainScreen + jsr AllowDraw + jsr EnableNMI + + +;;;;;;;;;; +;; +;; Infinite Loop +;; +;;;;;;;;;; + +Forever: + jmp Forever + + +;;;;;;;;;; +;; +;; Game Main Loop +;; +;;;;;;;;;; + +;; Game Main Loop +MAINLOOP: ;non-maskable interrupt (draw screen) + lda #$00 + sta $2003 ;set low byte (00) of the ram address + lda #$02 + sta $4014 ;set high byte (02) of the ram address, start transfer + ldx drawAllowed + cpx #$01 + bne DrawFinished ;continue if drawAllowed is 1 + lda $2002 + lda #$00 + sta $2005 + sta $2005 ;Reset scroll by passing #$0000 to $2005 + lda #%10001000 + jsr EnableNMI + jsr Draw +DrawFinished: + ; Update game + jsr Update +MAINLOOPDone: + rti ;return from interrupt + + +;;;;;;;;;; +;; +;; Draw Blank and Update +;; +;;;;;;;;;; + +;set up and enable NMI +EnableNMI: + lda #%10001000 + sta $2000 + rts + +;set up the PPU +Draw: + lda #%00001110 + sta $2001 + rts + +DisableNMI: + lda #$00 + sta $2000 + rts + +AllowDraw: + lda #$01 + sta drawAllowed + rts + +Blank: + lda #$00 + sta $2001 ; disable rendering + sta drawAllowed + rts + +;update logic states +Update: + jsr LatchController + jsr PollController + jsr ReadUp + jsr ReadLeft + jsr ReadRight + jsr ReadStart + jsr ReadSelect + jsr ReadAUp + jsr ReadADown + jsr ReadALeft + jsr ReadARight + jsr ReadBUp + jsr ReadBDown + jsr ReadBLeft + jsr ReadBRight + rts + + +;;;;;;;;;; +;; +;; Math +;; +;;;;;;;;;; + +MCELLMPointerHexToDec: + lda #$00 + sta tmpNum + sta (tmpNum + 1) + sta (tmpNum + 2) + ldx mPointer + ldy (mPointer + 1) + cpx #$01 ;; if x == 1, end + bne MCELLMPointerHexToDecEndMPointer ; Continue if x is not 1 + jmp MCELLMPointerHexToDecDone +MCELLMPointerHexToDecEndMPointer: + ldx (MCELL), y +MCELLMPointerHexToDecLoop: + cpx #$00 ; If x == 0, end loop + beq MCELLMPointerHexToDecLoopDone ; Continue if x is not 1 + dex + ldy (tmpNum + 2) + cpy #$09 ;; if tmpNum + 2 != 9 + beq MCELLMPointerHexToDecLoopIncTmpNum2Done + inc (tmpNum + 2) + jmp MCELLMPointerHexToDecLoop +MCELLMPointerHexToDecLoopIncTmpNum2Done: + ldy #$00 + sty (tmpNum + 2) + ldy (tmpNum + 1) + cpy #$09;; if tmpNum + 1 != 9 + beq MCELLMPointerHexToDecLoopIncTmpNum1Done + inc (tmpNum + 1) + jmp MCELLMPointerHexToDecLoop +MCELLMPointerHexToDecLoopIncTmpNum1Done: + ldy #$00 + sty (tmpNum + 1) + inc tmpNum + jmp MCELLMPointerHexToDecLoop +MCELLMPointerHexToDecLoopDone: + ldx pPointer ;; If pPointer != 1 + cpx #$01 + beq MCELLMPointerHexToDecDone ; Continue if mPointer != 1 + lda tmpNum + cmp #$00 + beq MCELLMPointerHexToDecTensDigit ; Continue if tmpNum != 0 + ldy (pPointer + 1) + clc + adc #$30 + sta (PCELL), y + inc (pPointer + 1) + ldx (pPointer + 1) ;; If pPointer + 1 rolls back to 0, inc mPointer + cpx #$00 + bne MCELLMPointerHexToDecTensDigit ; Continue if mPointer + 1 wraps up to 0 + inc pPointer +MCELLMPointerHexToDecTensDigit: + ldx pPointer ;; If pPointer != 1 + cpx #$01 + beq MCELLMPointerHexToDecDone ; Continue if mPointer != 1 + lda (tmpNum + 1) + cmp #$00 + beq MCELLMPointerHexToDecOnesDigit ; Continue if tmpNum + 1 != 0 + ldy (pPointer + 1) + clc + adc #$30 + sta (PCELL), y + inc (pPointer + 1) + ldx (pPointer + 1) ;; If pPointer + 1 rolls back to 0, inc mPointer + cpx #$00 + bne MCELLMPointerHexToDecOnesDigit ; Continue if mPointer + 1 wraps up to 0 + inc pPointer +MCELLMPointerHexToDecOnesDigit: + ldx pPointer ;; If pPointer != 1 + cpx #$01 + beq MCELLMPointerHexToDecDone ; Continue if mPointer != 1 + lda (tmpNum + 2) + ldy (pPointer + 1) + clc + adc #$30 + sta (PCELL), y + inc (pPointer + 1) + ldx (pPointer + 1) ;; If pPointer + 1 rolls back to 0, inc mPointer + cpx #$00 + bne MCELLMPointerHexToDecDone ; Continue if pPointer + 1 wraps up to 0 + inc pPointer +MCELLMPointerHexToDecDone: + rts + +MCELLMPointerToASCII: + ldx pPointer + cpx #$01 + beq MCELLMPointerToASCIIDone ; Continue if pPointer is not 1 + ldx mPointer + ldy (mPointer + 1) + cpx #$01 ;; if x == 1, end + bne MCELLMPointerToASCIIEndMPointer ; Continue if mPointer is not 1 + jmp MCELLMPointerToASCIIDone +MCELLMPointerToASCIIEndMPointer: + ldx (MCELL), y + txa + ;; if mcell value is under #$20, write 0 + sec ; Set carry flag + sbc #$20 + bcs MCELLMPointerToASCIICheckOver126 ; Continue if A did not drop below 0 + ldx #$00 + jmp MCELLMPointerToASCIIWriteValue +MCELLMPointerToASCIICheckOver126 + txa + ;; if mcell value is over #$7E, write 127 + sec ; Set carry flag + sbc #$7E + bcc MCELLMPointerToASCIIWriteValue ; Continue if A dropped below 0 + ldx #$7F +MCELLMPointerToASCIIWriteValue: + ldy (pPointer + 1) + txa + sta (PCELL), y +MCELLMPointerToASCIIIncPPointer: + inc (pPointer + 1) + ldy (pPointer + 1) + cpy #$00 + bne MCELLMPointerToASCIIDone; Continue if pPointer + 1 wrapped up to 0 + inc pPointer +MCELLMPointerToASCIIDone: + rts + +;;;;;;;;;; +;; +;; Palette Management +;; +;;;;;;;;;; + +;; Load palette data +LoadPalettes: + lda $2002 ;read PPU to reset write toggle + lda #$3f + sta $2006 + lda #$00 ;point $2006 to the nametable (0x3F00) + sta $2006 + ldx #$00 ;set to start of palette +LoadPalettesLoop: + lda PaletteData, x ;loads a 32 byte palette + sta $2007 + inx + cpx #$20 + bne LoadPalettesLoop + rts + + +;;;;;;;;;; +;; +;; Controller Handling +;; +;;;;;;;;;; + +;; Tell both controllers to latch buttons +LatchController: + lda #$01 + sta $4016 + lda #$00 + sta $4016 ;Sending 01 then 00 to $4016 strobes the controllers for input + rts + +;;Get button presses from both controllers +PollController: + lda controller + sta controllerLastFrame + ldx #$08 ;8 buttons total +PollControllerLoop: + lda $4016 ;player 1 + lsr A ;shift right + rol controller ;rotate left button vector in controller mem location + dex + bne PollControllerLoop ;repeat loop if x != 0 + rts + +ReadAUp: + lda controller + cmp #(BUTTON_A + BUTTON_UP) + bne ReadAUpDone ; continue if A + Up was pressed +; Code if buttons were pressed + ldx processmode + cpx #EDIT_MODE + bne ReadAUpDone ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #(BUTTON_A + BUTTON_UP) + beq ReadAUpDone +; Code if this button combination wasn't pressed last frame + jsr AddPlus + jsr WriteOperators +ReadAUpDone: + rts + +ReadADown: + lda controller + cmp #(BUTTON_A + BUTTON_DOWN) + bne ReadADownDone ; continue if A + Down was pressed +; Code if buttons were pressed + ldx processmode + cpx #EDIT_MODE + bne ReadADownDone ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #(BUTTON_A + BUTTON_DOWN) + beq ReadADownDone +; Code if this button combination wasn't pressed last frame + jsr AddMinus + jsr WriteOperators +ReadADownDone: + rts + +ReadALeft: + lda controller + cmp #(BUTTON_A + BUTTON_LEFT) + bne ReadALeftDone ; continue if A + Left was pressed +; Code if buttons were pressed + ldx processmode + cpx #EDIT_MODE + bne ReadALeftDone ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #(BUTTON_A + BUTTON_LEFT) + beq ReadALeftDone +; Code if this button combination wasn't pressed last frame + jsr AddEqual + jsr WriteOperators +ReadALeftDone: + rts + +ReadARight: + lda controller + cmp #(BUTTON_A + BUTTON_RIGHT) + bne ReadARightDone ; continue if A + Right was pressed +; Code if buttons were pressed + ldx processmode + cpx #EDIT_MODE + bne ReadARightDone ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #(BUTTON_A + BUTTON_RIGHT) + beq ReadARightDone +; Code if this button combination wasn't pressed last frame + jsr AddQuestionMark + jsr WriteOperators +ReadARightDone: + rts + +ReadSelect: + lda controller + cmp #(BUTTON_SELECT) + bne ReadSelectDone ; continue if Select was pressed +; Code if buttons were pressed + ldx processmode + cpx #EDIT_MODE + bne ReadSelectDone ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #(BUTTON_SELECT) + beq ReadSelectDone +; Code if this button combination wasn't pressed last frame + jsr Backspace + jsr WriteOperators +ReadSelectDone: + rts + +ReadBUp: + lda controller + cmp #(BUTTON_B + BUTTON_UP) + bne ReadBUpDone ; continue if B + Up was pressed +; Code if buttons were pressed + ldx processmode + cpx #EDIT_MODE + bne ReadBUpDone ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #(BUTTON_B + BUTTON_UP) + beq ReadBUpDone +; Code if this button combination wasn't pressed last frame + jsr AddLeftSquare + jsr WriteOperators +ReadBUpDone: + rts + +ReadBDown: + lda controller + cmp #(BUTTON_B + BUTTON_DOWN) + bne ReadBDownDone ; continue if B + Down was pressed +; Code if buttons were pressed + ldx processmode + cpx #EDIT_MODE + bne ReadBDownDone ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #(BUTTON_B + BUTTON_DOWN) + beq ReadBDownDone +; Code if this button combination wasn't pressed last frame + jsr AddRightSquare + jsr WriteOperators +ReadBDownDone: + rts + +ReadBLeft: + lda controller + cmp #(BUTTON_B + BUTTON_LEFT) + bne ReadBLeftDone ; continue if B + Left was pressed +; Code if buttons were pressed + ldx processmode + cpx #EDIT_MODE + bne ReadBLeftDone ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #(BUTTON_B + BUTTON_LEFT) + beq ReadBLeftDone +; Code if this button combination wasn't pressed last frame + jsr AddLeftAngle + jsr WriteOperators +ReadBLeftDone: + rts + +ReadBRight: + lda controller + cmp #(BUTTON_B + BUTTON_RIGHT) + bne ReadBRightDone ; continue if B + Right was pressed +; Code if buttons were pressed + ldx processmode + cpx #EDIT_MODE + bne ReadBRightDone ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #(BUTTON_B + BUTTON_RIGHT) + beq ReadBRightDone +; Code if this button combination wasn't pressed last frame + jsr AddRightAngle + jsr WriteOperators +ReadBRightDone: + rts + +ReadUp: + lda controller + cmp #BUTTON_UP + bne ReadUpDone ; continue if Up was pressed +; Code if buttons were pressed + ldx processmode + cpx #INPUT_MODE + bne ReadUpDone ; Continue if process mode is input mode + ldx controllerLastFrame + cpx #BUTTON_UP + beq ReadUpDone +; Code if this button combination wasn't pressed last frame + ldx inputbytePointer + lda (inputbyte), x + cmp #$00 + bne FlipInputByteToZero; Continue if inputbyte + x is 0 + lda #$01 + jmp FlipInputByteToZeroDone +FlipInputByteToZero: + lda #$00 +FlipInputByteToZeroDone: + sta (inputbyte), x + jsr Blank + jsr DrawInputByte + jsr AllowDraw +ReadUpDone: + rts + +ReadLeft: + lda controller + cmp #BUTTON_LEFT + bne ReadLeftDone ; continue if Left was pressed +; Code if buttons were pressed + ldx processmode + cpx #INPUT_MODE + bne ReadLeftDone ; Continue if process mode is input mode + ldx controllerLastFrame + cpx #BUTTON_LEFT + beq ReadLeftDone +; Code if this button combination wasn't pressed last frame + ldx inputbytePointer + cpx #$00 + beq ReadLeftDone; Continue if inputbytePointer is not 0 + dec inputbytePointer + jsr Blank + jsr DrawInputByte + jsr AllowDraw +ReadLeftDone: + rts + +ReadRight: + lda controller + cmp #BUTTON_RIGHT + bne ReadRightDone ; continue if Right was pressed +; Code if buttons were pressed + ldx processmode + cpx #INPUT_MODE + bne ReadRightDone ; Continue if process mode is input mode + ldx controllerLastFrame + cpx #BUTTON_RIGHT + beq ReadRightDone +; Code if this button combination wasn't pressed last frame + ldx inputbytePointer + cpx #$07 + beq ReadRightDone; Continue if inputbytePointer is not 7 + inc inputbytePointer + jsr Blank + jsr DrawInputByte + jsr AllowDraw +ReadRightDone: + rts + +ReadStart: + lda controller + cmp #BUTTON_START + bne ReadStartDone ; continue if Start was pressed +; Code if buttons were pressed + ldx processmode + cpx #EXECUTE_MODE + bne ReadStartEditCheck ; Continue if execute mode + ldx controllerLastFrame + cpx #BUTTON_START + beq ReadStartDone + ldx #EDIT_MODE + stx processmode + jsr Blank + lda #<(Commands) + sta addr + lda #>(Commands) + sta (addr + 1) + jsr WriteBottom + jsr WriteOperators + jsr AllowDraw + jmp ReadStartDone +ReadStartEditCheck: + ldx processmode + cpx #EDIT_MODE + bne ReadStartInputCheck ; Continue if process mode is edit mode + ldx controllerLastFrame + cpx #BUTTON_START + beq ReadStartDone +; Code if this button combination wasn't pressed last frame and edit mode + ldx #EXECUTE_MODE + stx processmode + ;; + jsr Blank + jsr ClearBottom + jsr AllowDraw + ;; + jsr ExecuteTBAS + jmp ReadStartDone +ReadStartInputCheck: + ldx processmode + cpx #INPUT_MODE + bne ReadStartDone ; Continue if process mode is input mode + ldx controllerLastFrame + cpx #BUTTON_START + beq ReadStartDone +; Code if this button combination wasn't pressed last frame and input mode + jsr InterpretInputByte + ;; + jsr ClearBottom + ;; + ldx #EXECUTE_MODE + stx processmode + inc (ePointer + 1) + ldy (ePointer + 1) + cpy #$00 + bne ReadStartIncEPointerDone ; Continue if ePointer + 1 wrapped up to 0 + inc ePointer +ReadStartIncEPointerDone: + jsr DisableNMI + jsr ExecuteTBASLoop +ReadStartDone: + rts + + +;;;;;;;;;; +;; +;; Top Text Management +;; +;;;;;;;;;; + +WriteOutput: + ldx #$00 + ldy #$00 + lda #$20 + sta $2006 + lda #$a3 + sta $2006 +WriteOutputLoop: + lda (PCELL), y + sta $2007 + inx + cpx #26 + bne WriteOutputLoopNotX26 ;Continue if x is 26 + ldx #$00 + lda #$01 + sta $2007 + sta $2007 + sta $2007 + sta $2007 + sta $2007 + sta $2007 +WriteOutputLoopNotX26: + iny + cpy #$00 + bne WriteOutputLoop ; Continue if y wrapped to 0 +WriteOutputDone: + rts + +WriteOperators: + jsr Blank + ldx #$00 + ldy #$00 + lda #$20 + sta $2006 + lda #$a3 + sta $2006 +WriteOperatorsLoop: + lda (ECELL), y + sta $2007 + inx + cpx #26 + bne WriteOperatorsLoopNotX26 ;Continue if x is 26 + ldx #$00 + lda #$01 + sta $2007 + sta $2007 + sta $2007 + sta $2007 + sta $2007 + sta $2007 +WriteOperatorsLoopNotX26: + iny + cpy #$00 + bne WriteOperatorsLoop ; Continue if y wrapped to 0 +WriteOperatorsDone: + jsr AllowDraw + rts + + +;;;;;;;;;; +;; +;; Input Byte Text Management +;; +;;;;;;;;;; + +InitializeInputByte: + ldx #$00 + stx inputbytePointer + ldy #$00 +InitializeInputByteLoop: + ldx (inputbyte), y + iny + cpy #$08 + bne InitializeInputByteLoop ; Continue if y is 8 +InitializeInputByteDone: + rts + +DrawInputByte: + lda #$22 + sta $2006 + lda #$8b + sta $2006 + ldx #$00 +DrawInputByteLoop: + lda (inputbyte), x + cpx inputbytePointer + bne DrawInputByteCursorBit ; Continue if x is inputbytePointer + clc + adc #$30 + jmp DrawInputByteCursorBitDone +DrawInputByteCursorBit: + clc + adc #$d0 +DrawInputByteCursorBitDone: + sta $2007 + inx + cpx #$08 + bne DrawInputByteLoop +DrawInputByteDone: + rts + +;;;;;;;;;; +;; +;; Command Management +;; +;;;;;;;;;; + +Backspace: + lda #$00 + ldy ePointer + ldx (ePointer + 1) + cpy #$00 + bne BackspaceEPointerDec ; Continue if ePointer is 0 + cpx #$00 + bne BackspaceEPointerDec ; Continue if ePointer + 1 is 0 + jmp BackspaceDone +BackspaceEPointerDec: + dec (ePointer + 1) + ldx (ePointer + 1) + cpx #$ff + bne BackspaceEPointerDecDone; Continue if ePointer + 1 rolled back to ff + dec ePointer +BackspaceEPointerDecDone: + ldx (ePointer + 1) + sta (ECELL), x +BackspaceDone: + rts + +ExecuteTBAS: + jsr DisableNMI + lda #$00 + sta iomode + sta ePointer + sta (ePointer + 1) + sta pPointer + sta (pPointer + 1) + sta mPointer + sta (mPointer + 1) + ldy #$00 +ExecuteTBASClearCELLsLoop: + sta (MCELL), y + sta (PCELL), y + iny + cpy #$00 + bne ExecuteTBASClearCELLsLoop ; Continue if y rolled forward to 0 +ExecuteTBASLoop: + ldx ePointer + ldy (ePointer + 1) + cpx #$01 + bne ExecuteTBASEPointerNot1 ; Continue if ePointer is 1 + jmp ExecuteTBASExecuteDone +ExecuteTBASEPointerNot1: + lda (ECELL), y + cmp #$00 + beq ExecuteTBASExecuteDone ; Continue if ecell value is not 0 +ExecuteTBASLeftSquare: + cmp #$5b + bne ExecuteTBASRightSquare ; Continue if ecell value is [ + jsr LeftSquare + jmp ExecuteTBASOperatorDone +ExecuteTBASRightSquare: + cmp #$5d + bne ExecuteTBASLeftAngle ; Continue if ecell value is [ + jsr RightSquare + jmp ExecuteTBASLoop +ExecuteTBASLeftAngle: + cmp #$3c + bne ExecuteTBASRightAngle ; Continue if ecell value is < + jsr LeftAngle + jmp ExecuteTBASOperatorDone +ExecuteTBASRightAngle: + cmp #$3e + bne ExecuteTBASPlus ; Continue if ecell value is > + jsr RightAngle + jmp ExecuteTBASOperatorDone +ExecuteTBASPlus: + cmp #$2b + bne ExecuteTBASMinus ; Continue if ecell value is + + jsr Plus + jmp ExecuteTBASOperatorDone +ExecuteTBASMinus: + cmp #$2d + bne ExecuteTBASEqual ; Continue if ecell value is - + jsr Minus + jmp ExecuteTBASOperatorDone +ExecuteTBASEqual: + cmp #$3d + bne ExecuteTBASQuestion ; Continue if ecell value is = + jsr Equal + jmp ExecuteTBASOperatorDone +ExecuteTBASQuestion: + cmp #$3f + bne ExecuteTBASOperatorDone ; Continue if ecell value is ? + jsr QuestionMark + lda iomode + cmp #$02 + beq ExecuteTBASDone ; Continue if iomode value is not 2 + jmp ExecuteTBASOperatorDone +ExecuteTBASOperatorDone: + inc (ePointer + 1) + ldy (ePointer + 1) + cpy #$00 + bne ExecuteTBASIncEPointerDone ; Continue if ePointer + 1 wrapped up to 0 + inc ePointer +ExecuteTBASIncEPointerDone: + jmp ExecuteTBASLoop +ExecuteTBASExecuteDone: + lda #$00 + ldx #$00 + stx ePointer + stx (ePointer + 1) +ExecuteTBASClearECELLLoop: + sta (ECELL), x + inx + cpx #$00 + bne ExecuteTBASClearECELLLoop ; Continue if x rolled forward to 0 + + jsr Blank + lda #<(ExecutingDone) + sta addr + lda #>(ExecutingDone) + sta (addr + 1) + jsr WriteBottom + jsr WriteOutput + jsr AllowDraw + + jsr EnableNMI +ExecuteTBASDone: + rts + +InterpretInputByte: + lda #$00 + ; Manually check of all 8 positions + ldx #$07 + ldy inputbyte, x + cpy #$01 + bne InputByteBit7Done; Continue if inputbyte + x is 1 + clc + adc #$01 +InputByteBit7Done: + dex + ldy inputbyte, x + cpy #$01 + bne InputByteBit6Done; Continue if inputbyte + x is 1 + clc + adc #$02 +InputByteBit6Done: + dex + ldy inputbyte, x + cpy #$01 + bne InputByteBit5Done; Continue if inputbyte + x is 1 + clc + adc #$04 +InputByteBit5Done: + dex + ldy inputbyte, x + cpy #$01 + bne InputByteBit4Done; Continue if inputbyte + x is 1 + clc + adc #$08 +InputByteBit4Done: + dex + ldy inputbyte, x + cpy #$01 + bne InputByteBit3Done; Continue if inputbyte + x is 1 + clc + adc #$10 +InputByteBit3Done: + dex + ldy inputbyte, x + cpy #$01 + bne InputByteBit2Done; Continue if inputbyte + x is 1 + clc + adc #$20 +InputByteBit2Done: + dex + ldy inputbyte, x + cpy #$01 + bne InputByteBit1Done; Continue if inputbyte + x is 1 + clc + adc #$40 +InputByteBit1Done: + dex + ldy inputbyte, x + cpy #$01 + bne InputByteBit0Done; Continue if inputbyte + x is 1 + clc + adc #$80 +InputByteBit0Done: + ldx mPointer + ldy (mPointer + 1) + cpx #$01 + beq InterpretInputByteDone ; Continue if mPointer is not 1 + sta (MCELL), y +InterpretInputByteDone: + ldx #EXECUTE_MODE + stx processmode + rts + +;;;;;;;;;; +;; +;; Operation Management +;; +;;;;;;;;;; + +AddLeftSquare: + ldx ePointer + cpx #$01 + beq AddLeftSquareDone; Continue if ePointer is not 1 +InsertLeftSquare: + lda #$5b + ldy (ePointer + 1) + sta (ECELL), y + ldx (ePointer + 1) + inc (ePointer + 1) + cpx #$ff + bne AddLeftSquareDone; Continue if (ePointer + 1) rolled over to 0 from ff + inc ePointer +AddLeftSquareDone: + rts + +AddRightSquare: + ldx ePointer + cpx #$01 + beq AddRightSquareDone; Continue if ePointer is not 1 +InsertRightSquare: + lda #$5d + ldy (ePointer + 1) + sta (ECELL), y + ldx (ePointer + 1) + inc (ePointer + 1) + cpx #$ff + bne AddRightSquareDone; Continue if (ePointer + 1) rolled over to 0 from ff + inc ePointer +AddRightSquareDone: + rts + +AddLeftAngle: + ldx ePointer + cpx #$01 + beq AddLeftAngleDone; Continue if ePointer is not 1 +InsertLeftAngle: + lda #$3c + ldy (ePointer + 1) + sta (ECELL), y + ldx (ePointer + 1) + inc (ePointer + 1) + cpx #$ff + bne AddLeftAngleDone; Continue if (ePointer + 1) rolled over to 0 from ff + inc ePointer +AddLeftAngleDone: + rts + +AddRightAngle: + ldx ePointer + cpx #$01 + beq AddRightAngleDone; Continue if ePointer is not 1 +InsertRightAngle: + lda #$3e + ldy (ePointer + 1) + sta (ECELL), y + ldx (ePointer + 1) + inc (ePointer + 1) + cpx #$ff + bne AddRightAngleDone; Continue if (ePointer + 1) rolled over to 0 from ff + inc ePointer +AddRightAngleDone: + rts + +AddPlus: + ldx ePointer + cpx #$01 + beq AddPlusDone; Continue if ePointer is not 1 +InsertPlus: + lda #$2b + ldy (ePointer + 1) + sta (ECELL), y + ldx (ePointer + 1) + inc (ePointer + 1) + cpx #$ff + bne AddPlusDone; Continue if (ePointer + 1) rolled over to 0 from ff + inc ePointer +AddPlusDone: + rts + +AddMinus: + ldx ePointer + cpx #$01 + beq AddMinusDone; Continue if ePointer is not 1 +InsertMinus: + lda #$2d + ldy (ePointer + 1) + sta (ECELL), y + ldx (ePointer + 1) + inc (ePointer + 1) + cpx #$ff + bne AddMinusDone; Continue if (ePointer + 1) rolled over to 0 from ff + inc ePointer +AddMinusDone: + rts + +AddEqual: + ldx ePointer + cpx #$01 + beq AddEqualDone; Continue if ePointer is not 1 +InsertEqual: + lda #$3d + ldy (ePointer + 1) + sta (ECELL), y + ldx (ePointer + 1) + inc (ePointer + 1) + cpx #$ff + bne AddEqualDone; Continue if (ePointer + 1) rolled over to 0 from ff + inc ePointer +AddEqualDone: + rts + +AddQuestionMark: + ldx ePointer + cpx #$01 + beq AddQuestionMarkDone; Continue if ePointer is not 2 +InsertQuestionMark: + lda #$3f + ldy (ePointer + 1) + sta (ECELL), y + ldx (ePointer + 1) + inc (ePointer + 1) + cpx #$ff + bne AddQuestionMarkDone; Continue if (ePointer + 1) rolled over to 0 from ff + inc ePointer +AddQuestionMarkDone: + rts + +LeftSquare: + ldx #$00 + stx squarebrackets + stx (squarebrackets + 1) + ldx mPointer + ldy (mPointer + 1) + lda (MCELL), y + cmp #$00 + bne LeftSquareDone ; Continue if MCELL value is 0 +LeftSquareLoop: + ;; ePtr++ + inc (ePointer + 1) + ldx (ePointer + 1) + cpx #$00 + bne LeftSquareEPointerIncDone; Continue if ePointer + 1 rolled over to 0 + inc ePointer +LeftSquareEPointerIncDone: + ;; if ePtr[0] == 1, end + ldx ePointer + ldy (ePointer + 1) + cpx #$01 + beq LeftSquareDone ; Continue if ePointer is not 1 + ;; get ECELL[ePtr] + lda (ECELL), y + cmp #$00 + beq LeftSquareDone ; Continue if ECELL value is not 0 + cmp #$5b + bne LeftSquareIncSquareBracketsDone ; Continue if ECELL value is [ + inc (squarebrackets + 1) + ldy (squarebrackets + 1) + cpy #$00 + bne LeftSquareIncSquareBracketsDone ; Continue if squarebrackets + 1 is 0 + inc squarebrackets + jmp LeftSquareLoop +LeftSquareIncSquareBracketsDone: + cmp #$5d + bne LeftSquareLoop ; Continue if ePointer is on ] + ldx squarebrackets + cpx #$00 + bne LeftSquareDecSquareBrackets ; Continue if squarebrackets is 0 + ldy (squarebrackets + 1) + cpy #$00 + bne LeftSquareDecSquareBrackets ; Continue if squarebrackets is 0 + jmp LeftSquareDone +LeftSquareDecSquareBrackets: + dec (squarebrackets + 1) + ldx (squarebrackets + 1) + cpx #$ff + bne LeftSquareDecSquareBracketsDone ; Continue if squarebrackets + 1 is ff + dec squarebrackets +LeftSquareDecSquareBracketsDone: + jmp LeftSquareLoop +LeftSquareDone: + rts + +RightSquare: + ldx #$00 + stx squarebrackets + stx (squarebrackets + 1) +RightSquareLoop: + ;; ePtr-- + dec (ePointer + 1) + ldy (ePointer + 1) + cpy #$ff + bne RightSquareEPointerDecDone; Continue if ePointer + 1 rolled back to ff + dec ePointer +RightSquareEPointerDecDone: + ;; if ePtr == FF -- + ldx ePointer + cpx #$ff + beq RightSquareSpinErr ; Continue if ePointer is not ff + ;; Get eCell[ePtr] + lda (ECELL), y + cmp #$5d + bne RightSquareIncSquareBracketsDone ; Continue if ECELL value is ] + inc (squarebrackets + 1) + ldy (squarebrackets + 1) + cpy #$00 + bne RightSquareIncSquareBracketsDone ; Continue if squarebrackets + 1 is 0 + inc squarebrackets + jmp RightSquareLoop +RightSquareIncSquareBracketsDone: + cmp #$5b + bne RightSquareLoop ; Continue if ePointer is on [ + ldx squarebrackets + cpx #$00 + bne RightSquareDecSquareBrackets ; Continue if squarebrackets is 0 + ldy (squarebrackets + 1) + cpy #$00 + bne RightSquareDecSquareBrackets ; Continue if squarebrackets is 0 + jmp RightSquareDone +RightSquareDecSquareBrackets: + dec (squarebrackets + 1) + ldx (squarebrackets + 1) + cpx #$ff + bne LeftSquareDecSquareBracketsDone ; Continue if squarebrackets + 1 is ff + dec squarebrackets +RightSquareDecSquareBracketsDone: + jmp LeftSquareLoop +RightSquareSpinErr: + ldx #$00 + stx ePointer + stx (ePointer + 1) + jmp RightSquareLoop +RightSquareDone: + rts + +LeftAngle: + lda mPointer + cmp #$00 + bne MCellLeft ; Continue if mPointer is 0 + lda (mPointer + 1) + cmp #$00 + bne MCellLeft ; Continue if (mPointer + 1) is 0 + jmp LeftAngleDone +MCellLeft: + lda (mPointer + 1) + dec (mPointer + 1) + cmp #$00 + bne LeftAngleDone ; Continue if (mPointer + 1) rolled back to ff from 0 + dec mPointer +LeftAngleDone: + rts + +RightAngle: + lda mPointer + cmp #$01 + bne MCellRight ; Continue if mPointer is 1 + lda (mPointer + 1) + cmp #$ff + bne MCellRight ; Continue if (mPointer + 1) is ff + jmp RightAngleDone +MCellRight: + lda (mPointer + 1) + inc (mPointer + 1) + cmp #$ff + bne RightAngleDone; Continue if (mPointer + 1) rolled over to 0 from ff + inc mPointer +RightAngleDone: + rts + +Plus: + ldx mPointer + ldy (mPointer + 1) + lda (MCELL), y + cmp #$ff + beq PlusDone; Continue if MCELL point is not ff + ldx (MCELL), y + inx + txa + sta (MCELL), y +PlusDone: + rts + +Minus: + ldx mPointer + ldy (mPointer + 1) + lda (MCELL), y + cmp #$00 + beq MinusDone; Continue if MCELL point is not 0 + ldx (MCELL), y + dex + txa + sta (MCELL), y +MinusDone: + rts + +Equal: + ldx mPointer + ldy (mPointer + 1) + lda (MCELL), y + ldx (MCELL), y + sec ; Set carry flag + sbc #IO_MODE_COUNT + bcs EqualDone ; Continue if A dropped below 0 + stx iomode +EqualDone: + rts + +QuestionMark: + ldx iomode + cpx #$00 + bne QuestionMarkIOMode1 ; Continue if iomode is 0 + jsr WriteDecimal + jmp QuestionMarkDone +QuestionMarkIOMode1: + cpx #$01 + bne QuestionMarkIOMode2 ; Continue if iomode is 1 + jsr WriteASCII + jmp QuestionMarkDone +QuestionMarkIOMode2: + cpx #$02 + bne QuestionMarkIOMode3 ; Continue if iomode is 2 + jsr ReadByteInput + jmp QuestionMarkDone +QuestionMarkIOMode3: + cpx #$03 + bne QuestionMarkIOMode4 ; Continue if iomode is 3 + jsr AppendFIFO + jmp QuestionMarkDone +QuestionMarkIOMode4: + cpx #$04 + bne QuestionMarkIOMode5 ; Continue if iomode is 4 + jsr ClearFIFO + jmp QuestionMarkDone +QuestionMarkIOMode5: + cpx #$05 + bne QuestionMarkDone ;Continue if iomode is 5 + jsr ExecuteFIFO +QuestionMarkDone: + rts + + +;;;;;;;;;; +;; +;; IO Modes +;; +;;;;;;;;;; + +WriteDecimal: + jsr MCELLMPointerHexToDec + rts + +WriteASCII: + jsr MCELLMPointerToASCII + rts + +ReadByteInput: + ldx #INPUT_MODE + stx processmode + jsr EnableNMI + ldx #$00 + stx inputbytePointer + ldy #$08 +ClearInputBytesLoop: + dey + stx (inputbyte), y + cpy #$00 + bne ClearInputBytesLoop ; Continue if y is 0 + ;; + jsr InitializeInputByte + jsr Blank + lda #<(Input) + sta addr + lda #>(Input) + sta (addr + 1) + jsr WriteBottom + jsr DrawInputByte + jsr AllowDraw + ;; + ldx #INPUT_MODE + stx processmode + rts + +AppendFIFO: + lda fifoPointer + cmp #$01 + beq AppendFIFODone ; Continue if fifoPointer is not 1 + ldx mPointer + ldy (mPointer + 1) + lda MCELL, y + ldx (fifoPointer + 1) + ldy fifoPointer + sta (FIFO), x + inc (fifoPointer + 1) + lda (fifoPointer + 1) + cmp #00 + bne AppendFIFODone; Continue if fifoPointer + 1 rolls over to 0 + inc fifoPointer +AppendFIFODone: + rts + +ClearFIFO: ; Clear FIFO Buffer + lda #$00 + ldx #$00 +ClearFIFOLoop: + sta ($600), x + inx + bne ClearFIFOLoop ; Continue if x rolls over to 0 + sta fifoPointer + sta (fifoPointer + 1) + rts + +ExecuteFIFO: ; Execute FIFO Buffer + jsr $0600 + rts + +;;;;;;;;;; +;; +;; Background Management +;; +;;;;;;;;;; + +LoadMainScreen: + jsr Blank + ;get high and low byte of lobby screen nametable file + lda #<(MainNametable) + sta addr + lda #>(MainNametable) + sta (addr + 1) + ;set to start of title screen nametable + lda #$20 + sta $2006 + lda #$00 ;point $2006 to the nametable (0x2000) + sta $2006 + ;set counter of 4 for x, y will iterate 256 times per x counter + ldx #$04 + ldy #$00 +LoadMainScreenLoop: ;loop through and load nam file into PPU + lda (addr), y + sta $2007 + iny + bne LoadMainScreenLoop ;loop if y is not 0 + inc (addr + 1) + dex ;decrement x by 1 to indicate one set of y iterations is finished + bne LoadMainScreenLoop ;loop if x is not 0 +LoadMainScreenDone: + ;; + lda #<(Commands) + sta addr + lda #>(Commands) + sta (addr + 1) + jsr WriteBottom + ;; + rts + +ClearBottom: + lda #$22 + sta bottomlinestart + sta $2006 + lda #$83 ; Set bottomlinestart to 2283 + sta (bottomlinestart + 1) + lda #$80 ; Set $2006 to 2280 + sta $2006 + lda #$01 ; Set lda to #$01 (bottom background) + ldx #$00 ; + ldy #$01 ; When x rolls back to 0, decrement y by 1 +ClearBottomLoop: + sta $2007 + inx + bne ClearBottomLoop ; Continue if x is 0 + dey + bne ClearBottomLoop; Continue if y is 0 + rts + +WriteBottom: + jsr ClearBottom + ldy #$00 ; Reset Y + ldx #$00 ; Reset X +WriteBottomNewLine: + lda bottomlinestart + sta $2006 + lda (bottomlinestart + 1) + sta $2006 +WriteLineLoop: + lda (addr), y + cmp #$00 + beq WriteBottomDone ; Skip to end if A is 0 + iny + cmp #$01 + beq AddToBottomLineStart ; Continue if A is 1 + jsr OffsetBottomText ; Get offset for A value and store in A + sta $2007; + jmp WriteLineLoop +AddToBottomLineStart: + clc ; Clear carry flag + lda (bottomlinestart + 1) + adc #$20 ; Add #$20 to bottomlinestart + 1 + sta (bottomlinestart + 1) + lda bottomlinestart + adc #$00 ; Add 0 (or 1 if carry from bottomlinestart + 1) + sta bottomlinestart + jmp WriteBottomNewLine +WriteBottomDone: + rts + +OffsetBottomText: + clc + adc #$A0 + rts + + +;;;;;;;;;; +;; +;; Values And Files +;; +;;;;;;;;;; + +TitleTextValue: + .byte "NES TBAS INTERPRETER V1", $01, $01 + .byte "READY.", $01, $00 + +ExecutingDone: + .byte "START !CONTINUE", $00 + +Commands: + .byte "B+UP ![ A+UP !+", $01 + .byte "B+DOWN !] A+DOWN !-", $01 + .byte "B+LEFT !< A+LEFT !=", $01 + .byte "B+RIGHT !> A+RIGHT !?", $01 + .byte $01 + .byte "SELECT !BACKSPACE", $01 + .byte "START !EXECUTE", $00 + +Input: + .byte "INPUT - ", $01 + .byte $01 + .byte "UP !FLIP BIT", $01 + .byte "LEFT !CURSOR LEFT", $01 + .byte "RIGHT !CURSOR RIGHT", $01 + .byte $01 + .byte "START !SUBMIT", $00 + +MainNametable: + .incbin "graphics/nametable.nam" ;nametable data, must be 1024 bytes long + +PaletteData: + .incbin "graphics/palette.pal" ;palette data, must be 36 bytes long + + .org $fffa + +nescallback: + .dw MAINLOOP ;(NMI_Routine) + .dw RESET ;(Reset_Routine) + .dw 0 ;(IRQ_Routine) + + .base $0000 + .incbin "graphics/tiles.chr" ;must be 8192 bytes long