File: evaluator.s

package info (click to toggle)
atari800 5.2.0-2
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid, trixie
  • size: 7,196 kB
  • sloc: ansic: 86,829; asm: 18,694; sh: 3,173; cpp: 2,798; java: 2,453; xml: 957; makefile: 727; perl: 334; pascal: 178
file content (734 lines) | stat: -rw-r--r-- 20,545 bytes parent folder | download
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
; Altirra BASIC - Expression evaluator module
; Copyright (C) 2014 Avery Lee, All Rights Reserved.
;
; Copying and distribution of this file, with or without modification,
; are permitted in any medium without royalty provided the copyright
; notice and this notice are preserved.  This file is offered as-is,
; without any warranty.

;===========================================================================
; The stack handling is conceptually similar to that of Atari BASIC, but
; has diverged quite a bit for efficiency. It now looks like this:
;
; +----------------------+  LOMEM + $100
; |    operator stack    |
; +----------------------+
; .          |           .
; .          v           .
; .                      .
; .          ^           .
; .          |           .
; +----------------------+
; |   argument stack 2   |
; +----------------------+  LOMEM + $6C
; .                      .
; .                      .
; .          ^           .
; .          |           .
; +----------------------+
; |   argument stack 1   |
; +----------------------+  LOMEM
;
; The argument stacks together contain the values pushed onto the stack
; and grow upward, while the operator stack grows downward from the top.
; opsp points to the last valid location. There is enough room for 36
; levels of nesting.
;
; The paired argument stack is very different from Atari BASIC. First,
; it only contains six bytes per entry instead of eight, omitting the
; type and variable bytes. This is because most of the time keeping
; these on the stack is unnecessary -- the argument types for each token
; type are already known and enforced by the parser. A couple of statements
; do take both types, including LIST, INPUT, and PRINT, and for those we
; maintain a type byte for the top of stack. Variable information is
; available from LVARPTR for the leftmost variable and VARPTR for the
; rightmost variable.
;
; The six bytes of the argument stack are split between the two argument
; stacks, with even bytes in stack 1 and odd bytes in stack 2. This serves
; two purposes, one being to reduce the amount of stack pointer futzing
; we have to do, and also to provide easy access to 16-bit quantities.
; argsp points to the next available location. It would be faster to
; store the stack as SoA like Turbo Basic XL does, but since we are running
; from ROM and have neither a suitable absolute addressed writable area
; nor enough zero page to burn on 6 pointers, we sacrifice a little
; speed here.
;
; There is one other trick that we do, which is to cache the top of stack
; in FR0. Each argument stack is actually shifted up one entry, with the
; first entry not used. This is a substantial performance and size
; optimization as it eliminates a lot of paired pushes and pops. For
; instance, instead of doing pop(fr1)/pop(fr0)/fadd/push(fr0) for an add
; token, we simply just do pop(fr1)/fadd. Unary operators are even
; simpler -- ABS() just has to clear FR0 bit 7!
;
; The bottom of the stack, argsp=0, has special significance when in an
; assignment context (expAsnCtx bit 7 = 1). Two differences in execution
; occur in this situation. First, a pointer to the variable's value is
; stashed in LVARPTR for later use. Second, the string indexing function
; allows selecting a range beyond the current length of a string.

;===========================================================================
ExprEvalIOCBCommaInt:
		jsr		evaluateHashIOCB
ExprSkipCommaAndEvalPopInt:
		inc		exLineOffset
.proc	evaluateInt
		jsr		evaluate
		jmp		ExprConvFR0Int
.endp

;===========================================================================
.proc evaluateHashIOCBOpt
		;default to IOCB #0
		ldx		#0
		stx		iocbidx
		
		;check if we have an IOCB
		jsr		ExecTestEnd
		cmp		#TOK_EXP_HASH
		sec							;set C=1 to indicate no #iocb found
		bne		valid_iocb

.def :evaluateHashIOCBNoCheckZero = *
		;fetch IOCB# -- note that we deliberately don't check the high
		;byte, for compatbility with Atari BASIC
		jsr		ExprSkipCommaAndEvalPopIntPos

		;IOCB #0 is allowed by some statements, so we don't check it here.
		;	OPEN - not allowed
		;	CLOSE - not allowed
		;	XIO - not allowed
		;	GET - not allowed
		;	PUT - not allowed
		;	NOTE - not allowed
		;	POINT - not allowed
		;	STATUS - not allowed
		;	PRINT - allowed
		;	INPUT - allowed
		;IOCB #8-15 aren't allowed, but #16 is (#$&*#)
		txa
		asl
		asl
		asl
		asl
		bmi		invalid_iocb
		sta		iocbidx

		clc							;set C=0 to indicate #iocb found
valid_iocb:
		stx		iocbidx2
		rts

invalid_iocb:
		jmp		errorBadDeviceNo
.endp

;===========================================================================
.proc	evaluateHashIOCB
		jsr		evaluateHashIOCBNoCheckZero
		txa
		beq		evaluateHashIOCBOpt.invalid_iocb
		rts
.endp

;===========================================================================
ExprSkipCommaAndEvalVar = ExprSkipCommaAndEval
evaluateVar = evaluate

;===========================================================================
.proc ExprPushLiteralConst
		sta		expType
		jsr		ExprPushExtFR0
		ldy		exLineOffset
		:6 mva (stmcur),y+ fr0+#
		sty		exLineOffset
		;##TRACE "Pushing literal constant: %g" fr0
		bne		evaluate.loop
.endp

;===========================================================================
.proc ExprPushLiteralStr
		;build argument stack entry
		jsr		ExprPushExtFR0
		lda		#$83
		sta		expType
		
		;length
		;dimensioned length	
		;load and stash string literal length (so we don't have to thrash Y)
		ldy		exLineOffset
		lda		(stmcur),y
		sta		fr0+2
		
		;skip past length and string in statement text
		sec
		adc		exLineOffset
		sta		exLineOffset		

		;address
		tya
		sec								;+1 to skip length
		adc		stmcur
		sta		fr0
		lda		#0
		sta		fr0+3
		adc		stmcur+1
		sta		fr0+1
		
		;all done
		bne		evaluate.loop
.endp


;===========================================================================
; Main expression evaluator.
;
; _assign_entry:
;	Special entry point that takes custom evaluation flags in the A
;	register:
;
;	bit 7 = assignment context - allow string bounds beyond current
;	        length for first lvalue
;
;	bit 6 = DIM context - allow references to undimensioned array/string
;	        variables
;
ExprSkipCommaAndEval:
		inc		exLineOffset
.proc	evaluate
_tmpadr = fr0+1

		;set up rvalue context
		lda		#0
		dta		{bit $0100}				;bit $80xx
.def :evaluateAssignment
		lda		#$80
_assign_entry:
		sta		expAsnCtx
		
		;;##TRACE "Beginning evaluation at $%04x+$%02x = $%04x" dw(stmcur) db(exLineOffset) dw(stmcur)+db(exLineOffset)

		;reset stack pointers
		ldy		#0
		sty		opsp
		sty		argsp
loop_open_parens:
		sty		expCommas
loop:
		;get next token
		ldy		exLineOffset
		inc		exLineOffset
		lda		(stmcur),y
		;;##TRACE "Processing token: $%02x ($%04x+$%02x=$%04x)" (a) dw(stmcur) y dw(stmcur)+y
		
		;check if this token needs to be reduced immediately
		bmi		is_variable
		cmp		#$0f
		bcc		ExprPushLiteralConst
		beq		ExprPushLiteralStr
			
		;==== reduce loop ====
				
		;reduce while precedence of new operator is equal or lower than
		;precedence of last operator on stack
		
		;get push-on / shift precedence
		;
		;if bit 7 is set, we immedately shift
		pha
		tax
		lda		prec_table-$12,x
		bmi		shift
		sta		expCurPrec
		;;##TRACE "Current operator get-on precedence = $%02x" a

reduce_loop:		
		ldy		opsp
		beq		reduce_done
		lda		(argstk),y
		
		;get pull-off/reduce precendence
		tax
		lda		prec_table-$12,x		;!! - $10 (COM) and $11 (CLOSE) can never follow an expression
		and		#$7e
		
		;stop reducing if the current operator has higher precedence
		;;##TRACE "Checking precedence: tos $%02x vs. cur $%02x" a db(expCurPrec)
		cmp		expCurPrec
		bcc		reduce_done

		inc		opsp
		jsr		dispatch
		;##ASSERT (db(argsp)%3)=0
		jmp		reduce_loop

reduce_done:
		;exit if this is not an expression token
		lda		expCurPrec
		beq		done

		;push current operator on stack
shift:
		dec		opsp	
		ldy		opsp
		pla
		;##TRACE "Shift: $%02x" (a)
		sta		(argstk),y
		bne		loop					;!! - unconditional (we would never shift a $00 token)

done:
		pla
		;;##TRACE "Exiting evaluator"
		dec		exLineOffset
		rts
				
is_variable:
		;##TRACE "Push variable $%02X" (a)
		;get value address of variable
		jsr		VarGetAddr0
		
		;check if this is the first var at the base -- if so, set the
		;lvalue ptr for possible assignment
		ldy		argsp
		bne		not_lvalue
		
		lda		varptr
		adc		#2
		sta		lvarptr
		lda		varptr+1
		adc		#0
		sta		lvarptr+1
		
		;since we know the stack is empty, we know we don't need to push, either
		ldy		#3
		sty		argsp
		bne		skip_push_fr0

not_lvalue:

		;push variable entry from VNTP onto argument stack
		jsr		ExprPushFR0NonEmpty

skip_push_fr0:
		;load variable
		jsr		VarLoadFR0

		;fetch type and set expression type
		ldy		#0
		lda		(varptr),y
		sta		expType
		
		;check if we had an array or string		
		;;##TRACE "arg %02x %02x %02x %02x" db(dw(argstk)+0) db(dw(argstk)+1) db(dw(argstk)+2) db(dw(argstk)+3)
		cmp		#$40
		bcc		loop

		;check if it is dimensioned
		lsr
		bcc		not_dimmed
		
undim_ok:
		;check if we have a relative pointer
		lsr
		bcs		loop
		
		;it's relative -- convert relative pointer to absolute
		;;##TRACE "Converting to absolute"
		ldy		#starp
		jsr		IntAddToFR0
		bne		loop		;!! - unconditional

not_dimmed:
		;check if we allow unDIM'd vars (i.e. we're in DIM)
		bit		expAsnCtx
		bvs		undim_ok
		jmp		errorDimError

dispatch:
		;##TRACE "Reduce: $%02x (%y) by %02x - %u values on stack (%02X%02X%02X%02X%02X%02X %g)" (x) db(functionDispatchTableLo-$1D+x)+256*db(functionDispatchTableHi-$1D+x)+1 db($100+((s+1)&$ff)) db(argsp)/3 db(dw(argstk)+db(argsp)-3) db(dw(argstk2)+db(argsp)-3) db(dw(argstk)+db(argsp)-2) db(dw(argstk2)+db(argsp)-2) db(dw(argstk)+db(argsp)-1) db(dw(argstk2)+db(argsp)-1) fr0
		lda		functionDispatchTableHi-$1D,x
		pha
		lda		functionDispatchTableLo-$1D,x
		pha

		;On entry to all functions, X is guaranteed to hold the operator
		;token. This is used by some multi-dispatch points.
		rts
.endp

;===========================================================================
; Precedence tables
;
; There are two precedences for each operator, a go-on and come-off
; precedence. A reduce happens if prec_on(cur) <= prec_off(tos); a
; shift happens otherwise. A prec_on of zero also terminates evaluation
; after the entire stack is reduced. prec_on is the value from the table,
; while prec_off = prec_on & $7F.
;
; If bit 7 is set, the operator always shifts when it is encountered.
; Unary operators and open parens need this.
;
; For arithmetic operators, prec_on <= prec_off for left associativity and
; prec_on > prec_off for right associativity. Bit 0 therefore indicates
; right associativity.
;
; Parentheses use a bit of a hack: open parens are force-shifted onto the
; stack but have a low precedence to allow arguments to accumulate. The
; close parens also has a low precedence and causes reduction of everything
; in between, including comma operators and the open paren. The open paren's
; reduction routine then terminates the reduction loop to prevent the close
; paren from reducing more than one nesting level or itself being
; shifted/reduced.
;
; Commas are shifted onto the stack along with the parameters they separate,
; and when finally reduced due to the close parenthesis, increment the comma
; count as they reduce. They have to be shifted onto the stack instead of
; reducing immediately so that expressions like USR(X,USR(A,B,C)) work --
; the outer call's commas need to be stacked so they don't collide with those
; of the inner call.
;
; Unary operators have to be right-associative. We don't care about order of
; unary +/-, but we do need to preserve ordering of NOT versus +/- so that
; -NOT 0 works. Atari BASIC does not allow this sequence, but Basic XE does.
;
PREC_PCLOSE		= 2
PREC_POPEN		= 4+$80
PREC_COMMA		= 6+$01			;Commas must be right associative so that nesting works, i.e. USR(0,1,2*(X-Y)
PREC_ASSIGN		= 8
PREC_OR			= 10
PREC_AND		= 12
PREC_NOT		= 14+$80
PREC_REL		= 16
PREC_ADD		= 18
PREC_MUL		= 20
PREC_BITWISE	= 22
PREC_EXP		= 24
PREC_UNARY		= 26+$80
PREC_RELSTR		= 28

;Normally, making functions right associative wouldn't make sense because
;there are always parentheses in between -- the closing parens will force
;reduction to the open parens, which will in turn force reduction of the
;function operator. However, SYSGEN from the Corvus disk has a uniquely
;broken statement:
;
; $494B: 22001 
; $494F:    $12 $36   
; $4950:      $80        DAT$
; $4951:      $2E        =
; $4952:      $3E        CHR$
; $4953:      $3A        (
; $4954:      $0E        16
; $495B:      $2C        )
; $495C:      $14        : 
; $495E:    $2C $36   
; $495F:      $80        DAT$
; $4960:      $37        (
; $4961:      $0E        2
; $4968:      $3C        ,
; $4969:      $0E        2
; $4970:      $2C        )
; $4971:      $2E        =
; $4972:      $3E        CHR$
; $4973:      $47        SIN
; $4974:      $9B        DRVNUM
; $4975:      $2C        )
; $4976:      $14        : 
; $4978:    $38 $36   
; $4979:      $9C        THELEN
; $497A:      $2D        =
; $497B:      $0E        2
; $4982:      $14        : 
; $4984:    $3C $0C   GOSUB 
; $4985:      $8A        BCI
; $4986:      $14        : 
; $4988:    $3F $24   RETURN  {end $498A}
;
;There is a SIN token where there should be a function open token. This
;happens to work because Atari BASIC computes SIN(DRVNUM) first, which
;rounds off to be 1 again. To make this work, we have to ensure that the
;SIN reduces first.
;
PREC_FUNC		= 30+$80

.proc	prec_table
		dta		0				;$12	,
		dta		0				;$13	$
		dta		0				;$14	: (statement end)
		dta		0				;$15	;
		dta		0				;$16	EOL
		dta		0				;$17	goto
		dta		0				;$18	gosub
		dta		0				;$19	to
		dta		0				;$1A	step
		dta		0				;$1B	then
		dta		0				;$1C	#
		dta		PREC_REL		;$1D	<=
		dta		PREC_REL		;$1E	<>
		dta		PREC_REL		;$1F	>=
		dta		PREC_REL		;$20	<
		dta		PREC_REL		;$21	>
		dta		PREC_REL		;$22	=
		dta		PREC_EXP		;$23	^
		dta		PREC_MUL		;$24	*
		dta		PREC_ADD		;$25	+
		dta		PREC_ADD		;$26	-
		dta		PREC_MUL		;$27	/
		dta		PREC_NOT		;$28	not
		dta		PREC_OR			;$29	or
		dta		PREC_AND		;$2A	and
		dta		PREC_POPEN		;$2B	(
		dta		PREC_PCLOSE		;$2C	)
		dta		PREC_ASSIGN		;$2D	= (numeric assignment)
		dta		PREC_ASSIGN		;$2E	= (string assignment)
		dta		PREC_RELSTR		;$2F	<= (strings)
		dta		PREC_RELSTR		;$30	<>
		dta		PREC_RELSTR		;$31	>=
		dta		PREC_RELSTR		;$32	<
		dta		PREC_RELSTR		;$33	>
		dta		PREC_RELSTR		;$34	=
		dta		PREC_UNARY		;$35	+ (unary)
		dta		PREC_UNARY		;$36	-
		dta		PREC_POPEN		;$37	( (string left paren)
		dta		PREC_POPEN		;$38	( (array left paren)
		dta		PREC_POPEN		;$39	( (dim array left paren)
		dta		PREC_POPEN		;$3A	( (fun left paren)
		dta		PREC_POPEN		;$3B	( (dim str left paren)
		dta		PREC_COMMA		;$3C	, (array/argument comma)
		
		;$3D and on are functions
		dta		PREC_FUNC		;$3D
		dta		PREC_FUNC		;$3E
		dta		PREC_FUNC		;$3F
		dta		PREC_FUNC		;$40
		dta		PREC_FUNC		;$41
		dta		PREC_FUNC		;$42
		dta		PREC_FUNC		;$43
		dta		PREC_FUNC		;$44
		dta		PREC_FUNC		;$45
		dta		PREC_FUNC		;$46
		dta		PREC_FUNC		;$47
		dta		PREC_FUNC		;$48
		dta		PREC_FUNC		;$49
		dta		PREC_FUNC		;$4A
		dta		PREC_FUNC		;$4B
		dta		PREC_FUNC		;$4C
		dta		PREC_FUNC		;$4D
		dta		PREC_FUNC		;$4E
		dta		PREC_FUNC		;$4F
		dta		PREC_FUNC		;$50
		dta		PREC_FUNC		;$51
		dta		PREC_FUNC		;$52
		dta		PREC_FUNC		;$53
		dta		PREC_FUNC		;$54
		dta		PREC_FUNC		;$55
		dta		PREC_BITWISE	;$56	% (xor)
		dta		PREC_BITWISE	;$57	! (or)
		dta		PREC_BITWISE	;$58	& (and)
		dta		PREC_FUNC		;$59
		dta		PREC_POPEN		;$5A	BUMP(
		dta		PREC_FUNC		;$5B
		dta		PREC_FUNC		;$5C
		dta		PREC_FUNC		;$5D
		dta		PREC_FUNC		;$5E
		dta		PREC_FUNC		;$5F
		dta		PREC_FUNC		;$60
		dta		PREC_FUNC		;$61
.endp

;===========================================================================
ExprPopExtFR0 = expPopFR0

;===========================================================================
ExprFmoveAndPopFR0:
		jsr		fmove
.proc	expPopFR0
		ldy		argsp
		;##ASSERT (y%3)=0 and y
		dey
		mva		(argstk2),y fr0+5
		mva		(argstk),y fr0+4
		dey
		mva		(argstk2),y fr0+3
		mva		(argstk),y fr0+2
		dey
		mva		(argstk2),y fr0+1
		mva		(argstk),y fr0
		sty		argsp
		rts
.endp

;===========================================================================
; Output:
;	A:X = integer value
;	P.N,Z = set from A
;	P.C = 0 (since we fire an error if C=1 from FPI)
;
.proc	expPopFR0Int
		jsr		expPopFR0
.def :ExprConvFR0Int = *
		jsr		fpi
		bcs		fail
		ldx		fr0
		lda		fr0+1
exit:
		rts
fail:
		jmp		errorValueErr
.endp

;===========================================================================
; Output:
;	A:X = integer value
;	P.N,Z = set from A
;	P.C = 0
;
ExprSkipCommaAndEvalPopIntPos:
		inc		exLineOffset
ExprEvalPopIntPos:
		jsr		evaluate
.proc	ExprConvFR0IntPos
		jsr		ExprConvFR0Int
		bpl		expPopFR0Int.exit
		jmp		errorValue32K
.endp

;===========================================================================
; Exit:
;	A = exponent/sign of popped value
;	P.NZ = set from A
;
.proc	expPopFR1
		ldy		argsp
		;##ASSERT (y%3)=0 and y
		dey
		mva (argstk2),y fr1+5
		mva (argstk),y fr1+4
		dey
		mva (argstk2),y fr1+3
		mva (argstk),y fr1+2
		dey
		mva (argstk2),y fr1+1
		mva (argstk),y fr1
		sty		argsp
		rts
.endp

;===========================================================================
.proc	ExprPushExtFR0
		ldy		argsp
		beq		stack_empty
.def :ExprPushFR0NonEmpty = *
		mva		fr0 (argstk),y
		mva		fr0+1 (argstk2),y+
		mva		fr0+2 (argstk),y
		mva		fr0+3 (argstk2),y+
		mva		fr0+4 (argstk),y
		mva		fr0+5 (argstk2),y+
		dta		{bit $0100}
stack_empty:
		ldy		#3
		sty		argsp
.def :funNoOp
		rts
.endp

;===========================================================================
.macro FUNCTION_DISPATCH_TABLE
		;$1D
		dta		:1[funCompare-1]
		dta		:1[funCompare-1]
		dta		:1[funCompare-1]

		;$20
		dta		:1[funCompare-1]
		dta		:1[funCompare-1]
		dta		:1[funCompare-1]
		dta		:1[funPower-1]
		dta		:1[funMultiply-1]
		dta		:1[funAdd-1]
		dta		:1[funSubtract-1]
		dta		:1[funDivide-1]
		dta		:1[funNot-1]
		dta		:1[funOr-1]
		dta		:1[funAnd-1]
		dta		:1[funOpenParens-1]
		dta		:1[funNoOp-1]				;Necessary for SYSGEN (see above).
		dta		:1[funAssignNum-1]
		dta		:1[funAssignStr-1]
		dta		:1[funStringCompare-1]

		;$30
		dta		:1[funStringCompare-1]
		dta		:1[funStringCompare-1]
		dta		:1[funStringCompare-1]
		dta		:1[funStringCompare-1]
		dta		:1[funStringCompare-1]
		dta		:1[funUnaryPlus-1]
		dta		:1[funUnaryMinus-1]
		dta		:1[funArrayStr-1]
		dta		:1[funArrayNum-1]
		dta		:1[funDimArray-1]
		dta		:1[funOpenParens-1]
		dta		:1[funDimStr-1]
		dta		:1[funArrayComma-1]
		
		;$3D
		dta		:1[funStr-1]
		dta		:1[funChr-1]
		dta		:1[funUsr-1]

		;$40
		dta		:1[funAsc-1]
		dta		:1[funVal-1]
		dta		:1[funLen-1]
		dta		:1[funAdr-1]
		dta		:1[funAtn-1]
		dta		:1[funCos-1]
		dta		:1[funPeek-1]
		dta		:1[funSin-1]
		dta		:1[funRnd-1]
		dta		:1[funFre-1]
		dta		:1[funExp-1]
		dta		:1[funLog-1]
		dta		:1[funClog-1]
		dta		:1[funSqr-1]
		dta		:1[funSgn-1]
		dta		:1[funAbs-1]
		
		;$50
		dta		:1[funInt-1]
		dta		:1[funPaddleStick-1]		;PADDLE
		dta		:1[funPaddleStick-1]		;STICK
		dta		:1[funPaddleStick-1]		;PTRIG
		dta		:1[funPaddleStick-1]		;STRIG
		dta		:1[funInvalid-1]			;USING
		dta		:1[funBitwiseXor-1]
		dta		:1[funBitwiseOr-1]
		dta		:1[funBitwiseAnd-1]
		dta		:1[funInvalid-1]			;semicolon
		dta		:1[funBump-1]				;BUMP
		dta		:1[funInvalid-1]			;FIND
		dta		:1[funHex-1]
		dta		:1[funInvalid-1]			;RANDOM
		dta		:1[funDpeek-1]
		dta		:1[funInvalid-1]			;SYS

		;$60
		dta		:1[funVstick-1]
		dta		:1[funHstick-1]
		dta		:1[funPmadr-1]				;PMADR
		dta		:1[funErr-1]
.endm

funInvalid = errorWTF

;===========================================================================
.proc functionDispatchTableLo
		FUNCTION_DISPATCH_TABLE <
.endp

.proc functionDispatchTableHi
		FUNCTION_DISPATCH_TABLE >
.endp