diff --git a/CONFIG.md b/CONFIG.md index 9c1fa54c..e94e7ac6 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -285,9 +285,9 @@ Feature needs about 330 bytes in BASIC segment. If unsure - enable. ### `CONFIG_TAPE_WEDGE` -If enabled, a simple DOS wedge is available from the direct mode for tape loading - supports `←L` only. +If enabled, a simple DOS wedge is available from the direct mode for tape loading - supports `←L` (for `LOAD`) and `←M` (for `MERGE`), optionally with a file name. -Feature needs few bytes in BASIC segment. If unsure - enable. +Feature needs several bytes in BASIC segment. If unsure - enable. ### `CONFIG_TAPE_HEAD_ALIGN` diff --git a/Makefile b/Makefile index 92dff008..7dcd2041 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ SRCDIR_BASIC = $(SRCDIR_COMMON) \ src/basic/basic_functions \ src/basic/board_m65 \ src/basic/board_x16 \ + src/basic/editor \ src/basic/engine \ src/basic/init \ src/basic/math \ @@ -28,7 +29,6 @@ SRCDIR_BASIC = $(SRCDIR_COMMON) \ src/basic/memory \ src/basic/print \ src/basic/rom_revision \ - src/basic/tokenizer \ src/basic/wedge SRCDIR_KERNAL = $(SRCDIR_COMMON) \ diff --git a/STATUS.md b/STATUS.md index 145e4a86..276414db 100644 --- a/STATUS.md +++ b/STATUS.md @@ -12,7 +12,7 @@ Here are the features of the Open ROMs not found in the original ROMs from the 8 * DOS wedge (direct mode only) - `@`, `@`, `@$`, `@$`, `@` * BASIC extensions, see the [documentation](doc/Extended-BASIC.md) -* turbo tape load support (as device 7, or using `←L`), quite sophisticated: up to 250 blocks (can store bytes under I/O), automatically adjusts for tape speed differences +* turbo tape load support (as device 7, or using `←L` / `←M`, optionally with a file name), quite sophisticated: up to 250 blocks (can store bytes under I/O), automatically adjusts for tape speed differences * normal tape load error log is limited by free stack space only (no artificial limitation like in original ROMs) * tape format autodetection; normal vs turbo is mostly transparent to user * improved support for tape adapters (for using regular casette players and other audio devices instead of Datasette) and emulators @@ -22,6 +22,7 @@ Here are the features of the Open ROMs not found in the original ROMs from the 8 * start/end addresses are displayed, in the Final cartridge style * command with just the file name tries to use the last device if it's number seems sane; otherwise uses 8 * command without parameters tries to load first file from drive chosen as above +* `LIST` command should never damage the screen if trying to list memory content which is not a BASIC program * uses RAM under ROM and I/O: 51199 bytes free (up to `$CFFF`) by default, but 61438 is also possible * cold/warm start silences multiple SID chips diff --git a/bin/basic_generic.rom b/bin/basic_generic.rom index 90052287..dc9dba7b 100644 Binary files a/bin/basic_generic.rom and b/bin/basic_generic.rom differ diff --git a/bin/basic_ultimate64.rom b/bin/basic_ultimate64.rom index b84cd139..804c3705 100644 Binary files a/bin/basic_ultimate64.rom and b/bin/basic_ultimate64.rom differ diff --git a/bin/kernal_generic.rom b/bin/kernal_generic.rom index 06a5a7fb..75684fcc 100644 Binary files a/bin/kernal_generic.rom and b/bin/kernal_generic.rom differ diff --git a/bin/kernal_ultimate64.rom b/bin/kernal_ultimate64.rom index 9f4108ff..379d1847 100644 Binary files a/bin/kernal_ultimate64.rom and b/bin/kernal_ultimate64.rom differ diff --git a/bin/mega65.rom b/bin/mega65.rom index 6c788321..c299e1c5 100644 Binary files a/bin/mega65.rom and b/bin/mega65.rom differ diff --git a/bin/mega65_pxlfont.rom b/bin/mega65_pxlfont.rom index 2b2c51e8..e496e285 100644 Binary files a/bin/mega65_pxlfont.rom and b/bin/mega65_pxlfont.rom differ diff --git a/doc/Extended-BASIC.md b/doc/Extended-BASIC.md index c5ebca1b..dd48b323 100644 --- a/doc/Extended-BASIC.md +++ b/doc/Extended-BASIC.md @@ -1,9 +1,45 @@ # Extended BASIC -NOTE: Command syntax, behaviour details, and tokens are still subject to change! In the future most (if not all) extended commands will be available only on machines with extended ROM, like the Mega65. +NOTE: Command syntax, behaviour details, and tokens are still subject to change! Currently the availability of some depends on the build configuration, in the future most (if not all) extended commands will be available only on machines with extended ROM, like the Mega65 or Ultimate 64. -## Additional commands +## DOS Wedge + +The DOS Wedge commands are only available in direct mode, and they have to start from the first character of the line. + +### `@` + +Displays the error/status read from the current drive. Current drive is either the last device used by any I/O command, the one set by DOS Wedge command, or 8 as a fallback. + +### `@$` + +Displays the directory of the current drive. Does not destroy BASIC program in memory. + +### `@device_number` + +Changes the current drive number. + +### `@command` + +Sends the DOS command to the current drive. + +## Tape Wedge + +The Tape Wedge commands are only available in direct mode, and they have to start from the first character of the line. + +### `←L ["file_name"]` + +Loads the file from tape. Depending on the configuration, it tries to load the file in TurboTape 64 format, in the standard CBM format, or autodetects the file format. + +### `←M ["file_name"]` + +Like above, but merges a BASIC program to the one already stored in memory. + +### `←H` + +Launches a built-in tape head alignment tool. Destorys program in memory. Press `RUN/STOP` to terminate the tool. + +## Additional BASIC commands ### `FAST` @@ -13,9 +49,13 @@ Tries to switch the machine to turbo mode - details differ depending on the conf Tries to switch the machine back to 1 MHz operation mode. +### `OLD` + +Tries to restore program destroyed by `NEW` or a reset. + ### `MERGE "file_name" [, device_number]` -Appends the BASIC program from a storage medium to the one currently present in memory. NOTE: due to bug in BASIC editor (improper VARTAB handling) it currently works only with programs loaded from the storage medium, not the ones typed in! +Appends the BASIC program from a storage medium to the one currently present in memory. ### `BLOAD "file_name", device_number, start_address` diff --git a/empty.d64 b/empty.d64 deleted file mode 100644 index cb9940fe..00000000 Binary files a/empty.d64 and /dev/null differ diff --git a/src/aliases/,aliases_ram_lowmem.s b/src/aliases/,aliases_ram_lowmem.s index 3e206102..eb492b70 100644 --- a/src/aliases/,aliases_ram_lowmem.s +++ b/src/aliases/,aliases_ram_lowmem.s @@ -32,19 +32,19 @@ .label LASTPT = $17 // $17-$18 pointer to the last used slot in the temporary string descriptor .label TEMPST = $19 // $19-$21 temporary string stack descriptors .label INDEX = $22 // $22-$25 temporary variables, [!] our usage might be different - .label RESHO = $26 // $26-$2A -- NOT IMPLEMENTED -- + .label RESHO = $26 // $26-$2A temporary variables, [!] our usage might be different .label TXTTAB = $2B // $2B-$2C start of BASIC code .label VARTAB = $2D // $2D-$2E end of BASIC code, start of variables .label ARYTAB = $2F // $2F-$30 -- NOT IMPLEMENTED -- .label STREND = $31 // $31-$32 -- NOT IMPLEMENTED -- - .label FRETOP = $33 // $33-$34 -- NOT IMPLEMENTED -- + .label FRETOP = $33 // $33-$34 pointer to bottom of strings area .label FRESPC = $35 // $35-$36 [!] our implementation uses this as temporary string pointer .label MEMSIZ = $37 // $37-$38 highest address of BASIC memory + 1 .label CURLIN = $39 // $39-$3A current BASIC line number .label OLDLIN = $3B // $3B-$3C previous BASIC line number .label OLDTXT = $3D // $3D-$3E current BASIC line pointer .label DATLIN = $3F // $3F-$40 -- NOT IMPLEMENTED -- - .label DATPTR = $41 // $41-$42 -- NOT IMPLEMENTED -- + .label DATPTR = $41 // $41-$42 pointer for READ/DATA copmmands .label INPPTR = $43 // $43-$44 -- NOT IMPLEMENTED -- .label VARNAM = $45 // $45-$46 -- NOT IMPLEMENTED -- .label VARPNT = $47 // $47-$48 -- NOT IMPLEMENTED -- @@ -71,6 +71,12 @@ .label FAC2_mantissa = $6A // $6A - $6D .label FAC2_sign = $6E + // We re-use FAC2 for memory move pointers, since we cannot be doing calculations while moving memory + .label memmove__src = $69 + .label memmove__dst = $6B + .label memmove__size = $6D + + .label ARISGN = $6F // -- NOT IMPLEMENTED -- .label FACOV = $70 // FAC1 low order mantissa .label FBUFPT = $71 // $71-$72 -- NOT IMPLEMENTED -- @@ -229,15 +235,15 @@ // IRQs are disabled when doing such accesses, and a default NMI handler only increments // a counter, so that if an NMI occurs, it does not crash the machine, but can be captured. - .label missed_nmi_flag = $2A7 - .label tiny_nmi_handler = $2A8 - .label peek_under_roms = tiny_nmi_handler + peek_under_roms_routine - tiny_nmi_handler_routine - .label poke_under_roms = tiny_nmi_handler + poke_under_roms_routine - tiny_nmi_handler_routine - .label memmap_allram = tiny_nmi_handler + memmap_allram_routine - tiny_nmi_handler_routine - .label memmap_normal = tiny_nmi_handler + memmap_normal_routine - tiny_nmi_handler_routine + .label missed_nmi_flag = $2A7 + .label tiny_nmi_handler = $2A8 + .label peek_under_roms = tiny_nmi_handler + peek_under_roms_routine - tiny_nmi_handler_routine + .label poke_under_roms = tiny_nmi_handler + poke_under_roms_routine - tiny_nmi_handler_routine + .label memmap_allram = tiny_nmi_handler + memmap_allram_routine - tiny_nmi_handler_routine + .label memmap_normal = tiny_nmi_handler + memmap_normal_routine - tiny_nmi_handler_routine #if SEGMENT_BASIC - .label shift_mem_up = tiny_nmi_handler + shift_mem_up_routine - tiny_nmi_handler_routine - .label shift_mem_down = tiny_nmi_handler + shift_mem_down_routine - tiny_nmi_handler_routine + .label shift_mem_up_internal = tiny_nmi_handler + shift_mem_up_routine - tiny_nmi_handler_routine + .label shift_mem_down_internal = tiny_nmi_handler + shift_mem_down_routine - tiny_nmi_handler_routine #endif #endif // CONFIG_MEMORY_MODEL_60K diff --git a/src/basic/,aliases.s b/src/basic/,aliases.s index 37741819..291df1a7 100644 --- a/src/basic/,aliases.s +++ b/src/basic/,aliases.s @@ -11,9 +11,3 @@ .label __tokenise_work3 = $0B // COUNT .label __tokenise_work4 = $0F // GARBFL .label __tokenise_work5 = $0C // DIMFLG - - // We re-use FAC2 for memory move pointers, since - // we can't be doing calculations while moving memory - .label __memmove_src = $69 - .label __memmove_dst = $6B - .label __memmove_size = $6D diff --git a/src/basic/ab1e.strout.s b/src/basic/ab1e.strout.s index 0c2e99b7..9f99e7bf 100644 --- a/src/basic/ab1e.strout.s +++ b/src/basic/ab1e.strout.s @@ -10,6 +10,7 @@ // print string routine STROUT: + // Setup pointer sty FRESPC+1 sta FRESPC+0 diff --git a/src/basic/basic_vector_defaults.s b/src/basic/assets/basic_vector_defaults.s similarity index 100% rename from src/basic/basic_vector_defaults.s rename to src/basic/assets/basic_vector_defaults.s diff --git a/src/basic/c000.skip_gap.s b/src/basic/assets/c000.skip_gap.s similarity index 100% rename from src/basic/c000.skip_gap.s rename to src/basic/assets/c000.skip_gap.s diff --git a/src/basic/assets/str_rev_question.s b/src/basic/assets/str_rev_question.s new file mode 100644 index 00000000..ba0c8302 --- /dev/null +++ b/src/basic/assets/str_rev_question.s @@ -0,0 +1,9 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + +// String - question mark in reverse + +str_rev_question: + + .byte $12, $3F, $92, $00 diff --git a/src/basic/basic_commands/cmd__unimplemented.s b/src/basic/basic_commands/cmd__unimplemented.s index 4e67d479..6495feb6 100644 --- a/src/basic/basic_commands/cmd__unimplemented.s +++ b/src/basic/basic_commands/cmd__unimplemented.s @@ -27,8 +27,6 @@ cmd_let: nop cmd_if: nop -cmd_restore: - nop cmd_gosub: nop cmd_return: diff --git a/src/basic/basic_commands/cmd_clr.s b/src/basic/basic_commands/cmd_clr.s index 6b4b493c..f944eacc 100644 --- a/src/basic/basic_commands/cmd_clr.s +++ b/src/basic/basic_commands/cmd_clr.s @@ -5,7 +5,7 @@ cmd_clr: - jsr basic_do_clr + jsr do_clr // CLR command does not stop execution (confirmed on a C64) diff --git a/src/basic/basic_commands/cmd_list.s b/src/basic/basic_commands/cmd_list.s index be79ad4c..3da8ca4f 100644 --- a/src/basic/basic_commands/cmd_list.s +++ b/src/basic/basic_commands/cmd_list.s @@ -31,14 +31,14 @@ list_loop: // LIST terminates any running program, // because it has fiddled with the current line pointer. // (Confirmed by testing on a C64) - jmp basic_main_loop + jmp shell_main_loop list_more_lines: + lda STKEY bmi !+ jmp cmd_stop !: jsr list_single_line - // Now link to the next line - jsr basic_find_next_line + jsr follow_link_to_next_line jmp list_loop diff --git a/src/basic/basic_commands/cmd_load_verify.s b/src/basic/basic_commands/cmd_load_verify.s index aab37558..9a0d59fd 100644 --- a/src/basic/basic_commands/cmd_load_verify.s +++ b/src/basic/basic_commands/cmd_load_verify.s @@ -56,7 +56,7 @@ cmd_load_no_error: // C64 BASIC apparently does not clear variables after a LOAD in the // middle of a program. For safety, we do. - jsr basic_do_clr + jsr do_clr // Now relink the loaded program, as we cannot trust the line // links supplied. For example, the VICE virtual drive emulation @@ -80,4 +80,4 @@ cmd_load_no_error: cmd_load_end: - jmp basic_main_loop + jmp shell_main_loop diff --git a/src/basic/basic_commands/cmd_new.s b/src/basic/basic_commands/cmd_new.s index ce879826..e7d7189f 100644 --- a/src/basic/basic_commands/cmd_new.s +++ b/src/basic/basic_commands/cmd_new.s @@ -5,8 +5,8 @@ cmd_new: - jsr basic_do_new + jsr do_new // NEW command terminates execution (confirmed on a C64) - jmp basic_main_loop + jmp shell_main_loop diff --git a/src/basic/basic_commands/cmd_restore.s b/src/basic/basic_commands/cmd_restore.s new file mode 100644 index 00000000..034db54e --- /dev/null +++ b/src/basic/basic_commands/cmd_restore.s @@ -0,0 +1,12 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + + +cmd_restore: + + jsr do_restore + + // RESTORE command does not stop execution (confirmed on a C64) + + jmp execute_statements diff --git a/src/basic/basic_commands/cmd_run_goto.s b/src/basic/basic_commands/cmd_run_goto.s index 65570149..4282ca1c 100644 --- a/src/basic/basic_commands/cmd_run_goto.s +++ b/src/basic/basic_commands/cmd_run_goto.s @@ -5,7 +5,7 @@ cmd_run: // RUN clears all variables - jsr basic_do_clr + jsr do_clr cmd_goto: // Disable Kernal messages lda #$00 @@ -18,7 +18,7 @@ cmd_goto: bcs !+ jsr basic_parse_line_number - jsr basic_find_line + jsr find_line bcc !+ // Line does not exist, so report error jmp do_UNDEFD_STATEMENT_error diff --git a/src/basic/basic_commands/cmd_stop_end.s b/src/basic/basic_commands/cmd_stop_end.s index 51c4906e..3cf6f64c 100644 --- a/src/basic/basic_commands/cmd_stop_end.s +++ b/src/basic/basic_commands/cmd_stop_end.s @@ -27,4 +27,4 @@ cmd_stop: cmd_end: - jmp basic_main_loop + jmp shell_main_loop diff --git a/src/basic/basic_commands_CC/cmd__jumptable_CC.s b/src/basic/basic_commands_CC/cmd__jumptable_CC.s index 72168c3e..52a9cb50 100644 --- a/src/basic/basic_commands_CC/cmd__jumptable_CC.s +++ b/src/basic/basic_commands_CC/cmd__jumptable_CC.s @@ -13,7 +13,9 @@ cmd_bload, cmd_bsave, - cmd_bverify + cmd_bverify, + + cmd_old ) diff --git a/src/basic/basic_commands_CC/cmd_bload_bverify.s b/src/basic/basic_commands_CC/cmd_bload_bverify.s index a85891f5..2b6db9fb 100644 --- a/src/basic/basic_commands_CC/cmd_bload_bverify.s +++ b/src/basic/basic_commands_CC/cmd_bload_bverify.s @@ -39,4 +39,4 @@ cmd_bload: // XXX when number parsing is done, change to continue execution - jmp basic_main_loop + jmp shell_main_loop diff --git a/src/basic/basic_commands_CC/cmd_merge.s b/src/basic/basic_commands_CC/cmd_merge.s index 5734b68d..42f3b1ed 100644 --- a/src/basic/basic_commands_CC/cmd_merge.s +++ b/src/basic/basic_commands_CC/cmd_merge.s @@ -16,9 +16,15 @@ cmd_merge: jsr helper_load_fetch_devnum - // Perform loading - just for a different address + // FALLTROUGH - // XXX sometimes works, sometimes does not - probably improper handling of VARTAB somewhere +cmd_merge_got_params: + + // Make sure VARTAB is correctly set + + jsr update_VARTAB_do_clr + + // Perform loading - just for a different address sec lda VARTAB+0 @@ -36,18 +42,10 @@ cmd_merge: cmd_merge_no_error: - // Store last loaded address + // Relink loaded program, clear variables - stx VARTAB+0 - sty VARTAB+1 - - // Clear the variables - jsr basic_do_clr - - // Now relink the loaded program, line links supplied are almost certainly wrong - - jsr LINKPRG + jsr update_LINKPRG_VARTAB_do_clr // XXX when number parsing is done, change to continue execution - jmp basic_main_loop + jmp shell_main_loop diff --git a/src/basic/basic_commands_CC/cmd_old.s b/src/basic/basic_commands_CC/cmd_old.s new file mode 100644 index 00000000..9b816edf --- /dev/null +++ b/src/basic/basic_commands_CC/cmd_old.s @@ -0,0 +1,24 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + + +cmd_old: + + // First make sure we are in direct mode + + ldx CURLIN+1 + inx + bne_16 do_DIRECT_MODE_ONLY_error + + // Now try to restore program linkage and VARTAB + + ldy #$01 + tya + sta (TXTTAB),y + + jsr update_LINKPRG_VARTAB_do_clr + + // Quit + + jmp shell_main_loop diff --git a/src/basic/basic_do_new.s b/src/basic/basic_do_new.s deleted file mode 100644 index 0c9119eb..00000000 --- a/src/basic/basic_do_new.s +++ /dev/null @@ -1,99 +0,0 @@ -// #LAYOUT# STD * #TAKE -// #LAYOUT# M65 * #TAKE -// #LAYOUT# X16 BASIC_0 #TAKE -// #LAYOUT# * * #IGNORE - - -basic_do_new: - -#if (ROM_LAYOUT_M65 && SEGMENT_BASIC_0) - - jsr map_BASIC_1 - jsr_ind VB1__basic_do_new - jmp map_NORMAL - -basic_do_clr: - - jsr map_BASIC_1 - jsr_ind VB1__basic_do_clr - jmp map_NORMAL - -#else - - // Setup pointers to memory storage XXX should depend on text start vector - lda #<$0801 - sta TXTTAB+0 - lda #>$0801 - sta TXTTAB+1 - sta VARTAB+1 - lda #<$0803 - sta VARTAB+0 - - // Zero line pointer - // XXX - We should also zero $0800, i.e., TXTTAB - 1 - ldy #$00 - tya - sta (TXTTAB),y - iny - sta (TXTTAB),y - - - // Get top of memory. - // If a cart is present, then it is $7FFF, - // if not, then it is $F7FF, since we support having - // programs and variables under ROM. - // i.e., we support 56KB RAM for BASIC, keeping some space - // free for some optimisation structures, e.g., FOR/NEXT loop - // records (without them going on the stack), GOSUB stack - // (same story), expression value cache? - - sec // Read, not write value - jsr JMEMTOP - cpx #$80 - beq !+ -#if CONFIG_MEMORY_MODEL_60K - lda #>$F7FF -#elif CONFIG_MEMORY_MODEL_50K - lda #>$D000 -#elif CONFIG_MEMORY_MODEL_46K - lda #>$C000 -#else // CONFIG_MEMORY_MODEL_38K - lda #>$A000 -#endif - skip_2_bytes_trash_nvz -!: - lda #>$8000 - sta MEMSIZ+1 - sta FRETOP+1 -#if CONFIG_MEMORY_MODEL_60K - cpx #$80 - beq !+ - lda #$FF - skip_2_bytes_trash_nvz -!: - lda #<$00 -#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K - lda #<$00 -#endif - sta MEMSIZ+0 - sta FRETOP+0 - - // FALLTHROUGH - -basic_do_clr: - - // XXX shouldnt FRETOP go here? - - // Clear variables, arrays and strings - - lda VARTAB+0 - sta ARYTAB+0 - sta STREND+0 - lda VARTAB+1 - sta ARYTAB+1 - sta STREND+1 - - rts - - -#endif // ROM layout diff --git a/src/basic/basic_find_next_line.s b/src/basic/basic_find_next_line.s deleted file mode 100644 index cd38025f..00000000 --- a/src/basic/basic_find_next_line.s +++ /dev/null @@ -1,35 +0,0 @@ -// #LAYOUT# STD * #TAKE -// #LAYOUT# * BASIC_0 #TAKE -// #LAYOUT# * * #IGNORE - -// Advance OLDTXT by following the -// link to the next line - -basic_find_next_line: - - ldy #0 - -#if CONFIG_MEMORY_MODEL_60K - ldx #VARTAB - // .byte $f0,VARTAB - // .byte $0d - // .text "SHIFTING DOWN $" - // .byte $f1,<__memmove_size,>__memmove_size - // .byte $f0,<__memmove_size,>__memmove_size - // .text " BYTES FROM $" - // .byte $f1,<__memmove_src,>__memmove_src - // .byte $f0,<__memmove_src,>__memmove_src - // .text " TO $" - // .byte $f1,<__memmove_dst,>__memmove_dst - // .byte $f0,<__memmove_dst,>__memmove_dst - // .byte $0d,0 - - // The copy routine that copies under the ROMs is as simple - // as possible to be as small as possible, so we have - // to adjust the pointers and counts so that the simple - // increment and decrement bounds checks work. - - // Specifically we want Y pre-set so that the end of the - // copy ends when Y = $00. - // So if copying 1 byte, we want Y=$FF - // This means we have to then reduce the source and - // target pointers by the same amount - - lda __memmove_size+0 - eor #$ff - sta __tokenise_work3 - sta __memmove_size+0 - - lda __memmove_src+0 - sec - sbc __tokenise_work3 - sta __memmove_src+0 - lda __memmove_src+1 - sbc #0 - sta __memmove_src+1 - - lda __memmove_dst+0 - sec - sbc __tokenise_work3 - sta __memmove_dst+0 - lda __memmove_dst+1 - sbc #0 - sta __memmove_dst+1 - - // Increase copy page count so we can post-decrement compare with $00 - inc __memmove_size+1 - - // jsr printf - // .text "REVISED BOUNDS $" - // .byte $f1,<__memmove_size,>__memmove_size - // .byte $f0,<__memmove_size,>__memmove_size - // .text " BYTES FROM $" - // .byte $f1,<__memmove_src,>__memmove_src - // .byte $f0,<__memmove_src,>__memmove_src - // .text " TO $" - // .byte $f1,<__memmove_dst,>__memmove_dst - // .byte $f0,<__memmove_dst,>__memmove_dst - // .byte $0d,0 - - // Get Y value ready for the copy - ldy __memmove_size+0 - - jsr shift_mem_down - - // Get length of deletion back - pla - sta __tokenise_work3 - - // Check if we still have any lines left - ldy #0 - jsr peek_line_pointer_null_check - bcs relink_down_next_line - // Nope, so just return - clc - rts - - -relink_down_next_line: - // inc $d020 - // jmp relink_down_next_line - - // Subtract __tokenise_work3 from the pointer - ldy #0 - -#if CONFIG_MEMORY_MODEL_60K - ldx #VARTAB - // .byte $f0,VARTAB - // .byte $0d - // .text "SHIFTING UP $" - // .byte $f1,<__memmove_size,>__memmove_size - // .byte $f0,<__memmove_size,>__memmove_size - // .text " BYTES FROM $" - // .byte $f1,<__memmove_src,>__memmove_src - // .byte $f0,<__memmove_src,>__memmove_src - // .text " TO $" - // .byte $f1,<__memmove_dst,>__memmove_dst - // .byte $f0,<__memmove_dst,>__memmove_dst - // .byte $0d,0 - - // To make life simple for the copy routine that lives in RAM, - // we have to adjust the end pointers down one page and set Y to the low - // byte of the copy size. - lda __memmove_src+0 - sec - sbc __memmove_size+0 - sta __memmove_src+0 - lda __memmove_src+1 - sbc #0 - sta __memmove_src+1 - - lda __memmove_dst+0 - sec - sbc __memmove_size+0 - sta __memmove_dst+0 - lda __memmove_dst+1 - sbc #0 - sta __memmove_dst+1 - - // Now make exit easy, by being able to check for zero on size high byte when done - inc __memmove_size+1 - - // Then set Y to the number offset required - ldy __memmove_size+0 - iny - - stx __tokenise_work3 - - // jsr printf - // .text "REVISED BOUNDS $" - // .byte $f1,<__memmove_size,>__memmove_size - // .byte $f0,<__memmove_size,>__memmove_size - // .text " BYTES FROM $" - // .byte $f1,<__memmove_src,>__memmove_src - // .byte $f0,<__memmove_src,>__memmove_src - // .text " TO $" - // .byte $f1,<__memmove_dst,>__memmove_dst - // .byte $f0,<__memmove_dst,>__memmove_dst - // .byte $0d,0 - - // Do the copy - jsr shift_mem_up - - // Now fix the pointer to the next line - - // First, we need to point the current BASIC line - // pointer to itself, so that we can add the shift - // to make it end up pointing to the next line - ldy #0 - - lda OLDTXT+0 - -#if CONFIG_MEMORY_MODEL_60K - ldx #VARTAB - // .byte $f0,VARTAB - // .text $d," BEFORE = $" - // .byte $f1,<__memmove_dst,>__memmove_dst - // .byte $f0,<__memmove_dst,>__memmove_dst - // .text ", AFTER = $" - // .byte $f1,<__memmove_src,>__memmove_src - // .byte $f0,<__memmove_src,>__memmove_src - // .byte $d - // .byte 0 - - // Write __memmove_src back to current line pointer - ldy #0 - lda __memmove_src+0 - -#if CONFIG_MEMORY_MODEL_60K - ldx #255 bytes. // Either way, things are bad, so abort. + jmp do_MEMORY_CORRUPT_error !: // Length can now be safely assumed to be in the low // byte only, i.e., stored in __tokenise_work3 - lda __tokenise_work3 - pha - tax + ldx __tokenise_work3 // Shuffle everything down - jsr basic_shift_mem_down_and_relink + jsr shift_txt_down - // Now decrease top of BASIC mem - pla - sta __tokenise_work3 - lda VARTAB+0 - sec - sbc __tokenise_work3 - sta VARTAB+0 - lda VARTAB+1 - sbc #0 - sta VARTAB+1 + // Finish by fixing program linkage and calculating new - clc - rts + jmp update_LINKPRG_VARTAB_do_clr diff --git a/src/basic/basic_find_line.s b/src/basic/editor/find_line.s similarity index 57% rename from src/basic/basic_find_line.s rename to src/basic/editor/find_line.s index be9a962e..47569971 100644 --- a/src/basic/basic_find_line.s +++ b/src/basic/editor/find_line.s @@ -5,19 +5,30 @@ // Find the BASIC line with number LINNUM +// XXX it should be possible to start from the current line (OLDTXT), or from the startup, +// depending on the current and target line number + + +find_line: -basic_find_line: // Get pointer to start of BASIC text lda TXTTAB+0 sta OLDTXT+0 lda TXTTAB+1 sta OLDTXT+1 -basic_find_line_loop: - // Then search for line number - // Line number - - ldy #3 + // FALLTROUGH + +find_line_loop: + + // Check if line is not empty + + jsr peek_line_pointer_null_check + bcc find_line_fail + + + // Fetch the high byte of line number and compare + ldy #$03 #if CONFIG_MEMORY_MODEL_60K ldx #OLDTXT // .byte $0d,0 - lda OLDTXT+0 - pha - lda OLDTXT+1 - - pha - // Get number of bytes in tokenised line after line number lda __tokenise_work2 sec sbc __tokenise_work1 - // Add on the four bytes space we need + + // Add on the five bytes space we need (2 bytes for linkage, 2 bytes for line number, 1 byte for terminator) clc - adc #5 - pha + adc #$05 tax - + // Make the space - jsr basic_shift_mem_up_and_relink + jsr shift_txt_up - // Now increase top of BASIC mem - pla - clc - adc VARTAB+0 - sta VARTAB+0 - lda VARTAB+1 - adc #0 - sta VARTAB+1 - - // Get pointer back - pla - sta OLDTXT+1 - pla - sta OLDTXT+0 - - // Write the line number - ldy #2 - lda LINNUM+0 + // Put dummy linkage, we will correct it later + ldy #$01 + tya #if CONFIG_MEMORY_MODEL_60K ldx #BUF - sta TXTPTR+1 - - // There is no stored line, so zero that pointer out - lda #$00 - sta OLDTXT+0 - sta OLDTXT+1 - - // Put invalid line number in current line number value, - // so that we know we are in direct mode - // (Computes Mapping the 64 p19) - lda #$FF - sta CURLIN+1 +shell_execute_line: + jsr prepare_direct_execution jmp execute_statements + diff --git a/src/basic/editor/shift_txt_down.s b/src/basic/editor/shift_txt_down.s new file mode 100644 index 00000000..c093f671 --- /dev/null +++ b/src/basic/editor/shift_txt_down.s @@ -0,0 +1,43 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + + +// +// Shift the BASIC program down to cover the area of removed line +// + + +shift_txt_down: + + // First byte of destination is OLDTXT + + lda OLDTXT+0 + sta memmove__dst+0 + lda OLDTXT+1 + sta memmove__dst+1 + + // First byte of source is memmove__dst + .X + + clc + txa + + adc memmove__dst+0 + sta memmove__src+0 + lda memmove__dst+1 + adc #$00 + sta memmove__src+1 + + // Size is distance from source to the end of BASIC text (VARTAB) + + lda VARTAB+0 + sec + sbc memmove__src+0 + sta memmove__size+0 + lda VARTAB+1 + sbc memmove__src+1 + sta memmove__size+1 + + // Perform the copy + + jmp shift_mem_down diff --git a/src/basic/editor/shift_txt_up.s b/src/basic/editor/shift_txt_up.s new file mode 100644 index 00000000..221e1460 --- /dev/null +++ b/src/basic/editor/shift_txt_up.s @@ -0,0 +1,58 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + + +// +// Shift the BASIC program up to make space for a new line +// + + +shift_txt_up: + + // Last byte of source is VARTAB-1 + +#if !HAS_OPCODES_65CE02 + + sec + lda VARTAB+0 + sbc #$01 + sta memmove__src+0 + lda VARTAB+1 + sbc #$00 + sta memmove__src+1 + +#else // HAS_OPCODES_65CE02 + + lda VARTAB+0 + sta memmove__src+0 + lda VARTAB+1 + sta memmove__src+1 + dew memmove__src + +#endif + + // Last byte of destination is memmove__src + .X + + clc + txa + + adc memmove__src+0 + sta memmove__dst+0 + lda memmove__src+1 + adc #$00 + sta memmove__dst+1 + + // Size is distance from OLDTXT to the end of BASIC text (VARTAB) + + lda VARTAB+0 + sec + sbc OLDTXT+0 + sta memmove__size+0 + lda VARTAB+1 + sbc OLDTXT+1 + sta memmove__size+1 + + // Perform the copy + + jmp shift_mem_up diff --git a/src/basic/tokenizer/tk_cut_away.s b/src/basic/editor/tk_cut_away.s similarity index 79% rename from src/basic/tokenizer/tk_cut_away.s rename to src/basic/editor/tk_cut_away.s index c88e3c6a..4741388c 100644 --- a/src/basic/tokenizer/tk_cut_away.s +++ b/src/basic/editor/tk_cut_away.s @@ -18,6 +18,15 @@ tk_cut_away_1: // for single-byte tokenized keywords dec tk__len_unpacked // keyword length, afterwards number of bytes to cut away + // Correct the line length + + sec + lda tk__length + sbc tk__len_unpacked + sta tk__length + + // Copy the remaining,. untokenized part in the proper place + inc tk__offset ldy tk__offset diff --git a/src/basic/tokenizer/tk_pack.s b/src/basic/editor/tk_pack.s similarity index 91% rename from src/basic/tokenizer/tk_pack.s rename to src/basic/editor/tk_pack.s index 99c27e12..26114fde 100644 --- a/src/basic/tokenizer/tk_pack.s +++ b/src/basic/editor/tk_pack.s @@ -26,14 +26,14 @@ tk_pack: - // Reuse the CPU stack - addresses below $102 are used by 'tokenise_line.s' + // Reuse the CPU stack - address $100 is 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 // 2 bytes for quick shortening of packed candidate - .label tk__nibble_flag = $106 // $00 = start from new byte, $FF = start from high nibble - .label tk__byte_offset = $107 // offset of the current byte (to place new data) in tk__packed + .label tk__len_unpacked = $101 // length of unpacked data; it could replace 'tk__nibble_flag', but at the cost of code size/performance + .label tk__shorten_bits = $102 // 2 bytes for quick shortening of packed candidate + .label tk__nibble_flag = $104 // $00 = start from new byte, $FF = start from high nibble + .label tk__byte_offset = $105 // offset of the current byte (to place new data) in tk__packed - .label tk__packed = $108 // packed candidate, 25 bytes is enough for worst case - a 16 byte keyword + .label tk__packed = $106 // packed candidate, 25 bytes is enough for worst case - a 16 byte keyword // Initialize variables diff --git a/src/basic/tokenizer/tk_search.s b/src/basic/editor/tk_search.s similarity index 100% rename from src/basic/tokenizer/tk_search.s rename to src/basic/editor/tk_search.s diff --git a/src/basic/tokenizer/tk_shorten.s b/src/basic/editor/tk_shorten.s similarity index 97% rename from src/basic/tokenizer/tk_shorten.s rename to src/basic/editor/tk_shorten.s index 1d3fe0a1..916a8f41 100644 --- a/src/basic/tokenizer/tk_shorten.s +++ b/src/basic/editor/tk_shorten.s @@ -31,9 +31,8 @@ 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 + bmi tk_shorten_1n_lo // FALLTROUGH @@ -45,7 +44,7 @@ tk_shorten_1n_hi: // tk__nibble_flag = $00, both nibbles fr sty tk__byte_offset lda tk__packed, y - and #$F0 + and #$0F sta tk__packed, y // Adjust remaining counters / flags, and quit @@ -78,7 +77,7 @@ tk_shorten_3n: // Now we need to check which nibble to cut away bit tk__nibble_flag - bcs tk_shorten_3n_2bytes + bmi tk_shorten_3n_2bytes // FALLTROUGH diff --git a/src/basic/tokenizer/tokenise_line.s b/src/basic/editor/tokenise_line.s similarity index 91% rename from src/basic/tokenizer/tokenise_line.s rename to src/basic/editor/tokenise_line.s index dc52f9de..960d5a92 100644 --- a/src/basic/tokenizer/tokenise_line.s +++ b/src/basic/editor/tokenise_line.s @@ -21,22 +21,20 @@ tokenise_line: #else - // Reuse the CPU stack - addresses above $101 are used by 'tk_pack.s' + // Reuse the CPU stack - addresses above $100 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? + .label tk__length = __tokenise_work1 // length of the raw string // Initialize variables lda #$00 sta tk__offset - ldx __tokenise_work1 - stx tk__length - // Terminate input string with $00 // XXX do we need this? + ldx tk__length sta BUF, x // FALLTROUGH @@ -123,23 +121,19 @@ tokenise_line_keyword_V2: sta BUF, x // Cut away unnecessary bytes - jsr tk_cut_away_1 // Special handling for REM command - after this one nothing more should be tokenised pla cmp #$0F // REM token index - jmp tokenise_line_loop + bne tokenise_line_loop // FALLTROUGH tokenise_line_done: - // Update line length and quit - - lda tk__length - sta __tokenise_work1 + // Quit rts diff --git a/src/basic/editor/update_vartab_do_clr.s b/src/basic/editor/update_vartab_do_clr.s new file mode 100644 index 00000000..e8f67a38 --- /dev/null +++ b/src/basic/editor/update_vartab_do_clr.s @@ -0,0 +1,47 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + +// +// Calculate the VARTAB value - to mark the end of BASIC program code. Also clears all variables. +// + +update_VARTAB_do_clr: + + lda TXTTAB+0 + sta OLDTXT+0 + lda TXTTAB+1 + sta OLDTXT+1 +!: + lda OLDTXT+0 + sta VARTAB+0 + lda OLDTXT+1 + sta VARTAB+1 + + jsr follow_link_to_next_line + + lda OLDTXT+0 + ora OLDTXT+1 + bne !- // branch if pointer not NULL + + // Correct VARTAB by 2 bytes NULL pointer + +#if HAS_OPCODES_65CE02 + + inw VARTAB + inw VARTAB + +#else + + clc + lda VARTAB+0 + adc #$02 + sta VARTAB+0 + bcc !+ + inc VARTAB+1 +!: +#endif + + // Call CLR to set-up remaining variables + + jmp do_clr diff --git a/src/basic/engine/do_error.s b/src/basic/engine/do_error.s index e916f016..e4979e86 100644 --- a/src/basic/engine/do_error.s +++ b/src/basic/engine/do_error.s @@ -196,4 +196,4 @@ do_basic_error: // error code in .X ldx #$FE txs - jmp basic_main_loop + jmp shell_main_loop diff --git a/src/basic/engine/do_new_clr_restore.s b/src/basic/engine/do_new_clr_restore.s new file mode 100644 index 00000000..3a249d2a --- /dev/null +++ b/src/basic/engine/do_new_clr_restore.s @@ -0,0 +1,103 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# M65 * #TAKE +// #LAYOUT# X16 BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + + +// See https://www.c64-wiki.com/wiki/Memory_(BASIC) for BASIC memory organization + + +do_new: + +#if (ROM_LAYOUT_M65 && SEGMENT_BASIC_0) + + jsr map_BASIC_1 + jsr_ind VB1__do_new + jmp map_NORMAL + +do_clr: + + jsr map_BASIC_1 + jsr_ind VB1__do_clr + jmp map_NORMAL + +do_restore: + + jsr map_BASIC_1 + jsr_ind VB1__do_restore + jmp map_NORMAL + +#else + + // See Computes Mapping the Commodore 64 - page 96 + + ldy #$00 + tya + sta (TXTTAB),y + iny + sta (TXTTAB),y + + // The book does not mention this, but IMHO it is necessary to initialize VARTAB here too + + clc + lda TXTTAB+0 + adc #$02 + sta VARTAB+0 + lda TXTTAB+1 + adc #$00 + sta VARTAB+1 + +do_clr: + + // See Computes Mapping the Commodore 64 - page 96 + + // XXX check on real ROMs that it is being done too when entering new line + jsr JCLALL + + lda MEMSIZ+0 + sta FRETOP+0 + lda MEMSIZ+1 + sta FRETOP+1 + + // The book is not precise here $31-$32 is definitely not the end of BASIC text + + lda VARTAB+0 + sta ARYTAB+0 + sta STREND+0 + + lda VARTAB+1 + sta ARYTAB+1 + sta STREND+1 + + // FALLTROUGH (confirmed on real C64 that CLR indeed resets DATPTR) + +do_restore: + + // See Computes Mapping the Commodore 64 - page 98 + // Initial DATPTR value checked on real C64 + +#if HAS_OPCODES_65CE02 + + lda TXTTAB+0 + sta DATPTR+0 + lda TXTTAB+1 + sta DATPTR+1 + + dew DATPTR + +#else + + sec + lda TXTTAB+0 + sbc #$01 + sta DATPTR+0 + lda TXTTAB+1 + sbc #$00 + sta DATPTR+1 + +#endif + + rts + + +#endif // ROM layout diff --git a/src/basic/engine/execute_line_statements.s b/src/basic/engine/execute_line_statements.s index 58b10a3d..51f34df4 100644 --- a/src/basic/engine/execute_line_statements.s +++ b/src/basic/engine/execute_line_statements.s @@ -164,7 +164,7 @@ execute_statements_end_of_line: ldx CURLIN+1 inx - beq_16 basic_main_loop + beq_16 shell_main_loop // Advance to the next line - first copy the line number // to previous line number @@ -177,11 +177,11 @@ execute_statements_end_of_line: // Advance the basic line pointer to the next line; if end, // go to the main loop - jsr basic_follow_link_to_next_line + jsr follow_link_to_next_line lda OLDTXT+0 ora OLDTXT+1 - beq_16 basic_main_loop + beq_16 shell_main_loop // FALLTROUGH @@ -191,7 +191,7 @@ execute_line: ldy #0 jsr peek_line_pointer_null_check - bcc_16 basic_main_loop // branch if end of program reached + bcc_16 shell_main_loop // branch if end of program reached // Skip pointer and line number to get address of first statement diff --git a/src/basic/basic_follow_link_to_next_line.s b/src/basic/engine/follow_link_to_next_line.s similarity index 94% rename from src/basic/basic_follow_link_to_next_line.s rename to src/basic/engine/follow_link_to_next_line.s index b0b4e57a..99899f20 100644 --- a/src/basic/basic_follow_link_to_next_line.s +++ b/src/basic/engine/follow_link_to_next_line.s @@ -2,7 +2,8 @@ // #LAYOUT# * BASIC_0 #TAKE // #LAYOUT# * * #IGNORE -basic_follow_link_to_next_line: +follow_link_to_next_line: + ldy #0 #if CONFIG_MEMORY_MODEL_60K @@ -28,4 +29,5 @@ basic_follow_link_to_next_line: sta OLDTXT+1 pla sta OLDTXT+0 + rts diff --git a/src/basic/engine/prepare_direct_execution.s b/src/basic/engine/prepare_direct_execution.s new file mode 100644 index 00000000..1b2b1bb0 --- /dev/null +++ b/src/basic/engine/prepare_direct_execution.s @@ -0,0 +1,27 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + +// +// Prepare interpreter to execute commands in direct mode +// + +prepare_direct_execution: + + // Setup pointer to the statement + lda #BUF + sta TXTPTR+1 + + // There is no stored line, so zero that pointer out + lda #$00 + sta OLDTXT+0 + sta OLDTXT+1 + + // Put invalid line number in current line number value, + // so that we know we are in direct mode (Computes Mapping the 64 p19) + lda #$FF + sta CURLIN+1 + + rts diff --git a/src/basic/select_device.s b/src/basic/engine/select_device.s similarity index 100% rename from src/basic/select_device.s rename to src/basic/engine/select_device.s diff --git a/src/basic/init/basic_warm_start_internal.s b/src/basic/init/basic_warm_start_internal.s index 75fc68c5..81ff572c 100644 --- a/src/basic/init/basic_warm_start_internal.s +++ b/src/basic/init/basic_warm_start_internal.s @@ -27,4 +27,4 @@ basic_warm_start_real: lda CMP0+0 jsr print_hex_byte !: - jmp basic_main_loop + jmp shell_main_loop diff --git a/src/basic/init/initmsg_bytes_free.s b/src/basic/init/initmsg_bytes_free.s index b277340f..ed8db16f 100644 --- a/src/basic/init/initmsg_bytes_free.s +++ b/src/basic/init/initmsg_bytes_free.s @@ -10,7 +10,59 @@ initmsg_bytes_free: - jsr basic_do_new + // Setup pointers to BASIC memory storage + + lda #<$0801 + sta TXTTAB+0 + lda #>$0801 + sta TXTTAB+1 + + // Get top of memory. + // If a cart is present, then it is $7FFF, + // if not, then it is $F7FF, since we support having + // programs and variables under ROM. + // i.e., we support 56KB RAM for BASIC, keeping some space + // free for some optimisation structures, e.g., FOR/NEXT loop + // records (without them going on the stack), GOSUB stack + // (same story), expression value cache? + + sec // Read, not write value + jsr JMEMTOP + cpx #$80 + beq !+ +#if CONFIG_MEMORY_MODEL_60K + lda #>$F7FF +#elif CONFIG_MEMORY_MODEL_50K + lda #>$D000 +#elif CONFIG_MEMORY_MODEL_46K + lda #>$C000 +#else // CONFIG_MEMORY_MODEL_38K + lda #>$A000 +#endif + skip_2_bytes_trash_nvz +!: + lda #>$8000 + sta MEMSIZ+1 +#if CONFIG_MEMORY_MODEL_60K + cpx #$80 + beq !+ + lda #$FF + skip_2_bytes_trash_nvz +!: + lda #<$00 +#else // CONFIG_MEMORY_MODEL_38K || CONFIG_MEMORY_MODEL_46K || CONFIG_MEMORY_MODEL_50K + lda #<$00 +#endif + sta MEMSIZ+0 + + // XXX move the part above to a more suitable place + + // Initialize other variables by performing NEW + + jsr do_new + + // Print number of bytes free + sec lda MEMSIZ+0 sbc TXTTAB+0 @@ -20,6 +72,5 @@ initmsg_bytes_free: jsr print_integer - // Print rest of the start up message ldx #IDX__STR_BYTES jmp print_packed_misc_str diff --git a/src/basic/memory/shift_mem_adapt_pointers.s b/src/basic/memory/shift_mem_adapt_pointers.s new file mode 100644 index 00000000..072615a3 --- /dev/null +++ b/src/basic/memory/shift_mem_adapt_pointers.s @@ -0,0 +1,36 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + +// +// Helper routine for memory copy. Moves down the memmove_src and memmove_dst pointers to compensate for .Y not being 0 +// + + +shift_mem_adapt_pointers: + + sty RESHO+4 + + // Adapt source pointer + + sec + lda memmove__src+0 + sbc RESHO+4 + sta memmove__src+0 + bcs !+ + dec memmove__src+1 +!: + // Adapt destination pointer + + sec + lda memmove__dst+0 + sbc RESHO+4 + sta memmove__dst+0 + bcs !+ + dec memmove__dst+1 +!: + // Adapt data size + + inc memmove__size+1 + + rts diff --git a/src/basic/memory/shift_mem_down.s b/src/basic/memory/shift_mem_down.s new file mode 100644 index 00000000..7a6e41df --- /dev/null +++ b/src/basic/memory/shift_mem_down.s @@ -0,0 +1,59 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + + +// +// Copies the memory down +// +// Input: +// - memmove__src - pointer to the first byte of source +// - memmove__dst - pointer to the first byte of destination +// - memmove__size - number of bytes to copy +// +// All the input variables, .A, and .Y register, are destroyed. Also uses RESHO+4 variable. +// + + +shift_mem_down: + + // The copy routine is simplified, so that it is fast and (for memory model 60K) + // can fit in a small RAM area - therefore it requires to pre-set .Y, so that + // the copy ends when it reaches 0; so if copying one byte we want .Y = $FF + + sec + lda #$00 + sbc memmove__size+0 + tay + + // Now we have to move down the memmove_src and memmove_dst pointers to compensate for .Y not being 0 + + jsr shift_mem_adapt_pointers + +#if CONFIG_MEMORY_MODEL_38K + +shift_mem_down_internal: + + // Move memmove__size bytes from memmove__src to memmove__dst, where memmove__dst > memmove__src + // + // This means we have to copy from the back end down. + // This routine assumes the pointers are already pointed to the end of the areas, and that .Y is correctly initialized + + php +!: + lda (memmove__src),y + sta (memmove__dst),y + iny + bne !- + inc memmove__src+1 + inc memmove__dst+1 + dec memmove__size+1 + bne !- + plp + rts + +#else + + jmp shift_mem_down_internal + +#endif diff --git a/src/basic/memory/shift_mem_down_38k.s b/src/basic/memory/shift_mem_down_38k.s deleted file mode 100644 index 471ac56b..00000000 --- a/src/basic/memory/shift_mem_down_38k.s +++ /dev/null @@ -1,30 +0,0 @@ -// #LAYOUT# STD * #TAKE -// #LAYOUT# * BASIC_0 #TAKE -// #LAYOUT# * * #IGNORE - - -#if CONFIG_MEMORY_MODEL_38K - -shift_mem_down: - - // Move __memmove_size bytes from __memmove_src to __memmove_dst, - // where __memmove_dst > __memmove_src - // This means we have to copy from the back end down. - // This routine assumes the pointers are already pointed - // to the end of the areas, and that Y is correctly initialised - // to allow the copy to begin. - - php -!: - lda (__memmove_src),y - sta (__memmove_dst),y - iny - bne !- - inc __memmove_src+1 - inc __memmove_dst+1 - dec __memmove_size+1 - bne !- - plp - rts - -#endif diff --git a/src/basic/memory/shift_mem_up.s b/src/basic/memory/shift_mem_up.s new file mode 100644 index 00000000..320c01db --- /dev/null +++ b/src/basic/memory/shift_mem_up.s @@ -0,0 +1,56 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + + +// +// Copies the memory up +// +// Input: +// - memmove__src - pointer to the last byte of source +// - memmove__dst - pointer to the last byte of destination +// - memmove__size - number of bytes to copy +// +// All the input variables, .A, and .Y register, are destroyed. Also uses RESHO+4 variable. +// + + +shift_mem_up: + + // The copy routine is simplified, so that it is fast and (for memory model 60K) + // can fit in a small RAM area - therefore it requires to pre-set .Y, so that + // the copy ends when it reaches 0; so if copying one byte we want .Y = $01 + + ldy memmove__size+0 + + // Now we have to move down the memmove_src and memmove_dst pointers to compensate for .Y not being 0 + + jsr shift_mem_adapt_pointers + +#if CONFIG_MEMORY_MODEL_38K + +shift_mem_up_internal: + + // Move memmove__size bytes from memmove__src to memmove__dst, where memmove__dst > memmove__src + // + // This means we have to copy from the back end down. + // This routine assumes the pointers are already pointed to the end of the areas, and that .Y is correctly initialized + + php +!: + lda (memmove__src),y + sta (memmove__dst),y + dey + bne !- + dec memmove__src+1 + dec memmove__dst+1 + dec memmove__size+1 + bne !- + plp + rts + +#else + + jmp shift_mem_up_internal + +#endif diff --git a/src/basic/memory/shift_mem_up_38k.s b/src/basic/memory/shift_mem_up_38k.s deleted file mode 100644 index c8fab14b..00000000 --- a/src/basic/memory/shift_mem_up_38k.s +++ /dev/null @@ -1,30 +0,0 @@ -// #LAYOUT# STD * #TAKE -// #LAYOUT# * BASIC_0 #TAKE -// #LAYOUT# * * #IGNORE - - -#if CONFIG_MEMORY_MODEL_38K - -shift_mem_up: - - // Move __memmove_size bytes from __memmove_src to __memmove_dst, - // where __memmove_dst > __memmove_src - // This means we have to copy from the back end down. - // This routine assumes the pointers are already pointed - // to the end of the areas, and that Y is correctly initialised - // to allow the copy to begin. - - php -!: - lda (__memmove_src),y - sta (__memmove_dst),y - dey - bne !- - dec __memmove_src+1 - dec __memmove_dst+1 - dec __memmove_size+1 - bne !- - plp - rts - -#endif diff --git a/src/basic/memory/under_rom_routines_46k_50k.s b/src/basic/memory/under_rom_routines_46k_50k.s index 7ceb7a84..261ae7da 100644 --- a/src/basic/memory/under_rom_routines_46k_50k.s +++ b/src/basic/memory/under_rom_routines_46k_50k.s @@ -70,14 +70,12 @@ peek_under_roms_via_FAC1_PLUS_1: -shift_mem_up: +shift_mem_up_internal: - // Move __memmove_size bytes from __memmove_src to __memmove_dst, - // where __memmove_dst > __memmove_src + // Move memmove__size bytes from memmove__src to memmove__dst, where memmove__dst > memmove__src + // // This means we have to copy from the back end down. - // This routine assumes the pointers are already pointed - // to the end of the areas, and that Y is correctly initialised - // to allow the copy to begin. + // This routine assumes the pointers are already pointed to the end of the areas, and that .Y is correctly initialized // Unmap BASIC lower ROM @@ -88,30 +86,28 @@ shift_mem_up: // Perform the copying !: - lda (__memmove_src),y - sta (__memmove_dst),y + lda (memmove__src),y + sta (memmove__dst),y dey bne !- - dec __memmove_src+1 - dec __memmove_dst+1 - dec __memmove_size+1 + dec memmove__src+1 + dec memmove__dst+1 + dec memmove__size+1 bne !- #if HAS_OPCODES_65C02 - bra shift_mem_finalize + bra shift_mem_internal_finalize #else - jmp shift_mem_finalize + jmp shift_mem_internal_finalize #endif -shift_mem_down: +shift_mem_down_internal: - // Move __memmove_size bytes from __memmove_src to __memmove_dst, - // where __memmove_dst > __memmove_src + // Move memmove__size bytes from memmove__src to memmove__dst, where memmove__dst > memmove__src + // // This means we have to copy from the back end down. - // This routine assumes the pointers are already pointed - // to the end of the areas, and that Y is correctly initialised - // to allow the copy to begin. + // This routine assumes the pointers are already pointed to the end of the areas, and that .Y is correctly initialized // Unmap BASIC lower ROM @@ -122,17 +118,18 @@ shift_mem_down: // Perform the copying !: - lda (__memmove_src),y - sta (__memmove_dst),y + lda (memmove__src),y + sta (memmove__dst),y iny bne !- - inc __memmove_src+1 - inc __memmove_dst+1 - dec __memmove_size+1 + inc memmove__src+1 + inc memmove__dst+1 + dec memmove__size+1 bne !- + // FALLTROUGH -shift_mem_finalize: +shift_mem_internal_finalize: // Restore memory mapping and quit diff --git a/src/basic/memory/under_rom_routines_60k.s b/src/basic/memory/under_rom_routines_60k.s index 0a07dbc7..a3b72346 100644 --- a/src/basic/memory/under_rom_routines_60k.s +++ b/src/basic/memory/under_rom_routines_60k.s @@ -70,44 +70,44 @@ memmap_allram_routine: rts shift_mem_up_routine: - // Move __memmove_size bytes from __memmove_src to __memmove_dst, - // where __memmove_dst > __memmove_src + + // Move memmove__size bytes from memmove__src to memmove__dst, where memmove__dst > memmove__src + // // This means we have to copy from the back end down. - // This routine assumes the pointers are already pointed - // to the end of the areas, and that Y is correctly initialised - // to allow the copy to begin. + // This routine assumes the pointers are already pointed to the end of the areas, and that .Y is correctly initialized + php jsr memmap_allram -smu1: - lda (__memmove_src),y - sta (__memmove_dst),y +!: + lda (memmove__src),y + sta (memmove__dst),y dey - bne smu1 - dec __memmove_src+1 - dec __memmove_dst+1 - dec __memmove_size+1 - bne smu1 + bne !- + dec memmove__src+1 + dec memmove__dst+1 + dec memmove__size+1 + bne !- plp jmp memmap_normal shift_mem_down_routine: - // Move __memmove_size bytes from __memmove_src to __memmove_dst, - // where __memmove_dst > __memmove_src + + // Move memmove__size bytes from memmove__src to memmove__dst, where memmove__dst > memmove__src + // // This means we have to copy from the back end down. - // This routine assumes the pointers are already pointed - // to the end of the areas, and that Y is correctly initialised - // to allow the copy to begin. + // This routine assumes the pointers are already pointed to the end of the areas, and that .Y is correctly initialized + php jsr memmap_allram -smd1: - lda (__memmove_src),y - sta (__memmove_dst),y +!: + lda (memmove__src),y + sta (memmove__dst),y iny - bne smd1 - inc __memmove_src+1 - inc __memmove_dst+1 - dec __memmove_size+1 - bne smd1 + bne !- + inc memmove__src+1 + inc memmove__dst+1 + dec memmove__size+1 + bne !- plp jmp memmap_normal diff --git a/src/basic/print/list_single_line.s b/src/basic/print/list_single_line.s index a97077b5..65581a6e 100644 --- a/src/basic/print/list_single_line.s +++ b/src/basic/print/list_single_line.s @@ -67,7 +67,9 @@ list_print_loop: lda (OLDTXT),y #endif - beq list_end_of_line + bne !+ + rts // end of line +!: cmp #$22 bne list_not_quote lda QTSW @@ -118,6 +120,10 @@ list_display_token_CC: // Subtract $1 from token to get offset in token list dex + // Check if token is known + cpx #TK__MAXTOKEN_keywords_CC + bcs list_display_unknown_token // XXX consider displaying two question marks here + // Now ask for it to be printed jsr print_packed_keyword_CC @@ -156,6 +162,10 @@ list_display_token_CD: // Subtract $1 from token to get offset in token list dex + // Check if token is known + cpx #TK__MAXTOKEN_keywords_CD + bcs list_display_unknown_token // XXX consider displaying two question marks here + // Now ask for it to be printed jsr print_packed_keyword_CD @@ -174,9 +184,13 @@ list_display_token_V2: // Subtract $80 from token to get offset in token list txa - and #$7f + and #$7F tax + // Check if token is known + cpx #TK__MAXTOKEN_keywords_V2 + bcs list_display_unknown_token + // Now ask for it to be printed jsr print_packed_keyword_V2 @@ -188,12 +202,14 @@ list_token_displayed: ply_trash_a pla - cmp #$8f + cmp #$8F bne list_not_rem // REM command locks quote flag on until the end of the line, allowing // funny characters in REM statements without problem. sta QTSW // Any value other than $00 or $FF will lock quote mode on, so the token value of REM is fine here - // FALL THROUGH + + // FALLTHROUGH + list_not_rem: iny @@ -205,13 +221,52 @@ list_is_pi: // FALLTROUGH list_is_literal: - jsr JCHROUT - iny - bne list_print_loop - -list_end_of_line: - - rts + pha + bit QTSW + bpl list_is_literal_known // in quote mode every character is allowed + and #$7F + cmp #$12 + beq list_is_literal_known // enabling/disabling reverse mode is allowed + and #%01100000 + bne list_is_literal_known + pla + + // FALLTROUGH + +list_is_unknown: + + phy_trash_a + lda #str_rev_question + jsr STROUT + ply_trash_a + +#if HAS_OPCODES_65C02 + bra !+ +#else + jmp !+ +#endif + +list_is_literal_known: + + pla + jsr JCHROUT +!: + iny + bne_16 list_print_loop + + rts // end of line + +list_display_unknown_token: + + pla + pla + +#if HAS_OPCODES_65C02 + bra list_is_unknown +#else + jmp list_is_unknown +#endif #endif // ROM layout diff --git a/src/basic/wedge/wedge_dos.s b/src/basic/wedge/wedge_dos.s index 0e9b5d3d..c8fb1a48 100644 --- a/src/basic/wedge/wedge_dos.s +++ b/src/basic/wedge/wedge_dos.s @@ -96,7 +96,7 @@ wedge_dos_change_drive: jmp do_ILLEGAL_DEVICE_NUMBER_error !: sta FA - jmp basic_main_loop + jmp shell_main_loop wedge_dos_status_get: @@ -238,7 +238,7 @@ wedge_dos_directory_display: wedge_dos_clean_exit: jsr JCLALL - jmp basic_main_loop + jmp shell_main_loop wedge_dos_basic_error: pha diff --git a/src/basic/wedge/wedge_tape.s b/src/basic/wedge/wedge_tape.s index ac89415d..951ced7e 100644 --- a/src/basic/wedge/wedge_tape.s +++ b/src/basic/wedge/wedge_tape.s @@ -6,20 +6,21 @@ #if CONFIG_TAPE_WEDGE -// .X has to contain size of the buffer - wedge_tape: - lda #$FF - sta CURLIN+1 // in case of error do not print line number + // Prepare for execution - cpx #$02 - bne_16 do_SYNTAX_error + jsr prepare_direct_execution + jsr fetch_character - lda BUF+1 + // First character is a 'left arrow', we can ignore it - determine the command + + jsr fetch_character cmp #$4C // 'L' beq wedge_arrow_L + cmp #$4D // 'M' + beq wedge_arrow_M #if CONFIG_TAPE_HEAD_ALIGN @@ -30,31 +31,18 @@ wedge_tape: jmp do_SYNTAX_error -wedge_arrow_L: - - // Execute 'arrow + L' - - lda #$00 - sta FNLEN // default file name is empty - sta VERCKB // operation is LOAD, not VERIFY - - ldy #$01 -#if CONFIG_TAPE_TURBO - ldx #$07 // turbo tape device -#else - ldx #$01 // normal tape device -#endif - - jsr JSETFLS - - // Perform loading - - jmp cmd_load_got_params - #if CONFIG_TAPE_HEAD_ALIGN wedge_arrow_H: + // Make sure the syntax is correct + + jsr injest_spaces + jsr fetch_character + + cmp #$00 + bne_16 do_SYNTAX_error + jsr tape_head_align jsr print_return @@ -62,5 +50,23 @@ wedge_arrow_H: #endif +wedge_arrow_L: + + jsr wedge_tape_prepare_load + + // Perform loading + + jmp cmd_load_got_params + +wedge_arrow_M: + + jsr wedge_tape_prepare_load + ldy #$00 + sty SA // for MERGE secondary address has to be 0! + + // Perform merging + + jmp cmd_merge_got_params + #endif // CONFIG_TAPE_WEDGE diff --git a/src/basic/wedge/wedge_tape_prepare_load.s b/src/basic/wedge/wedge_tape_prepare_load.s new file mode 100644 index 00000000..cac50b40 --- /dev/null +++ b/src/basic/wedge/wedge_tape_prepare_load.s @@ -0,0 +1,68 @@ +// #LAYOUT# STD * #TAKE +// #LAYOUT# * BASIC_0 #TAKE +// #LAYOUT# * * #IGNORE + + +#if CONFIG_TAPE_WEDGE + +// +// Common part for LOAD and MERGE operations via tape wedge +// + +wedge_tape_prepare_load: + + // Make sure the syntax is correct + + jsr injest_spaces + jsr fetch_character + + cmp #$00 + beq wedge_tape_prepare_load_no_filename // branch if no file name given + cmp #$22 + bne_16 do_SYNTAX_error // branch if no opening quote + + // Fetch the file name + + lda TXTPTR+0 + sta FNADDR+0 + lda TXTPTR+1 + sta FNADDR+1 + + ldx #$00 +!: + jsr fetch_character + + cmp #$00 + beq !+ + cmp #$22 + beq !+ + + inx + bne !- +!: + stx FNLEN + + lda #$00 + beq wedge_tape_prepare_load_got_filename + +wedge_tape_prepare_load_no_filename: + + sta FNLEN // .A is 0 here, default file name is empty + + // FALLTROUGH + +wedge_tape_prepare_load_got_filename: // .A has to be 0 + + sta VERCKB // operation is LOAD, not VERIFY + + ldy #$01 +#if CONFIG_TAPE_TURBO + ldx #$07 // turbo tape device +#else + ldx #$01 // normal tape device +#endif + + jmp JSETFLS + + +#endif // CONFIG_TAPE_WEDGE diff --git a/src/kernal/tape/tape_common.s b/src/kernal/tape/tape_common.s index f254dc43..e46ce6b1 100644 --- a/src/kernal/tape/tape_common.s +++ b/src/kernal/tape/tape_common.s @@ -282,10 +282,20 @@ tape_match_loop: cmp #KEY_ASTERISK beq tape_return_ok + iny + iny + iny + iny + iny + cmp (TAPE1), y bne tape_return_not_ok - iny + dey + dey + dey + dey + cpy #$10 bne tape_match_loop diff --git a/strings/0001020304 b/strings/0001020304 new file mode 100644 index 00000000..a295016a --- /dev/null +++ b/strings/0001020304 @@ -0,0 +1,3 @@ +sequence 0, 1, 2, 3, 4 + +Trivial sequence of bytes. Not copyrightable. diff --git a/strings/008526852785288529 b/strings/008526852785288529 new file mode 100644 index 00000000..2eaf0a02 --- /dev/null +++ b/strings/008526852785288529 @@ -0,0 +1,3 @@ +$00 + STA $26 + STA $27 + STA $28 + STA $29 (temporary variables) + +Couple of instructions settin 0 into temporary zeropage variables. Trivial - not copyrightable. diff --git a/strings/01020304 b/strings/01020304 new file mode 100644 index 00000000..8223fcbb --- /dev/null +++ b/strings/01020304 @@ -0,0 +1,3 @@ +sequence 1, 2, 3, 4 + +Trivial sequence of bytes. Not copyrightable. diff --git a/strings/0320FEED b/strings/0320FEED new file mode 100644 index 00000000..add9e8af --- /dev/null +++ b/strings/0320FEED @@ -0,0 +1,3 @@ +BCS (offset matches) + JSR (address only partially matches) + +Instruction + some part. Trivial - not copyrightable. diff --git a/strings/07C922F0 b/strings/07C922F0 new file mode 100644 index 00000000..edabb3cc --- /dev/null +++ b/strings/07C922F0 @@ -0,0 +1,3 @@ +$07 + CMP #$22 + BEQ (offset does not match) + +Instruction + some parts. Trivial - not copyrightable. diff --git a/strings/18A52B69 b/strings/18A52B69 new file mode 100644 index 00000000..f7c7810c --- /dev/null +++ b/strings/18A52B69 @@ -0,0 +1,3 @@ +CLC + LDA $2B + ADC #(value does not match) + +Small part of code performing addition with a given zeropage variable. Trivial - not copyrightable. diff --git a/strings/20E7FFA537 b/strings/20E7FFA537 new file mode 100644 index 00000000..898666f0 --- /dev/null +++ b/strings/20E7FFA537 @@ -0,0 +1,3 @@ +JSR $FFE7 + LDA $37 + +Calling a documented routine (CLALL) and fetching a byte from zeropage variable. Trivial - not copyrightable. diff --git a/strings/2C382099FF b/strings/2C382099FF new file mode 100644 index 00000000..3adeb7a2 --- /dev/null +++ b/strings/2C382099FF @@ -0,0 +1,3 @@ +$2C + SEC + JSR $FF99 (MEMTOP) + +Call of a well-known routine, following operation type set and some part. Trivial - not copyrightable. diff --git a/strings/38A52BE901 b/strings/38A52BE901 new file mode 100644 index 00000000..37a90188 --- /dev/null +++ b/strings/38A52BE901 @@ -0,0 +1,3 @@ +SEC + LDA $2B + SBC #$01 + +Small part of code performing subtraction from a given zeropage variable. Trivial - not copyrightable. diff --git a/strings/60A561F0 b/strings/60A561F0 new file mode 100644 index 00000000..e1c4eb54 --- /dev/null +++ b/strings/60A561F0 @@ -0,0 +1,3 @@ +RTS + LDA $61 + BEQ (offset does not match) + +End of pprevious routine + a new one starting from checking, whether zeropage variable is 0 or not. Trivial - not copyrightable. diff --git a/strings/60C922D00B b/strings/60C922D00B new file mode 100644 index 00000000..ba46a79a --- /dev/null +++ b/strings/60C922D00B @@ -0,0 +1,3 @@ +RTS + CMP #$22 + BNE (offset matches) + +End of the routine + beginning of a new one, which starts by checking for a particular character. Not creative - not copyrightable. diff --git a/strings/6902852D b/strings/6902852D new file mode 100644 index 00000000..db6b0ea3 --- /dev/null +++ b/strings/6902852D @@ -0,0 +1,3 @@ +ADC #$02 + STA $2D + +Add 2, store value in zeropage variable. Trivial - not copyrightable. diff --git a/strings/6902852DA52C6900852E20 b/strings/6902852DA52C6900852E20 new file mode 100644 index 00000000..4e0943b8 --- /dev/null +++ b/strings/6902852DA52C6900852E20 @@ -0,0 +1,3 @@ +ADC #$02 + STA $2D + LDA $2C + ADC #$00 + STA $2E + JSR (address does not match) + +Code calculatin VARTAB value from TXTTAB - by adding 2. Trivial - not copyrightable. diff --git a/strings/A200A000 b/strings/A200A000 new file mode 100644 index 00000000..c840f00c --- /dev/null +++ b/strings/A200A000 @@ -0,0 +1,3 @@ +LDX #$00 + LDY #$00 + +Load $00 into both index registers. Trivial - not copyrightable. diff --git a/strings/A56160A5 b/strings/A56160A5 new file mode 100644 index 00000000..0e9cf4a5 --- /dev/null +++ b/strings/A56160A5 @@ -0,0 +1,3 @@ +LDA $61 + RTS + LDA (zeropage address does not match) + +One routine ending with returning a zeropage variable, another one starting with fetching data to acccumulator (not even the same address). Trivial - not copyrightable. diff --git a/strings/B9903C20 b/strings/B9903C20 new file mode 100644 index 00000000..2bb1fa38 --- /dev/null +++ b/strings/B9903C20 @@ -0,0 +1,3 @@ +$B9 + BCC (offset matches) + JSR (addres does not match) + +Instruction + some parts. Trivial - not copyrightable. diff --git a/strings/C901F008C903 b/strings/C901F008C903 new file mode 100644 index 00000000..ff92a76f --- /dev/null +++ b/strings/C901F008C903 @@ -0,0 +1,3 @@ +CMP #$01 + BEQ (offset matches) + CMP #$03 + +Two decisions based on accumulator value (second not even the same one). Trivial - not copyrightable. diff --git a/strings/C922D008A5D449 b/strings/C922D008A5D449 new file mode 100644 index 00000000..e175dea4 --- /dev/null +++ b/strings/C922D008A5D449 @@ -0,0 +1,3 @@ +CMP #$22 + BNE (offset matches) + LDA $D4 (QTSW) + EOR #(value does not match) + +Small piece of code handling QTSW flag - telling whether we are in the quote mode. Nothing complicated here - not creative, not copyrightable. diff --git a/strings/FFFF0000 b/strings/FFFF0000 new file mode 100644 index 00000000..893fe180 --- /dev/null +++ b/strings/FFFF0000 @@ -0,0 +1,3 @@ +$FF, $FF, $00, $00 + +Sequence of bytes being similar most likely fue to ROM not being filled in completely. Trivial - not copyrightable. diff --git a/tools/generate_strings.cc b/tools/generate_strings.cc index 790aaa8a..bfbd08e0 100644 --- a/tools/generate_strings.cc +++ b/tools/generate_strings.cc @@ -1,5 +1,5 @@ // -// Utility to generate compressed messages and BASIC tokens +// Utility to generate compressed messages and BASIC keywords // #include "common.h" @@ -22,31 +22,32 @@ std::string CMD_outFile = "out.s"; std::string CMD_cnfFile = ""; // -// Type definition for text/tokens to generate +// Type definition for strings/keywords to generate // typedef struct StringEntry { - bool enabledSTD; // whether enabled for basic build - bool enabledM65; // whether enabled for Mega65 build - bool enabledX16; // whether enabled for Commander X16 build - std::string alias; // alias, for the assembler - std::string string; // string/token - uint8_t abbrevLen = 0; // length of token abbreviation + bool enabledSTD; // whether enabled for standard build + bool enabledM65; // whether enabled for Mega 65 build + bool enabledU64; // whether enabled for Ultimate 64 build + bool enabledX16; // whether enabled for Commander X16 build + std::string alias; // alias, for the assembler + std::string string; // string/token + uint8_t abbrevLen = 0; // length of token abbreviation } StringEntry; enum class ListType { - KEYWORDS, - STRINGS_BASIC, - DICTIONARY // for internal use only + KEYWORDS, + STRINGS_BASIC, + DICTIONARY // for internal use only }; typedef struct StringEntryList { - ListType type; - std::string name; - std::vector list; + ListType type; + std::string name; + std::vector list; } StringEntryList; typedef std::vector StringEncoded; @@ -83,157 +84,158 @@ typedef std::vector StringEncodedList; That website offers the page under the GFDL license, and we can therefore take the list under those license terms if we wish, should the need arise. - Note that in providing these justifications above, we do not in any way acknowledge that + Note that in providing these justifications above, we do not in any way acknowledge that without these conditions that the BASIC keyword list could be copyright, but provide this - information as a further layer of defence against any claims that it could somehow + information as a further layer of defence against any claims that it could somehow infringe on the copyrights of the C64/C65/C128 etc ROMs. */ const StringEntryList GLOBAL_Keywords_V2 = { ListType::KEYWORDS, "keywords_V2", { - // STD M65 X16 - { true, true, true, "KV2_80", "END", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_81", "FOR", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_82", "NEXT", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_83", "DATA", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_84", "INPUT#", 2 }, // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php - { true, true, true, "KV2_85", "INPUT", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_86", "DIM", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_87", "READ", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_88", "LET", 2 }, // https://en.wikipedia.org/wiki/Atari_BASIC - { true, true, true, "KV2_89", "GOTO", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_8A", "RUN", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_8B", "IF", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_8C", "RESTORE", 3 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_8D", "GOSUB", 3 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_8E", "RETURN", 3 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_8F", "REM", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_90", "STOP", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_91", "ON", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_92", "WAIT", 2 }, // http://www.picaxe.com/BASIC-Commands/Time-Delays/wait/ - { true, true, true, "KV2_93", "LOAD", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_94", "SAVE", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_95", "VERIFY", 2 }, // https://en.wikipedia.org/wiki/Sinclair_BASIC - { true, true, true, "KV2_96", "DEF", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_97", "POKE", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_98", "PRINT#", 2 }, // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php - { true, true, true, "KV2_99", "PRINT", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_9A", "CONT", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_9B", "LIST", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_9C", "CLR", 2 }, // Apple I Replica Creation: Back to the Garage, p125 - { true, true, true, "KV2_9D", "CMD", 2 }, // https://en.wikipedia.org/wiki/List_of_DOS_commands - { true, true, true, "KV2_9E", "SYS", 2 }, // https://www.lifewire.com/dos-commands-4070427 - { true, true, true, "KV2_9F", "OPEN", 2 }, // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php - // STD M65 X16 - { true, true, true, "KV2_A0", "CLOSE", 3 }, // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php - { true, true, true, "KV2_A1", "GET", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_A2", "NEW", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_A3", "TAB(", 2 }, // http://www.antonis.de/qbebooks/gwbasman/tab.html - { true, true, true, "KV2_A4", "TO", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_A5", "FN", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_A6", "SPC(", 2 }, // http://www.antonis.de/qbebooks/gwbasman/spc.html - { true, true, true, "KV2_A7", "THEN", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_A8", "NOT", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_A9", "STEP", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_AA", "+", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_AB", "-", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_AC", "*", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_AD", "/", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_AE", "^", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_AF", "AND", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_B0", "OR", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_B1", ">", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_B2", "=", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_B3", "<", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_B4", "SGN", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_B5", "INT", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_B6", "ABS", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_B7", "USR", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_B8", "FRE", 2 }, // http://www.antonis.de/qbebooks/gwbasman/fre.html - { true, true, true, "KV2_B9", "POS", 0 }, // http://www.antonis.de/qbebooks/gwbasman/pos.html - { true, true, true, "KV2_BA", "SQR", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_BB", "RND", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_BC", "LOG", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_BD", "EXP", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_BE", "COS", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_BF", "SIN", 2 }, // https://www.landsnail.com/a2ref.htm - // STD M65 X16 - { true, true, true, "KV2_C0", "TAN", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_C1", "ATN", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_C2", "PEEK", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_C3", "LEN", 0 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_C4", "STR$", 3 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_C5", "VAL", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_C6", "ASC", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_C7", "CHR$", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_C8", "LEFT$", 3 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_C9", "RIGHT$", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_CA", "MID$", 2 }, // https://www.landsnail.com/a2ref.htm - { true, true, true, "KV2_CB", "GO", 0 }, // https://en.wikipedia.org/wiki/Goto + // STD M65 U64 X16 + { true, true, true, true, "KV2_80", "END", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_81", "FOR", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_82", "NEXT", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_83", "DATA", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_84", "INPUT#", 2 }, // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php + { true, true, true, true, "KV2_85", "INPUT", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_86", "DIM", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_87", "READ", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_88", "LET", 2 }, // https://en.wikipedia.org/wiki/Atari_BASIC + { true, true, true, true, "KV2_89", "GOTO", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_8A", "RUN", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_8B", "IF", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_8C", "RESTORE", 3 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_8D", "GOSUB", 3 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_8E", "RETURN", 3 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_8F", "REM", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_90", "STOP", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_91", "ON", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_92", "WAIT", 2 }, // http://www.picaxe.com/BASIC-Commands/Time-Delays/wait/ + { true, true, true, true, "KV2_93", "LOAD", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_94", "SAVE", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_95", "VERIFY", 2 }, // https://en.wikipedia.org/wiki/Sinclair_BASIC + { true, true, true, true, "KV2_96", "DEF", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_97", "POKE", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_98", "PRINT#", 2 }, // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php + { true, true, true, true, "KV2_99", "PRINT", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_9A", "CONT", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_9B", "LIST", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_9C", "CLR", 2 }, // Apple I Replica Creation: Back to the Garage, p125 + { true, true, true, true, "KV2_9D", "CMD", 2 }, // https://en.wikipedia.org/wiki/List_of_DOS_commands + { true, true, true, true, "KV2_9E", "SYS", 2 }, // https://www.lifewire.com/dos-commands-4070427 + { true, true, true, true, "KV2_9F", "OPEN", 2 }, // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php + // STD M65 U64 X16 + { true, true, true, true, "KV2_A0", "CLOSE", 3 }, // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php + { true, true, true, true, "KV2_A1", "GET", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_A2", "NEW", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_A3", "TAB(", 2 }, // http://www.antonis.de/qbebooks/gwbasman/tab.html + { true, true, true, true, "KV2_A4", "TO", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_A5", "FN", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_A6", "SPC(", 2 }, // http://www.antonis.de/qbebooks/gwbasman/spc.html + { true, true, true, true, "KV2_A7", "THEN", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_A8", "NOT", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_A9", "STEP", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_AA", "+", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_AB", "-", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_AC", "*", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_AD", "/", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_AE", "^", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_AF", "AND", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_B0", "OR", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_B1", ">", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_B2", "=", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_B3", "<", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_B4", "SGN", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_B5", "INT", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_B6", "ABS", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_B7", "USR", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_B8", "FRE", 2 }, // http://www.antonis.de/qbebooks/gwbasman/fre.html + { true, true, true, true, "KV2_B9", "POS", 0 }, // http://www.antonis.de/qbebooks/gwbasman/pos.html + { true, true, true, true, "KV2_BA", "SQR", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_BB", "RND", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_BC", "LOG", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_BD", "EXP", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_BE", "COS", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_BF", "SIN", 2 }, // https://www.landsnail.com/a2ref.htm + // STD M65 U64 X16 + { true, true, true, true, "KV2_C0", "TAN", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_C1", "ATN", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_C2", "PEEK", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_C3", "LEN", 0 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_C4", "STR$", 3 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_C5", "VAL", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_C6", "ASC", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_C7", "CHR$", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_C8", "LEFT$", 3 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_C9", "RIGHT$", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_CA", "MID$", 2 }, // https://www.landsnail.com/a2ref.htm + { true, true, true, true, "KV2_CB", "GO", 0 }, // https://en.wikipedia.org/wiki/Goto } }; // extended BASIC keywords - reserved for generic (hardware independent) BASIC commands const StringEntryList GLOBAL_Keywords_CC = { ListType::KEYWORDS, "keywords_CC", { - // STD M65 X16 - { true, true, true, "KCC_01", "MERGE", }, // OpenROMs specific, not present in CBM BASIC dialects - { true, true, true, "KCC_02", "BLOAD", }, // http://www.antonis.de/qbebooks/gwbasman/bload.html - { true, true, true, "KCC_03", "BSAVE", }, // http://www.antonis.de/qbebooks/gwbasman/bsave.html - { true, true, true, "KCC_04", "BVERIFY", }, + // STD M65 U64 X16 + { true, true, true, true, "KCC_01", "MERGE", }, // OpenROMs specific, not present in CBM BASIC dialects + { true, true, true, true, "KCC_02", "BLOAD", }, // http://www.antonis.de/qbebooks/gwbasman/bload.html + { true, true, true, true, "KCC_03", "BSAVE", }, // http://www.antonis.de/qbebooks/gwbasman/bsave.html + { true, true, true, true, "KCC_04", "BVERIFY", }, + { true, true, true, true, "KCC_05", "OLD", }, // OpenROMs specific, not present in CBM BASIC dialects } }; // extended BASIC keywords - reserved for BASIC commands likely making sense on most C64-compatible machines const StringEntryList GLOBAL_Keywords_CD = { ListType::KEYWORDS, "keywords_CD", { - // STD M65 X16 - { true, true, true, "KCD_01", "SLOW", }, // https://en.wikipedia.org/wiki/Sinclair_BASIC - ZX81 variant - { true, true, true, "KCD_02", "FAST", }, // https://en.wikipedia.org/wiki/Sinclair_BASIC - ZX81 variant + // STD M65 U64 X16 + { true, true, true, true, "KCD_01", "SLOW", }, // https://en.wikipedia.org/wiki/Sinclair_BASIC - ZX81 variant + { true, true, true, true, "KCD_02", "FAST", }, // https://en.wikipedia.org/wiki/Sinclair_BASIC - ZX81 variant } }; // extended BASIC keywords - reserved for board-specific BASIC commands const StringEntryList GLOBAL_Keywords_CE = { ListType::KEYWORDS, "keywords_CE", { - // STD M65 X16 - { false, false, false, "KCE_01", "TESTCMD", }, + // STD M65 U64 X16 + { false, false, false, false, "KCE_01", "TESTCMD", }, } }; const StringEntryList GLOBAL_Keywords_CF = { ListType::KEYWORDS, "keywords_CF", { - // STD M65 X16 - { false, false, false, "KCF_01", "TESTCMD", }, + // STD M65 U64 X16 + { false, false, false, false, "KCF_01", "TESTCMD", }, } }; // extended BASIC keywords - reserved for generic (hardware independent) BASIC functions const StringEntryList GLOBAL_Keywords_D0 = { ListType::KEYWORDS, "keywords_D0", { - // STD M65 X16 - { false, false, false, "KD0_01", "TESTFUN", }, + // STD M65 U64 X16 + { false, false, false, false, "KD0_01", "TESTFUN", }, } }; // extended BASIC keywords - reserved for BASIC functions likely making sense on most C64-compatible machines const StringEntryList GLOBAL_Keywords_D1 = { ListType::KEYWORDS, "keywords_D1", { - // STD M65 X16 - { false, false, false, "KD1_01", "TESTFUN", }, + // STD M65 U64 X16 + { false, false, false, false, "KD1_01", "TESTFUN", }, } }; // extended BASIC keywords - reserved for board-specific BASIC functions const StringEntryList GLOBAL_Keywords_D2 = { ListType::KEYWORDS, "keywords_D2", { - // STD M65 X16 - { false, false, false, "KD2_01", "TESTFUN", }, + // STD M65 U64 X16 + { false, false, false, false, "KD2_01", "TESTFUN", }, } }; const StringEntryList GLOBAL_Keywords_D3 = { ListType::KEYWORDS, "keywords_D3", { - // STD M65 X16 - { false, false, false, "KD3_01", "TESTFUN", }, + // STD M65 U64 X16 + { false, false, false, false, "KD3_01", "TESTFUN", }, } }; @@ -251,66 +253,66 @@ const StringEntryList GLOBAL_Keywords_D3 = { ListType::KEYWORDS, "keywords_D3", const StringEntryList GLOBAL_Errors = { ListType::STRINGS_BASIC, "errors", { - // STD M65 X16 --- error strings compatible with CBM BASIC V2 - { true, true, true, "EV2_01", "TOO MANY FILES" }, - { true, true, true, "EV2_02", "FILE OPEN" }, - { true, true, true, "EV2_03", "FILE NOT OPEN" }, - { true, true, true, "EV2_04", "FILE NOT FOUND" }, - { true, true, true, "EV2_05", "DEVICE NOT PRESENT" }, - { true, true, true, "EV2_06", "NOT INPUT FILE" }, - { true, true, true, "EV2_07", "NOT OUTPUT FILE" }, - { true, true, true, "EV2_08", "MISSING FILENAME" }, - { true, true, true, "EV2_09", "ILLEGAL DEVICE NUMBER" }, - { true, true, true, "EV2_0A", "NEXT WITHOUT FOR" }, - { true, true, true, "EV2_0B", "SYNTAX" }, - { true, true, true, "EV2_0C", "RETURN WITHOUT GOSUB" }, - { true, true, true, "EV2_0D", "OUT OF DATA" }, - { true, true, true, "EV2_0E", "ILLEGAL QUANTITY" }, - { true, true, true, "EV2_0F", "OVERFLOW" }, - { true, true, true, "EV2_10", "OUT OF MEMORY" }, - { true, true, true, "EV2_11", "UNDEF\'D STATEMENT" }, - { true, true, true, "EV2_12", "BAD SUBSCRIPT" }, - { true, true, true, "EV2_13", "REDIM\'D ARRAY" }, - { true, true, true, "EV2_14", "DIVISION BY ZERO" }, - { true, true, true, "EV2_15", "ILLEGAL DIRECT" }, - { true, true, true, "EV2_16", "TYPE MISMATCH" }, - { true, true, true, "EV2_17", "STRING TOO LONG" }, - { true, true, true, "EV2_18", "FILE DATA" }, - { true, true, true, "EV2_19", "FORMULA TOO COMPLEX" }, - { true, true, true, "EV2_1A", "CAN\'T CONTINUE" }, - { true, true, true, "EV2_1B", "UNDEF\'D FUNCTION" }, - { true, true, true, "EV2_1C", "VERIFY" }, - { true, true, true, "EV2_1D", "LOAD" }, - { true, true, true, "EV2_1E", "BREAK" }, - // STD M65 X16 --- error strings compatible with CBM BASIC V7 - { false, false, false, "EV7_1F", "CAN'T RESUME" }, // not used for now - { false, false, false, "EV7_20", "LOOP NOT FOUND" }, // not used for now - { false, false, false, "EV7_21", "LOOP WITHOUT DO" }, // not used for now - { true, true, true, "EV7_22", "DIRECT MODE ONLY" }, - { false, false, false, "EV7_23", "NO GRAPHICS AREA" }, // not used for now - { false, false, false, "EV7_24", "BAD DISK" }, // not used for now - { false, false, false, "EV7_25", "BEND NOT FOUND" }, // not used for now - { false, false, false, "EV7_26", "LINE NUMBER TOO LARGE" }, // not used for now - { false, false, false, "EV7_27", "UNRESOLVED REFERENCE" }, // not used for now - { true, true, true, "EV7_28", "NOT IMPLEMENTED" }, // this message actually differs from the CBM one - { false, false, false, "EV7_29", "FILE READ" }, // not used for now - // STD M65 X16 --- error strings specific to OpenROMs, not present in CBM BASIC dialects - { true, true, true, "EOR_2A", "MEMORY CORRUPT" }, + // STD M65 U64 X16 --- error strings compatible with CBM BASIC V2 + { true, true, true, true, "EV2_01", "TOO MANY FILES" }, + { true, true, true, true, "EV2_02", "FILE OPEN" }, + { true, true, true, true, "EV2_03", "FILE NOT OPEN" }, + { true, true, true, true, "EV2_04", "FILE NOT FOUND" }, + { true, true, true, true, "EV2_05", "DEVICE NOT PRESENT" }, + { true, true, true, true, "EV2_06", "NOT INPUT FILE" }, + { true, true, true, true, "EV2_07", "NOT OUTPUT FILE" }, + { true, true, true, true, "EV2_08", "MISSING FILENAME" }, + { true, true, true, true, "EV2_09", "ILLEGAL DEVICE NUMBER" }, + { true, true, true, true, "EV2_0A", "NEXT WITHOUT FOR" }, + { true, true, true, true, "EV2_0B", "SYNTAX" }, + { true, true, true, true, "EV2_0C", "RETURN WITHOUT GOSUB" }, + { true, true, true, true, "EV2_0D", "OUT OF DATA" }, + { true, true, true, true, "EV2_0E", "ILLEGAL QUANTITY" }, + { true, true, true, true, "EV2_0F", "OVERFLOW" }, + { true, true, true, true, "EV2_10", "OUT OF MEMORY" }, + { true, true, true, true, "EV2_11", "UNDEF\'D STATEMENT" }, + { true, true, true, true, "EV2_12", "BAD SUBSCRIPT" }, + { true, true, true, true, "EV2_13", "REDIM\'D ARRAY" }, + { true, true, true, true, "EV2_14", "DIVISION BY ZERO" }, + { true, true, true, true, "EV2_15", "ILLEGAL DIRECT" }, + { true, true, true, true, "EV2_16", "TYPE MISMATCH" }, + { true, true, true, true, "EV2_17", "STRING TOO LONG" }, + { true, true, true, true, "EV2_18", "FILE DATA" }, + { true, true, true, true, "EV2_19", "FORMULA TOO COMPLEX" }, + { true, true, true, true, "EV2_1A", "CAN\'T CONTINUE" }, + { true, true, true, true, "EV2_1B", "UNDEF\'D FUNCTION" }, + { true, true, true, true, "EV2_1C", "VERIFY" }, + { true, true, true, true, "EV2_1D", "LOAD" }, + { true, true, true, true, "EV2_1E", "BREAK" }, + // STD M65 U64 X16 --- error strings compatible with CBM BASIC V7 + { false, false, false, false, "EV7_1F", "CAN'T RESUME" }, // not used for now + { false, false, false, false, "EV7_20", "LOOP NOT FOUND" }, // not used for now + { false, false, false, false, "EV7_21", "LOOP WITHOUT DO" }, // not used for now + { true, true, true, true, "EV7_22", "DIRECT MODE ONLY" }, + { false, false, false, false, "EV7_23", "NO GRAPHICS AREA" }, // not used for now + { false, false, false, false, "EV7_24", "BAD DISK" }, // not used for now + { false, false, false, false, "EV7_25", "BEND NOT FOUND" }, // not used for now + { false, false, false, false, "EV7_26", "LINE NUMBER TOO LARGE" }, // not used for now + { false, false, false, false, "EV7_27", "UNRESOLVED REFERENCE" }, // not used for now + { true, true, true, true, "EV7_28", "NOT IMPLEMENTED" }, // this message actually differs from the CBM one + { false, false, false, false, "EV7_29", "FILE READ" }, // not used for now + // STD M65 U64 X16 --- error strings specific to OpenROMs, not present in CBM BASIC dialects + { true, true, true, true, "EOR_2A", "MEMORY CORRUPT" }, } }; // BASIC errors - miscelaneous strings const StringEntryList GLOBAL_MiscStrings = { ListType::STRINGS_BASIC, "misc", { - // STD M65 X16 --- misc strings as on CBM machines - { true, true, true, "STR_RET_QM", "\r?" }, - { true, true, true, "STR_BYTES", " BASIC BYTES FREE" }, // https://github.com/stefanhaustein/expressionparser - { true, true, true, "STR_READY", "\rREADY.\r" }, // https://www.ibm.com/support/knowledgecenter/zosbasics/com.ibm.zos.zconcepts/zconc_whatistsonative.htm https://github.com/stefanhaustein/expressionparser - { true, true, true, "STR_ERROR", " ERROR" }, // simply the word error that is attached to the other parts of messages https://fjkraan.home.xs4all.nl/comp/apple2faq/app2asoftfaq.html - { true, true, true, "STR_IN", " IN " }, - // STD M65 X16 --- misc strings specific to OpenROMs, not present in CBM ROMs - { true, true, true, "STR_BRK_AT", "\rBRK AT $" }, - // Note: depending on configuration, additional strings will be added here + // STD M65 U64 X16 --- misc strings as on CBM machines + { true, true, true, true, "STR_RET_QM", "\r?" }, + { true, true, true, true, "STR_BYTES", " BASIC BYTES FREE" }, // https://github.com/stefanhaustein/expressionparser + { true, true, true, true, "STR_READY", "\rREADY.\r" }, // https://www.ibm.com/support/knowledgecenter/zosbasics/com.ibm.zos.zconcepts/zconc_whatistsonative.htm https://github.com/stefanhaustein/expressionparser + { true, true, true, true, "STR_ERROR", " ERROR" }, // simply the word error that is attached to the other parts of messages https://fjkraan.home.xs4all.nl/comp/apple2faq/app2asoftfaq.html + { true, true, true, true, "STR_IN", " IN " }, + // STD M65 U64 X16 --- misc strings specific to OpenROMs, not present in CBM ROMs + { true, true, true, true, "STR_BRK_AT", "\rBRK AT $" }, + // Note: depending on configuration, additional strings will be added here } }; @@ -318,129 +320,129 @@ const StringEntryList GLOBAL_MiscStrings = { ListType::STRINGS_BASIC, "misc", - https://sites.google.com/site/h2obsession/CBM/basic/tokens - http://www.zimmers.net/anonftp/pub/cbm/programming/cbm-basic-tokens.txt - "RGR" // $CC - "RCLR" // $CD + "RGR" // $CC + "RCLR" // $CD - "JOY" // $CF // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html - "RDOT" // $D0 - "DEC" // $D1 // Amos Professional Manual, Command Index - "HEX$" // $D2 // http://www.antonis.de/qbebooks/gwbasman/hexs.html - "ERR$" // $D3 // Amos Professional Manual, Command Index - "INSTR" // $D4 // Amos Professional Manual, Command Index - "ELSE" // $D5 // Amos Professional Manual, Command Index - "RESUME" // $D6 // Amos Professional Manual, Command Index - "TRAP" // $D7 // Amos Professional Manual, Command Index - "TRON" // $D8 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html - "TROFF" // $D9 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html - "SOUND" // $DA // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html - "VOL" // $DB - "AUTO" // $DC // http://www.antonis.de/qbebooks/gwbasman/auto.html - "PUDEF" // $DD - "GRAPHIC" // $DE - "PAINT" // $DF // Amos Professional Manual, Command Index - "CHAR" // $E0 // https://www.c64-wiki.com/wiki/Screen_Graphics_64 - "BOX" // $E1 // Amos Professional Manual, Command Index - "CIRCLE" // $E2 // https://en.wikipedia.org/wiki/Sinclair_BASIC - "PASTE" // $E3 // Amos Professional Manual, Command Index - "CUT" // $E4 - "LINE" // $E5 // https://en.wikipedia.org/wiki/Sinclair_BASIC - "LOCATE" // $E6 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html - "COLOR" // $E7 - "SCNCLR" // $E8 - "SCALE" // $E9 // https://www.c64-wiki.com/wiki/Super_Expander_64 - "HELP" // $EA // http://www.classic-games.com/commodore64/cbmtoken.html, Speech Basic 2.7 - "DO" // $EB // Amos Professional Manual, Command Index - "LOOP" // $EC // Amos Professional Manual, Command Index - "EXIT" // $ED // Amos Professional Manual, Command Index - "DIR" // $EE // Amos Professional Manual, Command Index - "DSAVE" // $EF // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "DLOAD" // $F0 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "HEADER" // $F1 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "SCRATCH" // $F2 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "COLLECT" // $F3 - "COPY" // $F4 // https://en.wikipedia.org/wiki/Sinclair_BASIC - "RENAME" // $F5 // Amos Professional Manual, Command Index - "BACKUP" // $F6 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "DELETE" // $F7 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "RENUMBER" // $F8 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "KEY" // $F9 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html - "MONITOR" // $FA // Amos Professional Manual, Command Index - "USING" // $FB // http://www.antonis.de/qbebooks/gwbasman/printusing.html - "UNTIL" // $FC // Amos Professional Manual, Command Index - "WHILE" // $FD // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html + "JOY" // $CF // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html + "RDOT" // $D0 + "DEC" // $D1 // Amos Professional Manual, Command Index + "HEX$" // $D2 // http://www.antonis.de/qbebooks/gwbasman/hexs.html + "ERR$" // $D3 // Amos Professional Manual, Command Index + "INSTR" // $D4 // Amos Professional Manual, Command Index + "ELSE" // $D5 // Amos Professional Manual, Command Index + "RESUME" // $D6 // Amos Professional Manual, Command Index + "TRAP" // $D7 // Amos Professional Manual, Command Index + "TRON" // $D8 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html + "TROFF" // $D9 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html + "SOUND" // $DA // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html + "VOL" // $DB + "AUTO" // $DC // http://www.antonis.de/qbebooks/gwbasman/auto.html + "PUDEF" // $DD + "GRAPHIC" // $DE + "PAINT" // $DF // Amos Professional Manual, Command Index + "CHAR" // $E0 // https://www.c64-wiki.com/wiki/Screen_Graphics_64 + "BOX" // $E1 // Amos Professional Manual, Command Index + "CIRCLE" // $E2 // https://en.wikipedia.org/wiki/Sinclair_BASIC + "PASTE" // $E3 // Amos Professional Manual, Command Index + "CUT" // $E4 + "LINE" // $E5 // https://en.wikipedia.org/wiki/Sinclair_BASIC + "LOCATE" // $E6 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html + "COLOR" // $E7 + "SCNCLR" // $E8 + "SCALE" // $E9 // https://www.c64-wiki.com/wiki/Super_Expander_64 + "HELP" // $EA // http://www.classic-games.com/commodore64/cbmtoken.html, Speech Basic 2.7 + "DO" // $EB // Amos Professional Manual, Command Index + "LOOP" // $EC // Amos Professional Manual, Command Index + "EXIT" // $ED // Amos Professional Manual, Command Index + "DIR" // $EE // Amos Professional Manual, Command Index + "DSAVE" // $EF // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "DLOAD" // $F0 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "HEADER" // $F1 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "SCRATCH" // $F2 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "COLLECT" // $F3 + "COPY" // $F4 // https://en.wikipedia.org/wiki/Sinclair_BASIC + "RENAME" // $F5 // Amos Professional Manual, Command Index + "BACKUP" // $F6 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "DELETE" // $F7 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "RENUMBER" // $F8 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "KEY" // $F9 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html + "MONITOR" // $FA // Amos Professional Manual, Command Index + "USING" // $FB // http://www.antonis.de/qbebooks/gwbasman/printusing.html + "UNTIL" // $FC // Amos Professional Manual, Command Index + "WHILE" // $FD // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html - "POT" // $CE $02 - "BUMP" // $CE $03 - "PEN" // $CE $04 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html - "RSPPOS" // $CE $05 // https://www.c64-wiki.com/wiki/Super_Expander_64 - "RSPRITE" // $CE $06 - "RSPCOLOR" // $CE $07 - "XOR" // $CE $08 - "RWINDOW" // $CE $09 - "POINTER" // $CE $0A + "POT" // $CE $02 + "BUMP" // $CE $03 + "PEN" // $CE $04 // https://www.qsl.net/hb9xch/computer/amstrad/locomotivebasic.html + "RSPPOS" // $CE $05 // https://www.c64-wiki.com/wiki/Super_Expander_64 + "RSPRITE" // $CE $06 + "RSPCOLOR" // $CE $07 + "XOR" // $CE $08 + "RWINDOW" // $CE $09 + "POINTER" // $CE $0A - "BANK" // $FE $02 // Amos Professional Manual, Command Index - "FILTER" // $FE $03 // https://www.c64-wiki.com/wiki/Super_Expander_64 - "PLAY" // $FE $04 // Amos Professional Manual, Command Index - "TEMPO" // $FE $05 // Amos Professional Manual, Command Index - "MOVSPR" // $FE $06 - "SPRITE" // $FE $07 // Amos Professional Manual, Command Index - "SPRCOLOR" // $FE $08 - "RREG" // $FE $09 - "ENVELOPE" // $FE $0A - "SLEEP" // $FE $0B - "CATALOG" // $FE $0C // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "DOPEN" // $FE $0D - "APPEND" // $FE $0E // Amos Professional Manual, Command Index - "DCLOSE" // $FE $0F - "BSAVE" // $FE $10 // http://www.antonis.de/qbebooks/gwbasman/bsave.html - "BLOAD" // $FE $11 // http://www.antonis.de/qbebooks/gwbasman/bload.html - "RECORD" // $FE $12 - "CONCAT" // $FE $13 - "DVERIFY" // $FE $14 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "DCLEAR" // $FE $15 - "SPRSAV" // $FE $16 // https://www.c64-wiki.com/wiki/Super_Expander_64 - "COLLISION" // $FE $17 - "BEGIN" // $FE $18 - "BEND" // $FE $19 - "WINDOW" // $FE $1A // http://www.antonis.de/qbebooks/gwbasman/window.html - "BOOT" // $FE $1B - "WIDTH" // $FE $1C // http://www.antonis.de/qbebooks/gwbasman/width.html - "SPRDEF" // $FE $1D // https://www.c64-wiki.com/wiki/Super_Expander_64 - "QUIT" // $FE $1E // http://www.classic-games.com/commodore64/cbmtoken.html, Speech Basic 2.7 + "BANK" // $FE $02 // Amos Professional Manual, Command Index + "FILTER" // $FE $03 // https://www.c64-wiki.com/wiki/Super_Expander_64 + "PLAY" // $FE $04 // Amos Professional Manual, Command Index + "TEMPO" // $FE $05 // Amos Professional Manual, Command Index + "MOVSPR" // $FE $06 + "SPRITE" // $FE $07 // Amos Professional Manual, Command Index + "SPRCOLOR" // $FE $08 + "RREG" // $FE $09 + "ENVELOPE" // $FE $0A + "SLEEP" // $FE $0B + "CATALOG" // $FE $0C // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "DOPEN" // $FE $0D + "APPEND" // $FE $0E // Amos Professional Manual, Command Index + "DCLOSE" // $FE $0F + "BSAVE" // $FE $10 // http://www.antonis.de/qbebooks/gwbasman/bsave.html + "BLOAD" // $FE $11 // http://www.antonis.de/qbebooks/gwbasman/bload.html + "RECORD" // $FE $12 + "CONCAT" // $FE $13 + "DVERIFY" // $FE $14 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "DCLEAR" // $FE $15 + "SPRSAV" // $FE $16 // https://www.c64-wiki.com/wiki/Super_Expander_64 + "COLLISION" // $FE $17 + "BEGIN" // $FE $18 + "BEND" // $FE $19 + "WINDOW" // $FE $1A // http://www.antonis.de/qbebooks/gwbasman/window.html + "BOOT" // $FE $1B + "WIDTH" // $FE $1C // http://www.antonis.de/qbebooks/gwbasman/width.html + "SPRDEF" // $FE $1D // https://www.c64-wiki.com/wiki/Super_Expander_64 + "QUIT" // $FE $1E // http://www.classic-games.com/commodore64/cbmtoken.html, Speech Basic 2.7 - "STASH" // $FE $1F - v7 - "FETCH" // $FE $21 - v7 - "SWAP" // $FE $23 - v7 // http://www.antonis.de/qbebooks/gwbasman/swap.html + "STASH" // $FE $1F - v7 + "FETCH" // $FE $21 - v7 + "SWAP" // $FE $23 - v7 // http://www.antonis.de/qbebooks/gwbasman/swap.html - "DMA" // $FE $17/$21/$23 - v10 + "DMA" // $FE $17/$21/$23 - v10 - "OFF" // $FE $24 // http://www.classic-games.com/commodore64/cbmtoken.html, Speech Basic 2.7 - "FAST" // $FE $25 // https://en.wikipedia.org/wiki/Sinclair_BASIC - ZX81 variant - "SLOW" // $FE $26 // https://en.wikipedia.org/wiki/Sinclair_BASIC - ZX81 variant - "TYPE" // $FE $27 - "BVERIFY" // $FE $28 - "DIRECTORY" // $FE $29 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "ERASE" // $FE $2A // https://en.wikipedia.org/wiki/Sinclair_BASIC - "FIND" // $FE $2B // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) - "CHANGE" // $FE $2C - "SET" // $FE $2D - "SCREEN" // $FE $2E // http://www.antonis.de/qbebooks/gwbasman/screens.html - "POLYGON" // $FE $2F // Amos Professional Manual, Command Index - "ELLIPSE" // $FE $30 // Amos Professional Manual, Command Index - "VIEWPORT" // $FE $31 - "GCOPY" // $FE $32 - "PEN" // $FE $33 // Amos Professional Manual, Command Index - "PALETTE" // $FE $34 // Amos Professional Manual, Command Index - "DMODE" // $FE $35 - "DPAT" // $FE $36 - "PIC" // $FE $37 - "GENLOCK" // $FE $38 - "FOREGROUND" // $FE $39 + "OFF" // $FE $24 // http://www.classic-games.com/commodore64/cbmtoken.html, Speech Basic 2.7 + "FAST" // $FE $25 // https://en.wikipedia.org/wiki/Sinclair_BASIC - ZX81 variant + "SLOW" // $FE $26 // https://en.wikipedia.org/wiki/Sinclair_BASIC - ZX81 variant + "TYPE" // $FE $27 + "BVERIFY" // $FE $28 + "DIRECTORY" // $FE $29 // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "ERASE" // $FE $2A // https://en.wikipedia.org/wiki/Sinclair_BASIC + "FIND" // $FE $2B // http://www.classic-games.com/commodore64/cbmtoken.html, AtBasic (@Basic) + "CHANGE" // $FE $2C + "SET" // $FE $2D + "SCREEN" // $FE $2E // http://www.antonis.de/qbebooks/gwbasman/screens.html + "POLYGON" // $FE $2F // Amos Professional Manual, Command Index + "ELLIPSE" // $FE $30 // Amos Professional Manual, Command Index + "VIEWPORT" // $FE $31 + "GCOPY" // $FE $32 + "PEN" // $FE $33 // Amos Professional Manual, Command Index + "PALETTE" // $FE $34 // Amos Professional Manual, Command Index + "DMODE" // $FE $35 + "DPAT" // $FE $36 + "PIC" // $FE $37 + "GENLOCK" // $FE $38 + "FOREGROUND" // $FE $39 - "BACKGROUND" // $FE $3B - "BORDER" // $FE $3C // https://en.wikipedia.org/wiki/Sinclair_BASIC - "HIGHLIGHT" // $FE $3D + "BACKGROUND" // $FE $3B + "BORDER" // $FE $3C // https://en.wikipedia.org/wiki/Sinclair_BASIC + "HIGHLIGHT" // $FE $3D */ std::map GLOBAL_ConfigOptions; @@ -449,94 +451,102 @@ std::map GLOBAL_ConfigOptions; // Work class definitions // +// This class encapsulates the optional dictionary encoding of selected string lists class DictEncoder { public: - void addString(const std::string &inString, StringEncoded *outPtr); + void addString(const std::string &inString, StringEncoded *outPtr); - void process(StringEntryList &outDictionary); + void process(StringEntryList &outDictionary); private: - bool optimizeSplit(); - bool optimizeJoin(); - void optimizeOrder(); + bool optimizeSplit(); + bool optimizeJoin(); + void optimizeOrder(); - void cleanupDictionary(); + void cleanupDictionary(); - void extractWords(std::vector &candidateList); - int32_t evaluateCandidate(std::string &candidate); + void extractWords(std::vector &candidateList); + int32_t evaluateCandidate(std::string &candidate); - std::vector encodings; - std::vector dictionary; + std::vector encodings; + std::vector dictionary; }; +// Main class to encode strings based on character frequency class DataSet { public: - void addStrings(const StringEntryList &stringList); + void addStrings(const StringEntryList &stringList); - const std::string &getOutput(); + const std::string &getOutput(); private: - void process(); + void process(); - void generateConfigDepStrings(); - void validateLists(); - void calculateFrequencies(); - void encodeStringsDict(); - void encodeStringsFreq(); + void generateConfigDepStrings(); + void validateLists(); + void calculateFrequencies(); + void encodeStringsDict(); + void encodeStringsFreq(); - void encodeByFreq(const std::string &plain, StringEncoded &encoded) const; + void encodeByFreq(const std::string &plain, StringEncoded &encoded) const; - void prepareOutput(); - void prepareOutput_1n_3n(std::ostringstream &stream); - void prepareOutput_labels(std::ostringstream &stream, - const StringEntryList &stringEntryList, - const StringEncodedList &stringEncodedList); - void prepareOutput_packed(std::ostringstream &stream, - const StringEntryList &stringEntryList, - const StringEncodedList &stringEncodedList); + void prepareOutput(); + void prepareOutput_1n_3n(std::ostringstream &stream); + void prepareOutput_labels(std::ostringstream &stream, + const StringEntryList &stringEntryList, + const StringEncodedList &stringEncodedList); + void prepareOutput_packed(std::ostringstream &stream, + const StringEntryList &stringEntryList, + const StringEncodedList &stringEncodedList); - void putCharEncoding(std::ostringstream &stream, uint8_t idx, char character, bool is3n); + void putCharEncoding(std::ostringstream &stream, uint8_t idx, char character, bool is3n); - bool isCompressionLvl2(const StringEntryList &list) const; + bool isCompressionLvl2(const StringEntryList &list) const; - virtual bool isRelevant(const StringEntry &entry) const = 0; - virtual std::string layoutName() const = 0; + virtual bool isRelevant(const StringEntry &entry) const = 0; + virtual std::string layoutName() const = 0; - std::vector stringEntryLists; - std::vector stringEncodedLists; + std::vector stringEntryLists; + std::vector stringEncodedLists; - std::vector as1n; // list of bytes to be encoded as 1 nibble - std::vector as3n; // list of bytes to be encoded as 3 nibbles + std::vector as1n; // list of bytes to be encoded as 1 nibble + std::vector as3n; // list of bytes to be encoded as 3 nibbles - uint8_t tk__packed_as_3n = 0; - uint8_t tk__max_keyword_len = 0; + uint8_t tk__packed_as_3n = 0; + uint8_t tk__max_keyword_len = 0; - size_t maxAliasLen = 0; - std::string outFileContent; + size_t maxAliasLen = 0; + std::string outFileContent; }; class DataSetSTD : public DataSet { - bool isRelevant(const StringEntry &entry) const { return entry.enabledSTD; } - std::string layoutName() const { return "STD"; } + bool isRelevant(const StringEntry &entry) const { return entry.enabledSTD; } + std::string layoutName() const { return "STD"; } }; class DataSetM65 : public DataSet { - bool isRelevant(const StringEntry &entry) const { return entry.enabledM65; } - std::string layoutName() const { return "M65"; } + bool isRelevant(const StringEntry &entry) const { return entry.enabledM65; } + std::string layoutName() const { return "M65"; } +}; + +class DataSetU64 : public DataSet +{ + bool isRelevant(const StringEntry &entry) const { return entry.enabledU64; } + std::string layoutName() const { return "STD"; } // XXX to be changed for 'U64' once the layout is finished }; class DataSetX16 : public DataSet { - bool isRelevant(const StringEntry &entry) const { return entry.enabledX16; } - std::string layoutName() const { return "X16"; } + bool isRelevant(const StringEntry &entry) const { return entry.enabledX16; } + std::string layoutName() const { return "X16"; } }; // @@ -545,1119 +555,1119 @@ class DataSetX16 : public DataSet void DictEncoder::addString(const std::string &inString, StringEncoded *outPtr) { - // Store the pointer to encoding + // Store the pointer to encoding - encodings.push_back(outPtr); + encodings.push_back(outPtr); - // Check if string is already present in the dictionary + // Check if string is already present in the dictionary - auto pos = std::find(dictionary.begin(), dictionary.end(), inString); + auto pos = std::find(dictionary.begin(), dictionary.end(), inString); - // Create initial encoding + // Create initial encoding - if (pos != dictionary.end()) - { - encodings.back()->push_back(pos - dictionary.begin()); - } - else - { - if (dictionary.size() > 255) - { - ERROR("max 255 strings allowed for dictionary compression"); - } + if (pos != dictionary.end()) + { + encodings.back()->push_back(pos - dictionary.begin()); + } + else + { + if (dictionary.size() > 255) + { + ERROR("max 255 strings allowed for dictionary compression"); + } - dictionary.push_back(inString); - encodings.back()->push_back(dictionary.size() - 1); - } + dictionary.push_back(inString); + encodings.back()->push_back(dictionary.size() - 1); + } } void DictEncoder::extractWords(std::vector &candidateList) { - for (const auto &dictionaryEntry : dictionary) - { - // Split the dictionary entry into words - - std::istringstream entryStream(dictionaryEntry); - while (entryStream) - { - std::string word_1; - entryStream >> word_1; + for (const auto &dictionaryEntry : dictionary) + { + // Split the dictionary entry into words + + std::istringstream entryStream(dictionaryEntry); + while (entryStream) + { + std::string word_1; + entryStream >> word_1; - if (word_1.empty()) continue; - - // Create possible variants with spaces + if (word_1.empty()) continue; + + // Create possible variants with spaces - std::string word_2 = " " + word_1; - std::string word_3 = word_1 + " "; - std::string word_4 = word_2 + " "; + std::string word_2 = " " + word_1; + std::string word_3 = word_1 + " "; + std::string word_4 = word_2 + " "; - // If necessary, add the word to candidate list - - auto addToList = [&dictionaryEntry, &candidateList](std::string &word) - { - // Before dding, make sure the word variant exists in the string, - // is not already on the list, and is long enough it makes sense to try + // If necessary, add the word to candidate list + + auto addToList = [&dictionaryEntry, &candidateList](std::string &word) + { + // Before dding, make sure the word variant exists in the string, + // is not already on the list, and is long enough it makes sense to try - if (word.size() < 1) return; - if (dictionaryEntry.find(word) == std::string::npos) return; - if (std::find(candidateList.begin(), candidateList.end(), word) != candidateList.end()) return; + if (word.size() < 1) return; + if (dictionaryEntry.find(word) == std::string::npos) return; + if (std::find(candidateList.begin(), candidateList.end(), word) != candidateList.end()) return; - candidateList.push_back(word); - }; - - addToList(word_1); - addToList(word_2); - addToList(word_3); - addToList(word_4); - } - } + candidateList.push_back(word); + }; + + addToList(word_1); + addToList(word_2); + addToList(word_3); + addToList(word_4); + } + } } int32_t DictEncoder::evaluateCandidate(std::string &candidate) { - // XXX it looks like this is not 100% right - debug the algorithm if dictionary compression is reintroduced + // XXX it looks like this is not 100% right - debug the algorithm if dictionary compression is reintroduced - // Build the score - find how much bytes can be spared by extracting this particular candidate - // Limited size of the dictionary is taken into consideration - - int32_t score = -(candidate.size() + 1); - uint32_t targetSize = dictionary.size() + 1; + // Build the score - find how much bytes can be spared by extracting this particular candidate + // Limited size of the dictionary is taken into consideration + + int32_t score = -(candidate.size() + 1); + uint32_t targetSize = dictionary.size() + 1; - // First check for situation when the candidate equals the currently existing dictionary entry + // First check for situation when the candidate equals the currently existing dictionary entry - for (const auto &dictionaryEntry : dictionary) - { - if (dictionaryEntry == candidate) - { - // Candidate equals the string + for (const auto &dictionaryEntry : dictionary) + { + if (dictionaryEntry == candidate) + { + // Candidate equals the string - score += candidate.size() + 1; - targetSize--; - continue; - } - } + score += candidate.size() + 1; + targetSize--; + continue; + } + } - // Now check the remaining cases + // Now check the remaining cases - for (const auto &dictionaryEntry : dictionary) - { - if (dictionaryEntry == candidate) continue; // already handled + for (const auto &dictionaryEntry : dictionary) + { + if (dictionaryEntry == candidate) continue; // already handled - // Find all the occurences of the candidate in the current string + // Find all the occurences of the candidate in the current string - std::vector occurences; - for (auto iter = dictionaryEntry.begin(); iter < (dictionaryEntry.end() - candidate.size() + 1); iter++) - { - if (candidate != std::string(iter, iter + candidate.size())) continue; - - occurences.push_back(iter - dictionaryEntry.begin()); - iter += candidate.size() - 1; // to make sure we have no occurence overlapping - } - - if (occurences.empty()) continue; - - // Now summarize what we are going to gain when doing replacement - - if (occurences[0] != 0) - { - // For the substring before the first occurence we need to create a separate entry - // in the dictionary - unless it is already there, it brings some additional cost + std::vector occurences; + for (auto iter = dictionaryEntry.begin(); iter < (dictionaryEntry.end() - candidate.size() + 1); iter++) + { + if (candidate != std::string(iter, iter + candidate.size())) continue; + + occurences.push_back(iter - dictionaryEntry.begin()); + iter += candidate.size() - 1; // to make sure we have no occurence overlapping + } + + if (occurences.empty()) continue; + + // Now summarize what we are going to gain when doing replacement + + if (occurences[0] != 0) + { + // For the substring before the first occurence we need to create a separate entry + // in the dictionary - unless it is already there, it brings some additional cost - std::string otherStr = std::string(dictionaryEntry.begin(), dictionaryEntry.end() + occurences[0]); + std::string otherStr = std::string(dictionaryEntry.begin(), dictionaryEntry.end() + occurences[0]); - if (std::find(dictionary.begin(), dictionary.end(), otherStr) != dictionary.end()) - { - // This extra string is already in the dictionary - that gives extra saving - score += otherStr.size() - 1; - } - else - { - // This extra string is not in the dictionary, it has to be added - score -= 2; - targetSize++; - } - } - - for (auto iter = occurences.begin(); iter < occurences.end(); iter++) - { - // With a replacement, we should gain some bytes - score += candidate.size() - 1; + if (std::find(dictionary.begin(), dictionary.end(), otherStr) != dictionary.end()) + { + // This extra string is already in the dictionary - that gives extra saving + score += otherStr.size() - 1; + } + else + { + // This extra string is not in the dictionary, it has to be added + score -= 2; + targetSize++; + } + } + + for (auto iter = occurences.begin(); iter < occurences.end(); iter++) + { + // With a replacement, we should gain some bytes + score += candidate.size() - 1; - // If the occurence ends with the last byte of dictionary string - nothing more to do - if (*iter + candidate.size() == dictionaryEntry.size()) break; + // If the occurence ends with the last byte of dictionary string - nothing more to do + if (*iter + candidate.size() == dictionaryEntry.size()) break; - // If next occurence starts immediately after this one - next iteration - if (iter + 1 != occurences.end() && (*(iter + 1) - *iter) == (uint8_t) candidate.size()) continue; + // If next occurence starts immediately after this one - next iteration + if (iter + 1 != occurences.end() && (*(iter + 1) - *iter) == (uint8_t) candidate.size()) continue; - // For the substring between this occurence and the next one (or the end of the string) - // we need to create a separate entry in the dictionary - unless it is already there, - // it brings some additional cost + // For the substring between this occurence and the next one (or the end of the string) + // we need to create a separate entry in the dictionary - unless it is already there, + // it brings some additional cost - std::string otherStr; - if (iter + 1 == occurences.end()) - { - otherStr = std::string(dictionaryEntry.begin() + *iter + candidate.size(), - dictionaryEntry.end()); - } - else - { - otherStr = std::string(dictionaryEntry.begin() + *iter + candidate.size(), - dictionaryEntry.begin() + *(iter + 1)); - } + std::string otherStr; + if (iter + 1 == occurences.end()) + { + otherStr = std::string(dictionaryEntry.begin() + *iter + candidate.size(), + dictionaryEntry.end()); + } + else + { + otherStr = std::string(dictionaryEntry.begin() + *iter + candidate.size(), + dictionaryEntry.begin() + *(iter + 1)); + } - if (std::find(dictionary.begin(), dictionary.end(), otherStr) != dictionary.end()) - { - // This extra string is already in the dictionary - that gives extra saving - score += otherStr.size() - 1; - } - else - { - // This extra string is not in the dictionary, it has to be added - score -= 2; - targetSize++; - } - } - - // We crossed the maximum number of strings (leave one free for the work buffer), - // do not consider this candidate - - if (targetSize > 254) return -1; - } - - return score; + if (std::find(dictionary.begin(), dictionary.end(), otherStr) != dictionary.end()) + { + // This extra string is already in the dictionary - that gives extra saving + score += otherStr.size() - 1; + } + else + { + // This extra string is not in the dictionary, it has to be added + score -= 2; + targetSize++; + } + } + + // We crossed the maximum number of strings (leave one free for the work buffer), + // do not consider this candidate + + if (targetSize > 254) return -1; + } + + return score; } void DictEncoder::cleanupDictionary() { - // Get rid of dictionary entries which are not needed anymore - - bool isClean = false; - - while (!isClean) - { - isClean = true; - - for (auto iter = dictionary.begin(); iter < dictionary.end(); iter++) - { - if (!iter->empty()) continue; - - // Found an empty string in the dictionary - remove it and adapt encoding - - isClean = false; - uint8_t obsoleteStrIdx = iter - dictionary.begin(); - - dictionary.erase(iter); - - for (auto &encoding : encodings) for (auto &byte : *encoding) - { - if (byte >= obsoleteStrIdx) byte--; - } - - break; - } - } + // Get rid of dictionary entries which are not needed anymore + + bool isClean = false; + + while (!isClean) + { + isClean = true; + + for (auto iter = dictionary.begin(); iter < dictionary.end(); iter++) + { + if (!iter->empty()) continue; + + // Found an empty string in the dictionary - remove it and adapt encoding + + isClean = false; + uint8_t obsoleteStrIdx = iter - dictionary.begin(); + + dictionary.erase(iter); + + for (auto &encoding : encodings) for (auto &byte : *encoding) + { + if (byte >= obsoleteStrIdx) byte--; + } + + break; + } + } } bool DictEncoder::optimizeSplit() { - // XXX - The code here is not the best one - redesign and algorithm improvements - // (like - consider optimizing as a game and apply alpha-beta scheme to select the best - // candidate, incorporate frequency encoding into candidate evaluation) could result in - // a slightly better compression. But currently we have very few strings, and the dictionary - // compression is disabled by default - so right now this is not worth the effort. - - if (dictionary.size() >= 255) return false; // up to 255 entries in the dictionary are possible - - // First select words, which can potentially be extracted to new substrings - - std::vector candidateList; - extractWords(candidateList); + // XXX - The code here is not the best one - redesign and algorithm improvements + // (like - consider optimizing as a game and apply alpha-beta scheme to select the best + // candidate, incorporate frequency encoding into candidate evaluation) could result in + // a slightly better compression. But currently we have very few strings, and the dictionary + // compression is disabled by default - so right now this is not worth the effort. + + if (dictionary.size() >= 255) return false; // up to 255 entries in the dictionary are possible + + // First select words, which can potentially be extracted to new substrings + + std::vector candidateList; + extractWords(candidateList); - // XXX possible future improvement: find a couple of largest common substrings and add them as candidates too - - // Now find the best word to be extracted - - auto bestCandidate = candidateList.end(); - int32_t bestCandidateScore = 0; - - for (auto iter = candidateList.begin(); iter < candidateList.end(); iter++) - { - int32_t candidateScore = evaluateCandidate(*iter); + // XXX possible future improvement: find a couple of largest common substrings and add them as candidates too + + // Now find the best word to be extracted + + auto bestCandidate = candidateList.end(); + int32_t bestCandidateScore = 0; + + for (auto iter = candidateList.begin(); iter < candidateList.end(); iter++) + { + int32_t candidateScore = evaluateCandidate(*iter); - if (candidateScore > bestCandidateScore) - { - bestCandidate = iter; - bestCandidateScore = candidateScore; - } - } - - // Only allow for replacements that brings some size benefit - - if (bestCandidateScore < 1) return false; - - // Extract the best candidate to a separate string - - // First add our selected candidate to the dictionary and replace all strings which are equal to it - - auto &selectedStr = *bestCandidate; - uint8_t selectedStrIdx = dictionary.size(); - - dictionary.push_back(selectedStr); + if (candidateScore > bestCandidateScore) + { + bestCandidate = iter; + bestCandidateScore = candidateScore; + } + } + + // Only allow for replacements that brings some size benefit + + if (bestCandidateScore < 1) return false; + + // Extract the best candidate to a separate string + + // First add our selected candidate to the dictionary and replace all strings which are equal to it + + auto &selectedStr = *bestCandidate; + uint8_t selectedStrIdx = dictionary.size(); + + dictionary.push_back(selectedStr); - for (auto iter = dictionary.begin(); iter < dictionary.end(); iter++) - { - uint8_t currentStrIdx = iter - dictionary.begin(); - - if (*iter != selectedStr || selectedStrIdx == currentStrIdx) continue; - - // Replace the current string with the new one - - for (auto &encoding : encodings) for (auto &byte : *encoding) - { - if (byte == currentStrIdx) byte = selectedStrIdx; - } - - // Mark obsolete dictionary entry as free for removal - - iter->clear(); - } + for (auto iter = dictionary.begin(); iter < dictionary.end(); iter++) + { + uint8_t currentStrIdx = iter - dictionary.begin(); + + if (*iter != selectedStr || selectedStrIdx == currentStrIdx) continue; + + // Replace the current string with the new one + + for (auto &encoding : encodings) for (auto &byte : *encoding) + { + if (byte == currentStrIdx) byte = selectedStrIdx; + } + + // Mark obsolete dictionary entry as free for removal + + iter->clear(); + } - cleanupDictionary(); + cleanupDictionary(); - // Now handle normal cases, when the string needs to be split - // XXX - this is not time-effective, optimize this in the future + // Now handle normal cases, when the string needs to be split + // XXX - this is not time-effective, optimize this in the future - bool optimizeAgain = true; - bool nexIteration = true; - while (nexIteration) - { - nexIteration = false; + bool optimizeAgain = true; + bool nexIteration = true; + while (nexIteration) + { + nexIteration = false; - for (auto iter = dictionary.begin(); iter < dictionary.end(); iter++) - { - auto ¤tStr = *iter; - uint8_t currentStrIdx = iter - dictionary.begin(); - auto selectedStrIter = std::find(dictionary.begin(), dictionary.end(), selectedStr); - selectedStrIdx = selectedStrIter - dictionary.begin(); + for (auto iter = dictionary.begin(); iter < dictionary.end(); iter++) + { + auto ¤tStr = *iter; + uint8_t currentStrIdx = iter - dictionary.begin(); + auto selectedStrIter = std::find(dictionary.begin(), dictionary.end(), selectedStr); + selectedStrIdx = selectedStrIter - dictionary.begin(); - if (currentStrIdx == selectedStrIdx) continue; - - // Check if the current string contains the selected string - - auto pos = currentStr.find(selectedStr); - if (pos == std::string::npos) continue; - - nexIteration = true; - optimizeAgain = true; - - // Prepare replacement sequence for 'selectedStrIdx' - - std::vector replacement; - - if (pos == 0) - { - // The current dictionary string starts with our selected substring - - replacement.push_back(selectedStrIdx); - currentStr = currentStr.substr(selectedStr.size(), currentStr.size() - selectedStr.size()); - - if (!currentStr.empty()) - { - // Check if the remaining string is present somewhere else in the dictionary; if so, reuse it - - uint8_t pos2 = currentStrIdx; - for (auto iter2 = dictionary.begin(); iter2 < dictionary.end(); iter2++) - { - if (iter2 == iter) continue; - if (currentStr == *iter2) - { - dictionary[pos2].clear(); - pos2 = iter2 - dictionary.begin(); - break; - } - } - - replacement.push_back(pos2); - } - } - else - { - // The current dictionary string does not start with our selected substring - - std::string newStr = currentStr.substr(0, pos); - currentStr = currentStr.substr(pos, currentStr.size() - pos); - - // Check if the newStr is present somewhere else in the dictionary; if so, reuse it - - bool found = false; - uint8_t pos2 = 0; - for (auto iter2 = dictionary.begin(); iter2 < dictionary.end(); iter2++) - { - if (*iter2 == newStr) - { - found = true; - pos2 = iter2 - dictionary.begin(); + if (currentStrIdx == selectedStrIdx) continue; + + // Check if the current string contains the selected string + + auto pos = currentStr.find(selectedStr); + if (pos == std::string::npos) continue; + + nexIteration = true; + optimizeAgain = true; + + // Prepare replacement sequence for 'selectedStrIdx' + + std::vector replacement; + + if (pos == 0) + { + // The current dictionary string starts with our selected substring + + replacement.push_back(selectedStrIdx); + currentStr = currentStr.substr(selectedStr.size(), currentStr.size() - selectedStr.size()); + + if (!currentStr.empty()) + { + // Check if the remaining string is present somewhere else in the dictionary; if so, reuse it + + uint8_t pos2 = currentStrIdx; + for (auto iter2 = dictionary.begin(); iter2 < dictionary.end(); iter2++) + { + if (iter2 == iter) continue; + if (currentStr == *iter2) + { + dictionary[pos2].clear(); + pos2 = iter2 - dictionary.begin(); + break; + } + } + + replacement.push_back(pos2); + } + } + else + { + // The current dictionary string does not start with our selected substring + + std::string newStr = currentStr.substr(0, pos); + currentStr = currentStr.substr(pos, currentStr.size() - pos); + + // Check if the newStr is present somewhere else in the dictionary; if so, reuse it + + bool found = false; + uint8_t pos2 = 0; + for (auto iter2 = dictionary.begin(); iter2 < dictionary.end(); iter2++) + { + if (*iter2 == newStr) + { + found = true; + pos2 = iter2 - dictionary.begin(); - break; - } - } - - if (found) - { - replacement.push_back(pos2); - } - else - { - dictionary.push_back(newStr); - replacement.push_back(dictionary.size() - 1); - } + break; + } + } + + if (found) + { + replacement.push_back(pos2); + } + else + { + dictionary.push_back(newStr); + replacement.push_back(dictionary.size() - 1); + } - replacement.push_back(currentStrIdx); - } + replacement.push_back(currentStrIdx); + } - // Now replace all the occurences of 'selectedStrIdx' with a 'replacement' vector + // Now replace all the occurences of 'selectedStrIdx' with a 'replacement' vector - for (auto &encoding : encodings) - { - for (uint8_t idx = 0; idx < encoding->size(); idx++) - { - if ((*encoding)[idx] != currentStrIdx) continue; - - // Perform replacement - - encoding->erase(encoding->begin() + idx); - - for (const auto replacementIdx : replacement) - { - encoding->insert(encoding->begin() + idx, replacementIdx); - idx++; - } - - idx--; - } - } - - cleanupDictionary(); - } - } + for (auto &encoding : encodings) + { + for (uint8_t idx = 0; idx < encoding->size(); idx++) + { + if ((*encoding)[idx] != currentStrIdx) continue; + + // Perform replacement + + encoding->erase(encoding->begin() + idx); + + for (const auto replacementIdx : replacement) + { + encoding->insert(encoding->begin() + idx, replacementIdx); + idx++; + } + + idx--; + } + } + + cleanupDictionary(); + } + } - return optimizeAgain; + return optimizeAgain; } bool DictEncoder::optimizeJoin() { - // Try to optimize by joining two substrings - - // XXX - possible future improvement - - return false; + // Try to optimize by joining two substrings + + // XXX - possible future improvement + + return false; } void DictEncoder::optimizeOrder() { - // XXX - it would be better to do this after the dictionary is compressed + // XXX - it would be better to do this after the dictionary is compressed - // Try to optimize order of strings in the dictionary for fastest display - // - put the shorter substrings first - // - for strings with roughly the same length put more frequently used first - - typedef struct Entry - { - uint32_t penalty; - std::string word; - } Entry; - - // Create copy of the dictionary with optimization info - - std::vector optimizedOrder; - for (auto iter = dictionary.begin(); iter < dictionary.end(); iter++) - { - const auto &dictionaryStr = *iter; - - uint8_t sizePenalty = dictionaryStr.size() / 3; - uint16_t freqPenalty = 65535; - - // Calculate penalty for low occurence frequency - - for (auto &encoding : encodings) - { - for (auto &byte : *encoding) - { - if (byte == (iter - dictionary.begin())) freqPenalty--; - } - } + // Try to optimize order of strings in the dictionary for fastest display + // - put the shorter substrings first + // - for strings with roughly the same length put more frequently used first + + typedef struct Entry + { + uint32_t penalty; + std::string word; + } Entry; + + // Create copy of the dictionary with optimization info + + std::vector optimizedOrder; + for (auto iter = dictionary.begin(); iter < dictionary.end(); iter++) + { + const auto &dictionaryStr = *iter; + + uint8_t sizePenalty = dictionaryStr.size() / 3; + uint16_t freqPenalty = 65535; + + // Calculate penalty for low occurence frequency + + for (auto &encoding : encodings) + { + for (auto &byte : *encoding) + { + if (byte == (iter - dictionary.begin())) freqPenalty--; + } + } - // Create new entry - - Entry newEntry; - newEntry.word = dictionaryStr; - newEntry.penalty = sizePenalty * 65536 + freqPenalty; + // Create new entry + + Entry newEntry; + newEntry.word = dictionaryStr; + newEntry.penalty = sizePenalty * 65536 + freqPenalty; - optimizedOrder.push_back(newEntry); - } - + optimizedOrder.push_back(newEntry); + } + - // Determine ordering - sort from smallest penalty to largest - - std::sort(optimizedOrder.begin(), optimizedOrder.end(), - [](const Entry &e1, const Entry &e2) { return e1.penalty < e2.penalty; }); + // Determine ordering - sort from smallest penalty to largest + + std::sort(optimizedOrder.begin(), optimizedOrder.end(), + [](const Entry &e1, const Entry &e2) { return e1.penalty < e2.penalty; }); - // Create helper table for reordering - - std::vector reorderTable; - - for (uint8_t idx1 = 0; idx1 < dictionary.size(); idx1++) - { - uint8_t idx2 = 0; - while (dictionary[idx1] != optimizedOrder[idx2].word) idx2++; - - reorderTable.push_back(idx2); - } + // Create helper table for reordering + + std::vector reorderTable; + + for (uint8_t idx1 = 0; idx1 < dictionary.size(); idx1++) + { + uint8_t idx2 = 0; + while (dictionary[idx1] != optimizedOrder[idx2].word) idx2++; + + reorderTable.push_back(idx2); + } - // Perform the reordering - - for (auto &encoding : encodings) for (auto &byte : *encoding) - { - byte = reorderTable[byte]; - } - - for (uint8_t idx = 0; idx < dictionary.size(); idx++) dictionary[idx] = optimizedOrder[idx].word; + // Perform the reordering + + for (auto &encoding : encodings) for (auto &byte : *encoding) + { + byte = reorderTable[byte]; + } + + for (uint8_t idx = 0; idx < dictionary.size(); idx++) dictionary[idx] = optimizedOrder[idx].word; } void DictEncoder::process(StringEntryList &outDictionary) { - if (dictionary.empty()) return; - // Optimize as long as it brings any improvement + if (dictionary.empty()) return; + // Optimize as long as it brings any improvement - while (optimizeSplit() || optimizeJoin()) ; - optimizeOrder(); + while (optimizeSplit() || optimizeJoin()) ; + optimizeOrder(); - // Export the dictionary to external format + // Export the dictionary to external format - outDictionary.type = ListType::DICTIONARY; - outDictionary.name = "DICTIONARY"; + outDictionary.type = ListType::DICTIONARY; + outDictionary.name = "DICTIONARY"; - for (const auto &dictionaryStr : dictionary) - { - StringEntry newEntry = { true, true, true, "", dictionaryStr }; - outDictionary.list.push_back(newEntry); - } + for (const auto &dictionaryStr : dictionary) + { + StringEntry newEntry = { true, true, true, "", dictionaryStr }; + outDictionary.list.push_back(newEntry); + } - // Adapt the encoding to external format (0 = end of string) + // Adapt the encoding to external format (0 = end of string) - for (auto &encoding : encodings) - { - for (auto &byte : *encoding) byte++; - encoding->push_back(0); - } + for (auto &encoding : encodings) + { + for (auto &byte : *encoding) byte++; + encoding->push_back(0); + } } bool DataSet::isCompressionLvl2(const StringEntryList &list) const { - return (GLOBAL_ConfigOptions["CONFIG_COMPRESSION_LVL_2"] && list.type == ListType::STRINGS_BASIC); + return (GLOBAL_ConfigOptions["CONFIG_COMPRESSION_LVL_2"] && list.type == ListType::STRINGS_BASIC); } void DataSet::addStrings(const StringEntryList &stringList) { - // Import the new list of strings + // Import the new list of strings - stringEntryLists.push_back(stringList); - stringEncodedLists.emplace_back(); + stringEntryLists.push_back(stringList); + stringEncodedLists.emplace_back(); - // Clear strings not relevant for the current configuration - - while (1) - { - if (isRelevant(stringEntryLists.back().list.back())) break; + // Clear strings not relevant for the current configuration + + while (1) + { + if (isRelevant(stringEntryLists.back().list.back())) break; - stringEntryLists.back().list.pop_back(); - if (stringEntryLists.back().list.empty()) - { - ERROR(std::string("no valid strings in layout '") + layoutName() + "', list'" + stringEntryLists.back().name + "'"); - } - } - - // Clear the output content - make sure it is not valid anymore + stringEntryLists.back().list.pop_back(); + if (stringEntryLists.back().list.empty()) + { + ERROR(std::string("no valid strings in layout '") + layoutName() + "', list'" + stringEntryLists.back().name + "'"); + } + } + + // Clear the output content - make sure it is not valid anymore - outFileContent.clear(); + outFileContent.clear(); } void DataSet::process() { - std::cout << "Processing file '" << CMD_cnfFile << "', layout '" << layoutName() << "'" << std::endl; + std::cout << "Processing file '" << CMD_cnfFile << "', layout '" << layoutName() << "'" << std::endl; - generateConfigDepStrings(); - validateLists(); - encodeStringsDict(); - calculateFrequencies(); - encodeStringsFreq(); - prepareOutput(); + generateConfigDepStrings(); + validateLists(); + encodeStringsDict(); + calculateFrequencies(); + encodeStringsFreq(); + prepareOutput(); } const std::string &DataSet::getOutput() { - if (outFileContent.empty()) - { - process(); - } + if (outFileContent.empty()) + { + process(); + } - return outFileContent; + return outFileContent; } void DataSet::generateConfigDepStrings() { - // Generate string to show the build features - - std::string featureStr; - - // Tape support features - - if (GLOBAL_ConfigOptions["CONFIG_TAPE_NORMAL"] && GLOBAL_ConfigOptions["CONFIG_TAPE_TURBO"]) - { - featureStr = "TAPE LOAD NORMAL TURBO\r"; - } - else if (GLOBAL_ConfigOptions["CONFIG_TAPE_NORMAL"]) - { - featureStr = "TAPE LOAD NORMAL\r"; - } - else if (GLOBAL_ConfigOptions["CONFIG_TAPE_TURBO"]) - { - featureStr = "TAPE LOAD TURBO\r"; - } - - // IEC support features - - if (GLOBAL_ConfigOptions["CONFIG_IEC"]) - { - featureStr += "IEC"; - - bool extendedIEC = false; - - if (GLOBAL_ConfigOptions["CONFIG_IEC_BURST_CIA1"]) - { - featureStr += " BURST1"; - extendedIEC = true; - } - if (GLOBAL_ConfigOptions["CONFIG_IEC_BURST_CIA2"]) - { - featureStr += " BURST2"; - extendedIEC = true; - } - if (GLOBAL_ConfigOptions["CONFIG_IEC_BURST_MEGA65"]) - { - featureStr += " BURST"; - extendedIEC = true; - } - - if (GLOBAL_ConfigOptions["CONFIG_IEC_DOLPHINDOS"]) - { - featureStr += " DOLPHIN"; - extendedIEC = true; - } - - if (GLOBAL_ConfigOptions["CONFIG_IEC_JIFFYDOS"]) - { - featureStr += " JIFFY"; - extendedIEC = true; - } - - if (!extendedIEC) featureStr += " NORMAL"; - - featureStr += "\r"; - } - - // RS-232 support features - - if (GLOBAL_ConfigOptions["CONFIG_RS232_UP2400"]) featureStr += "UP2400\r"; - if (GLOBAL_ConfigOptions["CONFIG_RS232_UP9600"]) featureStr += "UP9600\r"; - - // Keyboard support features - - if (GLOBAL_ConfigOptions["CONFIG_KEYBOARD_C128"]) featureStr += "KBD 128\r"; - if (GLOBAL_ConfigOptions["CONFIG_KEYBOARD_C65"]) featureStr += "KBD 65\r"; + // Generate string to show the build features + + std::string featureStr; + + // Tape support features + + if (GLOBAL_ConfigOptions["CONFIG_TAPE_NORMAL"] && GLOBAL_ConfigOptions["CONFIG_TAPE_TURBO"]) + { + featureStr = "TAPE LOAD NORMAL TURBO\r"; + } + else if (GLOBAL_ConfigOptions["CONFIG_TAPE_NORMAL"]) + { + featureStr = "TAPE LOAD NORMAL\r"; + } + else if (GLOBAL_ConfigOptions["CONFIG_TAPE_TURBO"]) + { + featureStr = "TAPE LOAD TURBO\r"; + } + + // IEC support features + + if (GLOBAL_ConfigOptions["CONFIG_IEC"]) + { + featureStr += "IEC"; + + bool extendedIEC = false; + + if (GLOBAL_ConfigOptions["CONFIG_IEC_BURST_CIA1"]) + { + featureStr += " BURST1"; + extendedIEC = true; + } + if (GLOBAL_ConfigOptions["CONFIG_IEC_BURST_CIA2"]) + { + featureStr += " BURST2"; + extendedIEC = true; + } + if (GLOBAL_ConfigOptions["CONFIG_IEC_BURST_MEGA65"]) + { + featureStr += " BURST"; + extendedIEC = true; + } + + if (GLOBAL_ConfigOptions["CONFIG_IEC_DOLPHINDOS"]) + { + featureStr += " DOLPHIN"; + extendedIEC = true; + } + + if (GLOBAL_ConfigOptions["CONFIG_IEC_JIFFYDOS"]) + { + featureStr += " JIFFY"; + extendedIEC = true; + } + + if (!extendedIEC) featureStr += " NORMAL"; + + featureStr += "\r"; + } + + // RS-232 support features + + if (GLOBAL_ConfigOptions["CONFIG_RS232_UP2400"]) featureStr += "UP2400\r"; + if (GLOBAL_ConfigOptions["CONFIG_RS232_UP9600"]) featureStr += "UP9600\r"; + + // Keyboard support features + + if (GLOBAL_ConfigOptions["CONFIG_KEYBOARD_C128"]) featureStr += "KBD 128\r"; + if (GLOBAL_ConfigOptions["CONFIG_KEYBOARD_C65"]) featureStr += "KBD 65\r"; - // Add strings to appropriate list - - for (auto &stringEntryList : stringEntryLists) - { - if (stringEntryList.name != std::string("misc")) continue; + // Add strings to appropriate list + + for (auto &stringEntryList : stringEntryLists) + { + if (stringEntryList.name != std::string("misc")) continue; - // List found + // List found - if (GLOBAL_ConfigOptions["CONFIG_SHOW_FEATURES"]) - { - StringEntry newEntry1 = { true, true, true, "STR_PAL", "PAL\r" }; - StringEntry newEntry2 = { true, true, true, "STR_NTSC", "NTSC\r" }; - StringEntry newEntry3 = { true, true, true, "STR_FEATURES", featureStr }; + if (GLOBAL_ConfigOptions["CONFIG_SHOW_FEATURES"]) + { + StringEntry newEntry1 = { true, true, true, true, "STR_PAL", "PAL\r" }; + StringEntry newEntry2 = { true, true, true, true, "STR_NTSC", "NTSC\r" }; + StringEntry newEntry3 = { true, true, true, true, "STR_FEATURES", featureStr }; - stringEntryList.list.push_back(newEntry1); - stringEntryList.list.push_back(newEntry2); - stringEntryList.list.push_back(newEntry3); + stringEntryList.list.push_back(newEntry1); + stringEntryList.list.push_back(newEntry2); + stringEntryList.list.push_back(newEntry3); - break; - } - } + break; + } + } } void DataSet::validateLists() { - for (const auto &stringEntryList : stringEntryLists) - { - for (const auto &stringEntry : stringEntryList.list) - { - // Check for maximum allowed string length - if (stringEntry.string.length() > 255) ERROR("string cannot be longer than 255 characters"); + for (const auto &stringEntryList : stringEntryLists) + { + for (const auto &stringEntry : stringEntryList.list) + { + // Check for maximum allowed string length + if (stringEntry.string.length() > 255) ERROR("string cannot be longer than 255 characters"); - // Check mor maximum keyword length - if (stringEntryList.type == ListType::KEYWORDS) - { - if (stringEntry.string.length() > 16) ERROR("keyword cannot be longer than 16 characters"); - tk__max_keyword_len = std::max(tk__max_keyword_len, (uint8_t) stringEntry.string.length()); - } + // Check mor maximum keyword length + if (stringEntryList.type == ListType::KEYWORDS) + { + if (stringEntry.string.length() > 16) ERROR("keyword cannot be longer than 16 characters"); + tk__max_keyword_len = std::max(tk__max_keyword_len, (uint8_t) stringEntry.string.length()); + } - // Update maximum alias length too - maxAliasLen = std::max(maxAliasLen, stringEntry.alias.length()); + // Update maximum alias length too + maxAliasLen = std::max(maxAliasLen, stringEntry.alias.length()); - for (const auto &character : stringEntry.string) - { - if ((unsigned char) character >= 0x80) - { - ERROR(std::string("character above 0x80 in string '") + stringEntry.string + "'"); - } - } - } - } + for (const auto &character : stringEntry.string) + { + if ((unsigned char) character >= 0x80) + { + ERROR(std::string("character above 0x80 in string '") + stringEntry.string + "'"); + } + } + } + } } void DataSet::encodeStringsDict() { - DictEncoder dictEncoder; + DictEncoder dictEncoder; - // Add strings for dictionary compresssion + // Add strings for dictionary compresssion - for (uint8_t idx = 0; idx < stringEntryLists.size(); idx++) - { - const auto &stringEntryList = stringEntryLists[idx]; - auto &stringEncodedList = stringEncodedLists[idx]; + for (uint8_t idx = 0; idx < stringEntryLists.size(); idx++) + { + const auto &stringEntryList = stringEntryLists[idx]; + auto &stringEncodedList = stringEncodedLists[idx]; - // Skip lists not to be encoded using the dictionary - if (!isCompressionLvl2(stringEntryList)) continue; + // Skip lists not to be encoded using the dictionary + if (!isCompressionLvl2(stringEntryList)) continue; - stringEncodedList.resize(stringEntryList.list.size()); - for (uint8_t idxEntry = 0; idxEntry < stringEntryList.list.size(); idxEntry++) - { - dictEncoder.addString(stringEntryList.list[idxEntry].string, - &stringEncodedList[idxEntry]); - } - } + stringEncodedList.resize(stringEntryList.list.size()); + for (uint8_t idxEntry = 0; idxEntry < stringEntryList.list.size(); idxEntry++) + { + dictEncoder.addString(stringEntryList.list[idxEntry].string, + &stringEncodedList[idxEntry]); + } + } - // Perform the compression + // Perform the compression - StringEntryList dictionary; - dictEncoder.process(dictionary); + StringEntryList dictionary; + dictEncoder.process(dictionary); - // Add new lists + // Add new lists - stringEntryLists.push_back(dictionary); - stringEncodedLists.emplace_back(); + stringEntryLists.push_back(dictionary); + stringEncodedLists.emplace_back(); } void DataSet::calculateFrequencies() { - as1n.clear(); - as3n.clear(); - - std::map freqMapGeneral; // general character frequency map - std::map freqMapKeywords; // frequency map for keywords + as1n.clear(); + as3n.clear(); + + std::map freqMapGeneral; // general character frequency map + std::map freqMapKeywords; // frequency map for keywords - // Calculate frequencies of characters in the strings + // Calculate frequencies of characters in the strings - for (const auto &stringEntryList : stringEntryLists) - { - // Skip lists encoded by the dictionary - if (isCompressionLvl2(stringEntryList)) continue; + for (const auto &stringEntryList : stringEntryLists) + { + // Skip lists encoded by the dictionary + if (isCompressionLvl2(stringEntryList)) continue; - for (const auto &stringEntry : stringEntryList.list) - { - if (!isRelevant(stringEntry)) continue; + for (const auto &stringEntry : stringEntryList.list) + { + if (!isRelevant(stringEntry)) continue; - for (const auto &character : stringEntry.string) - { - freqMapGeneral[character]++; - if (stringEntryList.type == ListType::KEYWORDS) freqMapKeywords[character]++; - } - } - } - - // Sort characters by frequency - - std::vector freqVector1; - - for (auto iter = freqMapGeneral.begin(); iter != freqMapGeneral.end(); ++iter) - { - freqVector1.push_back(iter->first); - } - - std::sort(freqVector1.begin(), freqVector1.end(), [&freqMapGeneral](char e1, char e2) - { return freqMapGeneral[e2] < freqMapGeneral[e1]; }); + for (const auto &character : stringEntry.string) + { + freqMapGeneral[character]++; + if (stringEntryList.type == ListType::KEYWORDS) freqMapKeywords[character]++; + } + } + } + + // Sort characters by frequency + + std::vector freqVector1; + + for (auto iter = freqMapGeneral.begin(); iter != freqMapGeneral.end(); ++iter) + { + freqVector1.push_back(iter->first); + } + + std::sort(freqVector1.begin(), freqVector1.end(), [&freqMapGeneral](char e1, char e2) + { return freqMapGeneral[e2] < freqMapGeneral[e1]; }); - // Check if minimal amount of characters needed, below 15 is not supported by the 6502 side code + // Check if minimal amount of characters needed, below 15 is not supported by the 6502 side code - if (freqVector1.size() < 15) ERROR(std::string("not enough distinct characters in layout '") + layoutName() + "', at least 15 needed"); + if (freqVector1.size() < 15) ERROR(std::string("not enough distinct characters in layout '") + layoutName() + "', at least 15 needed"); - // Extract 14 most frequent characters to be encoded as 1 nibble - - for (uint8_t idx = 0; idx < 14; idx++) - { - as1n.push_back(freqVector1[idx]); - } - - // Now sort them by frequency in keywords, in descending order - this will speed up the tokenizer a little - - std::sort(as1n.begin(), as1n.end(), [&freqMapKeywords](char e1, char e2) { return freqMapKeywords[e2] > freqMapKeywords[e1]; }); + // Extract 14 most frequent characters to be encoded as 1 nibble + + for (uint8_t idx = 0; idx < 14; idx++) + { + as1n.push_back(freqVector1[idx]); + } + + // Now sort them by frequency in keywords, in descending order - this will speed up the tokenizer a little + + std::sort(as1n.begin(), as1n.end(), [&freqMapKeywords](char e1, char e2) { return freqMapKeywords[e2] > freqMapKeywords[e1]; }); - // Extract characters to be encoded as 3 nibbles, which actually exist in keywords + // Extract characters to be encoded as 3 nibbles, which actually exist in keywords - std::vector freqVector2; + std::vector freqVector2; - tk__packed_as_3n = 0; - for (uint8_t idx = 14; idx < freqVector1.size(); idx++) - { - const auto &character = freqVector1[idx]; - - if (freqMapKeywords[character] > 0) - { - as3n.push_back(character); - tk__packed_as_3n++; - } - else - { - freqVector2.push_back(character); - } - } - tk__packed_as_3n = std::max(tk__packed_as_3n, (uint8_t) 1); + tk__packed_as_3n = 0; + for (uint8_t idx = 14; idx < freqVector1.size(); idx++) + { + const auto &character = freqVector1[idx]; + + if (freqMapKeywords[character] > 0) + { + as3n.push_back(character); + tk__packed_as_3n++; + } + else + { + freqVector2.push_back(character); + } + } + tk__packed_as_3n = std::max(tk__packed_as_3n, (uint8_t) 1); - // Again, sort them by frequency in keywords, in descending order - this will speed up the tokenizer a little - - std::sort(as3n.begin(), as3n.end(), [&freqMapKeywords](char e1, char e2) { return freqMapKeywords[e2] > freqMapKeywords[e1]; }); + // Again, sort them by frequency in keywords, in descending order - this will speed up the tokenizer a little + + std::sort(as3n.begin(), as3n.end(), [&freqMapKeywords](char e1, char e2) { return freqMapKeywords[e2] > freqMapKeywords[e1]; }); - // Finally extract the remaining characters to be encoded as 3 nibbles + // Finally extract the remaining characters to be encoded as 3 nibbles - for (const auto &character : freqVector2) - { - as3n.push_back(character); - } + for (const auto &character : freqVector2) + { + as3n.push_back(character); + } } void DataSet::encodeByFreq(const std::string &plain, StringEncoded &encoded) const { - bool fullByte = true; + bool fullByte = true; - auto push1n = [&](uint8_t val) // push 1 nibble - encoded character or 0xF mark - { - if (fullByte) - { - encoded.push_back(val); - fullByte = false; - } - else - { - encoded.back() += val * 0x10; - fullByte = true; - } - }; + auto push1n = [&](uint8_t val) // push 1 nibble - encoded character or 0xF mark + { + if (fullByte) + { + encoded.push_back(val); + fullByte = false; + } + else + { + encoded.back() += val * 0x10; + fullByte = true; + } + }; - auto push2n = [&](uint8_t val) // push the remaining nibbles for 3-niobble encoded characters - { - if (fullByte) - { - encoded.push_back(val); - } - else - { - // Encode byte in a way to be easily decoded by 6502 - encoded.back() += (val / 0x10) * 0x10; - encoded.push_back(val % 0x10); - } - }; + auto push2n = [&](uint8_t val) // push the remaining nibbles for 3-niobble encoded characters + { + if (fullByte) + { + encoded.push_back(val); + } + else + { + // Encode byte in a way to be easily decoded by 6502 + encoded.back() += (val / 0x10) * 0x10; + encoded.push_back(val % 0x10); + } + }; - // Encode every single character by frequency, put them in the output vector + // Encode every single character by frequency, put them in the output vector - for (const char &character : plain) - { - auto iterEncoding1 = std::find(as1n.begin(), as1n.end(), character); - if (iterEncoding1 != as1n.end()) - { - push1n(std::distance(as1n.begin(), iterEncoding1) + 1); - } - else - { - push1n(0x0F); + for (const char &character : plain) + { + auto iterEncoding1 = std::find(as1n.begin(), as1n.end(), character); + if (iterEncoding1 != as1n.end()) + { + push1n(std::distance(as1n.begin(), iterEncoding1) + 1); + } + else + { + push1n(0x0F); - auto iterEncoding3 = std::find(as3n.begin(), as3n.end(), character); - if (iterEncoding3 == as3n.end()) - { - ERROR("internal error in 'encodeByFreq'"); - } - push2n(std::distance(as3n.begin(), iterEncoding3) + 1); - } - } + auto iterEncoding3 = std::find(as3n.begin(), as3n.end(), character); + if (iterEncoding3 == as3n.end()) + { + ERROR("internal error in 'encodeByFreq'"); + } + push2n(std::distance(as3n.begin(), iterEncoding3) + 1); + } + } - // Make sure the last byte of encoded stream is 0 + // Make sure the last byte of encoded stream is 0 - if (encoded.size() == 0 || encoded.back() != 0) encoded.push_back(0); + if (encoded.size() == 0 || encoded.back() != 0) encoded.push_back(0); } void DataSet::encodeStringsFreq() { - // Encode every relevant string from every list - by character frequency + // Encode every relevant string from every list - by character frequency - for (uint8_t idx = 0; idx < stringEntryLists.size(); idx++) - { - const auto &stringEntryList = stringEntryLists[idx]; - auto &stringEncodedList = stringEncodedLists[idx]; + for (uint8_t idx = 0; idx < stringEntryLists.size(); idx++) + { + const auto &stringEntryList = stringEntryLists[idx]; + auto &stringEncodedList = stringEncodedLists[idx]; - // Skip lists encoded by the dictionary - if (isCompressionLvl2(stringEntryList)) continue; + // Skip lists encoded by the dictionary + if (isCompressionLvl2(stringEntryList)) continue; - // Perform frequency encoding of the list + // Perform frequency encoding of the list - for (const auto &stringEntry : stringEntryList.list) - { - stringEncodedList.emplace_back(); - auto &stringEncoded = stringEncodedList.back(); + for (const auto &stringEntry : stringEntryList.list) + { + stringEncodedList.emplace_back(); + auto &stringEncoded = stringEncodedList.back(); - if (isRelevant(stringEntry)) - { - encodeByFreq(stringEntry.string, stringEncoded); - } - } - } + if (isRelevant(stringEntry)) + { + encodeByFreq(stringEntry.string, stringEncoded); + } + } + } } void DataSet::putCharEncoding(std::ostringstream &stream, uint8_t idx, char character, bool is3n) { - stream << "\t.byte $" << std::uppercase << std::hex << - std::setfill('0') << std::setw(2) << +character << - " // " << std::setfill(is3n ? '0' : ' ') << std::setw(2) << +idx; + stream << "\t.byte $" << std::uppercase << std::hex << + std::setfill('0') << std::setw(2) << +character << + " // " << std::setfill(is3n ? '0' : ' ') << std::setw(2) << +idx; - std::string petscii; + std::string petscii; - if (character == 0x20) - { - petscii = " = SPACE"; - } - else if (character == 0x27) - { - petscii = " = APOSTROPHE"; - } - else if (character == 0x0D) - { - petscii = " = RETURN"; - } - else if (character > ' ' && character < 'a') - { - petscii = std::string(" = '") + character + "'"; - } + if (character == 0x20) + { + petscii = " = SPACE"; + } + else if (character == 0x27) + { + petscii = " = APOSTROPHE"; + } + else if (character == 0x0D) + { + petscii = " = RETURN"; + } + else if (character > ' ' && character < 'a') + { + petscii = std::string(" = '") + character + "'"; + } - stream << petscii << std::endl; + stream << petscii << std::endl; } void DataSet::prepareOutput_1n_3n(std::ostringstream &stream) { - uint8_t idx; + uint8_t idx; - // Export all nibble-encoded characters + // Export all nibble-encoded characters - stream << std::endl << ".macro put_packed_as_1n() // characters encoded as 1 nibble" << std::endl << "{" << std::endl; - - idx = 0; - for (const auto& encoding : as1n) - { - putCharEncoding(stream, ++idx, encoding, false); - } - - stream << "}" << std::endl; + stream << std::endl << ".macro put_packed_as_1n() // characters encoded as 1 nibble" << std::endl << "{" << std::endl; + + idx = 0; + for (const auto& encoding : as1n) + { + putCharEncoding(stream, ++idx, encoding, false); + } + + stream << "}" << std::endl; - // Export all byte-encoded characters + // Export all byte-encoded characters - stream << std::endl << ".macro put_packed_as_3n() // characters encoded as 3 nibbles" << std::endl << "{" << std::endl; + stream << std::endl << ".macro put_packed_as_3n() // characters encoded as 3 nibbles" << std::endl << "{" << std::endl; - idx = 0; - for (const auto& encoding : as3n) - { - if (idx == tk__packed_as_3n) stream << std::endl << + idx = 0; + for (const auto& encoding : as3n) + { + if (idx == tk__packed_as_3n) stream << std::endl << "\t// Characters below are not used by any BASIC keyword" << std::endl << std::endl; - putCharEncoding(stream, ++idx, encoding, true); - } + putCharEncoding(stream, ++idx, encoding, true); + } - stream << "}" << std::endl; + stream << "}" << std::endl; } void DataSet::prepareOutput_labels(std::ostringstream &stream, - const StringEntryList &stringEntryList, - const StringEncodedList &stringEncodedList) + const StringEntryList &stringEntryList, + const StringEncodedList &stringEncodedList) { - // For the dictionary we do not have any labels - if (stringEntryList.type == ListType::DICTIONARY) return; + // For the dictionary we do not have any labels + if (stringEntryList.type == ListType::DICTIONARY) return; - stream << std::endl; - for (uint8_t idx = 0; idx < stringEncodedList.size(); idx++) - { - const auto &stringEntry = stringEntryList.list[idx]; - const auto &stringEncoded = stringEncodedList[idx]; + stream << std::endl; + for (uint8_t idx = 0; idx < stringEncodedList.size(); idx++) + { + const auto &stringEntry = stringEntryList.list[idx]; + const auto &stringEncoded = stringEncodedList[idx]; - if (!stringEncoded.empty()) - { - stream << ".label IDX__" << stringEntry.alias << - std::string(maxAliasLen - stringEntry.alias.length(), ' ') << " = $" << - std::uppercase << std::hex << std::setfill('0') << std::setw(2) << +idx << std::endl; - } - } + if (!stringEncoded.empty()) + { + stream << ".label IDX__" << stringEntry.alias << + std::string(maxAliasLen - stringEntry.alias.length(), ' ') << " = $" << + std::uppercase << std::hex << std::setfill('0') << std::setw(2) << +idx << std::endl; + } + } } void DataSet::prepareOutput_packed(std::ostringstream &stream, - const StringEntryList &stringEntryList, - const StringEncodedList &stringEncodedList) + const StringEntryList &stringEntryList, + const StringEncodedList &stringEncodedList) { - if (isCompressionLvl2(stringEntryList)) - { - stream << std::endl << ".macro put_packed_dict_"; - } - else - { - stream << std::endl << ".macro put_packed_freq_"; - } + if (isCompressionLvl2(stringEntryList)) + { + stream << std::endl << ".macro put_packed_dict_"; + } + else + { + stream << std::endl << ".macro put_packed_freq_"; + } - stream << stringEntryList.name << "()" << std::endl << "{" << std::endl; + stream << stringEntryList.name << "()" << std::endl << "{" << std::endl; - enum LastStr { NONE, SKIPPED, WRITTEN } lastStr = LastStr::NONE; - for (uint8_t idxString = 0; idxString < stringEncodedList.size(); idxString++) - { - const auto &stringEncoded = stringEncodedList[idxString]; + enum LastStr { NONE, SKIPPED, WRITTEN } lastStr = LastStr::NONE; + for (uint8_t idxString = 0; idxString < stringEncodedList.size(); idxString++) + { + const auto &stringEncoded = stringEncodedList[idxString]; - if (stringEncoded.empty()) - { - if (stringEntryList.type == ListType::DICTIONARY) ERROR("internal error"); // should never happen + if (stringEncoded.empty()) + { + if (stringEntryList.type == ListType::DICTIONARY) ERROR("internal error"); // should never happen - if (lastStr == LastStr::WRITTEN) stream << std::endl; - stream << "\t.byte $00 // skipped " << stringEntryList.list[idxString].alias << std::endl; - lastStr = LastStr::SKIPPED; - } - else - { - if (lastStr != LastStr::NONE) stream << std::endl; + if (lastStr == LastStr::WRITTEN) stream << std::endl; + stream << "\t.byte $00 // skipped " << stringEntryList.list[idxString].alias << std::endl; + lastStr = LastStr::SKIPPED; + } + else + { + if (lastStr != LastStr::NONE) stream << std::endl; - // Output the label - as a comment + // Output the label - as a comment - if (stringEntryList.type != ListType::DICTIONARY) - { - stream << "\t// IDX__" << stringEntryList.list[idxString].alias << std::endl; - } + if (stringEntryList.type != ListType::DICTIONARY) + { + stream << "\t// IDX__" << stringEntryList.list[idxString].alias << std::endl; + } - // Output the source string - as a comment + // Output the source string - as a comment - stream << "\t// '"; - for (auto &character : stringEntryList.list[idxString].string) - { - if (character >= 32 && character <= 132 && character != 39 && character != 34) - { - stream << character; - } - else if (character == 13) - { - stream << ""; - } - else - { - stream << "_"; - } - } - stream << "'" << std::endl; + stream << "\t// '"; + for (auto &character : stringEntryList.list[idxString].string) + { + if (character >= 32 && character <= 132 && character != 39 && character != 34) + { + stream << character; + } + else if (character == 13) + { + stream << ""; + } + else + { + stream << "_"; + } + } + stream << "'" << std::endl; - // Output the encoding + // Output the encoding - stream << "\t.byte "; + stream << "\t.byte "; - bool first = true; - for (const auto &charEncoded : stringEncoded) - { - if (first) - { - first = false; - } - else - { - stream << ", "; - } + bool first = true; + for (const auto &charEncoded : stringEncoded) + { + if (first) + { + first = false; + } + else + { + stream << ", "; + } - stream << "$" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << +charEncoded; - } + stream << "$" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << +charEncoded; + } - stream << std::endl; - lastStr = LastStr::WRITTEN; - } - } + stream << std::endl; + lastStr = LastStr::WRITTEN; + } + } - // For the token list - put the 'end of keywords' mark + // For the token list - put the 'end of keywords' mark - if (stringEntryList.type == ListType::KEYWORDS) - { - stream << std::endl << "\t// Marker - end of the keyword list" << std::endl; - stream << "\t.byte $FF, $FF" << std::endl; - } + if (stringEntryList.type == ListType::KEYWORDS) + { + stream << std::endl << "\t// Marker - end of the keyword list" << std::endl; + stream << "\t.byte $FF, $FF" << std::endl; + } - stream << "}" << std::endl; + stream << "}" << std::endl; } void DataSet::prepareOutput() { - // Convert our encoded strings to a KickAssembler source + // Convert our encoded strings to a KickAssembler source - std::ostringstream stream; - stream << std::endl << "#if ROM_LAYOUT_" << layoutName() << std::endl; + std::ostringstream stream; + stream << std::endl << "#if ROM_LAYOUT_" << layoutName() << std::endl; - // Export 1-nibble and 3-nibble encoding data + // Export 1-nibble and 3-nibble encoding data - prepareOutput_1n_3n(stream); + prepareOutput_1n_3n(stream); - // Export additional data for the tokenizer + // Export additional data for the tokenizer - stream << std::endl << ".label TK__PACKED_AS_3N = $" << std::hex << +tk__packed_as_3n << + stream << std::endl << ".label TK__PACKED_AS_3N = $" << std::hex << +tk__packed_as_3n << std::endl << ".label TK__MAX_KEYWORD_LEN = " << std::dec << +tk__max_keyword_len << std::endl; - // Export encoded strings + // Export encoded strings - for (uint8_t idx = 0; idx < stringEntryLists.size(); idx++) - { - const auto &stringEntryList = stringEntryLists[idx]; - const auto &stringEncodedList = stringEncodedLists[idx]; + for (uint8_t idx = 0; idx < stringEntryLists.size(); idx++) + { + const auto &stringEntryList = stringEntryLists[idx]; + const auto &stringEncodedList = stringEncodedLists[idx]; - if (stringEncodedList.empty()) continue; + if (stringEncodedList.empty()) continue; - // Export labels for the current list + // Export labels for the current list - prepareOutput_labels(stream, stringEntryList, stringEncodedList); + prepareOutput_labels(stream, stringEntryList, stringEncodedList); - // For the token list - put the number of tokens available + // For the token list - put the number of tokens available - if (stringEntryList.type == ListType::KEYWORDS) - { - stream << std::endl << ".label TK__MAXTOKEN_" << stringEntryList.name << " = " << - std::dec << stringEncodedList.size() << std::endl; - } + if (stringEntryList.type == ListType::KEYWORDS) + { + stream << std::endl << ".label TK__MAXTOKEN_" << stringEntryList.name << " = " << + std::dec << stringEncodedList.size() << std::endl; + } - // Export the packed data + // Export the packed data - prepareOutput_packed(stream, stringEntryList, stringEncodedList); - } + prepareOutput_packed(stream, stringEntryList, stringEncodedList); + } - // Finalize the file stream + // Finalize the file stream - stream << std::endl << "#endif // ROM_LAYOUT_" << layoutName() << std::endl; - outFileContent = stream.str(); + stream << std::endl << "#endif // ROM_LAYOUT_" << layoutName() << std::endl; + outFileContent = stream.str(); } @@ -1667,52 +1677,52 @@ void DataSet::prepareOutput() void parseConfigFile() { - GLOBAL_ConfigOptions.clear(); - - // Open the configuration file - + GLOBAL_ConfigOptions.clear(); + + // Open the configuration file + std::ifstream cnfFile; cnfFile.open(CMD_cnfFile); if (!cnfFile.good()) ERROR("unable to open config file"); - - // Parse the file - + + // Parse the file + size_t lineNum = 0; - while (!cnfFile.eof()) - { - lineNum++; - std::string workStr; - - // Read a single line, remove leading spaces and tabs + while (!cnfFile.eof()) + { + lineNum++; + std::string workStr; + + // Read a single line, remove leading spaces and tabs - std::getline(cnfFile, workStr); - if (cnfFile.bad()) ERROR("error reading configuration file"); - workStr = std::regex_replace(workStr, std::regex("^[ \t]+"), ""); - - // Skip lines which are not preprocessor definitions - - if (workStr.empty() || workStr[0] != '#') continue; - - // Make sure this is '#define ' - - if (!std::regex_match(workStr, std::regex("^#define[ \t].*"))) - { - ERROR(std::string("only '#define' preprocessor directives allowed in config files - line ") + std::to_string(lineNum)); - } + std::getline(cnfFile, workStr); + if (cnfFile.bad()) ERROR("error reading configuration file"); + workStr = std::regex_replace(workStr, std::regex("^[ \t]+"), ""); + + // Skip lines which are not preprocessor definitions + + if (workStr.empty() || workStr[0] != '#') continue; + + // Make sure this is '#define ' + + if (!std::regex_match(workStr, std::regex("^#define[ \t].*"))) + { + ERROR(std::string("only '#define' preprocessor directives allowed in config files - line ") + std::to_string(lineNum)); + } - // Get rid of the directive and trailing spaces/comments - - workStr = std::regex_replace(workStr, std::regex("^#define[ \t]+"), ""); - workStr = std::regex_replace(workStr, std::regex("[ \t]+.*"), ""); + // Get rid of the directive and trailing spaces/comments + + workStr = std::regex_replace(workStr, std::regex("^#define[ \t]+"), ""); + workStr = std::regex_replace(workStr, std::regex("[ \t]+.*"), ""); - // Add definitin to config option map + // Add definitin to config option map - if (workStr.empty()) ERROR(std::string("error parsing config file - line ") + std::to_string(lineNum)); + if (workStr.empty()) ERROR(std::string("error parsing config file - line ") + std::to_string(lineNum)); - GLOBAL_ConfigOptions[workStr] = true; - } - - cnfFile.close(); + GLOBAL_ConfigOptions[workStr] = true; + } + + cnfFile.close(); } void printUsage() @@ -1739,7 +1749,7 @@ void parseCommandLine(int argc, char **argv) switch(opt) { case 'o': CMD_outFile = optarg; break; - case 'c': CMD_cnfFile = optarg; break; + case 'c': CMD_cnfFile = optarg; break; default: printUsage(); ERROR(); } } @@ -1747,61 +1757,77 @@ void parseCommandLine(int argc, char **argv) void writeStrings() { - std::string outputString; - - if (GLOBAL_ConfigOptions["CONFIG_PLATFORM_COMMANDER_X16"]) - { - DataSetX16 dataSetX16; + std::string outputString; + + if (GLOBAL_ConfigOptions["CONFIG_PLATFORM_COMMANDER_X16"]) + { + DataSetX16 dataSetX16; - // Add input data to computation objects - - dataSetX16.addStrings(GLOBAL_Keywords_V2); - dataSetX16.addStrings(GLOBAL_Keywords_CC); - dataSetX16.addStrings(GLOBAL_Keywords_CD); - dataSetX16.addStrings(GLOBAL_Errors); - dataSetX16.addStrings(GLOBAL_MiscStrings); + // Add input data to computation objects + + dataSetX16.addStrings(GLOBAL_Keywords_V2); + dataSetX16.addStrings(GLOBAL_Keywords_CC); + dataSetX16.addStrings(GLOBAL_Keywords_CD); + dataSetX16.addStrings(GLOBAL_Errors); + dataSetX16.addStrings(GLOBAL_MiscStrings); - // Retrieve the results - - outputString = dataSetX16.getOutput(); - } - else if (GLOBAL_ConfigOptions["CONFIG_PLATFORM_COMMODORE_64"] && GLOBAL_ConfigOptions["CONFIG_MB_MEGA_65"]) - { - DataSetM65 dataSetM65; + // Retrieve the results + + outputString = dataSetX16.getOutput(); + } + else if (GLOBAL_ConfigOptions["CONFIG_PLATFORM_COMMODORE_64"] && GLOBAL_ConfigOptions["CONFIG_MB_MEGA_65"]) + { + DataSetM65 dataSetM65; - // Add input data to computation objects + // Add input data to computation objects - dataSetM65.addStrings(GLOBAL_Keywords_V2); - dataSetM65.addStrings(GLOBAL_Keywords_CC); - dataSetM65.addStrings(GLOBAL_Keywords_CD); - dataSetM65.addStrings(GLOBAL_Errors); - dataSetM65.addStrings(GLOBAL_MiscStrings); - - // Retrieve the results - - outputString = dataSetM65.getOutput(); - } - else if (GLOBAL_ConfigOptions["CONFIG_PLATFORM_COMMODORE_64"]) - { - DataSetSTD dataSetSTD; + dataSetM65.addStrings(GLOBAL_Keywords_V2); + dataSetM65.addStrings(GLOBAL_Keywords_CC); + dataSetM65.addStrings(GLOBAL_Keywords_CD); + dataSetM65.addStrings(GLOBAL_Errors); + dataSetM65.addStrings(GLOBAL_MiscStrings); + + // Retrieve the results + + outputString = dataSetM65.getOutput(); + } + else if (GLOBAL_ConfigOptions["CONFIG_PLATFORM_COMMODORE_64"] && GLOBAL_ConfigOptions["CONFIG_MB_ULTIMATE_64"]) + { + DataSetU64 dataSetU64; - // Add input data to computation objects + // Add input data to computation objects - dataSetSTD.addStrings(GLOBAL_Keywords_V2); - dataSetSTD.addStrings(GLOBAL_Keywords_CC); - dataSetSTD.addStrings(GLOBAL_Keywords_CD); - dataSetSTD.addStrings(GLOBAL_Errors); - dataSetSTD.addStrings(GLOBAL_MiscStrings); + dataSetU64.addStrings(GLOBAL_Keywords_V2); + dataSetU64.addStrings(GLOBAL_Keywords_CC); + dataSetU64.addStrings(GLOBAL_Keywords_CD); + dataSetU64.addStrings(GLOBAL_Errors); + dataSetU64.addStrings(GLOBAL_MiscStrings); + + // Retrieve the results + + outputString = dataSetU64.getOutput(); + } + else if (GLOBAL_ConfigOptions["CONFIG_PLATFORM_COMMODORE_64"]) + { + DataSetSTD dataSetSTD; - // Retrieve the results + // Add input data to computation objects - outputString = dataSetSTD.getOutput(); - } - else - { - ERROR("unable to determine string set"); - } - + dataSetSTD.addStrings(GLOBAL_Keywords_V2); + dataSetSTD.addStrings(GLOBAL_Keywords_CC); + dataSetSTD.addStrings(GLOBAL_Keywords_CD); + dataSetSTD.addStrings(GLOBAL_Errors); + dataSetSTD.addStrings(GLOBAL_MiscStrings); + + // Retrieve the results + + outputString = dataSetSTD.getOutput(); + } + else + { + ERROR("unable to determine string set"); + } + // Remove old file unlink(CMD_outFile.c_str()); @@ -1822,7 +1848,7 @@ void writeStrings() outFile << std::endl << std::endl; // Close the file - + outFile.close(); std::cout << std::string("Compressed strings written to: ") + CMD_outFile + "\n\n"; @@ -1837,7 +1863,7 @@ int main(int argc, char **argv) printBanner(); parseCommandLine(argc, argv); - parseConfigFile(); + parseConfigFile(); writeStrings();