Click here to go to a German text Version
[ thanks to Geoff Wearmouth ]
; Disassembly of the file "C:\ACE\JupiterAce.rom"
;
; CPU Type: Z80
;
; Created with dZ80 1.50
;
; on Monday, 21 of January 2002 at 07:11 PM
;
; last updated 02-NOV-2002
;
; Cross-assembles to an 8K ROM file.
;
; Note. A Low-level Assembly Listing only.
#define DEFB .BYTE
#define DEFW .WORD
#define DEFM .TEXT
#define EQU .EQU
#define ORG .ORG
ORG $0000
; -------------------
; THE 'START' RESTART
; -------------------
L0000: DI ; disable interrupts.
LD HL,$3C00 ; start of 'User' RAM
LD A,$FC ; a test byte and 1K masking byte.
JR L0028 ; forward to continue at Part 2.
; -------------------
; THE 'PRINT' RESTART
; -------------------
L0008: EXX ; preserve main registers.
BIT 3,(IX+$3E) ; test FLAGS for print destination.
JP L03EE ; forward to
; ---------------------------
; THE 'STACK WORD DE' RESTART
; ---------------------------
L0010: LD HL,($3C3B) ; SPARE
LD (HL),E
INC HL
JP L085F ;
; -------------------------
; THE 'POP WORD DE' RESTART
; -------------------------
L0018: LD HL,($3C3B) ; SPARE
DEC HL
LD D,(HL)
JP L0859 ;
; -------------------
; THE 'ERROR' RESTART
; -------------------
L0020: POP HL
LD A,(HL)
LD ($3C3D),A ; ERR_NO
JP L00AD ;
; ------------------------------------
; THE 'INITIALIZATION ROUTINE' Part 2.
; ------------------------------------
L0028: INC H ; increase high byte
LD (HL),A ; insert A value
CP (HL) ; compare to expected
JR Z,L0028 ; loop back while RAM is populated.
AND H ; limit to nearest 1K segment.
LD H,A ; place back in H.
LD ($3C18),HL ; set system variable RAMTOP.
LD SP,HL ; initialize the stack pointer.
; the Z80 instructions CALL, PUSH and POP can now be used.
LD HL,L010D ; prepare to copy the system variables
; initial state from ROM.
JR L003B ; skip past the fixed-position restart.
; -----------------------
; THE 'INTERRUPT' RESTART
; -----------------------
L0038: JP L013A ; jump to somewhere more convenient.
;------------------------------------------------------------------------------
;
; MEMORY MAP
;
; $0000 +======================================================+
; | |
; | ROM 8K |
; | v $2300 |
; $2000 +======================================================+ - - - - - -
; | copy of $2400 |0|< cassette >|
; $2400 +-------------------------------------+-+--------------+
; | VIDEO MEMORY 768 bytes |0| PAD 254 bytes| 1K RAM
; $2800 +-------------------------------------+-+--------------+
; | copy of $2c00 ^ $2700 |
; $2C00 +------------------------------------------------------+
; | CHARACTER SET - Write-Only | 1K RAM
; $3000 +------------------------------------------------------+
; | copy of $3c00 |
; $3400 +------------------------------------------------------+
; | copy of $3c00 |
; $3800 +------------------------------------------------------+
; | copy of $3c00 |
; $3C00 +-------+----------------------------------------------+
; |SYSVARS| DICT {12} DATA STACK -> <- RET STACK | 1K RAM
; $4000 +=======+==============================================+ - - - - - -
; | |
; 48K AVAILABLE FOR EXPANSION.
; | |
; $FFFF +======================================================+
;
; The Ace had an 8K ROM and was sold with 3K of RAM each byte of which had
; at least two addresses and sometimes four addresses so the mapping of the
; 3K of RAM was as above.
; The 768 bytes of video memory is accessed by the ROM using addresses
; $2400 - $26FF. This gives priority to the video circuitry which also needs
; this information to build the TV picture. The byte at $2700 is set to zero
; so that it is easy for the ROM to detect when it is at the end of the screen.
; The 254 bytes remaining are the PAD - the workspace used by FORTH.
; This same area is used by the tape recorder routines to assemble the tape
; header information but since, for accurate tape timing, the FORTH ROM needs
; priority over the video circuitry, then the ROM uses addresses $2301 - $23FF.
;
; Similarly the Character Set is written to by the ROM (and User) at the 1K
; section starting at $2C00. The video circuitry accesses this using addresses
; $2800 - $2BFF to build the TV picture. It is not possible for the ROM or User
; to read back the information from either address so this precludes the saving
; of character sets and writing a driver for a device like the ZX Printer.
;
; The final 1K or RAM has four addresses although it is normal to use addresses
; $3C00 - $3FFF. The first sixty three bytes are the System Variables which
; hold information like the number BASE and CONTEXT, and even the plotting
; coordinates should the user wish to develop a word like DRAW to draw lines.
;
; Then comes the User Dictionary, the first word of which is "FORTH" which links
; to the Dictionary in ROM. Next a gap of 12 bytes to allow for Data Stack
; underflow and then the Data Stack itself which grows upwards.
; At the opposite end of free memory is the Return Stack (machine stack) which
; grows downwards.
; ------------------------------------
; THE 'INITIALIZATION ROUTINE' Part 3.
; ------------------------------------
L003B: LD DE,$3C24 ; destination system variable L_HALF
LD BC,$002D ; number of bytes.
LDIR ; copy initial state from ROM to RAM.
LD IX,$3C00 ; set IX to index the system variables.
LD IY,L04C8 ; set IY to the SLOW return address.
L004B: CALL L0A24 ; routine CLS.
XOR A ; clear accumulator.
LD ($2700),A ; make location after screen zero.
; There are 128 bit-mapped 8x8 characters.
; Define the 8 Battenberg graphics ($10 to $17) from low byte of address.
; This routine also sets the other characters $00 to $0F and $18 to $1F
; to copies of this range. The inverse form of character $17 is used as the
; normal cursor - character $97.
L0052: LD HL,$2C00 ; point to the start of the 1K write-
; only Character Set RAM.
L0055: LD A,L ; set A to low byte of address
AND $BF ; AND %10111111
RRCA ; rotate
RRCA ; three times
RRCA ; to test bit 2
JR NC,L005F ; forward if not set.
RRCA ; else rotate
RRCA ; twice more.
L005F: RRCA ; set carry from bit (3) or (6)
LD B,A
SBC A,A ; $00 or $FF
RR B
LD B,A
SBC A,A
XOR B
AND $F0
XOR B
LD (HL),A ; insert the byte.
INC L ; increment low byte of address
JR NZ,L0055 ; loop back until the first 256 bytes
; have been filled with 32 repeating
; characters.
; Now copy the bit patterns at the end of this ROM to the last 768 bytes of
; the Character RAM, filling in some blank bytes omitted to save ROM space.
; This process starts at high memory and works downwards.
L006E: LD DE,$2FFF ; top of destination.
LD HL,L1FFB ; end of copyright character.
LD BC,$0008 ; 8 characters
LDDR ; copy the © character
EX DE,HL ; switch pointers.
LD A,$5F ; set character counter to ninety five.
; i.e. %0101 1111
; bit 5 shows which 32-character sector
; we are in.
; enter a loop for the remaining characters supplying zero bytes as required.
L007C: LD C,$07 ; set byte counter to seven.
BIT 5,A ; test bit 5 of the counter.
JR Z,L0085 ; forward if not in middle section
; which includes "[A-Z]"
LD (HL),B ; else insert a zero byte.
DEC HL ; decrement the destination address.
DEC C ; and the byte counter.
L0085: EX DE,HL ; switch pointers.
LDDR ; copy the 5 or 6 characters.
EX DE,HL ; switch pointers.
LD (HL),B ; always insert the blank top byte.
DEC HL ; decrement the address.
DEC A ; decrement the character counter.
JR NZ,L007C ; back for all 95 characters.
IM 1 ; Select Interrupt Mode 1
JR L009B ; and then jump into the code for the
; QUIT word.
; ---------------
; THE 'QUIT' WORD
; ---------------
; ( -- )
; Clears return stack, empties input buffer and returns control to the
; keyboard.
L0092: DEFM "QUI" ; 'name field'
DEFB 'T' + $80
L0096: DEFW $0000 ; 'link field' - end of linked list.
L0098: DEFB $04 ; 'name length field'
L0099: DEFW L009B ; 'code field'
; address of machine code for routine.
; ---
L009B: LD SP,($3C18) ; set stack-pointer to RAMTOP.
EI ; Enable Interrupts.
JP L04F2 ; jump forward to the main execution
; loop.
; ----------------
; THE 'ABORT' WORD
; ----------------
; Clears the data and return stacks, deletes any incomplete definition
; left in the dictionary, prints 'ERROR' and the byte from address $3C3D
; if the byte is non-negative, empties the input buffer, and returns
; control to the keyboard.
L00A3: DEFM "ABOR" ; 'name field'
DEFB 'T' + $80
DEFW L0098 ; 'link field' to previous word QUIT.
L00AA: DEFB $05 ; 'name length field'
L00AB: DEFW L00AD ; 'code field'
; ---
; -> also continuation of the error restart.
L00AD: PUSH IY ; preserve current IY value slow/fast.
LD IY,L04B9 ; set IY to FAST
; now empty the data stack
LD HL,($3C37) ; STKBOT
LD ($3C3B),HL ; SPARE
LD HL,$3C3E ; address FLAGS
LD A,(HL) ; fetch status from FLAGS.
AND $B3 ; AND %10110011
; reset bit 2 - show definition complete
; reset bit 3 - output to screen.
; reset bit 6 - show in interpreter mode
BIT 2,(HL) ; was there an incomplete definition ?
LD (HL),A ; update FLAGS
JR Z,L00DE ; forward if no incomplete word.
L00C4: CALL L04B9 ; do forth
DEFW L0490 ; dict address of sv DICT
DEFW L08B3 ; @ value of sv DICT (d).
DEFW L104B ; stk_data d. length field
DEFB $05 ; five d, 5.
DEFW L0DD2 ; + d+5. code field
DEFW L086B ; dup d+5, d+5.
DEFW L1610 ; prvcur d+5.
DEFW L15B5 ; namefield n.
DEFW L1011 ; stackwrd n.
DEFW $3C37 ; (stkbot) n, stkbot.
DEFW L08C1 ; ! .
DEFW L1A0E ; end-forth. .
; at this stage the system variable STKBOT holds the address of the
; obsolete name field and the system variable CURRENT points to the
; address of the previous complete word - obtained from the old link field.
L00DE: BIT 7,(IX+$3D) ; test ERR_NO for normal value 255.
JR NZ,L00FF ; set-min then main-loop if OK.
CALL L1808 ; else pr-inline
; ---
L00E7: DEFM "ERRO" ; the message "ERROR" with the last
DEFB 'R' + $80 ; character inverted.
; ---
L00EC: CALL L04B9 ; forth
DEFW L1011 ; stack next word
DEFW $3C3D ; -> system variable ERR_NO
DEFW L0896 ; C@ - fetch content byte
DEFW L09B3 ; . - print it
DEFW L0A95 ; CR
DEFW L1A0E ; end-forth.
LD (IX+$3D),$FF ; set ERR_NO to 'No Error'
L00FF: LD HL,($3C37) ; fetch STKBOT
LD BC,$000C ; allow twelve bytes for stack underflow
ADD HL,BC ; add the extra
LD ($3C3B),HL ; set SPARE
POP IY ; restore previous state of IY
JR L009B ; rejoin main loop
; -------------------------
; THE 'DEFAULT ENVIRONMENT'
; -------------------------
; This is the default environment that is copied from ROM to RAM as part of
; the initialization process. This also contains the FORTH word FORTH definition
L010D: DEFW $26E0 ; L_HALF
DEFB $00 ; KEYCOD
DEFB $00 ; KEYCNT copy the 32 bytes.
DEFB $00 ; STATIN
DEFW $0000 ; EXWRCH
DEFB $00 ; FRAMES
DEFB $00 ; FRAMES
DEFB $00 ; FRAMES
DEFB $00 ; FRAMES
DEFB $00 ; XCOORD
DEFB $00 ; YCOORD
DEFW $3C4C ; CURRENT
DEFW $3C4C ; CONTEXT
DEFW $3C4F ; VOCLNK
DEFW $3C51 ; STKBOT
DEFW $3C45 ; DICT
DEFW $3C5D ; SPARE
DEFB $FF ; ERR_NO
DEFB $00 ; FLAGS
DEFB $0A ; BASE
; FORTH
DEFM "FORT" ; The 'name field'
DEFB 'H' + $80 ; FORTH
DEFW $0000 ; length field - filled when next word
; is defined.
DEFW L1FFF ; link field copied to $3C49.
DEFB $05 ; name length field
DEFW L11B5 ; code field
DEFW $3C49 ; address of parameters
DEFB $00 ; VOCLNK [$3C4F]
DEFB $00 ; - link to next vocabulary.
DEFB $00 ; last byte to be copied. to [$3C51]
; -----------------------------------------------
; THE 'CONTINUATION OF THE Z80 INTERRUPT' ROUTINE
; -----------------------------------------------
; The destination of the jump at $0038.
; Begin by saving both accumulators and the 3 main registers.
L013A: PUSH AF ; preserve both accumulators
EX AF,AF' ;
PUSH AF ;
PUSH BC ; and main registers.
PUSH DE ;
PUSH HL ;
; Now wait for 62 * 12 clock cycles. ( To avoid flicker perhaps? ).
LD B,$3E ; delay counter.
L0142: DJNZ L0142 ; self loop for delay
; Increment the 4-byte frames counter for use as a system clock.
LD HL,$3C2B ; FRAMES1
L0147: INC (HL) ; increment timer.
INC HL ; next significant byte of four.
JR Z,L0147 ; loop back if the value wrapped back
; to zero.
; Note. as manual points out, there is no actual check on this and if
; you leave your Ace switched on for 2.75 years it will advance to the
; following system variables although it takes several millennia to advance
; through the screen coordinates.
; Now read the keyboard and if no new key then exit after restoring the
; preserved registers.
CALL L0310 ; routine KEYBOARD.
LD HL,$3C28 ; address system variable STATIN
BIT 0,(HL) ; new key?
JR Z,L0176 ; forward if not to RESTORE/EXIT
AND A ; zero key code ?
JR Z,L0176 ; forward if so to EXIT.
CP $20 ; compare to SPACE
JR C,L0170 ; forward if less as an Editing Key.
BIT 1,(HL) ; CAPS shift?
CALL NZ,L0807 ; routine TO_UPPER
BIT 2,(HL) ; GRAPHICS mode?
JR Z,L0167 ; skip forward if not
AND $9F ; convert to one of 8 mosaic characters
L0167: BIT 3,(HL) ; INVERSE mode?
JR Z,L016D ; forward if not.
OR $80 ; set bit 7 to make character inverse.
L016D: CALL L0196 ; routine pr_buffer
L0170: CALL L01E6 ; routine EDIT_KEY
CALL L0282 ; routine pr_cursor
; Before exiting restore the preserved registers.
L0176: POP HL ;
POP DE ;
POP BC ;
POP AF ;
EX AF,AF' ;
POP AF ;
EI ; Enable Interrupts
RET ; return.
; -----------------------------------
; THE 'PRINT to LOWER SCREEN' ROUTINE
; -----------------------------------
L017E: CP $0D ; carriage return?
JR NZ,L0196 ; forward if not
; a carriage return to input buffer i.e. lower screen memory.
LD HL,$2700 ; set pointer to location after the
; input buffer.
LD ($3C22),HL ; set ENDBUF - end of logical line
LD ($3C20),HL ; set the CURSOR
XOR A ; clear A
CALL L0198 ; print character zero.
LD HL,$26E0 ; left hand position of bottom line.
LD ($3C1E),HL ; set INSCRN to this position.
RET ; return.
; ---------------------------------------
; THE 'PRINT CHARACTER TO BUFFER' ROUTINE
; ---------------------------------------
L0196: AND A ; check for zero character
RET Z ; return if so.
; => also called from previous routine only to print a zero skipping above test.
L0198: EX AF,AF' ; preserve the output character.
LD HL,($3C22) ; fetch ENDBUF end of logical line
LD A,(HL) ; fetch character from position
AND A ; is it zero ?
JR Z,L01A6 ; skip forward if so.
; else lower screen scrolling is required.
LD DE,$D900 ; $0000 - $2700
ADD HL,DE ; test if position is within video RAM
JR NC,L01CE ; forward if < $26FF
; now check that the limit of 22 lines in lower screen is not exceeded.
L01A6: LD DE,($3C24) ; fetch start of buffer from L_HALF
LD HL,$DBA0 ; $0000 - $2460
ADD HL,DE ;
JR NC,L01E4 ; forward to exit if buffer full.
LD HL,($3C1C) ; fetch position SCRPOS for upper screen
LD BC,$0020 ; allow an extra 32 characters - 1 line.
ADD HL,BC ;
SBC HL,DE ; subtract the start of input buffer
PUSH DE ; and save the L_HALF value
CALL NC,L0421 ; routine to scroll upper display.
CALL L02B0 ; find zerobyte loc in HL
POP DE ; retrieve the L_HALF value
CALL L042F ; routine scroll and blank
; The four system variables INSCRN, CURSOR, ENDBUF and L_HALF are each
; reduced by 32 bytes a screen line.
LD HL,$3C1E ; address INSCRN the left-hand location
; of the current input line.
LD B,$04 ; four system variables to update
L01C9: CALL L0443 ; routine SCR-PTRS
DJNZ L01C9 ; repeat for all four pointers.
; ok to print
L01CE: CALL L0302 ; routine find characters to EOL.
LD D,H ; HL is end of line
LD E,L ; transfer to DE register.
INC HL ; increment
LD ($3C22),HL ; update ENDBUF
DEC HL ; decrement
DEC HL ; so HL = DE -1
JR Z,L01DD ; skip if BC zero.
LDDR ; else move the characters.
L01DD: EX AF,AF' ; restore the output character.
LD (DE),A ; insert at screen position.
; (a zero if CR lower)
INC DE ; next character position
LD ($3C20),DE ; update CURSOR
L01E4: XOR A ; ?
RET ; return.
; -------------------------
; THE 'EDIT KEY' SUBROUTINE
; -------------------------
L01E6: LD HL,L01F0 ; address the EDIT KEYS table.
LD D,$00 ; prepare to index by one byte.
LD E,A ; character code to E.
ADD HL,DE ; index into the table.
LD E,(HL) ; pick up required offset to the
; handling routine.
ADD HL,DE ; add to the current address.
JP (HL) ; exit via the routine.
; ---------------------
; THE 'EDIT KEYS' TABLE
; ---------------------
L01F0: DEFB $20 ; L0210 $00 - RET
L01F1: DEFB $13 ; L0204 $01 - LEFT
L01F2: DEFB $0C ; L01FE $02 - CAPS
L01F3: DEFB $1E ; L0211 $03 - RIGHT
L01F4: DEFB $0A ; L01FE $04 - GRAPH
L01F5: DEFB $37 ; L022C $05 - DEL
L01F6: DEFB $1A ; L0210 $06 - RET
L01F7: DEFB $50 ; L0247 $07 - UP
L01F8: DEFB $06 ; L01FE $08 - INV
L01F9: DEFB $9C ; L0295 $09 - DOWN
L01FA: DEFB $C9 ; L02C3 $0A - DEL LINE
L01FB: DEFB $15 ; L0210 $0B - RET
L01FC: DEFB $14 ; L0210 $0C - RET
L01FD: DEFB $D3 ; L02D0 $0D - KEY-ENTER
; -------------------------------
; THE 'TOGGLE STATUS BIT' ROUTINE
; -------------------------------
; The keycodes have been cleverly mapped to individual bits of the STATIN
; system variable so this simple routine maintains all three status bits.
; KEY '2' - CAPS SHIFT, '4' - GRAPHICS, '8' - INVERSE VIDEO.
L01FE: LD HL,$3C28 ; system variable STATIN
XOR (HL) ; toggle the single relevant bit.
LD (HL),A ; put back.
RET ; return.
; ----------------------------
; THE 'CURSOR LEFT' SUBROUTINE
; ----------------------------
; this subroutine moves the cursor to the left unless the character at that
; position is zero.
L0204: LD HL,($3C20) ; fetch CURSOR.
DEC HL ; decrement value.
LD A,(HL) ; fetch character at new position.
AND A ; test for zero. (cr)
RET Z ; return if so. >>
LD ($3C20),HL ; else update CURSOR
INC HL ; step back
LD (HL),A ; and put character that was at new
; cursor position where cursor is now.
L0210: RET ; return.
; Note. various unallocated keys in the EDIT KEYS table point to the
; above RET instruction.
; -----------------------------
; THE 'CURSOR RIGHT' SUBROUTINE
; -----------------------------
L0211: LD HL,($3C20) ; fetch CURSOR position
INC HL ; and increment it.
LD DE,($3C22) ; fetch ENDBUF - end of current line.
AND A ; prepare to subtract.
SBC HL,DE ; test
RET Z ; return if zero - CURSOR is at ENDBUF
ADD HL,DE ; else reform the pointers.
LD ($3C20),HL ; update CURSOR
LD A,(HL) ; fetch character at new position.
DEC HL ; decrement
LD (HL),A ; and insert where cursor was.
RET ; ret.
; ---------------------------
; THE 'DELETE CURSOR' ROUTINE
; ---------------------------
; Moves cursor position to right and then continues into DEL-CHAR
L0225: LD HL,($3C20) ; fetch CURSOR
INC HL ; increment position.
LD ($3C20),HL ; update CURSOR
; ------------------------------
; THE 'DELETE CHARACTER' ROUTINE
; ------------------------------
L022C: CALL L0302 ; routine finds characters to EOL.
LD H,D ; transfer CURSOR position DE to HL.
LD L,E ;
DEC DE ; decrement DE
LD A,(DE) ; fetch character to left of original
; cursor.
AND A ; test for zero.
RET Z ; return if so. >>
LD ($3C20),DE ; else update CURSOR
LD A,B ; check for count of characters
OR C ; being zero
JR Z,L023F ; skip if so.
L023D: LDIR ; else shift characters to left.
L023F: DEC HL ; decrement HL so that points to end -
; last position on the logical line.
LD (HL),$20 ; insert a space.
LD ($3C22),HL ; set ENDBUF
INC C ; reset zero flag??
RET ; return.
; -----------------------
; THE 'CURSOR UP' ROUTINE
; -----------------------
; When the cursor is moved up while editing a multi-line word definition,
; then the cursor is first moved to the left of the screen abutting the
; character zeros at the leftmost position.
; These zero characters appear as spaces but mark the beginning of each logical
; line. A logical line may, for instance if it contains a text item, extend over
; several physical screen lines.
L0247: CALL L0204 ; routine CURSOR-LEFT
JR Z,L0254 ; skip forward if not possible.
; else move left by thirty two positions. This may achieve a vertical move if
; attempted when a word is first being entered. Alternatively if one of the
; calls to cursor left fails having encountered a zero, then all subsequent
; calls will fail. The routine will return with the cursor adjacent to the zero.
LD B,$1F ; count 31 decimal
L024E: CALL L0204 ; move cursor left thirty one times.
DJNZ L024E ; makes thirty two moves counting first
RET ; return.
; ---
L0254: LD HL,($3C1E) ; fetch INSCRN start of current line.
LD DE,($3C24) ; fetch L_HALF start of buffer.
AND A ; reset carry for
SBC HL,DE ; true subtraction.
RET Z ; return if at beginning of input buffer
CALL L0225 ; routine DEL-CURSOR
LD HL,($3C1E) ; fetch INSCRN leftmost location of
; current line.
LD DE,$FFE0 ; make DE minus thirty two.
XOR A ; clear accumulator to zero.
L0269: ADD HL,DE ; subtract 32
CP (HL) ; compare contents to zero
; ( i.e. prev (cr) or buffer start?)
JR NZ,L0269 ; loop back until HL holds zero.
LD ($3C1E),HL ; update INSCRN
CALL L02F4 ; find endbuf
LD ($3C20),HL ; set CURSOR
; ----------
; PR_CURSOR
; ----------
L0276: LD A,$A0 ; inverse space - so solid square
CALL L017E ; routine PR_LOWER
LD HL,($3C20) ; CURSOR
DEC HL
LD ($3C20),HL ; CURSOR
; -> from interrupt
L0282: LD HL,($3C20) ; CURSOR
LD A,($3C28) ; STATIN
RRA ; ignore bit 0
LD (HL),$97 ; pixel cursor.
RRA ; test bit 1 - CAPS
JR NC,L0290 ; forward if no CAPS SHIFT
LD (HL),$C3 ; inverse [C] cursor.
L0290: RRA ; test bit 2 - GRAPHICS.
RET NC ; return if not
L0292: LD (HL),$C7 ; inverse [G] cursor.
RET ; return
; -------------------------
; THE 'CURSOR DOWN' ROUTINE
; -------------------------
L0295: CALL L0211 ; routine CURSOR RIGHT
JR Z,L02A2 ; forward if not possible.
LD B,$1F ; set counter to thirty one.
L029C: CALL L0211 ; routine CURSOR RIGHT
DJNZ L029C ; thirty two moves altogether.
RET ; return.
; ---
L02A2: CALL L02B0 ; find zerobyte
RET PO ; return if found
PUSH HL ; save position
CALL L0225 ; routine DEL-CURSOR
POP HL ; retrieve position.
CALL L02ED ; set logical line
JR L0276 ; back to exit via pr_cursor.
; ---
; find zerobyte
; ---
; -> called 5 times
L02B0: LD HL,$2700 ; this location is always zero.
; the byte following video RAM.
LD DE,($3C1E) ; INSCRN e.g. $26E0
AND A ; prepare for true subtraction
SBC HL,DE ; subtract to give number of chars
LD B,H ; transfer count to
LD C,L ; the BC register pair.
EX DE,HL ; transfer INSCR value to HL.
INC HL ; start next location
XOR A ; search for a zero character.
CPIR ; at most BC locations.
; sets P/O flag if BC!=0
DEC HL ; step back to last non-zero
RET ; return.
; -------------------------
; THE 'DELETE LINE' ROUTINE
; -------------------------
; CHR$ 10
L02C3: LD HL,($3C22) ; ENDBUF
DEC HL ;
LD ($3C20),HL ; CURSOR
L02CA: CALL L022C ; KEY-DEL
JR NZ,L02CA ; repeat
RET ; return.
; --------------------------
; THE 'KEY-ENTER' SUBROUTINE
; --------------------------
L02D0: LD HL,$3C28 ; STATIN
SET 5,(HL) ; signal new key.
RES 0,(HL) ; reset new key flag
RET ; return.
; ------------------------
; THE 'SET BUFFER' ROUTINE
; ------------------------
; called by LIST, QUERY
L02D8: LD HL,$2700 ; one past end of screen.
LD DE,($3C24) ; fetch start of buffer from L_HALF
CALL L07FA ; routine SPACE_FILL
LD HL,$26E0 ; first location of bottom line.
LD ($3C24),HL ; set L_HALF
LD (HL),$00 ; insert a ZERO.
; -> called by retype
L02EA: LD HL,($3C24) ; fetch L_HALF
; -> from cursor down
L02ED: LD ($3C1E),HL ; set INSCRN
INC HL ; step past the zero
LD ($3C20),HL ; set CURSOR
; => from cursor up.
L02F4: CALL L02B0 ; find zerobyte
LD A,$20 ; prepare a space
L02F9: DEC HL ; move to the left.
CP (HL) ; compare to space.
JR Z,L02F9 ; back while spaces exist.
INC HL ; point to last space encountered.
LD ($3C22),HL ; set ENDBUF - end of logical line.
RET ; return.
; ----------------------------------
; THE 'COUNT TO END OF LINE' ROUTINE
; ----------------------------------
; Find the number of characters to the end of the logical line.
L0302: LD HL,($3C22) ; system variable ENDBUF
LD DE,($3C20) ; system variable CURSOR
AND A ; prepare to subtract.
SBC HL,DE ; subtract to give character places
LD B,H ; transfer result
LD C,L ; to the BC register pair.
ADD HL,DE ; reform the pointers.
RET ; return with zero flag set if cursor
; at EOL.
; ----------------------
; THE 'KEYBOARD' ROUTINE
; ----------------------
L0310: CALL L0336 ; routine KEY_SCAN
LD B,A ; save key in B
LD HL,($3C26) ; load L with KEYCOD - last key pressed
; load H with KEYCNT - debounce counter
XOR L ; compare to previous key.
JR Z,L0325 ; forward if a match.
XOR L ; reform original
JR Z,L0320 ; forward if zero - no key.
XOR A ; else clear accumulator.
CP L ; compare with last.
RET NZ ; return if not zero.
L0320: LD L,B ; set L to original keycode
LD H,$20 ; set counter to thirty two.
JR L0332 ; forward to store values and exit
; returning zero.
; ---
; Key is same as previously accepted key.
; It repeats after two interrupts
L0325: DEC H ; decrement the counter.
LD A,H ; fetch counter to A.
CP $1E ; compare to thirty.
JR Z,L0331 ; forward if so to return key in A.
XOR A ; clear accumulator.
CP H ; is counter zero?
JR NZ,L0332 ; forward if not to keep counting.
LD H,$04 ; else set counter to four.
L0331: LD A,L ; pick up previous key.
L0332: LD ($3C26),HL ; update KEYCOD/KEYCNT
RET ; return.
;----------------------------------------------------------------------------
; LOGICAL VIEW OF KEYBOARD
;
; 0 1 2 3 4 -Bits- 4 3 2 1 0
; PORT PORT
;
; F7FE [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] | [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 0 ] EFFE
; ^ | v
; FBFE [ Q ] [ W ] [ E ] [ R ] [ T ] | [ Y ] [ U ] [ I ] [ O ] [ P ] DFFE
; ^ | v
; FDFE [ A ] [ S ] [ D ] [ F ] [ G ] | [ H ] [ J ] [ K ] [ L ] [ ENT ] BFFE
; ^ | v
; FEFE [SHI] [SYM] [ Z ] [ X ] [ C ] | [ V ] [ B ] [ N ] [ M ] [ SPC ] 7FFE
; ^ v ^ v
; Start +------------>--------------------->-------------+ End
;
;
;----------------------------------------------------------------------------
; ----------------------------------
; THE 'KEYBOARD SCANNING' SUBROUTINE
; ----------------------------------
; This routine is called by the KEYBOARD routine 50 times a second and
; by the ACE FORTH 'INKEY' WORD.
; The above diagram shows the logical view of the Keyboard and PORTS.
; The physical view is similar except that the symbol shift key is to the
; left of the space key.
L0336: LD BC,$FEFE ; port address - B is also an 8 counter
IN D,(C) ; read from port to D.
; when a key is pressed, the
; corresponding bit is reset.
LD E,D ; save in E
SRL D ; read the outer SHIFT key.
SBC A,A ; $00 if SHIFT else $FF.
AND $D8 ; $00 if SHIFT else $D8.
SRL D ; read the symbol shift bit
JR C,L0347 ; skip if not pressed.
LD A,$28 ; load A with 40 decimal.
L0347: ADD A,$57 ; gives $7F SYM, $57 SHIFT, or $2F
; Since 8 will be subtracted from the initial key value there are three
; distinct ranges 0 - 39, 40 - 79, 80 - 119.
LD L,A ; save key range value in L
LD A,E ; fetch the original port reading.
OR $03 ; cancel the two shift bits.
LD E,$FF ; set a flag to detect multiple keys.
; KEY_LINE the half-row loop.
L034F: CPL ; complement bits
AND $1F ; mask off the rightmost five key bits.
LD D,A ; save a copy in D.
JR Z,L0362 ; forward if no keys pressed to do the
; next row.
LD A,L ; else fetch the key value
INC E ; test E for $FF
JR NZ,L036B ; forward if not now zero to quit
L0359: SUB $08 ; subtract 8 from key value
SRL D ; test next bit affecting zero and carry
JR NC,L0359 ; loop back until the set bit is found.
LD E,A ; transfer key value to E.
JR NZ,L036B ; forward to abort if more than one key
; is pressed in the row.
L0362: DEC L ; decrement the key value for next row.
RLC B ; rotate the 8 counter and port address
JR NC,L036D ; skip forward when all 8 rows have
; been read.
IN A,(C) ; else read the next half-row.
JR L034F ; and back to KEY_LINE.
; ---
; ABORTKEY
L036B: LD E,$FF ; signal invalid key.
; the normal exit checks if E holds a key and not $FF.
L036D: LD A,E ; fetch possible key value.
INC A ; increment
RET Z ; return if was $FF as original.
LD HL,L0376 ; else address KEY TABLE
ADD HL,DE ; index into table.
; (D is zero)
LD A,(HL) ; pick up character.
RET ; return with translated character.
; ---------------
; THE 'KEY TABLE'
; ---------------
; -----------------------
; THE '40 UNSHIFTED KEYS'
; -----------------------
L0376: DEFB $76 ; V - v
DEFB $68 ; H - h
DEFB $79 ; Y - y
DEFB $36 ; 6 - 6
DEFB $35 ; 5 - 5
DEFB $74 ; T - t
DEFB $67 ; G - g
DEFB $63 ; C - c
DEFB $62 ; B - b
DEFB $6A ; J - j
DEFB $75 ; U - u
DEFB $37 ; 7 - 7
DEFB $34 ; 4 - 4
DEFB $72 ; R - r
DEFB $66 ; F - f
DEFB $78 ; X - x
DEFB $6E ; N - n
DEFB $6B ; K - k
DEFB $69 ; I - i
DEFB $38 ; 8 - 8
DEFB $33 ; 3 - 3
DEFB $65 ; E - e
DEFB $64 ; D - d
DEFB $7A ; Z - z
DEFB $6D ; M - m
DEFB $6C ; L - l
DEFB $6F ; O - o
DEFB $39 ; 9 - 9
DEFB $32 ; 2 - 2
DEFB $77 ; W - w
DEFB $73 ; S - s
DEFB $00 ; SYMBOL
DEFB $20 ; SPACE
DEFB $0D ; ENTER
DEFB $70 ; P - p
DEFB $30 ; 0 - 0
DEFB $31 ; 1 - 1
DEFB $71 ; Q - q
DEFB $61 ; A - a
DEFB $00 ; SHIFT
; ---------------------
; THE '40 SHIFTED KEYS'
; ---------------------
DEFB $56 ; V - V
DEFB $48 ; H - H
DEFB $59 ; Y - Y
DEFB $07 ; 6 - 7 KEY-UP
DEFB $01 ; 5 - 1 KEY-LEFT
DEFB $54 ;
DEFB $47
DEFB $43
DEFB $42
DEFB $4A
DEFB $55
DEFB $09 ; 7 - 9 KEY-DOWN
DEFB $08 ; 4 - 8 INV-VIDEO
DEFB $52
DEFB $46
DEFB $58
DEFB $4E
DEFB $4B
DEFB $49
DEFB $03 ; 8 - 3 KEY-RIGHT
DEFB $33 ; 3 - 3
DEFB $45
DEFB $44
DEFB $5A
DEFB $4D
DEFB $4C
DEFB $4F
DEFB $04 ; 9 - 4 GRAPH
DEFB $02 ; 2 - 2 CAPS LOCK
DEFB $57 ; W - W
DEFB $53 ; S - S
DEFB $00 ; SYMB
DEFB $20 ; SPACE
DEFB $0D ; ENTER
DEFB $50 ; P - P
DEFB $05 ; 0 - 5 DEL
DEFB $0A ; 1 - 0A DEL_LINE
DEFB $51 ; Q - Q
DEFB $41 ; A - A
DEFB $00 ; SHIFT
; --------------------------
; THE '40 SYMBOL SHIFT KEYS'
; --------------------------
DEFB $2F ; V - /
DEFB $5E ; H - ^
DEFB $5B ; Y - [
DEFB $26 ; 6 - &
DEFB $25 ; 5 - %
DEFB $3E ; T - >
DEFB $7D ;
DEFB $3F
DEFB $2A
DEFB $2D
DEFB $5D
DEFB $27
DEFB $24
DEFB $3C
DEFB $7B
DEFB $60
DEFB $2C
DEFB $2B
DEFB $7F
DEFB $28
DEFB $23
DEFB $45
DEFB $5C
DEFB $3A
DEFB $2E
DEFB $3D
DEFB $3B
DEFB $29
DEFB $40 ; 2 - @
DEFB $57 ; W - W
DEFB $7C ; S
DEFB $00 ; SYMB
DEFB $20 ; SPACE
DEFB $0D ; ENTER
DEFB $22 ; P - "
DEFB $5F ; 0 - _
DEFB $21 ; 1 - !
DEFB $51 ; Q - Q
DEFB $7E ; A - ~
DEFB $00 ; SHIFT
; end of key tables
; ---------------------------
; THE 'PRINT ROUTINE' Part 2.
; ---------------------------
; If output is not directed into the input buffer then jump forward else
; call the routine to output to lower screen.
L03EE: JR Z,L03F5 ; forward to main screen print.
CALL L017E ; PR_LOWER
EXX ; restore main set
RET ; return. >>
; the print output is not directed to the input buffer but first check that
; the user has not set up a vector to their own routine to print characters
; for instance to a printer.
L03F5: LD B,A ; save the character in the B register.
LD HL,($3C29) ; fetch possible vector from EXWRCH
; (normally 0)
LD A,H ; test for
OR L ; the value zero.
LD A,B ; fetch the character back to A.
JR Z,L03FF ; skip forward if no user-supplied
; routine.
L03FE: JP (HL) ; else jump to user-supplied routine
; which should finish with a JP (IY)
; ---
; PRINTING TO UPPER SCREEN
; ---
L03FF: LD HL,($3C1C) ; SCRPOS
LD DE,($3C24) ; L_HALF
EX DE,HL ; ??
SCF ; inclusive byte.
SBC HL,DE ; subtract screen position+1 from
; the start of input buffer.
EX DE,HL ; hl=scrpos
CALL C,L0421 ; if no room then scroll upper display
CP $0D ; carriage return?
JR Z,L0416 ; skip forward if so.
LD (HL),A ; else insert the character.
INC HL ; point to next position.
JR L041C ; forward
; ---
; a carriage return
L0416: INC HL ; increment screen address.
LD A,L ; fetch low byte of address and mask.
AND $1F ; a zero result indicates a line skip.
JR NZ,L0416 ; loop until a new line of 32 columns
; is started.
; both paths converge.
L041C: LD ($3C1C),HL ; update SCRPOS
EXX ; back to main set.
RET ; return.
; -------------------------------------
; The 'UPPER DISPLAY SCROLLING' ROUTINE
; -------------------------------------
L0421: PUSH AF ; save character
LD HL,$3C1C ; address the low order byte SCRPOS
CALL L0443 ; routine cursor up
; i.e. SCRPOS = SCRPOS - 32
POP AF ; restore character
; now calculate the number of characters to scroll in the upper display.
LD HL,($3C24) ; fetch L_HALF the start of input buffer
LD DE,$2420 ; second line in video display
;
; => scroll lower display enters here
L042F: AND A ; prepare for true subtraction.
SBC HL,DE ; find number of characters to scroll.
LD B,H ; result to BC
LD C,L
LD HL,$FFE0 ; set HL to -32d
ADD HL,DE ; now HL = DE -32d
EX DE,HL ; switch so DE = HL - 32
LDIR ; scroll the lines up.
LD B,$20 ; blank a line of 32 characters
L043D: DEC HL ; decrement screen address.
LD (HL),$20 ; insert a space character
DJNZ L043D ; and loop for all 32 characters
RET ; return.
; --------------------------------
; THE 'SCREEN POINTERS' SUBROUTINE
; --------------------------------
;
L0443: LD A,(HL) ; fetch low byte of screen address
SUB $20 ; subtract thirty two characters.
LD (HL),A ; and put back.
INC HL ; address high-order byte.
JR NC,L044B ; forward if low byte did not wrap
DEC (HL) ; else decrement the high byte as the
; position has moved across a third of
; the display.
L044B: INC HL ; address following System Variable
RET ; return.
; -----------------------------------
; THE 'INDEX SYSTEM VARIABLE' ROUTINE
; -----------------------------------
; This routine is used by words CONTEXT, CURRENT, BASE etc. to index and then
; stack a system variable associated with a FORTH word. See shortly.
;
; It is a bit overblown considering the eventual position of the System
; Variables and ld d,$3c; rst 10h; jp (iy) could have been used instead of
; the long-winded addition below.
L044D: EX DE,HL ; HL addresses the offset byte.
LD E,(HL) ; fetch to E register
;
LD D,$00 ; prepare to add.
LD HL,$3C00 ; the address of start of SYSVARS
ADD HL,DE ; add the 8-bit offset
EX DE,HL ; location to DE.
RST 10H ; push word DE
JP (IY) ; to 'next'.
; ---------------
; THE 'HERE' WORD
; ---------------
; ( -- address)
; Leaves the address of one past the end of the dictionary.
L0459: DEFM "HER" ; 'name field'
DEFB 'E' + $80
DEFW L00AA ; 'link field'
L045F: DEFB $04 ; 'name length field'
L0460: DEFW L0462 ; 'code field'
; ---
L0462: LD DE,($3C37) ; system variable STKBOT.
RST 10H ; push word DE
JP (IY) ; to 'next'.
; ------------------
; THE 'CONTEXT' WORD
; ------------------
; ( -- 15411 )
; A system variable pointing to the context vocabulary.
; $3C33 CONTEXT
L0469: DEFM "CONTEX" ; 'name field'
DEFB 'T' + $80
DEFW L045F ; 'link field'
L0472: DEFB $07 ; 'name length field'
L0473: DEFW L044D ; 'code field'
; ---
L0475: DEFB $33 ; low byte of system variable.
; ------------------
; THE 'CURRENT' WORD
; ------------------
; ( -- 15409 )
; A system variable pointing to the current vocabulary.
; $3C31 CURRENT
L0476: DEFM "CURREN" ; 'name field'
DEFB 'T' + $80
DEFW L0472 ; 'link field'
L047F: DEFB $07 ; 'name length field'
L0480: DEFW L044D ; 'code field'
; ---
L0482: DEFB $31 ; a single parameter low-byte of $3C31.
; ---------------
; THE 'BASE' WORD
; ---------------
; ( -- 15423)
; A one-byte variable containing the system number base.
; $3C3F BASE
L0483: DEFM "BAS" ; 'name field'
DEFB 'E' + $80
DEFW L047F ; 'link field'
L0489: DEFB $04 ; 'name length field'
L048A: DEFW L044D ; 'code field'
; ---
L048C: DEFB $3F ; low-byte of system variable BASE
; ---
; These two Internal Words are used to stack the value of FLAGS and DICT.
; -------------------------
; The 'flags' Internal Word
; -------------------------
L048D: DEFW L044D ; headerless 'code field'
; ---
L048F: DEFB $3E ; low-order byte of FLAGS $3C3E
; -------------------------
; The 'dict' Internal Word
; -------------------------
L0490: DEFW L044D ; headerless 'code field'
; ---
L0492: DEFB $39 ; low-order byte of DICT $3C39
; --------------
; THE 'PAD' WORD
; --------------
; ( -- 9985 )
; Stacks the address of the 254-byte workpad.
; On most FORTH systems the PAD floats about in memory but on the Ace it is
; fixed in location and size. Its definition is simply a constant.
l0493 DEFM "PA" ; 'name field'
DEFB 'D' + $80
DEFW L0489 ; 'link field'
L0498: DEFB $03 ; 'name length field'
L0499: DEFW L0FF5 ; 'code field' - stack word
; ---
L049B: DEFW $2701 ; parameter is 9985 decimal -
; work pad address
; ------------
; THE ';' WORD
; ------------
; Terminates colon, DEFINER and COMPILER definitions.
L049D: DEFB ';' + $80 ; 'name field'
DEFW L0498 ; 'link field'
L04A0: DEFB $41 ; length 1 + $40 (immediate word)
L04A1: DEFW L1108 ; 'code field' - compile
; ---
L04A3: DEFW L04B6 ; exit
L04A5: DEFW L12D8 ; check-for
DEFB $0A ; ten marker byte?
DEFW L1A0E ; end-forth.
; code gels
L04AA: LD HL,$3C3E ; address FLAGS
LD A,(HL) ; fetch FLAGS value.
AND $BB ; AND %10111011
; reset bit 2 - show definition complete
; reset bit 6 - show in interpreter mode
LD (HL),A ; update FLAGS value.
JP (IY) ; to 'next'.
; ----
; Note. these backward links to the beginning of words will probably be less
; of a mystery when the syntax checking and listing modules are more fully
; explored. A value of $FFFF sometimes occurs.
x04b3 DEFB $00 ;;
x04b4 DEFB $E8 ;;
x04b5 DEFB $FF ;; 04b5 + ffe8 = 049d = ';'
; ----------------------------------
; THE 'ADDRESS' INTERPRETER ROUTINES
; ----------------------------------
; ------------------------
; The 'Exit' Internal Word
; ------------------------
; Drops the 'Next Word' pointer from the Return Stack thereby ending a
; subroutine and returning to next word in calling thread.
L04B6: DEFW L04B8 ; headerless 'code field'
; ---
L04B8: POP HL ; discard the next word pointer.
; ------------------------------
; THE 'ADDRESS INTERPRETER' LOOP
; ------------------------------
; Sometimes known as the Sequencer.
;
; iy_fast
L04B9: POP HL ; word pointer.
; =====> from DOCOLON and BRANCH
L04BA: LD E,(HL)
INC HL
LD D,(HL)
INC HL
PUSH HL ; word pointer.
; ==>
;
L04BF: EX DE,HL
LD E,(HL)
INC HL
LD D,(HL)
INC HL
EX DE,HL
JP (HL) ; jump to machine code (4 clock cycles)
; which will terminate with a JP (IY)
; instruction (8 clock cycles).
; --------------------------------
; The 'Memory Check' Internal Word
; --------------------------------
; This internal word which also checks the BREAK key is only used from the
; start of the LINE definition. However the machine code entry point is the
; normal value of the IY register and so this code is executed at the end of
; every word.
L04C6: DEFW L04C8 ; headerless 'code field'
; iy_slow
L04C8: LD BC,$000B ; allow overhead of eleven bytes
LD DE,($3C3B) ; SPARE
LD HL,($3C37) ; STKBOT
ADD HL,BC ; add the overhead
SBC HL,DE ; subtract the SPARE value
JR C,L04D9 ; forward if the original 12 byte gap
; remains.
; else stack underflow has occurred.
L04D7: RST 20H ; Error 2
DEFB $02 ; Data stack underflow.
; ---
L04D9: LD BC,$0000 ; allow no overhead.
CALL L0F8C ; check free memory
CALL L04E4 ; check BREAK key.
JR L04B9 ; back to iy_fast
; ------------------------------------
; THE 'CHECK FOR BREAK KEY' SUBROUTINE
; ------------------------------------
; Check for the key combination SHIFT/SPACE.
L04E4: LD A,$FE ; read port $FEFE -
IN A,($FE) ; keys SPACE, SYMSHIFT, M, N, B.
RRA ; test bit for outermost key
RET C ; return if not pressed.
LD A,$7F ; read port $7FFE -
IN A,($FE) ; keys SHIFT, Z, X, C, V.
RRA ; test bit for outermost key
RET C ; return if not pressed.
L04F0: RST 20H ; Error 3.
DEFB $03 ; BREAK pressed.
; -------------------------
; THE 'MAIN EXECUTION' LOOP
; -------------------------
; The final part of the QUIT definition, as in all FORTH implementations,
; just loops through two FORTH words.
; The first call - to the Address Interpreter - does not return.
; The return address is the next word QUERY which the interpreter pops off
; the Return Stack and then before executing puts the address of the next word
; on Return Stack. The default action of the Address Interpreter is to execute
; words in turn until some word, such as branch, alters this default behaviour.
L04F2: CALL L04B9 ; forth.
L04F5: DEFW L058C ; QUERY - input buffer
DEFW L0506 ; LINE - interpret buffer
DEFW L0536 ; prOK - print OK
DEFW L1276 ; branch - relative jump
L04FD: DEFW $FFF7 ; back to L04F5
; ---
; the first high-level interpreted word.
; ---
; ---------------
; THE 'LINE' WORD
; ---------------
; Interprets input buffer as a normal FORTH line.
L04FF: DEFM "LIN" ; 'name field'
DEFB 'E' + $80
DEFW L04A0 ; 'link field'
L0505: DEFB $04 ; 'name length field'
L0506: DEFW L0EC3 ; 'code field' - docolon
; ---
L0508: DEFW L04C6 ; check mem each time through loop
; as dictionary could be expanding.
DEFW L063D ; FIND - search the dictionary
DEFW L08EE ; ?DUP - duplicate if found
DEFW L1283 ; ?branch - forward if not a
L0510: DEFW $0007 ; to L0518 - word.
DEFW L054F ; test and stack??
DEFW L1276 ; branch
L0516: DEFW $FFF1 ; back to L0508
L0518: DEFW L06A9 ; NUMBER
DEFW L08EE ; ?DUP
DEFW L1283 ; ?branch - forward if not a
L051E: DEFW $0007 ; to L0526 - number.
DEFW L0564 ; pop de with test
DEFW L1276 ; branch
L0524: DEFW $FFE3 ; loop back to L0508
L0526: DEFW L061B ; stack-length
DEFW L0C1A ; 0=
DEFW L1283 ; ?branch - forward with anything
L052C: DEFW $0003 ; to L0530 - else
L052E: DEFW L04B6 ; EXIT >>>
; ---
L0530: DEFW L0578 ; RETYPE - [?] at relevant place
DEFW L1276 ; branch - once corrected back
L0534: DEFW $FFD3 ; to L0508 - to the loop.
; ----------------------------
; The 'Print OK' Internal Word
; ----------------------------
; prints the OK message after successful execution.
L0536: DEFW L0538 ; headerless 'code field'
L0538: LD A,($3C3E) ; fetch system variable FLAGS
BIT 6,A ; test for 'COMPILER' mode.
JR NZ,L054D ; forward if so.
BIT 4,A ; test for 'INVIS' mode.
JR NZ,L054D ; forward if so.
CALL L1808 ; else print the inline string.
; ---
DEFM " OK" ; the OK message between two spaces.
DEFB ' ' + $80 ; last one inverted.
; ---
L054A: LD A,$0D ; prepare a carriage return.
RST 08H ; and PRINT also.
L054D: JP (IY) ; to 'next'.
; ------------------------------
; The 'XXXXXXXXXX' Internal Word
; ------------------------------
; to handle a Word from LINE
L054F: DEFW L0551 ; headerless 'code field'
; ---
L0551: RST 18H ; pop address from Data Stack to DE
DEC DE ; point to the 'name length field'
LD A,(DE) ; fetch contents of the address.
CPL ; complement.
AND (IX+$3E) ; FLAGS
AND $40 ; isolate BIT 6 of FLAGS, set if in
; compiler mode.
INC DE ; increment address to 'code field'
JR Z,L0561 ; forward if not in compiling mode
RST 10H ; push word DE - add to dict
LD DE,L0F4E ; ',' - enclose
L0561: JP L04BF ; next word.
; -----------------------
; The '???' Internal Word
; -----------------------
; after handling a number from LINE
L0564: DEFW L0566 ; headerless 'code field'
; ---
L0566: RST 18H ; pop word DE
BIT 6,(IX+$3E) ; test FLAGS - compiler mode ?
JR NZ,L0561 ; loop back while in compiler mode.
JP (IY) ; to 'next'.
; -----------------
; THE 'RETYPE' WORD
; -----------------
; Allows user to edit the input line. Turns cursor to [?].
L056F: DEFM "RETYP" ; 'name field'
DEFB 'E' + $80
DEFW L058B ; 'link field'
L0577: DEFB $06 ; 'name length field'
L0578: DEFW L057A ; 'code field'
; ---
L057A: CALL L02EA ; routine sets logical line.
CALL L0276 ; routine pr_cursor
LD (HL),$BF ; the inverse [?] character
JR L0594 ; forward to join the QUERY routine.
; ----------------
; THE 'QUERY' WORD
; ----------------
; Clears input buffer, then accepts characters until ENTER pressed.
; Buffer can be edited as usual and is limited to 22 lines.
L0584: DEFM "QUER" ; 'name field'
DEFB 'Y' + $80
DEFW L0505 ; 'link field'
L058B: DEFB $05 ; 'name length field'
L058C: DEFW L058E ; 'code field'
; ---
L058E: CALL L02D8 ; routine SETBUF
CALL L0276 ; routine pr_cursor
; ->
L0594: LD HL,$3C28 ; fetch STATIN
SET 0,(HL) ;
RES 5,(HL) ; (bit 5 set by interrupt when the user
; presses the ENTER key)
L059B: BIT 5,(HL) ; wait for interrupt to set the bit.
JR Z,L059B ; loop until.
CALL L0225 ; routine DEL-CURSOR
JP (IY) ; to 'next'.
; ---------------
; THE 'WORD' WORD
; ---------------
; WORD text
; ( delimiter -- address )
; Takes text out of the input buffer up as far as a delimiter, and copies it
; to pad, starting at the second byte there. Puts the length (not including
; the delimiter) in the first byte of the pad, and stacks the address of the
; first byte of the pad.
; At most 253 characters are taken from the input buffer. If there are more
; left before the delimiter, then the first byte of the pad shows 254.
; Initial delimiters are ignored.
L05A4: DEFM "WOR" ; 'name field'
DEFB 'D' + $80
DEFW L0577 ; 'link field'
L05AA: DEFB $04 ; 'name length field'
L05AB: DEFW L05AD ; 'code field'
; ---
L05AD: RST 18H ; pop word DE
LD HL,$27FE ; set HL to penultimate byte of 'pad'.
LD B,$FD ; the count is 253.
L05B3: LD (HL),$20 ; insert a space in pad.
DEC HL ; decrement the address.
DJNZ L05B3 ; repeat for the 253 locations.
PUSH DE ; save the delimiter.
EX DE,HL ; save in HL also, DE is start of pad.
RST 10H ; stack data word DE
POP DE ; retrieve the delimiter.
CALL L05E1 ;
INC B
DEC B
JR Z,L05C6 ;
LD BC,$00FF
L05C6: LD HL,$2701
LD (HL),C
INC HL
LD A,$FC
CP C
JR NC,L05D1 ;
LD C,A
L05D1: INC C
PUSH DE
PUSH BC
EX DE,HL
LDIR
POP BC
POP DE
DEC C
CALL L07DA ;
JP (IY) ; to 'next'.
; --------------------------------
; THE 'GET BUFFER TEXT' SUBROUTINE
; --------------------------------
; Called from FIND, NUMBER and XXXXX. Word may have leading spaces and is
; terminated by a space or newline (zero).
; It is also used to find the end of a comment delimited by ')'.
;
; =>
L05DF: LD E,$20 ; set a space as the skip character.
; =>called with E holding delimiter.
;
L05E1: LD HL,($3C24) ; fetch L_HALF - start of screen buffer.
LD ($3C1E),HL ; make INSCRN start of logical line the
; same.
LD BC,$0000 ; initialize letter count to zero.
; -> loop
L05EA: INC HL ; increment screen address.
LD A,(HL) ; fetch character to A.
CP E ; compare to character in E.
JR Z,L05EA ; loop while character matches.
AND A ; test for zero (at $2700?)
JR Z,L0600 ; forward if so.
; a word has been found on the screen line.
PUSH HL ; save pointer to start of word.
L05F3: INC BC ; increment the letter count.
INC HL ; increment the screen pointer.
LD A,(HL) ; fetch new character
AND A ; test for zero.
JR Z,L05FC ; skip forward as at end of word.
CP E ; compare to the skip character.
JR NZ,L05F3 ; loop back if still within a word.
L05FC: POP DE ; retrieve pointer to start of word.
XOR A ;; clear A
CP B ;; compare to B zero
RET ; return. with carry reset for success.
; ---
L0600: PUSH DE ; save delimiter
CALL L02B0 ; routine find zerobyte
JP PO,L0614 ; jump if found to exit failure
LD DE,($3C24) ; else set DE from L_HALF
CALL L07FA ; routine SPACE_FILL (DE-HL)
LD ($3C24),HL ; set L_HALF to next line
POP DE ; restore delimiter
JR L05E1 ; loop back using new line.
; ---
; branch here if a word not found.
L0614: EX DE,HL ; DE addresses cursor.
POP BC ; discard saved delimiter
LD BC,$0000 ; set BC, to zero
SCF ; signal not found
RET ; return.
; --------------------------------
; The 'stack length' Internal Word
; --------------------------------
; used once only from LINE to check for any extraneous text that is not a Word
; or a Number.
L061B: DEFW L061D ; headerless 'code field'
; ---
L061D: CALL L05DF ; get buffer
LD D,B ; transfer length of word
LD E,C ; from BC to DE
RST 10H ; push word DE
JP (IY) ; to 'next'.
; ----------------
; THE 'VLIST' WORD
; ----------------
; List dictionary to screen, including words in ROM.
; (no pause after 18 lines)
L0625: DEFM "VLIS" ; 'name field'
DEFB 'T' + $80
DEFW L05AA ; 'link field'
L062C: DEFB $05 ; 'name length field'
L062D: DEFW L062F ; 'code field'
; ---
L062F: LD A,$0D ; prepare a newline
RST 08H ; print it.
LD C,$00 ; set a flag for 'do all names'.
JR L0644 ; forward to FIND.
; ---------------
; THE 'FIND' WORD
; ---------------
; ( -- compilation address )
; Leaves compilation address of first word in input buffer, if defined in
; context vocabulary; else 0.
L0636: DEFM "FIN" ; 'name field'
DEFB 'D' + $80
DEFW L062C ; 'link field'
L063C: DEFB $04 ; 'name length field'
L063D: DEFW L063F ; 'code field'
; ---
L063F: CALL L05DF ; get buffer word, gets length in C.
JR C,L068A ; back if null to stack word zero
; ->
L0644: LD HL,($3C33) ; fetch value of system variable CONTEXT
LD A,(HL) ; extract low byte of address.
INC HL ; increment pointer.
LD H,(HL) ; extract high byte of address.
LD L,A ; address now in HL.
; The address points to the 'name length field' of the most recent word in the
; Dictionary.
L064B: LD A,(HL) ; fetch addressed byte.
AND $3F ; discount bit 6, the immediate word
; indicator, to give length 1-31
JR Z,L067F ; a 'zero' length indicates this is a
; link like the example at the end of
; this ROM.
XOR C ; match against C.
JR Z,L0657 ; skip forward if lengths match.
LD A,C ; test flag C
AND A ; for value zero.
JR NZ,L067F ; forward if C not zero.
; else a name that matches the search length or all names are required - VLIST.
L0657: PUSH DE ; preserve DE
PUSH HL ; preserve 'name length field' pointer.
CALL L15E8 ; routine WORDSTART finds start of name.
; A is returned as zero.
OR C ; test C for zero
JR Z,L0676 ; branch forward to print if in VLIST.
; else the search is for a specific word and a word with same length, at least,
; has been found.
LD B,C ; copy the length to counter B.
L0660: LD A,(DE) ; fetch first letter of match word.
CALL L0807 ; routine UPPERCASE
INC DE ; update pointer (in lower screen)
XOR (HL) ; match against letter (in dictionary).
AND $7F ; disregard any inverted bit.
INC HL ; increment dictionary pointer.
JR NZ,L067D ; exit loop to try next link if no match
DJNZ L0660 ; else loop back for all letters.
; Oh Frabjous day - a match.
POP DE ; pop 'name length field' pointer.
INC DE ; increment to point to compilation
; address.
RST 10H ; stack date word DE.
; the remaining task is to clean up the input buffer in the lower screen.
POP DE ; pop the DE - screen pointer.
CALL L07DA ; clean up - backfill with spaces.
JP (IY) ; to 'next'.
; -----------------------
; THE 'PRINT NAME' BRANCH
; -----------------------
; This branch is taken from the above loop when all found words are to be
; printed by VLIST. It takes its time as if the user has expanded the
; dictionary then the list will scroll off the top of the screen. By waiting
; for an interrupt each time, it ensures that a standard listing takes about
; three seconds and there is ample opportunity to press BREAK to stop at a
; certain point.
L0676: CALL L17FB ; routine print string and space
HALT ; wait for an interrupt.
CALL L04E4 ; routine checks BREAK key.
L067D: POP HL ; restore 'name length field' pointer
POP DE ; restore DE
L067F: DEC HL ; point to high byte of 'link field'
LD A,(HL) ; hold it in A.
DEC HL ; point to low byte of 'link field'
LD L,(HL) ; transfer address of the new
LD H,A ; 'name length field' to HL pointer.
OR L ; test if address is zero - for the
; last entry in the linked list.
JR NZ,L064B ; loop back while this is not the
; last entry in the vocabulary.
L0687: DEFB $C3 ; A JP instruction i.e. JP L068A
; Note. The intention is to jump past the headerless code word for the internal
; word stk_zero. Since the word that would follow the first byte of the jump
; instruction would be identical to the word it is jumping over then the word
; can be omitted. Only saves one byte but this is back in 1983.
; ----------------------------
; The 'stk-zero' Internal Word
; ----------------------------
; ( -- 0 )
L0688: DEFW L068A ; headerless 'code field'
; ---
L068A: LD DE,$0000 ; load DE with the value zero.
RST 10H ; stack Data Word DE
JP (IY) ; to 'next'.
; ------------------
; THE 'EXECUTE' WORD
; ------------------
; ( compilation address -- )
; Executes the word with the given compilation address.
L0690: DEFM "EXECUT" ; 'name field'
DEFB 'E' + $80
DEFW L063C ; 'link field'
L0699: DEFB $07 ; 'name length field'
L069A: DEFW L069C ; 'code field'
; ---
L069C: RST 18H
JP L04BF ;
; -----------------
; THE 'NUMBER' WORD
; -----------------
; Takes a number from the start of the input buffer. Leaves the number and
; a non-zero address on the stack. (The address is the compilation address
; of a literal compiler, so that if you then say EXECUTE, the literal compiler
; compiles the number into the dictionary as a literal - for an integer it
; is 4102, for a floating point number it is 4181).
; If no valid number then leaves just 0 on the stack.
L06A0: DEFM "NUMBE" ; 'name field'
DEFB 'R' + $80
DEFW L0699 ; 'link field'
L06A8: DEFB $06 ; 'name length field'
L06A9: DEFW L06AB ; 'code field'
; ---
L06AB: CALL L05DF ; get buffer
JR C,L068A ; if empty stack word zero.
PUSH BC
PUSH DE
CALL L074C ;
JR NZ,L06BC ;
LD DE,$1006 ; addr literal?
JR L0714 ;
; ---
L06BC: RST 18H ; pop word DE
LD DE,$0000
RST 10H ; push word DE
LD DE,$4500
POP BC
PUSH BC
LD A,(BC)
CP $2D ; is it '-' ?
JR NZ,L06CE ;
LD D,$C5
INC BC
L06CE: RST 10H ; push word DE
LD D,B
LD E,C
DEC HL
DEC HL
L06D3: CALL L0723 ; routine GET_DECIMAL
INC HL
INC (HL)
DEC HL
JR NC,L06D3 ;
CP $FE
JR NZ,L071C ;
L06DF: CALL L0723 ; routine GET_DECIMAL
JR NC,L06DF ;
ADD A,$30 ; add '0' converting to letter.
CALL L077B ;
JR NZ,L06EF ;
LD E,$00
JR L06FD ;
L06EF: AND $DF ;
CP $45 ; is it 'E' - extended format?
JR NZ,L071C ;
PUSH HL
CALL L074C ;
RST 18H ; pop word DE
POP HL
JR NZ,L071C ;
L06FD: CALL L0740 ;
JR Z,L0711 ;
INC HL
LD A,(HL)
AND $7F
ADD A,E
JP M,L071C ; forward +->
JR Z,L071C ; forward +->
XOR (HL)
AND $7F
XOR (HL)
LD (HL),A
L0711: LD DE,L1055 ; stk_fp
L0714: RST 10H ; push word DE
POP DE
POP BC
CALL L07DA ;
JP (IY) ; to 'next'.
; ---
; +->
L071C: POP HL
POP HL
RST 18H ; pop word DE
RST 18H ; pop word DE
JP L068A ;
; ----------------------------
; THE 'GET DECIMAL' SUBROUTINE
; ----------------------------
; Fetch character and return with carry set if after conversion is not in
; range 0 to 9.
L0723: LD A,(DE)
INC DE
SUB $30 ; subtract '0'
RET C ; return if was less than '0'
CP $0A ; compare to ten.
CCF ; complement
RET C ; return - with carry set if over 9.
; ---------
; normalize?
; ---------
; => from below only.
L072C: LD C,A
LD A,(HL)
AND $F0
RET NZ
LD A,C
; => (int/print_fp)
L0732: DEC HL
DEC HL
LD C,$03
L0736: RLD ; A = xxxx3210 <-- 7654<-3210 (HL)
INC HL ;
DEC C
JR NZ,L0736 ;
DEC (HL) ; decrement exponent
DEC HL ; point to start of BCD nibbles
CP A
RET
; ---
; from ufloat to normalize 6-nibble mantissa
L0740: LD B,$06 ; six nibbles
L0742: XOR A
CALL L072C ;
RET NZ
DJNZ L0742 ;
INC HL
LD (HL),B
RET
; ---------------------------
; THE 'GET NUMBER' SUBROUTINE
; ---------------------------
; can be called twice by the above code for the word 'NUMBER'.
; Once to get the first number encountered and sometimes, if in extended
; format, the exponent as well.
L074C: RST 10H ; push word DE
CALL L04B9 ; forth
L0750: DEFW L086B ; dup
DEFW L0896 ; C@
DEFW L104B ; stk-data
DEFB $2D ; chr '-'
DEFW L0C4A ; =
DEFW L086B ; dup
DEFW L0DA9 ; negate
DEFW L08D2 ; >R
DEFW L0DD2 ; +
DEFW L0E1F ; 1-
DEFW L0688 ; stk-zero
DEFW L0688 ; stk-zero
DEFW L08FF ; rot
L0769: DEFW L078A ; convert
DEFW L08FF ; rot
DEFW L08DF ; R>
DEFW L0D94 ; pos
DEFW L08FF ; rot
DEFW L0879 ; drop
DEFW L0885 ; swap
DEFW L1A0E ; end-forth.
L0779: RST 18H ; pop word DE
LD A,(DE)
L077B: CP $20
RET Z
AND A
RET
; ------------------
; THE 'CONVERT' WORD
; ------------------
; ( ud1, addr1 -- ud2, addr2 )
: Accumulates digits from text into an unsigned double length
; number ud1: for each digit, the double length accumulator is
; multiplied by the system number base and the digit (converted
; from ASCII) is added on. The text starts at addr1 + 1. addr2 is
; the address of the first unconvertible character, ud2 is the
; final value of the accumulator.
L0780: DEFM "CONVER" ; 'name field'
DEFB 'T' + $80
DEFW L06A8 ; 'link field'
L0789: DEFB $07 ; 'name length field'
L078A: DEFW L0EC3 ; 'code field' - docolon
; ---
L078C: DEFW L0E09 ; 1+
L078E: DEFW L086B ; dup
L0790: DEFW L08D2 ; >R
L0792: DEFW L0896 ; C@
L0794: DEFW L07B8 ; stk_digit
L0796: DEFW L1283 ; ?branch
L0798: DEFW $001B ; to 0799 + 1B = $07B4
L079A: DEFW L0885 ; swap
L079C: DEFW L048A ; get base
L079E: DEFW L0896 ; C@
L07A0: DEFW L0CA8 ; u*
L07A2: DEFW L0879 ; drop
L07A4: DEFW L08FF ; rot
L07A6: DEFW L048A ; get base
L07A8: DEFW L0896 ; C@
L07AA: DEFW L0CA8 ; U*
L07AC: DEFW L0DEE ; D+
L07AE: DEFW L08DF ; R>
L07B0: DEFW L1276 ; branch
L07B2: DEFW $FFD9 ; loop back to L078C
L07B4: DEFW L08DF ; R>
L07B6: DEFW L04B6 ; exit
; -----------------------------
; The 'stk_digit' Internal Word
; -----------------------------
L07B8: DEFW L07BA ; headerless 'code field'
; ---
L07BA: RST 18H ; pop word DE
LD A,E ; character to A
CALL L0807 ; to_upper
ADD A,$D0 ; add to give carry with '0' and more.
JR NC,L07D7 ; if less than '0' push byte 0 false.
CP $0A ; compare to ten.
JR C,L07CD ; forward to stack bytes 0 - 9.
ADD A,$EF ;
JR NC,L07D7 ; push word false 0.
ADD A,$0A
L07CD: CP (IX+$3F) ; compare to BASE
JR NC,L07D7 ; push word false 0.
; else digit is within range of number base
LD D,$00
LD E,A
RST 10H ; push word DE
SCF ; set carry to signal true
L07D7: JP L0C21 ; push word 1 or 0
; ---
; ??
; ---
L07DA: LD H,D
LD L,E
INC BC
ADD HL,BC
PUSH HL
BIT 4,(IX+$3E) ; FLAGS
CALL Z,L097F ; pr_string
CALL L02B0 ; curs?
POP DE
AND A
SBC HL,DE
LD B,H
LD C,L
LD HL,($3C1E) ; INSCRN
INC HL
EX DE,HL
JR C,L07FB ;
JR Z,L07FA ; forward to SPACE_FILL.
LDIR
; ------------------------
; The 'SPACE FILL' routine
; ------------------------
; -> from cls
L07FA: AND A ; prepare to subtract two screen
; pointers.
L07FB: SBC HL,DE ; number of bytes in HL.
EX DE,HL ; now in DE, HL = start of area.
L07FE: LD A,D ; check if the
OR E ; counter is zero.
RET Z ; return if so. >>
LD (HL),$20 ; insert a space character.
INC HL ; next address.
DEC DE ; decrement byte counter.
JR L07FE ; loop back to exit on zero.
; --------------------------
; THE 'UPPERCASE' SUBROUTINE
; --------------------------
; converts characters to uppercase.
L0807: AND $7F ; ignore inverse bit 7
CP $61 ; compare to 'a'
RET C ; return if lower
CP $7B ; compare to 'z' + 1
RET NC ; return if higher than 'z'
AND $5F ; make uppercase
RET ; return.
; --------------
; THE 'VIS' WORD
; --------------
; Allows copy-up mechanism and 'OK'.
L0812: DEFM "VI" ; 'name field'
DEFB 'S' + $80
DEFW L0789 ; 'link field'
L0817: DEFB $03 ; 'name length field'
L0818: DEFW L081A ; 'code field'
; ---
L081A: RES 4,(IX+$3E) ; update FLAGS signal visible mode.
JP (IY) ; to 'next'.
; ----------------
; THE 'INVIS' WORD
; ----------------
; Suppresses copy-up mechanism and 'OK'.
L0820: DEFM "INVI" ; 'name field'
DEFB 'S' + $80
DEFW L0817 ; 'link field'
L0827: DEFB $05 ; 'name length field'
L0828: DEFW L082A ; 'code field'
; ---
L082A: SET 4,(IX+$3E) ; update FLAGS signal invisible mode.
JP (IY) ; to 'next'.
; ---------------
; THE 'FAST' WORD
; ---------------
; Fast mode - runs without error checks.
; Debugged programs run 25% faster.
L0830: DEFM "FAS" ; 'name field'
DEFB 'T' + $80
DEFW L0827 ; 'link field'
L0836: DEFB $04 ; 'name length field'
L0837: DEFW L0839 ; 'code field'
; ---
L0839: LD IY,L04B9 ; miss memory checks on return
JP (IY) ; to 'next'.
; ---------------
; THE 'SLOW' WORD
; ---------------
; ( -- )
; Slow mode with error checking.
; Make IY point to a return routine that performs housekeeping.
L083F: DEFM "SLO" ; 'name field'
DEFB 'W' + $80
DEFW L0836 ; 'link field'
L0845: DEFB $04 ; 'name length field'
L0846: DEFW L0848 ; 'code field'
; ---
L0848: LD IY,L04C8 ; set vector to memory checks each pass
JP (IY) ; to 'next'.
; ---------------------------------
; THE 'DATA STACK TO BC' SUBROUTINE
; ---------------------------------
; Called on twenty occasions to fetch a word from the Data Stack into the
; BC register pair. Very similar to RST 18H which does the same thing with the
; DE register pair as the destination on 73 occasions.
; In fact, as two Z80 restarts are unused, then 40 bytes of ROM code could have
; been saved by making this a restart also.
L084E: LD HL,($3C3B) ; fetch SPARE - start of Spare Memory.
DEC HL ; decrement to point to last stack item
LD B,(HL) ; load high byte to B.
DEC HL ; address low byte of word.
LD C,(HL) ; and load to C.
LD ($3C3B),HL ; update the system variable SPARE to
; a location two bytes less than it was.
RET ; return.
; -----------------------------------------
; THE 'CONTINUATION OF THE RST 18H' RESTART
; -----------------------------------------
; complete the operation of popping a word to DE from the data stack.
L0859: DEC HL ;
LD E,(HL) ;
LD ($3C3B),HL ; update SPARE
RET ; return.
; -----------------------------------------
; THE 'CONTINUATION OF THE RST 10H' RESTART
; -----------------------------------------
; complete the operation of pushing a word in DE to the data stack.
L085F: LD (HL),D ;
INC HL ;
LD ($3C3B),HL ; update SPARE
RET ; return.
; --------------
; THE 'DUP' WORD
; --------------
; ( n -- n, n )
; Duplicates the top of the stack.
L0865: DEFM "DU" ; 'name field'
DEFB 'P' + $80
DEFW L0845 ; 'link field'
L086A: DEFB $03 ; 'name length field'
L086B: DEFW L086D ; 'code field'
; ---
L086D: RST 18H ; unstack Data Word DE
RST 10H ; stack Data Word DE
RST 10H ; stack Data Word DE
JP (IY) ; to 'next'.
; ---------------
; THE 'DROP' WORD
; ---------------
; ( n -- )
; Throws away the top of the stack.
L0872: DEFM "DRO" ; 'name field'
DEFB 'P' + $80
DEFW L086A ; 'link field'
L0878: DEFB $04 ; 'name length field'
L0879: DEFW L087B ; 'code field'
; ---
L087B: RST 18H ; unstack Data Word DE
JP (IY) ; to 'next'.
; ---------------
; THE 'SWAP' WORD
; ---------------
; (n1, n2 -- n2, n1)
L087E: DEFM "SWA" ; 'name field'
DEFB 'P' + $80
DEFW L0878 ; 'link field'
L0884: DEFB $04 ; 'name length field'
L0885: DEFW L0887 ; 'code field'
; ---
L0887: RST 18H ; pop word DE
CALL L084E ; stk_to_bc
RST 10H ; push word DE
LD D,B ;
LD E,C ;
RST 10H ; push word DE
JP (IY) ; to 'next'.
; -------------
; THE 'C@' WORD
; -------------
; (address -- byte)
; Fetches the contents of a given address.
L0891: DEFB 'C' ; 'name field'
DEFB '@' + $80
DEFW L0884 ; 'link field'
L0895: DEFB $02 ; 'name length field'
L0896: DEFW L0898 ; 'code field'
; ---
L0898: RST 18H ; pop word DE
LD A,(DE)
LD E,A
LD D,$00
RST 10H ; push word DE
JP (IY) ; to 'next'.
; -------------
; THE 'C!' WORD
; -------------
; (n, address -- )
; Stores the less significant byte on n at a given address.
L08A0: DEFB 'C' ; 'name field'
DEFB '!' + $80
DEFW L0895 ; 'link field'
L08A4: DEFB $02 ; 'name length field'
L08A5: DEFW L08A7 ; 'code field'
; ---
L08A7: RST 18H ; pop word DE
CALL L084E ; stk_to_bc
LD A,C
LD (DE),A
JP (IY) ; to 'next'.
; ------------
; THE '@' WORD
; ------------
; (address -- n)
; Leaves on stack the single length integer at the given address.
L08AF: DEFB '@' + $80 ; 'name field'
DEFW L08A4 ; 'link field'
L08B2: DEFB $01 ; 'name length field'
L08B3: DEFW L08B5 ; 'code field'
; ---
L08B5: RST 18H ; pop word DE
EX DE,HL
LD E,(HL)
INC HL
LD D,(HL)
RST 10H ; push word DE
JP (IY) ; to 'next'.
; ------------
; THE '!' WORD
; ------------
; (n,address --)
; Stores the single-length integer n at the given address in memory.
L08BD: DEFB '!' + $80 ; 'name field'
DEFW L08B2 ; 'link field'
L08C0: DEFB $01 ; 'name length field'
L08C1: DEFW L08C3 ; 'code field'
; ---
L08C3: RST 18H ; pop word DE
CALL L084E ; stk_to_bc
EX DE,HL
LD (HL),C
INC HL
LD (HL),B
JP (IY) ; to 'next'.
; -------------
; THE '>R' WORD
; -------------
; (n -- )
; Transfers top entry on data stack to return stack.
; It can be copied back using 'I'.
L08CD: DEFB '>' ; 'name field'
DEFB 'R' + $80
DEFW L08C0 ; 'link field'
L08D1: DEFB $02 ; 'name length field'
L08D2: DEFW L08D4 ; 'code field'
; ---
L08D4: RST 18H
POP BC
PUSH DE
PUSH BC
JP (IY) ; to 'next'.
; -------------
; THE 'R>' WORD
; -------------
; ( -- entry from return stack)
; Transfers top entry on return stack to data stack.
L08DA: DEFB 'R' ; 'name field'
DEFB '>' + $80
DEFW L08D1 ; 'link field'
L08DE: DEFB $02 ; 'name length field'
L08DF: DEFW L08E1 ; 'code field'
; ---
L08E1: POP BC
POP DE
PUSH BC
RST 10H ; push word DE
JP (IY) ; to 'next'.
; ---------------
; THE '?DUP' WORD
; ---------------
; (n -- n, n) if n!=0.
; (n -- n) if n=0.
L08E7: DEFM "?DU" ; 'name field'
DEFB 'P' + $80
DEFW L08DE ; 'link field'
L08ED: DEFB $04 ; 'name length field'
L08EE: DEFW L08F0 ; 'code field'
; ---
L08F0: RST 18H ; fetch word DE
RST 10H ; push it back
LD A,D ; test if fetched
OR E ; word is zero
CALL NZ,L0010 ; push word DE if non-zero
JP (IY) ; to 'next'.
; --------------
; THE 'ROT' WORD
; --------------
; (n1, n2, n3 -- n2, n3, n1)
L08F9: DEFM "RO" ; 'name field'
DEFB 'T' + $80
DEFW L08ED ; 'link field'
L08FE: DEFB $03 ; 'name length field'
L08FF: DEFW L0EC3 ; |