PASM2/FORTH
The ROM (internal) FORTH interface needs only a PropPlug and a serial terminal emulator to work.
It is possible to call multiple assembly language functions in another cog from FORTH.
This can be done by setting a shared command variable like for instance 'Cmd'.
PASM2 commands can then be investigated interactively to find out exactly how they work.
This can be used to make documentation.
Functions DoPush and DoPop shown below work with the initialized cog's hardware stack.
The opcodes can be found by hand assembly if no assembler is available.
The FORTH words in this block set up a cog to do this.
long Cmd long Xhub long Yhub 27 longs TestCode $FD800007 TestCode 0 + ! --- JMP #\ClearCmd ' Cmd TestCode 4 + ! --- Cmd_ptr LONG 0 ' Pointers Xhub TestCode 8 + ! --- X_ptr LONG 0 ' to hub (for Xhub) Yhub TestCode 12 + ! --- Y_ptr LONG 0 ' variables. (for Yhub) $00000000 TestCode 16 + ! --- Cmd LONG 0 ' Local command to exec. $00000000 TestCode 20 + ! --- Xcog LONG 0 ' Local input variable. $00000000 TestCode 24 + ! --- Ycog LONG 0 ' Local return variable. --- '---------------------------------------------------- $F6040800 TestCode 28 + ! --- ClearCmd MOV Cmd,#0 ' Clear command. $FC600801 TestCode 32 + ! --- WRLONG Cmd,Cmd_ptr ' Store in hub. $FB000801 TestCode 36 + ! --- DoInstr RDLONG Cmd,Cmd_ptr ' Get command from hub. $F21C0800 TestCode 40 + ! --- CMP Cmd,#0 WCZ ' $AD800009 TestCode 44 + ! --- IF_Z JMP #\DoInstr ' If (Cmd==0) Wait. $ED800007 TestCode 48 + ! --- IF_LE JMP #\ClearCmd ' If (Cmd<=0) Clear Cmd. $F21C0802 TestCode 52 + ! --- CMP Cmd,#2 WCZ ' $1D800007 TestCode 56 + ! --- IF_A JMP #\ClearCmd ' If (Cmd>2) Clear Cmd. --- '---------------------------------------------------- $FD600830 TestCode 60 + ! --- JMPREL Cmd ' Jump table. $FD800009 TestCode 64 + ! --- JMP #\DoInstr ' 0=Do nothing. $FD800013 TestCode 68 + ! --- JMP #\DoPush ' 1=Do first function. $FD800017 TestCode 72 + ! --- JMP #\DoPop ' 2=Do second function. --- '---------------------------------------------------- $FD640C5F TestCode 76 + ! --- DoPush DRVNOT #6 $FB000A02 TestCode 80 + ! --- RDLONG Xcog,X_ptr ' Xcog=Xhub; $FD600A2A TestCode 84 + ! --- PUSH Xcog ' $FD800007 TestCode 88 + ! --- JMP #\ClearCmd ' --- '---------------------------------------------------- $FD640C5F TestCode 92 + ! --- DoPop DRVNOT #6 $FD600C2B TestCode 96 + ! --- POP Ycog ' $FC600C03 TestCode 100 + ! --- WRLONG Ycog,Y_ptr ' Store Ycog to Yhub. $FD800007 TestCode 104 + ! --- JMP #\ClearCmd ' TestCode 1 COGINIT --- ok
Then the cog's (in this case) two functions can be used like this.
3 Xhub ! --- Stores 3 in the shared memory variable Xhub to be pushed to the stack.
1 Cmd ! --- Stores 1 into Cmd. This causes the DoPush function to push Xhub to the stack.
2 Cmd ! --- Stores 2 into Cmd. This causes the the DoPop function to pop Yhub from the stack.
Yhub @ . --- Prints the resulting Yhub.
This block shows 10 numbers being pushed to the hardware stack.
# 10 Xhub ! 1 Cmd ! --- ok # 9 Xhub ! 1 Cmd ! --- ok # 8 Xhub ! 1 Cmd ! --- ok # 7 Xhub ! 1 Cmd ! --- ok # 6 Xhub ! 1 Cmd ! --- ok # 5 Xhub ! 1 Cmd ! --- ok # 4 Xhub ! 1 Cmd ! --- ok # 3 Xhub ! 1 Cmd ! --- ok # 2 Xhub ! 1 Cmd ! --- ok # 1 Xhub ! 1 Cmd ! --- ok
And this shows trying to read them back.
# 2 Cmd ! Yhub @ . --- 1 ok # 2 Cmd ! Yhub @ . --- 2 ok # 2 Cmd ! Yhub @ . --- 3 ok # 2 Cmd ! Yhub @ . --- 4 ok # 2 Cmd ! Yhub @ . --- 5 ok # 2 Cmd ! Yhub @ . --- 6 ok # 2 Cmd ! Yhub @ . --- 7 ok # 2 Cmd ! Yhub @ . --- 8 ok # 2 Cmd ! Yhub @ . --- 8 ok (The cog's hardware stack is indeed 8 levels deep.)
Many things could be investigated using this idea.
1. The CNT counter.
2. Bit operations.
3. Conditional execution.
4. JMP CALL RET branching.
5. Smart pins.
6. Math instructions details. Like how the Carry and Zero flags work.
7. CORDIC functions.
8. Repeating instructions.
9. FIFO/Streamer.
10. Locks.
11. Event handling.
12. Etc...
Here is some added information about the idea.
As before, a cog can be started with code stored in a longs array.
25 longs AnArray { Long Address ByteOffset Add Store } $00000000 AnArray 0 + ! $00000001 AnArray 4 + ! ...
Then a new cog can be started like this.
AnArray 1 COGINIT
Executing the word AnArray returns the address where the code longs are located.
A cog can also be started with longs loaded in a published word. It requires less typing.
pub SomeWord $00000000 , $ 00000000 , etc ... ;
Trying to execute the word SomeWord would probably crash because it is not FORTH code.
A new cog that runs these assembly code longs can be started like this.
COGINIT uses two stack parameters (HubAddress CogNum -- )
The tick mark ( ' ) retrieves the address of SomeWord and puts it into the stack,
ready for COGINIT to use. So ( ' ) needs to be included.
Assembly code to count low to high transitions of a smart pin.
It arbitrarily uses pin 5.
'======================================================================= ' Put total number of lo/hi transitions on pin in hub Count variable. '----------------------------------------------------------------------- ORG '----------------------------------------------------------------------- $FD800006 Top JMP #\Init ' Jump over variables. $00000000 pCount LONG 0 ' Pointer to hub Count. $00000005 pin LONG 5 ' Local pin number. $00000000 tmp LONG 0 ' Used to transfer count. ' AAAA_BBBB_FFF_MMMMMMMMMMMMM_TT_SSSSS_0 $0000001C incUp LONG %0000_0000_000_0000000000000_00_01110_0 ' Inc A up. $0098967E halfsec LONG 9_999_998 ' 20,000,000 MHz / 2 - 2 clock. '----------------------------------------------------------------------- $FD600440 Init DIRL pin ' Hold pin in reset. $FC000802 WRPIN incUp,pin ' Mode = increment on A-rise. $FC180002 WXPIN #0,pin ' Measurement is continuous not periodic. $FC280002 WYPIN #0,pin ' Ignore adjacent B pin. $FD600441 DIRH pin ' Start smart pin. $FA880602 L1 RDPIN tmp,pin ' Get count. Ack pin. $FC600601 WRLONG tmp,pCount ' HUB["count"]= (A-rise) count; $FD64025F DRVNOT #1 ' Blink LED 1. $FD600A1F WAITX halfsec ' Wait for 1/2 second. $FD80000B JMP #\L1 ' Keep reading and writing count. '-----------------------------------------------------------------------
Here is how to run this using internal FORTH.
The cog first initializes pin 5 as a smart pin to count total Low to High transitions.
Then it goes into a loop that keeps writing the total count of transitions and a 1/2 second delay.
Here is what it looks like to query the variable Count while the cog is running.
Because buttons are usually noisy, often the count will skip ahead more than one.
The smart pin does not know about the 1/2 second delay.
For some unknown reason, this button is very quiet. But it skips sometimes.
This cog code can be used as a digital logic counter.