Solo Forth development history in 2015-06

Description of the page content

Solo Forth development history in 2015-06.

Tags:

2015-06-02

Start, based on the disassembled code of Abersoft Forth.

First changes:

Some fig-Forth words are renamed with their modern forms: vlist as words, -dup as ?dup, minus as negate, dminus as dnegate, s->d as s>d, mon as bye, free as unused.

Old synonyms are removed: end and endif.

The editor is removed.

branch and back are modified to do absolute branches. The branches are converted with a simple substitution, thanks to the comments prepared by the disassembling of Abersoft Forth.

New words, from the Afera library: \, .(, char and [char], 2-, 1-, 2*.

2+ and 1+ are converted to Z80.

forth is not immediate any more.

' is converted to non-immediate; new immediate version: ['].

2015-06-03

dliteral is renamed as 2literal.

Removed: verify.

2015-06-04

New: upperc and uppers.

Improvement: -find is made case insensitive.

key is documented.

Removed: udg, init-disc, .cpu.

New: <>.

Improvement: the lower part of the screen can be used.

Removed: loadt, savet, (tape) and tape headers.

Removed: triad.

Change: inp renamed as p@; outp renamed as p!.

Change: stacks and buffers are moved to the bottom of the memory map, before the interpreter. They must be below 0xC000 in order to use the memory banks.

Change: r renamed as r@; the Z80 code of i is moved to r@, and the cfa of i is pointed to the pfa of r@. This is logical and faster (formerly r was a colon word that called i).

New: clit and cliteral. Their code ocuppies 46 bytes, but there are 84 compiled bytes that save one byte when using clit instead of lit, and it's faster.

Change: bleep is moved to the blocks disk.

Improvement: quit is optimized: faster and smaller.

New: bounds; used in type.

Removed: not (synonym of 0=).

Change: offset deactivated.

New: block>sector and first tries with the new version of r/w, that uses the sectors of the disk. The screens can be listed, but only the first time. They are shown corrupted when trying to access them more than once. Something is wrong.

New: plotted?, with the code of Afera's point.

New: [if] [else] [then], from Afera. They still need changes.

New: copied require and its related words from lina ciforth and started adapted them.

2015-06-05

New: nip, tuck.

Improvement: 2drop rewritten in Z80.

New: rdrop, in Z80 (exit is pointed to it); 2rdrop and unloop.

Removed: plot (faster version, from Afera, added to the blocks disk); draw (faster version will be create).

Improvement: Error messages are included in screens 4 and 5 of the blocks disk. In order to avoid numbers 0 and 16 (that coincide with the first lines of screens 4 and 5, and are incompatible with the fsb format), some error messages are reorganized (and updated in the main source).

New: -rot in the blocks disk.

New: recurse.

Change: pfa is renamed as nfa>pfa; the rest of the family is renamed with the prefix "pfa>". This is a first step before reorganizing the headers to make them faster, probably moving the lfa before the nfa, and also modifying ' and ['] to return the cfa instead of the pfa.

Fix: there was an old relative branch calculation in then.

Fix: cliteral, recently implemented, used comma!

Fix: +buf used clit for the new b/buf 4 +, that is greater than 0xFF! Now the blocks in disk work fine.

Change: The first six user variables, unused, are moved to the end of the space. All the related pointers are apdated. Conditional compilation is used, just in case. The final goal is to reorganize the parameter area, removing all unnecessary fig-Forth stuff.

Change: Entry points are moved to the start (new origin).

New: Copied decode from Afera. First changes to adapt it.

2015-06-06

New: msg-scr, idea from lina ciforth, useful to customize the organization of the screens on disk.

2015-06-07

Change: faster and smaller versions of ink and paper, from the Afera library. This saves 264 bytes!

New: constants true and false; primitives on and off.

Change: conditional compilation flag to free the IX register, originally used as the user variables pointer, in order to hold next. This way jumps to next are faster and save one byte per jump (52 bytes saved).

Change: user_variables_pointer_init_value is removed. The fig-Forth model uses it in cold, but it's not needed because the memory map is not calculated during the cold start. In fact it is not used by Abersoft Forth.

Fix: The new version of at, taken from Spectrum Forth-83, had a copying mistake, a missing dup.

New: continued, as a factor of load.

Change: optimizations in the definitions, thanks to the new words: 1- instead of 1 minus, 2- instead of 2 -. Also 2dup instead of over over, an oversight of Abersoft Forth.

Improvement: Some operators are (re)written, using Z80 code from DZX-Forth.

2015-06-08

Improvement: faster blanks and erase, with Z80 jumps into fill.

Improvement: the branches of x (the null word) are optimized.

Improvement: one branch removed from ." (2 bytes less, and faster in compiling mode).

Change: Now ' and ['] return the cfa, not the pfa.

New: cfa>pfa, cfa>lfa, cfa>nfa (pfa>nfa and pfa>lfa could be removed).

Change: forget is updated to the new behaviour of ' and moved to the blocks file; also the code in the blocks file is updated.

2015-06-09

Change: flash, bright, gover and inverse are moved to the disk.

Problem detected with (emit) and those four booleans attributes.

New: pick, from DZX-Forth.

Change: The original system to print out with link wasn't practical, because it copied to the printer everything that was printed on the screen. It is removed. Now the new words printer and display are used instead.

2015-06-10

Changes in (emit), in order to fix the problems with boolean attributes. It seems the channel must be opened every time; or the temporary attributes must be set. Finally, all color words are written in Z80, in the kernel, sharing one main routine: printing the chars required to set the temporary attribute, and then calling the ROM routine that makes it permanent. This method works for all attributes and print flags. Example:

paper_nfa:
  db 0x05+0x80,'PAPE','R'+0x80
paper_lfa:
  dw bright_nfa
paper_:
  dw paper_pfa
paper_pfa:
  ld a,paper_char
  jp color

ink_nfa:
  db 0x03+0x80,'IN','K'+0x80
ink_lfa:
  dw paper_nfa
ink_:
  dw ink_pfa
ink_pfa:
  ld a,ink_char
color:
  ; Set a color attribute (ink, paper, bright, flash, inverse or gover).
  ; a = attribute control char
  ; (tos) = color attribute value
  rst 0x10
  pop hl
  ld a,l
  rst 0x10
  call rom_set_permanent_colors_0x1CAD
  _jp_next

The color words are removed from the blocks disk.

Improvement: smaller and faster ?terminal.

Change: the use of the IX register to hold the address of next is made definitive. The conditional compilation for the old method is removed.

Improvement: when the IX register must be preserved because of ROM calls, now ld ix,next is used instead of push ix and pop ix; it's faster and easier, and uses the same memory (4 bytes).

New: ms, time@, time! and time0, copied from Afera.

2015-06-11

The parameters of enclose have been exchanged. This saves a swap in word.

New: pixel-add Z80 routine in the library disk.

Improvement: plot and plotted? use the whole screen.

2015-06-13

The parsing bug has been found. The problem is enclose use an 8-bit register as offset counter. It can not be used to scan a 512-byte buffer. The code has been rewritten to use a pair register as offset counter.

There's a second bug: something fails when the parsing of the screen passes from the first to the second disk block.

Some experimental changes to the null word, with simpler and faster flow.

2015-06-17

Modification in expect; the code that manages the delete key wasted some bytes.

Fix: on and off changed only the first part of the value! That was the reason the second block of screens was not parsed: in was not properly set to zero in the null word.

Improvement: One byte saved in id..

Fix: typo in number in pixel. Now the new point and plot work with Y coordinate range 0..191.

New: the strings module of the Afera library is included in the disk.

New: code (based on create, that will be rewritten), end-code, assembler (empty vocabulary).

New: [defined], [undefined], ?--> and ?\ are included in the kernel, copied from the Afera library. They are essential to manage the disk screens.

Improvement: 2swap is rewritten is Z80, copying the code from DZX-Forth.

Change: size is moved to the disk. It's needed only by system.

Change: -find is renamed as find, and factored out with context-find, in order to implement defined?, needed by required.

Change: i' is moved to the disk.

New: rp, required to rewrite i' outside the kernel.

New: [true] and [false], fram Afera.

2015-06-18

Improvement: Macros are used to create the headers.

Fix: ?exit.

Improvement: faster and smaller 2@ and 2over, do_two_constant.

if 0 ; XXX OLD -- From Abersoft Forth

  inc hl    ; T06  1
  inc hl    ; T06  1 ; high part
  ld e,(hl) ; T07  1
  inc hl    ; T06  1
  ld d,(hl) ; T07  1
  push de   ; T11  1
  dec hl    ; T06  1
  dec hl    ; T06  1
  ld d,(hl) ; T07  1
  dec hl    ; T06  1
  ld e,(hl) ; T07  1
  push de   ; T11  1
  _jp_next  ; T08  2
            ;T104 14 TOTAL

else ; XXX NEW -- From DZX-Forth

  ld e,(hl) ; T07  1
  inc hl    ; T06  1
  ld d,(hl) ; T07  1
  inc hl    ; T06  1
  ld a,(hl) ; T07  1
  inc hl    ; T06  1
  ld h,(hl) ; T07  1
  ld l,a    ; T04  1
  ex de,hl  ; T04  1
  jp pushde ; T10  3
            ; T11  0 push de
            ; T11  0 push hl
            ; T86 12 TOTAL

  ; Faster and shorter than the code of C/PM fig-Forth 1.1g
  ; (T94 14):

  if 0

  EXX         ; T04  1 ; save IP
  POP HL      ; T10  1 ; addr
  LD  C,(HL)  ; T07  1
  INC HL      ; T06  1
  LD  B,(HL)  ; T07  1 ; dH
  INC HL      ; T06  1
  LD  E,(HL)  ; T07  1
  INC HL      ; T06  1
  LD  D,(HL)  ; T07  1 ; dL
  PUSH  DE    ; T11  1 ; (S2)<--dL
  PUSH  BC    ; T11  1 ; (S1)<--dH
  EXX         ; T04  1 ; restore IP
  _jp_next    ; T08  2 ; jp (ix)
              ; T94 14

  endif

New: 2r@.

Change: First changes to modify the format of the name field, removing the end bit of the last char. The new format will make some things easier.

Change: dr0 removed.

Improvement: nip used in / and */.

2015-06-19

Improvement: The new format of name fields is finished and working. The old format can be selected with conditional compilation, just in case, until the system is fully tested.

Some things, like id., are simpler now:

  _colon_header id_dot_,'ID','.'

if fig_name_field?

  dw pad_
  dw c_lit_
  db max_word_length+1
  dw c_lit_
  db 0x5F ; XXX why this char?
  dw fill_
  dw dup_,nfa_to_lfa_,over_ ; ( nfa lfa nfa )
  dw minus_ ; ( nfa len+1 )
  dw pad_,swap_,cmove_
  dw pad_,count_ ; ( pad len+name_bound_bit_mask+n )
  dw c_lit_
  db max_word_length
  dw and_ ; ( pad len )
  dw two_dup_,plus_,one_minus_ ; address of the last char ( pad len pad+len-1 )
if 0 ; XXX OLD
  dw dup_,fetch_
  dw lit_,0xFF7F,and_
  dw swap_,store_
else ; XXX NEW
  dw dup_,c_fetch_
  dw c_lit_
  db 0x7F
  dw and_,swap_,c_store_
endif
  dw type_,space_
  dw semicolon_s_

else ; XXX NEW -- name field without end bit in the last char

  dw count_
  dw c_lit_
  db max_word_length_bit_mask
  dw and_
  dw type_,space_
  dw semicolon_s_

endif

The main change is (find):

  _code_header paren_find_,'(FIND',')'

; (FIND)  ( ca nfa --- pfa b tf | ff )

if fig_name_field?

  ; XXX TODO optimize speed with absolute jumps?
  ; XXX TODO -- return cfa instead of pfa
  ; XXX TODO -- always return two elements, after Forth-94
  pop de ; nfa
paren_find.begin:
  ; (sp) = string address
  ; de = nfa
  pop hl ; string address
  push hl ; save for next iteration
  ld a,(de) ; length byte
  xor (hl) ; filter deviations
  and 0x3F ; mask msb and precedence bit
  jr nz,paren_find.skip_name_field ; lengths differ
paren_find.compare_next_char:
  inc hl ; next character in string
  inc de ; next character in name field
  ld a,(de)
  xor (hl) ; filter deviations
  add a,a
  jr nz,paren_find.not_a_match ; no match
  jr nc,paren_find.compare_next_char ; match so far, loop again
  ; The string matches.
  ; de = address of the last char of the name field
  ld hl,0x0005 ; offset from lfa-1 to pfa
  add hl,de
  ex (sp),hl ; pfa to stack
paren_find.search_for_length_byte:
  dec de ; position de on nfa
  ld a,(de)
  or a ; msb=1?; if so, length byte
  jp p,paren_find.search_for_length_byte ; no, try next char
  ld e,a ; length byte
  ld d,0x00
  ld hl,true
  jp pushde ; name field found, return
paren_find.not_a_match:
  ; Above name field not a match, try next one
  jr c,paren_find.name_field_skipped ; carry = end of name field
paren_find.skip_name_field:
  ; Find end of name field
  inc de
  ld a,(de)
  or a ; msb=1?
  jp p,paren_find.skip_name_field ; no, loop
paren_find.name_field_skipped:
  inc de ; lfa
  ex de,hl
  ld e,(hl)
  inc hl
  ld d,(hl)
  ld a,d
  or e ; end of dictionary?
  jp nz,paren_find.begin ; if not, continue
  ; No match found, return
  pop hl ; drop string address
  jp false_pfa

else

  ld (ip_backup),bc ; save Forth IP
  pop de ; nfa
  pop hl ; string address
  ld (paren_find.string_address),hl
paren_find.begin:
  ; Compare the string with a new word.
  ; de = nfa
  ld (paren_find.nfa_backup),de ; save for later
paren_find.string_address: equ $+1
  ld hl,0 ; string address
  ld a,(de) ; name field length byte
  ld c,a    ; save for later
  and max_word_length_bit_mask  ; actual length
  cp (hl)
  jr nz,paren_find.not_a_match ; lengths differ
  ; Lengths match.
  ld a,c ; name field length byte
  and max_word_length_bit_mask  ; actual length
  ld b,a
paren_find.compare_next_char:
  inc hl ; next character in string
  inc de ; next character in name field
  ld a,(de)
  cp (hl)
  jr nz,paren_find.not_a_match ; no match
  djnz paren_find.compare_next_char ; match so far, loop again

  ; The string matches.
  ; de = address of the last char of the name field
  ; c = name field length byte
  ld hl,5 ; offset from lfa-1 to pfa
  add hl,de
  push hl ; pfa
  ld e,c ; length byte
  ld d,0
  ld hl,true
  ld bc,(ip_backup) ; restore Forth IP
  jp pushde

paren_find.not_a_match:
  ; Not a match, try next one.
paren_find.nfa_backup: equ $+1
  ld hl,0 ; nfa
  ld a,c ; length byte
  and max_word_length_bit_mask  ; actual length
  ld c,a ; actual length
  inc c ; plus the length byte
  ld b,0
  add hl,bc ; hl = lfa
  ld e,(hl)
  inc hl
  ld d,(hl)
  ld a,d
  or e ; end of dictionary?
  jp nz,paren_find.begin ; if not, continue

  ; No match found, return.
  ld bc,(ip_backup) ; restore Forth IP
  jp false_pfa

endif

Change: The new word header substitutes create. create is changed after Forth-94. Its role in fig-Forth was already taken by code.

Change: First steps to implement a modern version of find, after

Forth-83 and Forth-94.

Fix: message had a strange condition, from Abersoft Forth, to prevent from printing message number zero when warning is on.

Added ?branch to decode. Solo Forth uses both 0branch (branch if zero) and ?branch (branch if not zero).

2015-06-20

The new version of find, rewritten after Forth-83 and Forth-94, seems to work fine. Also the new version of the name field works fine. Conditional compilation is used for both changes, just in case.

New: error does not push the values of blk and in, but stores them in a new double variable called error-pos. where fetchs them from there. This is cleaner and more flexible, because the last error position can be shown any time.

New: label, a define word that returns the address of its pfa, like a variable, but stores nothing into it. Useful to create machine code routines or data buffers.

New: nfa>string, adapted from DZX-Forth. It was a natural factor of the new version of id..

Improvement: error saves the error number into the new variable error#.

Change: slit, sliteral, s, and s" are moved from the disk to the kernel. Basic string support is needed to manage the disk screens (require, locate, compare and others).

New: scr/disk constant, screens per disk. It will be needed to compile a variant for +3DOS, or to use disk drives of different sizes.

New: cells, cell and cell+.

New: set of hex numbers printing words, adapted from lina.

2015-06-21

compare is finished. Some changes were necessary in the code of DZX-Forth.

; ----------------------------------------------
  _code_header compare_,'COMPAR','E'

  ; Adapted from DZX-Forth.

  pop de      ; de = len2
  pop hl      ; hl = ca2
  ex (sp),hl  ; hl = len1 ; ( ca1 ca2 )
  ld a,d
  cp h
  jr nz,compare.lengths
  ld a,e
  cp l
compare.lengths:
  ; cy = string2 is longer than string1?
  jr c,compare.ready
  ex de,hl
compare.ready:
  ; de = length of the short string
  ; hl = length of the long string
  ld l,c
  ld h,b ; hl = Forth IP
  pop bc ; bc = ca2
  ex (sp),hl ; hl = ca1 ; save Forth IP
  push af ; save carry flag
compare_.compare_strings: equ $+1 ; XXX not used yet
  call compare_strings_case_sensitive
  ;jr z,compare.match
  jr nz,compare.no_match

compare.match:
  ; The smaller string matches.
  pop af ; restore flags
  jr compare.end

compare.no_match:
  ; The smaller string does not match.
  pop bc ; useless carry flag

compare.end:
  pop bc ; restore Forth IP
  ld hl,1
  jp c,pushhl
  dec hl ; 0
  jp z,pushhl ; string1 equals string2
  dec hl ; -1
  jp pushhl

compare_strings_case_sensitive:
  ; Copied from DZX-Forth.
  ; Used by 'compare' and 'search'.
  ; Input:
  ;   HL = a1
  ;   BC = a2
  ;   DE = len
  ; Output:
  ;   Z = match?
  ld a,e
  or d
  ret z
  ld a,(bc)
  cp (hl)
  ret nz
  inc hl
  inc bc
  dec de
  jp compare_strings_case_sensitive

New: <mark, <resolve, >mark and >resolve,, after Forth-83. <resolve substitutes fig-Forth's back. With this words, the code of control structures is clearer, more legible, and it's easier to implement new ones.

Change: A bit simpler and faster endcase.

Change: cswap is renamed as flip, after eForth; upperc is renamed as upper.

New: :noname, copied from the Afera library.

New: search, adapted from DZX-Forth.

Change: count is rewritten in Z80, faster and smaller (using the end of clit to push the result).

2015-06-22

Improvement: faster and smaller > and <, using code from DZX-Forth. New: 0>, based on the same code.

  _code_header zero_greater_than_,'0','>'

  pop de
  ld hl,0
  jp is_de_less_than_hl

  _code_header greater_than_,'','>'

  pop hl
  pop de
  jp is_de_less_than_hl

  _code_header less_than_,'','<'

if 1 ; XXX NEW -- from DZX-Forth

  pop de
  pop hl
is_de_less_than_hl:
  call compare_de_hl_signed
  if size_optimization?
    jp true_if_cy
  else
    jp c,true_pfa
    jp false_pfa
  endif

else ; XXX OLD

  pop de ; n2
  pop hl ; n1
  ld a,d
  xor h ; one of them negative?
  jp m,less_than.wich_negative ; if so, determine which
  ; no one is negative
  and a ; clear carry
  sbc hl,de

less_than.wich_negative:

  if 0 ; XXX OLD

  ; Original version from Abersoft Forth, badly optimized
  ; because of the two 'ld hl' and two `jp push_hl`.

  inc h
  dec h ; h negative?
  jp m,less_than.true
  ld hl,false
  jp push_hl
less_than.true:
  ld hl,true
  jp push_hl

  else ; XXX NEW

  ; Version from CP/M fig-Forth 1.1g,
  ; with additional improvement for speed:

  ; bit 7,h ; h negative?
  ; ld hl,false
  ; jp z,push_hl
  ; inc l ; true ; XXX TODO -- change to `dec hl` when true=-1
  ; jp push_hl

  ; Optimized for space:

  bit 7,h ; h negative?
  jp z,false_pfa
  jp true_pfa

  endif

endif

Faster and smaller version of u<, in Z80; the correspondent u> is added.

; ----------------------------------------------
  _code_header 'U','>'

  pop hl
u_greater_than.hl:
  pop de
  jp u_less_than.de_hl

; ----------------------------------------------

if 0 ; XXX OLD -- original

  _colon_header u_less_than_,'U','<'

  dw two_dup_
  dw xor_
  dw zero_less_than_
  dw zero_branch_,branch_destination_0x6585
  dw drop_
  dw zero_less_than_
  dw zero_equals_
  dw branch_,u_less_than_.end
branch_destination_0x6585:
  dw minus_
  dw zero_less_than_
u_less_than_.end:
  dw semicolon_s_

else

  _code_header u_less_than_,'U','<'

  pop de
  pop hl
u_less_than.de_hl:
  call compare_de_hl_unsigned
  if size_optimization?
    jp true_if_cy
  else
    jp c,true_pfa
    jp false_pfa
  endif

endif


New: emits, converted from eForth; spaces is rewritten to use it.

line is removed; it was a rest from the editor.

Faster and smaller lfa>nfa:


if 0 ; XXX OLD

  _colon_header lfa_to_nfa_,'LFA>NF','A'

lfa_to_nfa.begin:
  dw one_minus_,dup_,c_fetch_ ; ( a c )
  dw c_lit_
  db name_bound_bit_mask
  dw and_
  dw zero_branch_,lfa_to_nfa.begin ; until
  dw semicolon_s_

else ; XXX NEW

  _code_header lfa_to_nfa_,'LFA>NF','A'

  pop hl
lfa_to_nfa.do:
  dec hl
  bit name_bound_bit,(hl)
  jp z,lfa_to_nfa.do:
  jp push_hl

endif


Improvement: simpler and faster version of the null word.

  _colon_header x_,'',0,immediate

if 0 ; XXX OLD -- original version

  dw blk_,fetch_ ; input stream from disk?
  dw zero_branch_,x.from_terminal
  ; From disk.
  dw one_,blk_,plus_store_ ; next disk buffer
  dw in_,off_ ; clear `in`, preparing parsing of input text
  dw blk_,fetch_,b_slash_scr_,one_minus_,and_,zero_equals_ ; last buffer?
  dw zero_branch_,x.end
  ; The last buffer, the end of the text block.
  dw question_exec_ ; error if not executing
  ; Discard the address of `?stack` after `execute` in the interpretation loop.
  dw from_r_,drop_
  dw branch_,x.end
x.from_terminal:
  dw from_r_,drop_
x.end:
  dw semicolon_s_

else ; XXX NEW -- simpler and faster flow

  dw blk_,fetch_ ; input stream from disk?
  dw zero_branch_,x.exit ; if not, branch
  ; From disk.
  dw one_,blk_,plus_store_ ; next disk buffer
  dw in_,off_ ; clear `in`, preparing parsing of input text
  ; Note: This check of the last block is specific for 2 blocks per screen;
  ; the generic slower check would be `blk @ b/scr 1- and`.
  dw blk_,fetch_,one_,and_ ; was it the last block of the screen?
  dw question_branch_,x.end ; if not, branch
  ; Last block of the screen.
  dw question_exec_ ; error if not executing
x.exit:
  dw r_drop_
x.end:
  dw semicolon_s_

endif

New smaller and faster versions of min and max, in Z80. New: umin and umax. All of them are adapted from DZX-Forth:

; ----------------------------------------------
  _code_header umax_,'UMA','X'

  ; umax  ( u1 u2 -- u1 | u2 )

  pop de
  pop hl
  call compare_de_hl_unsigned
  jp max.1

; ----------------------------------------------
  _code_header umin_,'UMI','N'

  ; umin  ( u1 u2 -- u1 | u2 )

  pop de
  pop hl
  call compare_de_hl_unsigned
  jp max.2

; ----------------------------------------------

if 0 ; XXX OLD

  _colon_header min_,'MI','N'

  dw two_dup_
  dw greater_than_
  dw zero_branch_,min.end
  dw swap_
min.end:
  dw drop_
  dw semicolon_s_

else

  _code_header min_,'MI','N'

  pop de
  pop hl
  call compare_de_hl_signed
  jp max.2

endif

; ----------------------------------------------

if 0 ; XXX OLD

  _colon_header max_,'MA','X'

  dw two_dup_
  dw less_than_
  dw zero_branch_,max.end
  dw swap_
max.end:
  dw drop_
  dw semicolon_s_

else

  _code_header max_,'MA','X'

  pop de
max.de:
  pop hl
  call compare_de_hl_signed
max.1:
  ccf
max.2:
  jp c,push_hl
  ex de,hl
  jp push_hl

endif

Change: in renamed as >in, blanks renamed as blank,; forgot during the vocabulary modernizing.

First tries to implement a more efficient parsing method, with parse.

Change: screen, p! and p@ are moved to the disk.

Improvement: Optimized version of s>d:

  _code_header s_to_d_,'S>','D'

if 0 ; XXX OLD

  pop de
  ld hl,0
  ld a,d
  and 0x80 ; negative?
  jr z,s_to_d.end ; jump if not negative
  dec hl
s_to_d.end:
  jp push_de_hl

else ; XXX NEW

  ld hl,0
  pop de
  ld a,d
  or a
  jp p,push_de_hl ; jump if positive
  dec hl
  jp push_de_hl

endif

Fix: severe error recently introduced in id..

New: 2>r and 2r>, adapted from DZX-Forth:

; ----------------------------------------------
  _header _two_to_r,'2>','R'

; 2>r  ( -- x1 x2 ) ( R: x1 x2 -- )

  ld hl,(return_stack_pointer)
  ld de,-cell*2
  add hl,de
  ld (return_stack_pointer),hl
  jp two_store.into_hl_pointer

; ----------------------------------------------
  _header _two_from_r,'2R','>'

; 2r>  ( -- x1 x2 ) ( R: x1 x2 -- )

  ld hl,(return_stack_pointer)
  push hl
  ld de,cell*2
  add hl,de
  ld (return_stack_pointer),hl
  jp two_fetch_pfa

The original fig-Forth text is removed. It has become obsolete after implementing parse:

if 0 ; XXX OLD

  _colon_header text_,'TEX','T'

  ; text  ( c "ccc<c>" -- pad len )
  ; Accept following text to `pad`. _c_ is the text delimiter.

  dw here_,c_slash_l_,one_plus_,blank_ ; fill the word buffer with blanks
  dw word_
  dw here_ ; origin
  dw pad_ ; destination
  dw c_slash_l_,one_plus_ ; count
  dw cmove_
  dw pad_,count_
  dw semicolon_s_

endif

variable, 2variable, constant, 2constant and label are simplified, rewritten around create.

Adapted ." to parse:

  _colon_header dot_quote_,'.','"',immediate

if 0 ; XXX OLD

  dw c_lit_
  db '"'
  dw comp_question_ ; compiling?
  dw zero_branch_,dot_quote.interpreting
  ; Compiling.
  dw compile_,paren_dot_quote_
  dw word_
  dw here_,c_fetch_,one_plus_,allot_
  dw semicolon_s_
dot_quote.interpreting:
  dw word_,here_,count_,type_
  dw semicolon_s_

else

  dw c_lit_
  db '"'
  dw parse_ ; ( ca len )
  dw comp_question_
  dw zero_branch_,dot_quote.interpreting
  ; Compiling.
  dw compile_,paren_dot_quote_,s_comma_
  dw semicolon_s_
dot_quote.interpreting:
  dw type_
  dw semicolon_s_

endif

Copied .s from Afera. The original algorithm was taken from v.Forth.

: .s   ( -- )
  depth
  dup s>d <# [char] > hold #s [char] < hold #> type space
  if  sp@ 2- s0 @ 2- do i @ . -2 +loop  then  ;

Copied 2nip from Afera to the library disk. The original code was taken from DZX-Forth.

2015-06-23

New: parse-word.

Improved cmove:

  _code_header cmove_,'CMOV','E'

if 0 ; XXX OLD

  ld l,c          ; 04t 01b
  ld h,b          ; 04t 01b
  pop bc          ; 10t 01b
  pop de          ; 10t 01b
  ex (sp),hl      ; 19t 01b
  ld a,b          ; 04t 01b
  or c            ; 04t 01b
  jr z,cmove.end  ; 07t 02b / 12t
  ldir            ;     02b ; 21t/01t
cmove.end:
  pop bc          ; 10t 01b
                  ; 62t 12b TOTAL
  _jp_next

else ; XXX NEW

  exx             ; 04t 01b
  pop bc          ; 10t 01b
  pop de          ; 10t 01b
  pop hl          ; 10t 01b
  ld a,b          ; 04t 01b
  or c            ; 04t 01b
  jr z,cmove.end  ; 07t 02b / 12t
  ldir            ;     02b ; 21t/01t
cmove.end:
  exx             ; 04t 01b
                  ; 52t 11b TOTAL
  _jp_next

endif

Change: cmove> and move are moved to the kernel. move is rewritten in Z80, with code from DZX-Forth.

New: $!, with the code of DZX-Forth's packed. Name after lina's string words.

Improvement: faster and smaller -trailing, with code from DZX-Forth:

if 0 ; XXX OLD

  _colon_header minus_trailing_,'-TRAILIN','G'

  dw dup_,zero_
  dw paren_do_
minus_trailing.do:
  dw two_dup_,plus_
  dw one_minus_
  dw c_fetch_,b_l_,not_equals_
  dw zero_branch_,minus_trailing.space
  ; not a space
  dw leave_
  dw branch_,minus_trailing.loop
minus_trailing.space:
  dw one_minus_
minus_trailing.loop:
  dw paren_loop_,minus_trailing.do
  dw semicolon_s_

else ; XXX NEW

  _code_header minus_trailing_,'-TRAILIN','G'

  pop de
  pop hl
  push hl
  add hl,de
  ex de,hl
  ; de = address after the string
  ; hl = length of the string
minus_trailing.begin:
  ld a,l
  or h ; exhausted?
  jp z,push_hl
  dec de ; next char
  ld a,(de)
  cp ' ' ; space?
  jp nz,push_hl
  dec hl ; new length
  jp minus_trailing.begin ; repeat

endif

Fix: 2>r and 2r> had typos in the header macro.

Faster and smaller char, with parse-word instead of word.

First tries with require and its related words, adapted from lina ciforth.

New: binary number printing words, after the lina's tool for hex numbers.

2015-06-24

A bit better code for (do), from CP/M fig-Forth 1.1g

  _code_header paren_do_,'(DO',')'

if 0 ; XXX OLD -- original from Abersoft Forth

  ld hl,(return_stack_pointer)  ; 20t 03b
  dec hl                        ; 06t 01b
  dec hl                        ; 06t 01b
  dec hl                        ; 06t 01b
  dec hl                        ; 06t 01b
  ld (return_stack_pointer),hl  ; 16t 03b
  pop de                        ; 10t 01b
  ld (hl),e                     ; 07t 01b
  inc hl                        ; 06t 01b
  ld (hl),d                     ; 07t 01b
  pop de                        ; 10t 01b
  inc hl                        ; 06t 01b
  ld (hl),e                     ; 07t 01b
  inc hl                        ; 06t 01b
  ld (hl),d                     ; 07t 01b
                                ;126t 19b TOTAL

else ; XXX NEW -- from CP/M fig-Forth 1.1g

  exx                           ; 04t 01b
  pop de                        ; 10t 01b
  pop bc                        ; 10t 01b
  ld hl,(return_stack_pointer)  ; 20t 03b
  dec hl                        ; 06t 01b
  ld (hl),b                     ; 07t 01b
  dec hl                        ; 06t 01b
  ld (hl),c                     ; 07t 01b
  dec hl                        ; 06t 01b
  ld (hl),d                     ; 07t 01b
  dec hl                        ; 06t 01b
  ld (hl),e                     ; 07t 01b
  ld (return_stack_pointer),hl  ; 16t 03b
  exx                           ; 04t 01b
                                ;116t 18b TOTAL
endif

2015-06-25

Fix: require and locate needed to save the parsed words to the circular string buffer.

Fix: emits didn't check values less than zero; this affected spaces and d.r.

New: smove, equivalent to swap move but much faster.

New: bank, required to use a memory page to store the name fields and link fields.

  _code_header bank_,'BAN','K'

; doc{
;
; bank  ( n -- )
;
; Page memory bank _n_ at 0xC000..0xFFFF.
;
; }doc

  di
  pop de ; e = bank
  ld a,(sys_bankm) ; get the saved status of BANKM
  and 0xF8 ; erase bits 0-2
  or e ; modify bits 0-2
  ld (sys_bankm),a ; update BANKM
  out (bank1_port),a ; page the bank
  ei
  _jp_next

The fig-Forth name field format is definitively removed. It remained as an option, just in case, but everything works fine with the new format.

Finished the conversion of [if] [else] [then].

Counted the number of times constants 0, 1, 2 and 3 are compiled in the code, in order to decide about them:

Number Times compiled (not including error numbers)
0 20
1 11
2 11
3 6

2015-06-26

First changes to store the name and link fields in a memory bank (with a pointer to the correspondent cfa), freeing dictionary memory.

2015-06-27

Fix: The final memory bank selection in (find) corrupted the returned cfa.

Fix: The algorithm of the new cfa>nfa (for name fields in paged memory) had a bug.

Fix: The new nfa>string (for name fields in a paged memory) had a wrong header.

New: the version header for name fields in paged memory is finished.

2015-06-28

First tries with field names in paged memory. So far the code was tried without actually changing the memory bank, but using the same memory addresses. nfa>cfa and words need to change the bank.

New: n@ fetchs from the names bank.

Change: renamed s0 to sp0 and r0 to rp0, after Laxen&Perry's F83.

The old original optional code related to fig-Forth's find is removed. The new version of find with all its related words has been working fine.

2015-06-29

Fix: immediate and smudge didn't paged the names bank.

New: postpone:

  _colon_header postpone_,'POSTPONE',immediate

; doc{
;
; postpone ( "name" -- )  \ ANS Forth, C I
;
; Skip leading space delimiters. Parse name delimited by a
; space. Find name. Append the compilation semantics of _name_ to
; the current definition.
;
; }doc

  dw defined_ ; ( ca 0 | cfa 1 | cfa -1 )
  dw dup_,question_defined_ ; error if not found
  dw zero_less_than_ ; non-immediate word?
  dw zero_branch_,postpone.end
  ; Non-immediate word.
  dw compile_,compile_ ; compile `compile`
postpone.end:
  dw comma_ ; compile the cfa
  dw semicolon_s_

First drafts of alternative definitions of do-loop structures, with code adapted from Spectrum Forth-83, v.Forth and DZX-Forth, in order to implement ?do.

Change: now variable and 2variable do not initialize their contents, after ANS Forth. 3 has been removed.

Fix: continued could not be used from the command line, because it does not save and restore the input-source specification. A check is added, with ?loading; (load) is factored out, and called also by load.

fig-Forth recognizes the end of the input stream when a null character is found. Disk buffers already have a null at the end of the actual data space, and expect adds it after the typed text. enclose parses the null character as an unconditional delimiter and treats it apart in order to simulate the null word has been parsed, which is the word that actually does the unnesting.

The problem of the described method is the null character needs a special parsing treatment, what makes new words like skip and scan to be less versatile.

In order to solve that, a change has been made: instead of a null character, a null word is stored at the end of the buffers: a null character surrounded by spaces. This way, the null character needs no special treatment during the parsing. This new method is compatible with the original fig-Forth parsing words, though they will be replaced.

Change: expect is improved after Forth-83 (it does not add anything after the text any more); query is updated accordingly, adding the null word at the end of the received text.

Fix: The new version of word didn't updated >in to the space after the word, what caused the new versions of s", .( and .", that use parse, to include the first space of their strings.