-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathforth.asm
368 lines (347 loc) · 7 KB
/
forth.asm
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
//#define PRELOAD_TEST_FUNCTION
//#define TEST_PARSE
//#define NO_TITLE
//#define PARN_IGNORE
//#define RAM_LOAD
#ifdef RAM_LOAD
* = $2000
#else
* = $E288
#endif
#define _PUSH_FRAME \
pha : \
phx : \
phy : \
php
#define _PULL_FRAME \
plp : \
ply : \
plx : \
pla
#define _ROL4 \
rol : \
rol : \
rol : \
rol
; system vectors
CHAROUT = $8001
CHARIN = $8002
RANDOM = $8003
; ZP variables
B16L = $0
B16H = $1
PRL = $2
PRH = $3
STACK_OFFSET = $4
LINE_IN_IDX = $5
TEMP = $6
CURWORD_IDX = $7
WORD_LEN = $8
MULT_TEMP = $9
DIV_TEMP = $A
WORD_PTRL = $B
WORD_PTRH = $C
LL_ROOTL = $D
LL_ROOTH = $E
LL_CURL = $F
LL_CURH = $10
YSTORE = $11
NUM_CONV = $12
NUM_CONV_2 = $13
NUM_CONV_HEX = $14
XSTORE = $15
CURLINE_L = $16
CURLINE_H = $17
CURLINE_IDX = $18
NEXT_USER_L = $19 ; pointer to next available address for user funcitons
NEXT_USER_H = $1A ; "
IN_FUNCTION = $1B
IN_LOOP = $1C
LOOP_BEGIN_IDX = $1D
LOOP_COUNT = $1E
LOOP_LIMIT = $1F
IN_IF_BLOCK = $20
IN_WHILE_LOOP = $21
WHILE_LOOP_IDX = $22
LOOP_OUTER_IDX = $23
EXT_MEM_L = $24
EXT_MEM_H = $25
ARGA = $26
ARGX = $27
ARGY = $28
; buffers
LINE_IN = $0200 ; 0200 - 0300
STACK = $0300 ; 0300 - 0400
TOKEN_BUFF = $0500 ; 0400 - 040F
VARIABLE_PAGE = $0600
USER_FUNC = $0700 ; arbitrary for now
INIT: .(
clc
stz STACK_OFFSET
lda #<USER_FUNC ; Reset pointer to next available address in RAM for
sta NEXT_USER_L ; user function definitions.
lda #>USER_FUNC ; "
sta NEXT_USER_H ; "
#ifndef NO_TITLE
jsr NEWLINE
ldx #<TITLE
ldy #>TITLE
jsr PRINTS
#endif
BAD_OPTION:
jsr NEWLINE
ldx #<WCS
ldy #>WCS
jsr PRINTS
L: lda CHARIN
sta CHAROUT
beq L
cmp #'W'
beq WARM
cmp #'C'
beq COLD
jmp BAD_OPTION
WARM:
jsr STARTUP_WARM
bra WC_END
COLD:
stz USER_FUNC
WC_END:
jmp MAIN
WCS: .byte "WARM/COLD?: ",0
#ifdef PRELOAD_TEST_FUNCTION
.( ; FUNCTION DEF BLOCK MUST BE TERMINATED BY $
ldy #$0
L: lda TF, y
cmp #'$'
beq E
sta (NEXT_USER_L), y
iny
jmp L
E: lda #$0C
sta NEXT_USER_L
lda #$06
sta NEXT_USER_H
jmp MAIN
TF: .byte $1, 'X'
.byte $6
.byte "0 ;"
.byte $1, 'Y'
.byte $6
.byte "1 ;",$0,"$"
.)
#endif
.)
MAIN:
ldx #$FF
txs
stz IN_FUNCTION
stz IN_LOOP
stz IN_WHILE_LOOP
stz LINE_IN_IDX
stz CURLINE_IDX
stz IN_IF_BLOCK
#ifdef TEST_PARSE
TEST: .(
ldx #<CMD
ldy #>CMD
jsr PARSE_LINE
brk
CMD: .byte "DELETE X",0
.)
#endif
jsr GETLINE
jsr NEWLINE
ldx #<LINE_IN
ldy #>LINE_IN
jsr PARSE_LINE
jmp MAIN
; Parse the string line at (X, Y),
PARSE_LINE:
#print PARSE_LINE
stx CURLINE_L
sty CURLINE_H
ldy #$0
FUNC_END:
#print FUNC_END
NEXT: .( ; parse next token in line
ldy CURLINE_IDX
jsr GETTOKEN
bne N
jmp EOL ; if at EOL, end parsing
N: jsr COPY_TOKEN
lda #<LL_ROOT_NODE
sta LL_CURL
lda #>LL_ROOT_NODE
sta LL_CURH
SEEK: ; begin linked list lookup
ldy #$0
lda (LL_CURL), y ; fetch top byte from LL node
beq NOT_BUILTIN_WORD; if zero, it's the end of the list
cmp WORD_LEN ; compare to token length
bne SKIP ; skip to next node in list if not equal
CHECK: ; command length matches length of token
clc
tax
tay
dex ; Remove one from X (len(token) - 1)
CHECK_LOOP: ; compare strings in reverse
lda (LL_CURL), y
cmp TOKEN_BUFF, x
bne SKIP
dey
cpy #$0
beq FOUND
dex
jmp CHECK_LOOP
FOUND:
clc
lda WORD_LEN ; get function pointer from LL node
adc #$3 ; "
tay
lda (LL_CURL), y
sta WORD_PTRL ; store function pointer
iny
lda (LL_CURL), y
sta WORD_PTRH
jmp (WORD_PTRL) ; now jump to function pointer
SKIP:
clc
ldy #$0
lda (LL_CURL), y
adc #$1
tay
lda (LL_CURL), y
tax
iny
lda (LL_CURL), y
sta LL_CURH
stx LL_CURL
jmp SEEK
NOT_BUILTIN_WORD: ; check for user-defined function
#print NOT_BUILTIN_WORD
lda #<USER_FUNC
sta LL_CURL
lda #>USER_FUNC
sta LL_CURH
jsr USER_SEEK
beq NOT_WORD
jmp RUN_SUBSTRING
NOT_WORD: ; try to interpret token as integer
#print NOT_WORD
ldy #$0 ; reload the first character of the token
stz NUM_CONV_HEX
L: lda TOKEN_BUFF, y
cmp #SPACE
beq END
cmp #$0
beq END
cmp #CR
beq END
cmp #LF
beq END
pha
lda NUM_CONV_HEX ; convert decimal input into hex for storage
ldx #$A
clc
jsr MULT
sta NUM_CONV_HEX
pla
jsr CTON
cmp #$0
bmi ERR
cmp #$A
bcs ERR
clc
adc NUM_CONV_HEX
sta NUM_CONV_HEX
iny
jmp L
END:
lda NUM_CONV_HEX
jsr STACK_PUSH
jmp FUNC_END
ERR:
jmp BAD_WORD
EOL:lda IN_WHILE_LOOP
beq EOL1
jmp LOOP_END_MISSING
EOL1:
lda IN_LOOP
beq EOL2
jmp LOOP_END_MISSING
EOL2:
rts
BAD_WORD:
lda #ERRC
sta CHAROUT
jmp MAIN
.)
; This function is called from the main interpreter loop, F_SEE, F_DELETE
USER_SEEK: .(
ldy #$0
lda (LL_CURL), y ; seek through the RAM list for a custom function
beq NOT_FOUND ; if function name length == 0, stop seeking
cmp WORD_LEN
bne USER_SKIP
USER_CHECK:
clc
tax
tay
dex
USER_CHECK_LOOP:
lda (LL_CURL), y
cmp TOKEN_BUFF, x
bne USER_SKIP
dey
cpy #$0
beq FOUND
dex
jmp USER_CHECK_LOOP
USER_SKIP:
clc
ldy #$0
lda (LL_CURL), y ; fetch offset from link-list to next node
adc #$1
tay
lda (LL_CURL), y
clc
adc LL_CURL
sta LL_CURL
bcc NO_CARRY
inc LL_CURH
clc
NO_CARRY:
jmp USER_SEEK
NOT_FOUND:
lda #$0
rts
FOUND:
lda #$1
rts
.)
; reloads user function list. Scans through USER_LIST until it finds
; a null terminator. This null terminator is NEXT_USER_L/H
STARTUP_WARM: .(
ldy #$0
SEEK_LOOP:
lda (NEXT_USER_L), y
beq END
clc
inc NEXT_USER_L
bne NO_CARRY
inc NEXT_USER_H
clc
NO_CARRY:
jmp SEEK_LOOP
END:rts
.)
#include "src/ascii_const.asm"
#include "src/getline.asm"
#include "src/util.asm"
#include "src/prints.asm"
#include "src/gettoken.asm"
#include "src/forth_builtin_list.asm"
#include "src/forth_strings.asm"
#include "src/forth_utils.asm"
#include "src/forth_builtin_funcdefs.asm"