new text compression and tokeniser - faster, about 100 bytes saved
This commit is contained in:
1 parent
51e45e9983
commit
94170f156c
24 files changed
+1848
-872
No files matched your search
@@ -3,6 +3,7 @@
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
packed_keywords:
|
||||
|
||||
put_packed_keywords()
|
||||
packed_as_1n:
|
||||
|
||||
put_packed_as_1n()
|
||||
@@ -3,6 +3,7 @@
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
packed_message_chars:
|
||||
|
||||
put_packed_chars()
|
||||
packed_as_3n:
|
||||
|
||||
put_packed_as_3n()
|
||||
@@ -3,6 +3,7 @@
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
packed_message_tokens:
|
||||
|
||||
put_packed_tokens()
|
||||
packed_str_errors:
|
||||
|
||||
put_freq_packed_errors()
|
||||
@@ -0,0 +1,9 @@
|
||||
// #LAYOUT# STD * #TAKE
|
||||
// #LAYOUT# M65 BASIC_1 #TAKE
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
|
||||
packed_str_keywords_V2:
|
||||
|
||||
put_freq_packed_keywords_V2()
|
||||
@@ -3,6 +3,7 @@
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
packed_message_words:
|
||||
|
||||
put_packed_words()
|
||||
packed_str_misc:
|
||||
|
||||
put_freq_packed_misc()
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
|
||||
cmd_stop:
|
||||
ldx #30 // "BREAK"
|
||||
jsr print_packed_message
|
||||
ldx #IDX__EV2_1E // "BREAK"
|
||||
jsr print_packed_error
|
||||
|
||||
// Are we in direct mode
|
||||
lda CURLIN+1
|
||||
@@ -13,8 +13,8 @@ cmd_stop:
|
||||
beq !+
|
||||
|
||||
// Not direct mode
|
||||
ldx #31
|
||||
jsr print_packed_message
|
||||
ldx #IDX__STR_IN
|
||||
jsr print_packed_misc_str
|
||||
|
||||
lda CURLIN+1
|
||||
ldx CURLIN+0
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
basic_main_loop:
|
||||
|
||||
// Tell user we are ready
|
||||
ldx #30
|
||||
jsr print_packed_message
|
||||
ldx #IDX__STR_READY
|
||||
jsr print_packed_misc_str
|
||||
|
||||
// Enable Kernal messages
|
||||
lda #$80
|
||||
|
||||
@@ -11,13 +11,15 @@
|
||||
|
||||
// Label definitions
|
||||
|
||||
.label VB1__INITMSG = $4000 + 2 * 0
|
||||
.label VB1__LINKPRG = $4000 + 2 * 1
|
||||
.label VB1__tokenise_line = $4000 + 2 * 2
|
||||
.label VB1__list_single_line = $4000 + 2 * 3
|
||||
.label VB1__print_packed_message = $4000 + 2 * 4
|
||||
.label VB1__basic_do_clr = $4000 + 2 * 5
|
||||
.label VB1__basic_do_new = $4000 + 2 * 6
|
||||
.label VB1__INITMSG = $4000 + 2 * 0
|
||||
.label VB1__LINKPRG = $4000 + 2 * 1
|
||||
.label VB1__tokenise_line = $4000 + 2 * 2
|
||||
.label VB1__list_single_line = $4000 + 2 * 3
|
||||
.label VB1__print_packed_error = $4000 + 2 * 4
|
||||
.label VB1__print_packed_misc_str = $4000 + 2 * 5
|
||||
.label VB1__print_packed_keyword_V2 = $4000 + 2 * 6
|
||||
.label VB1__basic_do_clr = $4000 + 2 * 7
|
||||
.label VB1__basic_do_new = $4000 + 2 * 8
|
||||
|
||||
#else
|
||||
|
||||
@@ -27,7 +29,9 @@
|
||||
.word LINKPRG
|
||||
.word tokenise_line
|
||||
.word list_single_line
|
||||
.word print_packed_message
|
||||
.word print_packed_error
|
||||
.word print_packed_misc_str
|
||||
.word print_packed_keyword_V2
|
||||
.word basic_do_clr
|
||||
.word basic_do_new
|
||||
|
||||
|
||||
@@ -151,10 +151,10 @@ do_basic_error: // error code in .X
|
||||
|
||||
// Error message text + " ERROR"
|
||||
|
||||
jsr print_packed_message
|
||||
jsr print_packed_error
|
||||
|
||||
ldx #33
|
||||
jsr print_packed_message
|
||||
ldx #IDX__STR_ERROR
|
||||
jsr print_packed_misc_str
|
||||
|
||||
// Check if direct mode
|
||||
|
||||
@@ -164,8 +164,8 @@ do_basic_error: // error code in .X
|
||||
|
||||
// We were in a program, so show IN <line>
|
||||
|
||||
ldx #31
|
||||
jsr print_packed_message
|
||||
ldx #IDX__STR_IN
|
||||
jsr print_packed_misc_str
|
||||
|
||||
lda CURLIN+1
|
||||
ldx CURLIN+0
|
||||
|
||||
@@ -19,8 +19,8 @@ basic_warm_start_real:
|
||||
ora CMP0+1
|
||||
beq !+
|
||||
|
||||
ldx #32
|
||||
jsr print_packed_message
|
||||
ldx #IDX__STR_BRK_AT
|
||||
jsr print_packed_misc_str
|
||||
|
||||
lda CMP0+1
|
||||
jsr print_hex_byte
|
||||
|
||||
@@ -21,5 +21,5 @@ initmsg_bytes_free:
|
||||
jsr print_integer
|
||||
|
||||
// Print rest of the start up message
|
||||
ldx #34
|
||||
jmp print_packed_message
|
||||
ldx #IDX__STR_BYTES
|
||||
jmp print_packed_misc_str
|
||||
@@ -1,260 +0,0 @@
|
||||
// #LAYOUT# STD * #TAKE
|
||||
// #LAYOUT# M65 BASIC_1 #TAKE
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
// Pack a word using the simple compression scheme.
|
||||
// This has to be the same implementation as in compress_text.c
|
||||
// because we compress input lines of BASIC to do the tokenisation.
|
||||
// This allows us to search through the compressed tokens directly
|
||||
// using the compressed fragment of BASIC, and thus work out the
|
||||
// token for a given piece of string.
|
||||
// This also helps ensure that we have an implementation that is totally
|
||||
// different to that of the original C64 BASIC, as well as likely
|
||||
// saving a bit of space in the ROM, especially once we start implementing
|
||||
// larger keyword vocabs for BASIC 7 or BASIC 10 compatibility, since the
|
||||
// compression and decompression machinery is of fixed size.
|
||||
|
||||
pack_word:
|
||||
// Inputs:
|
||||
// X = offset of string to pack. Address is $0200 + X
|
||||
// __tokenise_work1 = number of bytes to pack
|
||||
// __tokenise_work2 = packed length of string
|
||||
|
||||
// Outputs:
|
||||
// Output is written to $0100 (bottom of stack).
|
||||
|
||||
// Memory modified:
|
||||
// __tokenise_work4 = temporary storage
|
||||
// __tokenise_work3 = flag for whether we have a nybl prepared or not.
|
||||
// (This can also be checked on exit, to know whether the last byte
|
||||
// is incomplete, which is important for searching for abbreviated
|
||||
// key words.)
|
||||
|
||||
|
||||
// Make sure we have a sane request
|
||||
lda __tokenise_work1
|
||||
bne !+
|
||||
sec
|
||||
rts
|
||||
!:
|
||||
// Initialise internal variables
|
||||
lda #$00
|
||||
sta __tokenise_work2
|
||||
sta __tokenise_work3
|
||||
|
||||
pack_char_loop:
|
||||
|
||||
// Get the next char
|
||||
lda $0200,x
|
||||
|
||||
// Work out what type of character we have, and thus
|
||||
// how we need to pack it.
|
||||
jsr get_packed_char_index
|
||||
|
||||
cmp #$00
|
||||
bne not_exception_char
|
||||
|
||||
// Char is an exception char.
|
||||
// We need to either finish the previous byte with $xF,
|
||||
// Or if there is no existing nybl, then we need to output
|
||||
// either $FE or $FF, depending on whether this is the last
|
||||
// byte or not
|
||||
|
||||
lda __tokenise_work3
|
||||
bne have_nybl_before_exception_char
|
||||
|
||||
// No nybl exists before the exception char, so prefix with $FE or $FF
|
||||
// based on whether we are at the end of the input string or not
|
||||
inx
|
||||
lda __tokenise_work1
|
||||
cmp #$01
|
||||
beq at_end
|
||||
lda #$FF
|
||||
.byte $2C
|
||||
at_end:
|
||||
lda #$FE
|
||||
dex
|
||||
jsr write_unpacked_char
|
||||
// Fall through
|
||||
|
||||
output_exception_byte:
|
||||
lda $0200,x
|
||||
jsr write_unpacked_char
|
||||
jmp consider_next_char
|
||||
|
||||
have_nybl_before_exception_char:
|
||||
// Or existing byte with #$0F, and clear nybl flag
|
||||
ldy __tokenise_work2
|
||||
lda $0100,y
|
||||
ora #$0f
|
||||
sta $0100,y
|
||||
inc __tokenise_work2
|
||||
|
||||
// FALL THROUGH
|
||||
|
||||
write_literal_and_terminate_if_required:
|
||||
|
||||
// Clear have nybl flag
|
||||
lda #$00
|
||||
sta __tokenise_work3
|
||||
|
||||
lda $0200,x
|
||||
jsr write_unpacked_char
|
||||
|
||||
jmp end_string_if_required
|
||||
|
||||
end_string_if_required:
|
||||
// Check if X points to last char of string to be packed
|
||||
inx
|
||||
lda __tokenise_work1
|
||||
cmp #$01
|
||||
beq !+
|
||||
dex
|
||||
jmp consider_next_char
|
||||
!:
|
||||
dex
|
||||
// Fall through
|
||||
|
||||
end_string:
|
||||
// Write end of string sequence if required
|
||||
lda #$00
|
||||
jsr write_unpacked_char
|
||||
rts
|
||||
|
||||
write_unpacked_char:
|
||||
ldy __tokenise_work2
|
||||
sta $0100,y
|
||||
iny
|
||||
sty __tokenise_work2
|
||||
rts
|
||||
|
||||
not_exception_char:
|
||||
// It wasn't an exception char, so write the symbol out
|
||||
cmp #$10
|
||||
bcs whole_byte_symbol
|
||||
|
||||
// Nybl encoded value: Put in upper half or lower half of a byte?
|
||||
ldy __tokenise_work3
|
||||
bne store_low_nybl
|
||||
|
||||
// Store in high nybl -- so shift it into the hi nybl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
ldy __tokenise_work2
|
||||
sta $0100,y
|
||||
bne stored_nybl // Must be taken, as A cannot be $00
|
||||
|
||||
store_low_nybl:
|
||||
// Low nybl gets added to high nybl already stored in the byte
|
||||
ldy __tokenise_work2
|
||||
ora $0100,y
|
||||
sta $0100,y
|
||||
|
||||
stored_nybl:
|
||||
// Now toggle the nybl flag
|
||||
lda __tokenise_work3
|
||||
eor #$ff
|
||||
sta __tokenise_work3
|
||||
// And advance the offset if we filled this byte up
|
||||
bne consider_next_char // Taken if byte has a nybl free, i.e., don't advance pointer while half byte remains free
|
||||
|
||||
// Count the filled up byte
|
||||
inc __tokenise_work2
|
||||
|
||||
// Add termination byte if required
|
||||
// (This re-writes the same byte again, which we don't care about)
|
||||
jmp end_string_if_required
|
||||
|
||||
whole_byte_symbol:
|
||||
// It's a $Fx symbol.
|
||||
// But if we have a nybl to flush, then we handle it like
|
||||
// a full exception character
|
||||
|
||||
ldy __tokenise_work3
|
||||
beq nothing_to_flush
|
||||
|
||||
// Set low nybl to $F to mark next byte as literal
|
||||
ldy __tokenise_work2
|
||||
lda $0100,y
|
||||
ora #$0f
|
||||
sta $0100,y
|
||||
inc __tokenise_work2
|
||||
|
||||
jmp write_literal_and_terminate_if_required
|
||||
|
||||
nothing_to_flush:
|
||||
// Write the symbol as it is, or the literal
|
||||
// if it has been passed to us via fall-through
|
||||
jsr write_unpacked_char
|
||||
|
||||
jmp end_string_if_required
|
||||
|
||||
consider_next_char:
|
||||
// Pack next char
|
||||
inx
|
||||
dec __tokenise_work1
|
||||
beq !+
|
||||
jmp pack_char_loop
|
||||
!:
|
||||
// Add 1 to length if nybl waiting to be flushed
|
||||
// (but leave flag set so the caller knows if the
|
||||
// bottom nybl might be different if the string were
|
||||
// longer).
|
||||
lda __tokenise_work3
|
||||
beq !+
|
||||
inc __tokenise_work2
|
||||
rts
|
||||
!:
|
||||
// Last byte was full, so we need at $00 on the end
|
||||
// (unless a $FE token was written 2 bytes ago)
|
||||
ldy __tokenise_work2
|
||||
iny
|
||||
lda #$00
|
||||
sta $0100,y
|
||||
inc __tokenise_work2
|
||||
|
||||
rts
|
||||
|
||||
get_packed_char_index:
|
||||
// Find char in A in the packed char list, and return
|
||||
// $1 - $E, $F1 - $FD if the charactar is abbreviable,
|
||||
// or else return $00 if the character has to be stored literally.
|
||||
|
||||
// Y register and A are modified, as are processor flags.
|
||||
|
||||
ldy #$00
|
||||
find_char_loop:
|
||||
cmp packed_message_chars,y
|
||||
bne not_a_match
|
||||
enumerate:
|
||||
// Is it a nybl abbreviable char?
|
||||
cpy #14
|
||||
bcs not_nybl_char
|
||||
// Yes, so add 1 and return it
|
||||
iny
|
||||
tya
|
||||
rts
|
||||
|
||||
not_nybl_char:
|
||||
cpy #27
|
||||
bcs not_a_match
|
||||
// It's a $Fx character.
|
||||
// Since the lowest index is $E, and it should be encoded as $F1
|
||||
// we need to add $E3
|
||||
tya
|
||||
clc
|
||||
adc #$E3
|
||||
rts
|
||||
|
||||
not_a_match:
|
||||
iny
|
||||
// Loop until we find it, or we discover it isn't
|
||||
// a character with a special encoding
|
||||
cpy #27
|
||||
bne find_char_loop
|
||||
// Not a char that we can pack, so return 0
|
||||
lda #$00
|
||||
rts
|
||||
@@ -86,22 +86,16 @@ list_not_quote:
|
||||
pha
|
||||
phy_trash_a
|
||||
|
||||
// Get pointer to compressed keyword list
|
||||
lda #<packed_keywords
|
||||
sta FRESPC+0
|
||||
lda #>packed_keywords
|
||||
sta FRESPC+1
|
||||
|
||||
// Subtract $80 from token to get offset in word
|
||||
// list
|
||||
|
||||
// Subtract $80 from token to get offset in token list
|
||||
txa
|
||||
and #$7f
|
||||
tax
|
||||
|
||||
// Now ask for it to be printed
|
||||
ldy #$ff
|
||||
jsr packed_word_search
|
||||
jsr print_packed_keyword_V2
|
||||
|
||||
// Restore registers
|
||||
ply_trash_a
|
||||
pla
|
||||
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
// #LAYOUT# STD * #TAKE
|
||||
// #LAYOUT# M65 * #TAKE
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
// This routine prints messages that have been packed
|
||||
// using the make_error_tables program.
|
||||
// The general idea is to save space in the ROM.
|
||||
|
||||
// The output of make_error_tables is written to packed_messages.s, and
|
||||
// contains the following:
|
||||
//
|
||||
// packed_message_words - List of nybl packed words.
|
||||
// packed_message_tokens - List of tokens that get joined to make messages
|
||||
// packed_message_chars - List of characters in messages sorted by frequency.
|
||||
|
||||
|
||||
print_packed_message:
|
||||
// .X = message number
|
||||
|
||||
#if (ROM_LAYOUT_M65 && SEGMENT_BASIC_0)
|
||||
|
||||
jsr map_BASIC_1
|
||||
jsr_ind VB1__print_packed_message
|
||||
jmp map_NORMAL
|
||||
|
||||
#else
|
||||
|
||||
lda #$ff
|
||||
ldy #$ff
|
||||
packed_message_search:
|
||||
iny
|
||||
cpx #$00
|
||||
beq found_message_in_token_stream
|
||||
lda packed_message_tokens,y
|
||||
cmp #$ff
|
||||
bne !+
|
||||
dex
|
||||
!:
|
||||
cpy #$ff
|
||||
bne packed_message_search
|
||||
// Could not find message
|
||||
sec
|
||||
rts
|
||||
|
||||
found_message_in_token_stream: // XXX size-optimize this
|
||||
// Now print each word in the message
|
||||
phy_trash_a
|
||||
lda packed_message_tokens,y
|
||||
cmp #$FF
|
||||
beq found_message_in_token_stream_done
|
||||
|
||||
jsr print_packed_word
|
||||
ply_trash_a
|
||||
iny
|
||||
beq found_message_in_token_stream_done
|
||||
!:
|
||||
phy_trash_a
|
||||
lda packed_message_tokens,y
|
||||
cmp #$FF
|
||||
beq found_message_in_token_stream_done
|
||||
|
||||
pha
|
||||
jsr print_space
|
||||
pla
|
||||
|
||||
jsr print_packed_word
|
||||
ply_trash_a
|
||||
iny
|
||||
bne !-
|
||||
|
||||
found_message_in_token_stream_done:
|
||||
pla
|
||||
rts
|
||||
|
||||
print_packed_word:
|
||||
// A = the word to print
|
||||
tax
|
||||
|
||||
// Setup pointer to packed words
|
||||
lda #<packed_message_words
|
||||
sta FRESPC+0
|
||||
lda #>packed_message_words
|
||||
sta FRESPC+1
|
||||
|
||||
// Find the word in the list
|
||||
ldy #$ff
|
||||
packed_word_search:
|
||||
iny
|
||||
cpx #$00
|
||||
beq found_packed_word
|
||||
|
||||
// $x0 is an end of work marker
|
||||
lda (FRESPC),y
|
||||
and #$0f
|
||||
cmp #$00
|
||||
bne !+
|
||||
dex
|
||||
!:
|
||||
// $FE $xx is an end of work marker
|
||||
lda (FRESPC),y
|
||||
cmp #$fe
|
||||
bne !+
|
||||
dex
|
||||
iny // skip next byte
|
||||
!:
|
||||
cmp #$ff
|
||||
bne !+
|
||||
iny // skip next byte
|
||||
!:
|
||||
|
||||
// Advance to next page if required
|
||||
cpy #$ff
|
||||
bne packed_word_search
|
||||
inc FRESPC+1
|
||||
jmp packed_word_search
|
||||
|
||||
found_packed_word:
|
||||
|
||||
// Get pointer to start of packed word
|
||||
// Make sure we do not wrap on a page boundary
|
||||
tya
|
||||
clc
|
||||
adc FRESPC+0
|
||||
sta FRESPC+0
|
||||
lda FRESPC+1
|
||||
adc #$00
|
||||
sta FRESPC+1
|
||||
|
||||
print_packed_string:
|
||||
ldy #$ff
|
||||
|
||||
next_packed_word_char:
|
||||
// .Y = offset into packed word data - 1
|
||||
iny
|
||||
|
||||
// Check for end of word
|
||||
lda (FRESPC),y
|
||||
cmp #$00
|
||||
beq end_of_packed_word
|
||||
cmp #$fe
|
||||
beq last_literal
|
||||
cmp #$ff
|
||||
beq next_is_literal_char
|
||||
and #$f0
|
||||
beq end_of_packed_word
|
||||
|
||||
// Check if it is an uncommon char
|
||||
lda (FRESPC),y
|
||||
and #$f0
|
||||
cmp #$f0
|
||||
beq is_uncommon_char
|
||||
|
||||
// Is not an uncommon char, so get char of upper nybl
|
||||
lda (FRESPC),y
|
||||
|
||||
// Put high nybl into low bits of X
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
tax
|
||||
|
||||
// X=1-14 = first 14 chars, so subtract one
|
||||
lda packed_message_chars-1,x
|
||||
jsr JCHROUT // preserves .Y
|
||||
|
||||
// See if there is a char in the low nybl to print
|
||||
lda (FRESPC),y
|
||||
and #$0f
|
||||
beq end_of_packed_word
|
||||
cmp #$0f
|
||||
beq no_lo_nybl_char
|
||||
|
||||
has_lo_nybl_char:
|
||||
// We need to print the low nybl char as well
|
||||
tax
|
||||
phy_trash_a
|
||||
|
||||
// Skip lower nybls of $F or $0 in nybl packed
|
||||
// bytes, as these do not encode characters
|
||||
cpx #$f
|
||||
beq !+
|
||||
has_nybl:
|
||||
cpx #0
|
||||
beq !+
|
||||
|
||||
// X=1-14 = first 14 chars, so subtract one
|
||||
lda packed_message_chars-1,x
|
||||
jsr JCHROUT
|
||||
!:
|
||||
ply_trash_a
|
||||
// FALLTHROUGH
|
||||
|
||||
no_lo_nybl_char:
|
||||
|
||||
// If current byte has $0F in low nybl, then the next
|
||||
// byte is a whole byte.
|
||||
lda (FRESPC),y
|
||||
and #$0f
|
||||
cmp #$0f
|
||||
beq next_is_literal_char
|
||||
|
||||
ready_for_next_char:
|
||||
cpy #$ff
|
||||
bne next_packed_word_char
|
||||
end_of_packed_word:
|
||||
// Hit end of packed data
|
||||
rts
|
||||
|
||||
last_literal:
|
||||
jsr unpack_literal_char
|
||||
rts
|
||||
|
||||
next_is_literal_char:
|
||||
jsr unpack_literal_char
|
||||
jmp ready_for_next_char
|
||||
|
||||
unpack_literal_char:
|
||||
// Output the literal char, and ready for next char
|
||||
iny
|
||||
phy_trash_a
|
||||
lda (FRESPC),y
|
||||
jsr JCHROUT
|
||||
pla
|
||||
tay
|
||||
rts
|
||||
|
||||
is_uncommon_char:
|
||||
// Lower nybl has offset into low-frequency part of the table
|
||||
|
||||
lda (FRESPC),y
|
||||
and #$0f
|
||||
clc
|
||||
adc #14
|
||||
|
||||
// We need to print the low nybl char as well
|
||||
tax
|
||||
phy_trash_a
|
||||
|
||||
jmp has_nybl
|
||||
|
||||
|
||||
#endif // ROM layout
|
||||
@@ -0,0 +1,182 @@
|
||||
// #LAYOUT# STD * #TAKE
|
||||
// #LAYOUT# M65 * #TAKE
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
// This routine prints strings that have been packed using the 'generate_trings' tool.
|
||||
// The general idea is to save space in the ROM.
|
||||
|
||||
|
||||
#if (ROM_LAYOUT_M65 && SEGMENT_BASIC_0)
|
||||
|
||||
print_packed_error:
|
||||
|
||||
jsr map_BASIC_1
|
||||
jsr_ind VB1__print_packed_error
|
||||
jmp map_NORMAL
|
||||
|
||||
print_packed_misc_str:
|
||||
|
||||
jsr map_BASIC_1
|
||||
jsr_ind VB1__print_packed_misc_str
|
||||
jmp map_NORMAL
|
||||
|
||||
print_packed_keyword_V2:
|
||||
|
||||
jsr map_BASIC_1
|
||||
jsr_ind VB1__print_packed_keyword_V2
|
||||
jmp map_NORMAL
|
||||
|
||||
#else
|
||||
|
||||
print_packed_error: // .X - error string index
|
||||
|
||||
lda #<packed_str_errors
|
||||
ldy #>packed_str_errors
|
||||
bne print_freq_packed_string // branch always
|
||||
|
||||
print_packed_misc_str: // .X - misc string index
|
||||
|
||||
lda #<packed_str_misc
|
||||
ldy #>packed_str_misc
|
||||
bne print_freq_packed_string // branch always
|
||||
|
||||
print_packed_keyword_V2: // .X - token number
|
||||
|
||||
lda #<packed_str_keywords_V2
|
||||
ldy #>packed_str_keywords_V2
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
print_freq_packed_string: // not to be used directly XXX rename to print_packed_string
|
||||
|
||||
// First store the initial address of the packed strings list
|
||||
|
||||
sta FRESPC+0
|
||||
sty FRESPC+1
|
||||
|
||||
// Now find the address of the string to print out
|
||||
// The implementation assumes no packed string is longer than 255 bytes, including trailing zero
|
||||
!:
|
||||
cpx #$00
|
||||
beq print_freq_packed_string_found // branch if no need to advance anymore
|
||||
dex
|
||||
|
||||
ldy #$00
|
||||
!:
|
||||
lda (FRESPC), y
|
||||
iny
|
||||
cmp #$00
|
||||
bne !- // branch if not the end of packed string
|
||||
|
||||
clc // advance FREESPC pointer
|
||||
tya
|
||||
adc FRESPC+0
|
||||
sta FRESPC+0
|
||||
lda #$00
|
||||
adc FRESPC+1
|
||||
sta FRESPC+1
|
||||
|
||||
jmp !--
|
||||
|
||||
print_freq_packed_string_found:
|
||||
|
||||
// At this point FRESPC contains a pointer to the string to display
|
||||
// and we should start from the lower nibble
|
||||
|
||||
ldy #$00
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
print_freq_packed_string_nibble_lo:
|
||||
|
||||
// Get the low nibble and check for the end of packed string
|
||||
|
||||
lda (FRESPC), y
|
||||
and #$0F
|
||||
beq print_freq_packed_string_end
|
||||
|
||||
// Not the end - check if this is a common character, encoded by a single nibble
|
||||
|
||||
cmp #$0F
|
||||
beq print_freq_packed_string_3n_split
|
||||
|
||||
// Character is encoded by a single nibble - fetch it and display
|
||||
|
||||
tax
|
||||
lda packed_as_1n-1, x
|
||||
jsr JCHROUT // preserves .Y
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
print_freq_packed_string_nibble_hi:
|
||||
|
||||
// Get the high nibble and check for the end of packed string
|
||||
|
||||
lda (FRESPC), y
|
||||
and #$F0
|
||||
beq print_freq_packed_string_end
|
||||
|
||||
// Not the end - check if this is a common character, encoded by a single nibble
|
||||
|
||||
cmp #$F0
|
||||
beq print_freq_packed_string_3n_single
|
||||
|
||||
// Character is encoded by a single nibble - fetch it and display
|
||||
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
|
||||
tax
|
||||
lda packed_as_1n-1, x
|
||||
jmp !+ // XXX should be bra on some CPUs
|
||||
|
||||
print_freq_packed_string_3n_single:
|
||||
|
||||
// Character is encoded using 3 nibbles (1st is a mark, already interpreted)
|
||||
|
||||
iny
|
||||
lda (FRESPC), y
|
||||
tax
|
||||
lda packed_as_3n-1, x
|
||||
!:
|
||||
jsr JCHROUT // preserves .Y
|
||||
|
||||
// Advance to next nibble
|
||||
|
||||
iny
|
||||
bne print_freq_packed_string_nibble_lo // branch always
|
||||
|
||||
print_freq_packed_string_3n_split:
|
||||
|
||||
// Character is encoded using 3 nibbles (1st is a mark, already interpreted),
|
||||
// but the 2 remaining nibbles are in two neighbour bytes; use a stack to combine them
|
||||
|
||||
lda (FRESPC), y
|
||||
and #$F0
|
||||
tsx
|
||||
pha
|
||||
|
||||
iny
|
||||
lda (FRESPC), y
|
||||
and #$0F
|
||||
|
||||
clc
|
||||
adc STACK, x
|
||||
|
||||
txs // one cycle faster than PLA
|
||||
|
||||
tax
|
||||
lda packed_as_3n-1, x
|
||||
jsr JCHROUT // preserves .Y
|
||||
|
||||
jmp print_freq_packed_string_nibble_hi
|
||||
|
||||
print_freq_packed_string_end:
|
||||
|
||||
rts
|
||||
|
||||
|
||||
#endif // ROM layout
|
||||
@@ -1,306 +0,0 @@
|
||||
// #LAYOUT# STD * #TAKE
|
||||
// #LAYOUT# M65 * #TAKE
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
// Tokenise a line of BASIC
|
||||
// Stored at $0200
|
||||
// Length in __tokenise_work1
|
||||
|
||||
tokenise_line:
|
||||
|
||||
#if (ROM_LAYOUT_M65 && SEGMENT_BASIC_0)
|
||||
|
||||
jsr map_BASIC_1
|
||||
jsr_ind VB1__tokenise_line
|
||||
jmp map_NORMAL
|
||||
|
||||
#else
|
||||
|
||||
// We need to consider each offset in the string to see if it matches
|
||||
// a token. No BASIC keyword is > 7 bytes, so we need to start with that
|
||||
// length (or remaining length of string) and count our way back down.
|
||||
// This means we need separate start and end points to keep track of.
|
||||
|
||||
// $0110 = offset into string
|
||||
// $0111 = number of bytes to pack
|
||||
// $0112 = offset into tokenised output
|
||||
// $0113 = length of raw string
|
||||
|
||||
lda #$00
|
||||
sta $0110
|
||||
sta $0112
|
||||
sta QTSW
|
||||
|
||||
lda __tokenise_work1
|
||||
sta $0113
|
||||
|
||||
tokenise_char:
|
||||
|
||||
// Check if we have reached end of the line?
|
||||
lda $0110
|
||||
cmp $0113
|
||||
bne !+
|
||||
|
||||
// End of line reached
|
||||
|
||||
// Finished tokenising line
|
||||
// Update __tokenise_work1 and terminate with null
|
||||
ldx $0112
|
||||
lda #$00
|
||||
sta $0200,x
|
||||
stx __tokenise_work1
|
||||
|
||||
// Clear quote mode flag that is also used by KERNAL for screen display
|
||||
lda #$00
|
||||
sta QTSW
|
||||
rts
|
||||
!:
|
||||
// More to do
|
||||
|
||||
// Maximum keyword length
|
||||
lda #7
|
||||
sta $0111
|
||||
|
||||
tokenise_char_loop:
|
||||
// Pack the word from the start (start = X, raw len = __tokenise_work1)
|
||||
// Returns packed length in __tokenise_work2 and writes the packed
|
||||
// word at $0100+
|
||||
|
||||
ldx $0110
|
||||
lda $0200,x
|
||||
cmp #$22
|
||||
beq tk_quote
|
||||
cmp #$DE
|
||||
beq tk_pi
|
||||
cmp #$3F
|
||||
bne tk_not_special
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_question_mark:
|
||||
|
||||
lda #$99
|
||||
skip_2_bytes_trash_nvz
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_pi:
|
||||
|
||||
lda #$FF
|
||||
ldx $0110
|
||||
sta $0200,x
|
||||
|
||||
jmp tk_literal_char
|
||||
|
||||
tk_quote:
|
||||
|
||||
// Quote
|
||||
lda QTSW
|
||||
eor #$ff
|
||||
sta QTSW
|
||||
jmp tk_literal_char
|
||||
|
||||
tk_not_special:
|
||||
// Check for literal characters in various ranges
|
||||
// (This just speeds the tokeniser up, by avoiding the packed token search
|
||||
// for such characters).
|
||||
|
||||
// SPACE and punctuation before +,- and *
|
||||
cmp #$2a
|
||||
bcc tk_literal_char
|
||||
// Don't try to tokenise digits
|
||||
cmp #$30
|
||||
bcc tk_might_be_keyword
|
||||
cmp #$3a
|
||||
bcs tk_might_be_keyword
|
||||
jmp tk_literal_char
|
||||
|
||||
tk_might_be_keyword:
|
||||
// Don't tokenise in quote mode
|
||||
lda QTSW
|
||||
bne tk_literal_char
|
||||
|
||||
// Pack string
|
||||
ldx $0110
|
||||
lda $0111
|
||||
sta __tokenise_work1
|
||||
|
||||
// XXX - We should implement an optimisation where we
|
||||
// trim the last symbol of successively, instead of re-packing the word
|
||||
// again with one less char each time.
|
||||
// XXX - We should limit the word length to the length of interesting chars
|
||||
// that are available.
|
||||
|
||||
jsr pack_word
|
||||
lda __tokenise_work2
|
||||
cmp #$10
|
||||
bcc !+
|
||||
// Packed string too long
|
||||
jmp do_STRING_TOO_LONG_error
|
||||
!:
|
||||
|
||||
// Search for it in keywords
|
||||
jsr keyword_search
|
||||
|
||||
// Did we find a keyword?
|
||||
bcc found_token_in_line
|
||||
|
||||
// Nope, so try shorter
|
||||
dec $0111
|
||||
bne tokenise_char_loop
|
||||
|
||||
tk_literal_char:
|
||||
// Is not a token, so copy to output verbatim
|
||||
ldx $0110
|
||||
ldy $0112
|
||||
lda $0200,x
|
||||
sta $0200,y
|
||||
inc $0112
|
||||
inc $0110
|
||||
|
||||
// Tokenise next char
|
||||
jmp tokenise_char
|
||||
|
||||
found_token_in_line:
|
||||
|
||||
// Got a token, so write it out
|
||||
ldx $0112
|
||||
sta $0200,x
|
||||
inx
|
||||
stx $0112
|
||||
|
||||
// If it is REM, then lock us in quote mode to the end of the line
|
||||
cmp #$8f
|
||||
bne not_rem
|
||||
sta QTSW
|
||||
not_rem:
|
||||
|
||||
// Now skip over the length of the token
|
||||
lda $0110
|
||||
clc
|
||||
adc $0111
|
||||
sta $0110
|
||||
|
||||
// See if there is more of the line left to process
|
||||
jmp tokenise_char
|
||||
|
||||
|
||||
|
||||
|
||||
// Search for word in keyword list
|
||||
// The packed form is canonical, so we can search for the keyword while
|
||||
// it remains compressed. This also saves a little time.
|
||||
// Algorithm is simply to find matching first bytes, and then look for
|
||||
// the rest being a match.
|
||||
|
||||
keyword_search:
|
||||
// Search for the compressed keyword stored at $0100 in the list
|
||||
// of compressed keywords
|
||||
|
||||
// __tokenise_work1 = offset in line of input
|
||||
// __tokenise_work2 = length of token we are matching
|
||||
// __tokenise_work3 = offset in token list
|
||||
// __tokenise_work5 = temporary work space
|
||||
// X and Y registers used as temporary space.
|
||||
// returns C=1 if no matching token, else C=0 and A=token
|
||||
|
||||
lda #$00
|
||||
sta __tokenise_work1
|
||||
sta __tokenise_work3
|
||||
next_kw_offset:
|
||||
// Advance offset in compressed keyword list, to see if a match
|
||||
// BEGINS here
|
||||
|
||||
// Get count of bytes to compare
|
||||
lda __tokenise_work2
|
||||
sta __tokenise_work5
|
||||
|
||||
// Load current position
|
||||
ldy __tokenise_work3
|
||||
ldx __tokenise_work1
|
||||
|
||||
// Advance offset in compressed token list, and stop at the end
|
||||
inc __tokenise_work3
|
||||
lda __tokenise_work3
|
||||
cmp #packed_keyword_table_len
|
||||
bne next_in_match
|
||||
jmp done_searching_for_token
|
||||
next_in_match:
|
||||
lda $0100,x
|
||||
cmp packed_keywords,y
|
||||
bne next_kw_offset
|
||||
// Advance to next chars in source and target
|
||||
inx
|
||||
iny
|
||||
|
||||
dec __tokenise_work5 // Have we compared all bytes yet?
|
||||
|
||||
bne next_in_match
|
||||
|
||||
// Keyword matches!
|
||||
|
||||
// Now make sure we started on a keyword boundary
|
||||
// (we know we finished on one, because of the structure of the compressed
|
||||
// keyword data).
|
||||
// Offset 0
|
||||
// previous byte is $x0
|
||||
// two byte prior is $FE
|
||||
|
||||
// Get offset of first byte of this token
|
||||
ldx __tokenise_work3
|
||||
dex // pointer points one late, so rewind it
|
||||
|
||||
// At start of list?
|
||||
cpx #$00
|
||||
beq word_boundary
|
||||
dex
|
||||
// Previous byte is $x0 end token?
|
||||
lda packed_keywords,x
|
||||
and #$0f
|
||||
beq word_boundary
|
||||
dex
|
||||
// Uber-previouss byte is $FE final literal token?
|
||||
lda packed_keywords,x
|
||||
cmp #$fe
|
||||
beq word_boundary
|
||||
|
||||
// Not a word boundary, so ignore
|
||||
jmp next_kw_offset
|
||||
|
||||
word_boundary:
|
||||
// Found a token whose offset is in __tokenise_work3
|
||||
// Now trace back to the start of the compressed keyword list
|
||||
// to work out what keyword number we are, so that we can return
|
||||
// the token number.
|
||||
|
||||
ldy #$80
|
||||
|
||||
token_count_loop:
|
||||
lda packed_keywords,x
|
||||
cmp #$fe
|
||||
bne !+
|
||||
iny
|
||||
!:
|
||||
and #$0f
|
||||
cmp #$00
|
||||
bne !+
|
||||
iny
|
||||
!:
|
||||
cpx #$00
|
||||
beq !+
|
||||
dex
|
||||
jmp token_count_loop
|
||||
!:
|
||||
|
||||
tya
|
||||
clc
|
||||
rts
|
||||
|
||||
done_searching_for_token:
|
||||
// No token found
|
||||
sec
|
||||
lda #$00
|
||||
rts
|
||||
|
||||
#endif // ROM layout
|
||||
@@ -0,0 +1,228 @@
|
||||
// #LAYOUT# STD * #TAKE
|
||||
// #LAYOUT# M65 BASIC_1 #TAKE
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
//
|
||||
// Packs a keyword candidate using a simple compression scheme,
|
||||
// same as in 'generate_strings.cc'.
|
||||
//
|
||||
// This allows us to search through the compressed tokens directly
|
||||
// using the compressed fragment of BASIC, and thus work out the
|
||||
// token for a given piece of string.
|
||||
//
|
||||
// This also helps ensure that we have an implementation that is totally
|
||||
// different to that of the original C64 BASIC, as well as likely
|
||||
// saving a bit of space in the ROM, especially once we start implementing
|
||||
// larger keyword vocabs for extended BASIC, since the compression and
|
||||
// decompression machinery is of fixed size.
|
||||
//
|
||||
// Input:
|
||||
// - .X = offset of string to pack, address is BUF ($0200) + .X
|
||||
// Output:
|
||||
// - see labels in 'tk_pack'
|
||||
//
|
||||
|
||||
|
||||
tk_pack:
|
||||
|
||||
// Reuse the CPU stack - addresses below $102 are used by 'tokenise_line.s'
|
||||
|
||||
.label tk__len_unpacked = $103 // length of unpacked data; it could replace 'tk__nibble_flag', but at the cost of code size/performance
|
||||
.label tk__shorten_bits = $104 // for quick shortening of packed candidate
|
||||
.label tk__nibble_flag = $105 // $00 = start from new byte, $FF = start from high nibble
|
||||
.label tk__byte_offset = $106 // offset of the current byte (to place new data) in tk__packed
|
||||
|
||||
.label tk__packed = $107 // packed candidate, 13 bytes is enough for worst case - 8 byte keyword
|
||||
|
||||
// Initialize variables
|
||||
|
||||
lda #$00
|
||||
ldy #(tk__packed - tk__len_unpacked + 13) // XXX maybe do not clear that much, just put 0 at the end while compressing
|
||||
!:
|
||||
sta tk__len_unpacked, y
|
||||
dey
|
||||
bpl !-
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_pack_loop:
|
||||
|
||||
// Get the next character
|
||||
|
||||
lda BUF, x
|
||||
|
||||
// If character cannot be a part of a keyword - do not pack anything more
|
||||
|
||||
cmp #$23 // '#' - first sane character for a keyword
|
||||
bcc tk_pack_done // branch if below '#'
|
||||
cmp #$7B // 'Z' + 1
|
||||
bcs tk_pack_done // branch if above 'Z'
|
||||
|
||||
// Keywords should not contain digits, colons, or semicolons
|
||||
|
||||
cmp #$30
|
||||
bcc !+ // branch if below '0'
|
||||
cmp #$3C
|
||||
bcc tk_pack_done // branch if below '<'
|
||||
!:
|
||||
// Try to find a nibble to encode the character
|
||||
|
||||
ldy #$0E
|
||||
!:
|
||||
cmp packed_as_1n-1, y
|
||||
beq tk_pack_1n
|
||||
|
||||
dey
|
||||
bne !-
|
||||
|
||||
// Try to find a 3-nibble sequence to encode the character (1st nibble is always 0xF in this case)
|
||||
|
||||
ldy #TK__PACKED_AS_3N
|
||||
!:
|
||||
cmp packed_as_3n-1, y
|
||||
beq tk_pack_3n
|
||||
|
||||
dey
|
||||
bne !-
|
||||
|
||||
// If it is not on the list, it cannot be a part of token - quit
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_pack_done:
|
||||
|
||||
rts
|
||||
|
||||
tk_pack_1n:
|
||||
|
||||
// .Y contains our nibble; check whether we should start a new encoded byte
|
||||
|
||||
bit tk__nibble_flag
|
||||
bpl tk_pack_1n_new_byte
|
||||
|
||||
// Store in the high nibble of the current byte
|
||||
|
||||
tya
|
||||
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
|
||||
ldy tk__byte_offset
|
||||
ora tk__packed, y
|
||||
sta tk__packed, y
|
||||
|
||||
// Adjust remaining counters / flags
|
||||
|
||||
inc tk__byte_offset
|
||||
inc tk__nibble_flag // $FF -> $00
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_pack_1n_done:
|
||||
|
||||
// Put the bit mark - to indicate 1-nibble encoding
|
||||
|
||||
clc
|
||||
ror tk__shorten_bits
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_pack_loop_next:
|
||||
|
||||
// Prepare for the next loop iteration - increment counters
|
||||
|
||||
inx
|
||||
inc tk__len_unpacked
|
||||
|
||||
// Check length, if it is OK to pack one more byte; quit if not
|
||||
|
||||
lda tk__len_unpacked
|
||||
cmp #TK__MAX_KEYWORD_LEN
|
||||
|
||||
bne tk_pack_loop
|
||||
rts
|
||||
|
||||
tk_pack_1n_new_byte:
|
||||
|
||||
// Store in the low nibble of the next byte
|
||||
|
||||
tya
|
||||
ldy tk__byte_offset
|
||||
sta tk__packed, y
|
||||
|
||||
// Adjust remaining counters / flags
|
||||
|
||||
dec tk__nibble_flag // $00 -> $FF
|
||||
bmi tk_pack_1n_done
|
||||
|
||||
tk_pack_3n:
|
||||
|
||||
// .Y contains 2 nibbles to encode; check whether we should start a new encoded byte
|
||||
|
||||
bit tk__nibble_flag
|
||||
bpl tk_pack_3n_new_byte
|
||||
|
||||
// Store 1st nibble (alwys 0xF) in the high nibble of the current byte
|
||||
|
||||
phy_trash_a
|
||||
|
||||
lda #$F0
|
||||
ldy tk__byte_offset
|
||||
ora tk__packed, y
|
||||
sta tk__packed, y
|
||||
|
||||
// Store byte in the next byte
|
||||
|
||||
iny
|
||||
pla
|
||||
sta tk__packed, y
|
||||
|
||||
// Adjust remaining counters / flags
|
||||
|
||||
iny
|
||||
sty tk__byte_offset
|
||||
inc tk__nibble_flag // $FF -> $00
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_pack_3n_done:
|
||||
|
||||
// Put the bit mark - to indicate 3-nibble encoding
|
||||
|
||||
sec
|
||||
ror tk__shorten_bits
|
||||
|
||||
bcc tk_pack_loop_next // branch always
|
||||
|
||||
tk_pack_3n_new_byte:
|
||||
|
||||
// First store everything in the current byte, we will set '0xF' mark later
|
||||
|
||||
tya
|
||||
ldy tk__byte_offset
|
||||
sta tk__packed, y
|
||||
|
||||
// Now store just the low nibble in the next byte
|
||||
|
||||
iny
|
||||
and #$0F
|
||||
sta tk__packed, y
|
||||
|
||||
// Go back for a moment and set the '0xF' mark in the low nibble
|
||||
|
||||
dey
|
||||
lda tk__packed, y
|
||||
ora #$0F
|
||||
sta tk__packed, y
|
||||
iny
|
||||
|
||||
// Adjust remaining counters / flags
|
||||
|
||||
iny
|
||||
sty tk__byte_offset
|
||||
dec tk__nibble_flag // $00 -> $FF
|
||||
|
||||
bmi tk_pack_3n_done // branch always
|
||||
@@ -0,0 +1,93 @@
|
||||
// #LAYOUT# STD * #TAKE
|
||||
// #LAYOUT# M65 BASIC_1 #TAKE
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
//
|
||||
// Search for a token on the list pointed by FRESPC
|
||||
//
|
||||
// Input:
|
||||
// - list address in FRESPC, has to be refreshed after routine call
|
||||
// - uses same variables as 'tk_pack'
|
||||
// Output:
|
||||
// - Carry set = not found
|
||||
// - .X = index of token found
|
||||
//
|
||||
|
||||
|
||||
tk_search:
|
||||
|
||||
ldx #$00 // initial token index
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_search_loop:
|
||||
|
||||
// Check if end of the list
|
||||
|
||||
ldy #$00
|
||||
lda (FRESPC), y
|
||||
cmp #$FF
|
||||
bne tk_search_compare
|
||||
iny
|
||||
lda (FRESPC), y
|
||||
dey
|
||||
cmp #$FF
|
||||
bne tk_search_compare
|
||||
|
||||
// End of the list
|
||||
|
||||
sec
|
||||
rts
|
||||
|
||||
tk_search_compare:
|
||||
|
||||
// Compare the packed candidate with the current keyword list entry
|
||||
|
||||
lda (FRESPC), y
|
||||
cmp tk__packed, y
|
||||
bne tk_search_not_matching
|
||||
|
||||
cmp #$00
|
||||
beq tk_search_found
|
||||
|
||||
// For now everything matches, try the next byte
|
||||
|
||||
iny
|
||||
bne tk_search_compare // branch always
|
||||
|
||||
tk_search_not_matching:
|
||||
|
||||
inx // increment token index
|
||||
|
||||
// We need to find the end of the keyword on the list
|
||||
// and proceed one byte further
|
||||
|
||||
cmp #$00
|
||||
beq tk_search_next
|
||||
!:
|
||||
iny
|
||||
lda (FRESPC), y
|
||||
bne !-
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_search_next:
|
||||
|
||||
// Update FRESPC - advance by .Y
|
||||
|
||||
iny
|
||||
tya
|
||||
clc
|
||||
adc FRESPC+0
|
||||
sta FRESPC+0
|
||||
lda FRESPC+1
|
||||
adc #$00
|
||||
sta FRESPC+1
|
||||
|
||||
bcc tk_search_loop // branch always
|
||||
|
||||
tk_search_found:
|
||||
|
||||
clc
|
||||
rts
|
||||
@@ -0,0 +1,112 @@
|
||||
// #LAYOUT# STD * #TAKE
|
||||
// #LAYOUT# M65 BASIC_1 #TAKE
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
//
|
||||
// Shortens a keyword candidate previously packed by tk_pack by 1 character
|
||||
//
|
||||
// Input:
|
||||
// - uses same variables as 'tk_pack'
|
||||
//
|
||||
|
||||
|
||||
tk_shorten:
|
||||
|
||||
// First make sure we actually can shorten anything
|
||||
|
||||
dec tk__len_unpacked
|
||||
beq tk_shorten_end
|
||||
|
||||
// Now check if the last character was encoded as 1 nibble or 3 nibbles
|
||||
|
||||
asl tk__nibble_flag
|
||||
bcs tk_shorten_3n
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_shorten_1n:
|
||||
|
||||
ldy tk__byte_offset // will be needed in both cases
|
||||
|
||||
// Now we need to check which nibble to cut away
|
||||
|
||||
bit tk__nibble_flag
|
||||
bcs tk_shorten_1n_lo
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_shorten_1n_hi: // tk__nibble_flag = $00, both nibbles free
|
||||
|
||||
// Go back one byte and clear the high nibble only
|
||||
|
||||
dey
|
||||
sty tk__byte_offset
|
||||
|
||||
lda tk__packed, y
|
||||
and #$F0
|
||||
sta tk__packed, y
|
||||
|
||||
// Adjust remaining counters / flags, and quit
|
||||
|
||||
dec tk__nibble_flag // $00 -> $FF
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_shorten_end:
|
||||
|
||||
rts
|
||||
|
||||
tk_shorten_1n_lo: // tk__nibble_flag = $FF, only the high nibble is free
|
||||
|
||||
// Clear the whole byte, it will be faster
|
||||
|
||||
lda #$00
|
||||
sta tk__packed, y
|
||||
|
||||
// Adjust remaining counters / flags, and quit
|
||||
|
||||
inc tk__nibble_flag // $FF -> $00
|
||||
rts
|
||||
|
||||
tk_shorten_3n:
|
||||
|
||||
ldy tk__byte_offset // will be needed in both cases
|
||||
lda #$00
|
||||
|
||||
// Now we need to check which nibble to cut away
|
||||
|
||||
bit tk__nibble_flag
|
||||
bcs tk_shorten_3n_2bytes
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tk_shorten_3n_byte_nibble: // tk__nibble_flag = $00, both nibbles free
|
||||
|
||||
// Go back one byte, clear it, go back and clear the high nibble only
|
||||
|
||||
dey
|
||||
sta tk__packed, y
|
||||
|
||||
// Reuse 'tk_shorten_1n_hi'
|
||||
|
||||
sty tk__byte_offset
|
||||
#if HAS_OPCODES_65C02
|
||||
bra tk_shorten_1n_hi
|
||||
#else
|
||||
jmp tk_shorten_1n_hi
|
||||
#endif
|
||||
|
||||
tk_shorten_3n_2bytes: // tk__nibble_flag = $FF, only the high nibble is free
|
||||
|
||||
// Clear the byte, go back, clear one more byte
|
||||
|
||||
sta tk__packed, y
|
||||
dey
|
||||
sta tk__packed, y
|
||||
|
||||
// Adjust remaining counters / flags, and quit
|
||||
|
||||
sty tk__byte_offset
|
||||
inc tk__nibble_flag // $FF -> $00
|
||||
rts
|
||||
@@ -0,0 +1,176 @@
|
||||
// #LAYOUT# STD * #TAKE
|
||||
// #LAYOUT# M65 * #TAKE
|
||||
// #LAYOUT# X16 BASIC_0 #TAKE
|
||||
// #LAYOUT# * * #IGNORE
|
||||
|
||||
//
|
||||
// Tokenise a line of BASIC stored at $0200
|
||||
//
|
||||
// Input:
|
||||
// - length in __tokenise_work1
|
||||
//
|
||||
|
||||
|
||||
tokenise_line:
|
||||
|
||||
#if (ROM_LAYOUT_M65 && SEGMENT_BASIC_0)
|
||||
|
||||
jsr map_BASIC_1
|
||||
jsr_ind VB1__tokenise_line
|
||||
jmp map_NORMAL
|
||||
|
||||
#else
|
||||
|
||||
// Reuse the CPU stack - addresses above $101 are used by 'tk_pack.s'
|
||||
|
||||
.label tk__offset = $100 // offset into string
|
||||
.label tk__length = $101 // length of the raw string XXX reuse __tokenise_work1?
|
||||
|
||||
// Initialize variables
|
||||
|
||||
lda #$00
|
||||
sta tk__offset
|
||||
|
||||
ldx __tokenise_work1
|
||||
stx tk__length
|
||||
|
||||
// Terminate input string with $00
|
||||
|
||||
// XXX do we need this?
|
||||
sta BUF, x
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tokenise_line_loop:
|
||||
|
||||
// Check if we have reached end of the line
|
||||
|
||||
lda tk__offset
|
||||
cmp tk__length
|
||||
beq tokenise_line_done
|
||||
|
||||
// Check for some special characters
|
||||
|
||||
ldx tk__offset
|
||||
lda BUF, x
|
||||
|
||||
cmp #$22
|
||||
beq tokenise_line_quote
|
||||
|
||||
cmp #$DE
|
||||
beq tokenise_line_pi
|
||||
|
||||
cmp #$3F
|
||||
beq tokenise_line_question_mark // shortcut for PRINT command
|
||||
|
||||
// Try to tokenise
|
||||
|
||||
jsr tk_pack
|
||||
!:
|
||||
lda tk__len_unpacked
|
||||
beq tokenise_line_char // branch if attempt to tokenise failed
|
||||
|
||||
lda #<packed_str_keywords_V2
|
||||
sta FRESPC+0
|
||||
lda #>packed_str_keywords_V2
|
||||
sta FRESPC+1
|
||||
|
||||
jsr tk_search
|
||||
bcc tokenise_line_keyword_V2 // branch if keyword identified
|
||||
|
||||
// XXX add support for additional keyword lists here
|
||||
|
||||
// Shorten packed keyword candidate and try again
|
||||
|
||||
jsr tk_shorten
|
||||
|
||||
#if HAS_OPCODES_65C02
|
||||
bra !-
|
||||
#else
|
||||
jmp !-
|
||||
#endif
|
||||
|
||||
tokenise_line_keyword_V2:
|
||||
|
||||
// .X contains a token ID, starting from 0
|
||||
|
||||
txa
|
||||
pha // store the token on the stack, we will need it for REM support
|
||||
|
||||
// Store the token
|
||||
|
||||
ldx tk__offset
|
||||
clc
|
||||
adc #$80
|
||||
sta BUF, x
|
||||
|
||||
// Cut away unnecessary bytes
|
||||
|
||||
inc tk__offset
|
||||
ldx tk__offset
|
||||
|
||||
dec tk__len_unpacked // keyword length, afterwards number of bytes to cut away
|
||||
lda tk__len_unpacked
|
||||
clc
|
||||
adc tk__offset
|
||||
tay
|
||||
!:
|
||||
lda BUF, y
|
||||
sta BUF, x
|
||||
beq !+
|
||||
|
||||
inx
|
||||
iny
|
||||
bne !-
|
||||
!:
|
||||
// Special handling for REM command - after this one nothing more should be tokenised
|
||||
|
||||
pla
|
||||
cmp #$0F // REM token index
|
||||
bne tokenise_line_loop
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tokenise_line_done:
|
||||
|
||||
// Update line length and quit
|
||||
|
||||
lda tk__length
|
||||
sta __tokenise_work1
|
||||
|
||||
rts
|
||||
|
||||
tokenise_line_quote:
|
||||
|
||||
// For the quote, we advance without tokenizing, until the next quote is found
|
||||
|
||||
inc tk__offset
|
||||
ldx tk__offset
|
||||
|
||||
lda BUF, x
|
||||
beq tokenise_line_done
|
||||
|
||||
cmp #$22
|
||||
beq tokenise_line_char
|
||||
bne tokenise_line_quote // branch always
|
||||
|
||||
tokenise_line_pi:
|
||||
|
||||
lda #$FF // token for PI
|
||||
skip_2_bytes_trash_nvz
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tokenise_line_question_mark:
|
||||
|
||||
lda #$99 // token for PRINT
|
||||
sta BUF, x
|
||||
|
||||
// FALLTROUGH
|
||||
|
||||
tokenise_line_char:
|
||||
|
||||
inc tk__offset
|
||||
jmp tokenise_line_loop
|
||||
|
||||
#endif // ROM layout
|
||||
Reference in new issue
Block a user