TurboForth Release Note
This Release: |
V1.2 |
Released Date: |
18th October 2013 |
Issued By: |
Mark Wills |
For Public Release: |
Yes |
Change List |
Working OK Confirmation |
||
Change Description |
Status |
Comment |
|
The word -! (subtract and store) is superfluous and has been removed. |
RFT |
If required for legacy code it can be defined as follows: :
-! ( value address--) |
|
Improvement to RND |
RFT |
RND has been improved to produce a better sequence of random numbers. Usage remains unchanged. |
|
Sprite handling improvements. |
RFT |
SPRMOV has been improved so that only the sprites being moved are updated in VDP, rather than updating the sprite descriptor table for all 32 sprites regardless. |
|
Improvements to looping words. |
RFT |
FOR and NEXT have been re-written to use DO and +LOOP internally. I and J are available (as they were before) but additionally, it is now possible to use LEAVE to exit a FOR/NEXT loop early. |
|
Improvements to looping words |
RFT |
DO/LOOPs and FOR/NEXT loops now only reserve 6 bytes on the return stack, rather than 8 bytes. |
|
Improvements to looping words |
N/A |
LOOP and +LOOP code merged into a single routine, improving efficiency. |
|
Improvement to inner interpreter. |
N/A |
DOCOL routine (part of the inner interpreter) improved, removing one instruction. |
|
Improvement to inner interpreter. |
N/A |
EXIT routine (part of the inner interpreter) improved, removing one instruction. |
|
Return stack direction reversed. |
N/A |
Return stack direction reversed. Return stack now 'grows' towards lower memory addresses. This has resulted in a further saving of one instruction in EXIT. (Exit is now a single assembly language instruction, plus a branch to NEXT). |
|
Data stack direction reversed. |
N/A |
Data stack direction reversed. Data stack now grows towards lower memory addresses. This has resulted in many efficiency and performance gains throughout the entire code-base. |
|
Changes to BRANCH |
N/A |
BRANCH now expects an explicit address as a parameter, rather than a relative offset, increasing the efficiency (and speed) of BRANCH. Various flow control words' internal code changed as a result (resulting in simplification). |
|
Changes to 0BRANCH |
N/A |
0BRANCH now expects an explicit address as a parameter, rather than a relative offset, increasing the efficiency (and speed) of 0BRANCH. Various flow control words' internal code changed as a result (resulting in simplification). |
|
CREATE/DOES> improved. |
RFT |
CREATE and DOES> changed. (DOES> completely re-written). Previous versions wasted a cell in all CREATEd words (in case DOES> needed to patch in a GOTO xxx sequence). A completely different mechanism is now employed, as invented by Dean Sanderson of Forth, Inc. and brought to my attention by Dr. James Currie (thanks, Jim for the explanation and code!). |
|
>BODY improved. |
RFT |
>BODY modified since CREATEd words no longer waste a cell. >BODY now simply executes 2+'s code. |
|
CONSTANT improved. |
RFT |
CONSTANT modified as a cell is no longer wasted by children of CREATE. Code simplified as a result. |
|
CHARS functionality changed/improved. |
RFT |
CHARS is now an immediate word. Since CHARS has no effect on the stack, and is merely present for compatibility, it now executes at compile time, and takes no action whatsoever (it does not get compiled into a definition). This improves efficiency since the call to CHARS is never compiled. |
|
SMLIST renamed |
RFT |
The word SMLIST which is used to define movement vectors for sprites has been changed to SPRVEC which better describes its function/purpose. |
|
TERMINAL? removed. |
N/A |
The word TERMINAL? has been removed. TERMINAL? scans the keyboard, returning TRUE if the break key (FCTN 4) is pressed, otherwise returning FALSE. If required for legacy code it can be defined as follows: : TERMINAL? ( – flag ) KEY? 2 = ; |
|
MKDSK renamed to MKBLK |
RFT |
In previous versions the word MKDSK was used to create a blocks file, used for holding Forth source code and general data. This has been renamed to MKBLK (“make block”) as the new name more correctly describes the purpose/function of the word. MKDSK was never used for making disks. It was used for making new blocks files, hence the re-name. |
|
PANEL improved |
RFT |
Internal changes to how PANEL works, reducing code size, increasing efficiency and performance. |
|
SCROLL |
RFT |
Internal changes to how SCROLL works, due to changes in PANEL, resulting in efficiency and performance improvements. |
|
Missing characters now defined |
RFT |
For some reason, characters with ASCII codes from 123 to 126 (that is { | } and ~) are not loaded by the console power-up routines. They are now defined at startup. Previously they would be either completely blank or display as a “TF” symbol. |
|
.S modified. |
RFT |
.S now produces stack dumps with respect to the new variable UNSIGNED. |
|
TO improved |
RFT |
Performance of TO has been improved dramatically (>100%). Additionally, each compiled instance requires only 4 bytes rather than 6, resulting in a 33% space saving per compiled instance. |
|
+TO improved |
RFT |
Performance of +TO has been improved dramatically (>100%). Additionally, each compiled instance requires only 4 bytes rather than 6, resulting in a 33% space saving per compiled instance. |
|
SWAP performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
LIT performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
DUP performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
DROP performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
OVER performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
1+ performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
1- performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
2+ performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
2- performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
+ performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
- performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
* performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
0BRANCH performance increase |
RFT |
This word now executes from 16-bit PAD memory, resulting in an approximate 100% performance increase. |
|
DATA improved |
RFT |
DATA now compiles a run-time helper word called (DATA) which pushes the count and address of the data list at runtime. Furthermore, (DATA) has an entry in the dictionary, facilitating de-compilation, via the SEE utility, of colon-definitions that use DATA. Additionally, since (DATA) has a dictionary entry, it is now possible to build compile time (i.e. immediate) words which build data lists to be executed at run-time. |
|
Terminal Input Buffer |
RFT |
Has been moved to VDP memory from CPU memory, resulting in numerous code improvements. |
|
Screen Scroll Buffer |
RFT |
Has been removed, resulting in 82 bytes CPU RAM returned to the system for general use. The screen scrolling routines now use memory starting at HERE as a buffer. |
|
#TIB |
RFT |
#TIB now returns a pointer to maximum size of the terminal input buffer. |
|
SPAN |
RFT |
Span now returns a pointer to the number of characters placed into the terminal input buffer by EXPECT. |
|
MKBLK |
RFT |
The syntax of MKBLK has changed. Now, the size of the file to create is passed via the stack. Old Syntax: MKBLK DSKx.FILENAME <size> New syntax: 80 MKBLK DSKx.FILENAME |
|
GMODE |
RFT |
GMODE improved, whereby an illegal value passed into GMODE will cause the default 40 column mode to be selected. This prevents situations where bugged user code could render the display un-viewable. |
Fix List |
Confirmation |
||
Problem Description |
Status |
Comment |
Confirmed by |
JOYST |
RFT |
Stack corruption bug in JOYST fixed. |
|
HEX |
RFT |
HEX is no longer an immediate word, in line with the Forth-83 standard. |
|
DECIMAL |
RFT |
DECIMAL is no longer an immediate word, in line with the Forth-83 standard. |
|
['] |
RFT |
['] is now an immediate word, in line with the Forth-83 standard. |
|
LOAD |
RFT |
Nesting issue with LOAD has been fixed. It is possible to nest loading of programs/blocks as long as there is sufficient space allocated to the return stack. From the command line, there is room on the stack to nest 6 blocks deep. Nesting of block loads is expensive, and requires 14 bytes of return stack space per nest (which is returned when the loading un-nests). The return stack is in high memory, immediately before compiled Forth code. The return stack grows towards lower memory addresses. It is therefore possible to 'crash' the return stack into the data stack (and even further beyond) if loading is nested too deeply. Note that THRU does not nest the loading of blocks (unless the blocks being loaded invoke LOAD themselves, of course). It is not advisable to 'nest' loading of blocks (i.e. blocks that LOAD other blocks that LOAD other blocks etc.). Generally, associated blocks should be 'chained' together with --> wherever possible, or THRU should be used. |
|
BLOAD |
RFT |
Problem with BLOAD fixed when called from within a block. BLOAD failed to save/restore the current interpretation state. |
|
BLOAD |
RFT |
Problem with BLOAD failing to update free memory pointers FFAILM & FFAIHM has been fixed. |
|
STREAM |
RFT |
Problem with STREAM fixed whereby it would erroneously push -1 to the stack with each invocation. |
|
WHERE |
RFT |
Problem with WHERE reporting incorrect block numbers on 1024 block block files has been corrected. |
|
LEAVE |
RFT |
Bug in LEAVE fixed. LEAVE would cause a crash if invoked before a loop had executed at least once. Also greatly simplified due to the improvements in DO/LOOP in general. |
|
#EOF? |
RFT |
#EOF?, whilst working, did not match the published stack signature. Rather than returning TRUE (-1 (0xFFFF)) when at the end of the file, it actually returned 1 (0x1). This had the potential to cause problems if the returned result is used in conjunction with NOT to logically invert the returned value. #EOF? has now been corrected to return a boolean (-1) in the event of an EOF condition being detected, which now means NOT can safely be used with it. For example: BEGIN myFile #EOF? NOT WHILE <code to execute while not end of file> REPEAT myFile #CLOSE |
|
Block loading bug |
RFT |
A bug whereby attempting to load a 'dirty' (i.e. edited) block via LOAD could cause a corrupted display has been fixed. |
|
VMBR and VMBW byte count bug fixed |
RFT |
The words VMBR and VMBW have been improved. Previously if a byte count of 0 was specified then the routines attempt to copy 65536 bytes. This has been corrected. If a byte count of 0 is passed then no copying takes place and the words exit immediately. |
|
New Features |
Tested |
||
Feature Description |
Status |
Comment |
Confirmed by |
WARN |
RFT |
WARN is a variable, accessed with @ (fetch) and ! (store). When set to a non-zero value (i.e. TRUE) a warning shall be issued when the colon compiler re-defines an already existing word. When set to FALSE, warnings shall be suppressed. |
|
CSEN |
RFT |
CSEN is variable, accessed with @ (fetch) and ! (strore). When set to a non-zero value (i.e. TRUE) dictionary searches shall be case insensitive. When set to FALSE, dictionary searches shall be case sensitive. |
|
MEM |
RFT |
Reports the number of free bytes in low memory, the number of free bytes in high memory, and the total number of free bytes respectively to the screen. |
|
Ability to change TurboForth workspace pointer |
RFT |
It is now possible to change the register workspace of TurboForth while it is running. To do so, R3, R4, R5 and R12 must be copied to their respective positions in the new workspace, a copy of the address of the workspace must be saved to >A012 and finally, the new workspace must be loaded using a LWPI instruction. By default, TurboForth register space starts at >8300 (in 16-bit wide PAD ram). However, it can be useful to change it if other assembly language routines require PAD memory. The following assembly code (in Forth assembler) provides a word, called TFWS which changes the workspace address from a value passed on the stack:
asm: tfws ( addr --) *sp+ r1 mov, \ get new ws address r1
$A012 @@ mov, \ load into ws restore r1 r2 mov, \ copy addr r2 6 ai, \ point to r3 in target ws r3 r2 *+ mov, \ copy program counter r4 r2 *+ mov, \ data stack pointer r5 r2 *+ mov, \ copy return stack pointer
r2 12 ai, \ point to r12 in target ws r12 r2 *+ mov, \ copy NEXT pointer r0 $02E0 li, \ LWPI op-code r2 $045C li, \ NEXT op-code
r0 b, \ execute code in this workspace ;asm
The above code copies the program counter and stack pointers to the new workspace, then it actually writes a small program in the current workspace and branches to it to execute the LWPI instruction. If this sounds complicated, it's because it is not possible to set the workspace from a register. It has to be an immediate instruction. |
|
Block editor improvement |
RFT |
The block editor shows an * symbol next to the block number if the block is dirty (i.e. has been changed, but not yet flushed to disk). |
|
Vector added to allow simple restoration of TurboForth's workspace |
RFT |
Assembly language subroutines that run from their own workspace can restore the TurboForth workspace and return to TurboForth with a BLWP to address >A012. This address is fixed and will not change in subsequent releases. Address >A012 must have been previously loaded with TurboForth's workspace address. At start-up, this address is initialised to >8300. |
|
UNSIGNED |
RFT |
A new word, UNSIGNED has been added: UNSIGNED ( addr – ) UNSIGNED is a variable, read with @ and written with !. When set to a non zero value, the word N>S (Number to String) will treat all numbers as unsigned for conversion purposes. When set to zero, N>S will treat numbers as signed. In addition, the word .S has been modified to produce either signed or unsigned stack dumps in accordance with the current value of UNSIGNED. |
|
VTYPE |
RFT |
New word, VTYPE added: VTYPE ( vdp_address count – ) VTYPE will type a sequence of characters to the screen, in the same way as TYPE, however, the source address is an address in VDP memory rather than CPU memory. Internally, EMIT is used to display the character, therefore issues such as screen scrolling etc. are handled in exactly the same way as TYPE. |
|
EVALUATE |
RFT |
A new word, EVALUATE has been added. This word is an ANS94 Forth word. When given a string, EVALUATE will evaluate the contents of the string as if the string were directly entered at the command line. For example: S” : TEST 100 0 DO I . LOOP ;” EVALUATE Results in a word being created with the executable code shown above, just the same as if it had been entered from the command line. The ability to compile code from strings is a powerful feature, and means that the compilation of source code from other sources becomes trivial. For example, the following code implements a full text file compiler which can compile code from DV80 text files: fbuf: fileIn : srcFN s" DSK2. DF80SI" ; : include ( "filename" -- ) srcFN DROP 20 BL FILL srcFN DROP SWAP CMOVE srcFN fileIn FILE fileIn #OPEN ABORT" Can't open file" BEGIN fileIn #EOF? NOT WHILE PAD fileIn #GET DROP PAD COUNT EVALUATE REPEAT fileIn #CLOSE ; To compile source code from a text file, usage is as simple as S” DSKx.FILENAME” INCLUDE A caveat does apply: EVALUATE cannot be used directly in a loading disk block. This same restriction also applies to the ANS version. Note also that EVALUATE reserves 8 bytes on the return stack, prior to invoking a new instance of the interpreter, thus a total of 10 bytes are reserved on the return stack when EVALUATE is called, which is released upon return. |
|
MARKER |
RFT |
A new word, MARKER has been added. MARKER is a defining words that resets the dictionary to state it is in at just after the creation of the marker. For example: MARKER RESET ( create a marker called RESET) ... various words added/compiled … RESET ( resets the dictionary, erasing everything defined after RESET) |
|
#BUF |
RFT |
A new word, #BUF (“number of buffers”) has been added. #BUF is a variable and is read with @ and written with !. #BUF allows the number of block buffers in VDP ram to be set or read. Legal values are from 1 to 6. Unpredictable behaviour will result if values outside of this range are used. |
|
0 character modified |
RFT |
The character 0 (zero) now has a slash running through it, to allow it to be more easily differentiated against the letter O. |
|
PAD RAM Usage
The changes applied to the inner interpreter has resulted in some of the addresses of the routines changing in PAD ram. For the avoidance of doubt the addresses are given below.
Routine or Word |
Description |
Address |
DOCOL |
Pushes return address to return stack. Used to begin nesting of Forth words. |
>8320 |
NEXT |
Executes the next instruction in a thread of addresses. |
>8326 |
EXIT |
Un-nests the return stack so that a program can resume from the calling word. Opposite of DOCOL. |
>832C |
BANK1 |
Bridging routine allowing a routine in ROM bank 0 to call code in ROM bank 1. |
>8332 |
RETB0 |
Un-bridging routine, return control from a routine in bank 1 to the routine in bank 0 that called it. |
>833A |
SPSTAT |
Reads a byte from the speech synthesizer, complete with recommended delay period. |
>8340 |
RUNISR |
Routine to execute TurboForth interrupt service routines in bank 1. |
>834C |
ISRXIT |
Returns to the active bank after an interrupt has executed. |
>8354 |
SWAP |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>835C |
LIT |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>8368 |
DUP |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>837E |
DROP |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>8386 |
OVER |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>838A |
1+ |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>8392 |
1- |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>8396 |
2+ |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>839A |
2- |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>839E |
+ |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>83A2 |
- |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>83A6 |
* |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>83AA |
0BRANCH |
Forth primitive. Implemented in 16 16-bit wide memory for performance. |
>83B4 |
Vectors at top of High Memory:
The following vectors are defined starting at >A000. The values stored in these vectors can/will change between builds, however the addresses of the vectors themselves are guaranteed not to change. The vectors are provided to change behaviour (for example, to patch in your own FIND or interpreter) or call routines within the TurboForth ROM in a portable (i.e. works across all versions) manner.
Address |
Description |
>A000 |
vector for INTERPRET. Use to patch in your own interpreter. |
>A002 |
vector for BLOCK. Use to patch in your own block loader. |
>A004 |
vector for NUMBER. Use to patch in your own number interpreter. |
>A006 |
vector for FIND. Use to patch in your own dictionary lookup code. |
>A008 |
vector for user interrupt service routine. Load this address with the address of a machine code routine to execute on each VDP interrupt. The machine code routine runs in GPL workspace. The code should return with an RT instruction. Note: VDP interrupts are only enabled by TurboForth during the execution of VDP memory access words, such as HCHAR, VCHAR, SCROLL, EMIT, etc. This may change in future releases. |
>A00A |
Spare. Can be used for general use. Not used by TurboForth. |
>A00C |
pointer to DSRLNK vector in ROM bank 1. (see below for information on bank-switching) |
>A00E |
pointer to GPLLNK vector in ROM bank 1. (see below for information on bank-switching) |
>A010 |
pointer to scratch-pad restore code in ROM bank 1. (see below for information on bank-switching) |
>A012 |
pointer to TurboForth's workspace. Set on power-up to >8300. |
>A014 |
pointer to TurboForth's NEXT routine in the inner interpreter. Set on power-up to >8326. |
>A016 |
Pointer to DOCON's executable code. VALUES are also implemented using CONSTANTs |
>A018 |
Pointer to CREATE's executable code (VARIABLEs are implemented via CREATE) |
Bank-Switching Information
TurboForth is physically implemented as a 16K ROM, however, the cartridge space in the TI-99/4A is only 8K, thus TurboForth is implemented as two 8K ROM banks. Circuitry in the cartridge detects a write to the cartridge area and is used to select either bank 0 or bank 1.
To select bank 0, a write to memory location >6002 must be performed. For example:
CLR @>6002
To select bank 1, a write to memory location >6000 must be performed. For example:
CLR @>6000
Note: User assembly language routines are always launched with bank 0 being the active bank. If a user assembly language routine selects bank 1 for any reason it must re-select bank 0 before returning to the Forth environment using a B *R12 instruction. The exception is assembly code that is executed via means of the user interrupt service routine vector. See the above table for clarification.
TurboForth Register Usage
TurboForth uses the following registers:
Register |
Description |
R3 |
The Forth program counter |
R4 |
Data stack pointer |
R5 |
Return stack pointer |
R12 |
Pointer to NEXT routine (see PAD RAM usage, above) |
User assembly language code should generally avoid changing these registers. If these registers are required, consider giving your assembly language routine its own workspace, or save their values elsewhere and restore them after use.
Passing Parameters To/From the Data Stack
Within an assembly language routine, data may be popped off the stack using the following instruction:
MOV *R4+,R[x]
Where x is an appropriately selected available register.
Data may be pushed to the stack using the following assembly language phrase:
DECT R4 * create space on data stack
MOV R[x],*R4 * write data to the data stack
Note: If using the TurboForth assembler, SP can be substituted for R4, as follows (the following is TurboForth assembly language code):
*SP+ R[x] MOV,
and...
SP DECT, \ create space on data stack
R[x] *SP MOV, \ write data to the data stack
...respectively.