Jump to content
IGNORED

fbForth—TI Forth with File-based Block I/O [Post #1 UPDATED: 06/05/2024]


Lee Stewart

Recommended Posts

Well—I finally finished my translation of the 40/80-column fbForth editor. Now for the painful job of debugging it! :-o It's currently occupying 2148 bytes. There are surely tweaks that could reduce that; but, I must first get what I have cleaned up! Maybe sometime this week for the next beta.

 

...lee

  • Like 1
Link to comment
Share on other sites

I know—my debugging has taken far too long! :skull: I got side-tracked on an OOo Writer macro for about a week—kept banging my head against a longstanding bug in OOo's macro recorder. Now, back to debugging the 40/80-column editor. It's probably going to be awhile yet because I keep running into indirection errors I made in translating from Forth to ALC. I keep forgetting (I'm old, dontcha know) that I can only execute indirect addresses in registers! :woozy: I also just discovered that I'd loaded a jump table address into a register and jumped to it (the DATA statement!?!) instead of indirectly to the contained address! Oh, well—I'll eventually get there thanks to Classic99's Debugger. It's still painful, though.

 

I keep thinking there must be some tricks I'm missing. I have used the X instruction to manipulate jumps, which was inspired by @Tursi's implementation of the bitmap LINE word a few posts back. Right now, however, I'm just going to focus on making the editor work. Then, perhaps, I can reduce its footprint—I don't know.

 

Anyway—I'm back on it... :P

 

...lee

  • Like 1
Link to comment
Share on other sites

I am now much farther along than I ever thought possible. I think I only have TAB and -TAB (moving the cursor forward and backward by words through the block being edited) to fix. They almost work, but not quite. As long as everything holds up through this storm that's passing through in the next hour or so, I should have another beta in a day or two! :-o

 

...lee

  • Like 1
Link to comment
Share on other sites

I am pretty much done with the 40/80-column editor. I need to fix a couple of routines that should be setting the dirty bit, but are not. I also may include a navigation menu à la @Willsy's TurboForth editor. I should have a new beta by day's end.

 

For those interested in how I used the X instruction to vary jumping in the TAB and -TAB routines, here they are for your delectation:

 

 

 

;[*++ TAB 
*++ These instruction codes need to be updated whenever changes
*++  are made to increase the distance between "X R2" and either
*++  of the jump targets, TABBL or TABNOB.
JEQTAB EQU  >1306           ; "JEQ TABBL" for X instruction
JNETAB EQU  >1609           ; "JNE TABNOB" for X instruction

TAB    MOV  @EDBLK,R0       ; get block address
       A    @CUR,R0         ; get cursor address
       LI   R4,1024         ; load counter with maximum
       S    @CUR,R4         ; adjust to cursor location
       LI   R2,JEQTAB       ; set jump address for "blank found"
TAB01  CB   *R0,@CONBL      ; is it a blank?
       X    R2              ; execute jump instruction
TAB02  DEC  R4              ; decrement counter
       JEQ  TABNOB          ; jump if we're done:
       INC  R0              ; increment block address
       INC  @CUR            ; increment block cursor
       JMP  TAB01           ; let's check another character
TABBL  LI   R2,JNETAB       ; set jump address for "blank not found"
       JMP  TAB02           ; continue

*++ we found the next word or reached the end of the block
TABNOB BL   @DCUR           ; update CURPOS etc.
       B    @VEDLP          ; return to editor loop
;]* 

;[*++ -TAB 
*++ These instruction codes need to be updated whenever changes
*++  are made to increase the distance between "X R2" and any
*++  of the jump targets, BTABL1, BTBNOB or BTABL2.
JEQBT1 EQU  >1306           ; "JEQ BTABL1" for X instruction
JNEBTB EQU  >1609           ; "JNE BTBNOB" for X instruction
JEQBT2 EQU  >130C           ; "JEQ BTABL2" for X instruction

BTAB   MOV  @EDBLK,R0       ; get block address
       A    @CUR,R0         ; get cursor address
       MOV  @CUR,R4         ; load cursor location
       INC  R4              ; correct counter
       LI   R2,JEQBT1       ; set jump address for "blank found" the 1st time
BTAB01 CB   *R0,@CONBL      ; is it a blank?
       X    R2              ; execute jump instruction
BTAB02 DEC  R4              ; decrement counter
       JEQ  TABNOB          ; if we're done, finish up in TAB routine
       DEC  R0              ; decrement block address
       DEC  @CUR            ; decrement block cursor
       JMP  BTAB01          ; let's check another character
BTABL1 LI   R2,JNEBTB       ; set jump address for "blank not found"
       JMP  BTAB02          ; continue

*++ we found a non-blank character
BTBNOB LI   R2,JEQBT2       ; set jump address for "blank found" the 2nd time
       JMP  BTAB02          ; continue

*++ we found the blank before the previous word or reached the beginning of the block
BTABL2 INC  @CUR            ; restore cursor to 1st char of word we just passed
       JMP  TABNOB          ; finish up in TAB routine
;]* 

 

 

 

...lee

 

 

  • Like 1
Link to comment
Share on other sites

OK—The beta 6 files are posted in the Post #1 of this thread. Let me know what problems you find—particularly with the 40/80-column editor, which is now part of the resident dictionary and is invoked by

 

<block#> EDIT

 

1 EDIT will start the editor with block #1 of the current blocks file (default is FBLOCKS). More detailed instructions are to be found in the fbForth 1.0 manual in the development thread. I have yet to edit the fbForth 2.0 manual. It'll come. I'm just not quite finished with the fbForth 2.0 cartridge. :P

 

...lee

Link to comment
Share on other sites

I found an error in the name field of VED , the main function of the 40/80-column editor. It doesn't hurt the editor's function, but VED won't be found with a name search. Instead, there are two LINE words found! That's what happens with cut-n-paste when one is not extremely careful. :dunce: I will post a corrected version in Post #1 later today or tomorrow.

 

...lee

Link to comment
Share on other sites

Post#1 has the corrected beta 6a files. Please test and apprise me of any problems you find.

 

I have just two more libraries I would like to add to cartridge space before I officially release fbForth 2.0:

  • File I/O—This should be pretty simple because I will probably just translate the original TI Forth library from the blocks file—though I probably should try to improve it a little.
  • Floating Point Math—This will be the library I adapted from the Geneve MDOS library for TurboForth, but this time tailored to fbForth.
    • I don't remember whether @Willsy and I determined if it was faster than the console routines.
    • I know that the display of scientific notation is better.
    • Probably all of the transcendental routines are faster than those in the console, which were written in GPL.
    • I think there might have been an improvement of the accuracy of the SIN routine.
    • The XML-based console routines (+, -, *, /, etc.) might be faster, however, because the console ROMs are on a 16-bit bus.

 

...lee

Link to comment
Share on other sites

Here's the ALC for the 40/80-column editor (all 2366 bytes of it!), in case anyone is interested in perusing/improving it: :grin:

 

 

 

* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
* >>>>>>>>>>>>>>>>>>>>> 40/80 Column Editor <<<<<<<<<<<<<<<<<<<<<<
* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
* 
;[*++ OLDCUR        variable array  +++ Storage for old cursor pattern
*++ OLDCUR is located in low RAM        ; 8 bytes
;]* 
;[*++ CUR           variable        +++ Storage for block cursor
CUR    EQU  $RNUM+$UVAR             ; contains cursor position in block)
;]* 
;[*++ S_H           variable        +++ Storage for 40-column page flag
S_H    EQU  FREEPD              ; 0|1 for left|right screen half
;]*
;[*++ EDBLK         variable        +++ Current block buffer address
EDBLK  EQU  FREEPD+2        ; current block buffer address
;]* 
;[*++ CURCH         variable        +++ Char under cursor (one byte)
CURCH  EQU  FREEPD+4        ; char under cursor (one byte)
;]* 
;[*++ EDPAD         variable        +++ Address of PAD upon entering the editor
EDPAD  EQU  FREEPD+6
;]*
;[*++ LIN#          variable        +++ Temp storage for Forth block line#
LIN#   EQU  FREEPD+8        ; temporary storage for Forth block line#
;]*
;[*++ COL#          variable        +++ Temp storage for Forth block column#
COL#   EQU  FREEPD+10       ; temporary storage for Forth block column#
;]*
;[*++ BLKPOS        variable        +++ Temp storage for Forth block cursor address
BLKPOS EQU  FREEPD+12       ; temporary storage for Forth block cursor address
;]*
;[*++ LNSTRT        variable        +++ Starting line# for call to LINE.
LNSTRT EQU  FAC+2           ; temporary storage for starting line# for LINE.
;]*
;[*++ LNCNT         variable        +++ Number of lines for call to LINE.
LNCNT  EQU  FAC+4           ; temporary storage for # of lines for LINE.
;]*
* ( RKEY required variables)
;[*++ BLINK         variable        +++ Cursor blink logger
BLINK  EQU  ARG             ; init, 0
;]* 
;[*++ OKEY          variable        +++ Old key for repeat comparison
OKEY   EQU  ARG+2           ; init, 0
;]* 
;[*++ KC            variable        +++ Key counter for repeat
KC     EQU  ARG+4           ; init, 0
;]* 
;[*++ RLOG          variable        +++ Has short or long wait value for repeat logging
RLOG   EQU  ARG+6           ; init, 150
;]* 
;[*++ RL            constant        +++ Short wait value for key repeats
RL     DATA 30
;]* 
;[*++ RH            constant        +++ Long wait value for start of key repeats
RH     DATA 450
;]* 
;[*++ BOX           constant array  +++ Editor's cursor pattern
BOX    DATA >FC84,>8484,>8484,>84FC     ; editor's cursor
;]* 
;[*++ CON01         constant        +++ >0001
CON01  DATA >0001           ; constant for use in various places
;]*
;[*++ CONFF         constant        +++ >FF
CONFF  BYTE >FF           ; byte constant >FF for comparison
;]* 
;[*++ CONBL         constant        +++ >20
CONBL  BYTE >20             ; byte constant >20 = ASCII blank
       EVEN
;]*
;[*++ CN8000        constant        +++ >8000
CN8000 DATA >8000           ; constant for use in various places
;]*
;[*++ Navigation menu string (NAVMNU)
NAVMNU BYTE 158
       TEXT "F1:Del F2:Ins F3:Del Line F4:Nxt Block  "
       TEXT "F5:30 spcs L/R F6:Prv Block F7:Del EOL  "
       TEXT "F8:Ins Line F9:Exit ESDX:Cursor ^V:Tab  "
       TEXT "ENT:Nxt Line ^8:Ins Blank Line FV:-Tab"
EVEN
;]*
;[*++ !CUR          ( n --- )       +++ Store new block cursor (n) in CUR
*++ Store new block cursor position (n) in CUR
*++ STCUR expects n on stack

STCUR  CLR  R0                  ; zero R0
       C    *SP,R0              ; new block cursor pos > 0?
       JGT  STCUR1              ; jump if so
       MOV  R0,*SP              ; no, store 0 on stack
STCUR1 LI   R0,>3FF             ; load 1023 for comparison
       C    R0,*SP              ; 1023 > new block cursor pos?
       JGT  STCUR2              ; jump if so
       MOV  R0,*SP              ; no, store 1023 on stack
STCUR2 MOV  *SP+,@CUR           ; pop new block cursor pos
       RT                       ; return to caller
;]* 
;[*++ +CUR          ( n --- )       +++ Add n to block cursor
PLCUR  DECT R                   ; make room on return stack to
       MOV  LINK,*R             ;   save return
       A    @CUR,*SP            ; add current block cursor pos to num on stack
       BL   @STCUR              ; update cursor position
       MOV  *R+,LINK            ; pop return address
       RT                       ;   and return
;]* 
;[*++ LINE.                         +++ Display LNCNT lines beginning with line LNSTRT
*++ This routine expects two 16-bit values after call:
*++     1. The starting line number in LNSTRT
*++     2. The number of lines to be typed in LNCNT
*++
*++ Called by:
*++     BL   @LINE.
*++
*++ Register usage:
*++     R0: VRAM screen destination for VMBW
*++     R1: RAM Forth block buffer source for VMBW
*++     R2: character count for VMBW
*++     R3: # lines to process
*++     R4: calculation of VRAM screen address
*++     R5:     "       "   "      "      "

LINE.  MOV  @LNSTRT,R4      ; load starting line # (0-15)
       MOV  R4,R1           ; copy for RAM source calc
       SLA  R1,6            ; * 64 to get starting line offset
       A    @EDBLK,R1       ; Forth buffer starting address
       MOV  @LNCNT,R3       ; load # of lines to process (1-16)
       AI   R4,3            ; calc starting screen row
       MPY  @$SWDTH(U),R4   ; calc starting screen-row offset
       AI   R5,3            ; calc starting screen-column offset
       A    @$SSTRT(U),R5   ; calc VRAM starting screen address
       MOV  R5,R0           ; copy to R0 for VMBW
       LI   R2,64           ; load characters/screen-line (assume 80-column MODE)
       LIMI 0               ; disable interrupts because VMBW doesn't
       MOV  @$VDPM(U),R5    ; 80-column mode?
       JEQ  LINE.1          ; jump if so
       LI   R2,34           ; load characters/line for 40-column mode
       MOV  @S_H,R4         ; right half of 40-column screen?
       JEQ  LINE.1          ; jump if not
       AI   R1,30           ; correct Forth buffer source address
LINE.1 BLWP @VMBW           ; type a line
       A    @$SWDTH(U),R0   ; calc VRAM screen address for next line
       AI   R1,64           ; calc Forth buffer address for next line
       DEC  R3              ; are we done?
       JNE  LINE.1          ; do another if not
       LIMI 2               ; restore interrupts
       RT                   ; return to caller
;]* 
;[*++ LISTL         ( block# --- )  +++ Display left half (40-col) or full (80-col) screen
LISTL  
*++ Save stuff
       DECT R                   ; make room on return stack to
       MOV  LINK,*R             ;   save return
       DECT R                   ; make room on return stack to
       MOV  @$BASE(U),*R        ;   save current radix

*++ Load requested block and update pointers
       MOV  *SP,@$SCR(U)        ; update last block accessed (SCR)
       BL   @BLA2F              ; load requested block
       DATA BLOCK
       MOV  *SP,@EDBLK          ; get block buffer address (save stack spot)

*++ Type left header =======================================================

*++ Type "BLOCK #n" at top left
       LI   R2,0                ; load x
       LI   R3,0                ; load y
       BL   @GXY                ; place text cursor
       LI   R4,B#TXT            ; text to type
       BL   @MSGTYP             ; type it
       MOV  @$SCR(U),*SP        ; get block # to stack
       BL   @BLA2F              ; type it
       DATA DOT

*++ Type units digits of line numbers down left side
       LI   R2,16               ; load # of line #s
       LI   R3,LN#S             ; next char to write
       MOV  @$SWDTH(U),R0       ; set starting screen address to row 1
       SLA  R0,1                ;   row2
       INC  R0                  ;   row 2, column 1
       LIMI 0
LSTL01 A    @$SWDTH(U),R0       ; next row
       MOVB *R3+,R1             ; get next char to write
       BLWP @VSBW               ; write it
       DEC  R2                  ; are we done?
       JNE  LSTL01              ; nope, write next char
 
*++ Type tens digits (1) of line numbers down left side
       LI   R0,0                ; load x
       LI   R6,13               ; load y
       LI   R1,>3100            ; ASCII[>31]
       LI   R2,6                ; load count
       BL   @__VCHR             ; type vertical line of 6 1s
       
*++ Type left side of editing box
       LI   R0,2                ; load x
       LI   R6,3                ; load y
       LI   R1,>CF00            ; ASCII[207]
       LI   R2,16               ; load count
       BL   @__VCHR             ; type vertical line

*++ Type top row of tens digits
       LI   R2,3                ; load x
       LI   R3,1                ; load y
       BL   @GXY                ; place text cursor
       LI   R4,TL10S            ; text to type
       BL   @MSGTYP             ; type it

*++ Type top of editing box
       LI   R2,3                ; load x
       LI   R3,2                ; load y
       BL   @GXY                ; place text cursor
       LI   R4,TLLIN            ; text to type
       BL   @MSGTYP             ; type it

*++ Type bottom of editing box
       LI   R0,>CD00            ; get ASCII[205] to R0 MSB for FILL1 (VDP fill routine)
       LI   R1,19               ; load y
       MPY  @$SWDTH(U),R1       ; multiply by SCRN_WIDTH
       AI   R2,3                ; add x
       A    @$SSTRT(U),R2       ; calculate actual VRAM start address for FILL1
       LI   R1,34               ; put 40-col cnt in R1 for FILL1
       MOV  @$VDPM(U),R4        ; 80-col mode?
       JNE  LIST02              ; jump if not
       LI   R1,64               ; 80-col cnt
LIST02 LIMI 0                   ; disable interrupts because FILL1 & VSBW don't
       BL   @FILL1              ; do VFILL

*++ Type corners of editing box
       LI   R2,4                ; count
       LI   R3,CNRDAT           ; address of corner data
       MOV  @$VDPM(U),R0        ; 80 column?
       JNE  CORNER              ; jump if not
       AI   R3,8                ; else point to 80 column data
CORNER LI   R1,>C900            ; ASCII[201] in msb
CRNLP  MOV  *R3+,R0             ; get address
       A    @$SSTRT(U),R0       ; add SCRN_START
       BLWP @VSBW               ; write to screen
       AI   R1,>0100            ; increment ascii character
       DEC  R2                  ; decrement counter
       JNE  CRNLP               ; loop if not finished
       
*++ Type right side of 40-column editing box
       MOV  @$VDPM(U),R0        ; 80-column mode?
       JEQ  LIST03              ; jump if so
       LI   R0,37               ; load x
       LI   R6,3                ; load y
       LI   R1,>D000            ; ASCII[208]
       LI   R2,16               ; load count
       BL   @__VCHR             ; type vertical line
       JMP  LIST04
LIST03 

*++ Type right side of 80-column editing box
       LI   R0,67               ; load x for right side
       LI   R6,3                ; load y
       LI   R1,>CF00            ; ASCII[207]
       LI   R2,16               ; load count
       BL   @__VCHR             ; type vertical line

*++ Type remainder of top row of tens digits for 80-column editing box
       LI   R2,33               ; load x
       LI   R3,1                ; load y
       BL   @GXY                ; place text cursor
       LI   R4,TR10S            ; text to type
       BL   @MSGTYP             ; type it

*++ Type remainder of top of 80-column editing box
       LI   R2,33               ; load x
       LI   R3,2                ; load y
       BL   @GXY                ; place text cursor
       LI   R4,TLLIN            ; text to type
       BL   @MSGTYP             ; type it
LIST04 

*++ Store 0 in S_H
       CLR  @S_H
       
*++ Type current block
       CLR  @LNSTRT             ; starting line = 0
       LI   R0,16               ; 16 lines to display
       MOV  R0,@LNCNT           ; copy for LINE.
       BL   @LINE.              ; display the block

*++ Clean up and return
       MOV  *R+,@$BASE(U)       ; pop old radix and restore it
       MOV  *R+,LINK            ; pop return address
       RT                       ;   and return

B#TXT  BYTE 8
       TEXT " BLOCK #"
LN#S   TEXT "0123456789012345"
TL10S  BYTE 31
       TEXT "0         1         2         3"
TR10S  BYTE 31
       TEXT "3         4         5         6"
TLLIN  BYTE 34
       BYTE >30,205,205,205,205,206,205,205,205,205,>30
       BYTE 205,205,205,205,206,205,205,205,205,>30,205
       BYTE 205,205,205,206,205,205,205,205,>30,205,205,205
       EVEN
*++ location data for corner chars - 40 column mode
CNRDAT DATA 2*40+2              ; top left
       DATA 2*40+37             ; top right
       DATA 19*40+2             ; bottom left
       DATA 19*40+37            ; bottom right

*++ location data for corner chars - 80 column mode
       DATA 2*80+2              ; top left
       DATA 2*80+67             ; top right
       DATA 19*80+2             ; bottom left
       DATA 19*80+67            ; bottom right

*++ patterns for editing box
BDCHRS DATA >0000,>003C,>3C30,>3030       ; ASCII[201], top left corner
       DATA >0000,>00F0,>F030,>3030       ; ASCII[202], top right corner
       DATA >3030,>303C,>3C00,>0000       ; ASCII[203], bottom left corner
       DATA >3030,>30F0,>F000,>0000       ; ASCII[204], bottom right corner
       DATA >0000,>00FC,>FC00,>0000       ; ASCII[205], horizontal line segment
       DATA >0000,>00FC,>FC30,>3030       ; ASCII[206], hashed horizontal line segment
       DATA >3030,>3030,>3030,>3030       ; ASCII[207], vertical line segment
       DATA >3060,>C070,>380C,>1830       ; ASCII[208], broken vertical line segment
;]* 
;[*++ LISTR ++*     Display right half of 40-column editor screen
LISTR  
       DECT R                   ; make room on return stack to
       MOV  LINK,*R             ;   save return

*++ Type left side of 40-column editing box
       LIMI 0
       LI   R0,2                ; load x
       LI   R6,3                ; load y
       LI   R1,>D000            ; ASCII[208]
       LI   R2,16               ; load count
       BL   @__VCHR             ; type vertical line

*++ Type right side of editing box
       LI   R0,37               ; load x for right side
       LI   R6,3                ; load y
       LI   R1,>CF00            ; ASCII[207]
       LI   R2,16               ; load count
       BL   @__VCHR             ; type vertical line

*++ Type top row of tens digits
       LI   R2,3                ; load x
       LI   R3,1                ; load y
       MOV  R3,@S_H             ; Store 1 in S_H  (interleave to get a free 1)
       BL   @GXY                ; place text cursor
       LI   R4,TR10S            ; text to type
       BL   @MSGTYP             ; type it
       
*++ Type current block
       CLR  @LNSTRT             ; starting line = 0
       LI   R0,16               ; 16 lines to display
       MOV  R0,@LNCNT           ; copy for LINE.
       BL   @LINE.              ; display the block

       MOV  *R+,LINK            ; pop return address
       RT                       ;   and return
;]* 
;[*++ PTR ++*       Get address of block cursor to BLKPOS
PTR    MOV  @EDBLK,@BLKPOS      ; load beginning block address
       A    @CUR,@BLKPOS        ; calculate address of cursor position
       RT
;]* 
;[*++ R_C ++*       Get cursor's row and column to LIN# and COL#, respectively
*++ Called with:
*++     BLWP @R_C
*++
*++ Returns:
*++     LIN#: Forth block line#
*++     COL#: Forth block column
 
R_C    DATA UTILWS,R_CENT
R_CENT MOV  @CUR,R0         ; get block cursor position
       MOV  R0,R1           ; copy for column calc
       SRL  R0,6            ; divide by 64 to get line#
       MOV  R0,@LIN#        ; store it
       MOV  R0,R2           ; temp copy
       SLA  R2,6            ; calc col 0 of line#
       S    R2,R1           ; calc column
       MOV  R1,@COL#        ; store it
       RTWP
;]* 
;[*++ CMV ++*       Copies from low RAM to high RAM or vice versa 
*++ Pass data in data statements after call as follows:
*++     BL   @CMV
*++
*++     Register usage for input:
*++         R0: src
*++         R1: dst
*++         R2: count
*++         R3: inc|dec
*++

CMV    MOVB *R0,*R1         ; copy src to dst
       A    R3,R0           ; add inc|dec to src
       A    R3,R1           ; add inc|dec to dst
       DEC  R2              ; done?
       JNE  CMV             ; copy another if not
       RT
;]*
;[*++ BLPAD ++*     Copies 66 blanks to PAD
BLPAD  DECT R                   ; make room on return stack to
       MOV  LINK,*R             ;   save return
       
       MOV  @EDPAD,R0       ; PAD address to R0
       LI   R1,66           ; initialize counter
BLPAD1 MOVB @CONBL,*R0+     ; write blank to next PAD location
       DEC  R1              ; decrement counter
       JNE  BLPAD1          ; jump if not done (=0)

       MOV  *R+,LINK            ; pop return address
       RT                       ;   and return
;]*
;[*++ ERASHF ++*    Erase from cursor to EOL
ERASHF 
*++ 66 blanks to PAD
       BL   @BLPAD

*++ copy remainder of line (from cursor to end of line) to PAD
       BL   @PTR            ; get block cursor address
       MOV  @BLKPOS,R0      ; copy it to R0
       MOV  @EDPAD,R1       ; PAD address to R1
       BLWP @R_C            ; get cursor line# and column#
       LI   R2,64           ; initialize counter
       S    @COL#,R2        ; correct count
       LI   R3,1            ; we're copying from low RAM to high RAM
       BL   @CMV            ; do the copy
       
*++ Blank remainder of line
*++ ...BLKPOS and COL# should be correct from above
       BL   @BLNKS          ; blank the source of the copy
       
*++ Re-display line, set dirty bit and return to editor loop
       B    @RELINE         ; re-display line etc.
;]*     
;[*++ .CUR ++*      Update CURPOS from CUR, with 40-column left|right display changes
*++ DCUR updates CURPOS from CUR, with possible 40-column left|right display changes.

DCUR   DECT R                   ; make room on return stack to
       MOV  LINK,*R             ;   save return
DCUR04 BLWP @R_C                ; get LIN# and COL# of Forth block cursor
       MOV  @COL#,R2            ; block column to R2 for GXY
       AI   R2,3                ; calc screen column
       MOV  @LIN#,R3            ; block line# to R3 for GXY
       AI   R3,3                ; calc screen row
       MOV  @$VDPM(U),R0        ; 80-column mode?
       JEQ  DCUR01              ; jump if so
                            
*++ 40-column adjustments
       MOV  @S_H,R0             ; left half of block?
       JEQ  DCUR02              ; jump if so

*++ right-half adjustments
       CI   R2,32               ; [COL#+3]>32?
       JGT  DCUR03              ; jump if so
       DECT SP                  ; reserve stack space for block#
       MOV  @$SCR(U),*SP        ; push block# to stack
       BL   @LISTL              ; show left half of block
       JMP  DCUR04              ; re-update CURPOS
DCUR03 AI   R2,-30              ; move screen cursor left 30
       JMP  DCUR01              ; update CURPOS

*++ left-half adjustments
DCUR02 CI   R2,37               ; [COL#+3]<37?
       JLT  DCUR01              ; jump if so
       BL   @LISTR              ; display right half of block
       JMP  DCUR04              ; re-update CURPOS

*++ Adjust CURPOS
DCUR01 BL   @GXY                ; update CURPOS
       MOV  *R+,LINK            ; pop return address
       RT                       ;   and return
;]* 
;[*++ DELLIN ++*    Delete current line
DELLIN 
*++ Copy line to be deleted to PAD
       BLWP @R_C            ; get line# and column# of block cursor
       DECT SP              ; reserve stack space
       MOV  @COL#,*SP       ; push column# to stack
       NEG  *SP             ; negate it
       BL   @PLCUR          ; update block cursor to beginning of line
       BL   @PTR            ; get address of block cursor
       MOV  @BLKPOS,R0      ; copy to R0 for CMV src
       MOV  @EDPAD,R1       ; copy PAD address to R1 for CMV dst
       LI   R2,64           ; 64 to R2 for cnt for CMV
       LI   R3,1            ; low RAM to high RAM for CMV
       BL   @CMV            ; copy the line to PAD
       
*++ move rest of block up 1 line
       MOV  @BLKPOS,R0      ; initialize src to beginning of copied line
       MOV  R0,R1           ; make it dst
       AI   R0,64           ; correct src to beginning of next line
       LI   R2,960          ; initialize cnt to copy 15 lines (1024-64)
       S    @CUR,R2         ; correct it to current cursor location
       BL   @CMV            ; do the copy (R3 still set to 1)
       
*++ blank last line
       MOV  @EDBLK,R0       ; get address of current block
       AI   R0,960          ; correct to beginning of last line
       LI   R1,64           ; load counter
DELLN1 MOVB @CONBL,*R0+     ; write a blank to next location
       DEC  R1              ; done?
       JNE  DELLN1          ; jump if not
       
*++ cursor should still be pointing to beginning of original line

*++ Redraw display with line changes, mark block as dirty and return to editor loop
       B    @REDRAW
;]* 
;[*++ BLNKLN ++*    Pastes (inserts) a 64-char blank line to start of current block line#
BLNKLN
*++ Copy lines down 1 line in prep for blanking or pasting to current line
       BL   @INSLIN         ; make room for pasting

*++ Blank current line#
*++ ...BLKPOS and COL# should be correct from INSLIN
       BL   @BLNKS
       
*++ Redraw display with line changes, mark block as dirty and return to editor loop
       B    @REDRAW
;]*
;[*++ PSTELN ++*    Pastes (inserts) a 64-char line from PAD to start of current block line#
PSTELN
*++ Copy lines down 1 line in prep for blanking or pasting to current line
       BL   @INSLIN         ; make room for pasting

*++ Copy line to be pasted from PAD to beginning of current line#
*++ ...BLKPOS should be correct from INSLIN
       MOV  @EDPAD,R0       ; copy PAD address to R0 for CMV src
       MOV  @BLKPOS,R1      ; copy to R1 for CMV dst
       LI   R2,64           ; 64 to R2 for cnt for CMV
       LI   R3,1            ; low RAM to high RAM for CMV
       BL   @CMV            ; copy the line to PAD
       
*++ Redraw display with line changes, mark block as dirty and return to editor loop
       B    @REDRAW
;]*
;[*++ INSLIN ++*    Insert line, preparing for pasting or blanking
INSLIN 
       DECT R               ; make room on return stack to
       MOV  LINK,*R         ;   save return

*++ Get block cursor to start of current line and get its address
       BLWP @R_C            ; get line# and column# of block cursor
       DECT SP              ; reserve stack space
       MOV  @COL#,*SP       ; push column# to stack
       NEG  *SP             ; negate it
       BL   @PLCUR          ; update block cursor to beginning of line
       BL   @PTR            ; get address of block cursor for caller
       
*++ Copy lines down 1 line in prep for blanking or pasting to current line
       MOV  @EDBLK,R0       ; initialize src for CMV
       AI   R0,959          ; correct to end of 1 line before end of block
       MOV  R0,R1           ; initalize dst for CMV
       AI   R1,64           ; correct to end of block
       LI   R2,960          ; initialize cnt for CMV
       S    @CUR,R2         ; correct cnt
       SETO R3              ; set R3 to -1 for copy from high RAM to low RAM for CMV
       BL   @CMV            ; do the copy

       MOV  *R+,LINK        ; pop return address
       RT                   ;   and return
;]* 
;[*++ RELINE ++*    Redraw current line, updating CURPOS and marking block dirty
RELINE LI   R2,13           ; set up to emit CR
       LIMI 0
       BL   @EMT            ; emit CR to put display cursor at beginning of line
       
*++ Type current line of block
       MOV  @LIN#,@LNSTRT   ; starting line #       <--ensure LIN# is still correct!!!
       CLR  @LNCNT
       INC  @LNCNT          ; display 1 line
       BL   @LINE.          ; display the block

*++ Update CURPOS etc.
       BL   @DCUR
       
*++ Mark block as dirty and return to editor loop
       B    @DIRTY
;]* 
;[*++ +.CUR         ( n --- )       +++ Increment Forth block cursor by n and updates CURPOS
*++ PLDCUR increments Forth block cursor by n and updates CURPOS

PLDCUR DECT R                   ; make room on return stack to
       MOV  LINK,*R             ;   save return
       BL   @PLCUR              ; call +CUR
       BL   @DCUR               ; call .CUR
       MOV  *R+,LINK            ; pop return address
       RT                       ;   and return
;]* 
;[*++ TAB ++*       Tab forward one word
*++ These instruction codes need to be updated whenever changes
*++  are made to increase the distance between "X R2" and either
*++  of the jump targets, TABBL or TABNOB.
JEQTAB EQU  >1306           ; "JEQ TABBL" for X instruction
JNETAB EQU  >1609           ; "JNE TABNOB" for X instruction

TAB    MOV  @EDBLK,R0       ; get block address
       A    @CUR,R0         ; get cursor address
       LI   R4,1024         ; load counter with maximum
       S    @CUR,R4         ; adjust to cursor location
       LI   R2,JEQTAB       ; set jump address for "blank found"
TAB01  CB   *R0,@CONBL      ; is it a blank?
       X    R2              ; execute jump instruction
TAB02  DEC  R4              ; decrement counter
       JEQ  TABNOB          ; jump if we're done:
       INC  R0              ; increment block address
       INC  @CUR            ; increment block cursor
       JMP  TAB01           ; let's check another character
TABBL  LI   R2,JNETAB       ; set jump address for "blank not found"
       JMP  TAB02           ; continue

*++ we found the next word or reached the end of the block
TABNOB BL   @DCUR           ; update CURPOS etc.
       B    @VEDLP          ; return to editor loop
;]* 
;[*++ -TAB ++*      Tab backward one word
*++ These instruction codes need to be updated whenever changes
*++  are made to increase the distance between "X R2" and any
*++  of the jump targets, BTABL1, BTBNOB or BTABL2.
JEQBT1 EQU  >1306           ; "JEQ BTABL1" for X instruction
JNEBTB EQU  >1609           ; "JNE BTBNOB" for X instruction
JEQBT2 EQU  >130C           ; "JEQ BTABL2" for X instruction

BTAB   MOV  @EDBLK,R0       ; get block address
       A    @CUR,R0         ; get cursor address
       MOV  @CUR,R4         ; load cursor location
       INC  R4              ; correct counter
       LI   R2,JEQBT1       ; set jump address for "blank found" the 1st time
BTAB01 CB   *R0,@CONBL      ; is it a blank?
       X    R2              ; execute jump instruction
BTAB02 DEC  R4              ; decrement counter
       JEQ  TABNOB          ; if we're done, finish up in TAB routine
       DEC  R0              ; decrement block address
       DEC  @CUR            ; decrement block cursor
       JMP  BTAB01          ; let's check another character
BTABL1 LI   R2,JNEBTB       ; set jump address for "blank not found"
       JMP  BTAB02          ; continue

*++ we found a non-blank character
BTBNOB LI   R2,JEQBT2       ; set jump address for "blank found" the 2nd time
       JMP  BTAB02          ; continue

*++ we found the blank before the previous word or reached the beginning of the block
BTABL2 INC  @CUR            ; restore cursor to 1st char of word we just passed
       JMP  TABNOB          ; finish up in TAB routine
;]* 
;[*++ DIRTY ++*     Set current block's dirty bit
DIRTY
*++ set update bit for block
       MOV  @EDBLK,R2           ; load block address
       DECT R2                  ; adjust to block # location
       SOC  @CN8000,*R2         ; set update bit
       
*++ Because this is the last routine called by all callers,
*++ we will branch directly back to the editor loop.
       B    @VEDLP              ; return to editor loop
;]*
;[*++ ASCII ++*     Character processing routine for VED
ASCII  CLR  R1                  ; zero R1 preparing for byte copy
       MOVB @KYCHAR,R1          ; copy new char to HIGH BYTE OF R1
       CI   R1,>2000            ; not too low?
       JLT  ASCERR              ; jump if too low
       CI   R1,>7E00            ; not too high?
       JGT  ASCERR              ; jump if too high
       BL   @PTR                ; get block cursor address to BLKPOS
       MOV  @BLKPOS,R2          ; load block cursor position
       MOVB R1,*R2              ; copy new char to block at cursor position

*++ set up to emit new character
       CLR  R2                  ; clear R2 because xferring only 1 byte
       MOVB @KYCHAR,@F_R2LB     ; move char to LSB of R2 for EMT
       LIMI 0
       BL   @EMT                ; emit character

*++ increment cursors etc.
       DECT SP                  ; reserve space on stack
       LI   R0,1                ; load 1 for cursor advance
       MOV  R0,*SP              ; push 1 to stack
       BL   @PLDCUR             ; call +.CUR

*++ set update bit for block and return to editor loop
       B    @DIRTY

*++ set up for error tone
ASCERR LI   R2,7                ; load error tone for EMT
       LIMI 0
       BL   @EMT                ; emit error tone
ASCEXT B    @VEDLP              ; return to editor loop
;]*
;[*++ BLNKS ++*     Blank remainder of current line in block
*++ BLKPOS and COL# must be current!!
BLNKS  DECT R                   ; make room on return stack
       MOV  LINK,*R             ; save return

*++ Should not need these 2 statements because all callers have already set them
*        BL   @PTR                ; get address of block cursor
*        BLWP @R_C                ; get block line# and column#

       MOV  @BLKPOS,R0          ; copy block cursor to R0
       LI   R1,64               ; initialize counter
       S    @COL#,R1            ; calculate count
BLNKS1 MOVB @CONBL,*R0+         ; store blank in next location
       DEC  R1                  ; decrement counter
       JNE  BLNKS1              ; jump if not 0
       MOV  *R+,LINK            ; pop return address
       RT                       ;   and return to caller
;]* 
;[*++ FLIP ++*      Move cursor 30 chars left or right (to start in 80-column mode)
*++ Move cursor 30 columns left or right for 40-column editor or to beginning
*++ of block for 80-column editor

FLIP   MOV  @$VDPM(U),R0    ; TEXT80 mode?
       JNE  FLIP01          ; jump if not
       CLR  @CUR            ; zero block cursor
       BL   @DCUR           ; update cursors and display
       JMP  FLIPEX          ; we're outta here!
FLIP01 DECT SP              ; reserve stack space
       LI   R0,30           ; load cursor step
       MOV  @S_H,R1         ; left half of 40-column editor?
       JEQ  FLIP02          ; jump if so
       NEG  R0              ; right half, so go other direction
FLIP02 MOV  R0,*SP          ; push cursor step to stack
       BL   @PLDCUR         ; update cursors etc.
FLIPEX B    @VEDLP          ; return to editor loop
;]* 
;[*++ REDRAW ++*    Redraw screen, updating CURPOS and marking block dirty
REDRAW MOV  @S_H,R0             ; test which half of screen (40-column editor)
       JEQ  REDRW1              ; jump if left half
       BL   @LISTR              ; show right half of block
       JMP  REDRW2
REDRW1 DECT SP                  ; reserve stack space for block#
       MOV  @$SCR(U),*SP        ; push block# to stack
       BL   @LISTL              ; show left half of block

*++ Update CURPOS etc.
REDRW2 BL   @DCUR
       
*++ Mark block as dirty and return to editor loop
       B    @DIRTY
;]* 
;[*++ DEL ++*       Delete character under cursor
*++ Delete one character and close up line

DEL    BL   @PTR            ; get cur_addr
       MOV  @BLKPOS,R1      ; dst to R1 for CMV
       MOV  R1,R0           ; initialize src for CMV
       INC  R0              ; correct src
       LI   R2,64           ; initialize cnt for CMV
       BLWP @R_C            ; get block lin# and col#
       S    @COL#,R2        ; correct cnt
       MOV  R2,R4           ; save cnt for later
       LI   R3,1            ; set copy direction for CMV
       BL   @CMV            ; close the gap left by deleted char
       DEC  R4              ; correct R4 
       A    @BLKPOS,R4      ; get location of last char of line
       MOVB @CONBL,*R4      ; store blank in that location
       
*++ Re-display line, set dirty bit and return to editor loop
       B    @RELINE         ; re-display line etc.
;]* 
;[*++ INS ++*       Insert blank char at cursor
*++ Insert a blank and move rest of line 1 char right

INS    BL   @PTR            ; get cur_addr
       LI   R2,64           ; initialize cnt for CMV
       BLWP @R_C            ; get block lin# and col#
       S    @COL#,R2        ; correct cnt
       MOV  @BLKPOS,R1      ; initialize dst for CMV
       A    R2,R1           ; add # of chars
       DEC  R1              ; correct to last char of line
       MOV  R1,R0           ; initialize src for CMV
       DEC  R0              ; correct to penultimate char of line
       SETO R3              ; set copy direction for CMV (high RAM to low)
       BL   @CMV            ; allow room for the extra char
       MOV  @BLKPOS,R0      ; get insertion point
       MOVB @CONBL,*R0      ; store a blank there
       
*++ Re-display line, set dirty bit and return to editor loop
       B    @RELINE         ; re-display line etc.
;]* 
;[*++ PCHCUR ++*    Display cursor or char under cursor
*++ Character to be displayed at CURPOS must already be in MSB of R1

PCHCUR MOV  @CURPO$(U),R0       ; copy screen CURPOS to R0 for VSBW
       LIMI 0                   ; disable interrupts because VSBW doesn'y
       BLWP @VSBW               ; display cursor or character under cursor
       LIMI 2                   ; re-enable interrupts
       RT                       ; return to caller
;]* 
;[*++ RKEY ++*      Get next key and repeats
*++ RKEY is key acquisition/repetition and cursor blinking routine.
*++
*++ Register usage:

RKEY   DECT R                   ; make room on return stack to
       MOV  LINK,*R             ;   save return

RKEYLP LIMI 0                       ; disable interrupts because KSCAN doesn't
       BLWP @KSCAN                  ; scan the keyboard (interrupts will be enabled at return)
       INC  @BLINK                  ; increment blink logger
       LI   R3,180                  ; load 180
       C    @BLINK,R3               ; has it been 180 clicks?
       JLT  RKEY01                  ; jump if not
       MOVB @CURCH,R1               ; copy character under cursor
       JMP  RKEY02                  ; restore it
RKEY01 LI   R1,>1E00                ; load cursor character
RKEY02 BL   @PCHCUR                 ; display cursor or char under cursor
       SLA  R3,1                    ; load 360
       C    @BLINK,R3               ; has it been 360 clicks?
       JNE  RKEY03                  ; jump if not
       CLR  @BLINK                  ; clear blink logger
RKEY03 CB   @KYCHAR,@CONFF          ; no key?
       JEQ  RKEY05                  ; jump if so
       
*++ We have a key!
       SZCB @CN8000,@KYCHAR         ; force KYCHAR byte to ASCII
       MOV  @KC,R3                  ; save key counter for test
       INC  @KC                     ; increment key counter for wait
       CLR  @BLINK                  ; zero blink logger
       MOV  R3,R3                   ; waiting to repeat?
       JEQ  RKEYEX                  ; finish up and exit if not

*++ waiting to repeat
       C    @RLOG,@KC               ; long enough?
       JLT  RKEY04                  ; jump if so

*++ We may not have waited long enough yet.
       CB   @OKEY,@KYCHAR           ; same key?
       JEQ  RKEYLP                  ; wait some more if same key

*++ We're outta here!
       MOV  @CON01,@KC              ; load key counter with 1
       JMP  RKEYEX                  ; finish up

*++ we've waited long enough
RKEY04 MOV  @RL,@RLOG               ; load short wait time for repeat logger
       MOV  @CON01,@KC              ; load key counter with 1
       JMP  RKEYEX                  ; clean up and back to editor loop

*++ No key was pressed.
RKEY05 MOV  @RH,@RLOG               ; re-init RLOG
       CLR  @KC                     ; zero key counter
       JMP  RKEYLP                  ; scan keyboard again

*++ End of RKEY processing
RKEYEX MOVB @KYCHAR,@OKEY           ; current key to old key
       MOVB @CURCH,R1               ; character under cursor to R1
       BL   @PCHCUR                 ; restore it to display
       MOV  *R+,LINK                ; pop return address
       RT
;]*
;[*++ NXTBLK++*     Load next block
NXTBLK DECT SP
       CLR  *SP
       DECT SP
       MOV  @$SCR(U),*SP
       INC  *SP
       BL   @LISTL
       BL   @STCUR
       BL   @DCUR
       B    @VEDLP
;]* 
;[*++ PRVBLK ++*    Load previous block
PRVBLK DECT SP
       CLR  *SP
       DECT SP
       MOV  @$SCR(U),R0
       DEC  R0
       JGT  PRVBK1
       LI   R0,1
PRVBK1 MOV  R0,*SP
       BL   @LISTL
       BL   @STCUR
       BL   @DCUR
       B    @VEDLP
;]* 
;[*++ BCK ++*       Exit the 40/80-column editor 
BCK    CLR  R2                  ; set column = 0 for GXY
       CLR  R3                  ; set row = 0 for GXY
       BL   @GXY                ; update CURPOS
       LI   R0,>00F0            ; load PDT offset for cursor char, ASCII[30]
       A    @$PDT(U),R0         ; get its PDT address
       LI   R1,OLDCUR           ; load RAM source for old cursor
       LI   R2,8                ; load byte count
       LIMI 0                   ; disable interrupts because VMBW doesn't
       BLWP @VMBW               ; write the pattern
       BL   @CLS$               ; clear screen
       BL   @BLA2F              ; call into Forth for ABORT, we won't be back
       DATA ABORT
;]* 
;[*++ MOVCUR ++*    End of the next 5 routines
MOVCUR DECT SP          ; make room on stack
       MOV  R0,*SP      ; push new cursor offset to stack
       BL   @PLDCUR     ; update cursors etc.
       B    @VEDLP      ; back to editor loop
;]* 
;[*++ LFTCH ++*     Move left one character position
LFTCH  SETO R0
       JMP  MOVCUR
;]* 
;[*++ RTCH ++*      Move right one character position
RTCH   LI   R0,1
       JMP  MOVCUR
;]* 
;[*++ DOWNLN ++*    Move down to same character position on next line
DOWNLN LI   R0,64
       JMP  MOVCUR
;]* 
;[*++ UPLN ++*      Move up to same character position on previous line
UPLN   LI   R0,64
       NEG  R0
       JMP  MOVCUR
;]* 
;[*++ NXTLN ++*     Move down to first character position of next line
NXTLN  BLWP @R_C
       MOV  @LIN#,R0
       SLA  R0,6
       MOV  R0,@CUR
       JMP  DOWNLN
;]* 
;[*++ VEDCMD ++*    Jump table for VED
VEDCMD DATA ASCII,ERASHF,NXTBLK,DEL,INS,ASCII,PSTELN
       DATA DELLIN,LFTCH,RTCH,DOWNLN,UPLN,PRVBLK
       DATA NXTLN,FLIP,BCK,TAB,BLNKLN,BTAB
;]*
;[*** VED ***       ( block# blkpos --- ) +++ 40/80-column editor's main, endless loop
*        DATA BSAV_N
* VED__N DATA 3+TERMBT*LSHFT8+'V','ED'+TERMBT
* VED    DATA $+2
* VED_P  BL   @BLF2A
*        DATA _VED->6000+BANK1

_VED   
*++ Insure we're in TEXT or TEXT80 mode

*++ Initialize EDPAD (location of PAD while in editor)
       MOV  @$DP(U),R0
       AI   R0,68
       MOV  R0,@EDPAD

*++ 66 blanks to PAD
       BL   @BLPAD

*++ Load border characters
       LIMI 0
       LI   R0,201          ; load first border character code
       SLA  R0,3            ; * 8
       A    @$PDT(U),R0     ; get offset into PDT
       LI   R1,BDCHRS       ; load address of border character patterns
       LI   R2,64           ; load byte count
       BLWP @VMBW           ; write new patterns

*++ Save cursor pattern
       LI   R0,>00F0        ; cursor pattern offset into PDT (ASCII 30)
       A    @$PDT(U),R0     ; compute address in PDT
       LI   R1,OLDCUR       ; RAM storage
       LI   R2,8            ; 8 bytes
       BLWP @VMBR           ; save it for later

*++ Load our cursor
       LI   R1,BOX          ; RAM location of our cursor pattern
       BLWP @VMBW           ; store our cursor pattern (R0 & R2 still good)

*++ Clear screen
       BL   @CLS$           ; R0,R1,R2,R7 trashed; interrupts enabled at return

*++ Display navigation menu
       LI   R2,0                ; load x
       LI   R3,20               ; load y
       BL   @GXY                ; place text cursor
       LI   R4,NAVMNU           ; text to type
       BL   @MSGTYP             ; type NAVMNU

*++ Swap top 2 stack cells
       BL   @BLA2F
       DATA SWAP
       
*++ List left half of screen for TEXT or full screen for TEXT80
       BL   @LISTL

*++ Update cursors' positions
       BL   @STCUR
       BL   @DCUR

*++ Initialize variables
       CLR  @BLINK
       CLR  @OKEY
       CLR  @KC
       MOV  @RH,@RLOG       ; load long wait time for repeat logger

*++ Infinite editor loop---
*++ All keystroke processing routines, except BCK, will come back
*++ here for another keystroke.
VEDLP  

*++ Get character under cursor
       MOV  @CURPO$(U),R0
       LIMI 0
       BLWP @VSBR
       MOVB R1,@CURCH
       LIMI 2                           ???

*++ Get a key       
       BL   @RKEY
       
*++ Process keystroke
       CLR  R2                  ; clear keystroke register
       MOVB @KYCHAR,@F_R2LB     ; load keystroke in LSB of R2
       CI   R2,>16              ; is it TAB?
       JNE  VED01               ; jump if not
       LI   R2,>10              ; load jump table offset for TAB
VED01  CI   R2,>1E              ; is it BLNKLN?
       JNE  VED02               ; jump if not
       LI   R2,>11              ; load jump table offset for BLNKLN
VED02  CI   R2,>7F              ; is it -TAB?
       JNE  VED03               ; jump if not
       LI   R2,>12              ; load jump table offset for -TAB
       JMP  VED04               ; process jump table
VED03  CI   R2,>13              ; compare to highest jump table offset + 1
       JLT  VED04               ; jump if less
       CLR  R2                  ; set jump table offset for ASCII
VED04  SLA  R2,1                ; correct jump table offset
       AI   R2,VEDCMD           ; get jump table target
       MOV  *R2,R2              ; copy address from table
       B    *R2                 ; branch to proper keystroke routine
;]* 
;[*** WHERE ***     ( offset block# --- ) +++ Enter editor at point of load error
*        DATA VED__N
* WHER_N DATA 5+TERMBT*LSHFT8+'W','HE','RE'+TERMBT
* WHERE  DATA DOCOL
* WHERP  DATA SWAP,TWOM,VED,SEMIS
;]* 
;[*** EDIT ***      ( block# --- )        +++ Entry into 40/80-column editor
*        DATA WHER_N
* EDIT_N DATA 4+TERMBT*LSHFT8+'E','DI','T '+TERMBT
* EDIT   DATA DOCOL
* EDITP  DATA ZERO,VED,SEMIS
;]* 
;[*** ED@ ***       ( --- )               +++ Re-edit last block edited
*        DATA EDIT_N
* EDAT_N DATA 3+TERMBT*LSHFT8+'E','D@'+TERMBT
* EDAT   DATA DOCOL
* EDATP  DATA SCR,AT,EDIT,SEMIS
;]* 

 

 

 

...lee

  • Like 2
Link to comment
Share on other sites

For the File I/O words, unless there are justifiable objections, I think I'm going to remove the high-level Forth references to the following words because they are only used by other File I/O words. I will explain this in the manual for fbForth 2.0 and also explain how one might define the words if they are really needed:

 

What follows is an excerpt from Chapter 8 of the fbForth 1.0 Manual—
The words that follow are available for the advanced user and their utility can be worked out by
examining their definitions in block 47ff in FBLOCKS. They are lower-level words that are used
in the definitions of the above file I/O words.
GET-FLAG ( --- b )
retrieves to the stack the flag/status byte b from byte 1 the current PAB. The high-order
3 bits are used for DSR error return, except for “bad device name”. With the “bad device
name” error, this error return will be 0; but, the GPL status byte (837Ch) will have the
COND bit set (20h). The low-order 5 bits are set by routines that set the file type prior to
calling OPN , which reads these bits. See table below for the meaning of each bit of the
flag/status byte:
Flag/Status Byte of PAB (Byte 1)
Bits Contents Meaning
-------------------------------------------------------
0‒2 Error Code 0 = no error. Error codes are decoded in table below.
3 Record Type 0 = fixed-length records; 1 = variable-length records.
4 Data Type 0 = DISPLAY; 1 = INTERNAL.
5‒6 Mode of Operation 0 = UPDATE; 1 = OUTPUT; 2 = INPUT; 3 = APPEND.
7 File Type 0 = sequential file; 1 = relative file.
Error Codes in Bits 0‒2 of Flag/Status Byte of PAB
Error
Code Meaning
--------------------------------------------------------
0 No error unless bit 2 of status byte at address 837Ch is set ( then, bad device name).
1 Device is write protected.
2 Bad OPEN attribute such as incorrect file type, incorrect record length, incorrect I/O mode or no records in a relative record file.
3 Illegal operation; i.e., an operation not supported on the peripheral or a conflict with the OPEN attributes.
4 Out of table or buffer space on the device.
5 Attempt to read past the end of file. When this error occurs, the file is closed. Also given for non-extant records in a relative record file.
6 Device error. Covers all hard device errors such as parity and bad medium errors.
7 File error such as program/data file mismatch, non-existing file opened in INPUT mode, etc.
PUT-FLAG ( b --- )
writes the flag/status byte b on the stack to the current PAB to clear the error bits and set
the file type prior to calling OPN . See table after GET-FLAG for the meaning of each bit.
CLR-STAT ( --- )
clears the error code in bits 0‒2 of the flag/status byte of the current PAB.
CHK-STAT ( --- )
checks the error code in bits 0‒2 of the flag/status byte of the current PAB. If it is not 0,
an appropriate error message is printed.
I/OMD ( --- b )
gets the flag/status byte b of the current PAB, clears the I/O mode bits (5 & 6) and leaves
it on the stack in preparation for setting the I/O mode with an I/O word.
CHAR-CNT! ( n --- )
stores the character count n in the current PAB prior to a write operation. CHAR-CNT! is
used by WRT .
CHAR-CNT@ ( --- n )
retrieves the character count n from the current PAB of the last read operation. It is used
by RD .
N-LEN! ( b --- )
stores in the current PAB the length byte b of the file descriptor associated with the
current PAB. For “DSK1.MYFILE”, this would be 11.
DOI/O ( n --- )
executes the DSRLNK word with the I/O opcode n on the stack. The current PAB must be
updated with the information required by opcode n before executing DOI/O . See Section
18.2.1 of the Editor/Assembler Manual for details or consult the definitions in block 47ff
in FBLOCKS of the I/O words, OPN , CLSE , RD , WRT , RSTR , LD , SV , DLT and STAT ,
all of which use this low-level word in their definitions.

...lee
Link to comment
Share on other sites

The File I/O words are done! :-o After a day or two of testing, I will post beta 7.

 

I had to move things around a little. The space left in banks 0 – 2 is 1524, 162 and 406 bytes, respectively. I obviously have breathing room in bank 0, but I can move a little from bank 1 if bank 1 gets too tight. I need to be careful, however, because I am about to attempt the Floating Point Library, which will consume all of bank 3 and cause some spillover into other banks to get all of the high-level definitions done.

 

Not long, now—I hope! :grin:

 

...lee

Link to comment
Share on other sites

I think I will post beta 7 sometime tomorrow. The file I/O words are mostly tested and working. I had run into a couple of snags that were driving me nuts until I realized I had forgotten the CFA names for the likes of VMBR and its siblings. I was using VMBR (the ALC low-level routine's name) instead of _VMBR in the high-level Forth ALC! :mad:

 

I also ran into a brick wall with DLT (delete file) in Classic99 until I checked it with MESS, where it worked. :-o I could not find anywhere in the docs for Classic99 (I'm one mod behind) that deleting a file in FIAD mode is not supported, but that may well be the case. I will update to the latest mod of Classic99 to be sure.

 

Anyway—soon...

 

...lee

Link to comment
Share on other sites

I will post beta 7 later today. I need to update FBLOCKS to remove the "File I/O Library" option. FBLOCKS is getting smaller and smaller! I need to bulk it up with more stuff. Certainly, one such application will be a font editor. But, first—I have to go into town to get my new glasses.

 

...lee

Link to comment
Share on other sites

Post#1 has the beta 7 files I just posted. Please test and apprise me of any problems you find.

 

And now—on to the Floating Point Math Library!

 

Regarding running fbForth 2.0 beta 7 on real iron, @Astharot is trying it, but has run into difficulty. We're trying to work it out; but, I have not yet created a cartridge to test. I will be doing that later this month. However, if anyone else has tried it or is about to, I would very much like to hear how you fare(d).

 

...lee

Link to comment
Share on other sites

Uh-oh! Now what? I just got this message from Asm994a:

 

!!! Fatal Error: Out of symbol table space :_(

 

I'm not sure what to do now. One thing that comes to mind is to take advantage of the fact that only one label in the Floating Point Library (FPL) is needed by the rest of the fbForth code. That would allow me to assemble the FPL separately with an AORG directive and patch it into the last bank. However, I still may be in trouble with the additional FP words I need to define; but, I guess it's worth a try.

 

Any other ideas?

 

...lee

Link to comment
Share on other sites

Wow! Never seen that error before. I wonder if we could get hold of Cory Burr and ask him to modify the assembler?

 

Either that, or I need to dust off the Java TMS99xx assembler that I started but never finished!

 

Are there any labels that you can remove from your source code? There probably are, if you look. For example, in TF, my dictionary entries look like this:

labelh data <pointer to previous dictionary header>
       data <word length and flags>
entryp data $+2 ; entry point for executable code

See that label "entryp" (entry-point) above? It's only needed *if* source code elsewhere needs to refer to that code. If it's not executed/called elsewhere in your code-base then you don't need a label for it. My convention in TF was to give them all labels, but it wasn't really necessary - though I never encountered that error message before!

 

How many labels do you have? Looking at the end of the LST file for TF (I have an old build on this machine) I have 1458 labels in the entire TF code-base. Of course fbForth is larger (32K Eprom?)? Any idea what the limit is?

 

Mark

Edited by Willsy
Link to comment
Share on other sites

Wow! Never seen that error before. I wonder if we could get hold of Cory Burr and ask him to modify the assembler?

 

Either that, or I need to dust off the Java TMS99xx assembler that I started but never finished!

 

Are there any labels that you can remove from your source code? There probably are, if you look. For example, in TF, my dictionary entries look like this:

labelh data <pointer to previous dictionary header>
       data <word length and flags>
entryp data $+2 ; entry point for executable code

See that label "entryp" (entry-point) above? It's only needed *if* source code elsewhere needs to refer to that code. If it's not executed/called elsewhere in your code-base then you don't need a label for it. My convention in TF was to give them all labels, but it wasn't really necessary - though I never encountered that error message before!

 

How many labels do you have? Looking at the end of the LST file for TF (I have an old build on this machine) I have 1458 labels in the entire TF code-base. Of course fbForth is larger (32K Eprom?)? Any idea what the limit is?

 

Mark

 

I have 2829 labels before I add the FPL. Asm994a chokes about halfway through the FPL. My guess is the max is somewhere near 3000 lab els, but I don't really know.

 

I have so many labels because of the splitting out of the dictionary headers. It will be very painful to try to strip out labels—though doable, of course!

 

...lee

Link to comment
Share on other sites

In the split-out Dictionary header list, there are two threaded lists, the name fields and the parameter field addresses (PFAs). I don't think I ever use the code field address (CFA) pointers. The CFA is always calculated from the the PFA by backing up 2 bytes. That's an excess of ~400 gratuitous labels!?! :-o That's also the approximate number of labels in the FPL ALC. I guess I need to bite the bullet and delete the CFA pointers to see if I get a successful assembly without them. :(

 

...lee

Link to comment
Share on other sites

OK—I removed the CFA pointers and re-assembled. There are now 2804 symbols and everything appears to be working. Now on to defining the Forth part of the Floating Point Library and testing the library! Hopefully, I'll have enough room in the symbol table, but I'm cutting it close! I may yet have to assemble the FPL separately.

 

...lee

  • Like 1
Link to comment
Share on other sites

I am now puzzling over how to handle floating point (FP) number output. I would like to maintain backward compatibility with TI Forth's (TIF) block-loaded library (fbForth 1.0's is the same code); but, I don't really like the format used in TIF.

 

TIF has 4 words for displaying an FP number, viz., F. , F.R , FF. and FF.R . Those that end in ' R ' right-justify the output in a specified field-width equal to or larger than the expected output of the number. The two that begin with ' F. ' are free-form output and the other two are fixed-format output that are specified by two numbers, the number of digits following the decimal point and the maximum number of digits to output. The reason for these specifications is that the Convert-Number-to-String (CNS) GPL routine used by TIF and fbF10 requires those specific inputs.

 

With fbForth 2.0, I will be using a different FP Library (FPL), which I modified (with permission) from the Geneve FPL to work with TurboForth 1.2 and now with fbForth 2.0 The new CNS routine is more versatile than the corresponding GPL routine. The fixed-format outputs are specified by three numbers, two that specify the number of digits (including the space for the sign, used or not) to the left of the decimal point (DP) and the number of digits (including the DP) to the right of the DP, and one that is a format specifier. The format specifier allows two different E-notation outputs in the fixed format; whereas, the only way with the GPL CNS routine to get E-notation is by specifying free-format output—and, you only get E-notation then when the number won't fit the specs otherwise.

 

I suppose I could define the four aforementioned words just as they are in TIF/fbF10 and define a fixed-format word for fbForth 2.0 that incorporates all of the new specs. The only problem I see with that is someone learning fbForth 2.0 without prior knowledge of TIF/fbForth10 being confused by the different number-of-digit specs for FF. and FF.R vis-à-vis those for the new CNS routine.

 

Thoughts?

 

...lee

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...