/
asm.sbl
3335 lines (2204 loc) · 84.3 KB
/
asm.sbl
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
-title mincod : phase 2 translation from minimal tokens to 80386 code
-stitl description
* Copyright 1987-2012 robert b. k. dewar and mark emmer.
* Copyright 2012-2017 david shields
* this file is part of macro spitbol.
* macro spitbol is free software: you can redistribute it and/or modify
* it under the terms of the gnu general public license as published by
* the free software foundation, either version 2 of the license, or
* (at your option) any later version.
* macro spitbol is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. see the
* gnu general public license for more details.
* you should have received a copy of the gnu general public license
* along with macro spitbol. if not, see <http://www.gnu.org/licenses/>.
* this program takes input file in minimal token form and produces assembly
* code for intel x64 processor. the program obtains the name of the file
* to be translated from the command line string in host(0).
* options relating to the processing of comments can be changed
* by modifying the source.
* In addition to the minimal token file, the program requires the name
* of a "machine definition file" that contains code specific to a
* particular 80386 assembler.
* you may also specify option flags on the command line to control the
* code generation. the following flags are processed:
* compress generate tabs rather than spaces in output file
* comments retain full-line and end-of-line comments
* the variable arch is set equal to the uppercase name of the machine
* being processed. specific tests upon this variable are discouraged,
* as all machine-dependent code should be placed in the machine-definition
* file if possible.
* in addition to the normal minimal register complement, one scratch work
* register, w0 is defined. see the register map below for
* specific allocations.
* this program is based in part on earlier translators for the it is based
* in part on earlier translators for the dec vax (vms and un*x)
* written by steve duff and robert goldberg, and the pc-spitbol
* translator written by david shields.
* to run under spitbol:
* spitbol -u "<file>:<machine>[:flag:...:flag]" codlinux.spt
* reads <file>.lex containing tokenized source code
* writes <file>.s with 80386 assembly code
* also writes <file>.err with err and erb error messages
* parts of m.hdr are prepended and appended to <file>.s
* also sets flags to 1 after converting names to upper case
* also reads <file>.pub for debug symbols to be declared public
* example:
* spitbol -u v37:dos:compress codlinux.spt
* error is used to report an error for current statement
-stitl chktrace()
define('chktrace()') :(chktrace_end)
chktrace
* :(return)
* output = 'chktrace:' iinput_lines ':' label ':' stmtout
* output = differ (label) 'chktrace label:' label ':'
* turn off skip mode when begin executable code
clabel = inlabel
old_z_skip = z_skip
old_z_exec = z_exec
old_is_exec = is_exec
z_skip = ident(inlabel,'s_aaa') 0
* incode ? any(lcase) :s(return)
uopcode = replace(incode, lcase,ucase)
* do not trace bsw (for now)
ident(uopcode,'bsw') :s(return)
is_exec = is_executable[uopcode]
z_exec = ne(z_trace) ident(inlabel, 's_aaa') 1
z_exec = gt(input_lines,2186) 1
* need to skip certain blocks since otherwise get branches that are
* too long skip when in code that won't assemble if try to trace
* this was discovered on a case-by-case basis.
z_skip = differ(inlabel) differ(skip_on[inlabel]) 1
z_skip = differ(inlabel) differ(skip_off[inlabel]) 0
ne(z_skip) :s(return)
eq(z_exec) :s(return)
eq(is_exec) :s(return)
* here to emit trace. need to emit trace after label if there is label
* ident(inlabel) :s(chktrace.1)
* only trace at labels since get jumps that are too removed otherwise
* ident(label) :s(return)
* here to emit trace code when there is label
* first need to emit label, then fall through
* stmtout ? break_ws . label spanws rem . body :f(outstmt5)
* stmtout = tab body
* outfile = label
* label =
ne(in_gcol) :s(return)
chktrace.1
genz()
:(return)
chktrace_end
-stitl comregs(line)t,pre,word
define('comregs(line)t,pre,word') :(comregs_end)
* map minimal register names to target register names
comregs
line p.comregs = :f(comregs1)
word = eq(size(word),2) differ(t = register(word)) t
comregs = comregs pre word :(comregs)
comregs1 comregs = comregs line :(return)
comregs_end
-stitl crack(line)
define('crack(line)operands,operand,char') :(crack.end)
* crack is called to create a stmt plex containing the various parts of
* the minimal source statement in line. for conditional assembly ops,
* the opcode is the op, and op1 is the symbol. note that dtc is
* handled as a special case to assure that the decomposition is correct.
* crack prints an error and fails if a syntax error occurs.
* crack parses stmt into a stmt data plex and returns it.
* it fails if there is a syntax error.
crack
nstmts = nstmts + 1
op1 = op2 = op3 = typ1 = typ2 = typ3 =
line p.csparse :s(return)
* here on syntax error
error('source line syntax error') :(freturn)
crack.end
-stitl error(text)
define('error(text)') :(error_end)
* this module handles reporting of errors with the offending
* statement text in thisline. comments explaining
* the error are written to the listing (including error chain), and
* the appropriate counts are updated.
error
outfile = filenami ': error: ' text
outfile = rpad(lpad(input_lines,6),size(filenami) -1) ' | ' thisline
lasterror = output_lines
output_lines = output_lines + 2
le(nerrors = nerrors + 1, 10) :s(opnext)
output = 'too many errors, quitting' :(end)
error_end
-stitl flush
define('flush()') :(flush_end)
* here to emit bstmts, cstmts, astmts. attach input label and
* comment to first instruction generated.
flush
eq(astmts.n) eq(bstmts.n) eq(cstmts.n) :f(flush_0)
* here if some statements to emit, so output single 'null' statement
* to get label and comment field right.
label = thislabel =
outstmt(tstmt()) :(flush_6)
flush_0
eq(bstmts.n) :s(flush_2)
i = 1
flush_1
outstmt(bstmts[i])
le(i = i + 1, bstmts.n) :s(flush_1)
flush_2
eq(cstmts.n) :s(flush_4)
i = 1
flush_3
outstmt(cstmts[i])
le(i = i + 1, cstmts.n) :s(flush_3)
flush_4 eq(astmts.n) :s(flush_6)
i = 1
ident(pifatal[incode]) :s(flush_5)
* here if post incrementing code not allowed
error('post increment not allowed for op ' incode)
flush_5
outstmt(astmts[i])
le(i = i + 1, astmts.n) :s(flush_5)
flush_6
astmts.n = bstmts.n = cstmts.n = :(return)
flush_end
-stitl genaop(stmt)
define('genaop(stmt)') :(genaop_end)
* generates code for statements that must be executed
* after this instruction.
genaop
astmts[astmts.n = astmts.n + 1] = stmt :(return)
genaop_end
-stitl genbop(stmt)
define('genbop(stmt)') :(genbop_end)
* generates code for statements that must be executed
* before this instruction.
genbop
bstmts[bstmts.n = bstmts.n + 1] = stmt :(return)
genbop_end
-stitl genlab()
define('genlab()') :(genlab_end)
* generate unique label for use in generated code
genlab
genlab = '_l' lpad(genlabels = genlabels + 1,4,'0'):(return)
genlab_end
-stitl genop(gopc,gop1,gop2,gop3)
define('genop(gopc,gop1,gop2,gop3)') :(genop_end)
* generate operation with no label
genop
genopl(,gopc,gop1,gop2,gop3) :(return)
genop_end
-stitl genopl(gopl,gopc,gop1,gop2,gop3)
define('genopl(gopl,gopc,gop1,gop2,gop3)') :(genopl_end)
* generate operation with label
genopl
cstmts[cstmts.n = cstmts.n + 1] =
+ tstmt(gopl,gopc,gop1,gop2,gop3) :(return)
genopl_end
-stitl define(genrep(op))
define('genrep(op)l1,l2)') :(genrep_end)
* generate code to repeat operation *op* using
* 'rep *op* loop' instruction.
genrep
l1 = genlab()
l2 = genlab()
genopl(l1 ':')
genop('or',wa,wa)
genop('jz',l2)
genop(op)
genop('dec',wa)
genop('jmp',l1)
genopl(l2 ':')
:(return)
genrep_end
-stitl genz
define('genz()') :(genz_end)
* generate trace instruction if needed.
genz
* no trace if trace has been suspended
* output = ne(z_suspend) 'z_suspend ' thisline
ne(z_suspend) :s(return)
* only trace at label definition
* ident(thislabel) :s(return)
z_count = z_count + 1
gt(z_first) le(z_count,z_first) :s(return)
gt(z_limit) gt(z_count, z_limit) :s(return)
* always generate trace if at label definition
z_desc = '"' replace(thisline,sepchar,' ') '"'
outfile = tab 'zzz' tab z_count ',' input_lines ',' z_desc
outlines = outlines + 1
:(return)
genz_end
-stitl getarg(iarg,imem)
define('getarg(iarg,imem)l1,l2,t1,t2,tmem') :(getarg_end)
* get argument to register and return that register.
getarg
* output = 'getarg text <' i.text(iarg) '> i.type <' i.type(iarg) '>'
tmem = (differ(imem) '', 'm_word ')
l1 = i.text(iarg)
l2 = i.type(iarg)
eq(l2) :f($('getarg_c.' l2))
getarg_c.1
getarg = l1 :(return)
* int
getarg = l1 :(return)
getarg_c.2
* dlbl
getarg = l1 :(return)
getarg_c.3
getarg_c.4
* wlbl, clbl
getarg = tmem '[' l1 ']' :(return)
getarg_c.5
getarg_c.6
* elbl, plbl
getarg = l1 :(return)
getarg_c.7
getarg_c.8
* w,x, map register name
getarg = register(l1) :(return)
getarg_c.9
* (x), register indirect
l1 len(1) len(2) . l2
l2 = register(l2)
getarg = tmem '[' l2 ']' :(return)
getarg_c.10
* (x)+, register indirect, post increment
* use lea reg,[reg+cfp_b] unless reg is esp, since it takes an extra byte.
* actually, lea reg,[reg+cfp_b] and add reg,cfp_b are both 2 cycles and 3 bytes
* for all the other regs, and either could be used.
l1 = substr(l1,2,2)
t1 = register(l1)
getarg = tmem '[' t1 ']'
(ident(l1,xs) genaop(tstmt(,'add',t1,'cfp_b'))) :s(return)
genaop(tstmt(,'lea',t1,'[' t1 '+cfp_b]')) :(return)
getarg_c.11
* -(x), register indirect, pre decrement
t1 = register(substr(l1,3,2))
* output = 'getarg_c.11 t1 <' t1 '>'
getarg = tmem '[' t1 ']'
genbop(tstmt(,'lea',t1,'[' t1 '-cfp_b]')) :(return)
getarg_c.12
getarg_c.13
* int(x)
* dlbl(x)
l1 break('(') . t1 '(' len(2) . t2
getarg = tmem '[(cfp_b*' t1 ')+' register(t2) ']':(return)
getarg_c.14
getarg_c.15
* name(x), where name is in working section
l1 break('(') . t1 '(' len(2) . t2
getarg = tmem '[' t1 '+' register(t2) ']' :(return)
getarg_c.16 getarg = l1 :(return)
* signed integer
getarg_c.17 getarg = l1 :(return)
* signed real
getarg_c.18
* =dlbl
getarg = substr(l1,2) :(return)
getarg_c.19
* *dlbl
getarg = 'cfp_b*' substr(l1,2) :(return)
getarg_c.20
getarg_c.21
* =name (data section)
getarg = substr(l1,2) :(return)
getarg_c.22
* =name (program section)
getarg = substr(l1,2) :(return)
getarg_c.23
getarg_c.24
* pnam, eqop
getarg = l1 :(return)
getarg_c.25
getarg_c.26
getarg_c.27
* ptyp, text, dtext
getarg = l1 :(return)
getarg_end
define('init()') :(init_end)
init
* revision history:
version = 'v1.12'
rcode = '_rc_'
* keyword initialization
&anchor = 1; &stlimit = 15000000; &trim = 1; &dump = 1
&dump = 2
* useful constants
ucase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
lcase = 'abcdefghijklmnopqrstuvwxyz'
letters = ucase lcase
nos = '0123456789'
tab = char(9)
* sepchar separates fields in input file
sepchar = '|'
* default the parameter string if none present
fileprefix = "sbl"
* cfp_b is bytes per word, cfp_c is characters per word these should
* agree with the values used in translator. set target-dependent
* configuration parameters.
* cfp_b is bytes per word, cfp_c is characters per word
* these should agree with values used in translator
cfp_b = 8
log_cfp_b = '3'
cfp_c = 8
log_cfp_c = '3'
* used for data declarations
op_w = 'q'
op_c = 'b'
* target register assignments
cp = 'cp'
* w0 is temp register
w0 = 'rax'
wa = 'rcx'
wb = 'rbx'
wb = 'wb'
wc = 'rdx'
ia = 'r12'
ra = 'xmm12'
cp = 'r13'
xl = 'rsi'
xl = 'xl'
xt = xl
xr = 'rdi'
xr = 'xr'
xs = 'rsp'
w0 = 'w0'; wa = 'wa'; wb = 'wb'; wc = 'wc'
xl = 'xl'; xr = 'xr'; xs = 'xs'; xt = xl
* internal work registers
* r10, r11 scope only within individual minimal opcodes
* values not preserved or used outside of the minimal opcode
r10 = 'r10'; r11 = 'r11'
* Global registers that are preserved
* _ia_ is mapped to _r12_ in nasm.h.
* _ra_ is mapped to _xmm12_ in nasm.h
ia = 'ia'
ra = 'ra'
cp = 'r13'
* cp is kept in memory
* symbolic target assignments
* real_op maps minimal real opcode to machine opcode
real_op = table(10)
real_op['adr'] = 'fadd'
real_op['atn'] = 'fpatan'
real_op['chp'] = 'frndint'
real_op['cos'] = 'fcos'
real_op['dvr'] = 'fdiv'
real_op['ldr'] = 'fld'
real_op['mlr'] = 'fmul'
real_op['ngr'] = 'fchs'
real_op['sbr'] = 'fsub'
real_op['sin'] = 'fsin'
real_op['sqr'] = 'fsqrt'
real_op['str'] = 'fst'
config_done
* set z_trace to enable instruction by instruction trace
z_trace = 1
z_trace = 0
* z_limit is maximum number of calls to be generated if non-zero
z_limit = 000
* set z_first non-zero to skip first number of instructions that would
* generate trace
z_first = 0
* will set in_executable when in part of program where executable
* instructions may occur
z_exec = 0
* z_suspend is set to temporarily disable the trace.
z_suspend = 0
* set in_skip when should not insert trace code, else assembly errors result.
* start with skip on, turn off when see first start of code.
z_skip = 1
* skip_on and skip_off are labels indicating the start and end,
* respectively, of sections of the code that should not be traced,
* usually because they contain a loop instruction that won't
* compile if too much trace code is inserted.
skip_on = table(50)
skip_off = table(50)
* skip_init('start:ini03')
skip_init('gbcol :gtarr')
* skip_init('gtn01:gtnvr')
* skip_init('bpf05:bpf07')
* skip_init('scv12:scv19')
* skip_init('exbl1:exbl2')
* skip_init('exbl5:expan')
* skip_init('prn17:prn18')
* skip_init('ini11:ini13')
* skip_init('oex13:oexp2')
* skip_init('oex14:oexp6')
* skip_init('bdfc1:b_efc')
* skip_init('sar01:sar10')
* skip_init('srpl5:srpl8')
* skip_init('pflu1:pflu2')
* skip_init('prpa4:prpa5')
* skip_init('prn17:prn18')
* skip_init('prtvl:prtt1')
* skip_init('trim4:trim5')
* skip_init('prnl1:prnl2')
* skip_init('prti1:prtmi')
* skip_init('srpl5:srpl8')
* data structures
data('minarg(i.type,i.text)')
data('tstmt(t.label,t.opc,t.op1,t.op2,t.op3,t.comment)')
sectnow = 0
* ppm_cases gives count of ppm/err statments that must follow call to
* a procedure
ppm_cases = table(50,,0)
p.comregs = break(letters) . pre span(letters) . word
* exttab has entry for external procedures
exttab = table(50)
* labtab records labels in the code section, and their line numbers
labtab = table(500)
* for each statement, code in generated into three
* arrays of statements:
* astmts: statements after opcode (()+, etc.)
* bstmts: statements before code (-(), etc)
* cstmts: generated code proper
astmts = array(20,'')
bstmts = array(10,'')
cstmts = array(20,'')
* genlabels is count of generated labels (cf. genlab)
genlabels = 0
* initialize variables
labcnt = output_lines = input_lines = nstmts = ntarget = nerrors = 0
lastopc = lastop1 = lastop2 =
data_lc = 0
max_exi = 0
* initial patterns
* p.csparse parses tokenized line
p.csparse = sepchar break(sepchar) . inlabel
+ sepchar break(sepchar) . incode
+ sepchar break(sepchar) . iarg1
+ sepchar break(sepchar) . iarg2
+ sepchar break(sepchar) . iarg3
+ sepchar break(sepchar) . incomment
+ sepchar rem . slineno
* pifatal maps minimal opcodes for which no a code allowed to nonzero value.
* such operations include conditional branches with operand of form (x)+
pifatal = initmap(
+ 'aov:1 beq:1 bne:1 bge:1 bgt:1 bhi:1 ble:1 blo:1 blt:1 bne:1 bnz:1 ceq:1 cne:1 mfi:1 nzb:1 zrb:1 ')
* trace not working for mvc (x64)
is_executable = initmap(
+ 'add:1 adi:1 adr:1 anb:1 aov:1 atn:1 '
+ 'bct:1 beq:1 bev:1 bge:1 bgt:1 bhi:1 ble:1 blo:1 blt:1 bne:1 bnz:1 bod:1 '
+ 'brn:1 bri:1 bsw:1 btw:1 bze:1 ceq:1 chk:1 chp:1 cmb:1 cmc:1 cmp:1 cne:1 csc:1 '
+ 'cos:1 ctb:1 ctw:1 cvd:1 cvm:1 dca:1 dcv:1 eti:1 dvi:1 dvr:1 erb:1 esw:1 etx:1 flc:1 '
+ 'ica:1 icp:1 icv:1 ieq:1 ige:1 igt:1 ile:1 ilt:1 ine:1 ino:1 iov:1 itr:1 jmp:1 '
+ 'jsr:1 lch:1 lct:1 lcp:1 lcw:1 ldi:1 ldr:1 lei:1 lnf:1 lsh:1 lsx:1 mcb:1 mfi:1 mli:1 mlr:1 '
+ 'mnz:1 mov:1 mti:1 mvw:1 mwb:1 ngi:1 eti:1 ngr:1 nzb:1 orb:1 plc:1 prc:1 psc:1 '
+ 'req:1 rge:1 rgt:1 rle:1 rlt:1 rmi:1 rne:1 rno:1 rov:1 rsh:1 rsx:1 rti:1 rtn:1 sbi:1 sbr:1 '
+ 'sch:1 scp:1 sin:1 sqr:1 ssl:1 sss:1 sti:1 str:1 sub:1 tan:1 trc:1 wtb:1 xob:1 zer:1 '
+ 'zgb:1 zrb')
* don't trace mvc as doing so causes just 'end' to fail. sort out later. (ds 01/09/13)
* various constants
comment.delim = ';'
* branchtab maps minimal opcodes 'beq', etc to desired target instruction
branchtab = initmap( 'beq:je bne:jne bgt:ja bge:jae ble:jbe blt:jb blo:jb bhi:ja ')
* optim.tab flags opcodes capable of participating in or optimization in outstmt routine.
optim.tab = initmap('and:1 add:1 sub:1 neg:1 or:1 xor:1 shr:1 shl:1 inc:1 dec:1 ')
* ismem is a map from operand type that is nonzero if the operand type is a memory reference.
ismem = initmap('3:1 4:1 5:1 9:1 10:1 11:1 12:1 13:1 14:1 15:1 ')
* other definitions that are dependent upon things defined in the machine definition file,
* and cannot be built until after the definition file has been read in.
* p.outstmt examines output lines for certain types of comment contructions
fillc = (ident(compress) " ",tab)
p.outstmt = (break(fillc) . label span(fillc)) . leader
+ comment.delim rem . comment
p.alltabs = span(tab) rpos(0)
filenami = 'sbl.lex'
input(.infile,1,filenami) :s(inputok)
inputok
report(filenami, 'input lex file')
* associate output files.
filenamo = 'sbl.asm'
output(.outfile,2,filenamo) :s(outputok)
output = ' cannot open asm file: ' filenamo :(end)
outputok
output = report(filenamo,'output asm file')
* open file for compilation of minimal err and erb messages
output(.errfile,3, fileprefix ".err") :s(err_ok)
output = " cannot open error message file: " fileprefix ".err":(end)
err_ok
* will have havehdr non-null if more remains to copy out at end.
:(nopub)
* read in pub file if it exists. this contains a list of symbols to
* be declared public when encountered.
pubtab = table(2)
input(.pubfile,5, fileprefix ".pub") :f(nopub)
pubtab = table(101)
pubcopy
line = pubfile :f(pubend)
pubtab[line] = 1 :(pubcopy)
pubend
endfile(5)
nopub
* get file name
* get definition file name following token file name, and flags.
* fileprefix ? break(';:') . fileprefix len(1)
*. (break(';:') | rem) . target
*. ((len(1) rem . flags) | '')
* $replace(target,lcase,ucase) = 1
* parse and display flags, setting each one's name to non-null value (1).
:(flgs.skip)
flgs
flags ? ((len(1) break('; :')) . flag len(1)) |
+ ((len(1) rem) . flag) = :f(flgs2)
flag = replace(flag,lcase,ucase)
output = " flag : " flag
$flag = 1 :(flgs)
flgs.skip
flgs2
:(return)
init_end
-stitl initmap(str)
define('initmap(str),key,val') :(initmap.end)
* Initialize a table from a string of key/value pairs.
initmap
initmap = table(size(str))
initmap.1
str break(':') $ key len(1) break(' ') $ val len(1) = :f(return)
key = convert(key,'integer')
val = convert(val,'integer' )
initmap[key] = val :(initmap.1)
initmap.end
-stitl include(filename)
* copy contents of *filename*
define('include(filename)includefile,line') :(include.end)
include
input(.includefile,4,filename) :s(include.next)
error('cannot open include file ' filename) :(return)
include.next
outfile = includefile :s(include.next)
endfile(4) :(return)
include.end
-stitl isreg(iarg)
define('isreg(iarg)') :(isreg_end)
* succeeds if *iarg* is a minimal register name, fails otherwise.
isreg
* output = 'isreg datatype ' datatype(iarg) ' <' datatype(iarg) '>'
ge(i.type(iarg),7) le(i.type(iarg),8) :f(freturn)s(return)
isreg_end
-stitl memmem()t
define('memmem()t') :(memmem.end)
memmem
* memmem is called for those ops for which both operands may be
* in memory, in which case, we generate code to load second operand
* to pseudo-register w0, and then modify the second argument
* to reference this register
eq(ismem[i.type(i1)]) :s(return)
eq(ismem[i.type(i2)]) :s(return)
* here if memory-memory case, load second argument and then make
* the second argument *w0*.
t = getarg(i2)
genop('mov',w0,getarg(i2))
i2 = minarg(8,'w0')
:(return)
memmem.end
-stitl outstmt(ostmt)label,opcode,op1,op2,op3,comment,line)
define('outstmt(ostmt)line,label,opcode,op1,op2,op3,comment,t,stmtout')
+ :(outstmt_end)
* outstmt is used to send a target statement to the output file.
outstmt label = t.label(ostmt)
* clear label if definition already emitted
label = ident(label, lastlabel)
outstmt1
* attach source comment to first generated instruction
differ(comment) :s(outstmt2)
ident(tcomment) :s(outstmt2)
comment = tcomment; tcomment =
outstmt2
opcode = t.opc(ostmt)
op1 = t.op1(ostmt)
op2 = t.op2(ostmt)
op3 = t.op3(ostmt)
differ(compress) :s(outstmt3)
line = rpad( rpad(label,7) ' ' rpad(opcode,4) ' '
+ (ident(op1), op1
+ (ident(op2), ',' op2
+ (ident(op3), ',' op3))) ,27)
:(outstmt4)
outstmt3
line = label tab opcode tab
+ (ident(op1), op1
+ (ident(op2), ',' op2
+ (ident(op3), ',' op3)))
outstmt4
line = trim(line)
line = le(size(line),48) rpad(line,48) '; ' comment:s(outstmt5)
line = le(size(line),56) rpad(line,56) '; ' comment:s(outstmt5)
line = line '; ' comment
outstmt5
**
** send text to output file if not null.
* line = replace(trim(line),'$','_')
eq(z_trace) :s(outstmt6)
* here if trace code desired for executable instructions
chktrace()
outstmt6
* output = line
outfile = line
ntarget = ntarget + 1
output_lines = output_lines + 1
* record code labels in table with delimiter removed.
(ge(sectnow,5) differ(thislabel)) :f(return)
label ? break(':') . label :f(return)
labtab<label> = output_lines :(return)
outstmt_end
-stitl prcent(n)
define('prcent(n)') :(prcent_end)
prcent prcent = 'prc_+cfp_b*' ( n - 1) :(return)
prcent_end
-stitl prsarg(iarg)
define('prsarg(iarg)l1,l2') :(prsarg_end)
prsarg
prsarg = minarg(0)
iarg break(',') . l1 ',' rem . l2 :f(return)
prsarg = minarg(convert(l1,'integer'),l2) :(return)
prsarg_end
-stitl readline()
define('readline()') :(readline_end)
* this routine returns the next statement line in the input lex file
* to the caller. it never fails. if there is no more input,
* then a minimal end statement is returned.
* comments are passed through to the output file directly.
readline
readline = infile :f(readline_eof)
input_lines = input_lines + 1
ident( readline ) :f(readline_2)
outfile = readline
output_lines = output_lines + 1 :(readline)
readline_2
* output = readline
lne(substr(readline,1,1 ),'*' ) :s(return)
* Here if comment.