-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkernel.fth
303 lines (189 loc) · 7.53 KB
/
kernel.fth
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
( block 0 )
This block file, kernel.fth, is used to create a target image when
starting riscy.tcl this way:
./riscy.tcl -flash 1
(normally, though, that would be done by the makefile)
It will then load block 1 (in this file), which is the load
block that will load other blocks.
Finally, at some point, SAVE-IMAGE must be executed in order
to save the results to the *.bin and *.dictionary files,
e.g.,
SAVE-IMAGE kernel INIT
would save the results to the files kernel.bin and
kernel.dictionary and set the boot word to QUIT
( shadow 0 )
( block 1 load block to create the kernel image )
2 LOAD ( QUIT (ABORT INIT )
3 LOAD ( S .CR .H etc )
4 LOAD ( comparisons)
5 LOAD ( math)
6 LOAD ( CMOVE etc)
7 LOAD ( Timing )
8 LOAD ( DUMP DU)
9 10 THRU ( multitasker)
SAVE-IMAGE kernel INIT
( shadow 1 )
( block 2 QUIT (ABORT INIT )
: QUIT ( -) RP! BEGIN PAUSE (SERIN? UNTIL queryinterpret ( f) ?ok QUIT ;
: (ABORT ( a -) COUNT TYPE SP! ?ok QUIT ;
: INIT ( -) ' (ABORT 'ABORT ! QUIT ;
( shadow 2 )
load block for creating the flash image for Riscy Pygness
The ";;" after QUIT is defined switches back to interpret mode so the
additional blocks can be loaded.
The last line on the block saves the image to the names kernel.bin and
kernel.dictionary, while setting that the "boot" or startup word will be
QUIT.
When controlled by the makefile, the image files will be renamed
appropriately for the specific ARM variant, such as kernel-lpc2106.bin
and kernel-lpc2106.dictionary.
( shadow 2 )
( block 3 miscellaneous)
: .S ( -) ROT DUP . ROT DUP . ROT DUP . ;
: CR ( -) $0D EMIT $0A EMIT ;
: .H ( u -) HEX U. DECIMAL ;
: ? ( a -) @ . ;
: BL ( - c) $20 ;
: SPACE ( -) BL EMIT ;
: SPACES ( # -) FOR SPACE NEXT ;
: BYTES ( aabb - bb aa) DUP $FF AND SWAP 256/ ;
( : CMOVE ( fr to # -)
( BYTES ( fr to ls# ms#) ( SWAP PUSH )
( fr to ms#) ( FOR 2DUP 256 (CMOVE 256 + 256 +UNDER NEXT )
( fr to) ( POP ?DUP IF (CMOVE ; THEN 2DROP ; )
( shadow 3 )
CMOVE ( fr to # -)
Split the count into ms# full 256-byte moves then a final ls# byte move
if ls# is not zero.
( block 4 comparisons )
: 0<> ( n - f) 0= NOT ;
( n is BETWEEN low and high iff y is WITHIN low and high+1 )
: BETWEEN ( n l h - f) 1+ ( fall through )
: WITHIN ( n l h - f) OVER - PUSH - POP U< ;
( shadow 4 )
( block 5 math )
: ABS DUP 0< IF NEGATE ; THEN ;
: MAX 2DUP < IF SWAP THEN DROP ;
: MIN 2DUP > IF SWAP THEN DROP ;
( Note that MOD is really UMOD )
: MOD ( a b - r) U/MOD DROP ;
: U/ ( a b - q) U/MOD NIP ;
( Signed division. Remember the sign, do an unsigned divide, )
( finally, apply the correct sign to the result. )
: / ( n n - n)
SWAP DUP 0< PUSH ABS
SWAP DUP 0< PUSH ABS ( u u)
U/ POP POP XOR IF NEGATE THEN ;
( shadow 5 math )
( block 6 CMOVE FILL etc)
( CMOVE> move bytes from high end of buffer so buffers can overlap.)
( This needs to be rewritten as a primitive. )
: CMOVE> ( fr to #) ( PAUSE )
PUSH R@ +UNDER R@ +
POP FOR ( fr to) -1 +UNDER 1- OVER C@ OVER C! NEXT
2DROP ;
: CMOVE ( fr to # -) ( PAUSE)
( Later, rewrite as a fast primitive based on ARM's load and )
( store multiple instructions.)
FOR ( from to) OVER C@ OVER C! 1 +UNDER 1+ NEXT 2DROP ;
: FILL ( a # c -) SWAP DUP 0= IF 3DROP ; THEN
PUSH ( a c) OVER C! DUP 1+ POP 1- CMOVE ;
: BLANK ( a # -) BL FILL ;
: ERASE ( a # -) 0 FILL ;
( shadow 6 )
( block 7 Timing )
: T1@ ( - u) T1TC @ ; ( read the timer )
: MS ( # -)
( This is very accurate but is only good to about
1165000 milliseconds, when PCLK is 3,686,400 Hz,
i.e. about 19 minutes. )
T1@ ( originalTimer)
SWAP TICKS/MS * + ( goal)
BEGIN PAUSE T1@ OVER U> UNTIL DROP ;
( shadow 7 Timing )
MS ( # -)
Kill the requested number of milliseconds.
( shadow 7)
( shadow 7 math )
( block 8 DUMP )
: DUMP ( a - a')
HEX CR DUP 5 U.R ( 2 SPACES)
DUP 2 FOR ( a a) 2 SPACES 8 FOR DUP C@ 3 U.R 1+ NEXT SPACE NEXT DROP
( a) 2 FOR 2 SPACES 8 FOR
DUP C@ DUP 32 127 WITHIN NOT IF DROP 46 THEN EMIT 1+
NEXT SPACE
NEXT DECIMAL ;
: DU ( a n -) FOR DUMP ( ?SCROLL) NEXT DROP ;
( shadow 8 DUMP )
Unlike the usual Pygmy version of DUMP, Riscy Pygness does not save and
restore the base. It always leaves the base set to DECIMAL.
( block 9 Multitasker )
: LOCAL ( task a - a') LINK - + ;
: AWAKE? ( task - a | 0)
PUSH ( ) LINK DUP ( start current)
BEGIN
( start current) DUP @ ( start current candidate) R@ =
( start current flag) IF POP DROP NIP ( a) ; THEN
( start current) @ ( start newcurrent) 2DUP =
UNTIL 2DROP POP DROP 0 ;
: SLEEP ( task -)
DUP TERMINAL = IF DROP ; THEN
DUP AWAKE? ?DUP IF ( task prevTask)
OVER @ SWAP ! ( ie unlink)
THEN ( task) DROP PAUSE ;
: STOP ( -) LINK SLEEP ;
: WAKE ( newtask -) DUP AWAKE? IF DROP ; THEN
DUP LINK ( new new curr )
DUP @ PUSH ( new new curr -- next)
! ( ie new is now successor to curr) ( new)
POP SWAP ! ( ie next is now successor to new) ;
( shadow 9 Multitasking )
LOCAL ( task a - a')
Convert an address A local to the current task to an equivalent address
local to the given task. E.g., TASK1 RP0 LOCAL answers the RP0 slot
in TASK1's TCB.
AWAKE? ( task - a | 0)
The LINK fields form a circular list which is, in effect, the active
task list. AWAKE? checks to see if a given task is in the active task
list. If not, a zero is returned. If it is, the address of the task
that points to the given task is returned. This is the link field that
points to the task we were asking about. Thus it is the field we need
to alter if we wish to remove the task from the active list.
The active task list is circular. It is never empty, as the foreground
task should never be put to sleep. With a single task, the LINK field
of that task contains the address of itself.
Executing a task name returns the address of that task, which is the
start of its user variables (i.e., the address of its LINK field).
AWAKE? works by running through the LINKs to see if any of them contain
an address that matches the given task. We don't really need to know
how many tasks are present in the LINKs as long as we know when to stop
looking (when we get back to the one we started with).
SLEEP ( task -)
Remove TASK from the active task list (if it is not the foreground task).
STOP ( -)
Put the current task to sleep (if it is not the foreground task).
WAKE ( newtask -)
Insert NEWTASK into the active task list.
( block 10 Multitasker )
: TASK! ( word task -)
PUSH ( word)
R@ 8 + ( word IPslot) ! ( )
R@ SP0 LOCAL @ R@ $0C + ( sp0 dstkSlot) !
R@ RP0 LOCAL @ POP $10 + ( rp0 rstkSlot) ! ;
( shadow 10 Multitasking )
TASK! ( word task -)
Set (or reset) TASK so it will run the Forth WORD when it gets
control. The LINK, SP0, and RP0 slot have already been initialized.
The TOS and RLOOP slots are "don't care". We store the Forth WORD
into the saved IP slot. Then we set the saved DSTK and RSTK slots
from the SP0 and RP0 slots.
Note that TASK1 LINK $XX + LOCAL is the same as TASK1 $XX +
( block 11 Multitasker Examples )
VARIABLE V1
VARIABLE V2
: TST1 ( -) BEGIN 1500 MS 1 V1 +! AGAIN ;
: TST2 ( -) BEGIN 300 MS 1 V2 +! AGAIN ;
( ' TST1 TASK1 TASK! )
( ' TST2 TASK2 TASK! )
( TASK1 WAKE TASK2 WAKE )
( shadow 11 Multitasking Examples )