commit 8e20553b524d8ef0ce45bdff3af44303d61ce139
Author: John Bintz <27256+johnbintz@users.noreply.github.com>
Date: Wed Jul 14 21:48:06 2021 -0400
initial commit
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a7b3028
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright © 2021 John Bintz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e90fe9e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,20 @@
+# NES Password ROM
+
+An NES ROM that shows how the Space character worked in Metroid passwords.
+It was also an opportunity to learn 6502 assembler and the basics of
+NES development! Visit
+[r1kr.blog/read/nestextpasswordrom](https://r1kr.blog/read/nestextpasswordrom)
+to learn more!
+
+## Building
+
+* Install [dasm](https://dasm-assembler.github.io/)
+* Run `dasm password.asm -f3 -opassword.nes`
+* Run `password.nes` in your favorite emulator.
+
+## Credits
+
+* 8bitworkshop: https://8bitworkshop.com/
+* galois16: https://github.com/bbbradsmith/prng_6502/blob/master/galois16.s
+* Calderon Handwriting Condensed: https://www.romhacking.net/fonts/32/
+
diff --git a/calderon_with_bamboo.chr b/calderon_with_bamboo.chr
new file mode 100644
index 0000000..765a694
Binary files /dev/null and b/calderon_with_bamboo.chr differ
diff --git a/data.asm b/data.asm
new file mode 100644
index 0000000..1040b2a
--- /dev/null
+++ b/data.asm
@@ -0,0 +1,70 @@
+Letters
+ byte "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?! "
+Passwords
+ byte "D N E "
+ byte "! ! ! "
+ byte "T R O "
+ byte "H R O "
+
+ byte "JUSTIN"
+ byte "BAILEY"
+ byte "!a!b!c"
+ byte "!d!e!f"
+
+ byte "6eW3!!"
+ byte "!!!!00"
+ byte "F38W!H"
+ byte "C0042N"
+
+ byte "llv!?l"
+ byte "z!!mU4"
+ byte "!!y000"
+ byte "00m03x"
+
+ byte "DRAGON"
+ byte "BALL Z"
+ byte "Dragon"
+ byte "Ball z"
+
+CanAlsoMessage
+ byte " might also be written as "
+ byte 0
+
+AButtonMessage
+ byte "A - Change password"
+ byte 0
+
+BButtonMessage
+ byte "B - Randomize alternative"
+ byte 0
+
+R1KRTitleOne:
+ byte "The Rabbit with"
+ byte 0
+R1KRTitleTwo:
+ byte "1000 Repos"
+ byte 0
+
+R1KRBlogTitle:
+ byte "NES Text Passwords"
+ byte 0
+
+R1KRBlogLinkOne:
+ byte "r1kr.blog/read/"
+ byte 0
+
+R1KRBlogLinkTwo:
+ byte "nestextpasswordrom"
+ byte 0
+
+Palette:
+ hex ff ;screen color
+ hex 0d262400 ;background 0
+ hex 0d262100 ;background 1
+ hex 0d262a00 ;background 2
+ hex 00122200 ;background 3
+ hex 19293900 ;sprite 0
+ hex 1a2a3a00 ;sprite 1
+ hex 1b2b3b00 ;sprite 2
+ hex 1c2c3c ;sprite 3
+
diff --git a/galois16.asm b/galois16.asm
new file mode 100644
index 0000000..7b49fed
--- /dev/null
+++ b/galois16.asm
@@ -0,0 +1,37 @@
+;
+; 6502 LFSR PRNG - 16-bit
+; Brad Smith, 2019
+; http://rainwarrior.ca
+;
+
+; A 16-bit Galois LFSR
+
+; Possible feedback values that generate a full 65535 step sequence:
+; $2D = %00101101
+; $39 = %00111001
+; $3F = %00111111
+; $53 = %01010011
+; $BD = %10111101
+; $D7 = %11010111
+
+; $39 is chosen for its compact bit pattern
+
+; simplest version iterates the LFSR 8 times to generate 8 random bits
+; 133-141 cycles per call
+; 19 bytes
+
+galois16: subroutine
+ ldy #8
+ lda Seed+0
+.one
+ asl ; shift the register
+ rol Seed+1
+ bcc .two
+ eor #$39 ; apply XOR feedback whenever a 1 bit is shifted out
+.two
+ dey
+ bne .one
+ sta Seed+0
+ cmp #0 ; reload flags
+ rts
+
diff --git a/nesdefs.dasm b/nesdefs.dasm
new file mode 100644
index 0000000..554d5a8
--- /dev/null
+++ b/nesdefs.dasm
@@ -0,0 +1,184 @@
+
+ processor 6502
+
+;;;;; CONSTANTS
+
+PPU_CTRL = $2000
+PPU_MASK = $2001
+PPU_STATUS = $2002
+OAM_ADDR = $2003
+OAM_DATA = $2004
+PPU_SCROLL = $2005
+PPU_ADDR = $2006
+PPU_DATA = $2007
+
+PPU_OAM_DMA = $4014
+DMC_FREQ = $4010
+APU_STATUS = $4015
+APU_NOISE_VOL = $400C
+APU_NOISE_FREQ = $400E
+APU_NOISE_TIMER = $400F
+APU_DMC_CTRL = $4010
+APU_CHAN_CTRL = $4015
+APU_FRAME = $4017
+
+JOYPAD1 = $4016
+JOYPAD2 = $4017
+
+; NOTE: I've put this outside of the PPU & APU, because it is a feature
+; of the APU that is primarily of use to the PPU.
+OAM_DMA = $4014
+; OAM local RAM copy goes from $0200-$02FF:
+OAM_RAM = $0200
+
+; PPU_CTRL flags
+CTRL_NMI = %10000000 ; Execute Non-Maskable Interrupt on VBlank
+CTRL_8x8 = %00000000 ; Use 8x8 Sprites
+CTRL_8x16 = %00100000 ; Use 8x16 Sprites
+CTRL_BG_0000 = %00000000 ; Background Pattern Table at $0000 in VRAM
+CTRL_BG_1000 = %00010000 ; Background Pattern Table at $1000 in VRAM
+CTRL_SPR_0000 = %00000000 ; Sprite Pattern Table at $0000 in VRAM
+CTRL_SPR_1000 = %00001000 ; Sprite Pattern Table at $1000 in VRAM
+CTRL_INC_1 = %00000000 ; Increment PPU Address by 1 (Horizontal rendering)
+CTRL_INC_32 = %00000100 ; Increment PPU Address by 32 (Vertical rendering)
+CTRL_NT_2000 = %00000000 ; Name Table Address at $2000
+CTRL_NT_2400 = %00000001 ; Name Table Address at $2400
+CTRL_NT_2800 = %00000010 ; Name Table Address at $2800
+CTRL_NT_2C00 = %00000011 ; Name Table Address at $2C00
+
+; PPU_MASK flags
+MASK_TINT_RED = %00100000 ; Red Background
+MASK_TINT_BLUE = %01000000 ; Blue Background
+MASK_TINT_GREEN = %10000000 ; Green Background
+MASK_SPR = %00010000 ; Sprites Visible
+MASK_BG = %00001000 ; Backgrounds Visible
+MASK_SPR_CLIP = %00000100 ; Sprites clipped on left column
+MASK_BG_CLIP = %00000010 ; Background clipped on left column
+MASK_COLOR = %00000000 ; Display in Color
+MASK_MONO = %00000001 ; Display in Monochrome
+
+; read flags
+F_BLANK = %10000000 ; VBlank Active
+F_SPRITE0 = %01000000 ; VBlank hit Sprite 0
+F_SCAN8 = %00100000 ; More than 8 sprites on current scanline
+F_WIGNORE = %00010000 ; VRAM Writes currently ignored.
+
+
+;;;;; CARTRIDGE FILE HEADER
+
+NES_MIRR_HORIZ = 0
+NES_MIRR_VERT = 1
+NES_MIRR_QUAD = 8
+
+ MAC NES_HEADER
+ seg Header
+ org $7ff0
+.NES_MAPPER SET {1} ;mapper number
+.NES_PRG_BANKS SET {2} ;number of 16K PRG banks, change to 2 for NROM256
+.NES_CHR_BANKS SET {3} ;number of 8K CHR banks (0 = RAM)
+.NES_MIRRORING SET {4} ;0 horizontal, 1 vertical, 8 four screen
+ byte $4e,$45,$53,$1a ; header
+ byte .NES_PRG_BANKS
+ byte .NES_CHR_BANKS
+ byte .NES_MIRRORING|(.NES_MAPPER<<4)
+ byte .NES_MAPPER&$f0
+ byte 0,0,0,0,0,0,0,0 ; reserved, set to zero
+ seg Code
+ org $8000
+ ENDM
+
+;;;;; NES_INIT SETUP MACRO (place at start)
+
+ MAC NES_INIT
+ sei ;disable IRQs
+ cld ;decimal mode not supported
+ ldx #$ff
+ txs ;set up stack pointer
+ inx ;increment X to 0
+ stx PPU_MASK ;disable rendering
+ stx DMC_FREQ ;disable DMC interrupts
+ stx PPU_CTRL ;disable NMI interrupts
+ bit PPU_STATUS ;clear VBL flag
+ bit APU_CHAN_CTRL ;ack DMC IRQ bit 7
+ lda #$40
+ sta APU_FRAME ;disable APU Frame IRQ
+ lda #$0F
+ sta APU_CHAN_CTRL ;disable DMC, enable/init other channels.
+ ENDM
+
+;;;;; NES_VECTORS - CPU vectors at end of address space
+
+ MAC NES_VECTORS
+ seg Vectors ; segment "Vectors"
+ org $fffa ; start at address $fffa
+ .word NMIHandler ; $fffa vblank nmi
+ .word Start ; $fffc reset
+ .word NMIHandler ; $fffe irq / brk
+ ENDM
+
+;;;;; PPU_SETADDR
- set 16-bit PPU address
+
+ MAC PPU_SETADDR
+ lda #>{1} ; upper byte
+ sta PPU_ADDR
+ lda #<{1} ; lower byte
+ sta PPU_ADDR
+ ENDM
+
+;;;;; PPU_SETVALUE - feed 8-bit value to PPU
+
+ MAC PPU_SETVALUE
+ lda #{1}
+ sta PPU_DATA
+ ENDM
+
+;;;;; SAVE_REGS - save A/X/Y registers
+
+ MAC SAVE_REGS
+ pha
+ txa
+ pha
+ tya
+ pha
+ ENDM
+
+;;;;; RESTORE_REGS - restore Y/X/A registers
+
+ MAC RESTORE_REGS
+ pla
+ tay
+ pla
+ tax
+ pla
+ ENDM
+
+;-------------------------------------------------------------------------------
+; SLEEP clockcycles
+; Original author: Thomas Jentzsch
+; Inserts code which takes the specified number of cycles to execute. This is
+; useful for code where precise timing is required.
+; LEGAL OPCODE VERSION MAY AFFECT FLAGS (uses 'bit' opcode)
+
+NO_ILLEGAL_OPCODES = 1
+
+ MAC SLEEP ;usage: SLEEP n (n>1)
+.CYCLES SET {1}
+
+ IF .CYCLES < 2
+ ECHO "MACRO ERROR: 'SLEEP': Duration must be > 1"
+ ERR
+ ENDIF
+
+ IF .CYCLES & 1
+ IFNCONST NO_ILLEGAL_OPCODES
+ nop 0
+ ELSE
+ bit $00
+ ENDIF
+.CYCLES SET .CYCLES - 3
+ ENDIF
+
+ REPEAT .CYCLES / 2
+ nop
+ REPEND
+ ENDM
diff --git a/nesppu.dasm b/nesppu.dasm
new file mode 100644
index 0000000..a570dc7
--- /dev/null
+++ b/nesppu.dasm
@@ -0,0 +1,64 @@
+
+;;;;; SUBROUTINES
+
+ClearRAM: subroutine
+ lda #0 ; A = 0
+ tax ; X = 0
+.clearRAM
+ sta $0,x ; clear $0-$ff
+ cpx #$fe ; last 2 bytes of stack?
+ bcs .skipStack ; don't clear it
+ sta $100,x ; clear $100-$1fd
+.skipStack
+ sta $200,x ; clear $200-$2ff
+ sta $300,x ; clear $300-$3ff
+ sta $400,x ; clear $400-$4ff
+ sta $500,x ; clear $500-$5ff
+ sta $600,x ; clear $600-$6ff
+ sta $700,x ; clear $700-$7ff
+ inx ; X = X + 1
+ bne .clearRAM ; loop 256 times
+ rts
+
+; wait for VSYNC to start
+WaitSync:
+ bit PPU_STATUS
+ bpl WaitSync
+ rts
+
+;;;;; RANDOM NUMBERS
+
+NextRandom subroutine
+ lsr
+ bcc .NoEor
+ eor #$d4
+.NoEor:
+ rts
+; Get previous random value
+PrevRandom subroutine
+ asl
+ bcc .NoEor
+ eor #$a9
+.NoEor:
+ rts
+
+;;;;; CONTROLLER READING
+
+ReadJoypad0 subroutine
+ ldy #0
+ReadJoypadY
+ lda #$01
+ sta JOYPAD1,y ; set strobe bit
+ lsr ; now A is 0
+ sta JOYPAD1,y ; clear strobe bit
+ ldx #8 ; read 8 bits
+.loop:
+ pha ; save A (result)
+ lda JOYPAD1,y ; load controller state
+ lsr ; bit 0 -> carry
+ pla ; restore A (result)
+ rol ; carry -> bit 0 of result
+ dex ; X = X - 1
+ bne .loop ; repeat if X is 0
+ rts ; controller bits returned in A
+
diff --git a/password.asm b/password.asm
new file mode 100644
index 0000000..61b0a5f
--- /dev/null
+++ b/password.asm
@@ -0,0 +1,265 @@
+PASSWORD_LENGTH = 24 ; length for comparing to Y
+SPACE_VALUE = 64
+SPACE_LOCATION = 64
+SPACE_BECOME_VALUE = SPACE_LOCATION - 1
+PASSWORD_COUNT = 5
+
+ include "nesdefs.dasm"
+
+;;;;; VARIABLES
+
+ seg.u ZEROPAGE
+ org $0
+
+;; global
+
+Seed ds 2
+PasswordInUse ds 1
+
+;; controller
+
+CurrentController ds 1
+BButtonDown ds 1
+AButtonDown ds 1
+
+;; decoding
+
+CurrentLetter ds 1
+CurrentPasswordChunk ds 1
+RandomValueForPasswordLetterModification ds 1
+DecodedPasswordOffset ds 1
+CurrentLetterPositionProcess ds 1
+CurrentLetterPositionDisplay ds 1
+
+;; rendering
+
+BambooRenderPos ds 1
+StringToWrite ds 2
+
+ seg.u PASSWORDS
+ org $400
+
+;; password storage
+
+DecodedPassword ds 24
+AlternatePassword ds 24
+
+
+;;;;; NES CARTRIDGE HEADER
+
+ NES_HEADER 0,2,1,NES_MIRR_HORIZ ; mapper 0, 2 PRGs, 1 CHR
+
+;;;;; START OF CODE
+
+Start:
+; wait for PPU warmup; clear CPU RAM
+ NES_INIT ; set up stack pointer, turn off PPU
+ jsr WaitSync ; wait for VSYNC
+ jsr ClearRAM ; clear RAM
+ jsr WaitSync ; wait for VSYNC (and PPU warmup)
+; set palette and nametable VRAM
+ jsr SetPalette ; set palette colors
+ lda #0
+ sta PasswordInUse
+
+; reset PPU address and scroll registers
+ lda #0
+ sta PPU_ADDR
+ sta PPU_ADDR ; PPU addr = $0000
+ sta PPU_SCROLL
+ sta PPU_SCROLL ; PPU scroll = $0000
+
+; seed rng
+ lda #25
+ sta Seed
+ lda #43
+ sta Seed+1
+
+; disable NMI and scroll
+ lda #0 ; disable NMI
+ sta PPU_CTRL
+
+ lda #$0
+ sta PPU_SCROLL
+ sta PPU_SCROLL ; PPU scroll = $0000
+
+; set up controller
+ lda #0
+ sta AButtonDown
+ sta BButtonDown
+
+ jsr DecodePassword
+ jsr ConvertDecodedPasswordToAlternatePassword
+
+ jsr WriteUI
+
+.endless
+ jsr ReadJoypad0
+ sta CurrentController
+
+ jsr HandleBButton
+ jsr HandleAButton
+
+ inc Seed
+ jmp .endless ; endless loop
+
+
+;;;;; COMMON SUBROUTINES
+
+ include "nesppu.dasm"
+ include "processing.asm"
+ include "view.asm"
+ include "galois16.asm"
+ include "data.asm"
+
+WriteString: subroutine
+ ldy #0
+.Continue:
+ lda (StringToWrite),y
+ beq .Done
+ sta PPU_DATA
+ iny
+ bne .Continue
+.Done
+ rts
+
+ MAC WRITE_STRING
+ lda #<{1}
+ sta StringToWrite
+ lda #>{1}
+ sta StringToWrite+1
+ jsr WriteString
+ ENDM
+
+WriteUI: subroutine
+ lda #0
+ sta PPU_MASK ; turn rendering off
+
+ PPU_SETADDR $2069
+ jsr WritePassword
+
+ PPU_SETADDR $20c1
+ WRITE_STRING CanAlsoMessage
+
+ PPU_SETADDR $2109
+ jsr WriteAlternateDecodedPassword
+
+ PPU_SETADDR $21a6
+ WRITE_STRING AButtonMessage
+
+ PPU_SETADDR $21c3
+ WRITE_STRING BButtonMessage
+
+ PPU_SETADDR $2242
+ jsr DrawBamboo
+
+ PPU_SETADDR $224a
+ WRITE_STRING R1KRTitleOne
+
+ PPU_SETADDR $226d
+ WRITE_STRING R1KRTitleTwo
+
+ PPU_SETADDR $22a9
+ WRITE_STRING R1KRBlogTitle
+
+ PPU_SETADDR $22e9
+ WRITE_STRING R1KRBlogLinkOne
+
+ PPU_SETADDR $230b
+ WRITE_STRING R1KRBlogLinkTwo
+
+;;;;; palette stuff
+
+ PPU_SETADDR $23e8
+
+ lda #%01010101
+ sta PPU_DATA
+ sta PPU_DATA
+
+ PPU_SETADDR $23f0
+
+ lda #%10101010
+ sta PPU_DATA
+ sta PPU_DATA
+
+ lda #MASK_BG
+ sta PPU_MASK ; enable rendering
+ lda #$0
+ sta PPU_ADDR
+ sta PPU_ADDR ; PPU addr = $0000
+
+ rts
+
+HandleBButton: subroutine
+ lda #%01000000 ; lowest bit is b
+ bit CurrentController
+ beq .BButtonUp ; button is up
+.BButtonDown
+ lda #0
+ cmp BButtonDown
+ bne .BButtonWasDown
+
+ jsr ConvertDecodedPasswordToAlternatePassword
+ jsr WriteUI
+
+.BButtonWasDown
+ lda #1
+ sta BButtonDown
+ bne .BButtonDone
+.BButtonUp
+ lda #0
+ sta BButtonDown
+.BButtonDone
+ rts
+
+HandleAButton: subroutine
+ lda #%10000000 ; lowest bit is b
+ bit CurrentController
+ beq .AButtonUp ; button is up
+.AButtonDown
+ lda #0
+ cmp AButtonDown
+ bne .AButtonWasDown
+
+ inc PasswordInUse
+ lda #PASSWORD_COUNT
+ cmp PasswordInUse
+ bne .NotEqual
+
+ lda #0
+ sta PasswordInUse
+.NotEqual
+
+ jsr DecodePassword
+ jsr ConvertDecodedPasswordToAlternatePassword
+ jsr WriteUI
+
+.AButtonWasDown
+ lda #1
+ sta AButtonDown
+ bne .AButtonDone
+.AButtonUp
+ lda #0
+ sta AButtonDown
+.AButtonDone
+ rts
+
+
+;;;;; INTERRUPT HANDLERS
+
+
+NMIHandler: subroutine
+ SAVE_REGS
+
+ RESTORE_REGS
+ rti
+
+;;;;; CPU VECTORS
+
+ NES_VECTORS
+
+ org $10000
+
+
+ incbin "calderon_with_bamboo.chr"
+ incbin "calderon_with_bamboo.chr"
diff --git a/password.nes b/password.nes
new file mode 100644
index 0000000..738fa2e
Binary files /dev/null and b/password.nes differ
diff --git a/processing.asm b/processing.asm
new file mode 100644
index 0000000..bb44c5c
--- /dev/null
+++ b/processing.asm
@@ -0,0 +1,118 @@
+GetCurrentLetterPosition: subroutine
+ lda #0
+ ldx #0
+SelectPassword:
+ cpx PasswordInUse
+ beq SelectPasswordDone
+ adc #24
+ inx
+ bne SelectPassword
+SelectPasswordDone:
+ rts
+
+DecodePassword: subroutine
+ lda #0 ; set up decoding variables
+ sta DecodedPasswordOffset
+ jsr GetCurrentLetterPosition
+ sta CurrentLetterPositionProcess
+ ldx #0
+.ReadLetterLoop
+ ldy CurrentLetterPositionProcess ; load the next letter of the password
+ lda Passwords,y
+ sta CurrentLetter ; ...and stick it in a spot in RAM
+ ldx #0 ;
+ ldy DecodedPasswordOffset
+.FindMatchLoop
+ lda Letters,x ; put the next search letter into accumulator
+ jsr PushLetterToDecodedPassword ; always push the letter offset onto the decode list
+ cmp CurrentLetter ; if it's the same, we're done
+ beq .FindMatchExit
+ inx
+ ;cpx #SPACE_LOCATION
+ bne .FindMatchLoop
+
+ lda Letters,x ; this is a space
+ ldx #SPACE_VALUE
+ jsr PushLetterToDecodedPassword
+.FindMatchExit
+ inc DecodedPasswordOffset
+ iny
+ inc CurrentLetterPositionProcess
+ cpy #PASSWORD_LENGTH
+ bne .ReadLetterLoop
+
+ rts
+
+ConvertDecodedPasswordToAlternatePassword: subroutine
+ ldx #0
+.NextCharacter
+ lda DecodedPassword,x
+ sta AlternatePassword,x
+ cpx #0 ; we can't do anything at position 0
+ beq .NotSpaceValue
+ cmp #SPACE_VALUE ; not a candidate for modifying the prior value
+ beq .SpaceValue
+ cmp #SPACE_BECOME_VALUE ; not a candidate for modifying the prior value
+ beq .CanBecomeSpaceValue
+ bne .NotSpaceValue
+.SpaceValue
+ lda #SPACE_BECOME_VALUE
+ sta AlternatePassword,x
+ jsr galois16
+ tay
+ and #%00000100
+ bne .UseASpace
+ dex
+ lda DecodedPassword,x
+ ora #%00000011
+ sta AlternatePassword,x
+ inx
+ bcs .NotSpaceValue
+.CanBecomeSpaceValue
+ ; check to make sure both low bits are set on prior
+ dex
+ lda AlternatePassword,x
+ and #%00000011
+ bne .DoneBecomingSpace
+ inx
+ jsr galois16
+ tay
+ and #%00000100
+ bne .NotSpaceValue
+ lda #SPACE_VALUE
+ sta AlternatePassword,x
+ dex
+ lda AlternatePassword,x
+ cmp #SPACE_VALUE
+.DoneBecomingSpace
+ inx
+ lda #0
+ beq .NotSpaceValue
+.UseASpace
+ tya
+ and #%00000011
+ sta RandomValueForPasswordLetterModification ; y contains the lower bits to use instead
+ lda #SPACE_LOCATION
+ sta AlternatePassword,x
+ dex
+ lda DecodedPassword,x
+ and #%11111100
+ ora RandomValueForPasswordLetterModification
+ sta AlternatePassword,x
+ inx
+.NotSpaceValue
+ inx
+ cpx #PASSWORD_LENGTH
+ bne .NextCharacter
+.done
+ rts
+
+PushLetterToDecodedPassword: subroutine
+ ; X contains the letters.indexOf(letter)
+ ; Y contains the current letter index
+ ; A will get preserved
+ pha
+ txa
+ sta DecodedPassword,y
+ pla
+ rts
\ No newline at end of file
diff --git a/view.asm b/view.asm
new file mode 100644
index 0000000..d19b1d4
--- /dev/null
+++ b/view.asm
@@ -0,0 +1,115 @@
+ MAC MOVE_TO_NEXT_CHUNK
+ lda #1
+ bit CurrentPasswordChunk
+ bne .NextLine
+ lda #0
+ sta PPU_DATA
+ beq .NextChunk
+.NextLine:
+ txa
+ pha
+ lda #0
+ ldx #19
+.NextLineLoop:
+ sta PPU_DATA
+ dex
+ cpx #0
+ bne .NextLineLoop
+ pla
+ tax
+.NextChunk:
+ inc CurrentPasswordChunk
+ ENDM
+
+WritePassword: subroutine
+ lda #0
+ sta CurrentPasswordChunk
+ jsr GetCurrentLetterPosition
+ tax
+ ldy #0
+.WriteLetterInChunk:
+ tya
+ pha
+ lda Passwords,x
+ sta PPU_DATA
+ pla
+ tay
+ cpy #5
+ beq .ChunkDone
+ inx
+ iny
+ bne .WriteLetterInChunk
+.ChunkDone:
+ MOVE_TO_NEXT_CHUNK
+ lda #4
+ ldy #0
+ inx
+ cmp CurrentPasswordChunk
+ bne .WriteLetterInChunk
+ rts
+
+WriteAlternateDecodedPassword: subroutine
+ lda #0
+ sta CurrentPasswordChunk
+ ldx #0
+ ldy #0
+.WriteLetterInChunk
+ tya
+ pha
+ lda AlternatePassword,x
+ tay
+ lda Letters,y
+ sta PPU_DATA
+ pla
+ tay
+ cpy #5
+ beq .ChunkDone
+ inx
+ iny
+ bne .WriteLetterInChunk
+.ChunkDone
+ MOVE_TO_NEXT_CHUNK
+ lda #4
+ ldy #0
+ inx
+ cmp CurrentPasswordChunk
+ bne .WriteLetterInChunk
+ rts
+
+DrawBamboo: subroutine
+ lda #$80
+ sta BambooRenderPos
+.WriteColumn:
+ lda BambooRenderPos
+ sta PPU_DATA
+ inc BambooRenderPos
+ lda #%00000111
+ and BambooRenderPos
+ bne .WriteColumn
+.MaybeNextRow
+ lda #%11111110
+ and BambooRenderPos
+ beq .Done
+ ldy #8
+ lda #0
+.ToNextRow
+ sta PPU_DATA
+ dey
+ cpy #0
+ bne .ToNextRow
+ bcs .WriteColumn
+.Done
+ rts
+
+; set palette colors
+SetPalette: subroutine
+; set PPU address to palette start
+ PPU_SETADDR $3f00
+ ldy #0
+.loop:
+ lda Palette,y ; lookup byte in ROM
+ sta PPU_DATA ; store byte to PPU data
+ iny ; Y = Y + 1
+ cpy #32 ; is Y equal to 32?
+ bne .loop ; not yet, loop
+ rts ; return to caller
\ No newline at end of file