On Friday, May 12, 2023 at 3:53:36 PM UTC-7, John Hart wrote:
Something more compact and efficient than a CASE statement was needed for defining logic equations in the FPGA4th system.
The solution was a set of words to build a look up table.
key MAP[ n \ begins the process of building the table.
a b MAP \ associates a with b
c d MAP \ associates c with d
e ]MAP \ ends the look up table process
note: n specifies the key size (2^n) 0 = 1 byte, 1 = 2 bytes, 2 = 4 bytes
At runtime, if the key = a, b is returned, if the key isn't found, e is returned
Two more words to complete the set.
a ]: starts compilation of code associated with a
;[ ends compilation
: ALU { a b cmd - - c } \ example
cmd MAP[ 1
O ]: a b + ;[ MAP \ add
1 ]: a b - ;[ MAP \ sub
2 ]: a b AND ;[ MAP \ and
3 ]: a b OR ;[ MAP \ or
]: 0 ;[ ]MAP
EXECUTE
;
jrh
This is a copy of my document file for the <SWITCH package: -------------------------------------------------------------------------------------------------
Documentation for FAST-SWITCH> and SLOW-SWITCH> in LIST.4TH
of the novice package --- this is ANS-Forth --- (c) Hugh Aguilar
The basic operation is this:
<SWITCH
:NONAME drop ... ; targ CASE-OF
...
:NONAME drop ... ; FAST-SWITCH> xxx ( selector -- )
The XXX is the name of the function being created.
The DROP in the :NONAME function gets rid of the selector-value.
You might need this sometimes though.
The last :NONAME before FAST-SWITCH> is the default action done if
no match was found. In many applications this indicates a bug so this
:NONAME function will abort and will not DROP the value but will
display it so the user can know what invalid selector got used.
There can be thousands of CASE-OF statements.
They don't have to be in any particular order.
Building the table is faster if they are pretty much in reverse order.
I'm using an insertion sort which is slow. I did this so that I would
find any duplicates immediately and would be able to abort on the
offending line so the user would know exactly where the duplicate was.
Note that Michael Gassenenko's CHOOSE doesn't tell where the duplicate
was. The slowness is at compile-time, so it doesn't matter very much
(compiling the assembler/simulator for my processor takes 17 seconds). Duplicates are illegal, although duplicates are allowed in ANS-Forth's
goofy CASE construct that just does a sequential search.
We have a VALUE called JT-LIMIT that sets the limit of the size of
a FAST-SWITCH> jump-table. Note that the FAST-SWITCH> jump-table is a
sparse array, so the number of selectors could be significantly less
than the size of the jump-table. JT-LIMIT is currently set at 2^16 to
allow simulation of a processor with 16-bit opcodes. The user can
change JT-LIMIT. If there are more selectors than JT-LIMIT allows or
the user just wants to use less memory, SLOW-SWITCH> can be used
instead of FAST-SWITCH>. This will build a packed sorted table and at
run-time will do a binary search. This packed table is usually smaller
than a FAST-SWITCH> sparse table, but if over half of the posible
selectors are represented, then the SLOW-SWITCH> table will not only
be slower but will also use more memory.
It is possible to assign a :NONAME action to multiple selectors:
:NONAME drop ... ; low-targ high-targ RANGE-OF
:NONAME drop ... ; ." xxx" CHARS-OF
:NONAME drop ... ; <WHEN ... WHEN>
:NONAME drop ... ; DIGIT-OF
:NONAME drop ... ; LOWER-OF
:NONAME drop ... ; UPPER-OF
:NONAME drop ... ; ALPHA-OF
:NONAME drop ... ; PUNCTUATION-OF
:NONAME drop ... ; BLANK-OF
RANGE-OF matches all of the values from LOW-TARG to HIGH-TARG
inclusive. CHARS-OF matches all of the chars in the string.
<WHEN ... WHEN> matches all of the values between <WHEN and WHEN>
(the ... means zero or more values)
DIGIT-OF is the numeric digits, LOWER-OF is the lowercase letters,
UPPER-OFis the upper-case letters, ALPHA-OF is all of the letters, PUNCTUATION-OF is punctuation, BLANK-OF is blanks.
It is trivial for the user to define macros like this. For example:
: punctuation-of ( head xt -- new-head )
s| .,!?'";:[]()@#$%&| chars-of ;
: blank-of ( head xt -- new-head )
0 32 range-of ;
So far, we have been assuming that the :NONAME function would DROP the selector. This isn't always done though. I have an assembler/simulator
for a custom processor with 16-bit opcodes. In many of the opcodes,
the lower 9 bits are an embedded literal. I use RANGE-OF so that all
$200 opcodes will execute the same :NONAME function. The :NONAME
function needs the selector (the opcode) so it can extract the
embedded literal with $1FF AND to use this literal in the simulation
of the opcode. RANGE-OF is hugely useful for similating opcodes
assuming that the embedded literal values are in the lower bits
(I designed the processor, so I can put there embedded literal there).
Note that, RANGE-OF has recently (for this processor) been upgraded to
insert the values in reverse order to help speed up the table build.
We also have CASE: ... ;; that was suggested by DXforth. Apparently he
wanted to loosely imitate the syntax of ANS-Forth's goofy CASE
construct, although I don't know why anybody would care about this.
Instead of this:
:NONAME drop ... ; targ CASE-OF
You do this:
targ CASE: ... ;;
I also have STRING-SWITCH> that is similar to SLOW-SWITCH> in that it
uses a binary search, but it is for strings rather than integers.
Instead of CASE-OF you use STRING-OF and your TARG value is not an
integer but is an ADR/CNT pair for a counted string (typically
provided by S" or S| or whatever).
This was written for supporting something similar to LEX, although I
have my STRING-STACK.4TH package now that would be better.
STRING-SWITCH> might still prove useful though. -------------------------------------------------------------------------------------------------
--- SoupGate-Win32 v1.05
* Origin: fsxNet Usenet Gateway (21:1/5)