Shop OBEX P1 Docs P2 Docs Learn Events
PASM2/FORTH — Parallax Forums

PASM2/FORTH

garyrigaryri Posts: 2
edited 2024-10-25 03:54 in 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.

image

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.

image

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.

image

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.

Sign In or Register to comment.