-
Notifications
You must be signed in to change notification settings - Fork 0
/
jf.nw
1847 lines (1451 loc) · 54.6 KB
/
jf.nw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\documentclass{tufte-handout}
\title{A Literate Forth for RISC-V Microcontrollers
\thanks{Adapted from Jonesforth for i386 Linux by Richard~W.M. Jones}}
\author[Nick Pascucci]{Nick Pascucci}
%\date{June 17, 2022} % without \date command, current date is supplied
%\geometry{showframe} % display margins for debugging page layout
\usepackage{csquotes} % allow for nice quotes
\usepackage{graphicx} % allow embedded images
\setkeys{Gin}{width=\linewidth,totalheight=\textheight,keepaspectratio}
\graphicspath{{graphics/}} % set of paths to search for images
\usepackage{amsmath} % extended mathematics
\usepackage{booktabs} % book-quality tables
\usepackage{units} % non-stacked fractions and better unit spacing
\usepackage{multicol} % multiple column layout facilities
\usepackage{lipsum} % filler text
\usepackage{fancyvrb} % extended verbatim environments
\fvset{fontsize=\normalsize}% default font size for fancy-verbatim environments
% Standardize command font styles and environments
\newcommand{\doccmd}[1]{\texttt{\textbackslash#1}}% command name -- adds backslash automatically
\newcommand{\docopt}[1]{\ensuremath{\langle}\textrm{\textit{#1}}\ensuremath{\rangle}}% optional command argument
\newcommand{\docarg}[1]{\textrm{\textit{#1}}}% (required) command argument
\newcommand{\docenv}[1]{\textsf{#1}}% environment name
\newcommand{\docpkg}[1]{\texttt{#1}}% package name
\newcommand{\doccls}[1]{\texttt{#1}}% document class name
\newcommand{\docclsopt}[1]{\texttt{#1}}% document class option name
\newenvironment{docspec}{\begin{quote}\noindent}{\end{quote}}% command specification environme
\usepackage{noweb}
\noweboptions{smallcode,longchunks}
\setcounter{secnumdepth}{1} % Number sections for autoref
% Define a shorthand for test cases, which shows in the margin of the woven sources.
% A sed preprocessing step in the Makefile will generate inputs to the Python tests from these
% definitions as well, though they don't appear in this file explicitly.
\newcommand{\testcase}[3]{
\marginnote{
\textit{Example:} \texttt{#1} \texttt{#2} $\rightarrow$ \texttt{#3}
}
}
\begin{document}
\maketitle
\pagestyle{noweb}
@ \paragraph{Introduction}
Forth is one of the \emph{ur-languages} of computer science. It originated in 1970 as a way to
program telescope control systems, and has generally found its place in deeply embedded applications
due to its small size and ease of porting to new processors. These same characteristics make it an
interesting object of study for programming language enthusiasts, and while this segment of the
Forth community sometimes is the subject of derision from seasoned Forth programmers, they
nonetheless have produced some of the best pedagogical materials for learning how the language
actually \emph{works} at a low level.
My interest in Forth stems from a desire to have a simple computing stack that I can fully
understand, from the ISA to the applications. For these same reasons, I am interested in the
open-source RISC-V instruction set architecture (ISA). I hoped that in joining the two, I could
create a system that could be easily understood --- or, at least, as free of unnecessary
complication as such things can be.
\newthought{Rather than starting from scratch,} I decided to port the existing \emph{Jonesforth}
implementation from its original i386 to RISC-V, and take it as a baseline to experiment with. As a
hedge against my poor memory and in the hope that I might make something useful to someone else at
some point, I've decided to document the program in a literate programming style using the
\emph{noweb} toolset written by Norman Ramsey. Using \emph{noweb} allows me to mix code and
documentation, presenting both in the order that makes the most sense and providing much better
context to the code through images and prose. The \texttt{noweave} command produces human-readable
documentation --- which you may be reading right now --- while the \texttt{notangle} command
combines all the code blocks into something we can execute. The \texttt{Makefile} included with this
repository handles the details.
\newpage
\section {The FE310-G002 Processor}
We're going to need a processor to run this thing. Forth naturally lends itself to embedded systems,
and the specific processor I want to use is the FE310 from SiFive, Inc. The chip is essentially a
demo of SiFive's RISC-V IP, aimed at showing that they can, in fact, make processors and that they
do, in fact, work. As such it is a fairly basic core implementing the RV32IMAC ISA with a few
peripherals. For our purposes the main things to keep in mind are that the core has 16kB of
instruction cache and 16kB of data SRAM along with hardware multiplication and division.
\begin{figure}[h]
\centering
\includegraphics[width=0.5\textwidth]{redboard}
\caption{The SparkFun RED-V Redboard is an Arduino-alike with an FE310 as the main processor.}
\label{fig:redboard}
\end{figure}
In addition to the core itself, we need a few accessories: an oscillator, a bank of durable storage
for our program, power conditioning, and support for talking to a computer. To get these we'll use
SparkFun's RED-V development board.
\begin{displayquote}
The RED-V RedBoard comes in the familiar Arduino Uno R3 form factor and includes the SiFive
Freedom E310 core, 32MB of QSPI flash, an NXP K22 ARM Cortex-M4 for USB connectivity and operating
as a JTAG interface, and a Qwiic connector to make future I2C offerings easy.
\end{displayquote}
RISC-V, like other RISC ISAs, uses a \textit{load-store architecture}. This means that the CPU can
only operate on values that are stored in registers, and all memory access is done explicitly
through \textit{load} and \textit{store} instructions. The ISA provides a plethora of registers to
use --- honestly, more than we need --- as described in \autoref{tab:rv-registers}.
\begin{table}[h!]
\begin{center}
\begin{tabular}{ |c|c|c|c| }
\hline
Register & ABI Name & Description & Saver \\
\hline
x0 & zero & Zero constant & - \\
x1 & ra & Return address & Caller \\
x2 & sp & Stack pointer & - \\
x3 & gp & Global pointer & - \\
x4 & tp & Thread pointer & Callee \\
x5-x7 & t0-t2 & Temporaries & Caller \\
x8 & s0 / fp & Saved / frame pointer & Callee \\
x9 & s1 & Saved register & Callee \\
x10-x11 & a0-a1 & Fn args/return values & Caller \\
x12-x17 & a2-a7 & Fn args & Caller \\
x18-x27 & s2-s11 & Saved registers & Callee \\
x28-x31 & t3-t6 & Temporaries & Caller \\
\hline
\end{tabular}
\caption{The RISC-V ISA defines 32 registers by default. While most are interchangeable, the ISA
does suggest conventional uses for them which is reflected in the ABI name.}
\label{tab:rv-registers}
\end{center}
\end{table}
Our Forth will use only a subset of the available registers. Typically, Forths implement a virtual
machine with five ``virtual registers'', which we map to RISC-V registers in
\autoref{tab:forth-registers}.
\begin{table}[h!]
\begin{center}
\begin{tabular}{ |c|c|c| }
\hline
Name & Description & Register \\
\hline
W & Working register & fp \\
X & Working register & t0 \\
IP & Instruction pointer & gp \\
SP & Data stack pointer & sp \\
RP & Return stack pointer & tp \\
UP & User data pointer (unimplemented) & n/a \\
\hline
\end{tabular}
\caption{A typical Forth virtual machine tracks 5-6 items in registers. Brad Rodriguez's
\href{https://www.bradrodriguez.com/papers/moving1.htm}{\textit{Moving Forth}} series has a good
introduction to these.}
\label{tab:forth-registers}
\end{center}
\end{table}
For the sake of keeping the code comprehensible we will aim to use the same registers consistently
for each of these roles. IP will be mapped to \texttt{gp} SP to \texttt{sp}, RP to \texttt{tp}. The
W and X working registers will be \texttt{fp} and \texttt{t0}, respectively. (UP is not currently
assigned as this Forth does not currently support multitasking.)
How each of these registers is used in practice is deeply intertwined with the implementation of the
Forth \textit{inner interpreter}, which we will discuss in \hyperref[sec:inner-interpreter]{a future
section}.
Some operations will need to use several scratch registers; these will all be drawn from the set
\texttt{t0-t6} so they will be obvious in the code. In addition, we will need a defined calling
convention for assembly-language subroutines; for these, we will use the registers \texttt{a0-a7} as
both input and output registers, hewing to the RISC-V convention. Because this code does not need to
interface with the C ABI, we will take some liberties such as not saving the "saved" registers.
@ \section{Memory map}\label{sec:memory-map}
On the RedBoard we have 16KiB of RAM and 32 MiB of flash ROM to work with. (By comparison,
Jonesforth allocates 64 KiB.) The FE310 maps these into the address space with ROM starting at
\texttt{0x2000\_0000} and RAM at \texttt{0x8000\_0000}. We can declare these to the linker to help
map out our binary's layout for flashing.
<<ROM and RAM declarations>>=
rom (irx!wa) : ORIGIN = 0x20400000, LENGTH = 0x1fc00000
ram (airwx) : ORIGIN = 0x80000000, LENGTH = 0x400000
@
We'll also declare a few labels we can use in the assembly sources.
<<Memory limits symbols>>=
PROVIDE(_memory_start = ORIGIN(ram));
PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
@
Assembly builtin words are stored into the nonvolatile flash memory. The "data segment" goes into
RAM. We need two regions: our return stack, which grows downwards into RAM from its start address,
and the data stack, which grows upwards into RAM starting where the return stack ends.
@ \subsection{Data and return stacks}\label{subsec:data-and-return-stacks}
We'll restrict the return and data stacks to 512 bytes each. The user dictionary will start after
the data stack. Unlike the original Jonesforth, we are running on bare metal and can't dynamically
allocate more memory if we run out. Also unlike the original, this Forth will not use a buffer to
store input as we have direct access to the hardware. Instead, we will read from the serial input
registers; see the implementation of KEY. % TODO Crossreference words
<<Stack size constants>>=
.set RETURN_STACK_SIZE, 512
.set DATA_STACK_SIZE, 512
@
The return stack and data stack start at the same address. We deconflict their accesses without
wasting space by ensuring that the return stack advances its pointer before pushing data and after
popping, and that the data stack does the opposite. Thus the starting word is part of the data
stack.
<<Stack declarations>>=
return_stack:
.space RETURN_STACK_SIZE
return_stack_top: /* Initial top of return stack. Grows down. */
data_stack_top: /* Also initial top of data stack. Grows up. */
.space RETURN_STACK_SIZE
@
<<Stack manipulation macros>>=
/* Pop a value from the top of the stack into a register */
.macro pop reg
addi sp, sp, -4
lw \reg, 0(sp)
.endm
/* Push a register onto the stack */
.macro push reg
sw \reg, 0(sp)
addi sp, sp, 4
.endm
@
@ \section{Dictionary layout}\label{sec:dictionary-layout}
<<Dictionary bitmasks>>=
.set F_IMMED,0x80
.set F_HIDDEN,0x20
.set F_LENMASK,0x1f
@
<<defword macro>>=
/* Define a Forth word with high-level components */
.macro defword name, namelen, flags=0, label, prev
.section .rodata
.p2align 2
.global name_\label
.int 0 /* For debugging purposes, add null bytes between dictionary entries */
name_\label :
.int name_\prev /* Link to previous word */
.byte \flags+\namelen
.ascii "\name"
.p2align 2
.global \label
\label :
.int DOCOL
/* Put list of word pointers after */
.endm
@
<<defcode macro>>=
/* Define a Forth word with assembly implementation. The in-memory layout for the word is:
| 4 bytes | 1 byte | n bytes |
| Prev Pointer | Flags + Length | Name |
*/
.macro defcode name, namelen, flags=0, label, prev
.section .rodata
.p2align 2
.int 0 /* For debugging purposes, add null bytes between dictionary entries */
name_\label :
.int name_\prev
.byte \flags+\namelen
.ascii "\name"
.p2align 2
\label :
.int code_\label
.text
code_\label :
/* Assembly code follows; must end with NEXT */
.endm
@
@ \section{The Forth Inner Interpreter}\label{sec:inner-interpreter}
<<NEXT macro>>=
.macro NEXT
lw fp, 0(gp) /* Load the jump target into fp */
addi gp, gp, 4 /* Increment gp, emulating LODSL to point to next word */
lw t0, 0(fp) /* Load address pointed to by codeword */
jr t0 /* Indirect jump to the codeword pointed to by address in fp. */
.endm
@
@ \section{Executing high-level Forth words}
<<Colon interpreter>>=
DOCOL: /* Colon interpreter. See jonesforth.s:501 */
PUSHRSP gp /* Push addr of next instr. at previous call level onto the r. stack */
addi fp, fp, 4 /* Advance fp to point to first data word in current definition */
mv gp, fp /* Make the data word the "next" word to execute */
NEXT
@
@ \section{Words provided by the Forth kernel}\label{sec:code-words-in-kernel}
Now we can begin to define some of the core Forth words which will be built in to the kernel. Where
possible, for each of these definitions we will want to define some reasonable examples that we can
use as test cases. Later on these will appear as inputs to a test program we can use to exercise the
implementation; see \autoref{sec:test-fixture}. In the code, each test case will be a tuple with
the initial stack contents followed by Forth words as the first element, and the resulting
top-of-stack value as the second element:
<<Test cases>>=
("1", "1"), # Just push the number 1 on the stack
("1 2", "1 2"), # Push 1 followed by 2
<<Elided test cases>>
@
% The elided tests above cause a single undefined reference error when compiling the LaTeX sources.
% This is because the elision filter removes all the referenced code blocks but doesn't remove the
% ref in the block above.
For the purposes of keeping the printed document tidy, I will place test cases in the margin in a
pretty-printed form throughout this section. The actual code is elided in the printed version of the
file since it's so repetitive and uninteresting.
\testcase{1 2}{}{1 2}
@ \subsection{Stack manipulation}\label{subsec:stack-manipulation}
The most iconic, and most commonly used, words in the Forth repertoire are \textit{stack
manipulation} words. Because of Forth's design, it is often necessary to do some amount of
re-arranging for elements on the data stack during processing; these provide the basic mechanisms to
do so.
\texttt{DROP} simply discards the top element from the stack.
\testcase{1 2}{DROP}{1}
<<Stack manipulation words>>=
defcode "DROP",4,,DROP,link_base
/* Just move the stack pointer back a cell */
addi sp, sp, -4
NEXT
@
\texttt{SWAP} swaps the order of the top two elements of the stack.
\testcase{1 2}{SWAP}{2 1}
<<Stack manipulation words>>=
defcode "SWAP",4,,SWAP,DROP
pop t0
pop t1
push t0
push t1
NEXT
@
\texttt{DUP} duplicates the top element on the stack.
\testcase{1}{DUP}{1 1}
<<Stack manipulation words>>=
defcode "DUP",3,,DUP,SWAP
pop t0
push t0
push t0
NEXT
@
\texttt{OVER} pushes a copy of the second value on the stack onto the top of the stack.
\testcase{1 2}{OVER}{1 2 1}
<<Stack manipulation words>>=
defcode "OVER",4,,OVER,DUP
pop t0
pop t1
push t1
push t0
push t1
NEXT
@
\texttt{ROT} ``rotates'' the three items on the top of the stack so that what was the third element
is now the top of the stack.
% TODO Diagram ROT
\testcase{1 2 3}{ROT}{2 3 1}
<<Stack manipulation words>>=
defcode "ROT",3,,ROT,OVER
pop t0
pop t1
pop t2
push t1
push t0
push t2
NEXT
@
\texttt{-ROT} does the opposite: it tucks the top of the stack into third place, exposing the item
that was below it.
\testcase{1 2 3}{-ROT }{3 1 2}
<<Stack manipulation words>>=
defcode "-ROT",4,,NROT,ROT
pop t0
pop t1
pop t2
push t0
push t2
push t1
NEXT
@
\texttt{2DROP}, unsurprisingly, drops two elements from the top of the stack.
\testcase{1 2 3}{2DROP}{1}
<<Stack manipulation words>>=
defcode "2DROP",5,,TWODROP,NROT
pop t0
pop t0
NEXT
@
\texttt{2DUP} duplicates the top \textit{two} items of the stack.
\testcase{1 2}{2DUP}{1 2 1 2}
<<Stack manipulation words>>=
defcode "2DUP",4,,TWODUP,TWODROP
pop t0
pop t1
push t1
push t0
push t1
push t0
NEXT
@
\texttt{2SWAP} swaps the top pair of elements with the second pair of elements.
\testcase{1 2 3 4}{2SWAP}{3 4 1 2}
<<Stack manipulation words>>=
defcode "2SWAP",5,,TWOSWAP,TWODUP
pop t0
pop t1
pop t2
pop t3
push t1
push t0
push t3
push t2
NEXT
@
\texttt{?DUP} duplicates the top of the stack, but only if it's not zero.
\testcase{1 2}{?DUP}{1 2 2}
\testcase{1 0}{?DUP}{1 0}
<<Stack manipulation words>>=
defcode "?DUP",4,,QDUP,TWOSWAP
pop t0
push t0
beqz t0, 1f
push t0
1: NEXT
@
@ \section{Arithmetic}\label{subsec:arithmetic}
\texttt{1+}, as you might imagine, adds one to the value on the top of the stack.
\testcase{1}{1+}{2}
\testcase{-1}{1+}{0}
<<Arithmetic words>>=
defcode "1+",2,,INCR,QDUP
pop t0
addi t0, t0, 1
push t0
NEXT
@
\texttt{1-} subtracts one from the element on the top of the stack.
\testcase{2}{1-}{1}
<<Arithmetic words>>=
defcode "1-",2,,DECR,INCR
pop t0
addi t0, t0, -1
push t0
NEXT
@
\texttt{4+} and \texttt{4-} similarly add and subtract 4; this is mostly useful for manipulating
addresses as in a 32-bit system each word is 4 bytes.
\testcase{1}{4+}{5}
\testcase{-1}{4+}{3}
<<Arithmetic words>>=
defcode "4+",2,,INCR4,DECR
pop t0
addi t0, t0, 4
push t0
NEXT
@
\testcase{5}{4-}{1}
<<Arithmetic words>>=
defcode "4-",2,,DECR4,INCR4
pop t0
addi t0, t0, -4
push t0
NEXT
@
\texttt{+} and \texttt{-} add and subtract the two values on the top of the stack, replacing them
with the result.
\testcase{1 1}{+}{2}
<<Arithmetic words>>=
defcode "+",1,,ADD,DECR4
pop t0
pop t1
add t0, t0, t1
push t0
NEXT
@
Note that for subtraction, the top of stack value is subtracted from the next element. This is
intuitive; if you rewrite the postfix expression $2\ 1\ -$ as the infix equivalent $2 - 1$, the
relationship is obvious.
\testcase{2 1}{-}{1}
<<Arithmetic words>>=
defcode "-",1,,SUB,ADD
pop t0 /* b */
pop t1 /* a */
sub t0, t1, t0 /* a - b */
push t0
NEXT
@
\texttt{*} multiplies the two values on the top of the stack, replacing them with the result.
\testcase{2 3}{*}{6}
<<Arithmetic words>>=
defcode "*",1,,MUL,SUB
pop t0
pop t1
mul t0, t0, t1
push t0
NEXT
@
\texttt{/MOD} is interesting: it's a combination division and modulus operator, and it's used to
define both of those words later in high-level Forth. It takes a divisor (i.e. the value we're
dividing \textit{by}) at the top of the stack, with the dividend (the value we're \textit{dividing})
below it, and leaves the remainder on top with the quotient below. So, for example, using 3 and 7 as
examples of the divisor and dividend: $7/3=2$ while $7 \mod 3 = 1$.
In i386 both results can be computed in a single instruction, but for RISC-V we need to do them
separately. This isn't a huge issue.
% TODO Compare the cycle times of i386 vs RISC-V for /MOD
\testcase{7 3}{/MOD}{1 2}
<<Arithmetic words>>=
defcode "/MOD",4,,DIVMOD,MUL
pop t0 /* Divisor */
pop t1 /* Dividend */
div t2, t1, t0 /* Quotient */
rem t3, t1, t0 /* Remainder */
push t3
push t2
NEXT
@
\texttt{UM/MOD} is similar to \texttt{/MOD}, but operates on unsigned values.
\testcase{7 3}{UM/MOD}{1 2}
<<Arithmetic words>>=
defcode "UM/MOD",6,,UMDIVMOD,DIVMOD
pop t0 /* Divisor */
pop t1 /* Dividend */
divu t2, t1, t0 /* Quotient */
remu t3, t1, t0 /* Remainder */
push t3
push t2
NEXT
@
@ \subsection{Logical and comparison words}\label{subsec:logic_words}
% Jonesforth uses a C-style boolean. I prefer the Forth style bitmask approach, but this is not
% yet implemented.
%
% If two bitfields are equal, then their XOR is zero. Let's check that this is true with a
% two-bit value:
%
% a/b 00 01 10 11
% 00 00 01 10 11
% 01 01 00 11 10
% 10 10 11 00 01
% 11 11 10 01 00
%
% Given this, we can return all ones if two values are equal by taking the one's complement of
% the XOR of the two values.
In this section we develop the basic logical operators needed to build more complex constructions in
high level Forth. In all of these words, note that any non-zero value is considered \textit{true},
while zero is \textit{false}; operations like \texttt{AND} and \texttt{OR} operate bit-wise and may
be used both for logic and for bitfield manipulation.
We'll start out with \texttt{=}, which does what it says on the tin: it returns \texttt{1} if the
two elements are equal to each other, and \texttt{0} otherwise.
\testcase{1 1}{=}{1}
\testcase{1 2}{=}{0}
<<Logic words>>=
defcode "=",1,,EQU,UMDIVMOD
pop t0
pop t1
beq t0, t1, 1f
li t0, 0
j 2f
1: li t0, 1
2: push t0
NEXT
@
\texttt{<>} is ``not equal'' ($\ne$), and does the opposite of \texttt{=}.
\testcase{1 1}{<>}{0}
\testcase{1 2}{<>}{1}
<<Logic words>>=
defcode "<>",2,,NEQU,EQU
pop t0
pop t1
bne t0, t1, 1f
li t0, 0
j 2f
1: li t0, 1
2: push t0
NEXT
@
\texttt{<}, \texttt{>}, \texttt{<=}, and \texttt{>=} are your standard partial ordering comparators.
\testcase{1 2}{<}{1}
\testcase{2 1}{<}{0}
\testcase{2 2}{<}{0}
<<Logic words>>=
defcode "<",1,,LT,NEQU
pop t0 /* b */
pop t1 /* a */
slt t1, t1, t0 /* Sets t1 to 1 if t1 is less than t0 (a < b) */
push t1 /* if t1=1 this is all 1s and if t1=0 all 0's. */
NEXT
@
\testcase{1 2}{>}{0}
\testcase{2 1}{>}{1}
\testcase{2 2}{>}{0}
<<Logic words>>=
defcode ">",1,,GT,LT
pop t0
pop t1
slt t1, t0, t1 /* Just swap the order of arguments here */
push t1
NEXT
@
\testcase{1 2}{<=}{1}
\testcase{2 2}{<=}{1}
\testcase{2 1}{<=}{0}
<<Logic words>>=
defcode "<=",2,,LE,GT
pop t0
pop t1
/* TODO This can almost certainly be made smaller. */
beq t1, t0, 1f
blt t1, t0, 1f
li t1, 0
j 2f
1: li t1, 1
2: push t1
NEXT
@
\testcase{1 2}{>=}{0}
\testcase{2 2}{>=}{1}
\testcase{2 1}{>=}{1}
<<Logic words>>=
defcode ">=",2,,GE,LE
pop t0
pop t1
bge t1, t0, 1f
li t1, 0
j 2f
1: li t1, 1
2: push t1
NEXT
@
\texttt{0=} tests if the top-of-stack value is equal to 0. It's effectively the same as a logical
`not'.
\testcase{1}{0=}{0}
\testcase{0}{0=}{1}
<<Logic words>>=
defcode "0=",2,,ZEQU,GE
pop t0
seqz t0, t0
push t0
NEXT
@
\testcase{1}{0<>}{1}
\testcase{0}{0<>}{0}
<<Logic words>>=
defcode "0<>",3,,ZNEQU,ZEQU
pop t0
snez t0, t0
push t0
NEXT
@
\texttt{0<} and \texttt{0>} provide handy comparisons for negative and positive numbers, without
stack manipulation.
\testcase{-1}{0<}{1}
\testcase{0}{0<}{0}
\testcase{1}{0<}{0}
<<Logic words>>=
defcode "0<",2,,ZLT,ZNEQU
pop t0
sltz t0, t0
push t0
NEXT
@
\testcase{-1}{0>}{0}
\testcase{0}{0>}{0}
\testcase{1}{0>}{1}
<<Logic words>>=
defcode "0>",2,,ZGT,ZLT
pop t0
sgtz t0, t0
push t0
NEXT
@
\testcase{-1}{0<=}{1}
\testcase{0}{0<=}{1}
\testcase{1}{0<=}{0}
<<Logic words>>=
defcode "0<=",3,,ZLE,ZGT
pop t0
bgtz t0, 1f
li t0, 1
j 2f
1: li t0, 0
2: push t0
NEXT
@
\testcase{-1}{0>=}{0}
\testcase{0}{0>=}{1}
\testcase{1}{0>=}{1}
<<Logic words>>=
defcode "0>=",3,,ZGE,ZLE
pop t0
bltz t0, 1f
li t0, 1
j 2f
1: li t0, 0
2: push t0
NEXT
@
\texttt{AND} performs a bitwise ``and'' of the top two items of the stack. \texttt{OR} behaves
similarly.
\testcase{0 0}{AND}{0}
\testcase{1 0}{AND}{0}
\testcase{0 1}{AND}{0}
\testcase{1 1}{AND}{1}
<<Logic words>>=
defcode "AND",3,,AND,ZGE
pop t0
pop t1
and t0, t0, t1
push t0
NEXT
@
\testcase{0 0}{OR}{0}
\testcase{1 0}{OR}{1}
\testcase{0 1}{OR}{1}
\testcase{1 1}{OR}{1}
<<Logic words>>=
defcode "OR",2,,OR,AND
pop t0
pop t1
or t0, t0, t1
push t0
NEXT
@
\testcase{0 0}{XOR}{0}
\testcase{1 0}{XOR}{1}
\testcase{0 1}{XOR}{1}
\testcase{1 1}{XOR}{0}
<<Logic words>>=
defcode "XOR",3,,XOR,OR
pop t0
pop t1
xor t0, t0, t1
push t0
NEXT
@
\texttt{INVERT} does a bit-wise inversion of the value on the top of the stack. Note that this is
not the same as a logical negation; while \texttt{0 INVERT} is logically \textit{true}, \texttt{1
INVERT} is not logically \textit{false}.
\testcase{0}{INVERT}{/}
\marginnote{(Note that in the ASCII character set, the character \texttt{/} comes before \texttt{0}
and thus represents -1 in our output encoding. See the test fixture in
\autoref{sec:test-fixture} for details.)}
<<Logic words>>=
defcode "INVERT",6,,INVERT,XOR
pop t0
not t0, t0
push t0
NEXT
@
@ \section{Interacting with the return stack}\label{subsec:return-stack}
<<Return stack manipulation>>=
/* Push a value onto the return stack */
defcode ">R",2,,TOR,__F_LENMASK
pop t0
PUSHRSP t0
NEXT
@
\testcase{}{}{}
<<Return stack manipulation>>=
/* Pop a value from the return stack */
defcode "R>",2,,FROMR,TOR
POPRSP t0
push t0
NEXT
@
\testcase{}{}{}
<<Return stack manipulation>>=
/* Get the value of the return stack pointer */
defcode "RSP@",4,,RSPFETCH,FROMR
push tp
NEXT
@
\testcase{}{}{}
<<Return stack manipulation>>=
/* Set the value of the return stack pointer */
defcode "RSP!",4,,RSPSTORE,RSPFETCH
pop tp
NEXT
@
\testcase{}{}{}
<<Return stack manipulation>>=
defcode "RDROP",5,,RDROP,RSPSTORE
addi tp, tp, 4 /* Increment return stack pointer by 1 cell */
NEXT
@
@ \section{Interacting with memory}\label{sec:interacting-with-memory}
\testcase{}{}{}
<<Memory words>>=
defcode "!",1,,STORE,LIT
pop t1 /* Address to store into */
pop t0 /* Value to store */
sw t0, 0(t1)
NEXT
@
\testcase{}{}{}
<<Memory words>>=
defcode "@",1,,FETCH,STORE
pop t1 /* Address to fetch */
lw t0, 0(t1) /* Read into t0 */
push t0 /* Store value onto the stack */
NEXT
@
\testcase{}{}{}