354 lines
10 KiB
Plaintext
354 lines
10 KiB
Plaintext
;license:MIT
|
|
;(c) 2020 by 4am
|
|
;
|
|
; data storage routines for puzzles
|
|
;
|
|
; Public functions:
|
|
; - InitPuzzleStorage
|
|
; - AddLineToPuzzle
|
|
; - AddTargetWordToPuzzle
|
|
; - CheckForTargetWord
|
|
; - FindLetterInColumn
|
|
; - ScrollPuzzleDown
|
|
; - ScrollPuzzleDownUnconditionally
|
|
; - ScrollPuzzleUp
|
|
; - IsPuzzleComplete
|
|
|
|
|
|
puzzle_logical_width = $0300 ; [0x01 byte ] number of letters per word (4..7)
|
|
puzzle_logical_height = $0301 ; [0x01 byte ] number of rows with letters (1..5)
|
|
puzzle_offsets = $0302 ; [0x08 bytes] how far each column has been scrolled down from top of grid
|
|
|
|
; 9 rows of data for current puzzle
|
|
; each byte is one of
|
|
; - letter with high bit off (unmatched letter)
|
|
; - letter with high bit on (matched letter, displayed in grey)
|
|
; - 0x00 (unused space)
|
|
; if letters per word is less than 7, extra bytes at end of each row are guaranteed to be 0x00
|
|
puzzle_data0 = $030A ; [0x08 bytes] character data
|
|
puzzle_data1 = $0312 ; [0x08 bytes]
|
|
puzzle_data2 = $031A ; [0x08 bytes]
|
|
puzzle_data3 = $0322 ; [0x08 bytes]
|
|
puzzle_data4 = $032A ; [0x08 bytes]
|
|
puzzle_data5 = $0332 ; [0x08 bytes]
|
|
puzzle_data6 = $033A ; [0x08 bytes]
|
|
puzzle_data7 = $0342 ; [0x08 bytes]
|
|
puzzle_data8 = $034A ; [0x08 bytes]
|
|
target_word_count = $0352 ; [0x01 byte ] number of records in <target_words>
|
|
target_words = $0353 ; [0x80 bytes]
|
|
; each 8-byte record is a <puzzle_logical_width>-length word, no prefix
|
|
; extra bytes in each record are guaranteed to be 0x00
|
|
; max 16 words per puzzle
|
|
|
|
;------------------------------------------------------------------------------
|
|
; InitPuzzleStorage
|
|
; initialize internal data structures before playing a new puzzle
|
|
;
|
|
; in: A = logical puzzle width (number of letters in each word, 4..7)
|
|
; out: A = 0
|
|
; X = 0
|
|
; Y preserved
|
|
; flags clobbered
|
|
;------------------------------------------------------------------------------
|
|
InitPuzzleStorage
|
|
sta puzzle_logical_width
|
|
ldx #$CF
|
|
lda #0
|
|
- sta puzzle_logical_width, x
|
|
dex
|
|
bne -
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; AddLineToPuzzle
|
|
; add one puzzle line while constructing a puzzle
|
|
;
|
|
; in: ($FE) points to <puzzle_logical_width>-length character buffer
|
|
; (no prefix, no suffix)
|
|
; out: all registers & flags clobbered
|
|
;------------------------------------------------------------------------------
|
|
AddLineToPuzzle
|
|
lda puzzle_logical_height
|
|
asl
|
|
asl
|
|
asl
|
|
tax
|
|
ldy #0
|
|
- lda ($FE), y
|
|
cmp #$20
|
|
bne +
|
|
lda #0
|
|
+ sta puzzle_data0, x
|
|
inx
|
|
iny
|
|
cpy puzzle_logical_width
|
|
bne -
|
|
inc puzzle_logical_height
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; AddTargetWordToPuzzle
|
|
; add one target word while constructing a puzzle
|
|
;
|
|
; in: ($FE) points to <puzzle_logical_width> length character buffer
|
|
; (no prefix, no suffix)
|
|
; out: all registers & flags clobbered
|
|
;------------------------------------------------------------------------------
|
|
AddTargetWordToPuzzle
|
|
lda target_word_count
|
|
asl
|
|
asl
|
|
asl
|
|
tax
|
|
ldy #0
|
|
- lda ($FE), y
|
|
sta target_words, x
|
|
inx
|
|
iny
|
|
cpy puzzle_logical_width
|
|
bne -
|
|
inc target_word_count
|
|
rts
|
|
|
|
unused_counter=$FD
|
|
;------------------------------------------------------------------------------
|
|
; CheckForTargetWord
|
|
; check if there is a target word in row 4 (between the lines)
|
|
;
|
|
; in: none
|
|
; out: C = 0 if a target word was found on row 4
|
|
; C = 1 if no target word found
|
|
; note: will still return C=1 if target word was found but all letters were
|
|
; already used (i.e. word had already been found before but player
|
|
; shifted letters out and back)
|
|
; all other registers & flags clobbered
|
|
;------------------------------------------------------------------------------
|
|
CheckForTargetWord
|
|
+LDADDR target_words
|
|
+ST16 $FE
|
|
ldx #0 ; word index
|
|
@checkword
|
|
ldy #0 ; character index
|
|
- lda puzzle_data4, y
|
|
and #$7F ; letters within puzzle have high bit set if they've been used before
|
|
cmp ($FE), y ; (which is fine, and on later puzzles is guaranteed to happen eventually)
|
|
bne @nextword
|
|
iny
|
|
cpy puzzle_logical_width
|
|
bne -
|
|
; found matching word
|
|
; set high bit on all letters in row 4
|
|
ldy #0
|
|
sty unused_counter ; to see if all letters were already used (i.e. this word was already found)
|
|
- lda puzzle_data4, y
|
|
bmi +
|
|
inc unused_counter
|
|
ora #$80
|
|
sta puzzle_data4, y
|
|
+ iny
|
|
cpy puzzle_logical_width
|
|
bne -
|
|
lda unused_counter
|
|
beq @notfound
|
|
clc
|
|
rts
|
|
@nextword
|
|
lda $FE
|
|
clc
|
|
adc #8
|
|
sta $FE
|
|
bcc +
|
|
inc $FF
|
|
+ inx
|
|
cpx target_word_count
|
|
bne @checkword
|
|
@notfound
|
|
sec
|
|
rts
|
|
|
|
lettertofind = $FD
|
|
;------------------------------------------------------------------------------
|
|
; FindLetterInColumn
|
|
; check whether a given column contains a given letter
|
|
; (used to support scroll-column-by-typing)
|
|
;
|
|
; in: A = letter (0x41..0x5A)
|
|
; Y = logical column to search
|
|
; out: C clear if letter was found in given column, and
|
|
; A = #$FF if letter was found below center column
|
|
; A = #$01 if letter was found above center column
|
|
; A = #$00 if letter was found in center column
|
|
; C set if letter was not found
|
|
; clobbers A/X
|
|
; preserves Y
|
|
; clobbers $FD,$FE,$FF
|
|
;------------------------------------------------------------------------------
|
|
FindLetterInColumn
|
|
ldx #$01
|
|
- cmp puzzle_data0, y
|
|
beq @down
|
|
cmp puzzle_data1, y
|
|
beq @down
|
|
cmp puzzle_data2, y
|
|
beq @down
|
|
cmp puzzle_data3, y
|
|
beq @down
|
|
cmp puzzle_data4, y
|
|
beq @even
|
|
cmp puzzle_data5, y
|
|
beq @up
|
|
cmp puzzle_data6, y
|
|
beq @up
|
|
cmp puzzle_data7, y
|
|
beq @up
|
|
cmp puzzle_data8, y
|
|
beq @up
|
|
ora #$80 ; check for used letters now
|
|
dex
|
|
beq - ; will only branch back once
|
|
sec ; no matches on used or unused letters, so we're done
|
|
rts
|
|
@down clc
|
|
lda #$01
|
|
rts
|
|
@up clc
|
|
lda #$FF
|
|
rts
|
|
@even clc
|
|
lda #$00
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; ScrollPuzzleDown
|
|
; rearrange internal data structures to 'scroll' a column downwards if possible
|
|
; (does not update screen)
|
|
;
|
|
; in: Y = logical column to scroll
|
|
; out: C clear if puzzle was scrolled down
|
|
; C set if puzzle was already as far down as it can go
|
|
; preserves X/Y
|
|
;------------------------------------------------------------------------------
|
|
ScrollPuzzleDown
|
|
lda puzzle_data3, y
|
|
beq +
|
|
; /!\ execution falls through to ScrollPuzzleDownUnconditionally
|
|
|
|
;------------------------------------------------------------------------------
|
|
; ScrollPuzzleDownUnconditionally
|
|
; rearrange internal data structures to 'scroll' a column downwards
|
|
; (does not update screen)
|
|
;
|
|
; in: Y = logical column to scroll
|
|
; out: C = 0
|
|
; preserves X/Y
|
|
;------------------------------------------------------------------------------
|
|
ScrollPuzzleDownUnconditionally
|
|
lda puzzle_data7, y
|
|
sta puzzle_data8, y
|
|
|
|
lda puzzle_data6, y
|
|
sta puzzle_data7, y
|
|
|
|
lda puzzle_data5, y
|
|
sta puzzle_data6, y
|
|
|
|
lda puzzle_data4, y
|
|
sta puzzle_data5, y
|
|
|
|
lda puzzle_data3, y
|
|
sta puzzle_data4, y
|
|
|
|
lda puzzle_data2, y
|
|
sta puzzle_data3, y
|
|
|
|
lda puzzle_data1, y
|
|
sta puzzle_data2, y
|
|
|
|
lda puzzle_data0, y
|
|
sta puzzle_data1, y
|
|
|
|
lda #0
|
|
sta puzzle_data0, y
|
|
|
|
lda puzzle_offsets, y
|
|
clc
|
|
adc #1
|
|
sta puzzle_offsets, y
|
|
clc
|
|
rts
|
|
+ sec
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; ScrollPuzzleUp
|
|
; rearrange internal data structures to 'scroll' a column upwards if possible
|
|
; (does not update screen)
|
|
;
|
|
; in: Y = logical column to scroll
|
|
; out: C clear if puzzle was scrolled up
|
|
; C set if puzzle was already as far up as it can go
|
|
; preserves X/Y
|
|
;------------------------------------------------------------------------------
|
|
ScrollPuzzleUp
|
|
lda puzzle_data5, y
|
|
beq +
|
|
|
|
lda puzzle_data1, y
|
|
sta puzzle_data0, y
|
|
|
|
lda puzzle_data2, y
|
|
sta puzzle_data1, y
|
|
|
|
lda puzzle_data3, y
|
|
sta puzzle_data2, y
|
|
|
|
lda puzzle_data4, y
|
|
sta puzzle_data3, y
|
|
|
|
lda puzzle_data5, y
|
|
sta puzzle_data4, y
|
|
|
|
lda puzzle_data6, y
|
|
sta puzzle_data5, y
|
|
|
|
lda puzzle_data7, y
|
|
sta puzzle_data6, y
|
|
|
|
lda puzzle_data8, y
|
|
sta puzzle_data7, y
|
|
|
|
lda #0
|
|
sta puzzle_data8, y
|
|
|
|
lda puzzle_offsets, y
|
|
sec
|
|
sbc #1
|
|
sta puzzle_offsets, y
|
|
clc
|
|
rts
|
|
+ sec
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; IsPuzzleComplete
|
|
; check if all letters in puzzle have been used
|
|
;
|
|
; in: none
|
|
; out: C = 0 if puzzle is complete (all letters have been matched)
|
|
; C = 1 if puzzle is not yet complete
|
|
; A,X clobbered
|
|
; Y preserved
|
|
;------------------------------------------------------------------------------
|
|
IsPuzzleComplete
|
|
ldx #$47
|
|
- lda puzzle_data0, x
|
|
beq @keepChecking
|
|
bmi @keepChecking
|
|
sec
|
|
rts
|
|
@keepChecking
|
|
dex
|
|
bpl -
|
|
clc
|
|
rts
|