Enable QBasic console mode usage and fix bugs in newline handling
differences with C64 basic (cbm). To enable console mode for QBasic
programs, have basicpp.py prefix the output with the QB64 specific
console activation variables/functions.
One change to basicpp.py to make this change more straightfowards is
recursive includes so that includes can appear in more than just the
top level step files. This allows us to conditionally include the
right readline implementation. For QBasic in the special console mode
(instead of the default full-screen UI mode) we need to use the LINE
INPUT command in order to read input.
- restructure memory dim/initialization to support QBasic which
requires all DIMs to be earlier in the code than references to the
DIM'd variables (unlike C64 which just requires the DIMs to be
called first logically).
- Fix printed header ("C64 QBasic" -> "QBasic")
- simplify DO_CONCAT.
- inline MAL_READ/PRINT.
- comment out memory debug/sanity checks.
- more aggressive space removal.
Saves over 900 bytes.
Increase Z% value memory by 374 to 9216 (8192+1024).
- read one character at a time from the file instead of chunking it
into the A$ string.
- fix an overflow that was happening during reads of long forms.
- Use variables A1, A2, B2 for Z%(A+1), Z%(A+2), Z%(B+2) respectively.
- Replace Z%(R)=Z%(R)+32 with GOSUB INC_REF_R
- Add functions TYPE_A and TYPE_F for (Z%(A)AND 31) and (Z%(F)AND 31)
respectively.
- Inline NATIVE_FUNCTION and MAL_FUNCTION.
All together saves over 500 bytes so increase Z% value memory by 250
entries.
Use a one dimensional array for the Z% value array. This enables
lists, vectors, environments and metadata pointers to all save off
1 word (2 bytes) of space.
Split the memory init and functions into mem.in.bas.
In addition, change type 14 to be metdata rather than any type 16-31.
This change saves about 560 bytes (no second array dimension
subscripts) and reduces Z% value usage by 10%-15%.
Bump the number of Z% words by 200 (to 8591). This enables
self-hosting up to step7 (without step8-stepA functions in core.mal).
- Alternate memory layout of hash-maps:
Instead of hash-maps being an alternating sequence of keys and values,
combine the key/values into a single entry. In other words, switch
from this:
8 -> next Z% index (0 for last)
14 key/value (alternating)
To this:
8 -> next Z% index (0 for last)
key value
This requires refactoring of the sequence reader, EVAL_AST and
especially DO_HASHMAP and DO_KEY_VALS. So that leads to the next big
refactoring:
- Change mapping/lapping constructs to share code:
Several pieces of mapping/looping code have a common structure, so
this moves that common structure to types.in.bas: MAP_LOOP_START,
MAP_LOOP_UPDATE, MAP_LOOP_DONE. Then use this code in:
- EVAL_AST
- READ_SEQ_*
- DO_MAP
- DO_HASH_MAP
- DO_KEYS_VALS
This also fixes the issue that several of these looping constructs
were creating new empty sequence entries instead of using the common
ones at the beginning of memory.
- Remove the use of DEREF_*.
This isn't actually needed because we no longer create structure that
refer to multiple levels of type 14 references. Replace DEREF_* with
VAL_* which gets the value of a particular sequence element i.e.
Z%(A+1,1).
All together, the above changes save over 300 bytes.
Also:
- Fix empty nil/false/true entries so they
are treated the same as other types of data with regards to
reference counting and ALLOC/RELEASE.
- Add a new memory summary function in debug.in.bas that just prints
out free, value count, and references for the early scalar and empty
list elements. Comment out the larger one. This saves about 90
bytes.
Save about 400 bytes.
Increase value Z% array by 100 to 4195.
Reduce string array by 1 (to 199) since in BASIC the value is the last
index not the size.
In core move incrementing of function index into
INIT_CORE_SET_FUNCTION. Switch 3 IF GOTO to ON GOTO. Reuse some
temporary variables.
Saves about 480 bytes.
Bump value array from 3950 to 4096. This allows step4 (sumdown 2) to
pass. Previously only (sumdown 1) passed.
Uses stack PUSH*/POP*/PEEK* routines instead of direct X% and Y%
access. Seems to be about the same performance (maybe a 5% performance
hit at most).
This gives us a larger stack (1920 2-byte words of $C000 rather
than 200 words as before). The release stack at $CF00 stays the same
size (64 4-byte addr/level entries).
Also saves over 1K or program and array space. So take the opportunity
to expand Z% entry space from 3712 to 3950.
- Save over 450 bytes. Bump up Z values by 128.
- Fix function printing when function is embedded in something else by
using strings storage as a stack while printing rather than using
RR$
- Simplify some error messages and sanity checks in RELEASE.
- Do the pop of CALL return value at the end of the subroutine
(callee) rather than at the call point (caller)
- shorten some character variables (CH$->C$, CH->C)
- remove spaces after OPEN, GET
- print REPL header directly in BASIC code
Together saves 404 bytes of memory.
- Strip linefeeds in run_argv_test.sh so that step6 arg test passes
for basic.
- Add gensym and convert or macro.
- Add gitignore entries for transpiled basic sources.
- Add conj/seq stubs so that step4 self-host loads (if non-step4
functions are commented out in core.mal)
- Bump up Z% value space by 256 spaces (1K)
- Remove old qb2cbm.sh
- Stop let binding eval on error. Also don't continue into EVAL if
error.
- if without a false position was freeing up too much when it
finished.
- fix reader so that it doesn't keep incrementing ref cnt of static
empty sequences.
step4 runs out of space attempting to load the program. Step2 and
step3 run out of memory (stack exhaustion) for more complicated forms.
- Use GOTO with return label on our stack instead of GOSUB for:
- APPLY function in types.in.bas
- "apply", "map" and "swap!" core functions
- Implement DO TCO. Change EVAL_AST to detect if we are called from DO
and exit one element early.
- Remove GOSUB recursion from EQUAL_Q
- Inline PAIR_Q. Reduce REPLACE stack use.
- Remove one level of GOSUB/stack by calling REP with GOTO
- Simplify mal/step2_eval.mal to remove use of (or ) macro in
eval_ast.
- Fix ON GOTO/GOSUB line detection in basicpp
- Add read-file which is similar to read-string but from a file name
rather than a string. This allows steps 0-2 to load although each
one eventuall crashes with out of memory after evaluating "123"
a few times.
- basicpp:
- Renumber the line numbers so they are ordinally increasing. This
saves 150 or so bytes because GOTO/GOSUB calls have smaller line
numbers.
- Shrink 'IF 123' -> 'IF123' for almost 300 byte savings.[:w
- Simplify PR_MEMORY_SUMMARY output. Save 75 bytes
- Add missing runtest.py change that allows basic tests to pass.
- Metadata support required expanding the type size (to 5 bits). This
also implies that ref cnt now only has 11 bits (2048).
- Added ^ reader macro (with-meta) which required some refactoring of
READ_MACRO to share code.
- Rename some more variables:
ZJ -> S
S% -> X%
ZR% -> Y%
ZM% -> Y
RE% -> D
This removes remaining % variables apart from the pre-allocated
arrays Z%, S% and X%.
- Modify ALLOC to take a type (rather than size) and take default
values to set for the 1-3 values/pointers. Let alloc do the
ownership taking of the referred values when appropriate.
- Add FORCE_SEQ_TYPE function to coerce sequence to given type. Fixes
apply and rest on vector. Simplifies concat.
- Use a double ON GOTO structure for calling the native functions in
DO_FUNCTION.
- Add some stub core functions.
- Move CHECK_FREE_LIST to debug.in.bas
- All changes together save over 1K
- Move apply logic in swap! to APPLY function in types and use that
for macroexpand
- Abort def! if error before updating the environment
- let* wasn't properly saving A2% for the final eval. Also, the
environment release check should be against the top-level EVAL env,
not the root repl env.
- (concat (list) ...) was broken so fix it to ignore empty lists that
aren't in the trailing position.
- nil, false and true in the reader were always being returned as
references (with an ref cnt) but we have the assumption that
references (14) are not ref cnt'd and are always part of a compound
type so fix the reader to just return the interned addresses.
Also:
- Add some step1 and step3 tests that were discovered during Basic
development.
- Move PR_MEMORY* to debug.in.bas
- Simplify Makefile deps
- Fix freeing in steps4-7 when error at deeper level
i.e. (prn (abc))
- add SLICE function to support concat implementation.
- combine some lines
- remove some unnecessary spaces and parens
- add string allocations to single routine in types
Also:
- remove blank lines in qb2cbm.sh output (does not save memory)
Also:
- command lines arguments are implemented by creating a file
".args.mal" that contains a list of arguments and that is loaded by
the run script (load-file) into -*ARGS*-. The rest is put in *ARGV*
and the first element is pulled out and used as the script name.
- fix string reading/printing using new REPLACE function
- add RE function to skip printing and to get back evaluated value
(result must be freed by caller). Needed for step6 to get first
argument pointer for scripting.
- Sync earlier steps
- add cons, first, rest to support parsing the command line.
- eval is implemented as standard function in core.in.mal
- fix println bug (using PR_STR rather than PR_STR_SEQ)
- change sequence printing to save the initial sequence type on the
stack and use that for the ending sequence delimeter. This way
sequences of one type can still use the tail of sequences of
a different type but still be considered the initial type.
- Add TCO recur to let*
- Release of all memory from recursive function and let* calls.
- Fix a number of cases of trying to release already freed memory.
- Print function in memory as 2 byte unit rather than two separate
memory locations.
Also:
- More/better detail in PR_MEMORY/PR_MEMORY_SUMMARY
- Fix *.prg image build (for running under Vice). Have to lower-case
everything since what C64/Vice shows as capitals are actually
lowercase.
Also:
- qb2cbm.sh: Add KEEP_REM level variable to tweak which REM/comments
are emitted/skipped.
- fix printer issue with recursive sequences after evaluation (with
refeerences in them)
- add stats target
This works with cbmbasic from https://github.com/mist64/cbmbasic. The
cbmbasic interpreter needs to be on the PATH.
The actually sources are *.in.bas which are "compiled" to *.bas using
the qb2cbm.sh. qb2cbm.sh translates from a QBasic-ish format to a line
numbered source with include files inlined (REM $INCLUDE: 'file.bas').
One additional advantage is that the *.in.bas versions can also be
indented and qb2cbm.sh will remove the indenting in the translated
code.