Development history of ForthCoupe
Description of the page content
Development history of the ForthCoupe project, a Forth for the SAM Coupé computer.
2013-01-02
First tinkering with the source of SamForth-B. But the disassembling is not mature for the fork yet.
2013-01-20
SamForth-B is finally forked. First version of a more clear BASIC loader.
First simple changes:
SAM
renamed toBYE
!- Removed the frames variable, not used by SamForth-B.
2013-01-21
Unknown zones deleted:
xxx_unknown_zone_00b_start:
defs 80
xxx_unknown_zone_00_start:
defb 0xd6
defb 0x45
xxx_unknown_zone_01b_start:
defs 18
The following duplicated routine is deleted too:
print_a_xxx_duplicated:
call jsvin_rom_routine
defw rst_10_rom_routine
ret
As well as its vector:
jp_print_a_xxx_duplicated: ; xxx -- not used; the calls are direct
jp print_a_xxx_duplicated
The jump vectors are deleted: jp jp_* are converted to jp -; call jp_* to call -. Same with sub_* labels in the vectors table.
93 call
+ ret#! pairs are converted to a single #!jp
.
Fix (also in SamForth2z80dasm): the jp_u_dot_header_end
is renamed to jp_u_dot_code_field
.
VLIST
renamed to WORDS
!
All scattered code is moved to the corresponding Forth words.
The call_rom_address_in_iy
routine is deleted. It's a useless remain from SamForth-A.
All editor code and words are removed.
BASE
is standarized: it does not change the base any more but returns its address.
The temp1
and temp2
variables are removed. They were used only in the following case:
save_dictionary_pointers:
ld hl,(here_fvar)
ld (temp1_fvar),hl
ld hl,(latest_fvar)
ld (temp2_fvar),hl
ret
restore_dictionary_pointers_and_set_interpretation_mode:
ld hl,(temp1_fvar)
ld (here_fvar),hl
ld hl,(temp2_fvar)
ld (latest_fvar),hl
ld a,0x00
ld (state_fvar),a
ret
This way it's shorter and faster:
save_dictionary_pointers:
ld hl,(here_fvar)
ld (saved_here),hl
ld hl,(latest_fvar)
ld (saved_latest),hl
ret
restore_dictionary_pointers_and_set_interpretation_mode:
saved_here: equ $+1
ld hl,0
ld (here_fvar),hl
saved_latest: equ $+1
ld hl,0
ld (latest_fvar),hl
ld a,0x00
ld (state_fvar),a
ret
The same is done with tmpstk_fvar
.
Some Z80 global optimizations: ld a,0x00
converted to xor a
; all unconditional relative jumps are converted to absolute jumps (the relative conditional jumps will be examined later, as their speed depends on the check); cp 0x00
converted to and a
; a push
- pop
pair is changed to use direct register loading.
The storage of the address returned by HERE
is moved to the code of the HERE
word, and pointed by the here
symbol. Also the chere_fvar
, latest_cfvar
and clate_cfvar
system variables are converted the same way.
PUSH-DE
is renamed PUSH-DE-HL
(HL
to TOS); POP-DE
is renamed POP-HL-DE
(TOS to HL
);
Removed the errsp_fvar
and stkend_fvar
variables. The addresses of the return stack can be used directly in the code instead.
New standard word STATE
.
The calls to link_l
used the HL
register, while L
was enough and faster. But an additional entry point link_a
makes the calls even faster; the A
register was already used by link_l
anyway.
New common use word BOUNDS
.
#S
doesn't work. A lot of checks done.
Unfortunately, the following simple alternative to the error message printing routine can not work — because the message table must be paged in too.
error_a:
ld de,error_messages
call jsvin_rom_routine
defw jpomsg_rom_routine
jr warm_restart
An alternative routine has been written. It's 7 bytes longer than the SamForth's original, but saves 11 ending bytes in the message table and its code will be factored and reused:
error_a:
ld hl,error_messages
next_message:
dec a
jr z,print_message
ex af,af'
skip_message:
ld a,(hl)
inc hl
and 0x80
jr z,skip_message
ex af,af'
jr next_message
print_message:
ld a,(hl)
inc hl
bit 7,a
push af
res 7,a
call print_a
pop af
jr z,print_message
jr warm_restart
2013-01-22
New standard word DEPTH
.
#S
fixed. The problem was a 0BRANCH
jumped to a ret
that had been combined with a preceding call
as part of the global optimizations. All 0BRANCH
jumps have been marked; also in SamForth disassembled.
Now (FIND)
ignores case. A little modification was needed:
compare_the_names:
; de = address of the name to be searched for
; b = its length
; hl = address of a word's name in the dictionary
dec hl ; point to the word name length
length_of_name_searched_for: equ $+1
ld c,0x00
ld a,(hl)
res 7,a ; compile-only bit
res 6,a ; precedence bit
cp c ; lengths are different?
jr nz,different_name_lengths
; lengths are equal
ld a,(hl) ; fetch the byte with its special bits
ld (leng_fvar),a
push hl
ld de,(ip_fvar)
ld b,(hl)
res 7,b ; compile-only bit
res 6,b ; precedence bit
next_char:
inc hl
; de = address of current char of the name to be searched for
; b = its remaining length
; hl = address of the current char of a word's name in the dictionary
ld c,(hl)
ld a,(de)
; convert the char in the A register to uppercase if needed
cp "a"
jr c,compare_chars
cp "z"+1
jr nc,compare_chars
and %01011111 ; convert to uppercase
compare_chars:
cp c
jp nz,different_names
Some useless variables removed.
2013-01-23
First draft for the new integrated init: it will search for free memory pages, page them in and copy the code to its execution location.
The print_a
subroutine is moved to EMIT
. The standard word SPACE
is created with the the print_a_space
subroutine.
The SamForth-B's ASCII
word is state-smart, what is inconvenient:
ld hl,(ip_fvar)
skip_space_before_char:
inc hl
ld a,space_char
cp (hl)
jr z,skip_space_before_char
ld a,(hl)
ld (ip_fvar),hl
ld l,a
ld h,0x00
call push_hl
ld a,(state)
bit 7,a ; compiling?
ret z
call pop_hl
ld b,h
ld c,l
ld de,lit_code_field
call compile_call_de
ld (hl),c
inc hl
ld (hl),b
inc hl
ld (here),hl
ret
It has a bug: the interpreter continues its work right after the char: ASCII A
will put the code of "A" on the stack; ASCII ANT
will do the same but then will cause an error because the word "NT" can not be found in the dictionary. Beside, some optimization can be done. Anyway the code has to be splitted into the standard words CHAR
and [CHAR]
.
2013-01-24
Symbols have been created for all system variables and special char codes used in the code.
All words in dictionary are converted to lowercase. They will be shown in lowercase here too.
The code of (find)
is adapted.
create
had a problem: it didn't add the default behaviour to the new word. Now it does the same than variable
.
New error added to the code that create headers: "Attempt to use zero-lenght string as a name" (after Gforth).
soff
renamed silence
. New common use word: perform
.
smode_fvar
removed. No need to store the current screen mode, because there's already a system variable for that.
Fixed an error introduced by me two days ago: Moving and removing Forth variables caused the basic loader poked them wrongly before doing a warm restart.
New structured version of the BASIC loader, but not fully rewritten yet:
10 REM ForthCoupe's loader
20 REM Copyright (C) 2013 Marcos Cruz (programandala.net)
30 REM ForthCoupe is free software
published under the terms of the GNU General Public License,
as published by the Free Software Foundation,
either version 3 of the license or (at your option) any later version.
See "http://gnu.org/licenses".
40 MODE 3
50 LOAD "d2:fc.bin"CODE 65536
REM xxx temporary code location
60 LET entry=50000
REM address of the code machine routine that starts ForthCoupe
70
REM ForthCoupe variables
80 LET fvars=&10000,svblk_fvar=fvars+6,slen_fvar=fvars+8
REM xxx todo -- finish
90
REM Cold start
100 LABEL cold
ON ERROR GO TO errorTrap
110 PRINT "ForthCoupe"'"Copyright (C) 2013 Marcos Cruz (programandala.net)"
120 coldSysVars
keys
130 pokeColdEnterRoutine
pokeReturnRoutine
140 GO TO start
150
REM Warm start
160 LABEL warm
ON ERROR GO TO errorTrap
170 REM warmSysVars
REM xxx -- not needed?
180 pokeWarmEnterRoutine
pokeReturnRoutine
190
REM Start
200 LABEL start
210 POKE SVAR 520,0
REM &5c08, xxx -- clear keyboard buffer?
220 CALL entry
230
REM Command dispatch
240 REM xxx todo -- rewrite
250 ON PEEK svblk_fvar
fsave
fload
directory
GO TO warm
GO TO bye
nop
nop
nop
nop
nop
bload
bsave
dload
dsave
260 GO TO warm
270
REM Bye
280 LABEL b
ON ERROR STOP
STOP
290
REM Disk operations
300 REM xxx todo -- rewrite or write in Z80
310 DEF PROC nop
END PROC
320 DEF PROC directory
SCROLL RESTORE
DIR PEEK &5A07 "*.F*"
END PROC
330 DEF PROC fload
LET b$=MEM$(&10226 TO &1022f),a$=TRUNC$ b$ + ".FS",b=(PEEK &1015D -1)*16384+DPEEK &10188
LOAD A$ CODE b
LET a=(PEEK 19314*16384)+(DPEEK 19315-32768)
DPOKE slen_fvar,a
END PROC
340 DEF PROC fsave
LET b$=MEM$(&10226 TO &1022f),a=(PEEK &1015D-1)*16384+DPEEK &10188b=DPEEK slen_fvar
LET a$=TRUNC$ b$ + ".FS"
SAVE A$ CODE a,b
END PROC
350 DEF PROC bload
LET b$=MEM$(&10226 TO &1022f),a$=TRUNC$ b$ + ".FC",b=49152+DPEEK &10188
IF DPEEK &10188 >=32768
LET b= (PEEK &1018E-1)*16384+DPEEK &10188
END IF
LOAD a$ CODE b
END PROC
360 DEF PROC bsave
LET b$=MEM$(&10226 TO &1022f),a=49152+DPEEK &10188,b=DPEEK slen_fvar
IF DPEEK &10188 >=32768
LET a= (PEEK &1018E-1)*16384+DPEEK &10188
END IF
LET a$=TRUNC$ b$ + ".FC"
SAVE a$ CODE a,b
END PROC
370 DEF PROC dload
LET b$=MEM$(&10226 TO &1022f),a$=TRUNC$ b$ + ".FD",b=49152+DPEEK &10188
LOAD A$ CODE b
END PROC
380 DEF PROC dsave
LET b$=MEM$(&10226 TO &1022F),a=49152+DPEEK &10188,b=DPEEK slen_fvar,a$=TRUNC$ b$+ ".FD"
SAVE A$ CODE a,b
END PROC
390
REM Machine code routines
400 REM xxx todo -- do these tasks (mainly memory paging to enter and return) in ForthCoupe instead
410 DEF PROC pokeColdEnterRoutine
POKE entry,&ed,&73,0,&f0,62,2,211,250,195,0,64
END PROC
420 DEF PROC pokeWarmEnterRoutine
POKE entry,&ed,&73,0,&f0,62,2,211,250,195,3,64
END PROC
430 DEF PROC pokeReturnRoutine
POKE entry+20,&ed,&7b,0,&f0,62,31,211,250,205,&66,1,201
END PROC
440
REM Error trap
450 LABEL errorTrap
IF error>83
PRINT "DOS";
ELSE PRINT "SAM";
END IF
PRINT " error ";error
460 GO TO warm
470
REM Config
480 DEF PROC coldSysVars
490 REM POKE &5A44,1
REM fat pixels
500 POKE &5ABA,1
REM "in quotes" flag
510 POKE &5A34,2
REM blocks
520 REM DPOKE &5C7D,16384
REM address of chr$ 169
530 REM POKE &5C6A,8
rem caps lock
540 END PROC
550 DEF PROC keys
560 DEF KEYCODE 209,"goto warm"
REM F9, usually boot
570 KEY 140+49,91
REM symbol+8=left bracket
580 KEY 140+58,93
REM symbol+9=right bracket
590 KEY 140+70,64
REM symbol+a=at sign
600 KEY 140+37,35
REM symbol+n=number sign
610 END PROC
620
REM Meta
630 DEF PROC s
SAVE OVER "fc" LINE 40
END PROC
2013-01-25
The flags_fvar
variable has been removed. Its second bit was set by the init, but never checked or changed. Its bit 7 was sen by link
when linking to stream 3, and checked by cls
in order to do nothing. Too slow and complex. Now link
simply stores the linked stream into the code of cls
. The IY
was used some times to set or check the bits of flags_fvar
. Now it's free.
New drafts and timings of the alternative data stack code.
New common use words: sp0
, sp@
sp!
, rp0
, rp@
and rp!
.
The old endif
is removed (it can be defined in Forth if needed); then
is prefered.
New word: spaces
.
Fixed: references to tib
and pad
start and end labels.
New words: tib
, on
and off
.
Improved: fence
holds its data.
2013-01-26
Timings of three versions of the data stack code. Faster versions without checks. New words to toggle them: fast
and slow
(after the Jupiter ACE's Forth).
Now a Pasmo's macro creates the headers:
_end_of_dictionary_: equ 0xffff
new_header_format: equ false
old_header_format: equ not new_header_format
macro header, label,name,flags,previous
; Create a word header in the dictionary
; label = label-format name
; name = string with the actual name
; flags = byte with the precedence and compile-only bits combined
; previous = label-format name of the previous word in the dictionary
local end_of_name
if new_header_format
label##_flags_field:
defb flags
label##_link_field:
if defined _##previous##_
defw _##previous##_
else
defw previous##_name_field
endif
label##_name_field:
defb end_of_name-$-1
defm name
end_of_name: equ $
label##_code_field:
else ; SamForth's header format:
label##_link_field:
if defined _##previous##_
defw _##previous##_
else
defw previous##_name_field+1
endif
label##_name_field:
defb end_of_name-$-1 or flags
label##_name:
defm name
end_of_name: equ $
label##_code_field:
endif
endm
The source has been converted with the following Vim's commands:
:'b,'ms@\(.\+\)_link_field:\n\s\+defw\s\(\S\{-}\)\(_name\)\?\n_name_field:\n\s\+defb\s0x\([0-9a-f]\)\([0-9a-f]\)\s*\n_name:\n\s\+defm \(\S\+\)\s*\n_code_field:\s*$@ header ,,{},\r@
:'b,'ms@{0}@0@
:'b,'ms@{4}@immediate@
:'b,'ms@{8}@compile_only@
Example of an old hard-coded header:
dot_quote_link_field:
defw beep_name
dot_quote_name_field:
defb 0x02
dot_quote_name:
defm '."'
dot_quote_code_field:
And its new version:
header dot_quote,'."',0,beep
Nevertheless there's a problem with Pasmo. The macro does not create the symbols when the "label" parameter is a Z80 command, e.g. "out", "in", "and"... Those are Forth words. The author of Pasmo has been contacted. Meanwhile, alternative label names are used ("__out", "__in", "__and"...).
New dp
word, a variable that stores the address returned by here
.
Some simple comparation words has been changed. Example, the original code of 0<
:
header zero_less,"0<",0,zero_equals
call pop_hl
bit 7,h
ld hl,0x0000
jr z,l5eddh
inc hl
l5eddh:
jp push_hl
Now it's a bit faster and beside it returns -1 as true, not 1 (the system is being converted to use -1 as true flag):
header zero_less,"0<",0,zero_equals
call pop_hl
bit 7,h
ld hl,0x0000
jp z,push_hl
dec hl
jp push_hl
The codes for the structure had been extracted from the original code and converted to symbols:
do_structure_id: equ 0x02
begin_structure_id: equ 0x03
if_structure_id: equ 0x04
builds_structure_id: equ 0x09
Now their checks are a bit faster too.
(
fixed: it wasn't immediate.
2013-01-27
- First assembling with macro header (using the old header format).
- Problem with Pasmo's macros: parameters can not have the same name than Z80 commands. This is a problem for the
header
macro because some Forth words have identical names (and
,or
,in
...). - Old dictionary (without macros) removed and saved apart.
- Comment added before every header.
- "__" prefix added to labels of words that are identical to Z80 commands; this way the
header
macro works. - New word
dp
. - Fixed
header
macro: the old format NFA was wrong; also the end of dictionary. (find)
partially adapted to the new header format — it does not work yet.words
adapted to the new header format.interpret
partially adapted to the new header format.- Removed old deprecated code and saved it apart.
- The space-skipping loop at the start of
interpret
is faster now. - New standard names:
minus
renamednegate
;dminus
renameddnegate
.
2013-01-28
- New standard word
quit
for the main loop and some init tasks, after ANS Forth. - Shorter symbol suffixes for the word header fields symbols: "_cfa", "_nfa" and "_lfa".
- New words:
(bootmessage)
andbootmessage
, after Gforth. variable
does not initialize the value any more.- New words: standard
defer
and comusnoop
. - The
create_header
routine is partially adapted to the new header format. - The cold and warm entry points have been tidied up and factored.
- The misc section has been divided.
pageno_fvar
moved to its routine and renamed toforth_hmp
.word
has been rewritten after ANS Forth.- New standard words
count
andbl
. - Now word
prompt
after Gforth; called inquit
. - The exit of the keyboard routine has been tidied up; now the command and the prompt work fine together.
- New words to page ram pages in:
lmp
andhmp
. - New standard word
page
, to clear the whole screen or send a line feed to the printer. - The SamForth's
cls
is rewritten: now it requires a parameter in order to clear the upper, lower or whole screen (or the current window).
2013-01-29
- New standard word
j
;i
is rewritten with a faster algorithm. at
converted toat-xy
(after ANS Forth).- New comus word
home
. - The keyboard input routine has been improved: no more flashing: the lower screen is cleared only at the end, not after every keypress. Beside, now the command line is cleared before the execution of the command.
- New standard words
[
and]
. - More checks: The new header format is not fully implemented yet: Words are not found.
2013-01-31
- Two bugs (in
(find)
andinterpret
) have been finally found and fixed. The new header format works. - New standard word
unused
.
2013-02-01
- New word
forthcoupe
, a noop word intended for conditional compilation. - New standard word
?dup
. - New standard word
time&date
; new ad-hoc words:year
,month
,day
,hour
,minute
,second
.
2013-02-02
- New standard word
literal
. - New standard words
>body
,1+
,1-
. - New common usage words
2+
,2-
. - New assembling option, faster, to omit some stack checks during execution.
2014-11-03
Some changes and updates in comments.
2014-12-16
Some typos.
2014-12-22
Some little fixes in the layout.
2014-12-25
The header
macro is improved, using ideas from DZX-Forth: no need to use the previous word as a parameter. It's calculated.
_previous_nfa: defl 0
macro header, label,name,flags
; Create a word header in the dictionary
; label = label-format name
; name = string with the actual name
; flags = byte with the precedence and compile-only bits combined
label##_flags_field: defb flags
label##_lfa: defw _previous_nfa
_previous_nfa: defl $
label##_nfa: defb label##_cfa-$-1,name
label##_cfa:
endm
The new header format is made definitive. The old header alternative code is removed.
2014-12-26
Some fixes in comments.
2014-12-30
Forked to Couplement Forth.
2015-01-03
Some stack comments completed.
2015-01-04
After the changes in Couplement Forth: Now all changes of state
are done with a call to [
or ]
; fix in the insert mode flag of the command input.
2015-01-21
Fixed the labels of>r
and r>
.
Additional header labels without the 'cfa' suffix; all 'cfa' suffix are removed from the labels.
Fix: wrong label in quit
.