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--)
SWAP NEGATE SWAP +! ;

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
\ vector

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.