.{{
| .:.:-- TACHYON --:.:. |
|
|
A very fast and very small Forth byte code interpreter for the Propeller chip. 2012 Peter Jakacki |
TABLE OF CONTENTS
{ *** BYTECODE DEFINITIONS VECTOR TABLE *** }
{ *** HIGH LEVEL FORTH AREA *** }
{ *** HIGH LEVEL BYTECODE DEFINITIONS *** }
( *** 8 channel 8-bit PWM *** )
( *** 32 channel 8-bit PWM *** )
{ *** NUMBER PRINT FORMATTING *** }
{ *** STRING TO NUMBER CONVERSION *** }
{ *** COMPILER EXTENSIONS *** }
{ *** CONSOLE INPUT HANDLERS *** }
{ *** MAIN TERMINAL CONSOLE *** }
{ *** DICTIONARY in RAM and EEPROM *** }
{ *** RUNTIME BYTECODE INTERPRETER *** }
{ *** SERIAL I/O OPERATORS *** }
{ *** PASM MODULE LOADER *** }
{ *** BRANCH STACK HANDLER *** }
{ *** LOOP STACK HANDLER *** }
{ *** DATA STACK HANDLER *** }
{ *** RETURN STACK HANDLER *** }
Propeller .:.:--TACHYON--:.:. Forth V23131204.0300 NAMES: $564C...73B4 for 7528 CODE: $0000...495F for 11255 (0003 bytes added) CALLS: 0087 vectors free RAM: 3309 bytes free AUTORUN GO MODULES LOADED: 400A: NETWORK.fth WIZNET NETWORK SERVERS 131023.1500 39F2: W5200.fth WIZNET W5200 driver 131107.0000 3130: MULTIFILE.fth FAT32/16 MultiFile Layer V1.1 131207-0000 29F2: SDCARD.fth SD CARD Toolkit - 131114.0000 27AF: CE1372.fth CE1372 HARDWARE DEFINITIONS 131109.2340 17C1: EXTEND.fth Primary extensions to TACHYON kernel - 131124-0000 *** Tachyon Forth Network and File Server *** Mounted SD Card Media mounted as E2C4.0203 mkdosfs PBJTECH-SDL FAT32 Cluster size = 4,096 Sectors = 7,744,512 Listing directory PBJTECH-SDL CE1372 .FTH EXTEND .FTH FAVICON .ICO FAVICON .PNG HOME .HTM HTTP404 .HTM LOG0001 .TXT LOG0002 .TXT LOG0003 .TXT LOG0004 .TXT LOG0005 .TXT LOG0006 .TXT LOG0007 .TXT LOG0008 .TXT LOGON . LOGON .TXT MENU .TXT MULTIFAT.FTH NETWORK .FTH ROM .BIN SDCARD .FTH SYSLOG .TXT TACHYON .JPG TACHYON .LST W5200 .FTH Waiting for Ethernet to come up ... ready! ************ W5200 STATUS ************ LINK *UP* CHIP VER 0003 SRC IP 192.168.016.150. MASK 255.255.255.000. GATEWAY 192.168.016.001. MAC 00.08.03.52.40.72. *** SOCKETS *** 0: MODE= TCP PORT#00023 00000 TXRW=0000.0000.RXRW=0000.0000.RXSZ=0000.IR=00 STAT=14 LISTEN 1: MODE= TCP PORT#00021 00000 TXRW=0000.0000.RXRW=0000.0000.RXSZ=0000.IR=00 STAT=14 LISTEN 4: MODE= TCP PORT#00080 00000 TXRW=0000.0000.RXRW=0000.0000.RXSZ=0000.IR=00 STAT=14 LISTEN 5: MODE= TCP PORT#00081 00000 TXRW=0000.0000.RXRW=0000.0000.RXSZ=0000.IR=00 STAT=14 LISTEN 6: MODE= TCP PORT#00082 00000 TXRW=0000.0000.RXRW=0000.0000.RXSZ=0000.IR=00 STAT=14 LISTEN 7: MODE= TCP PORT#00083 00000 TXRW=0000.0000.RXRW=0000.0000.RXSZ=0000.IR=00 STAT=14 LISTEN * WEB, FTP, and TELNET servers running * ---------------------------------------------------------------- |
V2
2 bytes - vectored CALL opcode + byte index into 512 entry table
Flexible SPI or I2C PASM code support words in kernel Construct fast serial drivers with minimal code
Access loop indices outside of definitions allows efficient factoring Avoids manipulation and corruption of return stack Static stack arrays for direct addressing of stack items Intrinsically safe data stack overflow and underflow - optional error reporting
Empty loops can execute in 550ns to 875ns (absolute worst case) Two to one stack operations ( + * AND etc) inc opcode fetch take 900ns to 1.087us (absolute worse case) *
|
( Link to color coded expanded map with file and network )
NORMAL MEMORY MAP WITH KERNEL + FULL EXTEND.fth
.STATS
NAMES: $5D21...741A for 5881 (3448 bytes added)
CODE: $0000...333E for 7237 (6846 bytes added)
CALLS: 0484 vectors free
RAM: 10723 bytes free
.MAP
REGISTERS x16
0024: .. .. .. .. 02 .. .. .. .. .. .. 01 02 03 02 ..
VECTORS x64
0124: 0E 11 14 10 13 17 15 0F 14 10 17 11 14 14 16 17
0524: 11 .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
KERNEL CODE x256
0928: 64 5F 87 89 60 58 61 6F 70 69 6F 60 65 71 63 3F
CODE x256
1880: 66 58 43 45 5D 77 69 62 69 65 63 66 6E 5F 58 64
2880: 6B 86 7F 6A 53 6F 51 71 65 64 57 .. .. .. .. ..
SPARE @333E: for 10,723
DICTIONARY x256
5D21: 53 68 6F 69 63 66 60 60 61 5F 5C 55 54 47 41 44
6D21: 4C 53 5A 51 57 6F 5A 0F 70 6E 7B 81 6D 90 1E ..
SPARE @7423: for 221
BUFFERS x64
7500: 1C 1D 1D 1C 17 1E 1A 1C 1B 1F 1E 21 1D 1E 21 24
7900: 1F 19 1E 1B 17 27 28 24 20 0C 01 .. .. .. .. ..
RX BUFFER x64
7D00: .. 20 23 1D 1E 04 1E ..
HUB STACK x16
7F60: .. 01 .. .. .. .. .. .. .. ..
SPINNERET MEMORY MAP (WHEN FULLY LOADED WITH EXTEND,HEADERS,SDFILE,W5200,NETWORK,APP)
2014/07/16 15:23:01
NAMES: $5626...7442 for 7708 (-586 bytes added)
CODE: $0000...48CD for 10929 (2433 bytes added)
CALLS: 0132 vectors free
RAM: 3417 bytes free
MODULES LOADED:
3F4C: EASYNET.fth WIZNET NETWORK SERVERS 140615.2300
38EA: W5100.fth WIZNET W5100 driver 140615.0000
2FC7: EASYFILE.fth FAT32 Virtual Memory Access File System Layer V1.1 140626-2100
2AF0: SDCARD.fth SD CARD Toolkit - 140626.1400
2A24: EPRINT.fth Stores PRINT strings in EEPROM 140626.0000
28A1: SPINNERET.fth Spinneret + W5100 HARDWARE DEFINITIONS 131204.1200
1881: EXTEND.fth Primary extensions to TACHYON kernel - 140703-14OO
BOOT: EXTEND.boot
POLL: ?EASYNET
BOOT: EASYNET
.MAP
REGISTERS x16
0024: .. 01 .. .. 02 .. .. .. .. .. .. .. 03 02 .. ..
VECTORS x64
0124: 10 0F 16 0F 17 13 10 17 13 14 12 13 14 12 15 14
0524: 16 18 18 17 15 16 18 19 1A 16 18 18 .. .. .. ..
KERNEL CODE x256
0928: 64 5D 89 85 63 61 5D 6F 6E 69 72 61 60 71 74 40
CODE x256
1881: 66 55 55 6E 76 76 5E 5B 65 6A 68 83 65 61 60 6E
2881: 54 6B 55 2D 7D 7B 86 38 4F 2E 6A 3B 6D 61 69 7C
3881: 6C 51 63 79 73 72 83 5B 7F 59 84 6C 83 7D 74 87
4881: 83 41 .. .. .. .. .. .. .. .. .. .. .. 30 66 63
SPARE @4A04: for 3,070
DICTIONARY x256
5602: 63 66 5F 5E 5A 58 55 55 5A 54 6F 67 65 66 63 5E
6602: 5F 5D 58 54 4B 42 43 4A 4D 5A 52 54 6B 5D 21 0B
SPARE @744B: for 181
BUFFERS x64
7500: 06 05 .. .. .. .. .. .. .. .. .. .. .. .. .. ..
7900: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
RX BUFFER x64
7D00: 01 0F 0E 0E 0D 0E 0F 0E
HUB STACK x16
7F60: .. 01 01 .. .. .. .. .. .. ..
-------------------- WHAT'S NEW --------------------
CHANGELOG
}}
DAT
version long 23_140704,2130
{{
V2.3
140717 Renamed CROP to MYOP to allow for user programmable opcode - not ever used in kernel or EXTEND
Optimising STREND and CMPSTR
disabled these additions and started a V2.4 kernel
140704 Added CROP opcode, just set a mask in COGREG3 once instead of accumulatively slower "constant AND"
140617 Modified behaviour of SDRD so that it could scan for a special character and report it's first position and count within the block
140616 Added STROBE instruction to pulse an I/O mask low for 100n
140606 Moved some words to EXTEND
Added a bytecode analyser module for MJB to work out which bytecodes are being used the most
140602 Optimized fast constants - COMPILE still not sorted out - removed fast constants 7,6,5, added 8. Added >N and >B for fast masking
140520 Added a header for BCOMPILE as "COMPILE" so that I can implement CREATE DOES structures - might still have to make some adjustments
140415 Fix CREATE$ so that it only requires a normal string and disable immediate atr
140320 Added test for 5 or 10MHz crystal operation
140318 Moved improved .S word back into kernel so DEBUG also uses this
Fixed NUMBER so that it does not require a counted string (use any string)
Float console TXD after data sent to allow other cogs to also transmit - may require a pullup on some boards!!!
140311 Removed 8 channel PWM
140304 Modified operation of NEWCNT etc. Now uses LAP which returns delta in REG3
140302 Modified CMPSTR must add dummy flag, LOADMOD now accepts dest as well
140301 Added REPS, WAITHILO
140228 Added kuroneko's version of the PWM32 module - runs faster to 7.6kHz
Changed VM kernel instruction return from a hard #doNEXT address to a soft unext which can be modified for tasking & debugging
140227 Moved ABS and U/ to pasm kernel, added -NEGATE pasm instruction (speeds up signed divide from 47us down to 18us)
Various pasm instructions optimized by kuroneko, mainly using the carry flag - CMOVE <CMOVE CMPSTR 1+ 1- > 0=
140227 Optimized a few more operations as recommended by kuroneko. I also decided to add a +FIB and optimize LOOP (faster)
140225 Optimized SETZ operation using method suggested by kuroneko
140216 Changed temp reg for counting brace depth to REG+11 rather than REG+15 as that location is used by LAPCNT
140203 Add NFAs for ERROR and NOTFOUND - removed NFAs for MARK and UNMARK
140130 Added lastkey variable which serial receiver sets so apps can bypass buffer to look for "special keys"
140128 Changed / to U/ (and added a signed divide to EXTEND)
140126 Fixed bug caused by numpadsz begin reduced to 39, needed to ensure alignments so added 4 bytes and removed vga longs
140122 Fixed bug in KEY? which was skipping KEYPOLL when no key was found
131230 Added flag to enforce PRIVATE attribute setting
131212 Fixed echo problem with delimiters (they would always echo even if echo was disabled, caused problem with network layer)
131207 Added IFDEF
131204 Fixed KEY? to follow ukey vector if set.
131130 Preping kernel to remove CMPSTR as a permanent PASM instruction (hybrid HLL or RUNMOD)
Removed VARIABLE, TABLE, CONSTANT creation words and moved to EXTEND
131125 Optimized SEARCH and FINDSTR - fixed so that "find" vector will only be called on a negative result
131124 Testing nested { } comments
131116 V2.3 which includes an SPI VM instruction with it's own mask registers - in addition to RUNMOD
V2.2
131115 V2.2 introduces the hybrid data stack with the top 4 elements as fixed cog registers and an overflow stack in hub RAM
V2.1
131107 If "ok" has been revectored then don't emit a space after a line has been accepted
131105 Included dictionary entries for ['] [NFA'] and >VEC
131029 Added an "accept" word variable that can be used to revector the "ok" confirmation - zero is default
130910 IDLE loop includes a small delay as this cuts normal run current from 65ma to 23ma
"prompt" word variable now executes user code at prompt rather than just echo a character
130812 Add COGINIT support for PASM objects
130811 Optimized serial receive to allow full buffered throughput at 2M baud (one stop bit between characters)
Removed FILL from cog kernel and implemented as a fast CMOVE operation instead (13ms/32K vs 6.5ms)
130714 Adding support for strings
130705 COGID moved to cog kernel
130611 Improved the CASE structures - it's a little more C like now but with improved control
130611 Fixed bug with COLD which was not freeing up the vector table fully
Optimized some kernel words ( - * FILL etc )
130610 Added a ?NEGATE kernel instruction to speed up some operations
130605 Modified the CLOCK instruction which is used by I2C routines to use COGREG 4 as the clock mask rather than 0 to prevent conflict with SPI routines which may be operating at the same time.
130520 Removed <BEGIN, UNITL>, and AGAIN> as they are not really used. This frees up some valuable cog space.
Added !RP as a native bytecode instruction rather than a RUNMOD and init RP in terminal loop
130412 Added !RP to allow return stack to be reset to prevent overflow etc
130411 Added CONSOLE word to allow quitting from an application back into the console mode
130404 Added missing headers for >UPPER
130227 Implement a simple CASE structure
130224 Add 16 as a fast constant but create separate code and integrate BL (32) as well.
130216 Active revectoring for KEY word using ukey
130214 Add symbol processing so that a : in a number will shift the current number left by one byte so that IP addresses can be entered like #192:168:0:1
Further improved this with a & prefix so the alternate method can use the standard period between decimal bytes - &192.168.0.1
130110 Include DEL key ($7F) as a backspace as many tablet keyboards don't support a real backspace (it seems)
130107 Added , word to compile longs (complements | and || which compile bytes and words)
121214 Fixed a small bug with DEBUG itself which also involved the STACKS entry in the dictionary.
121114 Needed a fast <CMOVE but to squeeze it in I have generalized the CMOVE (now pCMOVE) instruction to accept preconditioning
Now CMOVE passes an increment of 1 while <CMOVE adjusts the src and dst and passes an increment of -1
Moved some instructions such as > and ROT back into the first page rather than using XOPs (there's plenty of room)
V2.1 Added XOP opcode to fetch the next bytecode to directly index an "opcode" in the high 256 long bank of the cog's kernel.
Move slower and less used opcodes to high bank. Space freed up by removing jmp to high half in some opcodes
Add ESPIO module for enhanced SPIO operations. SSD module added for fast SSD2119 SPI operations
121105 Optimizing kernel
121027 Modified interrupts to use a global interrupt request in hub RAM and for each cog to have it's own interrupt mask and inhibit
Also the return stack is held in hub RAM with each cog having it's own space. Now each VM knows it's identity at boot.
121025 Added high level interrupt capability for up to 32 interrupts. See INTERRUPT for more information
121013 2 things to speed up compilation:
- Add number preprocessor so that numbers prefixed with #$% are processed immediately rather than after a dictionary search.
- Improved FINDSTR method to skip the dictionary entry using the count byte
Add BUFFERS word which points to the 2K VM image (reusable as temporary buffers - do not backup as Tachyon will not be able to boot)
121012 Modify serial break detect to check for floating inputs by pulsing the pin high for 1us then reading 1us later. If it is still low then it is counted as part of a break.
Strongly rearrange the memory allocation to place Tachyon VM image at top of RAM along with the dictionary so that the dictionary builds down and only uses memory as needed. The 2K RAM where the Tachyon VM image resides is usable as buffers etc as long as this area is not backed up.
The dictionary is being broken up into a RAM resident kernel dictionary with names arranged in some order so that frequently referenced words are located earlier. The extension and user dictionary will be separate and searched after the kernel dictionary or if an alternate method is specified then this will be used instead such as paging the dictionary from SD card.
Before searching the dictionary if the word begins with a %, #, or $ and ends in a digit then it will be processed as a number. Doing so will speed up number processing during compilation.
121010 Merge HS-SerialRxL into the Tachyon source and rearrange the memory map so that all task registers, vectors, and the dictionary etc are placed at the end for simplified backup. The serial rxbuffer has been dropped down to 512 bytes and overwrites it's image. BACKUP area is immediately after this buffer from task registers onwards.
121003 Fixed small bug (again) when executing RESET by moving intialization of task variables to a cold start
Removed WCALL and replaced with XCALL,YCALL,ZCALL,VCALL - updated compile time words
Finally got around to adding capability to handle a sign on numbers (must be the first character)
120928 Fixed small bug when executing RESET by moving intialization of task variables to a cold start
Added ZCALL and VCALL which are simple extensions of XCALL and YCALL which use 1K following the XCALLs table for vectors
- This probably means that there is no need for WCALL as it is highly unlikely that more will be needed due to memory limits
120919 Removed PLOT from cog kernel and implemented a LOADMOD version implemented in EXTEND - 9 longs now freed up in cog
Added [PWM!] module for faster PWM table updates (233us)
120917 Expanded area for dictionary (still need to optimise all this)
Modified ' (TICK) to force compilation of a literal so that it can be used inside of a definition
Added [COMPILE] word to force compilation of the next word in case it's an immediate
Added linenumber support for source code loading
120914 Modified SET and CLR to use MUXC operation rather than self-modifying code
Added BIT! which will SET or CLR depending upon the state ( mask addr flg -- )
120913 Added PWM module to support table based 8 channel 8-bit PWM
120912 Removed FONT from precompiled source. Leave VGA support as is although not activated.
VER now returns the address of the string of version numbers. So VER C@ will return the major version number
.VER works like the old VER in that it printed out the complete version information
120910 Modified CMPSTR so that it does not modify the dict parameter.
Modified FINDSTR and CREATEWORD to handle count byte in names.
Added dummy count bytesto all precompiled dictionary entries - run FIXDICT on cold to calculate and fix them up
Baud rate default set to 921600 for greater compatibility with terminals and Bluetooth
120908 Slight mods to adapt to HS-SerialRxL which has a larger buffer and symbol decoding and filtering
120906 V2B variant with stacked DO..LOOP and FOR..NEXT
plus added stacked branches <BEGIN..UNTIL> and <BEGIN..AGAIN> without affecting V1 structures
120905 Started Tachyon V2 which uses a MARK stack for storing loop and repeat branches.
Just using the return stack for testing LOOPs as present
<Refer to V1 for older changelogs>
To do:
Check signed operations
For the latest links:
Introduction to TACHYON Forth (including other links at the bottom of the page)
}}
DAT
baudrate long baud ' The baudrate is now stored as a variable
'intreqs long 0
'intvecs word 0[32]
CON
_clkmode = xtal1 + pll8x ' <------------ change to suit (don't change for 10 or 5MHz)
_xinfreq = 10_000_000 ' <------------ change to suit other crystals (leave this set to 10 for 5MHz)
baud = 230400 ' <-- change - tested from 300 baud to 3M baud
bufsize = 512 ' Serial rx buffer - reuses serial receive cog code image
extstk = $7F60 ' locate external data stack at end (overflows but it's ROM)
' Standard Port assignments
scl = 28
sda = 29
txd = 30
rxd = 31
' Stack sizes
datsz = 4 ' expands into hub RAM
retsz = 22
loopsz = 8
branchsz = 6
CON
numpadsz = 43 ' We really only need a large buffer for when long binary numbers with separators are used
wordsz = 39 ' any word up to 37 characters (1 count, 1 terminator)
tasksz = 8 ' 8 bytes/task RUN[2] FLAG[1]
' flags
echo = 1
linenums = 2 ' prepend line number to each new line
ipmode = 4 ' interpret this number in IP format where a "." separates bytes
leadspaces = 4
prset = 8 ' private headers set as default
striplf = $10 ' strip linefeeds from output if set
sign = $20
comp = $40 ' force compilation of the current word - resets each time
defining = $80
DAT
' Allocate storage memory for buffers and other variables
' Task registers can be switched easily by setting "regptr" ( 7 COGREG! )
' New tasks may only need room for the first 32 or 72 bytes
registers long 0[64] 'Variables used by kernel + general-purpose
org 0
' register offsets within "registers". Access as REG,delim ... REG,base ... etc
'
' Minimum registers required for a new task - other registers after the ' ---- are not needed other than by the console
temp res 12 ' general purpose
cntr res 4 ' hold CNT or temp
' @16
uemit res 2
ukey res 2
keypoll res 2 ' poll user routines - low priority background task
unum res 2 ' User number processing routine - executed if number failed and UNUM <> 0
baudcnt res 4 ' baud cnt value where baud = clkfreq/baudcnt
uswitch res 4 ' target parameter used in CASE structures
' @32
padwr res 1 ' write index (builds characters down from lsb to msb in MODULO style)
numpad res numpadsz ' Number print format routines assemble digit characters here
' @72
wordcnt res 1 ' length of current word (which is still null terminated)
wordbuf res wordsz ' words from the input stream are assembled here
' @112
' ----
' LONG aligned registers (can be used for bytes and words also)
'execloc res 4 ' used by EXECUTE to store bytecodes
'colorptr res 4 '
'pixelptr res 4
tasks res tasksz*8
anumber res 4 ' Assembled number from input
bnumber res 4
digits res 1 ' number of digits in current number that has just been processed
dpl res 1 ' Position of the decimal point if encountered (else zero)
' WORD aligned registers
findvec res 2 ' runs extended dictionary search if set after failing precompiled dictionary search
createvec res 2 ' If set will execute user create routines rather than the kernel's
rxptr res 2 ' Pointer to the terminal receive buffer - read & write index precedes
rxsz res 2
corenames res 2
res 2 ' backup of names used at start of TACHYON load
unames res 2
names res 2 ' start of dictionary (builds down)
prevname res 2 ' temp location used by CREATE
res 2
here res 2 ' pointer to compilation area (overwrites VM image)
codes res 2 ' current code compilation pointer (updates "here" or is reset by it)
cold res 2 ' pattern to detect if this is a cold or warm start (A55A )
autovec res 2 ' user autostart address if non-zero - called from within terminal
errors res 2
linenum res 2
' Unaligned registers
delim res 2 ' the delimiter used in text input and a save location
base res 2 ' current number base + backup location during overrides
lasttwo res 2 ' last two characters (looking for sequences)
prompt res 2 ' pointer to code to execute when Forth prompts for a new line
accept res 2 ' pointer to code to execute when Forth accepts a line to interpret (0=ok)
spincnt res 1 ' Used by spinner to rotate busy symbol
flags res 2
prefix res 1 ' NUMBER prefix
suffix res 1 ' NUMBER suffix
prevch res 1 ' used to detect LF only sequences vs CRLF to perform auto CR
lastkey res 1
endreg res 0
{
Kernel bytecode definitions need to be called and this table makes it easy to do so
with just a 2 byte call. Extra memory may be allocated for user definitions as well
The Spin compiler requires longs whereas we only need 16-bit words but this will do
at present. The runtime compiler can reuse the high-word of all these longs and compile
a YCALL rather than an XCALL so that the high-word is used instead
Also another table has been added to expand the call vectors up to 1024 entries.
}
DAT
org 0 ' ensure references can be reduced to a single byte index to be called by XCALL xx
'
XCALLS
xXCALLS long @_XCALLS+s
'xEXEC long @registers+execloc+s
'xMIN long @_MIN+s
'xMAX long @_MAX+s
xSETQ long @SETQ+s
xLT long @LT+s
xZLT long @ZLT+s
xULT long @ULT+s
xWITHIN long @WITHIN+s
xFILL long @FILL+s
xERASE long @ERASE+s
xms long @ms+s
xus long @us+s
'xJ long @J+s
'xK long @K+s
'xIX long @IX+s
xCOMMENT long @COMMENT+s
xBRACE long @BRACE+s
xCURLY long @CURLY+s
xIFNDEF long @IFNDEF+s
xIFDEF long @IFDEF+s
xPRTHEX long @PRTHEX+s
xPRTBYTE long @PRTBYTE+s
xPRTWORD long @PRTWORD+s
xPRTLONG long @PRTLONG+s
xPRTSTK long @PRTSTK+s
xPRTSTKS long @PRTSTKS+s
xDEBUG long @DEBUG+s
xDUMP long @DUMP+s
xCOGDUMP long @COGDUMP+s
xREBOOT long @_REBOOT+s
xSTOP long @STOP+s
xLOADMOD long @aLOADMOD+s
xSSD long @SSD+s
xESPIO long @ESPIO+s
xSPIO long @SPIO+s
xSPIOD long @SPIOD+s
xSDRD long @SDRD+s
xSDWR long @SDWR+s
'xSDRDSCAN long @SDRDSCAN+s
xPWM32 long @PWM32+s
xPWMST32 long @PWMST32+s
xPLOT long @PLOT+s
xBCA long @_BCA+s
'xCMOVE long @CMOVE+s
xRCMOVE long @RCMOVE+s
xCLS long @CLS+s
xEMIT long @EMIT+s
xSPACE long @SPACE+s
xBELL long @BELL+s
xCR long @CR+s
xOK long @OK+s
xSPINNER long @SPINNER+s
xBIN long @BIN+s
xDECIMAL long @DECIMAL+s
xHEX long @HEX+s
xKEYQ long @KEYQ+s
xREADBUF long @READBUF+s
xKEY long @KEY+s
xQEMIT long @QEMIT+s
'xTOUPPER long @TOUPPER+s
xPUTCHAR long @PUTCHAR+s
xPUTCHARPL long @PUTCHARPL+s
xSCRUB long @SCRUB+s
xdoCHAR long @doCHAR+s
xGETWORD long @GETWORD+s
xTICK long @TICK+s
xATICK long @ATICK+s
xNFATICK long @NFATICK+s
x_NFATICK long @_NFATICK+s
xTOVEC long @TOVEC+s
xPFA long @PFA+s
xSETPOLL long @SETPOLL+s
xIFEXIT long @IFEXIT+s
xSEARCH long @SEARCH+s
xFINDSTR long @FINDSTR+s
xEXECUTE long @EXECUTE+s
xGETVER long @GETVER+s
xPRTVER long @PRTVER+s
xTODIGIT long @TODIGIT+s
xNUMBER long @NUMBER+s
xTERMINAL long @TERMINAL+s
xCONSOLE long @CONSOLE+s
x_NUMBER long @_NUMBER+s
xGRAB long @GRAB+s
xRESFWD long @RESFWD+s
xATPAD long @ATPAD+s
xHOLD long @HOLD+s
xTOCHAR long @TOCHAR+s
xRHASH long @RHASH+s
xLHASH long @LHASH+s
xHASH long @HASH+s
xHASHS long @HASHS+s
x_STR long @_STR+s
xPSTR long @PSTR+s
xPRTSTR long @PRTSTR+s
xSTRLEN long @STRLEN+s
xUPRT long @UPRT+s
xPRT long @PRT+s
xPRTDEC long @PRTDEC+s
xLITCOMP long @LITCOMP+s
xBCOMP long @BCOMP+s
xBCOMPILE long @BCOMPILE+s
xCOMPILE long @COMPILE+s
xCCOMP long @CCOMP+s
xWCOMP long @WCOMP+s
xLCOMP long @LCOMP+s
xSTACKS long @STACKS+s
x_STR_ long @_STR_+s
x_PSTR_ long @_PSTR_+s
x_IF_ long @_IF_+s
x_ELSE_ long @_ELSE_+s
x_THEN_ long @_THEN_+s
x_BEGIN_ long @_BEGIN_+s
x_UNTIL_ long @_UNTIL_+s
x_AGAIN_ long @_AGAIN_+s
x_REPEAT_ long @_REPEAT_+s
xMARK long @MARK+s
xUNMARK long @UNMARK+s
xVECTORS long @VECTORS+s
xTASK long @TASK+s
xIDLE long @IDLE+s
xALLOT long @ALLOT+s
xALLOCATED long @ALLOCATED+s
xATO long @Ato+s
xATATR long @ATATR+s
xCOLON long @COLON+s
x_COLON long @_COLON+s
xPUBCOLON long @PUBCOLON+s
xPRIVATE long @PRIVATE+s
xENDCOLON long @ENDCOLON+s
xCOMPILES long @COMPILES+s
xCREATE long @CREATE+s
xCREATEWORD long @CREATEWORD+s
xCREATESTR long @CREATESTR+s
xAddACALL long @AddACALL+s
xHERE long @_HERE+s
xNFACFA long @NFACFA+s
x_TACHYON long @_TACHYON+s
xERROR long @ERROR+s
xNOTFOUND long @NOTFOUND+s
xDISCARD long @DISCARD+s
xAUTORUN long @AUTORUN+s
xFREE long @FREE+s
xAUTOST long @AUTOST+s
xFIXDICT long @FIXDICT+s
xBUFFERS long @BUFFERS+s
xCOLDST long @COLDST+s
xSWITCH long @_SWITCH+s
xSWITCHFETCH long @SWITCHFETCH+s
xISEQ long @ISEQ+s
xIS long @IS+s
xISEND long @ISEND+s
xISWITHIN long @ISWITHIN+s
xInitStack long @InitStack+s
xSPSTORE long @SPSTORE+s
xDEPTH long @_DEPTH+s
xCOGINIT long @aCOGINIT+s
'''xSETPLL long @SETPLL+s
' NOTE: this table is limited to 1024 word entries but leave room for extensions and user application to use the rest of these
xLAST long 0[512-xLAST] ' Reserve the rest of the area possible
long 0 ' plus one extra for termination
CON
loadsz = 19
DAT
org $01F0-loadsz ' fixed address - high-level code can always assume RUNMOD' cog origin is here
pRUNMOD
_RUNMOD res loadsz
DAT
' Emit a character
' EMIT ( char -- )
EMIT byte REG,uemit,WFETCH,QDUP,_IF,01,AJMP ' execute user EMIT if vector is non-zero
byte XOP,pEMIT,EXIT
' Print inline string
PRTSTR byte RPOP
pstlp byte CFETCHINC,QDUP,_IF,04,XCALL,xEMIT,_AGAIN,@pstret-@pstlp
pstret byte PUSHR,EXIT
{ debug print routines - also used by DUMP etc }
' .HEX ( n -- ) print nibble n as a hex character
PRTHEX ' ( n -- ) print n (0..$0F) as a hex character
byte toNIB
byte PUSH1,$30,PLUS
byte DUP,PUSH1,$39,GT,_IF,03,_BYTE,7,PLUS 'Adjust for A..F
PRTCH byte XCALL,xEMIT,EXIT
' .BYTE ( n -- ) print n as 2 hex characters
PRTBYTE byte DUP,_4,_SHR
byte XCALL,xPRTHEX,XCALL,xPRTHEX,EXIT
' .WORD ( n -- ) print n as 4 hex characters
PRTWORD byte DUP,_8,_SHR
byte XCALL,xPRTBYTE
PW1 byte XCALL,xPRTBYTE
PW2 byte EXIT
' .LONG ( n -- ) print n as 8 hex characters
PRTLONG byte DUP,PUSH1,16,_SHR
byte XCALL,xPRTWORD
byte _BYTE,".",XCALL,xEMIT
PRL1 byte XCALL,xPRTWORD
PRL2 byte EXIT
' Deprecated in favour of BDUMP in EXTEND.fth - still used internally but renamed to HDUMP to avoid conflict
' DUMP ( addr cnt -- ) Hex dump of hub RAM - (NOTE: if CFETCH is vectored then other memory can be accessed)
DUMP byte ADO
byte XCALL,xCR
byte I,XCALL,xPRTWORD
byte XCALL,xPRTSTR,": ",0
byte I,PUSH1,$10,ADO
byte I,CFETCH,XCALL,xPRTBYTE
byte PUSH1,$20,XCALL,xEMIT,LOOP
byte XCALL,xPRTSTR," ",0
byte I,PUSH1,$10,ADO
byte I,CFETCH,DUP,BL,XCALL,xLT,OVER,PUSH1,$7E,GT,_OR
byte _IF,03,DROP,PUSH1,"."
byte XCALL,xEMIT,LOOP
byte PUSH1,$10,PLOOP
byte XCALL,xCR,EXIT
' COGDUMP ( addr cnt -- ) Dump cog memory, but try to minimize stack usage
COGDUMP byte REG,temp,WSTORE,REG,temp+2,WSTORE,JUMP,@cdm2-@cdmlp
cdmlp byte REG,temp+2,WFETCH,_3,_AND,ZEQ,_IF,@cdm3-@cdm2
cdm2 byte XCALL,xCR,REG,temp+2,WFETCH,XCALL,xPRTWORD,XCALL,xPRTSTR,": ",0
cdm3 byte REG,temp+2,WFETCH,XOP,pCOGFETCH,XCALL,xPRTLONG,BL,XCALL,xEMIT
byte _1,REG,temp+2,WPLUSST,MINUS1,REG,temp,WPLUSST
byte REG,temp,WFETCH,ZEQ,_UNTIL,@cdm1-@cdmlp
cdm1 byte EXIT
PRTSTK
byte REG,base,CFETCH,PUSHR,XCALL,xDECIMAL
byte XCALL,xPRTSTR," Data Stack (",0,XCALL,xDEPTH,XCALL,xPRT,_BYTE,")",XCALL,xEMIT
byte XCALL,xDEPTH,_IF,@pstk2-@pstk3
pstk3 byte _16,XOP,pCOGREG,XOP,pCOGFETCH,_8,PLUS,XCALL,xDEPTH,_SHL1,_SHL1,OVER,PLUS
pstklp byte XCALL,xCR,_BYTE,"$",XCALL,xEMIT,DUP,FETCH,DUP,XCALL,xPRTLONG
byte XCALL,xPRTSTR," - ",0,XCALL,xPRT
byte _4,MINUS,OVER,OVER,EQ,_UNTIL,@pstk1-@pstklp
pstk1 byte DROP2
pstk2 byte RPOP,REG,base,CSTORE,EXIT
{
' .S Print out the top four numbers of the datastack
PRTSTK byte XCALL,xPRTSTR,$0D,$0A,"STACK: ",0
byte FOURTH,XCALL,xPRTLONG,XCALL,xSPACE
byte THIRD,XCALL,xPRTLONG,XCALL,xSPACE
byte OVER,XCALL,xPRTLONG,XCALL,xSPACE
byte DUP,XCALL,xPRTLONG,XCALL,xSPACE
byte EXIT
}
STACKS byte _0,XOP,pCOGREG,_BYTE,tos-REG0,PLUS,EXIT ' put the address of tos on the top of the stack
PRTSTKS ' Print stacks but avoid cluttering with data from debug routines
' byte XCALL,xSTACKS,PUSH1,datsz ' ( datstk siz )
' byte XCALL,xPRTSTR,$0D,$0A,"DATA STACK ",0
' byte XCALL,xCOGDUMP
byte XCALL,xPRTSTK
' RETURN STACK
byte XCALL,xSTACKS,PUSH1,datsz,PLUS,PUSH1,retsz
byte XCALL,xPRTSTR,$0D,$0A,"RETURN STACK ",0
byte XCALL,xCOGDUMP
' LOOP STACK
byte XCALL,xSTACKS,PUSH1,datsz+retsz,PLUS,PUSH1,loopsz
byte XCALL,xPRTSTR,$0D,$0A,"LOOP STACK ",0
byte XCALL,xCOGDUMP
' BRANCH STACK
byte XCALL,xPRTSTR,$0D,$0A,"BRANCH STACK ",0
byte XCALL,xSTACKS,PUSH1,datsz+retsz+loopsz,PLUS,PUSH1,branchsz
byte XCALL,xCOGDUMP
byte EXIT
' Print the stack(s) and dump the registers - also called by hitting <ctrl>D during text input
DEBUG byte XCALL,xPRTSTKS
byte XCALL,xPRTSTR,$0D,$0A,"REGISTERS",0
byte REG,temp,_WORD,1,00,XCALL,xDUMP
byte XCALL,xPRTSTR,$0D,$0A,"COMPILATION AREA",0
byte REG,here,WFETCH,PUSH1,$40,XCALL,xDUMP
byte EXIT
' COG CONTROL
_REBOOT ' REBOOT
byte _BYTE,$FF
' 000011 00, 0i 1111 dd, ddddddd s, ssssssss
byte _LONG,%000011_00,%01_1111_00,%0000000_0,%00000000
addstk byte _WORD,(tos+1)>>8,tos+1,_BYTE,9,_SHL,PLUS ' insert the address of tos+1 as D field into the PASM instruction
byte XOP,pPASM,DROP,EXIT
STOP ' STOP ( cog -- )
' 000011_00, 01 1111 ddddddddd
byte _LONG,%000011_00,%01_1111_00,%0000000_0,%00000011
byte _WORD,(tos+1)>>8,tos+1,_BYTE,9,_SHL,PLUS
byte XOP,pPASM,DROP2,EXIT
' HERE ( -- addr ) Address of next compilation location
_HERE byte REG,here,WFETCH,EXIT
' There are at 18 longs reserved in the VM for a code module which can be loaded
' Greyed out code is not implemented
{ TO DO:
Remove the dedicated CMPSTR instruction from the kernel and only load it as a PASM module when performing a full
source file load using TACHYON and END directives. So CMPSTR will be loaded with the TACHYON directive but otherwise
CMPSTR will be available as high-level function otherwise where compilation speed is not needed.
The TACHYON directive will also turn off echo and ok prompts as well as stop other TF cogs and keypoll/alarm functions.
pub CMPSTR ( src dict -- src dict+ flg )
tach C@ IF pCMPSTR
ELSE
DUP >L
BEGIN OVER C@ OVER C@ = WHILE 1+ SWAP 1+ SWAP REPEAT
OVER C@ DUP 0= SWAP $7F > OR
ROT DROP L> ROT ROT ( src dict+ flg )
THEN
;
Compare a null-terminated source string with a dictionary string which is 8th bit terminated.
This will always force a mismatch after which one is checked for a null while the other is checked
for the 8th bit and if verified then a match has been found.
The dict pointer is advanced to point to the end of the dict string on the 8th bit termination which
is the attribute byte as in: byte "CMPSTR",$80,CMPSTR
}
{
org _RUNMOD
' CMPSTR ( src dict -- src dict+ flg ) Compare strings at these two addresses
pCMPSTR
' CMPSTR ( src dict -- src dict+ flg ) Compare strings at these two addresses
_CMPSTR call #_PUSHX ' make room for a flag
mov R2,tos+1
mov X,tos+2 ' X = source
cmpstrlp rdbyte R0,X ' read in a character from the source
rdbyte R1,R2 ' read in from the dictionary
cmp R1,R0 wz ' are they the same?
if_nz jmp #nomatch
add X,#1 ' so far, so good, try next
add R2,#1 ' updates the dict pointer on the stack too
jmp #cmpstrlp ' keep at it
nomatch cmp R0,#1 wc ' was the src null terminated? (kuroneko method)
' xor R1,#$80
if_c test R1,#$80 wc ' set z flag if dict 8th bit set
jmp #SETZ
CMPSTRMOD byte _WORD,(@_CMPSTR+s)>>8,@_CMPSTR+s,_BYTE,14,XOP,pLOADMOD,EXIT
{ Implement CMPSTR as a RUNMOD
' CMPSTR ( src dict flg-- src dict+ flg ) Compare strings at these two addresses
mov R2,tos+1 wc ' force nc on entry, s[31] == 0 (doNEXT clears c)
cmpstrlp1 mov X,tos+2 ' X = source
cmpstrlp rdbyte R0,X ' read in a character from the source
add X,#1 ' hub has to wait anyway so get ready for next source byte
if_c add R2,#1 ' updates the copy of the dictionary pointer
rdbyte R1,R2 ' read in from the dictionary
cmp R1,R0 wz ' are they the same?
if_z jmpret par,#cmpstrlp wc,nr ' keep at it, set carry to enable dict+ (for local copy)
nomatch cmp R0,#1 wc ' was the src null terminated? (C means we have matched the string)
if_c test R1,#$80 wc ' set c flag if dict 8th bit set (C = cross-matched)
if_c jmp #SETZ ' Change our flag to -1 if C set (matched)
cmpnext rdbyte R1,R2
add R2,R1
add R2,#4
rdbyte R1,R2 wz wc
if_nz jmp cmpstrlp1
jmp #SETZ
FINDSTR
fstlp byte _0,XOP,pCMPSTR ' ( src dict flg )
byte _IF,@nxtword-@fst1 ' found it ( src dict )
fst1 byte DUP,XCALL,xNFACFA,DEC,CFETCH
byte _BYTE,sm,_AND,ZEQ,_IF,@nxtword-@fst0
fst0 byte NIP,EXIT ' ( nfaptr ) found
'
' Skip the attribute byte and codes and test for end of dictionary (entry = 00)
nxtword ' ( src dict ) advance past atr+codes to try next. (atr(1),bytecode)
byte CFETCHINC,PLUS,_3,PLUS ' jump over CFA to next NFA
' ( src dict ) dict points to bc1
byte DUP,CFETCH,ZEQ,_UNTIL,@fst2-@fstlp
' end of dictionary reached
fst2 byte DROP2,_FALSE,EXIT
}
}
{
org _RUNMOD
' Write byte to I2C bus
' COGREGS: 0=scl 1=sda
' I2CWR ( send -- ack ) ( A: miso mosi sck )
_I2CWR mov R1,#8
i2cwrlp andn OUTA,sck ' clock low
or DIRA,mosi ' make sure data is an output
shl sdat,#1 wc ' msb first
muxc OUTA,mosi ' send next bit of data out
call #i2cdly
or OUTA,sck ' clock high
djnz R1,#i2cwrlp
andn OUTA,sck ' get ready to read ack
andn DIRA,mosi ' float SDA
or OUT,sck
jmp unext
ic2dly nop
i2cdly_ret ret
I2CWR byte _WORD,(@_I2CWR+s)>>8,@_I2CWR+s,_BYTE,16,XOP,pLOADMOD,EXIT
}
{ *** ENHANCED SERIAL PERIPHERAL INPUT OUTPUT MODULE ***
}
org _RUNMOD
' COGREGS: 0=sck 1=mosi 2=miso 3=cnt 4=cs
' ESPIO ( send -- receive ) ( A: miso mosi sck )
_ESPIO
andn OUTA,scs ' chip select low
mov R1,#32
sub R1,scnt
shl tos,R1 ' left justify transmit data
mov R1,scnt
ESPIOlp andn OUTA,sck ' clock low
shl sdat,#1 wc ' assume msb first
test miso,INA wz ' test data from device while clock is low
muxc OUTA,mosi ' send next bit of data out
if_nz or sdat,#1 ' now assemble data (also setup time for mosi)
or OUTA,sck ' clock high
djnz R1,#ESPIOlp
tjnz scs,#ESxt ' leave with clock high if CS mask specified
andn OUTA,sck ' leave with clock low
ESxt or OUTA,scs ' chip select high
jmp unext
ESPIO byte _WORD,(@_ESPIO+s)>>8,@_ESPIO+s
aLOADMOD byte _WORD,_RUNMOD>>8,_RUNMOD,_BYTE,loadsz,XOP,pLOADMOD,EXIT
org _RUNMOD
' SSD module added for fast SSD2119 SPI operations
' COGREGS: 0=sck 1=mosi 2=miso 3=cnt 4=mode
' SSD ( send cnt -- ) ( A: miso mosi sck )
_SSD
SSDREP mov X,tos+1
shl X,#7 ' left justify but leave msb zero (control byte)
call #SSD8
call #SSD8D
call #SSD8D
djnz tos,#SSDREP
jmp #DROP2
SSD8D or X,#1 ' move 1 into msb = data
ror X,#1
SSD8 andn OUTA,scs ' chip select low
mov R1,scnt
SSDlp andn OUTA,sck ' clock low
shl X,#1 wc ' assume msb first
muxc OUTA,mosi ' send next bit of data out
or OUTA,sck ' clock high
djnz R1,#SSDlp
or OUTA,scs ' chip select high
SSD8D_ret
SSD8_ret ret
SSD byte _WORD,(@_SSD+s)>>8,@_SSD+s
byte XCALL,xLOADMOD,EXIT
{ FAST SPI MODE - FORUM CODE
entry
' Prepare video PLL, NCO runs @6.25MHz (4..8) which gives us 25MHz PLL clock.
movs ctra, #CLK ' output pin
movi ctra, #%0_00010_101 ' PLL (video + pin), VCO/4
mov frqa, frqx ' 6.25MHz * 16 / 4 = 25MHz
' Set frame format, 8 pixels or bits, 1 bit lasts a PLL clock.
movd vscl, #1 << (12 - 9) ' 1 clock per pixel
movs vscl, #8 ' 8 clocks per frame
' Start and connect video h/w.
movd vcfg, #vgrp ' pin group
movs vcfg, #vpin ' pins
movi vcfg, #%0_01_0_00_000 ' VGA, 2 colour mode
' Setup done. Do something with it.
mov dira, mask ' drive outputs
' Frame time is 80/25*8 clock cycles. Too close for hub window usage but
' relaxed enough for looping.
:loop waitvid bits, data ' data[0]
rol data, #8
waitvid bits, data ' data[1]
rol data, #8
waitvid bits, data ' data[2]
rol data, #8
waitvid bits, data ' data[3]
rol data, #8
add data, #1
jmp #:loop
' initialised data and/or presets
bits long $0000FF00
frqx long $14000000 ' 6.25MHz
mask long |< CLK | |< DTA
' uninitialised data and/or temporaries
data res 1 ' requires setup
fit
CON
zero = $1F0 ' par (dst only)
vpin = |< (DTA & %111) ' pin group mask
vgrp = DTA / 8 ' pin group
DAT
}
{ *** SERIAL PERIPHERAL INPUT OUTPUT MODULE ***
Transfers 1 to 32 bits msb first
Transmit data must be left justified ready for transmission
Receive data is in correct format
Data is shifted in and out while the clock is low
The clock is left low between transfers
}
org _RUNMOD
' COGREGS: 0=sck 1=mosi 2=miso 3=cnt
' SPIO ( send -- receive ) ( A: miso mosi sck )
_SPIO mov R1,scnt
or DIRA,mosi
SPIOlp andn OUTA,sck ' clock low
shl sdat,#1 wc ' assume msb first
test miso,INA wz ' test data from device while clock is low
muxc OUTA,mosi ' send next bit of data out
if_nz or sdat,#1 ' now assemble data (also setup time for mosi)
or OUTA,sck ' clock high
djnz R1,#SPIOlp
andn OUTA,sck ' leave with clock low
jmp unext
SPIO byte _WORD,(@_SPIO+s)>>8,@_SPIO+s
byte XCALL,xLOADMOD,EXIT
org _RUNMOD
' COGREGS: 0=sck 1=mosi 2=miso 3=cnt
' COGREG4 = delay
' SPIOD ( send -- receive ) ( A: miso mosi sck )
_SPIOD mov R1,scnt
or DIRA,mosi
SPIODlp andn OUTA,sck ' clock low
shl sdat,#1 wc ' assume msb first
test miso,INA wz ' test data from device while clock is low
muxc OUTA,mosi ' send next bit of data out
if_nz or sdat,#1 ' now assemble data (also setup time for mosi)
or OUTA,sck ' clock high
mov X,REG4
spiodly djnz X,#spiodly
djnz R1,#SPIODlp
andn OUTA,sck ' leave with clock low
jmp unext
SPIOD byte _WORD,(@_SPIOD+s)>>8,@_SPIOD+s
byte XCALL,xLOADMOD,EXIT
{
org _RUNMOD
' L6470 ( send -- receive ) ( A: miso mosi sck )
_L6470 mov R1,scnt
or DIRA,mosi
L6470lp andn OUTA,sck ' clock low
test miso,INA wz ' test data from device while clock is low
shl sdat,#1 wc ' assume msb first
muxc OUTA,mosi ' send next bit of data out
if_nz or sdat,#1 ' now assemble data (also setup time for mosi)
or OUTA,sck ' clock high
djnz R1,#L6470lp
jmp unext
L6470 byte _WORD,(@_L6470+s)>>8,@_L6470+s
byte XCALL,xLOADMOD,EXIT
}
{ Deprecated for test scanning version
org _RUNMOD
' Reads in a block from the SD card - 512 bytes takes 1.44ms or 2.92us/byte
' SDRD ( dst cnt -- ;read block from SD into memory )
_SDRD or OUTA,mosi ' keep data in high
or DIRA,mosi
SDRDlp mov R1,#8
SDRDbits andn OUTA,sck ' clock low
test miso,INA wc ' test data from device while clock is low
rcl R0,#1 ' now assemble data (also setup time for mosi)
or OUTA,sck ' clock high
djnz R1,#SDRDbits
andn OUTA,sck ' leave with clock low
wrbyte R0,tos+1 ' write byte to destination
add tos+1,#1 ' dst = dst+1
djnz tos,#SDRDlp
jmp #DROP2
' Load the SD read module (13us)
SDRD byte _WORD,(@_SDRD+s)>>8,@_SDRD+s
byte XCALL,xLOADMOD,EXIT
}
' SDRD ( dst char -- firstPos charcnt ;read block from SD into memory while scanning for special char )
' dst is a 32 bit SD-card address 0..4GB
' NOTE: ensure MOSI is set as an output high from caller by 1 COGREG@ OUTSET
org _RUNMOD
_SDRD
mov X,tos+1 ' dst --> X
mov R2,dst1 '$200 ' R2 = 512 bytes
SDRDSClp mov R1,#8 ' 8-bits
SDRDSCbits andn OUTA,sck ' clock low
test miso,INA wc ' test data from device while clock is low
rcl R0,#1 ' now assemble data (also setup time for mosi)
or OUTA,sck ' clock high
djnz R1,#SDRDSCbits
andn OUTA,sck ' leave with clock low
wrbyte R0,X ' write byte to destination
and R0,#$FF ' only want to compare a byte value
cmp R0,tos wz ' compare byte with char unsigned for equality
if_z add ACC,#1 ' found one and increment count
if_z cmp ACC,#1 wz ' if match and count = 1 (first occurrence)
if_z mov tos+1,X ' then save dst (tos+2) to firstpos (tos)
add X,#1 ' dst = dst+1
djnz R2,#SDRDSClp ' next
call #POPX ' discard "char"
jmp #PUSHACC ' push char count and clear ACC
' Load the SDSCAN read module (13us)
SDRD byte _WORD,(@_SDRD+s)>>8,@_SDRD+s
byte XCALL,xLOADMOD,EXIT
{
' moved to SDCARD.fth MJB
WORD scancnt,scanpos
BYTE scanch
--- Read in one block of 512 bytes from SD to dst
pri (SDRD) ( dst -- )
\ BYTE scanch holds the char to scan for
\ gives number of character matches found in WORD scancnt
\ autoincrements on each call, use scancnt W~ to init to 0 if needed
\ position of first match in WORD scanpos
[SDRD] 1 COGREG@ OUTSET scanch C@ RUNMOD
scancnt W+! scanpos W@ 0= IF scanpos W! ELSE DROP THEN ' to catch the earliest over many block reads - use scanpos W~ to init
;
}
' SD MODULE
org _RUNMOD
' Write a block to the SD card - 512 bytes
' SDWR ( src cnt -- ;write block from memory to SD )
_SDWR
or DIRA,mosi
SDWRlp rdbyte R0,tos+1 ' read next byte from source
add tos+1,#1 ' increment source address
shl R0,#24 ' left justify
mov R1,#8 ' send 8 bits
SDWRbits andn OUTA,sck ' clock low
shl R0,#1 wc ' assume msb first
muxc OUTA,mosi ' send next bit of data out
or OUTA,sck ' clock high
djnz R1,#SDWRbits
andn OUTA,sck ' leave with clock low
djnz tos,#SDWRlp ' next byte
jmp #DROP2
' Load the SD write module
SDWR byte _WORD,(@_SDWR+s)>>8,@_SDWR+s
byte XCALL,xLOADMOD,EXIT
{ DISABLE THIS MODULE!!! USE 32 CHANNEL VERSION INSTEAD
' PWM RUNTIME MODULE
{ PWM functions - 8 channel table based - any start pin but 8 channels are on consecutive pins
REG0 = table pointer
REG1 = pwmpins
2014-02-27 - kuroneko - timing mods to reduce jitter etc
timing: The initial waitcnt delay is kept minimal, subsequent pin updates follow with deltaT. The structure used
is as follows:
mov temp, skip
add temp, cnt
...
waitcnt temp, deltaT
For minimal timing (14 cycles) skip has to be 5. Anything between add and waitcnt needs to be taken care
of by skip. We have 5 normal instructions (5*4) and one hubop (8..23) which brings the worst case total
to 5+5*4+23.
Test init for channel 2 on P2
TABLE pwm #256 ALLOT
\ Set the frequecy
#5000 PWMFREQ
\ Start up PWM using from P0 for 8 channels
0 8 pwm RUNPWM
#50 % 2 PWM! \ just test a square wave on P2 for jitter
}
org _RUNMOD
_PWM mov cnt,#5{14}+5*4+23 ' minimal entry delay
add cnt,cnt
pwmlp mov X,REG0 ' read base address of table
pwmofs add X,#0_0{0..255} ' add in read index
add pwmofs,#1
andn pwmofs,#$100
rdbyte X,X
shl X,REG1 ' shift up to pwmpins
waitcnt cnt,tos
mov outa,X ' update outputs
jmp #pwmlp
PWM byte _WORD,(@_PWM+s)>>8,@_PWM+s
byte XCALL,xLOADMOD,EXIT
' PWM! SETUP TABLE MODULE
org _RUNMOD
' _PWM! ( duty8 mask table -- )
_PWMST mov R2,#0
pwmstlp rdbyte X,tos
andn X,tos+1 ' clear the bit anyway
cmp R2,tos+2 wc ' is duty8 > R2
if_c or X,tos+1
wrbyte X,tos
add R2,#1
add tos,#1
and R2,#$FF
tjnz R2,#pwmstlp
jmp #DROP3
' [PWM!]
PWMST byte _WORD,(@_PWMST+s)>>8,@_PWMST+s
byte XCALL,xLOADMOD,EXIT
}
' [PWM32] loads the PWM32 module into the cog ready for a RUNMOD
PWM32 byte _WORD,(@_PWM32+s)>>8,@_PWM32+s
byte XCALL,xLOADMOD,EXIT
' kuroneko's 7.6kHz version of the 32 channel PWM module
org _RUNMOD ' Compile for RUNMOD area in cog
_PWM32 movi ctrb, #%0_11111_000 ' LOGIC.always
mov frqb, tos+1 ' table base address
shr frqb, #1{/2} ' divided by 2 (expected to be 4n)
mov cnt,#5{14}+2*4+23 ' minimal entry delay
add cnt,cnt
mov phsb,#0 ' preset (not optional, 8n)
pwmlp32 and phsb,wrap32 ' $0400 to $0000 transition |
rdlong X,phsb ' read from index + 2*table/2 | need to be back-to-back
waitcnt cnt,tos ' |
mov outa,X ' update outputs
add phsb,#4 ' update read ptr |
rdlong X,phsb ' read from index + 2*table/2 | need to be back-to-back
add phsb,#4 ' update read ptr
waitcnt cnt,tos ' |
mov outa,X ' update outputs
jmp #pwmlp32
wrap32 long 256 * 4 -1
' PWM32! SETUP TABLE MODULE
' this module is run from the controlling cog whereas the PWM32 runtime is in a dedicated cog
org _RUNMOD
' _PWM32! ( duty8 mask table -- ) ' 104us (LOADMOD takes 18.8us)
_PWMST32 mov R2,#256 ' scan through all 256 slices
add tos,endtbl ' start from end (optimize hub access)
add tos+2,#1 ' compensate for cmp op so that $80 = 50 %, $100 = 100 %
pwmstlp32 rdlong X,tos ' read one 1/256 slice of 32 channels
cmp R2,tos+2 wc ' is duty8 > R2
muxc X,tos+1 ' set or clear the bit(s)
wrlong X,tos ' update slice
sub tos,#4 ' next long
djnz R2,#pwmstlp32 ' terminate on wrap
jmp #DROP3
endtbl long 256 * 4 -4 ' offset to last long of table
' [PWM32!] Load the PWM32! module which is used to setup the PWM table
PWMST32 byte _WORD,(@_PWMST32+s)>>8,@_PWMST32+s
byte XCALL,xLOADMOD,EXIT
' PLOT MODULE
' Used for VGA/TV or LCD graphics
' pixshift is always a multiple of two, 512 pixels/line = 6 etc
'
org _RUNMOD
' PLOT ( x y -- )
_PLOT shl tos,pixshift ' n^2 bytes/Y line
mov X,tos+1
shr tos+1,#3 ' byte offset in line
add tos,tos+1 ' byte offset in frame
add tos,pixeladr ' byte address in memory
and X,#7 ' get bit mask
mov tos+1,#1
shl tos+1,X
jmp #SET
' [PLOT]
PLOT byte _WORD,(@_PLOT+s)>>8,@_PLOT+s
byte XCALL,xLOADMOD,EXIT
{
CLKSET
(c) Copyright 2009 Philip C. Pilgrim (propeller@phipi.com)
see end of file for terms of use.
This object determines whether a 5MHz or 10MHz crystal is
being used and sets the PLL accordingly for 80MHz operation.
The main program should use the following settings:
_clkmode = xtal1 + pll8x
_xinfreq = 10_000_000
}
org _RUNMOD
_setpll movi ctra,#%0_00001_011 'Set ctra for pll on no pin at x1.
movi frqa,#%0100_0000_0 'Set frqa for clk / 4 (= 20MHz w/ 10MHz crystal, i.e. too high).
add pllx,cnt 'Give PLL time to lock (if it can) and stabilize.
waitcnt pllx,#0
movi vcfg,#$40 'Configure video for discrete pins, but no pins enabled.
mov vscl,#$10 'Set for 16 clocks per frame.
waitvid 0,0 'Wait once to clear time.
neg pllx,cnt 'Read -cnt.
waitvid 0,0 'Wait 16 pll clocks = 64 processor clks.
add pllx,cnt 'Compute elapsed time.
cmp pllx,#$40 wz 'Is it really 64 clocks?
if_z mov pllx,#$6f ' Yes: Crystal is 5MHz. Set clock mode to XTAL1 and PLL16X.
if_z clkset pllx
if_z wrbyte $-2, #4 'update clkmode location
jmp unext
pllx long $1_0000 '65536 clks for pll to stabilize.
'( BYTE CODE ANALYSER - 140606 - temporary, testing bytecode frequencies with MJB )
org _RUNMOD
' bytecode analyser
BCARUN ' of course you are faster than me ;-) --- BUT IT'S JUST A QUICK HACK,
' it compiles so maybe you would like to test it, just clear BUFFERS for 1K, BCA, and you are AWAY
' and then a nice listing with the names of the words ;-) --- yes, could scan headers for bytecode match
' I'll try !!!
' have to add a RUNMOD to it, or something....not crashing but not working..fixed to b
mov unext,bcavec
bca
rdbyte instr,IP 'read byte code instruction
add IP,#1 wc 'advance IP to next byte token (clears the carry too!)
mov R0,instr ' only process single byte opcodes
shl R0,#2 ' address longs
add R0,bcabuf ' in bcabuf (1K at BUFFERS)
rdlong X,R0 ' increment the counter for this bytecode
add X,#1
wrlong X,R0
jmp instr 'execute the code by directly indexing the first 256 long in cog
bcavec long bca
bcabuf long @RESET+s ' use BUFFERS (normally at $7500) buffers uses kernel image from RESET
' Load the bytecode analyser module
_BCA byte _WORD,(@bcarun+s)>>8,@bcarun+s ' fixed to point to bcarun not bca
byte XCALL,xLOADMOD,XOP,pRUNMOD,EXIT
' !SP - Initialize the data stack to a depth of zero
InitStack
byte _0,_BYTE,depth-REG0,XOP,pCOGREG,XOP,pCOGSTORE,EXIT ' zero the depth index
' SP! ( addr -- )
' Assign a data stack pointer for this cog - depth depends on use - typically 8 to 32 longs required
SPSTORE byte _BYTE,stkptr-REG0,XOP,pCOGREG,XOP,pCOGSTORE,EXIT
' DEPTH ( -- levels )
_DEPTH byte _BYTE,depth-REG0,XOP,pCOGREG,XOP,pCOGFETCH,_SHR1,_SHR1,DEC,EXIT
' BUFFERS ( -- addr )
BUFFERS byte _WORD,(@RESET+s)>>8,@RESET+s,EXIT
' COGINIT ( code pars cog -- ret )
aCOGINIT byte SWAP,_4,_SHL,_OR,SWAP,_BYTE,18,_SHL,_OR,XOP,_COGINIT,EXIT
' @PAD ( -- addr ) pointer to current position in number pad
ATPAD byte REG,padwr,CFETCH,REG,numpad,PLUS,EXIT
' HOLD ( char -- )
HOLD byte MINUS1,REG,padwr,CPLUSST,XCALL,xATPAD,CSTORE,EXIT
' >CHAR ( val -- ch ) convert binary value to an ASCII character
TOCHAR byte PUSH1,$3F,_AND,PUSH1,"0",PLUS,DUP,PUSH1,"9" ' convert to "0".."9"
byte GT,_BYTE,7,_AND,PLUS ' convert to "A"..
byte DUP,PUSH1,$5D,GT,ZEXIT,_3,PLUS,EXIT ' skip symbols to go to "a"..
' #> ( n1 -- caddr )
RHASH byte DROP,XCALL,xATPAD,_BYTE,leadspaces,REG,flags,CLR,EXIT
' <# ' resets number pad write index to end of pad
LHASH byte PUSH1,numpadsz,REG,padwr,CSTORE,_0,XCALL,xHOLD
byte EXIT
' # ( n1 -- n2 ) convert the next ls digit to a char and prepend to number string
HASH byte DUP,_IF,@has1-@has2
has2 byte REG,base,CFETCH,UDIVMOD,SWAP,XCALL,xtoCHAR
byte XCALL,xHOLD,EXIT
' conversion digits exhausted, use zeros or spaces
has1 byte _BYTE,$30,_BYTE,leadspaces,REG,flags,XCALL,xSETQ,_IF,02,DROP,BL,XCALL,xHOLD,EXIT
' #S ( n1 -- 0 ) Convert all digits
HASHS byte XCALL,xHASH,DUP,ZEQ,_UNTIL,06,EXIT
' STREND ( str -- strend )
'STREND byte CFETCHINC,QDUP,_IF,06,DUP,_BYTE,$7E,GT,_UNTIL,10,EXIT
STRLEN ' ( str -- len )
byte DUP,STREND,SWAP,MINUS,EXIT
' STR ( -- n ) Leave address of inline string on stack and skip to next instruction
_STR byte RPOP,DUP
STRlp byte CFETCHINC,ZEQ,_UNTIL,04,PUSHR,EXIT
' . ( n -- ) Print the number off the stack
PRT byte DUP,XCALL,xZLT,_IF,05,PUSH1,$2D,XCALL,xEMIT,NEGATE
' U. ( n -- ) Print an unsigned number
UPRT byte XCALL,xLHASH,XCALL,xHASHS,XCALL,xRHASH
' .STR ( adr -- ) Print the null or 8th bit terminated string
PSTR byte CFETCHINC,DUP,ZEQ,OVER,_BYTE,$7F,GT,_OR,_IF,02,DROP2,EXIT,XCALL,xEMIT,_AGAIN,16
PRTDEC byte _BYTE,10
' DEPPRECATED - only used by END - use .NUM from EXTEND.fth
' B. ( n base -- ) Print the number off the stack in the base specified
basePRT byte REG,base,CFETCH,PUSHL,REG,base,CSTORE
byte XCALL,xLHASH,XCALL,xHASH,XCALL,xHASH,XCALL,xHASH,XCALL,xHASHS,XCALL,xRHASH,XCALL,xPSTR
byte LPOP,REG,base,CSTORE,EXIT
' 0< ( n -- flg )
ZLT byte _0,XCALL,xLT,EXIT
' < ( n1 n2 -- flg )
LT byte SWAP,GT,EXIT
' U<
ULT byte OVER,OVER,_XOR,XCALL,xZLT,_IF,04
byte NIP,XCALL,xZLT,EXIT
byte MINUS,XCALL,xZLT,EXIT
' ( n lo hi -- flg ) true if n is within range of low and high inclusive
WITHIN byte INC,OVER,MINUS,PUSHR
byte MINUS,RPOP,XCALL,xULT
WT1 byte ZEQ,ZEQ,EXIT
' SET? ( mask caddr -- flg ) Test single bit of a byte in memory
SETQ byte CFETCH,_AND,ZEQ,ZEQ,EXIT
' ?EXIT ( flg -- ) Exit if flg is true
IFEXIT byte _IF,02,RPOP,DROP,EXIT
'' The read and write index is stored as two words preceding the buffer, read this as a word (faster)
'' BKEY ( buffer -- ch ) ' byte size buffer is preceded with a read index, go and read the next character
'
' READBUF ( buffer -- ch )
READBUF
byte DUP,DEC,DEC,DUP,WFETCH ' point to read index ( buffer writeptr writeindex )
byte SWAP,DEC,DEC,WFETCH,SWAP ' ( buffer readindex writeindex )
byte OVER,EQ,_IF,03,DROP2,_0,EXIT ' empty, return with null
' ( buffer read )
byte OVER,PLUS,CFETCH ' get character from buffer
' perform auto LF to CR subs (but not when it is part of a CRLF )
byte DUP,_BYTE,$0A,EQ
byte REG,prevch,CFETCH,_BYTE,$0D,EQ,ZEQ,_AND
byte _3,_AND,PLUS ' convert the LF to a CR
'
byte DUP,REG,prevch,CSTORE
byte _WORD,$01,00,PLUS ' get char ( buffer [buffer+read]+$100 )
byte SWAP,_4,MINUS ' key readptr )
byte DUP,WFETCH,INC,REG,rxsz,WFETCH,DEC,_AND,SWAP,WSTORE ' update read index and wrap
byte EXIT
' Check the serial input stream for a key
' KEY? ( -- ch flg )
KEYQ
byte REG,ukey,WFETCH,QDUP,_IF,03,ACALL,DUP,EXIT ' execute user code if set
byte REG,rxptr,WFETCH,XCALL,xREADBUF ' read a char from the buffer
byte DUP,toBYTE,SWAP,_8,_SHR,ZEQ,ZEQ ' convert to char and flag format
byte DUP,ZEQ,ZEXIT ' skip user keypoll if we are busy with a key
byte REG,keypoll,WFETCH,QDUP,_IF,01,ACALL ' do a user keypoll if nothing else is happening
byte EXIT
DOPOLL byte REG,keypoll,WFETCH,QDUP,_IF,01,ACALL ' execute background polling while waiting for input
' KEY ( -- ch )
KEY byte REG,ukey,WFETCH,QDUP,_IF,01,AJMP ' redirect key input to ukey vector?
byte REG,rxptr,WFETCH,XCALL,xREADBUF,QDUP,_UNTIL,@ky1-@DOPOLL
ky1 byte toBYTE,EXIT ' reduce buffer output to a character
' \ ( -- )
' Ignore following text till the end of line.
' IMMED
COMMENT byte XCALL,xKEY,_BYTE,$0D,EQ,_UNTIL,07,XCALL,xCR,EXIT
' ( stack or other short inline comments )
BRACE byte XCALL,xKEY,DUP,XCALL,xQEMIT,_BYTE,")",EQ,_UNTIL,10,EXIT
IFDEF byte XCALL,xNFATICK,ZEQ,JUMP,02
IFNDEF byte XCALL,xNFATICK,ZEXIT
CURLY byte _1,REG,11,CSTORE ' allow nesting by counting braces
CURLYlp byte XCALL,xKEY ' keep reading each char until we have a matching closing brace
byte DUP,_BYTE,"{",EQ,_IF,04,_1,REG,11,CPLUSST ' add up opening braces
byte _BYTE,"}",EQ,_IF,04,MINUS1,REG,11,CPLUSST ' count down closing braces
byte REG,11,CFETCH,ZEQ,_UNTIL,@CURLYxt-@CURLYlp
CURLYxt byte EXIT
{ 140606 moved to EXTEND
' J ( -- index ) Index of next outer loop
J byte _3,LSTACK,XOP,pCOGFETCH,EXIT
' K ( -- index ) Index of third loop
K byte _BYTE,5,LSTACK,XOP,pCOGFETCH,EXIT
' Fetch index of FOR..NEXT loop (or the limit for a DO..LOOP)
IX byte _0,LSTACK,XOP,pCOGFETCH,EXIT
}
' <CMOVE ( src dst cnt -- ) byte move in reverse from the ends to the start
RCMOVE byte ROT,OVER,PLUS,DEC,ROT,THIRD,PLUS,DEC,ROT,XOP,pRCMOVE,EXIT
' ( addr cnt -- )
ERASE byte _0
' ( addr cnt fillch -- )
FILL byte THIRD,CSTORE,DEC,OVER,INC,SWAP,XOP,pCMOVE,EXIT
' ms ( n -- ) Wait for n milliseconds
ms byte QDUP,ZEXIT,PUSH3,$01,$38,$80,UMMUL,DROP,DELTA,EXIT
' us ( n -- ) Wait for n microseconds (note- not accurate for small amounts)
us byte QDUP,ZEXIT,PUSH1,80,UMMUL,DROP,DELTA,EXIT
' change the default number bases
BIN byte _2,JUMP,@SetBase-@DECIMAL
DECIMAL byte PUSH1,10,JUMP,@SetBase-@HEX
HEX byte PUSH1,16
SetBase byte REG,BASE,CSTORE,EXIT
CLS byte PUSH1,$0C,XCALL,xEMIT,EXIT
SPACE byte BL,XCALL,xEMIT,EXIT
BELL byte _BYTE,7,XCALL,xEMIT,EXIT
CR byte PUSH1,$0D,XCALL,xEMIT,PUSH1,$0A,XCALL,xEMIT,EXIT
SPINNER byte REG,spincnt,CFETCH,_3,_SHR,_3,_AND,XCALL,x_STR,"|/-\",0,PLUS,CFETCH
byte XCALL,xEMIT,_8,XCALL,xEMIT,_1,REG,spincnt,CPLUSST,_1,XCALL,xms,EXIT
' PROMPT
OK byte XCALL,xPRTSTR," ok",$0D,$0A,0,EXIT
' ?EMIT ,( ch -- ) suppress emitting the character if echo flag is off
QEMIT byte _BYTE,echo,REG,flags,XCALL,xSETQ,_IF,03,XCALL,xEMIT,EXIT,DROP,EXIT
{
' >UPPER ( str1 -- ) Convert lower-case letters to upper-case
TULP byte INC
TOUPPER
byte DUP,CFETCH,QDUP,_IF,@TUX-@TU1 ' end of string?
TU1 byte _BYTE,"a",_BYTE,"z",XCALL,xWITHIN
byte _UNTIL,@TU2-@TULP
TU2 byte _BYTE,-$20,OVER,CPLUSST,_AGAIN,@TUX-@TULP
TUX byte DROP,EXIT
}
' functional test for now - optimize later
' Convert ASCII value as a digit to a numeric value - only interested in bases up to 16 at present
'
TODIGIT ' ( char -- val true | false )
byte DUP,PUSH1,"0",PUSH1,"9",XCALL,xWITHIN,_IF,@td8-@td7 ' only work with 0..9,A..F
td7 byte PUSH1,"0",MINUS,_TRUE,EXIT ' pass decimal digits
td8 byte DUP,PUSH1,"A",PUSH1,"F",XCALL,xWITHIN,_IF,@td2-@td1
td1 byte PUSH1,$37,MINUS,_TRUE,EXIT ' pass hex digits
td2 byte DROP,_FALSE,EXIT
{ Try to convert a string to a number
Allow all kinds of symbols but these are the rules for it to be treated as a number.
1. Leading character must be either a recognized prefix or a decimal digit
2. If trailing character is a recognized suffix then the first character must be a decimal digit
Acceptable forms are:
$1000 hex number
1000h
#1000 decimal number
1000d
%1000 binary number
1000b
Also as long as the first character and last character are valid (0..9,prefix,suffix) then any symbols me be mixed in the number i.e.
11:59 11.59 #5_000_000
}
_NUMBER ' ( str -- value digits | false )
byte _0,REG,4,STORE ' REG0L = 0
byte _BYTE,sign,REG,flags,CLR ' clear sign
snlp byte DUP,CFETCH,REG,prefix,CSTORE ' save prefix (it may or may not be)
byte DUP,STREND,DEC,CFETCH,REG,suffix,CSTORE ' save suffix (assume string has count byte)
byte DUP,CFETCH,_BYTE,"-",EQ,_IF,@sn01-@sn00 ' save SIGN
sn00 byte _BYTE,sign,REG,flags,SET,INC
sn01
' PREFIX HANDLER
byte DUP,CFETCH ' check prefix
' ( str ch )
byte _FALSE ' preset prefix flag = false
byte OVER,PUSH1,"$",EQ,_IF,04,XCALL,xHEX,_TRUE,_OR ' as does $ - also set hex base
byte OVER,PUSH1,"#",EQ,_IF,04,XCALL,xDECIMAL,_TRUE,_OR ' as does # - also set decimal base
byte OVER,PUSH1,"%",EQ,_IF,04,XCALL,xBIN,_TRUE,_OR ' as does % - also set binary base
byte OVER,PUSH1,"&",EQ,_IF,@pf1-@pf0 ' as does & - also set decimal base and IP notation
pf0 byte XCALL,xDECIMAL,_TRUE,_OR
byte _BYTE,$80,REG,bnumber+3,CSTORE ' this forces "." symbols to work the same as ":"
pf1 byte DUP,_IF,04,ROT,INC,ROT,ROT ' adjust string pointer to skip prefix
' ( str ch flg )
byte SWAP,PUSH1,"0",PUSH1,"9",XCALL,xWITHIN,_OR ' 0..9 forces processing as a number
' ( str flg )
byte ZEQ,_IF,03,DROP,_FALSE,EXIT ' Give up now, it isn't a candiate
' ( str ) ' so far, so good, now check suffix
' SUFFIX HANDLER
byte REG,suffix,CFETCH
byte DUP,PUSH1,"0",PUSH1,"9",XCALL,xWITHIN ' 0..9
byte OVER,PUSH1,"A",PUSH1,"F",XCALL,xWITHIN,_OR ' A..F ( str sfx flg ) true if still a digit
byte OVER,PUSH1,"h",EQ,_IF,04,XCALL,xHEX,_TRUE,_OR ' h = HEX
byte OVER,PUSH1,"b",EQ,_IF,04,XCALL,xBIN,_TRUE,_OR ' b = BINARY
byte SWAP,PUSH1,"d",EQ,_IF,04,XCALL,xDECIMAL,_TRUE,_OR ' d = DECIMAL
byte ZEQ,_IF,03,DROP,_FALSE,EXIT ' bad suffix, no good
' so far the prefix and suffx have been checked prior to attempt a number conversion
' From here on there must be at least one valid digit for a number to be accepted
' DIGIT EXTRACTION & ACCUMULATION
nmlp byte DUP,CFETCH,DUP,_IF,@nmend-@nm1 ' while there is another character
nm1 byte XCALL,xTODIGIT,_IF,@nmsym-@nm2 ' convert to a digit? or else check symbol
nm2 ' a digit has been found but is it valid for this base? ' ( str val )
byte DUP,REG,BASE,CFETCH,DEC,GT,_IF,@nmok-@nm3
nm3 byte DROP2,_FALSE,EXIT ' a digit but exceeded base
nmok byte REG,anumber,FETCH,REG,BASE,CFETCH,UMMUL,DROP ' shift anumber left one digit (base)
byte PLUS,REG,anumber,STORE ' and merge in new digit
byte _1,REG,digits,CPLUSST ' update number of digits
nmnxt byte INC,_AGAIN,@nmsym-@nmlp ' update str and loop
' character was not a digit - check for valid symbols (keep it simple for now)
' SYMBOLS
nmsym byte DUP,CFETCH,_BYTE,":",EQ
byte OVER,CFETCH,_BYTE,".",EQ
byte DUP,_IF,06,REG,digits,CFETCH,REG,dpl,CSTORE ' remember last decimal place
byte REG,bnumber,FETCH,ZEQ,ZEQ,_AND,_OR
byte _IF,@nmsym2-@nmsym1 ' Use : as special byte shift for IP notation etc
nmsym1 byte REG,bnumber,FETCH
byte REG,anumber,FETCH,PLUS,_8,_SHL
byte REG,bnumber,STORE,_0,REG,anumber,STORE ' accumulate & number in bnumber
nmsym2 byte _AGAIN,@nmend-@nmnxt ' just ignore other symbols for now
'
nmend ' end of string - check
byte DROP2,REG,digits,CFETCH,DUP,ZEXIT ' return with false if there are no digits
byte REG,anumber,FETCH,REG,bnumber,FETCH,PLUS
byte _BYTE,sign,REG,flags,XCALL,xSETQ,QNEGATE
byte SWAP,EXIT ' all good, return with number and true
' NUMBER processing -try to convert a string to a number
NUMBER ' ( str -- value digits | false )
byte DUP,XCALL,xSTRLEN,_2,EQ ' process control prefix i.e. ^A
byte OVER,CFETCH,_BYTE,"^",EQ,_AND,_IF,@ch01-@ctlch ' ^ch Accept caret char as <control> char
ctlch byte INC,CFETCH,_BYTE,$1F,_AND,_1,EXIT ' control character processed
ch01 byte DUP,XCALL,xSTRLEN,_3,EQ ' process character literal i.e. "A"
byte OVER,CFETCH,_BYTE,$22,EQ,_AND,_IF,@ch02-@ascch ' "ch" Accept as an ASCII literal
ascch byte INC,CFETCH,_1,EXIT
' It wasn't an ASCII literal, process as a number
ch02 byte REG,anumber,_BYTE,10,_0,XCALL,xFILL ' zero out assembled number (double), digits, dpl
byte REG,base,CFETCH,REG,base+1,CSTORE ' backup current base as it may be overridden
byte XCALL,x_NUMBER
nmb1 byte REG,base+1,CFETCH,REG,base,CSTORE,EXIT ' restore default base before returning
' Most of these words are acted upon immediately rather than compiled as they are
' part of the "compiler" in that they create the necessary structures
'
' dumb compiler for literals - improve later - just needs to optimize the number of bytes needed
LITCOMP ' ( n -- ) compile the literal according to size
byte DUP,PUSH1,24,_SHR
byte _IF,@lco1-@LITC4
' Compile 4 bytes - 32bits
LITC4 byte PUSH1,PUSH4,XCALL,xBCOMP
byte DUP,PUSH1,24,_SHR,XCALL,xBCOMP
byte DUP,PUSH1,16,_SHR,XCALL,xBCOMP
byte DUP,_8,_SHR,XCALL,xBCOMP
byte XCALL,xBCOMP,EXIT
lco1
byte DUP,PUSH1,16,_SHR
byte _IF,@lco2-@LITC3
' Compile 3 bytes - 24bits
LITC3 byte PUSH1,PUSH3,XCALL,xBCOMP
byte DUP,PUSH1,16,_SHR,XCALL,xBCOMP
byte DUP,_8,_SHR,XCALL,xBCOMP
byte XCALL,xBCOMP,EXIT
lco2
byte DUP,_8,_SHR
byte _IF,@LITC1-@LITC2
' Compile 2 bytes - 16bits
LITC2 byte PUSH1,PUSH2,XCALL,xBCOMP
byte DUP,_8,_SHR,XCALL,xBCOMP
byte XCALL,xBCOMP,EXIT
' Compile 1 byte - 8bits
LITC1 byte PUSH1,PUSH1,XCALL,xBCOMP
byte XCALL,xBCOMP,EXIT
' MARK ( addr tag -- tag&addr ) Merge tag and addr by shifting tag into hi word
MARK byte _BYTE,$10,_SHL,_OR,EXIT
' UNMARK ( tag&addr -- addr tag )
UNMARK byte DUP,_WORD,$FF,$FF,_AND,SWAP,_BYTE,$10,_SHR,EXIT
' BEGIN as in BEGIN...AGAIN or BEGIN...UNTIL
_BEGIN_ byte REG,codes,WFETCH,_BYTE,$BE,XCALL,xMARK ' generate branchs for BEGIN
bg01 byte EXIT
' UNTIL ( flg -- )
_UNTIL_ byte XCALL,xUNMARK
unt00 byte _BYTE,$BE,EQ,_IF,@badthen-@unt01
unt01 byte _BYTE,_UNTIL,XCALL,xBCOMP,JUMP,@calcback-@_REPEAT_ '
' AGAIN
_REPEAT_ byte XCALL,xUNMARK
rp00 byte _BYTE,$1F,EQ,_IF,@badrep-@rp02
rp02 byte REG,codes,WFETCH,INC,INC,OVER,MINUS,SWAP,DEC,CSTORE ' process branch of WHILE to after REPEAT
byte JUMP,@_AGAIN_-@badrep
badrep byte DROP2,JUMP,@badthen-@_AGAIN_
_AGAIN_ byte XCALL,xUNMARK
ag00 byte _BYTE,$BE,EQ,_IF,@badthen-@ag01 '
ag01 byte _BYTE,_AGAIN
' ( addr bc -- ) compile the bytecode and calculate the branch back
lpcalc byte XCALL,xBCOMP
calcback byte REG,codes,WFETCH,INC,SWAP,MINUS,XCALL,xBCOMP
byte EXIT
' IF as in IF...THEN or IF...ELSE...THEN
_WHILE_
_IF_ byte _BYTE,_IF,XCALL,xBCOMP,_0,XCALL,xBCOMP
byte REG,codes,WFETCH,_BYTE,$1F,XCALL,xMARK
if01 byte EXIT
' ELSE
_ELSE_ byte XCALL,xUNMARK
el00 byte _BYTE,$1F,EQ,_IF,@badthen-@el01 ' does this match an IF?
el01 byte _BYTE,JUMP,XCALL,xBCOMP,_0,XCALL,xBCOMP ' Compile a jump forward just like an IF
byte REG,codes,WFETCH,_BYTE,$1F,XCALL,xMARK ' mark the else to be processed on a THEN
el02 byte SWAP,_BYTE,$1F,XCALL,xMARK ' get the IF addr and proceed as if it were a THEN
el03
' THEN
_THEN_ byte XCALL,xUNMARK
th00 byte _BYTE,$1F,EQ,_IF,@badthen-@RESFWD
RESFWD byte REG,codes,WFETCH,OVER,MINUS,SWAP,DEC,CSTORE,EXIT ' calculate branch and update IF's branch
badthen byte XCALL,xPRTSTR," Structure mismatch! ",0
byte DROP,EXIT
' " Compile a literal string - no length restriction - any codes can be included except the delimiter "
_STR_ byte _BYTE,XCALL,XCALL,xBCOMP,_BYTE,x_STR,XCALL,xBCOMP ' compile bytecodes for string
byte JUMP,@COMPSTR-@_PSTR_
' ." Compile a literal print string - no length restriction - any codes can be included except the delimiter "
_PSTR_ byte _BYTE,XCALL,XCALL,xBCOMP,_BYTE,xPRTSTR,XCALL,xBCOMP
COMPSTR
pslp byte XCALL,xKEY,DUP,XCALL,xQEMIT ' echo string
byte DUP,_BYTE,$22,EQ,_IF,05,DROP,_0,XCALL,xBCOMP,EXIT
byte XCALL,xBCOMP,_AGAIN,@ps01-@pslp
ps01 '''
{ TACHYON CASE STRUCTURES
This implementation follows the C method to some degree.
Each CASE statement simply compares the supplied value with the SWITCH and executes an IF
To prevent the IF statement from falling through a BREAK is used (also acts as a THEN at CT)
The SWITCH can be tested directly and a manual CASE can be constructed with IF <code> BREAK
SWITCH ( switch -- ) \ Save switch value used in CASE structure
CASE ( val -- ) \ compare val with switch and perform an IF. Equivalent to SWITCH= IF
BREAK \ EXIT (return) but also completes IF structure at CT. Equivalent to EXIT THEN
\ extra functions to allow the switch to be manipulated etc
SWITCH@ ( -- switch ) \ Fetch last saved switch value
SWITCH= ( val -- flg ) \ Compare val with switch. Equivalent to SWITCH@ =
SWITCH>< ( from to -- flg ) \ Compare switch within range. Equivalent to SWITCH@ ROT ROT WITHIN
Usage:
pub CASEDEMO ( val -- )
SWITCH \ use the key value as a switch in the case statement
"A" CASE CR ." A is for APPLE " BREAK
"H" CASE CR ." H is for HAPPY " BREAK
"Z" CASE CR ." Z is for ZEBRA " BREAK
$08 CASE 4 REG ~ BREAK
\ Now accept 0 to 9 and build a number calculator style
"0" "9" SWITCH>< IF SWITCH@ $30 - 4 REG @ #10 * + 4 REG ! ." *" BREAK
\ On enter just display the accumulated number
$0D CASE CR ." and our lucky number is " 4 REG @ .DEC BREAK
\ show how we can test more than one value
"Q" SWITCH= "X" SWITCH= OR IF CR ." So you're a quitter hey?" CR CONSOLE BREAK
CR ." I don't know what " SWITCH@ EMIT ." is"
;
pub DEMO
BEGIN KEY UPPER CASEDEMO AGAIN
;
byte $20,"BREAK", hd+xc+im, XCALL,xISEND
byte $20,"CASE", hd+xc+im, XCALL,xIS
byte $20,"SWITCH", hd+xc, XCALL,xSWITCH
byte $20,"SWITCH@", hd+xc, XCALL,xSWITCHFETCH
byte $20,"SWITCH=", hd+xc, XCALL,xISEQ
byte $20,"SWITCH><", hd+xc, XCALL,xISWITHIN
}
' SWITCH ( val -- )
_SWITCH byte REG,uswitch,STORE,EXIT
' SWITCH@ ( -- val )
SWITCHFETCH
byte REG,uswitch,FETCH,EXIT
' SWITCH= ( val -- flg )
ISEQ byte REG,uswitch,FETCH,EQ,EXIT
' CASE ( compare -- )
IS byte _BYTE,XCALL,XCALL,xBCOMP,_BYTE,xISEQ,XCALL,xBCOMP,XCALL,x_IF_,EXIT
' BREAK
ISEND byte _BYTE,EXIT,XCALL,xBCOMP,XCALL,xALLOCATED,XCALL,x_THEN_,EXIT
' SWITCH>< ( from to -- flg )..
ISWITHIN byte XCALL,xSWITCHFETCH,ROT,ROT,XCALL,xWITHIN,EXIT
{ Table vectoring -
index a table of vectors and jump to that vector
A table limit is supplied as well as a default vector
Usage:
<limit> VECTORS <vector if over>
<vector0> <vector1> ...... <vectorx>)
Sample:
4 LOOKUP BELL \ an index of 4 or more will default to BELL
INDEX0 INDEX1 INDEX2 INDEX3 \ 0 to 3 will execute corresponding vectors
}
' VECTORS ( index range -- )
VECTORS byte OVER,GT,ZEQ,_IF,02,DROP,MINUS1 ' limit index to range or -1 (.>0)
byte INC,_SHL1,RPOP,PLUS,CFETCHINC,SWAP,CFETCH ' form index into vectors
EXECUTE ' ( bytecode1 bytecode2 -- ) ' 2 bytecodes
byte _WORD,(@bcexec+s)>>8,@bcexec+s
byte ROT,OVER,CSTORE,INC,CSTORE
bcexec byte EXIT,EXIT,EXIT
' XCALLS ( -- addr ) address of XCALLS vector table
_XCALLS byte _WORD,(@XCALLS+s)>>8,@XCALLS+s,EXIT
' 12103 - adding a more general method for creating a call vector
' +CALL ( addr -- index opcode ) ' index = 0..$3FF
AddACALL byte XCALL,xXCALLS,_0
byte _2,PLUS,OVER,OVER,PLUS,WFETCH,ZEQ,_UNTIL,09 ' ( addr base index )
byte SWAP,OVER,PLUS,ROT,SWAP,WSTORE ' ( index )
byte _SHR1,DUP,_1,_AND,_BYTE,XCALL,SWAP,MINUS
byte OVER,_WORD,$02,00,_AND,ZEQ,ZEQ,_SHL1,PLUS
byte SWAP,_SHR1,toBYTE,SWAP
byte EXIT
' ( bytecode -- ) append this bytecode to next free code location + append EXIT (without counting)
BCOMP
byte REG,codes,WFETCH,CSTORE,_1,REG,codes,WPLUSST
byte _BYTE,EXIT,REG,codes,WFETCH,CSTORE
byte EXIT
COMPILE byte XCALL,xGETWORD,DEC,XCALL,xSEARCH,EXIT ' get next word, search for it in dictionary
byte XCALL,xNFATICK,DEC
' ( atradr -- ) compile bytecodes according to attribute - 0 = one bytecode ; 2 = 2 bytecodes
BCOMPILE
byte CFETCHINC,_3,_AND
byte DUP,ZEQ,_IF,05,DROP,CFETCH,XCALL,xBCOMP,EXIT
byte DUP,_2,EQ,_IF,08,DROP,CFETCHINC,XCALL,xBCOMP,CFETCH,XCALL,xBCOMP,EXIT
byte DROP2,EXIT
' GRAB ( -- ) \ IMMEDIATE --- executes preceding code to make it available for any immediate words following
GRAB
byte PUSH1,EXIT,XCALL,xBCOMP ' append an EXIT
byte XCALL,xHERE,DUP,REG,codes,WSTORE,ACALL ' execute and release preceding code in text line
byte EXIT
' assign a new value for the constant
' 78 TO myconstant
ATO
byte XCALL,xGRAB,XCALL,xTICK,INC,STORE,EXIT
' ( -- atradr ) --- point to the attribute byte in the header of the latest name
ATATR byte REG,names,WFETCH,XCALL,xNFACFA,DEC,EXIT
' Set attribute of the latest word to PRIVATE -- used by RECLAIM (EXTEND.fth) to cull all unwanted headers.
PRIVATE byte XCALL,xCOLON,_BYTE,pr,XCALL,xATATR,SET,EXIT
' create a name in the dictionary - add
CREATEWORD
byte XCALL,xGETWORD ' ( str ) read the next word
CREATESTR
byte REG,names,WFETCH,REG,names+2,WSTORE ' backup names ptr (used to change fixed fields easily)
byte REG,flags,CFETCH,_BYTE,prset,_AND ' get attribute
byte _BYTE,hd+xc,_OR ' blend in private bit
byte XCALL,xPUTCHARPL ' add attribute byte
byte REG,codes,WFETCH ' Create a vector
byte XCALL,xAddACALL,XCALL,xPUTCHARPL,XCALL,xPUTCHARPL ' write opcode + index bytecode sequence
byte DUP,DEC,CFETCH 'XCALL,xSTRLEN ' ( str cnt )
byte DUP,NEGATE,REG,names,WPLUSST ' ( str cnt ) update names ptr by backwards count
byte REG,names,WFETCH,SWAP,XOP,pCMOVE ' copy it across
byte REG,names,WFETCH,DUP,XCALL,xSTRLEN ' ( names cnt )
byte MINUS1,REG,names,WPLUSST ' make room for the count
byte SWAP,DEC,CSTORE ' and set the count
' check for dictionary full
byte REG,names,WFETCH,REG,here,WFETCH,_BYTE,$40,PLUS,XCALL,xLT
byte _IF,@crw05-@crw04
crw04 byte XCALL,xPRTSTR," Warning!!!, Dictionary space full!!! ",0
byte XCALL,xERROR
crw05 byte EXIT
' CREATE <name> - Create a name in the dictionary and also a code entry
CREATE byte REG,createvec,WFETCH,QDUP,_IF,01,AJMP ' execute extended or user CREATE?
byte XCALL,xCREATEWORD '
byte _BYTE,VARB,XCALL,xBCOMP,_0 ' set default bytecode as a VARIABLE
' ALLOT ( bytes -- )
ALLOT byte REG,codes,WPLUSST
' lock in compiled code so far - do not release but set new "here" to the end of these codes
ALLOCATED
byte REG,codes,WFETCH,REG,here,WSTORE
byte EXIT
' C, ( n -- ) IMMEDIATE --- compile a byte into code and allocate
CCOMP
byte XCALL,xGRAB
cc01 byte XCALL,xBCOMP,XCALL,xALLOCATED,EXIT
' W, ( n -- )
WCOMP
byte XCALL,xGRAB
wc01 byte DUP,XCALL,xBCOMP,_8,_SHR,XCALL,xBCOMP,XCALL,xALLOCATED,EXIT
' , ( n -- ) Compile a long literal
LCOMP
byte XCALL,xGRAB
byte _4,FOR,DUP,XCALL,xBCOMP,_8,_SHR,forNEXT
byte DROP,XCALL,xALLOCATED,EXIT
' Create a new entry in the dictionary and also in the XCALLS table but also prevent any execution of code
' at an <enter> which would otherwise normally occur.
' unsmudge any previous name in case this is a fall-through.
' : <name>
COLON byte _BYTE,sm,XCALL,xATATR,CLR ' unsmudge any previous definition (fall-through)
byte XCALL,xCREATE ' this forms an XCALL,index to this new definition
_COLON byte _BYTE,sm,XCALL,xATATR,SET ' smudge it so it can't be referenced yet
byte MINUS1,XCALL,xALLOT ' write over VAR instruction
byte _BYTE,defining,REG,flags,SET,EXIT ' flag that we have entered a definition
PUBCOLON byte XCALL,xCOLON,_BYTE,prset,XCALL,xATATR,CLR,EXIT
' Update "here" pointer to point to current free position which "codes" pointer is now at
' Also unsmudge the headers tag
' ;
ENDCOLON byte _BYTE,EXIT,XCALL,xBCOMP ' compile an EXIT
byte _BYTE,defining,REG,flags,CLR,XCALL,xALLOCATED ' end definition and lock allocated bytes
byte _BYTE,sm,XCALL,xATATR,CLR,EXIT ' clear the smudge bit
' [COMPILE]
COMPILES byte _BYTE,comp,REG,flags,SET,EXIT
{
Replaced traditional parse function with realtime stream parsing
Each word is acted upon when a delimiter is encountered and this also allows for
interactive error checking and even autocompletion.
}
SCRUB byte XCALL,xHERE,REG,codes,WSTORE
byte _0,REG,wordcnt,CSTORE,_0,REG,wordbuf,CSTORE
byte _BYTE,$0D,REG,delim+1,CSTORE 'restore end-of-line delimiter to a CR
byte _BYTE,$0D,XCALL,xEMIT,_BYTE,$40,FOR,_BYTE,"-",XCALL,xEMIT,forNEXT 'horizontal line
byte XCALL,xCR,EXIT
' ( ch -- ) write a character into the next free position in the word buffer
PUTCHAR byte REG,wordcnt,DUP,CFETCH,SWAP,INC,PLUS,CSTORE,EXIT
PUTCHARPL byte XCALL,xPUTCHAR,REG,wordcnt,DUP,CFETCH,INC
byte _BYTE,wordsz,UDIVMOD,DROP,SWAP,CSTORE,EXIT
' As characters are accepted from the input stream, checks need to be made for delimiters,
' editing commands etc. 123us/CHAR, 184us/CTRL
doCHAR ' ( char -- flg ) Process char into wordbuf and flag true if all done
byte DUP,ZEXIT ' NULL - ignore
'
byte DUP,REG,lasttwo,DUP,CFETCH,OVER,INC,CSTORE,CSTORE ' keep a track of this and the last character
byte DUP,REG,delim+1,CSTORE ' delimiter is always last character
'''
byte _BYTE,$7F,OVER,EQ,_IF,02,DROP,_8 ' subs BS for DEL
byte DUP,BL,XCALL,xLT,_IF,@ischar-@doc3 ' only check for control characters
'
' PROCESS CONTROL CHARACTERS
'
doc3
' byte _BYTE,$80,OVER,_AND,_IF,04,_1,REG,flags+1,SET ' IAC or binary
byte _BYTE,$0A,OVER,EQ,_IF,03,DROP,_FALSE,EXIT ' LF - discard
byte _BYTE,$18,OVER,EQ,_IF,03,DROP,_TRUE,EXIT ' ^X reeXecute previous line
byte _1,REG,flags+1,XCALL,xSETQ,ZEQ,_IF,@ignore2-@ignore1
ignore1
byte _3,OVER,EQ,_IF,02,XCALL,xREBOOT ' ^C RESET
byte _4,OVER,EQ,_IF,05,DROP,XCALL,xDEBUG,_FALSE,EXIT ' ^D DEBUG
byte _2,OVER,EQ,_IF,09,DROP,_0,_WORD,$80,00,XCALL,xDUMP,_FALSE,EXIT ' ^B Block dump
byte _BYTE,$1A,OVER,EQ,REG,lasttwo+1,CFETCH,_BYTE,$1A,EQ,_AND
byte _IF,07,DROP,XCALL,xCOLDST,XCALL,xSCRUB,_FALSE,EXIT ' ^Z^Z cold start
ignore2
byte _BYTE,$1B,OVER,EQ,_IF,05,DROP,XCALL,xSCRUB,_TRUE,EXIT ' ESC will cancel line
byte _BYTE,$09,OVER,EQ,_IF,03,XCALL,xEMIT,BL ' TAB - substitute with a space
byte _BYTE,$1C,OVER,EQ,_IF,04,DROP,XCALL,xCR,BL ' ^| - multi-line interactive
byte _BYTE,$0D,OVER,EQ,_IF,03,DROP,_TRUE,EXIT ' CR - Return & indicate completion
'
byte _8,OVER,EQ,_IF,@ischar-@bksp1 ' BKSP - null out last char
bksp1 byte REG,wordcnt,CFETCH,_IF,@bksp3-@bksp2 ' don't backspace on empty word
bksp2 byte XCALL,xEMIT,XCALL,xSPACE,_8,XCALL,xEMIT ' backspace and clear
byte MINUS1,REG,wordcnt,CPLUSST,_0,XCALL,xPUTCHAR ' null previous char
byte _FALSE,EXIT
' '
bksp3 byte _BYTE,7,XCALL,xEMIT,DROP,_FALSE,EXIT ' can't backspace anymore, bell
'
ischar
byte _BYTE,echo,REG,flags,XCALL,xSETQ,_IF,03 ' don't echo if we don't want it
byte DUP,XCALL,xEMIT
byte REG,delim,CFETCH,OVER,EQ ' delimiter? (always accept a blank)
byte OVER,BL,EQ,_OR,_IF,05,DROP,REG,wordcnt,CFETCH,EXIT ' true if trailing delimiter - all done (flg=cnt)
'
' otherwise build text in wordbuf - null terminated with a preceding count .....
byte XCALL,xPUTCHARPL ' put a character into the word buffer
byte _FALSE,EXIT
' Build a delimited word and return immediately upon a valid delimiter
GETWORD ' ( -- str ) Build a text word from character input into wordbuf for wordcnt
byte REG,wordcnt,PUSH1,wordsz,_0,XCALL,xFILL 'Erase the word buffer & preceding count
gwlp byte XCALL,xKEY ' get another character
byte XCALL,xdoCHAR ' process
byte _UNTIL,@gw1-@gwlp 'continue building the next word
gw1 byte REG,wordbuf,EXIT
{ DICTIONARY SEARCH
Example of last entry in dictionary "COLD" 04 43 4F 4C 44 82 BF A4 00 00
cnt C O L D atr bc1 bc2 <end>
''
'' Compare a null-terminated source string with a dictionary string which is 8th bit terminated.
'' This will always force a mismatch after which one is checked for a null while the other is checked
'' for the 8th bit and if verified then a match has been found.
'' The dict pointer is advanced to point to the end of the dict string on the 8th bit termination which
'' is the attribute byte as in: byte "CMPSTR",$80,CMPSTR
''
_CMPSTR ' ( src dict -- src dict flg ) Compare strings at these two addresses
' byte XX,_IF,03,XOP,pRUNMOD,EXIT
byte OVER,SWAP
cmplp byte OVER,CFETCH,OVER,CFETCH,EQ,_IF,@nomatch-@cmps1
cmps1 byte INC,SWAP,INC,SWAP,_AGAIN,@nomatch,@cmplp
nomatch byte OVER,CFETCH,
}
{
DUP 54us
RESET 1ms
0 144us
BL 288us
KOLD 3.45ms
TAB 9ms
EXTEND.fth 7.87ms 9.85ms
12345 600us
}
SEARCH ' ( cstr -- nfaptr ) ' cstr points to the count+string+null
{ extra method fpr searching user dictionary first
byte REG,unames,WFETCH,QDUP,IF,@sr02-@sr01 ' user dictionary?
sr01
byte OVER,SWAP,XCALL,xFINDSTR ' search user words overrides all other searches
byte QDUP,_IF,02,NIP,EXIT ' found it - return now with result
}
sr02
byte DUP,REG,corenames,WFETCH,XCALL,xFINDSTR ' search core words first to improve compilation speed
byte QDUP,_IF,02,NIP,EXIT ' found it - return now with result
'
byte DUP,REG,names,WFETCH,XCALL,xFINDSTR ' search extended dictionary
byte QDUP,_IF,02,NIP,EXIT ' return with positive result
'
byte REG,findvec,WFETCH,QDUP,_IF,01,AJMP ' user specified search
byte DROP,_FALSE,EXIT ' not found in dictionary
' ( cstr dict -- nfaptr | false ) Try to find the counted string in the dictionary(s) using CMPSTR (ignore smudged entries)
FINDSTR
fstlp byte _0,XOP,pCMPSTR ' ( src dict flg )
byte _IF,@nxtword-@fst1 ' found it ( src dict )
fst1 byte DUP,XCALL,xNFACFA,DEC,CFETCH
byte _BYTE,sm,_AND,ZEQ,_IF,@nxtword-@fst0
fst0 byte NIP,EXIT ' ( nfaptr ) found
'
' Skip the attribute byte and codes and test for end of dictionary (entry = 00)
nxtword ' ( src dict ) advance past atr+codes to try next. (atr(1),bytecode)
byte CFETCHINC,PLUS,_3,PLUS ' jump over CFA to next NFA
' ( src dict ) dict points to bc1
byte DUP,CFETCH,ZEQ,_UNTIL,@fst2-@fstlp
' end of dictionary reached
fst2 byte DROP2,_FALSE,EXIT
' The CFA is the address of the 2 bytecodes stored in the header that are executed or compiled
' Typically these bytecodes will be in the form of "XCALL,xWord"
' or in the case of COG words such as DUP "DUP,EXIT" where only DUP is compiled
' NFA>CFA ( nfa -- cfa ) BEGIN C@++ $7F > UNTIL ;
NFACFA byte CFETCHINC,_BYTE,$7F,GT,_UNTIL,06,EXIT
' : >PFA ( cfa -- pfa )
PFA byte DUP,DEC,CFETCH,_BYTE,$80,EQ,_IF,02,CFETCH,EXIT ' if atr = COG BYTECODE - just return with it
' but this could be a two bytecode sequence rather than a call
byte DUP,CFETCH,_BYTE,XOP,EQ,_IF,07,CFETCHINC,_8,_SHL,SWAP,CFETCH,PLUS,EXIT
' \ MJB byte DUP,CFETCH,_BYTE,XOP,EQ,_IF,05,1+,CFETCH,_16,PLUS,EXIT ' saves 2 bytes and a hubop and works whether XOP is $01 or not (as it is now $0B)
byte XCALL,xTOVEC
byte WFETCH,EXIT
' >VEC ( cfa -- vecptr )
TOVEC byte DUP,INC,CFETCH,_SHL1,_SHL1,XCALL,xXCALLS,PLUS ' ( cfa xcallptr )
byte SWAP,CFETCH,_BYTE,VCALL,MINUS ' calculate which vector XYZ or V we are using
byte _3,_XOR ' ( xcallptr mod )
' ( ptr indexh ) 0 = XCALL, 1 = YCALL, 2 = ZCALL, 3 = VCALL
byte SWAP,OVER,_1,_AND,_SHL1,PLUS ' point to high or low vector word
byte SWAP,_2,_AND,_IF,04,_WORD,$04,00,PLUS
byte EXIT
' NFA'
NFATICK byte XCALL,xGETWORD,DEC,XCALL,xSEARCH,EXIT
_NFATICK byte XCALL,xNFATICK,XCALL,xLITCOMP,EXIT
' ' <name> ( -- pfa ) Find the address of the following word - zero if not found or it's pfa
TICK byte XCALL,xNFATICK,DUP,ZEXIT,XCALL,xNFACFA,XCALL,xPFA,EXIT
ATICK byte XCALL,xTICK
byte XCALL,xLITCOMP,EXIT
SETPOLL byte XCALL,xTICK,REG,keypoll,WSTORE,EXIT
{
WORDS byte REG,names,WFETCH
wdlp
byte DUP,CFETCH,ZEQ,DUP,_IF,06,SWAP,INC,DUP,CFETCH,ZEQ,ROT,_AND,_IF,@wd03-@wd02
wd02 byte DROP,XCALL,xCR,EXIT
wd03 byte XCALL,xCR,DUP,XCALL,xPRTWORD,XCALL,xPRTSTR,": ",0
byte INC,DUP,XCALL,xNFACFA,DEC,DUP,_3
byte ADO,I,CFETCH,XCALL,xPRTBYTE,XCALL,xSPACE,LOOP
byte _3,PLUS,SWAP,XCALL,xPSTR,XCALL,xSPACE,_AGAIN,@wd04-@wdlp
wd04
}
' AUTORUN <name> - Setup Tachyon to AUTORUN the word on reset - an invalid or no name will disable AUTORUN
' <name> must be a valid user word so it must be an XCALL
AUTORUN byte XCALL,xTICK
' AUTO! ( addr -- ) Set the AUTORUN vector
AUTOST
setauto byte REG,autovec,WSTORE,EXIT
' FREE ( -- free ) Read free memory from Spin header and round up to a 64 byte page (to suit EEPROM)
'FREE byte _BYTE,10,WFETCH,_BYTE,$80,PLUS,_BYTE,$3F,_ANDN,EXIT
FREE byte _WORD,(@last+s)>>8,@last+s,_BYTE,$80,PLUS,_BYTE,$3F,_ANDN,EXIT
' correct the count byte in each entry in the dictionary as the precompiled version leaves a dummy count (too mind-numbing)
FIXDICT byte _WORD,(@dictionary+s)>>8,@dictionary+s
fdlp byte DUP,INC,XCALL,xSTRLEN,OVER,CSTORE ' calc length and set count byte
byte DUP,CFETCH,_4,PLUS,PLUS,DUP,CFETCH,ZEQ,_UNTIL,@fd01-@fdlp
fd01 byte DROP,EXIT
' COLD Force factory defaults
COLDST byte XCALL,xPRTSTR," Cold start - no user code - setting defaults ",$0D,$0A,0
byte XCALL,xFREE,DUP,REG,here,WSTORE,REG,here-2,WSTORE ' free memory
byte _WORD,(@rxbuffers+s)>>8,@rxbuffers+s,REG,rxptr,WSTORE ' setup saved receive buffer address
byte _WORD,(@dictionary+s)>>8,@dictionary+s
byte DUP,REG,corenames,WSTORE,DEC
byte DUP,REG,names,WSTORE,REG,names-2,WSTORE ' Reset dictionary pointer
byte _0,REG,autovec,WSTORE,_0,REG,keypoll,WSTORE ' disable autorun and ext keypoll
byte XCALL,xFIXDICT
byte _BYTE,xLAST,DUP,_SHL1,_SHL1,XCALL,xXCALLS,PLUS ' find first free XCALL memory location
byte _WORD,$02,00,ROT,MINUS,_SHL1,_SHL1,_0,XCALL,xFILL ' clear all user XCALLs
byte XCALL,xXCALLS,INC,INC,_WORD,$08,00 'clear YCALLs (interleaved with XCALLS)
byte ADO,_0,I,WSTORE,_4,PLOOP
byte REG,tasks,_BYTE,tasksz*8,_0,XCALL,xFILL ' initialize 8 task words
byte _WORD,$A5,$5A,REG,cold,WSTORE
byte EXIT
' Discard the current line
DISCARD
dslp byte XCALL,xKEYQ,_AND,ZEQ,_UNTIL,@ds01-@dslp
ds01 byte _BYTE,100,XCALL,xms,XCALL,xKEYQ,_AND,ZEQ,_UNTIL,@ds02-@dslp
ds02 byte EXIT
' TASK ( cog -- addr ) Return with address of task control register in "tasks"
TASK byte _3,_SHL,REG,tasks,PLUS,EXIT
{
'RUN ( pfa cog -- )
RUN byte XCALL,xTASK,WSTORE,EXIT
}
org ' align the label - needs to be passed in 14-bit PAR register
' idle loop for other Tachyon cogs
IDLE byte XCALL,xInitStack
byte _1,XOP,_COGID,XCALL,xTASK,INC,INC,CSTORE ' task+2 = 1 to indicate Tachyon running
idlp byte _8,XCALL,xms ' do nothing for a bit - saves power
byte XOP,_COGID,XCALL,xTASK,WFETCH ' fetch cog's task variable
byte QDUP,_UNTIL,@id00-@idlp ' until it is non-zero
id00 byte DUP,_8,_SHR,_IF,01,ACALL ' Execute but ignore if task address is only 8-bits
byte _0,XOP,_COGID,XCALL,xTASK,WSTORE ' clear run address only if it has returned back to idle
byte _AGAIN,@id01-@IDLE
id01
org ' align the label
TERMINAL
byte XOP,pInitRP
SETPLL byte _WORD,(@_setpll+s)>>8,@_setpll+s ' autoset crystal operation
byte XCALL,xLOADMOD,XOP,pRUNMOD
byte _BYTE,txd,MASK,OUTSET ' be a friend and make the transmit an output
byte XCALL,xInitStack ' Init the internal stack and setup external stack space
byte _WORD,extstk>>8,extstk,XCALL,xSPSTORE
byte _WORD,00,50,XCALL,xms ' a little startup delay (also wait for serial cog)
byte _WORD,bufsize>>8,bufsize,REG,rxsz,WSTORE ' Set the rx buffer size
byte _BYTE,echo,REG,flags,CSTORE ' echo on
byte BL,REG,delim,CSTORE,XCALL,xHEX ' default delimiter is a space character
byte _BYTE,$0D,XCALL,xEMIT,XCALL,xPRTVER ' Show VERSION with optional CLS (default CR)
byte REG,cold,WFETCH,_WORD,$A5,$5A,EQ,ZEQ ' performing a check for a saved session
byte _IF,@warmst-@tm01 ' or not
tm01 byte XCALL,xCOLDST ' defaults
warmst
byte XCALL,xKEYQ,_AND,_1,EQ,_NOT,_IF,@termnew-@chkauto ' ^A abort autostart with ^A
chkauto byte REG,autovec,WFETCH,QDUP,_IF,@termnew-@runauto ' check for an AUTORUN
runauto byte ACALL ' and execute it
CONSOLE
termnew byte XOP,pInitRP ' init return stack in case (limited)
byte XCALL,xSCRUB
' Main console line loop - get a new line (word by word)
termcr byte REG,prompt,WFETCH,QDUP,_IF,01,ACALL ' execute user prompt code
byte XCALL,xHERE,REG,codes,WSTORE ' reset temporary code compilation pointer
'
' Main console loop - read a word and process
termlp
byte XCALL,xGETWORD ' Read a word from input stream etc
byte CFETCH,ZEQ,_IF,@trm1-@trm2 ' ignore empty string
trm2 byte REG,delim+1,CFETCH,_BYTE,$18,EQ,_NOT,_IF,@execinp-@trm3 ' ^X then repeat last line
trm3 byte REG,delim+1,CFETCH,_BYTE,$0D,EQ,_NOT,_IF,@chkeol-@trm1 ' Otherwise process ENTER
'
trm1 ' Preprocess prefixed numbers #$%
byte REG,wordbuf,CFETCH,_BYTE,"#",_BYTE,"%",XCALL,xWITHIN ' Numeric prefixes?
byte REG,wordbuf-1,CFETCH,_2,GT,_AND ' and more than 2 characters? (inc term)
byte REG,wordbuf-1,DUP,CFETCH,PLUS,CFETCH ' and last char is a digit or hex digit?
byte DUP,_BYTE,"0",_BYTE,"9",XCALL,xWITHIN ' decimal digit?
byte SWAP,_BYTE,"A",_BYTE,"F",XCALL,xWITHIN,_OR,_AND ' hex digit?
byte ZEQ,_IF,@tryanum-@trm4 ' good, process this as a number now @tryanum
' Search the dicitonary for a match (as a counted string)
trm4 byte REG,wordbuf,DEC,XCALL,xSEARCH ' try and find that word in the dictionary(s)
byte QDUP,_IF,@_notfound-@foundword ' found it
foundword ' found the word in the dictionary - compile or execute?
byte XCALL,xNFACFA,DEC ' point to attribute byte
byte PUSH1,im,OVER,XCALL,xSETQ ' is the immediate bit set?
byte _BYTE,comp,REG,flags,XCALL,xSETQ,ZEQ,_AND ' and comp flag off (not forced to compile)
byte _IF,@compword-@immed '
immed byte INC,CFETCHINC,SWAP,CFETCH,XCALL,xEXECUTE ' Fetch and EXECUTE code immediately
byte _ELSE,@chkeol-@compword
compword byte XCALL,xBCOMPILE ' or else COMPILE the bytecode(s) for ths word
byte _BYTE,comp,REG,flags,CLR ' reset any forced compile mode
' END OF LINE CHECK
chkeol byte REG,delim+1,CFETCH,_BYTE,$0D,EQ ' Was this the end of line?
byte DUP,_IF,@eol01-@eol00
eol00 byte REG,accept,WFETCH,ZEQ,_IF,02,XCALL,xSPACE ' Yes, put a space between any user input and response
byte _BYTE,linenums,REG,flags,XCALL,xSETQ,_IF,@eol01-@prtline
prtline byte _BYTE,$0D,XCALL,xEMIT ' List line number if enabled
byte REG,linenum,WFETCH,XCALL,xPRTDEC,XCALL,xSPACE
byte _1,REG,linenum,WPLUSST
eol01 byte DUP,_BYTE,defining,REG,flags,XCALL,xSETQ,_AND ' and are we in a definition or interactive?
byte _IF,02,XCALL,xCR ' If not interactive then CRLF (no other response)
byte _BYTE,defining,REG,flags,XCALL,xSETQ,ZEQ,_AND ' do not execute if still defining
byte _UNTIL,@execs-@termlp ' wait until CR to execute compiled codes
' EXECUTE CODE from user input
execs byte PUSH1,EXIT,XCALL,xBCOMP ' done - append an EXIT (minimum action on empty lines)
execinp byte XCALL,xHERE,ACALL ' execute from beginning
byte REG,accept,WFETCH ' if accept vector is <>0
byte QDUP,_IF,03,ACALL ' then execute it
byte _ELSE,02,XCALL,xOK ' else echo the "ok"
byte _AGAIN,@_notfound-@termcr
_notfound ' NOT FOUND - before trying to convert to a number check encoding for ASCII literals (^ and ")
' Attempt to process this word as a number
tryanum byte REG,wordbuf,XCALL,xNUMBER,_IF,@unknown-@compnum
compnum byte XCALL,xLITCOMP
byte _AGAIN,@unknown-@chkeol ' is it a number? ( value digits )
unknown byte REG,unum,WFETCH,QDUP,_IF,03,ACALL,_AGAIN,@un01-@chkeol ' UNKNOWN - try unum vector if set
un01
' byte _BYTE,echo,REG,flags,XCALL,xSETQ,ZEQ,_IF,@un03-@un00
un00 byte XCALL,xNOTFOUND
un02 byte _AGAIN,@trmend-@termlp
trmend
NOTFOUND
byte XCALL,xPRTSTR," --> ",0
byte REG,wordbuf,XCALL,xPSTR ' Spit out offending word
byte XCALL,xPRTSTR," <-- not found ",7,$0D,$0A,0 ' --> xxx <--- NOT FOUND
ERROR byte _1,REG,errors,WPLUSST ' count errors
' byte _BYTE,7,XCALL,xEMIT
byte XCALL,xDISCARD,EXIT
' TACHYON - used to verify that source code is intended for Tachyon and also to reset load stats
_TACHYON byte XCALL,xPRTVER
byte _0,REG,keypoll,WSTORE ' disable background keypoll during load
byte _0,REG,errors,WSTORE
byte XCALL,xHERE,REG,here-2,WSTORE ' remember code position
byte _0,REG,linenum,WSTORE,_BYTE,linenums,REG,flags,SET ' reset line# and set linenum mode
byte REG,names,WFETCH,REG,names-2,WSTORE ' backup dictionary pointer
byte LAP,EXIT ' time the load
' VER ( -- verptr )
GETVER byte _WORD,(@version+s)>>8,@version+s,EXIT
PRTVER byte XCALL,xPRTSTR,$0D,$0A
byte " Propeller .:.:--TACHYON--:.:. Forth V",0
byte XCALL,xGETVER,DUP,FETCH,XCALL,xPRTDEC
byte _BYTE,".",XCALL,xEMIT,_4,PLUS,WFETCH,XCALL,xPRTDEC
byte XCALL,xCR,EXIT
'************************************************************************************************************
last
TRIM byte 0[$5300-$] ' trim this to optimize top of memory (buffers @7400 etc)
{ *** DICTIONARY in RAM and EEPROM *** }{ The Forth dictionary is loaded into high RAM and is not used at runtime normally unless the console is used to "talk" to Forth. Search methods: Structure: 1 - Count byte - This speeds up searching the dictionary both in comparing and in jumping to the next string 2- Name string 3- Attribute byte (8th bit set also terminates name string ) 4- 1st bytecode, 2nd bytecode Dictionary entries do not need a link field as they are bunched together one after another and it is very easy to find the next entry by scanning forwards and looking for the attribute byte which will have the msb set then jumping 3 bytes. A name field that begins with a null indicates end of dictionary (or link to another), null null is the end. } |
CON
' Dictionary header attribute flags
hd = |<7 'indicates this is a an attribute (delimits the start of a null terminated name)
sm = |<6 'smudge bit - set to deactivate word during definition
im = |<5 'lexicon immediate bit
pr = |<3 'private (can be removed from the dictionary)
' code attributes 00 = single bytecode, 02 = XCALL bytecode (2 bytes), 03 = WCALL bytecode (3 bytes)
sq = |<2 'indicates the bytecode is a sequence of two PASM instructions (as opposed to a vectored call)
xc = |<1 'XCALL bytecode
ac = xc+|<0 'WCALL - 2 byte address - interpret header CFA as an absolute address
DAT
{ This is an 8th bit terminated string using the attribute byte so it saves one byte per entry plus it simplfies the string compare function. Searching still proceeds from lower memory to higher memory
}
dictionary
' The count field is left blank but filled in at runtime so that these do not need to be calculated when defining
'
' CNT,NAME ATR CODES
byte $20,"DUP", hd, DUP,EXIT
byte $20,"OVER", hd, OVER,EXIT
byte $20,"DROP", hd, DROP,EXIT
byte $20,"2DROP", hd, DROP2,EXIT
byte $20,"SWAP", hd, SWAP,EXIT
byte $20,"ROT", hd, ROT,EXIT
byte $20,"NIP", hd, NIP,EXIT
byte $20,"+FIB", hd, FIB,EXIT
' byte $20,"REPS", hd, REPS,EXIT
byte $20,"STREND", hd, STREND,EXIT
byte $20,"0", hd, _0,EXIT
byte $20,"1", hd, _1,EXIT
byte $20,"2", hd, _2,EXIT
byte $20,"3", hd, _3,EXIT
byte $20,"4", hd, _4,EXIT
' byte $20,"5", hd, _5,EXIT
' byte $20,"6", hd, _6,EXIT
' byte $20,"7", hd, _7,EXIT
byte $20,"8", hd, _8,EXIT
byte $20,"ON", hd, MINUS1,EXIT
byte $20,"TRUE", hd, MINUS1,EXIT
byte $20,"-1", hd, MINUS1,EXIT
byte $20,"BL", hd, BL,EXIT
byte $20,"16", hd, _16,EXIT
byte $20,"FALSE", hd, _0,EXIT
byte $20,"OFF", hd, _0,EXIT
byte $20,"1+", hd, INC,EXIT
byte $20,"1-", hd, DEC,EXIT
byte $20,"+", hd, PLUS,EXIT
byte $20,"-", hd, MINUS,EXIT
byte $20,"ADO", hd, ADO,EXIT
byte $20,"DO", hd, DO,EXIT
byte $20,"LOOP", hd, LOOP,EXIT
byte $20,"+LOOP", hd, PLOOP,EXIT
byte $20,"FOR", hd, FOR,EXIT
byte $20,"NEXT", hd, forNEXT,EXIT
byte $20,"INVERT", hd, INVERT,EXIT
byte $20,"AND", hd, _AND,EXIT
byte $20,"ANDN", hd, _ANDN,EXIT
byte $20,"OR", hd, _OR,EXIT
byte $20,"XOR", hd, _XOR,EXIT
byte $20,"ROL", hd, _ROL,EXIT
byte $20,"ROR", hd, _ROR,EXIT
byte $20,"SHR", hd, _SHR,EXIT
byte $20,"SHL", hd, _SHL,EXIT
byte $20,"2/", hd, _SHR1,EXIT
byte $20,"2*", hd, _SHL1,EXIT
byte $20,"REV", hd, _REV,EXIT
byte $20,"MASK", hd, MASK,EXIT
byte $20,">N", hd, toNIB,EXIT
byte $20,">B", hd, toBYTE,EXIT
' byte $20,">W", hd, toWORD,EXIT
byte $20,"0=", hd, ZEQ,EXIT
byte $20,"NOT", hd, ZEQ,EXIT
byte $20,"=", hd, EQ,EXIT
byte $20,">", hd, GT,EXIT
byte $20,"C@", hd, CFETCH,EXIT
byte $20,"W@", hd, WFETCH,EXIT
byte $20,"@", hd, FETCH,EXIT
byte $20,"C+!", hd, CPLUSST,EXIT
byte $20,"C!", hd, CSTORE,EXIT
byte $20,"C@++", hd, CFETCHINC,EXIT
byte $20,"W+!", hd, WPLUSST,EXIT
byte $20,"W!", hd, WSTORE,EXIT
byte $20,"+!", hd, PLUSST,EXIT
byte $20,"!", hd, STORE,EXIT
byte $20,"BIT!", hd, BIT,EXIT
byte $20,"SET", hd, SET,EXIT
byte $20,"CLR", hd, CLR,EXIT
' byte $20,"MYOP", hd, MYOP,EXIT
byte $20,"U/", hd, UDIVIDE,EXIT
byte $20,"U/MOD", hd, UDIVMOD,EXIT
byte $20,"UM*", hd, UMMUL,EXIT
byte $20,"ABS", hd, _ABS,EXIT
byte $20,"-NEGATE", hd, MNEGATE,EXIT
byte $20,"?NEGATE", hd, QNEGATE,EXIT
byte $20,"NEGATE", hd, NEGATE,EXIT
byte $20,"IN@", hd, PFETCH,EXIT
byte $20,"P@", hd, PFETCH,EXIT
byte $20,"OUT!", hd, PSTORE,EXIT
byte $20,"P!", hd, PSTORE,EXIT
byte $20,"CLOCK", hd, CLOCK,EXIT
byte $20,"STROBE", hd, STROBE,EXIT
byte $20,"OUTSET", hd, OUTSET,EXIT
byte $20,"OUTCLR", hd, OUTCLR,EXIT
byte $20,"OUTPUTS", hd, OUTPUTS,EXIT
byte $20,"INPUTS", hd, INPUTS,EXIT
' byte $20,"WAITLOW", hd, WAITLOW,EXIT
byte $20,"SHROUT", hd, SHROUT,EXIT
byte $20,"SHRINP", hd, SHRINP,EXIT
byte $20,"RESET", hd, RESET,EXIT
byte $20,"0EXIT", hd, ZEXIT,EXIT
byte $20,"EXIT", hd, EXIT,EXIT
byte $20,"NOP", hd, _NOP,EXIT
byte $20,"3DROP", hd, DROP3,EXIT
byte $20,"?DUP", hd, QDUP,EXIT
byte $20,"3RD", hd, THIRD,EXIT
byte $20,"4TH", hd, FOURTH,EXIT
byte $20,"CALL", hd, ACALL,EXIT
byte $20,"JUMP", hd, AJMP,EXIT
byte $20,"BRANCH>", hd, POPBRANCH,EXIT
byte $20,">R", hd, PUSHR,EXIT
byte $20,"R>", hd, RPOP,EXIT
byte $20,">L", hd, PUSHL,EXIT
byte $20,"L>", hd, LPOP,EXIT
byte $20,"REG", hd, ATREG,EXIT
byte $20,"LAP", hd, LAP,EXIT
byte $20,"NEWCNT", hd, LAP,EXIT ' alias for backward compatibilty
byte $20,"DELTA", hd, DELTA,EXIT
byte $20,"WAITCNT", hd, WAITCNTS,EXIT
byte $20,"(WAITPEQ)", hd, _WAITPEQ,EXIT
byte $20,"(WAITPNE)", hd, _WAITPNE,EXIT
byte $20,"(WAITHILO)", hd, WAITHILO,EXIT
{ we don't really need to have the names of these codes in the dictionary
byte $20,"(XCALL)", hd, XCALL,EXIT
byte $20,"(YCALL)", hd, YCALL,EXIT
byte $20,"(ELSE)", hd, _ELSE,EXIT
byte $20,"(IF)", hd, _IF,EXIT
byte $20,"(UNTIL)", hd, _UNTIL,EXIT
byte $20,"(AGAIN)", hd, _AGAIN,EXIT
byte $20,"(REG)", hd, REG,EXIT
byte $20,"(PUSH4)", hd, PUSH4,EXIT
byte $20,"(PUSH3)", hd, PUSH3,EXIT
byte $20,"(PUSH2)", hd, PUSH2,EXIT
byte $20,"(PUSH1)", hd, PUSH1,EXIT
byte $20,"(VAR)", hd, VARB,EXIT
}
byte $20,"LSTACK", hd, LSTACK,EXIT
byte $20,"I", hd, I,EXIT
byte $20,"SPIWRB", hd, SPIWRB,EXIT
byte $20,"SPIWR", hd, SPIWR,EXIT
byte $20,"SPIWRX", hd, SPIWRX,EXIT
byte $20,"SPIRD", hd, SPIRD,EXIT
byte $20,"SPIRDX", hd, SPIRDX,EXIT
' Extended operation - accesses high 256 longs of VM cog
byte $20,"CMOVE", hd+xc+sq, XOP,pCMOVE
byte $20,"(EMIT)", hd+xc+sq, XOP,pEMIT
byte $20,"CMPSTR", hd+xc+sq, XOP,pCMPSTR
byte $20,"LOADMOD", hd+xc+sq, XOP,pLOADMOD
byte $20,"RUNMOD", hd+xc+sq, XOP,pRUNMOD
byte $20,"COGID", hd+xc+sq, XOP,_COGID
byte $20,"!RP", hd+xc+sq, XOP,pInitRP
byte $20,"SPR@", hd+xc+sq, XOP,pSPRFETCH
byte $20,"COG@", hd+xc+sq, XOP,pCOGFETCH
byte $20,"COGREG", hd+xc+sq, XOP,pCOGREG
byte $20,"COG!", hd+xc+sq, XOP,pCOGSTORE
byte $20,"PASM", hd+xc+sq, XOP,pPASM
' Two instruction sequences
'
byte $20,"2+", hd+xc+sq, _2,PLUS
byte $20,"2-", hd+xc+sq, _2,MINUS
byte $20,"2DUP", hd+xc+sq, OVER,OVER
byte $20,"*", hd+xc+sq, UMMUL,DROP
byte $20,"0<>", hd+xc+sq, ZEQ,ZEQ
byte $20,"<>", hd+xc+sq, EQ,ZEQ
{ INTERPRETED BYTECODE HEADERS }
byte $20,"!SP", hd+xc, XCALL,xInitStack
byte $20,"SP!", hd+xc, XCALL,xSPSTORE
byte $20,"DEPTH", hd+xc, XCALL,xDEPTH
byte $20,"(:)", hd+xc, XCALL,x_COLON
byte $20,":", hd+xc+im, XCALL,xCOLON
byte $20,"pub", hd+xc+im, XCALL,xPUBCOLON
byte $20,"pri", hd+xc+im, XCALL,xPRIVATE
byte $20,"IF", hd+xc+im, XCALL,x_IF_
byte $20,"ELSE", hd+xc+im, XCALL,x_ELSE_
byte $20,"THEN", hd+xc+im, XCALL,x_THEN_
byte $20,"ENDIF", hd+xc+im, XCALL,x_THEN_
byte $20,"BEGIN", hd+xc+im, XCALL,x_BEGIN_
byte $20,"UNTIL", hd+xc+im, XCALL,x_UNTIL_
byte $20,"AGAIN", hd+xc+im, XCALL,x_AGAIN_
byte $20,"WHILE", hd+xc+im, XCALL,x_IF_
byte $20,"REPEAT", hd+xc+im, XCALL,x_REPEAT_
byte $20,";", hd+xc+im, XCALL,xENDCOLON
byte $20,"\", hd+xc+im, XCALL,xCOMMENT
byte $20,"''", hd+xc+im, XCALL,xCOMMENT
byte $20,"(", hd+xc+im, XCALL,xBRACE
byte $20,"{", hd+xc+im, XCALL,xCURLY
byte $20,"}", hd+xc+im, _NOP,EXIT
byte $20,"IFNDEF", hd+xc+im, XCALL,xIFNDEF
byte $20,"IFDEF", hd+xc+im, XCALL,xIFDEF
byte $20,$22, hd+xc+im, XCALL,x_STR_
byte $20,$2E,$22, hd+xc+im, XCALL,x_PSTR_
byte $20,"(",$2E,$22,")", hd+xc+im, XCALL,xPRTSTR
byte $20,"TO", hd+xc+im, XCALL,xATO
byte $20,"COMPILE", hd+xc+im, XCALL,xCOMPILE
byte $20,"BCOMP", hd+xc+im, XCALL,xBCOMP
byte $20,"C,", hd+xc+im, XCALL,xCCOMP
byte $20,"|", hd+xc+im, XCALL,xCCOMP
byte $20,"||", hd+xc+im, XCALL,xWCOMP
byte $20,",", hd+xc+im, XCALL,xLCOMP
byte $20,"BREAK", hd+xc+im, XCALL,xISEND
byte $20,"CASE", hd+xc+im, XCALL,xIS
byte $20,"SWITCH", hd+xc, XCALL,xSWITCH
byte $20,"SWITCH@", hd+xc, XCALL,xSWITCHFETCH
byte $20,"SWITCH=", hd+xc, XCALL,xISEQ
byte $20,"SWITCH><", hd+xc, XCALL,xISWITHIN
byte $20,"STACKS", hd+xc, XCALL,xSTACKS
byte $20,"XCALLS", hd+xc, XCALL,xXCALLS
byte $20,"REBOOT", hd+xc, XCALL,xREBOOT
byte $20,"STOP", hd+xc, XCALL,xSTOP
byte $20,"[SSD]", hd+xc, XCALL,xSSD
byte $20,"[ESPIO]", hd+xc, XCALL,xESPIO
byte $20,"[SPIO]", hd+xc, XCALL,xSPIO
byte $20,"[SPIOD]", hd+xc, XCALL,xSPIOD
byte $20,"[SDRD]", hd+xc, XCALL,xSDRD
byte $20,"[SDWR]", hd+xc, XCALL,xSDWR
byte $20,"[PWM32]", hd+xc, XCALL,xPWM32
byte $20,"[PWM32!]", hd+xc, XCALL,xPWMST32
byte $20,"[PLOT]", hd+xc, XCALL,xPLOT
byte $20,"BCA", hd+xc, XCALL,xBCA
byte $20,"SET?", hd+xc, XCALL,xSETQ
' byte $20,"MIN", hd+xc, XCALL,xMIN
' byte $20,"MAX", hd+xc, XCALL,xMAX
byte $20,"0<", hd+xc, XCALL,xZLT
byte $20,"<", hd+xc, XCALL,xLT
byte $20,"U<", hd+xc, XCALL,xULT
byte $20,"WITHIN", hd+xc, XCALL,xWITHIN
byte $20,"?EXIT", hd+xc, XCALL,xIFEXIT
' byte $20,"J", hd+xc, XCALL,xJ
' byte $20,"K", hd+xc, XCALL,xK
' byte $20,"IX", hd+xc, XCALL,xIX
byte $20,"ERASE", hd+xc, XCALL,xERASE
byte $20,"FILL", hd+xc, XCALL,xFILL
byte $20,"ms", hd+xc, XCALL,xms
byte $20,"us", hd+xc, XCALL,xus
byte $20,"READBUF", hd+xc, XCALL,xREADBUF
byte $20,"KEY?", hd+xc, XCALL,xKEYQ
byte $20,"KEY", hd+xc, XCALL,xKEY
byte $20,"HEX", hd+xc, XCALL,xHEX
byte $20,"DECIMAL", hd+xc, XCALL,xDECIMAL
byte $20,"BINARY", hd+xc, XCALL,xBIN
byte $20,".S", hd+xc, XCALL,xPRTSTK
byte $20,"HDUMP", hd+xc, XCALL,xDUMP
byte $20,"COGDUMP", hd+xc, XCALL,xCOGDUMP
byte $20,".STACKS", hd+xc, XCALL,xPRTSTKS
byte $20,"DEBUG", hd+xc, XCALL,xDEBUG
byte $20,"EMIT", hd+xc, XCALL,xEMIT
' byte $20,"?EMIT", hd+xc, XCALL,xQEMIT
byte $20,"CLS", hd+xc, XCALL,xCLS
byte $20,"SPACE", hd+xc, XCALL,xSPACE
byte $20,"BELL", hd+xc, XCALL,xBELL
byte $20,"CR", hd+xc, XCALL,xCR
byte $20,"SPINNER", hd+xc, XCALL,xSPINNER
byte $20,".HEX", hd+xc, XCALL,xPRTHEX
byte $20,".BYTE", hd+xc, XCALL,xPRTBYTE
byte $20,".WORD", hd+xc, XCALL,xPRTWORD
byte $20,".LONG", hd+xc, XCALL,xPRTLONG
byte $20,".", hd+xc, XCALL,xPRT
byte $20,">DIGIT", hd+xc, XCALL,xTODIGIT
byte $20,"NUMBER", hd+xc, XCALL,xNUMBER
' byte $20,">UPPER", hd+xc, XCALL,xTOUPPER
byte $20,"SCRUB", hd+xc, XCALL,xSCRUB
byte $20,"GETWORD", hd+xc, XCALL,xGETWORD
byte $20,"SEARCH", hd+xc, XCALL,xSEARCH
byte $20,"FINDSTR", hd+xc, XCALL,xFINDSTR
byte $20,"NFA>CFA", hd+xc, XCALL,xNFACFA
byte $20,"EXECUTE", hd+xc, XCALL,xEXECUTE
' byte $20,"V2", hd+xc, XCALL,xGETVER
byte $20,"VER", hd+xc, XCALL,xGETVER
byte $20,".VER", hd+xc, XCALL,xPRTVER
byte $20,"TACHYON", hd+xc, XCALL,x_TACHYON
byte $20,"@PAD", hd+xc, XCALL,xATPAD
byte $20,"HOLD", hd+xc, XCALL,xHOLD
byte $20,">CHAR", hd+xc, XCALL,xTOCHAR
byte $20,"#>", hd+xc, XCALL,xRHASH
byte $20,"<#", hd+xc, XCALL,xLHASH
byte $20,"#", hd+xc, XCALL,xHASH
byte $20,"#S", hd+xc, XCALL,xHASHS
' byte $20,"(STR)", hd+xc, XCALL,x_STR
byte $20,"PRINT$", hd+xc, XCALL,xPSTR
byte $20,"LEN$", hd+xc, XCALL,xSTRLEN
byte $20,"U.", hd+xc, XCALL,xUPRT
byte $20,".DEC", hd+xc, XCALL,xPRTDEC
byte $20,"DISCARD", hd+xc, XCALL,xDISCARD
byte $20,"COGINIT", hd+xc, XCALL,xCOGINIT
byte $20,"<CMOVE", hd+xc, XCALL,xRCMOVE
' TASK REGISTERS
byte $20,"REG", hd+xc, REG,0 '
byte $20,"flags", hd+xc, REG,flags
byte $20,"base", hd+xc, REG,base
byte $20,"digits", hd+xc, REG,digits
byte $20,"delim", hd+xc, REG,delim
byte $20,"word", hd+xc, REG,wordbuf
byte $20,"switch", hd+xc, REG,uswitch
byte $20,"autorun", hd+xc, REG,autovec
byte $20,"keypoll", hd+xc, REG,keypoll
byte $20,"tasks", hd+xc, REG,tasks
byte $20,"unum", hd+xc, REG,unum ' user number processing
byte $20,"uemit", hd+xc, REG,uemit
byte $20,"ukey", hd+xc, REG,ukey
byte $20,"names", hd+xc, REG,names
byte $20,"here", hd+xc, REG,here
byte $20,"codes", hd+xc, REG,codes
byte $20,"errors", hd+xc, REG,errors
byte $20,"baudcnt", hd+xc, REG,baudcnt
byte $20,"prompt", hd+xc, REG,prompt
byte $20,"ufind", hd+xc, REG,findvec ' user find method
byte $20,"create", hd+xc, REG,createvec ' user CREATE
byte $20,"lines", hd+xc, REG,linenum
byte $20,"lastkey", hd+xc, REG,lastkey
byte $20,"ALLOT", hd+xc, XCALL,xALLOT
byte $20,"ALLOCATED", hd+xc, XCALL,xALLOCATED
byte $20,"HERE", hd+xc, XCALL,xHERE
byte $20,"AUTO!", hd+xc, XCALL,xAUTOST
byte $20,">VEC", hd+xc, XCALL,xTOVEC
byte $20,">PFA", hd+xc, XCALL,xPFA
byte $20,"[NFA']", hd+xc, XCALL,xNFATICK
byte $20,"[']", hd+xc, XCALL,xTICK
byte $20,"ERROR", hd+xc, XCALL,xERROR
byte $20,"NOTFOUND", hd+xc, XCALL,xNOTFOUND
' IMMEDIATE
byte $20,"NFA'", hd+xc+im, XCALL,x_NFATICK
byte $20,"'", hd+xc+im, XCALL,xATICK
byte $20,"KEYPOLL", hd+xc+im, XCALL,xSETPOLL
byte $20,"AUTORUN", hd+xc+im, XCALL,xAUTORUN
' Building words
byte $20,"[COMPILE]", hd+xc+im, XCALL,xCOMPILES
byte $20,"GRAB", hd+xc+im, XCALL,xGRAB
byte $20,"LITERAL", hd+xc+im, XCALL,xLITCOMP
byte $20,"(CREATE)", hd+xc, XCALL,xCREATESTR
byte $20,"CREATEWORD", hd+xc+im, XCALL,xCREATEWORD
byte $20,"CREATE", hd+xc+im, XCALL,xCREATE
byte $20,"TASK", hd+xc, XCALL,xTASK
byte $20,"IDLE", hd+xc, XCALL,xIDLE
byte $20,"LOOKUP", hd+xc, XCALL,xVECTORS
byte $20,"VECTORS", hd+xc, XCALL,xVECTORS
byte $20,"+CALL", hd+xc, XCALL,xAddACALL
byte $20,"BUFFERS", hd+xc, XCALL,xBUFFERS
byte $20,"FREE", hd+xc, XCALL,xFREE
byte $20,"TERMINAL", hd+xc, XCALL,xTERMINAL
byte $20,"CONSOLE", hd+xc, XCALL,xCONSOLE
byte $20,"KOLD", hd+xc, XCALL,xCOLDST 'renamed to avoid conflict
byte $20,"*end*", hd+xc+sq, _NOP,_NOP ' dummy used to find the end
enddict byte 0,0,0 ' Mark the end of the kernel dictionary (2nd and 3rd byte is a pointer or null)
org $10
s ' just an offset to be used in DAT sections rather than the distracting +$10
' Align this area to a 256 byte boundary
aln1 byte 0[$100-(@aln1+s-((@aln1+s)&$FF00))]
{ TACHYON VM - COG KERNEL (PASM) } |
DAT
{{
Byte tokens directly address code in the first 256 longs of the cog.
A two byte-code instruction XOP allows access to the second 256 longs
Rather than a jump table most functions are short or cascaded to optimize COG memory
Larger fragments of code jump to the second half of the cog's memory.
As a result of not using a jump table (there's not enough memory) there are gaps
in the bytecode values and not all values are usable.
The formatted source has bytecode instruction labels as bold white on red background.
}}
org 0
RESET mov IP,PAR ' Load the IP with the address of the first instruction
' * * * *
' Fetch the next byte code instruction in hub RAM pointed to by the instruction pointer IP
' This is the very heart of the runtime interpreter
'
doNEXT rdbyte instr,IP 'read byte code instruction
add IP,#1 wc 'advance IP to next byte token (clears the carry too!)
jmp instr 'execute the code by directly indexing the first 256 long in cog
' Use next byte as an opcode that directly addresses top 256 words of cog
XOP rdbyte instr,IP ' get next bytecode
or instr,#$100 ' shift range
jmp #doNext+1 ' IP++, execute
' Find the end of the string which could end in a null or any characeter >$7F
' this is also used to find the end of a larger text buffer
' STREND ( ptr -- ptr2 )
STREND
fchlp rdbyte R0,tos ' read a byte
sub R0,#1 ' end is either a null or anything >$7F
cmp R0,#$7E wc
if_c add tos,#1
if_c jmp #fchlp
jmp unext
{ seldom used - disabled for other ops
' REPS ( cnt -- ) Repeat the next single bytecode instruction from cache, does not need to reread hub for each iteration
REPS mov repcnt,tos ' get the repeat count
rdbyte instr,IP ' read the next instr to repeat
mov unext,#doREPS ' ensures that the "return" goes where we want
call #POPX ' discard count and go straight into a repeat
doREPS djnz repcnt,instr wc ' count repeats here less 1 but on exit instruction is executed again
mov unext,#doNEXT ' restore normal runtime return to interpreter
jmp #doNEXT+1 ' execute cached instruction last time then back to nomral
}
' 0EXIT ( flg -- ) Exit if flg is false (or zero) Used in place of IF......THEN EXIT as false would just end up exiting
ZEXIT call #POPX
tjnz X,unext
'
' EXIT a bytecode definition by popping the top of the return stack into the IP
EXIT call #RPOPX ' Pop from return stack into X
JUMPX mov IP,X ' update IP
_NOP jmp unext ' continue
' DROP3 ( n1 n2 n3 -- ) Pop the top 3 items off the datastack and discard them (used mostly by cog kernel)
DROP3 call #POPX
' DROP2 ( n1 n2 -- ) Pop the top 2 items off the datastack and discard them
DROP2 call #POPX
' 1us execution time including bytecode read and execute
' DROP ( n1 -- ) Pop the top item off the datastack and discard it
DROP call #POPX
jmp unext
' ?DUP ( n1 -- n1 n1 | 0 ) DUP n1 if non-zero
QDUP tjz tos,unext
' DUP ( n1 - n1 n1 ) Duplicate the top item on the stack
DUP mov X,tos ' Read directly from the top of the data stack
PUSHX call #_PUSHX ' Push the internal X register onto the datastack
jmp unext
' OVER ( n1 n2 -- n1 n2 n1 )
OVER mov X,tos+1 'read second data item and push
jmp #PUSHX
' 3RD ( n1 n2 n3 -- n1 n2 n3 n1 ) Copy the 3rd item onto the stack
THIRD mov X,tos+2 ' read third data item
jmp #PUSHX
' 4TH ( n1 n2 n3 n4 -- n1 n2 n3 n4 n1 ) Copy the 4th item onto the stack
FOURTH mov X,tos+3
jmp #PUSHX
' +FIB ( n1 n2 -- n2+n1 n1 ) == OVER + SWAP
FIB add tos+1,tos
' SWAP ( n1 n2 -- n2 n1 ) Swap the top two items
SWAP mov X,tos+1
SWAPX mov tos+1,tos
PUTX mov tos,X
jmp unext
' ROT ( a b c -- b c a )
ROT mov X,tos+2
mov tos+2,tos+1
jmp #SWAPX
' - ( n1 n2 -- n3 ) Subtract n2 from n1
MINUS neg tos,tos ' (note: save one long by negating and adding)
' + ( n1 n2 -- n3 ) Add top two stack items together and replace with result
PLUS add tos+1,tos
jmp #DROP
' 1- ( n1 -- n1-1 )
DEC test $,#1 wc
' 1+ ( n1 -- n1+1 )
INC sumc tos,#1 ' inc or dec depending upon carry (default cleared by doNEXT)
jmp unext
' -NEGATE ( n1 sn -- n1 | -n1 ) negate n1 if the sign of sn is negative (used in signed divide op)
MNEGATE shr tos,#31
' ?NEGATE ( n1 flg -- n2 ) negate n1 if flg is true
QNEGATE tjz tos,#DROP
call #POPX
' NEGATE ( n1 -- n2 ) equivalent to n2 = 0-n1
NEGATE neg tos,tos
jmp unext
' u/mod ( u1 u2 -- remainder quotient) both remainder and quotient are 32 bit unsigned numbers
UDIVMOD call #_UDIVMOD
jmp unext
' U/ ( n1 n2 -- n3 ) unsigned divide
UDIVIDE call #_UDIVMOD
NIP mov tos+1,tos
jmp #DROP
' 400ns execution time including bytecode read and execute
' INVERT ( n1 -- n2 ) bitwise invert n1 and replace with result n2
INVERT add tos,#1
jmp #NEGATE
_AND and tos+1,tos
jmp #DROP
_ANDN andn tos+1,tos
jmp #DROP
_OR or tos+1,tos
jmp #DROP
_XOR xor tos+1,tos
jmp #DROP
' 1.2us execution time including bytecode read and execute
' SHR ( n1 cnt -- n2 ) Shift n1 right by count (5 lsbs )
_SHR shr tos+1,tos
jmp #DROP
_SHL shl tos+1,tos
jmp #DROP
_ROL rol tos+1,tos
jmp #DROP
_ROR ror tos+1,tos
jmp #DROP
' 400ns execution time including bytecode read and execute
' 2/ ( n1 -- n1 ) shift n1 right one bit (equiv to divide by 2)
_SHR1 shr tos,#1
jmp unext
' 2* ( n1 -- n2 ) shift n1 left one bit (equiv to multiply by 2)
_SHL1 shl tos,#1
jmp unext
' REV ( n1 bits -- n2 ) Reverse LSBs of n1 and zero-extend
_REV rev tos+1,tos
jmp #DROP
' 400ns execution time including bytecode read and execute
' MASK ( bitpos -- bitmask \ only the lower 5 bits of bitpos are taken, regardless of the higher bits )
MASK mov X,tos
mov tos,#1
shl tos,X
jmp unext
' >N ( n -- nibble ) mask n to a nibble
toNIB and tos,#$0F
' >B ( n -- nibble ) mask n to a byte
toBYTE and tos,#$FF
jmp unext
' Basic instructions from which other comparison instructions are built from
' = ( n1 n2 -- flg ) true if n1 is equal to n2
EQ sub tos+1,tos ' n1 == 0 if equal
call #POPX ' drop n2
' -------------
' 0= ( n1 -- flg ) true if n1 equals 0 - same as a boolean NOT where TRUE becomes FALSE
_NOT
ZEQ cmp tos,#1 wc ' kuroneko method, nice and neat
SETZ subx tos, tos ' a carry becomes -1, else 0
jmp unext
' > ( n1 n2 -- flg ) true if n1 > n2
GT
_GT cmps tos,tos+1 wc ' n1 > n2: carry set
subx tos+1,tos+1
jmp #DROP
' C@++ ( caddr -- caddr+1 byte ) fetch byte character and increment address
CFETCHINC mov X,tos ' dup the address
call #_PUSHX
add tos+1,#1 ' inc the backup address
' C@ ( caddr -- byte ) Fetch a byte from hub memory
CFETCH rdbyte tos,tos
jmp unext
' W@ ( waddr -- word ) Fetch a word from hub memory
WFETCH rdword tos,tos
jmp unext
' @ ( addr -- long ) Fetch a long from hub memory
FETCH rdlong tos,tos
jmp unext
' C+! ( n caddr -- ) add n to byte at hub addr
CPLUSST rdbyte X,tos ' read in word from adress
add tos+1,X ' add to contents of address - cascade
' C! ( n caddr -- ) store n to byte at addr
CSTORE wrbyte tos+1,tos ' write the byte using address on the tos
jmp #DROP2
' W+! ( n waddr -- ) add n to word at hub addr
WPLUSST rdword X,tos ' read in word from address
add tos+1,X
' W! ( n waddr -- ) store n to word at addr
WSTORE wrword tos+1,tos
jmp #DROP2
' +! ( n addr -- ) add n to long at hub addr
PLUSST rdlong X,tos ' read in long from address
add tos+1,X
' ! ( n addr -- ) store n to long at addr
STORE wrlong tos+1,tos
jmp #DROP2
' BIT! ( mask caddr state -- ) Set or clear bit(s) in hub byte
BIT call #POPX
tjz X,#CLR ' carry clear, finalize
' SET ( mask caddr -- ) Set bit(s) in hub byte
SET test $,#1 wc ' set the carry flag
' Finalize the bit operation by read/writing the result
' ( mask caddr -- )
CLR rdbyte X,tos ' Read the contents of the memory location
muxc X,tos+1 ' set or clear the bit(s) specd by mask
wrbyte X,tos ' update
jmp #DROP2
' programmable OPCODE or default fast crop operation - set mask in COGREG3 once and just issue CROP
' change at Forth level with <opcode> ' MYOP COG!
' NOTE: MYOP is not used in the kernel or EXTEND so it can be set by the user to suit.
' MYOP ( n -- n&mask )
MYOP ' and tos,reg3
' jmp unext
' 3.6us execution time including bytecode read and execute
' ( -- 32bits ) Push a 32-bit literal onto the datastack by reading in the next 4 bytes (non-aligned)
_LONG
PUSH4 call #ACCBYTE ' read the next byte @IP++ and shift accumulate
' 3us execution time including bytecode read and execute
' ( -- 24bits ) Push a 24-bit literal onto the datastack by reading in the next 3 bytes (non-aligned)
PUSH3 call #ACCBYTE
_WORD
' 2.4us execution time including bytecode read and execute
' ( -- 16bits) Push a 16-bit literal onto the datastack by reading in the next 2 bytes (non-aligned)
PUSH2 call #ACCBYTE
' 1.8us execution time including bytecode read and execute
' ( -- 8bits ) Push an 8-bit literal onto the datastack by reading in the next byte
_BYTE
PUSH1 call #ACCBYTE
PUSHACC call #_PUSHACC ' Push the accumulator onto the stack then zero it
jmp unext
' Push a preset literal onto the stack using just one bytecode
' Use the "accumulator" to push the value which is built up by incrementing and/or decrementing
' There is a minor penalty for the larger constants but it's still faster and more compact
' overall than using the PUSH1 method or the mov X,# method
' 140606 just reordered to 1 4 2 3 according to BCA results
' 140603 new method to allow any value in any order, relies on carry being cleared in doNEXT and min will always set carry here
BL if_nc min ACC,#32+1 wc ' 1.52us
_16 if_nc min ACC,#16+1 wc
_8 if_nc min ACC,#8+1 wc
_4 if_nc min ACC,#4+1 wc
_2 if_nc min ACC,#2+1 wc
_1 if_nc min ACC,#1+1 wc
_3 if_nc min ACC,#3+1 wc ' bytecode analysis reveals 3 is used quite heavily
_TRUE
MINUS1 sub ACC,#1
_FALSE
_0 jmp #PUSHACC ' 1.12us
' Long aligned constant - created with CONSTANT and already aligned
CONL
rdlong X,IP ' get constant
jmp #PUSHX_EXIT
' Byte aligned variables start with this single byte code which returns with the address of the byte variable following
' long variables just externally align this opcode a byte before the boundary
' INLINE:
VARB mov X,IP
PUSHX_EXIT call #_PUSHX ' push address of variable
jmp #EXIT
' P@ ( -- n1 ) Read the input port A (assume it is always A for Prop 1)
PFETCH mov X,INA
jmp #PUSHX
' P! ( n1 -- ) Store n1 to the output port A
PSTORE mov OUTA,tos
jmp #DROP
' CLOCK ( COGREG4=iomask ) Toggle multiple bits on the output)
CLOCK xor OUTA,clockpins
jmp unext
' STROBE ( iomask -- ) Generate a 100ns low pulse - pins must be preset as outputs (first up anyway)
STROBE andn OUTA,tos ' strobe low
jmp #OUTSET ' release high (use jmp to add one extra cycle)
' OUTCLR ( iomask -- ) Clear multiple NUMBERbits on the output
OUTCLR andn OUTA,tos
jmp #OUTPUTS
' OUTMASK ( data iomask -- )
' call #POPX
andn OUTA,X ' clear all iomask outputs
' OUTSET ( iomask -- ) Set multiple bits on the output
OUTSET or OUTA,tos
' OUTPUTS ( iomask -- ) Set selected port pins to outputs
OUTPUTS or DIRA,tos
jmp #DROP
' INPUTS ( iomask -- ) Set selected port pins to inputs
INPUTS andn DIRA,tos
jmp #DROP
{
' Waits for a low on the input but with a timeout = 150ns/count (1,000,000 = 150ms)
' returns with cnt non-zero if low is detected
' WAITLOW ( cnt iomask -- cnt )
WAITLOW test tos,INA wz
if_z jmp #DROP
djnz tos+1,#WAITLOW
jmp #DROP
}
' 130910 - change the way WAITP operates in that it reads a mask from COGREGS and also captures the CNT
WAITHILO waitpeq reg3,reg3 ' wait for a hi to lo - look for falling edge
' WAITPNE Wait until input is low - REG3 = mask, REG0 = CNT
_WAITPNE waitpne reg3,reg3 ' use COGREG3 as the mask
mov reg0,cnt ' capture count in COGREG0
jmp unext
' WAITPEQ Wait until input is high - REG3 = mask, REG1 = CNT
_WAITPEQ waitpeq reg3,reg3
mov reg1,cnt ' capture count in COGREG1
jmp unext
{
To maximize the speed of I/O operations especially serial I/O such as ASYNCH, I2C and SPI etc there are special
operators that avoid pushing and popping the stack and instead perform the I/O bit by bit and leave the
latest shifted version of the data on the stack.
}
' SHIFT from INPUT - Assembles with last bit received as msb - needs SHR to right justify if asynch data
' SHRINP ( iomask dat -- iomask dat/2 )
SHRINP test tos+1,INA wc
rcr tos,#1
jmp unext
{ SHIFT to OUT -
This is optimized for when you are sending out multiple bits as in asynchronous serial data or I2C
Shift data one bit right into output via iomask - leave mask & shifted data on stack (looping)
400ns execution time including bytecode read and execute or 200ns/bit with REPS }
' SHROUT ( iomask dat -- iomask dat/2 )
SHROUT shr tos,#1 wc ' Shift right and get lsb
muxc OUTA,tos+1 ' reflect state to output
jmp unext
{ *** SPI *** }
' SPI INSTRUCTIONS
' Simple fast, bare-bones SPI - transmits 8 bits MSB first - data must be left justified - data is not discarded
' Usage: $1234 16 SHL SPIWR SPIWR 'transmit 16 bits
' SPIWRX permits varibale number of bits if spicnt is set directly with @SPICNT COGREG!
' SPIWR ( data -- data<<8 )
' SPIWRX could be an instruction if we manually set the spicnt beforehand
SPIWRB shl tos,#24 ' left justify and write byte to SPI (2.8us)
SPIWR mov spicnt,#8 ' code execution time of 2.25us + overhead
SPIWRX rol tos,#1 wc ' assume msb first (140208 update now rotates)
muxc OUTA,spiout ' send next bit of data out
xor OUTA,spisck ' toggle clock
xor OUTA,spisck ' toggle clock
djnz spicnt,#SPIwrX
jmp unext
' Receive data and shift into existing stack parameter
' If MOSI needs to be in a certain state then this should be set beforehand
' Clock will simply pulse from it's starting state - normally low
' Useage: 0 SPIRD SPIRD SPIRD SPIRD 'read 32-bits as one long
' SPIRD ( data -- data<<8 ) ' 2.8us
SPIRD mov spicnt,#8 ' always read back 8-bits
SPIRDX xor OUTA,spisck ' toggle clock
test spiinp,INA wc ' read data from device
rcl tos,#1
xor OUTA,spisck ' toggle clock
djnz spicnt,#SPIRDX
jmp unext
' I ( -- index ) The current loop index is at a fixed address just under the loop limit at top of ascending loop stack
I mov X,loopstk+1
jmp #PUSHX
' Loop control words such as J K LEAVE etc implemented
' LSTACK ( index -- cog_addr ) push address of the loop stack in cog memory
LSTACK add tos,#loopstk
jmp unext
' ACALL ( adr -- ) Call arbitrary (from the stack) address
ACALL call #SAVEIP ' save current IP onto return stack
AJMP mov IP,tos ' jump to address on top of the data stack
jmp #DROP
{
Since Tachyon uses bytecodes for instructions it also uses bytes to address code.
Since bytes are limited to 256 values these are used instead to lookup the absolute address of the code
from a table of vectors. To extend that beyond 256 values there are further opcodes which index the table for a total of 1,024 vectors.
Vectored call instructions form a 10-bit index with the upper 2 bits derived from the instruction so calls to other words only use 2 bytes total
Note: Originally there was only an XCALL but new opcodes were added to expand the vector table however the compiled kernel only uses XCALL
}
VCALL mov ACC,#2 ' high word of upper page
ZCALL add ACC,zcalls ' low word of upper page
' Perform a call to kernel bytecode via the XCALLS but reusing the high word of each vector
' The YCALLs are implemented by the runtime compiler to fully utilize the XCALLs table (high word of longs)
YCALL add ACC,#2
' Inline call to kernel bytecode via the XCALLS vector table using the following inline byte as an index
XCALL add ACC,Xptr ' ACC = vector table offset ( lopage,loword -> hipage,hiword)
xycall call #SETUPIP ' Save IP and read next bytecode into X (offset in table)
shl X,#2 ' offset into longs in hub RAM
add X,ACC ' Add to vector table base,now points to vector
rdword IP,X ' Load IP with vector, now points to bytecode
ACC0 mov ACC,#0 ' Always clear ACC to zero ready for reuse (repos for hub access)
jmp unext
' Read the next byte as a negative displacement and jump back
_AGAIN
test $,#1 wc ' setc for negative displacement
' Jump forward by reading the next byte inline and adding that to the IP (at that point)
JUMP
_ELSE call #GETBYTE ' read the forward displacement byte
sumc IP,X ' jump to that forward location
jmp unext
' If flg is zero than jump forward by reading the next byte inline and adding that to the IP (at that point)
' IF R( flg -- )
' Read the next byte as a positive displacement and branch forward
JZBACK
_UNTIL test $,#1 wc 'set carry for negative branch
JZ
_IF call #GETBYTE 'read in next byte at IP and inc IP
JMPIF tjnz tos,#DROP 'test flag on stack - if non-zero then discard the branch
sumc IP,X 'Adjust IP forward according to flag
jmp #DROP 'discard flag
' ADO = BOUNDS DO - just a quick and direct way as BOUNDS is most often never used elsewhere
' ADO ( from cnt -- )
ADO mov X,tos+1
add tos+1,tos
mov tos,X
' DO ( to from -- )
DO call #_PUSHLP ' PUSH index onto loop stack
' FOR ( count -- ) Setup FOR...NEXT loop for count
FOR call #_PUSHBRANCH ' Push the IP onto the mark stack and set branch
' >L ( n -- ) Push n onto the loop stack
PUSHL call #_PUSHLP
jmp unext
' L> ( -- n ) Pop n from the loop stack
LPOP call #LPOPX ' Pop loop stack into X
jmp #PUSHX ' Push X onto the data stack as tos
' (+loop) ( n1 -- ) adds n1 to the loop index and branches back if not equal to the loop limit
PLOOP jmpret POPX_ret,DELTA wc ' DELTA calls POPX
' The comparison above is between the call insn (wr) at DELTA and the jump insn (nr) at POPX_ret,
' this will alwaye be carry set. The call itself is indirect.
' 400ns execution time including bytecode read and execute
LOOP if_nc mov X,#1 ' default loop increment of 1
add loopstk+1,X ' increment index
cmps loopstk,loopstk+1 wz,wc
BRANCH if_a mov IP,branchstk ' Branch to the address that is saved in branch
if_a jmp unext
jmpret LPOPX_ret,forNEXT+1 wc ' discard top of loop index stack
' then next loop and its branch address
' The call above borrows an indirect jump target from the call #LPOPX following the djnz at forNEXT.
' IOW it’s equivalent to a jmpret LPOPX_ret, #LPOPX or call #LPOPX (ignoring flag update).
' Average execution time = 400ns
' NEXT ( -- ) Decrement count (on loop stack) and loop until 0, then pop loop stack
forNEXT if_nc djnz loopstk,#BRANCH wc,wz ' not done yet, jump to branch
call #LPOPX
' POPBRANCH - pops the branch stack manually (use if forcing an exit from a stacked branch)
POPBRANCH call #_POPBRANCH
jmp unext
' >R ( n -- ) Push n onto the return stack
PUSHR mov R0,tos
call #_PUSHR
jmp #DROP
' R> ( -- n ) Pop n from the return stack
RPOP call #RPOPX ' Pop return stack into R and X
jmp #PUSHX ' Push X onto the data stack as tos
'' Registers can be used just like variables and the interpreted kernel uses some for itself
'' 128+ bytes are reserved which can be accessed as bytes/words/longs depending upon
'' the alignment. Since the registers are pointed to by "regptr" they can relocated
' (REG) ( -- addr ) Read the next inline byte and return with the register byte address
REG call #GETBYTE
call #_PUSHX
' REG ( index -- addr ) Find the address of the register
ATREG add tos,regptr
jmp unext
' temporary timing instructions
' Capture the current cnt value and calulate cycles since last LAP - result in lapcnt
' LAP ( -- ) delta in lapcnt (created independant regs rather than REG3,4)
LAP neg lapcnt,target ' -old
mov target,cnt ' new
add lapcnt,target ' new-old
jmp unext
' DELTA ( delta -- ) Calculate and set the cnt delta and waitcnt
DELTA call #POPX
mov deltaR,X ' use otherwise unused video reg for delta
mov cnt,X ' (kernel isn’t doing any video)
add cnt,cnt
' WAITCNT ( -- )
WAITCNTS waitcnt cnt,deltaR
jmp unext
' ABS ( n -- abs )
_ABS abs tos,tos
jmp unext
' um* ( u1 u2 -- u1*u2L u1*u2H ) \ unsigned 32bit * 32bit -- 64bit result - 1..11.8us
UMMUL mov R0,tos+1
min R0,tos ' max(tos, tos+1)
mov R2,tos+1
max R2,tos ' min(tos, tos+1)
mov R1,#0
mov tos,#0 ' zero result
mov tos+1,#0
UMMULLP shr R2,#1 wz,wc ' test next bit of u1
if_nc jmp #UMMUL1 ' skip if no bit here
add tos+1,R0 wc ' add in shifted u2
addx tos,R1 ' carry into upper long
UMMUL1 add R0,R0 wc ' shift u2 left
addx R1,R1 ' carry into 64-bits
if_nz jmp #UMMULLP ' exhausted u1?
jmp unext
{{*********************************** INTERNAL COG ROUTINES *********************************
Code from here after cog address $0FF cannot be indexed directly except by an XOP. }}
''
'' Compare a null-terminated source string with a dictionary string which is 8th bit terminated.
'' This will always force a mismatch after which one is checked for a null while the other is checked
'' for the 8th bit and if verified then a match has been found.
'' The dict pointer is advanced to point to the end of the dict string on the 8th bit termination which
'' is the attribute byte as in: byte "CMPSTR",$80,CMPSTR
''
' XOP
' CMPSTR ( src dict 0 -- src dict flg ) Compare strings at these two addresses
pCMPSTR
' CMPSTR ( src dict flg-- src dict+ flg ) Compare strings at these two addresses
mov R2,tos+1 wc ' force nc on entry, s[31] == 0 (doNEXT clears c)
mov X,tos+2 ' X = source
cmpstrlp rdbyte R0,X ' read in a character from the source
add X,#1 ' hub has to wait anyway so get ready for next source byte
if_c add R2,#1 ' updates the copy of the dictionary pointer
rdbyte R1,R2 ' read in from the dictionary
cmp R1,R0 wz ' are they the same?
if_z jmpret par,#cmpstrlp wc,nr ' keep at it, set carry to enable dict+ (for local copy)
nomatch cmp R0,#1 wc ' was the src null terminated? (C means we have matched the string)
if_c test R1,#$80 wc ' set c flag if dict 8th bit set (C = cross-matched)
jmp #SETZ ' Change our flag to -1 if C set (matched)
' pRCMOVE bytes from source to destination primitive - <CMOVE conditions the parameters before calling
' XOP (RCMOVE) ( src dst cnt -- ) Copy bytes from src to dst for cnt bytes starting from the ends (in reverse)
pRCMOVE test $,#1 wc ' set carry for decrementing (always cleared by doNEXT)
' XOP (CMOVE) ( src dst cnt -- ) Copy cnt bytes from src to dst address
pCMOVE rdbyte R0,bsrc ' read source byte
sumc bsrc,#1 ' inc or dec depending upon carry
wrbyte R0,bdst ' write destination byte
sumc bdst,#1 ' inc or dec depending upon carry!!
djnz bcnt,#pCMOVE
jmp #DROP3
_COGID cogid X
jmp #PUSHX
' _COGINIT ( dest -- cog )
_COGINIT coginit tos wr
jmp unext
' INIT STACKS
pInitRP
movs rpopins,#retstk
movd _PUSHR,#retstk
jmp unext
{ *** CONSOLE SERIAL OUTPUT *** }
{
Transmit the character that is in the tos register. This character is preconditioned with start and stop bits and the output direction is also set (to save cog space)
}
' (EMIT) ( bits -- )
pEMIT
and tos,#$0FF ' data controls loop so trim to 8-bits
add tos,#$100 wc ' add a stop bit (carry = start bit)
or dira,txmask ' make sure it's an output
mov R0,cnt
add R0,txticks
txbit muxc outa,txmask ' output bit
shr tos,#1 wz,wc ' lsb first
waitcnt R0,txticks ' bit timing
if_nz_or_c jmp #txbit ' next bit (stop bit: z & c)
''' andn dira,txmask ' leave it to be pulled up so another cog can also use it
jmp #DROP
{
16 longs (or more) are reserved for a selectable module as a helper for specialized functions such as SPI etc.
LOADMOD loads the longs into this area to be executed with RUNMOD
130811 - Added feature so that a large module can be loaded directly into address 0 and run automatically if cnt >loadsz
}
' LOADMOD ( src dst cnt -- ) Load a PASM module of up to 18 bytes (or loadsz) into the cog
pLOADMOD
movd lcdst,tos+1 ' Init dst pointer - just loading a small module alongside Tachyon
ldmlp add lcdst,dst1 ' post-increment long destination (pipelined after rdlong)
lcdst rdlong 0_0,tos+2 ' read a long from hub ram into cog's destination
add tos+2,#4 ' increment hub memory long address
djnz tos,#ldmlp
jmp #DROP3
' SPR@ ( index -- long ) Fetch a cog's SPR
pSPRFETCH add tos,#$01F0
' COG@ ( addr -- long ) Fetch a long from cog memory
pCOGFETCH movs _readsrc,tos
nop
_readsrc mov tos,0_0
jmp unext
' COGREG ( ix -- addr ) return with the address of the indexed cog register
pCOGREG add tos,#REG0
jmp unext
' COG! ( long addr -- ) Store a long to cog memory
pCOGSTORE movd _writedst,tos
nop
_writedst mov 0_0,tos+1
jmp #DROP2
' PASM ( val pasm -- result pasm ) 120919 modified to not drop
pPASM mov pasmins,tos
nop ' takes care of pipeline and uses high mem for remainder of code
pasmins nop
jmp unext
' Accumulate a literal byte by byte from 1 to 4 bytes long depending upon the number of times this routine is called.
' This allows literals to be byte aligned.
ACCBYTE call #GETBYTE ' Build a big endian literal by reading in another byte
shl ACC,#8 ' merge it into the "accumulator" byte by byte
or ACC,X
ACCBYTE_ret ret
' Read the next byte of code memory via IP
GETBYTE rdbyte X,IP ' Simply read a byte (non-code) and advance the IP
add IP,#1
GETBYTE_ret ret
' Called both by U/ and U/MOD
_UDIVMOD
mov R1,#32 ' 32 bits
mov R0,#0 ' quotient
udivmodlp shl tos+1, #1 wc ' dividend
rcl R0, #1 ' hi bit from dividend
cmpsub R0, tos wc,wr ' cmp divisor
rcl R2, #1 ' R2 - quotient
djnz R1, #udivmodlp
mov tos+1, R0 ' update stack with result
mov tos, R2
_UDIVMOD_ret ret
{
As well as the data and return stack, a loop and branch stack is also employed
The return stack should normally only used for return addresses
A separate loop stack means that loop indicies can still be accessed in a called function
The branch stack speeds up loops by having the current loop address available without having to read an offset and calculate
The data stack is accessed as 4 fixed registers with an non-addressed overflow stack in hub RAM.
}
{
The branch stack is used for fast loop branching and so holds the branch address
It is constructed as a fixed top-of-stack location which physically moves data when it's pushed or popped
This means also that it is not possible to corrupt other memory by over or underflow.
}
_PUSHBRANCH
mov branchstk+5,branchstk+4
mov branchstk+4,branchstk+3
mov branchstk+3,branchstk+2
mov branchstk+2,branchstk+1
mov branchstk+1,branchstk
mov branchstk,IP ' this is the address to loop to
_PUSHBRANCH_ret ret
_POPBRANCH mov branchstk,branchstk+1
mov branchstk+1,branchstk+2
mov branchstk+2,branchstk+3
mov branchstk+3,branchstk+4
mov branchstk+4,branchstk+5
_POPBRANCH_ret ret
{
The loop stack holds the loop limit and current index. It is constructed as a fixed top-of-stack location which physically moves data when it's pushed or popped
This means also that it is not possible to corrupt other memory by over or underflow.
}
' pop the loop stack into X
LPOPX
mov X,loopstk
mov loopstk,loopstk+1
mov loopstk+1,loopstk+2
mov loopstk+2,loopstk+3
mov loopstk+3,loopstk+4
mov loopstk+4,loopstk+5
mov loopstk+5,loopstk+6
mov loopstk+6,loopstk+7
mov loopstk+7,#0
LPOPX_ret ret
' Push tos onto the loop stack and drop tos
_PUSHLP
mov loopstk+7,loopstk+6
mov loopstk+6,loopstk+5
mov loopstk+5,loopstk+4
mov loopstk+4,loopstk+3
mov loopstk+3,loopstk+2
mov loopstk+2,loopstk+1
mov loopstk+1,loopstk
mov loopstk,tos
'
' falls through into a DROP to remove the tos
'
' Pop the data stack using fixed size stack in COG memory (allows fast direct access for operations)
' V2.2 adds an overflow stack in hub ram and reduces the size of the cog stack to 4
'
POPX mov X,tos ' pop old tos into X
mov tos,tos+1
mov tos+1,tos+2
mov tos+2,tos+3
tjz depth,POPX_ret ' do not allow ext stack to pop past bottom
sub depth,#4
testn depth,#%1111 wz ' nz: 16+
if_z jmp POPX_ret ' direct return
mov R0,stkptr
add R0,depth
rdlong tos+3,R0 ' pop from hub into bottom of cog stack
POPX_ret
_PUSHLP_ret
ret
_PUSHACC mov X,ACC ' Accumulator operation used for fast constants
_PUSHX mov ACC,#0 ' clear it for next operation
cmp depth,#16 wc ' faster stacking if we can avoid hub access, only on overflow
' tjz stkptr,#PUSHCOG ' skip external stack if no pointer assigned (set to ROM if not used)
mov R0,stkptr
add R0,depth
if_nc wrlong tos+3,R0 ' save bottom of stack to hub RAM (only if necessary)
PUSHCOG mov tos+3,tos+2 ' push the cog stack registers (4)
mov tos+2,tos+1
mov tos+1,tos
mov tos,X ' replace tos with X (DEFAULT)
add depth,#4 ' the depth variable indexes bytes in hub RAM (real depth = depth/4)
_PUSHACC_ret
_PUSHX_ret
ret
{
Return stack builds up in cog memory
Return stack items do not need to be directly addressed
This indexed method does not use movd and movs methods but directly inc/decs the
source and destination fields of the instruction.(retstk) so it must stay synchronized - Use !RP if needed
}
SETUPIP call #GETBYTE ' read the next byte into X and save the current IP
SAVEIP mov R0,IP
_PUSHR mov retstk,R0 ' save it on the stack (dest modified)
add rpopins,#1 ' update source for popping
add _PUSHR,dst1 ' update dest for pushing (points to next free)
SETUPIP_ret '
SAVEIP_ret '
_PUSHR_ret ret
RPOPX sub rpopins,#1 ' decrement rpop's source field
sub _PUSHR,dst1
rpopins mov X,retstk
RPOPX_ret ret
dst1 long $200 ' instruction's destination field increment
zcalls long $0400-2 ' 1K offset after XCALLs for ZCALL table
{ *** COG VARIABLES *** } |
clockpins long 0 ' I/O mask for CLOCK instruction
spisck long 0 ' I/O mask for SPI clock
spiout long 0 ' I/O mask for SPI data out (MOSI)
spiinp long 0 ' I/O mask for SPI data in (MISO)
spice long 0 ' I/O mask for SPI CE (not really required unless we use CE instr)
spicnt long 0 ' bit count for variable size Kernel SPI
{
sck res 1
mosi res 1
miso res 1
scnt res 1
scs
}
' Registers used by PASM modules to hold parameters such as I/O masks and bit counts etc
REG0 long 0
REG1 long 0
REG2 long 0
REG3 long 0
REG4 long 0
txticks long (80_000_000 / baud ) ' set transmit baud rate
txmask long |<txd ' change mask to reassign transmit
' COGREG 7 = TASK REGISTER POINTER
regptr long @registers+s ' used by REG
Xptr long @XCALLS+s ' used by XCALL, points to vector table (bytecode>address)
unext long doNEXT ' could redirect code if used
' COGREG 10
' rearranged these register to follow REG0 so that they can be directly accessed by COGREG instruction
IP long 0 ' Instruction pointer
ACC long 0 ' Accumulator for inline byte-aligned literals
X long 0 ' primary internal working registers
R0 long 0
R1 long 0
R2 long 0
' COGREG 16
stkptr long $8000 ' points to start of stack in hub ram - builds up (safe init to rom)
depth long 0 ' depth long index - points to top of overflow in hub ram
lapcnt long 0
target long 0
' *** STACKS ***
tos
datastk long 0[datsz]
retstk long 0[retsz]
loopstk long 0[loopsz]
branchstk long 0[branchsz]
_RUNMOD_ ' this is a dummy symbol but the org must be equal to _RUNMOD (or less)
long 0[loadsz]
fit 496
long 0[32] 'The kernel image becomes a general-purpose buffer and this expands it to at least 2K for coginits
' define some constants used by this cog
' The RUNMOD parameters are defined here so that the method can be changed easily
org tos
sdat res 1
org tos
bcnt res 1
bdst res 1
bsrc res 1
org REG0
sck res 1
mosi res 1
miso res 1
scs res 1
scnt res 1
org REG0
res 3
pixshift res 1
pixeladr res 1
endofkernel res 0 ' just a branch to indentify the end of the kernel in the listing (BST)
res 0
res 0
res 0
res 0
' If hub ram is selected for return stacks then the space here down is used. Each cog has 32 longs.
retstks
CON
instr = $1F5
repcnt = $1F7
deltaR = $1FF
DAT
'**************************************** SERIAL RECEIVE ******************************************
{ This is a dedicated serial receive routine that runs in it's own cog }
DAT
long 0[2] ' read and write ' this hub space is used for rxwr & rxrd at runtime
rxbuffers ' hub ram gets reused as the receive buffer
org
HSSerialRx mov rxwr,#0 ' init write index
wrword rxwr,hubrxwr ' clear rxrd in hub
wrword rxwr,hubrxrd ' make rxwr = rxrd
mov stticks,rxticks ' calculate start bit timing
shr stticks,#1 ' half a bit time less
sub stticks,#4 ' compensate timing - important at high speeds
mov breakcnt,#200 ' reset break count
receive mov rxcnt,stticks ' Adjusted bit timing for start bit
waitpne rxpin,rxpin ' wait for a low = start bit
'
' START BIT DETECTED
' ' time sample for middle of start bit
add rxcnt,cnt ' uses special start bit timing
waitcnt rxcnt,rxticks
test rxpin,ina wz ' sample middle of start bit
rxcond2 if_nz jmp #receive ' restart if false start otherwise bit time from center
'
' START bit validated
' Read in data bits lsb first
' No point in looping as we have plenty of code to play with
' and inlining (don't call) and unrolling (don't loop) can lead to higher receive speeds
'
waitcnt rxcnt,rxticks ' wait until middle of first data bit
test rxpin,ina wc ' sample bit 0
muxc rxdata,#01 ' and assemble rxdata
waitcnt rxcnt,rxticks
test rxpin,ina wc ' sample bit 1
muxc rxdata,#02
waitcnt rxcnt,rxticks
test rxpin,ina wc ' sample bit 2
muxc rxdata,#04
waitcnt rxcnt,rxticks
test rxpin,ina wc ' sample bit 3
muxc rxdata,#08
waitcnt rxcnt,rxticks
test rxpin,ina wc ' sample bit 4
muxc rxdata,#$10
' data bit 5
waitcnt rxcnt,rxticks
test rxpin,ina wc ' sample bit 5
muxc rxdata,#$20
mov X1,rxbuf 'squeeze in some overheads, calc write pointer
add X1,rxwr ' X points to buffer location to store
' data bit 6
waitcnt rxcnt,rxticks
test rxpin,ina wc ' sample bit 6
muxc rxdata,#$40
add rxwr,#1 ' update write index
and rxwr,wrapmask
' last data bit 7
waitcnt rxcnt,rxticks
test rxpin,ina wc ' sample bit 7
muxc rxdata,#$80
wrbyte rxdata,X1 ' save data in buffer - could take 287.5ns worst case
' stop bit
stopins waitcnt rxcnt,rxticks ' check stop bit early (need to detect errors and breaks)
test rxpin,ina wc ' sample stop bit
if_nc jmp #frmerror ' framing error - check for break - disregard timing now
if_c wrword rxwr,hubrxwr ' update hub index for code reading the buffer if all good
wrbyte rxdata,lkptr
jmp #receive
' Framing error - check if it's a null character as it may be part of a break condition
'
frmerror
sub rxwr,#1
cmp rxdata,#0 wz 'if it's a null it could be part of a break
if_nz mov breakcnt,#200 'reset the break count (compromise here to keep main loop tight)
if_nz jmp #receive 'ignore normal framing error
or outa,rxpin
or dira,rxpin 'unintentional? make sure the input is not floating
mov rxcnt,#16
djnz rxcnt,$
andn dira,rxpin 'restore input and delay
mov rxcnt,#16
djnz rxcnt,$
test rxpin,ina wz 'if it's still low then it is being intentionally driven
if_nz jmp #receive 'ignore floating input
djnz breakcnt,#receive
aboot
mov rxcnt,#$80
clkset rxcnt ' reboot
lkptr long @registers+s+lastkey
rxpin long |<rxd ' mask of rx pin
hubrxrd long @rxbuffers+s-4 ' ptr to rxrdin hub
hubrxwr long @rxbuffers+s-2 ' word address of rxwr in hub (after init)
rxbuf long @rxbuffers+s ' pointer to rxbuf in hub memory
breakcnt long 40
wrapmask long bufsize-1
mode long 0
rxticks long 0
stticks long 0
spticks long 0
rxcnt long 0
rxdata long 0 'assembled character
lastch long 0
X1 long 0
savdat long 0
rxwr long 0 'cog receive buffer write index - copied to hub ram
end res 0
{ Boot-time Spin startup - launches Tachyon into all the remaining cogs and starts serial receive in this cog = 0 }
PUB Start
rxticks := txticks := clkfreq / baudrate ' Force VM transmit routine to correct baud
cognew(@RESET, @TERMINAL) ' COG 1 - Tachyon terminal task
repeat 6
cognew(@RESET, @IDLE) ' COG 2 - clone Tachyon kernel into remaining cogs
coginit(0,@HSSerialRx, @rxbuffers) ' COG 0 - finally reassign the Spin cog for serial coms
{ Extend this kernel by pasting or sending EXTEND.fth to the Prop running the kernel - use 12ms line delay or more }
'( LINK TO EXTEND.fth )