- JMP UDOT1
-
-;-------------------------------------------------------------------------------
-; DICTIONARY MANAGEMENT
-;-------------------------------------------------------------------------------
-
-;https://forth-standard.org/standard/core/HERE
-;C HERE -- addr returns memory ptr
- FORTHWORD "HERE"
-HERE SUB #2,PSP
- MOV TOS,0(PSP)
- MOV &DDP,TOS
- mNEXT
-
-;https://forth-standard.org/standard/core/ALLOT
-;C ALLOT n -- allocate n bytes
- FORTHWORD "ALLOT"
-ALLOT ADD TOS,&DDP
- MOV @PSP+,TOS
- mNEXT
-
-;https://forth-standard.org/standard/core/CComma
-;C C, char -- append char
- FORTHWORD "C,"
-CCOMMA MOV &DDP,W
- MOV.B TOS,0(W)
- ADD #1,&DDP
- MOV @PSP+,TOS
- mNEXT
-
-;-------------------------------------------------------------------------------
-; BRANCH and LOOP OPERATORS
-;-------------------------------------------------------------------------------
-
-;Z branch -- branch always
-BRAN MOV @IP,IP ; 2
- mNEXT ; 4
-
-;Z ?FalseBranch x -- ; branch if TOS is FALSE (=zero)
-QFBRAN CMP #0,TOS ; 1 test TOS value
- MOV @PSP+,TOS ; 2 pop new TOS value (doesn't change flags)
- JZ BRAN ; 2 if TOS was = 0, take the branch = 11 cycles
- ADD #2,IP ; 1 else skip the branch destination
- mNEXT ; 4 ==> branch not taken = 10 cycles
-
-;Z ?TrueBranch x -- ; branch if TOS is true (<> zero)
-QTBRAN CMP #0,TOS ; 1 test TOS value
- MOV @PSP+,TOS ; 2 pop new TOS value (doesn't change flags)
- JNZ BRAN ; 2 if TOS was <> 0, take the branch = 11 cycles
- ADD #2,IP ; 1 else skip the branch destination
- mNEXT ; 4 ==> branch not taken = 10 cycles
-
-;Z (do) n1|u1 n2|u2 -- R: -- sys1 sys2 run-time code for DO
-; n1|u1=limit, n2|u2=index
-xdo MOV #8000h,X ;2 compute 8000h-limit "fudge factor"
- SUB @PSP+,X ;2
- MOV TOS,Y ;1 loop ctr = index+fudge
- MOV @PSP+,TOS ;2 pop new TOS
- ADD X,Y ;1
- PUSHM #2,X ;4 PUSHM X,Y, i.e. PUSHM LIMIT, INDEX
- mNEXT ;4
-
-;Z (+loop) n -- R: sys1 sys2 -- | sys1 sys2
-; run-time code for +LOOP
-; Add n to the loop index. If loop terminates, clean up the
-; return stack and skip the branch. Else take the inline branch.
-xploop ADD TOS,0(RSP) ;4 increment INDEX by TOS value
- MOV @PSP+,TOS ;2 get new TOS, doesn't change flags
-xloopnext BIT #100h,SR ;2 is overflow bit set?
- JZ BRAN ;2 no overflow = loop
- ADD #2,IP ;1 overflow = loop done, skip branch ofs
-UNXLOOP ADD #4,RSP ;1 empty RSP
- mNEXT ;4 16~ taken or not taken xloop/loop
-
-
-;Z (loop) R: sys1 sys2 -- | sys1 sys2
-; run-time code for LOOP
-; Add 1 to the loop index. If loop terminates, clean up the
-; return stack and skip the branch. Else take the inline branch.
-; Note that LOOP terminates when index=8000h.
-xloop ADD #1,0(RSP) ;4 increment INDEX
- JMP xloopnext ;2
-
-;https://forth-standard.org/standard/core/UNLOOP
-;C UNLOOP -- R: sys1 sys2 -- drop loop parms
- FORTHWORD "UNLOOP"
-UNLOOP JMP UNXLOOP
-
-;https://forth-standard.org/standard/core/I
-;C I -- n R: sys1 sys2 -- sys1 sys2
-;C get the innermost loop index
- FORTHWORD "I"
-II SUB #2,PSP ;1 make room in TOS
- MOV TOS,0(PSP) ;3
- MOV @RSP,TOS ;2 index = loopctr - fudge
- SUB 2(RSP),TOS ;3
- mNEXT ;4 13~
-
-;https://forth-standard.org/standard/core/J
-;C J -- n R: 4*sys -- 4*sys
-;C get the second loop index
- FORTHWORD "J"
-JJ SUB #2,PSP ; make room in TOS
- MOV TOS,0(PSP)
- MOV 4(RSP),TOS ; index = loopctr - fudge
- SUB 6(RSP),TOS
- mNEXT
-
-; ------------------------------------------------------------------------------
-; TERMINAL I/O, input part
-; ------------------------------------------------------------------------------
-
-;https://forth-standard.org/standard/core/KEY
-;C KEY -- c wait character from input device ; primary DEFERred word
- FORTHWORD "KEY"
-KEY MOV @PC+,PC ;3 Code Field Address (CFA) of KEY
-PFAKEY .word BODYKEY ; Parameter Field Address (PFA) of KEY, with default value
-BODYKEY MOV &TERM_RXBUF,Y ; empty buffer
- SUB #2,PSP ; 1 push old TOS..
- MOV TOS,0(PSP) ; 3 ..onto stack
- CALL #RXON
-KEYLOOP BIT #UCRXIFG,&TERM_IFG ; loop if bit0 = 0 in interupt flag register
- JZ KEYLOOP ;
- MOV &TERM_RXBUF,TOS ;
- CALL #RXOFF ;
- mNEXT
-
-;-------------------------------------------------------------------------------
-; INTERPRETER INPUT, the kernel of kernel !
-;-------------------------------------------------------------------------------
-
- .IFDEF SD_CARD_LOADER
- .include "forthMSP430FR_SD_ACCEPT.asm"
- .ENDIF
-
- .IFDEF DEFER_ACCEPT
-
-;https://forth-standard.org/standard/core/ACCEPT
-;C ACCEPT addr addr len -- addr len' get line at addr to interpret len' chars
- FORTHWORD "ACCEPT"
-ACCEPT MOV @PC+,PC ;3 Code Field Address (CFA) of ACCEPT
-PFAACCEPT .word BODYACCEPT ; Parameter Field Address (PFA) of ACCEPT
-BODYACCEPT ; BODY of ACCEPT = default execution of ACCEPT
-
- .ELSE
-
-;https://forth-standard.org/standard/core/ACCEPT
-;C ACCEPT addr addr len -- addr len' get line at addr to interpret len' chars
- FORTHWORD "ACCEPT"
-ACCEPT
-
- .ENDIF
-
- .IFDEF HALFDUPLEX ; to use FAST FORTH with half duplex input terminal (bluetooth or wifi connexion)
-
- .include "forthMSP430FR_HALFDUPLEX.asm"
-
- .ELSE ; to use FAST FORTH with full duplex terminal (USBtoUART bridge)
-
-; con speed of TERMINAL link, there are three bottlenecks :
-; 1- time to send XOFF/RTS_high on CR (CR+LF=EOL), first emergency.
-; 2- the char loop time,
-; 3- the time between sending XON/RTS_low and clearing UCRXIFG on first received char,
-; everything must be done to reduce these times, taking into account the necessity of switching to SLEEP (LPMx mode).
-; ----------------------------------;
-; ACCEPT part I prepare TERMINAL_INT;
-; ----------------------------------;
- MOV #ENDACCEPT,S ;2 S = XOFF_ret
- MOV #AKEYREAD1,T ;2 T = XON_ret
- PUSHM #3,IP ;5 PUSHM IP,S,T r-- ACCEPT_ret XOFF_ret XON_ret
- MOV TOS,W ;1 -- addr len
- MOV @PSP,TOS ;2 -- org ptr )
- ADD TOS,W ;1 -- org ptr W=Bound )
- MOV #0Dh,T ;2 T = 'CR' to speed up char loop in part II > prepare stack and registers for TERMINAL_INT use
- MOV #20h,S ;2 S = 'BL' to speed up char loop in part II )
- MOV #AYEMIT_RET,IP ;2 IP = return for YEMIT )
- BIT #UCRXIFG,&TERM_IFG ;3 RX_Int ?
- JZ ACCEPTNEXT ;2 no : case of quiet input terminal
- MOV &TERM_RXBUF,Y ;3 yes: clear RX_Int
- CMP #0Ah,Y ;2 received char = LF ? (end of downloading ?)
- JNZ RXON ;2 no : send XON then RET to AKEYREAD1 to process first char of new line.
-ACCEPTNEXT ADD #2,RSP ;1 replace XON_ret = AKEYREAD1 by XON_ret = SLEEP
- MOV #SLEEP,X ;2
- PUSHM #5,IP ;7 r-- ACCEPT_ret XOFF_ret YEMIT_ret 'BL' 'CR' bound XON_ret
-; ----------------------------------;
-
-; ----------------------------------;
-RXON ;
-; ----------------------------------;
- .IFDEF TERMINAL3WIRES ;
-RXON_LOOP BIT #UCTXIFG,&TERM_IFG ;3 wait the sending of last char, useless at high baudrates
- JZ RXON_LOOP ;2
- MOV #17,&TERM_TXBUF ;4 move char XON into TX_buf
- .ENDIF ;
- .IFDEF TERMINAL4WIRES ;
- BIC.B #RTS,&HANDSHAKOUT ;4 set RTS low
- .ENDIF ;
-; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;
-; starts first and 3th stopwatches ;
-; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^;
- RET ;4 to BACKGND (End of file download or quiet input) or AKEYREAD1 (get next line of file downloading)
-; ----------------------------------; ...or user defined
-
-; ----------------------------------;
-RXOFF ;
-; ----------------------------------;
- .IFDEF TERMINAL3WIRES ;
- MOV #19,&TERM_TXBUF ;4 move XOFF char into TX_buf
- .ENDIF ;
- .IFDEF TERMINAL4WIRES ;
- BIS.B #RTS,&HANDSHAKOUT ;4 set RTS high
- .ENDIF ;
- RET ;4 to ENDACCEPT, ...or user defined
-; ----------------------------------;
-
-; ----------------------------------;
- ASMWORD "SLEEP" ; may be redirected
-SLEEP MOV @PC+,PC ;3 Code Field Address (CFA) of SLEEP
-PFASLEEP .word BODYSLEEP ; Parameter Field Address (PFA) of SLEEP, with default value
-BODYSLEEP
- BIS &LPM_MODE,SR ;3 enter in LPMx sleep mode with GIE=1
-; ----------------------------------; default FAST FORTH mode (for its input terminal use) : LPM0.
-
-;###############################################################################################################
-;###############################################################################################################
-
-; ### # # ####### ####### ###### ###### # # ###### ####### ##### # # ####### ###### #######
-; # ## # # # # # # # # # # # # # # # # # # # #
-; # # # # # # # # # # # # # # # # # # # # # #
-; # # # # # ##### ###### ###### # # ###### # ##### ####### ##### ###### #####
-; # # # # # # # # # # # # # # # # # # # # #
-; # # ## # # # # # # # # # # # # # # # # # #
-; ### # # # ####### # # # # ##### # # ##### # # ####### # # #######
-
-;###############################################################################################################
-;###############################################################################################################
-
-
-; here, Fast FORTH sleeps, waiting any interrupt.
-; IP,S,T,W,X,Y registers (R13 to R8) are free for any interrupt routine...
-; ...and so PSP and RSP stacks with their rules of use.
-; remember: in any interrupt routine you must include : BIC #0x78,0(RSP) before RETI
-; to force return to SLEEP.
-; or (bad idea ? previous SR flags are lost) simply : ADD #2 RSP, then RET instead of RETI
-
-
-; ==================================;
- JMP SLEEP ;2 here is the return for any interrupts, else TERMINAL_INT :-)
-; ==================================;
-
-; **********************************;
-TERMINAL_INT ; <--- TEMR RX interrupt vector, delayed by the LPMx wake up time
-; **********************************; if wake up time increases, max bauds rate decreases...
-; (ACCEPT) part II under interrupt ; Org Ptr --
-; ----------------------------------;
- ADD #4,RSP ;1 remove SR and PC from stack, SR flags are lost (unused by FORTH interpreter)
- POPM #4,IP ;6 POPM W=buffer_bound, T=0Dh, S=20h, IP=AYEMIT_RET r-- ACCEPT_ret XOFF_ret
-; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;
-; starts the 2th stopwatch ;
-; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^;
-AKEYREAD MOV.B &TERM_RXBUF,Y ;3 read character into Y, UCRXIFG is cleared
-; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;
-; stops the 3th stopwatch ; 3th bottleneck result : 17~ + LPMx wake_up time ( + 5~ XON loop if F/Bds<230400 )
-; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^;
-AKEYREAD1 CMP.B S,Y ;1 printable char ?
- JHS ASTORETEST ;2 yes
- CMP.B T,Y ;1 CR ?
- JZ RXOFF ;2 then RET to ENDACCEPT
-; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;+ 4 to send RXOFF
-; stops the first stopwatch ;= first bottleneck, best case result: 27~ + LPMx wake_up time..
-; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^; ...or 14~ in case of empty line
- CMP.B #8,Y ;1 char = BS ?
- JNE WAITaKEY ;2 case of other control chars
-; ----------------------------------;
-; start of backspace ; made only by an human
-; ----------------------------------;
- CMP @PSP,TOS ; Ptr = Org ?
- JZ WAITaKEY ; yes: do nothing
- SUB #1,TOS ; no : dec Ptr
- JMP YEMIT1 ; send BS
-; ----------------------------------;
-; end of backspace ;
-; ----------------------------------;
-ASTORETEST CMP W,TOS ; 1 Bound is reached ?
- JZ YEMIT1 ; 2 yes: send echo then loopback
- MOV.B Y,0(TOS) ; 3 no: store char @ Ptr, send echo then loopback
- ADD #1,TOS ; 1 increment Ptr
-YEMIT1 ;
- BIT #UCTXIFG,&TERM_IFG ; 3 wait the sending end of previous char, useless at high baudrates
- JZ YEMIT1 ; 2 but there's no point in wanting to save time here:
-YEMIT2 ;
- .IFDEF TERMINAL5WIRES ;
- BIT.B #CTS,&HANDSHAKIN ; 3
- JNZ YEMIT2 ; 2
- .ENDIF ;
-YEMIT ; hi7/4~ lo:12/9~ send/send_not echo to terminal
- .word 4882h ; 4882h = MOV Y,&<next_adr>
- .word TERM_TXBUF ; 3
- mNEXT ; 4
-; ----------------------------------;
-AYEMIT_RET FORTHtoASM ; 0 YEMII NEXT address
- SUB #2,IP ; 1 reset YEMIT NEXT address to AYEMIT_RET
-WAITaKEY BIT #UCRXIFG,&TERM_IFG ; 3 new char in TERMRXBUF ?
- JNZ AKEYREAD ; 2 yes
- JZ WAITaKEY ; 2 no
-; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;
-; stops the 2th stopwatch ; best case result: 26~/22~ (with/without echo) ==> 385/455 kBds/MHz
-; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^;
-
-; ----------------------------------;
-ENDACCEPT ; --- Org Ptr r-- ACCEPT_ret
-; ----------------------------------;
- CMP #0,&LINE ; if LINE <> 0...
- JZ ACCEPTEND ;
- ADD #1,&LINE ; ...increment LINE
-ACCEPTEND SUB @PSP+,TOS ; -- len'
- MOV @RSP+,IP ; 2 return to INTERPRET with GIE=0: FORTH is protected against any interrupt...
-; ----------------------------------;
- MOV #LPM0+GIE,&LPM_MODE ; reset LPM_MODE to default mode LPM0 for next line of input stream
-; ----------------------------------;
- mNEXT ; ...until next falling down to LPMx mode of (ACCEPT) part1,
-; **********************************; i.e. when the FORTH interpreter has no more to do.
-
- .IFDEF DEFER_ACCEPT
-
-; CIB -- addr of Current Input Buffer
- FORTHWORD "CIB" ; constant, may be redirected as SDIB_ORG by OPEN.
-FCIB mDOCON ; Code Field Address (CFA) of FCIB
-PFACIB .WORD TIB_ORG ; Parameter Field Address (PFA) of FCIB
-
-; REFILL accept one line from input and leave org len of input buffer
-; : REFILL CIB DUP TIB_LEN ACCEPT ; -- CIB len shared by QUIT and [ELSE]
-REFILL SUB #6,PSP ;2
- MOV TOS,4(PSP) ;3
- MOV #TIB_LEN,TOS ;2
- MOV &PFACIB,0(PSP) ;5
- MOV @PSP,2(PSP) ;4
- JMP ACCEPT ;2
-
- .ELSE
-
-; REFILL accept one line from input and leave org len of input buffer
-; : REFILL TIB DUP TIB_LEN ACCEPT ; -- TIB len shared by QUIT and [ELSE]
-REFILL SUB #6,PSP ;2
- MOV TOS,4(PSP) ;3
- MOV #TIB_LEN,TOS ;2
- MOV #TIB_ORG,0(PSP) ;4
- MOV @PSP,2(PSP) ;4
- JMP ACCEPT ;2
-
- .ENDIF
-
-; ------------------------------------------------------------------------------
-; TERMINAL I/O, output part
-; ------------------------------------------------------------------------------
-
-;https://forth-standard.org/standard/core/EMIT
-;C EMIT c -- output character to the selected output device ; primary DEFERred word
- FORTHWORD "EMIT"
-EMIT MOV @PC+,PC ;3 Code Field Address (CFA) of EMIT
-PFAEMIT .word BODYEMIT ; Parameter Field Address (PFA) of EMIT, with its default value
-BODYEMIT MOV TOS,Y ; output character to the default output: TERMINAL
- MOV @PSP+,TOS ; 2
- JMP YEMIT1 ;9 12~
-
- .ENDIF ; HALFDUPLEX
-
-;Z ECHO -- connect terminal output (default)
- FORTHWORD "ECHO"
-ECHO MOV #4882h,&YEMIT ; 4882h = MOV Y,&<next_adr>
- MOV #0,&LINE ;
- mNEXT
-
-;Z NOECHO -- disconnect terminal output
- FORTHWORD "NOECHO"
-NOECHO MOV #NEXT,&YEMIT ; NEXT = 4030h = MOV @IP+,PC
- MOV #1,&LINE ;
- mNEXT
-
-;https://forth-standard.org/standard/core/SPACE
-;C SPACE -- output a space
- FORTHWORD "SPACE"
-SPACE SUB #2,PSP ;1
- MOV TOS,0(PSP) ;3
- MOV #20h,TOS ;2
- JMP EMIT ;17~ 23~
-
-;https://forth-standard.org/standard/core/SPACES
-;C SPACES n -- output n spaces
- FORTHWORD "SPACES"
-SPACES CMP #0,TOS
- JZ ONEDROP
- PUSH IP
- MOV #SPACESNEXT,IP
- JMP SPACE ;25~
-SPACESNEXT FORTHtoASM
- SUB #2,IP ;1
- SUB #1,TOS ;1
- JNZ SPACE ;25~ ==> 27~ by space ==> 2.963 MBds @ 8 MHz
-DROPEXIT MOV @RSP+,IP ;
-ONEDROP MOV @PSP+,TOS ; -- drop n
- mNEXT ;
-
-;https://forth-standard.org/standard/core/TYPE
-;C TYPE adr len -- type line to terminal
- FORTHWORD "TYPE"
-TYPE CMP #0,TOS
- JZ TWODROP ; abort fonction
- PUSHM #2,TOS ;4 R-- len,IP
- MOV #TYPE_NEXT,IP
-TYPELOOP MOV @PSP,Y ;2 -- adr x ; 30~ char loop
- MOV.B @Y+,TOS ;2
- MOV Y,0(PSP) ;3 -- adr+1 char
- SUB #2,PSP ;1 emit consumes one cell
- JMP EMIT ;15
-TYPE_NEXT FORTHtoASM
- SUB #2,IP ;1
- SUB #1,2(RSP) ;4 len-1
- JNZ TYPELOOP ;2
- POPM #2,TOS ;4 POPM IP,TOS
-TWODROP ADD #2,PSP ;
- MOV @PSP+,TOS ; --
- mNEXT ;
-
-;https://forth-standard.org/standard/core/CR
-;C CR -- send CR to the output device
- FORTHWORD "CR"
-CR MOV @PC+,PC ;3 Code Field Address (CFA) of CR
-PFACR .word BODYCR ; Parameter Field Address (PFA) of CR, with its default value
-BODYCR mDOCOL ; send CR to the default output device
- .word XSQUOTE
- .byte 2,13,10
- .word TYPE,EXIT