variable support cleanup

This commit is contained in:
FeralChild64 committed 2020-07-19 08:54:53 +02:00
1 parent 51aec45c8d
commit 09f85cea8a
64 files changed
+266 -87

No files matched your search

@@ -0,0 +1,283 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
assign_variable:
// Fetch variable name, should be followed by assign operator
jsr fetch_variable
#if HAS_OPCODES_65CE02
bcs_16 do_SYNTAX_error
jsr injest_assign
bcs_16 do_SYNTAX_error
#else
bcs !+
jsr injest_assign
bcc !++
!:
jmp do_SYNTAX_error
!:
#endif
// Determine variable type and push it to the stack
lda #$00
clc
bit VARNAM+0
bpl !+
adc #$01
!:
bit VARNAM+1
bpl !+
adc #$80
!:
pha
// Push the VARPNT to the stack - it might get overridden
lda VARPNT+0
pha
lda VARPNT+1
pha
// Evaluate the expression
jsr FRMEVL
// Restore VARPNT
pla
sta VARPNT+1
pla
sta VARPNT+0
// Determine what to assign
pla
bmi assign_string
beq assign_float
// FALLTROUGH
// XXX integer and float probably have much in common
assign_integer:
lda VALTYP
bmi_16 do_TYPE_MISMATCH_error
#if CONFIG_MEMORY_MODEL_60K
// XXX
// XXX: implement this
// XXX
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
// XXX consider optimized version without multiple JSRs
// XXX
// XXX: implement this
// XXX
#else // CONFIG_MEMORY_MODEL_38K
// XXX
// XXX: implement this
// XXX
#endif
jmp do_NOT_IMPLEMENTED_error
assign_float:
lda VALTYP
bmi_16 do_TYPE_MISMATCH_error
#if CONFIG_MEMORY_MODEL_60K
// XXX
// XXX: implement this
// XXX
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
// XXX consider optimized version without multiple JSRs
// XXX
// XXX: implement this
// XXX
#else // CONFIG_MEMORY_MODEL_38K
// XXX
// XXX: implement this
// XXX
#endif
jmp do_NOT_IMPLEMENTED_error
assign_string:
// Check if value type matches
lda VALTYP
bpl_16 do_TYPE_MISMATCH_error
// Copy the string descriptor to DSCPNT
jsr helper_strdesccpy
// First special case - check if the new string has size 0
lda __FAC1+0
bne assign_string_not_empty
// Yes, it is empty - free the old one
jsr varstr_free
// Set the new variable as empty string
ldy #$00
lda #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<VARPNT
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
sta (VARPNT), y
#endif
rts
assign_string_not_empty:
// Check if the source and destination strings are the same
lda DSCPNT+1
cmp __FAC1+1
bne assign_string_not_same
iny
lda DSCPNT+2
cmp __FAC1+2
bne assign_string_not_same
// If we are here, than both source and destination strings are the same - nothing more to be done
rts
assign_string_not_same:
// Strings are not the same - check if the new one is located within the text area, between TXTTAB and VARTAB
lda VARTAB+1
cmp __FAC1+2
bcc assign_string_not_text_area
bne !+
lda VARTAB+0
cmp __FAC1+1
bcc assign_string_not_text_area
!:
lda __FAC1+2
cmp TXTTAB+1
bcc assign_string_not_text_area
bne !+
lda __FAC1+1
cmp TXTTAB+0
bcc assign_string_not_text_area
!:
// String is located within text area - great, just copy the descriptor
#if CONFIG_MEMORY_MODEL_60K
// .X already contains #<VARPNT
ldy #$00
lda __FAC1+0
jsr poke_under_roms
iny
lda __FAC1+1
jsr poke_under_roms
iny
lda __FAC1+2
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
ldy #$00
lda __FAC1+0
sta (VARPNT), y
iny
lda __FAC1+1
sta (VARPNT), y
iny
lda __FAC1+2
sta (VARPNT), y
#endif
rts
assign_string_not_text_area:
// Check if the new string is a temporary one - if so, reuse alocation
// XXX
// Check if the old string belongs to the string area (is above FRETOP)
jsr helper_cmp_fretop
bcc assign_string_no_optimizations
// FALLTROUGH
assign_string_try_reuse:
// Check if we can reuse old string memory
lda DSCPNT+0
cmp __FAC1+0
// If size of both string equals - simply reuse it
beq_16 helper_strvarcpy
assign_string_try_reuse_unsuccesful:
// No special case optimization is possible - but first get rid of the old string
jsr varstr_free
// FALLTROUGH
assign_string_no_optimizations:
// Allocate memory for the new string
lda __FAC1+0
ldy #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<VARPNT
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
sta (VARPNT), y
#endif
jsr varstr_alloc
// Copy the string and quit
jmp helper_strvarcpy
+320
View File
@@ -0,0 +1,320 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Carry set = failure, not recognized variable name
//
fetch_variable:
// Set default second character and variable type
lda #$00
sta VARNAM+1
// Fetch the first character of variable name
jsr fetch_character_skip_spaces
jsr is_AZ
bcc !+
#if !HAS_OPCODES_65CE02
jsr unconsume_character
#else
dew TXTPTR
#endif
sec
rts
!:
sta VARNAM+0
// Fetch the (optional) second character
jsr fetch_character
jsr is_09_AZ
bcs fetch_variable_check_type
sta VARNAM+1
// Fetch the remaining variable characters - ignore them
!:
jsr fetch_character
jsr is_09_AZ
bcc !-
// FALLTROUGH
fetch_variable_check_type:
// Encode variable type within name, as documented in:
// - https://sites.google.com/site/h2obsession/CBM/basic/variable-format
cmp #$24 // '$'
beq fetch_variable_type_string
cmp #$25 // '%'
beq fetch_variable_type_integer
// FALLTORUGH
fetch_variable_type_float:
#if !HAS_OPCODES_65CE02
jsr unconsume_character
#else
dew TXTPTR
#endif
#if HAS_OPCODES_65C02
bra fetch_variable_special
#else
jmp fetch_variable_special
#endif
fetch_variable_type_integer:
lda VARNAM+0
ora #$80
sta VARNAM+0
lda #$00
skip_2_bytes_trash_nvz
// FALLTROUGH
fetch_variable_type_string:
lda #$80
sta VALTYP
lda VARNAM+1
ora #$80
sta VARNAM+1
// FALLTROUGH
fetch_variable_special:
lda VARNAM+0
cmp #$54 // 'T'
beq !+
cmp #$53 // 'S'
bne fetch_variable_find_addr
// First character is 'S', check the second one
lda VARNAM+1
cmp #$54 // 'T'
bne fetch_variable_find_addr
jmp fetch_variable_ST
!:
// First character is 'T', check the second one
lda VARNAM+1
cmp #$49
beq_16 fetch_variable_TI
cmp #$C9
beq_16 fetch_variable_TI_string
// FALLTROUGH
fetch_variable_find_addr:
// Now try to find variable address between VARTAB and ARYTAB, see
// - https://www.c64-wiki.com/wiki/Memory_(BASIC)
lda VARTAB+0
sta VARPNT+0
lda VARTAB+1
sta VARPNT+1
// FALLTROUGH
fetch_variable_find_addr_loop:
// Check if end of search loop
lda VARPNT+1
cmp ARYTAB+1
bne !+
lda VARPNT+0
cmp ARYTAB+0
beq fetch_variable_alocate
!:
// Compare current variable name with searched one
// XXX consider creating optimized version for 46K/50K memory models
ldy #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<VARPNT
jsr peek_under_roms
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
jsr peek_under_roms_via_VARPNT
#else // CONFIG_MEMORY_MODEL_38K
lda (VARPNT),y
#endif
cmp VARNAM+0
bne fetch_variable_find_addr_next
iny
#if CONFIG_MEMORY_MODEL_60K
jsr peek_under_roms
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
jsr peek_under_roms_via_VARPNT
#else // CONFIG_MEMORY_MODEL_38K
lda (VARPNT),y
#endif
cmp VARNAM+1
bne fetch_variable_find_addr_next
// FALLTROUGH
fetch_variable_adjust_VARPNT:
// Adjust VARPNT to point just after variable name and quit
#if !HAS_OPCODES_65CE02
clc
lda VARPNT+0
adc #$02
sta VARPNT+0
bcc !+
inc VARPNT+1
!:
#else // HAS_OPCODES_65CE02
inw VARPNT
inw VARPNT
#endif
clc
rts
fetch_variable_find_addr_next:
// Increase VARPNT by 7 - constant value, each variable uses 7 bytes, see:
// - https://sites.google.com/site/h2obsession/CBM/basic/variable-format
clc
lda VARPNT+0
adc #$07
sta VARPNT+0
bcc fetch_variable_find_addr_loop
inc VARPNT+1
bne fetch_variable_find_addr_loop // branch always
fetch_variable_alocate:
// First check if we have enough space for a new descriptor (if FRETOP - STREND >= 7)
sec
lda FRETOP+0
sbc STREND+0
pha
lda FRETOP+1
sbc STREND+1
bne !+ // branch if high byte of result > 0
pla
cmp #$07
bcs fetch_variable_alocate_space_OK
jmp do_OUT_OF_MEMORY_error
!:
pla
// FALLTROUGH
fetch_variable_alocate_space_OK:
// If needed, move all the arrays up
#if CONFIG_MEMORY_MODEL_60K
// XXX
// XXX: implement this for arrays
// XXX
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
// XXX consider optimized version without multiple JSRs
// XXX
// XXX: implement this for arrays
// XXX
#else // CONFIG_MEMORY_MODEL_38K
// XXX
// XXX: implement this for arrays
// XXX
#endif
// Adjust ARYTAB and STREND
clc
lda STREND+0
adc #$07
sta STREND+0
bcc !+
inc STREND+1
!:
clc
lda ARYTAB+0
adc #$07
sta ARYTAB+0
bcc !+
inc ARYTAB+1
!:
// VARPNT already points to the start of the variable descriptor
// Fill-in the new variable name, descriptor (it is enough to zero first 2 bytes of content)
ldy #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<VARPNT
lda VARNAM+0
jsr poke_under_roms
iny
lda VARNAM+1
jsr poke_under_roms
iny
lda #$00
jsr poke_under_roms
iny
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
lda VARNAM+0
sta (VARPNT),y
iny
lda VARNAM+1
sta (VARPNT),y
iny
lda #$00
sta (VARPNT),y
iny
sta (VARPNT),y
#endif
// Adjust variable pointer and quit
#if HAS_OPCODES_65C02
bra fetch_variable_adjust_VARPNT
#else
jmp fetch_variable_adjust_VARPNT
#endif
@@ -0,0 +1,10 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
fetch_variable_ST:
// XXX implement this
jmp do_NOT_IMPLEMENTED_error
@@ -0,0 +1,10 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
fetch_variable_TI:
// XXX implement this
jmp do_NOT_IMPLEMENTED_error
@@ -0,0 +1,95 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
// XXX does not fully work
fetch_variable_TI_string:
// Alocate memory for 6-byte temporary string, copy the string descriptor
lda #$06
jsr tmpstr_alloc
jsr helper_strdesccpy
// Fetch the time in a safe way, store it in temporary area
jsr JRDTIM
sty INDEX+0
stx INDEX+1
sta INDEX+2
// Now calculate the digits, starting from the most important one
#if CONFIG_MEMORY_MODEL_60K
ldx #<DSCPNT+1
#endif
ldy #$00
// FALLTROUGH
fetch_variable_TI_string_loop:
// Set the initial digit value ('0')
lda #$30
sta INDEX+3
// Compare the counter with value from table
!:
lda INDEX+2
cmp table_TI_hi, y
bcc fetch_variable_TI_string_got_digit
bne fetch_variable_TI_string_inc_digit
lda INDEX+1
cmp table_TI_mid, y
bcc fetch_variable_TI_string_got_digit
bne fetch_variable_TI_string_inc_digit
lda INDEX+0
cmp table_TI_lo, y
bcc fetch_variable_TI_string_got_digit
fetch_variable_TI_string_inc_digit:
// Increment digit and subtract the table value from the counter
inc INDEX+3
sec
lda INDEX+0
sbc table_TI_lo, y
sta INDEX+0
lda INDEX+1
sbc table_TI_mid, y
sta INDEX+1
lda INDEX+2
sbc table_TI_hi, y
sta INDEX+2
bcs !- // branch always
fetch_variable_TI_string_got_digit:
// Copy digit to the string
lda INDEX+3
#if CONFIG_MEMORY_MODEL_60K
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_50K || CONFIG_MEMORY_MODEL_60K
sta (DSCPNT+1), y
#endif
// Next iteration
iny
cpy #$06
bne fetch_variable_TI_string_loop
// Return success
clc
rts
@@ -0,0 +1,22 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Check string address (DSCPNT+1/+2) vs FRETOP
//
// Output:
// - Zero flag set - this is the first variable of the string area
// - Carry flag set - this string belongs to the string area
helper_cmp_fretop:
lda DSCPNT+2
cmp FRETOP+1
beq !+
rts
!:
lda DSCPNT+1
cmp FRETOP+0
rts
@@ -0,0 +1,31 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Helper routines for FRETOP handling
//
helper_FRETOP_down_A: // .A - bytes to lower FRETOP, uses DSCPNT+0
sta DSCPNT+0
sec
lda FRETOP+0
sbc DSCPNT+0
sta FRETOP+0
bcs !+
dec FRETOP+1
!:
// FALLTROUGH
helper_FRETOP_check: // check if FRETOP > STREND, Carry set if not
lda STREND+1
cmp FRETOP+1
beq !+
rts
!:
lda STREND+0
cmp FRETOP+0
rts
@@ -0,0 +1,19 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Helper routine for FRETOP handling
//
helper_FRETOP_up: // DSCPNT+0 - bytes to increment FRETO
clc
lda FRETOP+0
adc DSCPNT+0
sta FRETOP+0
bcc !+
inc FRETOP+1
!:
rts
@@ -0,0 +1,24 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Helper routine, used by garbage collector and string concatenation
//
#if CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_60K
helper_INDEX_up_A: // .A - bytes to increase TXTPTR, uses DSCPNT+0
sta DSCPNT+0
clc
lda INDEX+0
adc DSCPNT+0
sta INDEX+0
bcc !+
dec INDEX+1
!:
rts
#endif
@@ -0,0 +1,26 @@
// #LAYOUT# STD * #TAKE-HIGH
// #LAYOUT# * BASIC_0 #TAKE-HIGH
// #LAYOUT# * * #IGNORE
// This has to go $E000 or above - it can be used by routines which bank out the main BASIC ROM!
//
// Helper routine, used by garbage collector and string concatenation
//
#if CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
helper_INDEX_up_A: // .A - bytes to increase TXTPTR, uses DSCPNT+0
sta DSCPNT+0
clc
lda INDEX+0
adc DSCPNT+0
sta INDEX+0
bcc !+
dec INDEX+1
!:
rts
#endif
@@ -0,0 +1,53 @@
// #LAYOUT# STD * #TAKE-HIGH
// #LAYOUT# * BASIC_0 #TAKE-HIGH
// #LAYOUT# * * #IGNORE
// This has to go $E000 or above - routines below banks out the main BASIC ROM!
//
// Helper routine for string concatenation
//
#if CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
helper_strconcat:
// Unmap BASIC lower ROM
lda #$26
sta CPU_R6510
// Copy data from the first string
ldy #$00
!:
lda (__FAC2+1),y
sta (INDEX),y
iny
cpy __FAC2+0
bne !-
// Increase INDEX pointer by the size of the 1st string
tya
jsr helper_INDEX_up_A
// Copy data from the second string
ldy #$00
!:
lda (__FAC1+1),y
sta (INDEX),y
iny
cpy __FAC1+0
bne !-
// Restore default memory mapping
lda #$27
sta CPU_R6510
rts
#endif
@@ -0,0 +1,26 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Helper routine to copy string descriptor
//
#if CONFIG_MEMORY_MODEL_38K
helper_strdesccpy:
ldy #$00
lda (VARPNT), y
sta DSCPNT+0
iny
lda (VARPNT), y
sta DSCPNT+1
iny
lda (VARPNT), y
sta DSCPNT+2
rts
#endif
@@ -0,0 +1,40 @@
// #LAYOUT# STD * #TAKE-HIGH
// #LAYOUT# * BASIC_0 #TAKE-HIGH
// #LAYOUT# * * #IGNORE
// This has to go $E000 or above - routines below banks out the main BASIC ROM!
//
// Helper routine to copy string descriptor
//
#if CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
helper_strdesccpy:
// Unmap BASIC lower ROM
lda #$26
sta CPU_R6510
// Retrieve pointer to destination
ldy #$00
lda (VARPNT), y
sta DSCPNT+0
iny
lda (VARPNT), y
sta DSCPNT+1
iny
lda (VARPNT), y
sta DSCPNT+2
// Restore default memory mapping
lda #$27
sta CPU_R6510
rts
#endif
@@ -0,0 +1,28 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Helper routine to copy string descriptor
//
#if CONFIG_MEMORY_MODEL_60K
helper_strdesccpy:
ldx #<VARPNT
ldy #$00
jsr peek_under_roms
sta DSCPNT+0
iny
jsr peek_under_roms
sta DSCPNT+1
iny
jsr peek_under_roms
sta DSCPNT+2
rts
#endif
@@ -0,0 +1,34 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Helper routine to copy string descriptor
//
#if CONFIG_MEMORY_MODEL_38K
helper_strvarcpy:
// Retrieve pointer to destination
ldy #$02
lda (VARPNT), y
sta DSCPNT+2
dey
lda (VARPNT), y
sta DSCPNT+1
dey
// .Y is now 0 - copy the content
!:
lda (__FAC1+1),y
sta (DSCPNT+1),y
iny
cpy __FAC1+0
bne !-
rts
#endif
@@ -0,0 +1,46 @@
// #LAYOUT# STD * #TAKE-HIGH
// #LAYOUT# * BASIC_0 #TAKE-HIGH
// #LAYOUT# * * #IGNORE
// This has to go $E000 or above - routines below banks out the main BASIC ROM!
//
// Helper routine to copy string variable content
//
#if CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
helper_strvarcpy:
// Unmap BASIC lower ROM
lda #$26
sta CPU_R6510
// Retrieve pointer to destination
ldy #$02
lda (VARPNT), y
sta DSCPNT+2
dey
lda (VARPNT), y
sta DSCPNT+1
dey
// Copy the string content
!:
lda (__FAC1+1),y
sta (DSCPNT+1),y
iny
cpy __FAC1+0
bne !-
// Restore default memory mapping
lda #$27
sta CPU_R6510
rts
#endif
@@ -0,0 +1,39 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Helper routine to copy string descriptor
//
#if CONFIG_MEMORY_MODEL_60K
helper_strvarcpy:
ldx #<VARPNT
// Retrieve pointer to destination
ldy #$02
jsr peek_under_roms
sta DSCPNT+2
dey
jsr peek_under_roms
sta DSCPNT+1
dey
// .Y is now 0 - copy the content
!:
ldx #<__FAC1+1
jsr peek_under_roms
ldx #<DSCPNT+1
jsr poke_under_roms
iny
cpy __FAC1+0
bne !-
rts
#endif
@@ -0,0 +1,21 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Helper routine, used by garbage collector
//
helper_TXTPTR_down_A: // .A - bytes to decrease TXTPTR, uses DSCPNT+0
sta DSCPNT+0
sec
lda TXTPTR+0
sbc DSCPNT+0
sta TXTPTR+0
bcs !+
dec TXTPTR+1
!:
rts
+70
View File
@@ -0,0 +1,70 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Allocates memory for a temporary string
//
// Input:
// -.A desired string length, has to be > 0
//
// Output:
// - VARPNT points to the newly alocated descriptor
//
tmpstr_alloc:
tax
// First we need to find a free temporary string descriptor
lda TEMPPT
cmp #$22
bcs tmpstr_alloc_try_reuse
tay
sta LASTPT
clc
adc #$03
sta TEMPPT
// FALLTROUGH
tmpstr_alloc_found:
// Store desired string length
stx $00, y
// Store descriptor address
sty VARPNT+0
lda #$00
sta VARPNT+1
// Jump to alocation routine
jmp varstr_alloc
tmpstr_alloc_try_reuse:
// It seems there is no space left - try to find empty entry
ldy #$19
// FALLTROUGH
tmpstr_alloc_try_reuse_loop:
lda $00, y
beq tmpstr_alloc_found // branch if free entry found
iny
iny
iny
cpy #$22
bne tmpstr_alloc_try_reuse_loop
// Everything failed - give up
jmp do_FORMULA_TOO_COMPLEX_error
+97
View File
@@ -0,0 +1,97 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Frees the string if it belongs to temporary string stack
//
// Input:
// - DSCPNT+1/+2 - pointer to string (depending on the call addres, can fetch from FAC1/FAC2)
//
tmpstr_free_FAC1:
lda __FAC1+1
sta DSCPNT+1
lda __FAC1+2
#if HAS_OPCODES_65C02
bra !+
#else
jmp !+
#endif
tmpstr_free_FAC2:
lda __FAC2+1
sta DSCPNT+1
lda __FAC2+2
!:
sta DSCPNT+2
// FALLTROUGH
tmpstr_free:
// First find the descriptor matching the given string
ldx #$19
//FALLTROUGH
tmpstr_free_loop:
cpx TEMPPT
bne !+
rts // quit if not found
!:
// First fetch the string size - if 0, do not even try to compare
ldy $00, x
beq tmpstr_free_next_3
// Check if current string
inx
lda $00, x
cmp DSCPNT+1
bne tmpstr_free_next_2
inx
lda $00, x
cmp DSCPNT+2
bne tmpstr_free_next_1
// This is the string we need to free
sty DSCPNT+0
phx_trash_a
jsr varstr_free
plx_trash_a
// Mark empty string as free and quit
dex
dex
lda #$00
sta $00, x
rts
tmpstr_free_next_3:
inx
// FALLTROUGH
tmpstr_free_next_2:
inx
// FALLTROUGH
tmpstr_free_next_1:
inx
bne tmpstr_free_loop
@@ -0,0 +1,20 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
tmpstr_free_all:
// Go through all the temporary descriptors and free everything
ldx #$19
// XXX implement this
//FALLTROUGH
tmpstr_free_all_loop:
// XXX finish, deduplicate part with CLR
rts
+145
View File
@@ -0,0 +1,145 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Allocates memory for string - lowers FRETOP (but not below STREND)
//
// Note: We are not going to use the C64-style memory management here, as it implies
// a very slow garbage collector. Instead, we are using a scheme similar to used in
// BASIC V3.5 and later, with 'back-pointers' after each string, telling about the
// location of the string descriptor.
//
// See:
// - https://www.c64-wiki.com/wiki/Memory_(BASIC)
// - https://sites.google.com/site/h2obsession/CBM/basic/variable-format
//
// Input:
// - VARPNT - pointer to string descriptor, first pointed byte should contain desired length
// Output:
// - fills-in pointer in the string descriptor
//
// NOTE: if FAC1 or FAC2 contains a value (not a string descriptor - these are going to be updated
// if garbage collector starts), then preserve __FAC1+1/+2 and __FAC2+1/+2 somewhere, as there
// is a small chance they'll get damaged!
varstr_alloc:
// Mark garbage collector as not run yet
lda #$00
// FALLTROUGH
varstr_alloc_retry:
sta GARBFL // Computes Mapping the Commodore 64, page 10
// Backup FRETOP in case of alocation problems
lda FRETOP+0
sta INDEX+0
lda FRETOP+1
sta INDEX+1
// Lower FRETOP to make space for the back-pointer
#if !HAS_OPCODES_65CE02
lda #$02
jsr helper_FRETOP_down_A
#else // HAS_OPCODES_65CE02 - this time code is slightly longer, but faster
dew FRETOP
dew FRETOP
jsr helper_FRETOP_check
#endif
bcs varstr_alloc_fail
// Create the back-pointer to the string descriptor
ldy #$00
lda VARPNT+0
#if CONFIG_MEMORY_MODEL_60K
ldx #<FRETOP
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
sta (FRETOP), y
#endif
iny
lda VARPNT+1
#if CONFIG_MEMORY_MODEL_60K
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
sta (FRETOP), y
#endif
// Now lower FRETOP again to make space for the string content
ldy #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<VARPNT
jsr peek_under_roms
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
jsr peek_under_roms_via_VARPNT
#else // CONFIG_MEMORY_MODEL_38K
lda (VARPNT),y
#endif
jsr helper_FRETOP_down_A
bcs varstr_alloc_fail
// Success - fill in the string descriptor
ldy #$01
#if CONFIG_MEMORY_MODEL_60K
ldx #<VARPNT
lda FRETOP+0
jsr poke_under_roms
iny
lda FRETOP+1
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
lda FRETOP+0
sta (VARPNT), y
iny
lda FRETOP+1
sta (VARPNT), y
#endif
// The end
rts
varstr_alloc_fail:
// Unable to alocate memory - restore FRETOP value
lda INDEX+0
sta FRETOP+0
lda INDEX+1
sta FRETOP+1
// Check if it is worth to perform garbage collection and retry
bit GARBFL
bpl_16 do_OUT_OF_MEMORY_error
dec GARBFL // $00 -> $FF
jsr varstr_garbage_collect
#if HAS_OPCODES_65C02
bra varstr_alloc_retry
#else
jmp varstr_alloc_retry
#endif
+102
View File
@@ -0,0 +1,102 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
//
// Releases back memory used by string
//
// Input:
// - DSCPNT+0 - string size
// - DSCPNT+1, DSCPNT+2 - pointer to string
//
varstr_free:
// Check the string size - do not do anything if 0
lda DSCPNT+0
bne !+
rts
!:
// Check if string is above FRETOP (in string area)
// XXX move this check out of the routine
jsr helper_cmp_fretop
bcs !+
// No, it does not belong to string area - quit
rts
!:
// Check if this is the lowest string
bne varstr_free_inside
// This is the lowest string - so just increase FRETOP, this way
// the garbage collector will not be needed that quickly
jsr helper_FRETOP_up // free the string data
#if !HAS_OPCODES_65CE02
lda #$02 // free the back-pointer
sta DSCPNT+0
jmp helper_FRETOP_up
#else // HAS_OPCODES_65CE02
inw FRETOP // free the back-pointer
inw FRETOP
rts
#endif
varstr_free_inside:
// This is not the lowest string; mark it as free
// First preserve the string size, it will make it easier for the garbage collector
// Increase DSCPNT+1/+2 to point to the back-pointer minus 1
dec DSCPNT+0
clc
lda DSCPNT+1
adc DSCPNT+0
sta DSCPNT+1
bcc !+
inc DSCPNT+2
!:
// Put the size-1, garbage collector will make use of this value
lda DSCPNT+0
ldy #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<DSCPNT+1
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
sta (DSCPNT+1), y
#endif
// Now fill-in the back-pointer with 0's
tya
iny
#if CONFIG_MEMORY_MODEL_60K
jsr poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
sta (DSCPNT+1), y
#endif
iny
#if CONFIG_MEMORY_MODEL_60K
jmp poke_under_roms
#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
sta (DSCPNT+1), y
rts
#endif
@@ -0,0 +1,352 @@
// #LAYOUT# STD * #TAKE
// #LAYOUT# * BASIC_0 #TAKE
// #LAYOUT# * * #IGNORE
varstr_garbage_collect:
// The routine utilizes the 'shift_mem_up' - which leaves INDEX+0 and INDEX+1
// free to be used, but INDEX+2 and beyond would be overwritten
// We will reuse TXTPTR as the moving pointer, so store it on the stack
lda TXTPTR+0
pha
lda TXTPTR+1
pha
// Similarly, we will reuse OLDTXT as the pointer to string descriptor
lda OLDTXT+0
pha
lda OLDTXT+1
pha
// INDEX+0 and INDEX+1 will be our cumulative shift counter
lda #$00
sta INDEX+0
sta INDEX+1
// Start from the highest string and go downwards
lda MEMSIZ+0
sta TXTPTR+0
lda MEMSIZ+1
sta TXTPTR+1
varstr_garbage_collect_loop:
// Check if this was the last (lowest) string (if FRETOP == TXTPTR)
lda FRETOP+1
cmp TXTPTR+1
bne varstr_garbage_collect_check_bptr
lda FRETOP+0
cmp TXTPTR+0
bne varstr_garbage_collect_check_bptr
// End of strings - adapt FRETOP
clc
lda INDEX+0
adc FRETOP+0
sta FRETOP+0
lda INDEX+1
adc FRETOP+1
sta FRETOP+1
// FALLTROUGH
varstr_garbage_collect_end:
// Restore OLDTXT and TXTPTR, and quit
pla
sta OLDTXT+1
pla
sta OLDTXT+0
pla
sta TXTPTR+1
pla
sta TXTPTR+0
rts
varstr_garbage_collect_check_bptr:
// Decrement TXTPTR by 2, so that it points to the back-pointer
#if !HAS_OPCODES_65CE02
lda #$02
jsr helper_TXTPTR_down_A
#else // HAS_OPCODES_65CE02
dew TXTPTR
dew TXTPTR
#endif
// Copy the back-pointer to OLDTXT, check if it is NULL
ldy #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<TXTPTR
jsr peek_under_roms
sta OLDTXT+0
iny
jsr peek_under_roms
sta OLDTXT+1
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
// XXX consider optimized version without multiple JSRs
jsr peek_under_roms_via_TXTPTR
sta OLDTXT+0
iny
jsr peek_under_roms_via_TXTPTR
sta OLDTXT+1
#else // CONFIG_MEMORY_MODEL_38K
lda (TXTPTR),y
sta OLDTXT+0
iny
lda (TXTPTR),y
sta OLDTXT+1
#endif
ora OLDTXT+0
beq_16 varstr_garbage_collect_unused
// The back-pointer is not NULL - string is used
// Setup 'memmove__size' - number of bytes to copy
ldy #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<OLDTXT
jsr peek_under_roms
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
jsr peek_under_roms_via_OLDTXT
#else // CONFIG_MEMORY_MODEL_38K
lda (OLDTXT),y
#endif
sta memmove__size+0
lda #$00
sta memmove__size+1
// The 'memmove__size' contains string length - add back-pointer size
#if !HAS_OPCODES_65CE02
clc
lda memmove__size+0
adc #$02
sta memmove__size+0
bcc !+
inc memmove__size+1
!:
#else // HAS_OPCODES_65CE02
inw memmove__size
inw memmove__size
#endif
// Setup 'memmove__src' - last byte of source
iny
#if CONFIG_MEMORY_MODEL_60K
jsr peek_under_roms
sta memmove__src+0
iny
jsr peek_under_roms
sta memmove__src+1
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
// XXX consider optimized version without multiple JSRs
jsr peek_under_roms_via_OLDTXT
sta memmove__src+0
iny
jsr peek_under_roms_via_OLDTXT
sta memmove__src+1
#else // CONFIG_MEMORY_MODEL_38K
lda (OLDTXT),y
sta memmove__src+0
iny
lda (OLDTXT),y
sta memmove__src+1
#endif
// The 'memmove__src' contains the first byte of source - add 'memmove__size' to it
clc
lda memmove__src+0
adc memmove__size+0
sta memmove__src+0
lda memmove__src+1
adc memmove__size+1
sta memmove__src+1
// Shift 'memmove__src' to point last byte of source
#if !HAS_OPCODES_65CE02
sec
lda memmove__src+0
sbc #$01
sta memmove__src+0
bcs !+
dec memmove__src+1
!:
#else // HAS_OPCODES_65CE02
dew memmove__src
#endif
// Setup 'memmove__dst' - last byte of destination
clc
lda memmove__src+0
adc INDEX+0
sta memmove__dst+0
lda memmove__src+1
adc INDEX+1
sta memmove__dst+1
// Increase the string descriptor pointer by INDEX, so that it will point to the new position
ldy #$01
#if CONFIG_MEMORY_MODEL_60K
ldx #<OLDTXT
jsr peek_under_roms
clc
adc INDEX+0
php
jsr poke_under_roms
iny
jsr peek_under_roms
plp
adc INDEX+1
jsr poke_under_roms
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
// XXX consider optimized version without multiple JSRs
jsr peek_under_roms_via_OLDTXT
clc
adc INDEX+0
sta (OLDTXT),y
iny
php
jsr peek_under_roms_via_OLDTXT
plp
adc INDEX+1
sta (OLDTXT),y
#else // CONFIG_MEMORY_MODEL_38K
lda (OLDTXT),y
clc
adc INDEX+0
sta (OLDTXT),y
iny
lda (OLDTXT),y
adc INDEX+1
sta (OLDTXT),y
#endif
// Move string memory up
jsr shift_mem_up
// Update TXTPTR
ldy #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<OLDTXT
jsr peek_under_roms
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
jsr peek_under_roms_via_OLDTXT
#else // CONFIG_MEMORY_MODEL_38K
lda (OLDTXT),y
#endif
jsr helper_TXTPTR_down_A
// Check if variable descriptor in __FAC1 matches the previously moved string;
// if so - adapt it too
lda __FAC1+2
cmp TXTPTR+1
bne !+
lda __FAC1+1
cmp TXTPTR+0
bne !+
clc
adc INDEX+0
sta __FAC1+1
lda __FAC1+2
adc INDEX+1
sta __FAC1+2
!:
// Same for __FAC2
lda __FAC2+2
cmp TXTPTR+1
bne !+
lda __FAC2+1
cmp TXTPTR+0
bne !+
clc
adc INDEX+0
sta __FAC2+1
lda __FAC2+2
adc INDEX+1
sta __FAC2+2
!:
// Next iteration
jmp varstr_garbage_collect_loop
varstr_garbage_collect_unused:
// The back-pointer is NULL - string is a garbage to collect
// Fetch the string length
#if !HAS_OPCODES_65CE02
lda #$01
jsr helper_TXTPTR_down_A
#else // HAS_OPCODES_65CE02
dew TXTPTR
#endif
ldy #$00
#if CONFIG_MEMORY_MODEL_60K
ldx #<TXTPTR
jsr peek_under_roms
#elif CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K
jsr peek_under_roms_via_TXTPTR
#else // CONFIG_MEMORY_MODEL_38K
lda (TXTPTR),y
#endif
tax
// Increase INDEX (shift offset) by string length + back-pointer length
lda #$03
jsr helper_INDEX_up_A
txa
jsr helper_INDEX_up_A
// Decrease TXTPTR by unused string length-1
txa
jsr helper_TXTPTR_down_A
// Next iteration
jmp varstr_garbage_collect_loop