-
Notifications
You must be signed in to change notification settings - Fork 4
/
zmac.y
8581 lines (7621 loc) · 192 KB
/
zmac.y
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
%{
// GWP - keep track of version via hand-maintained date stamp.
#define VERSION "28jul2018"
/*
* zmac -- macro cross-assembler for the Zilog Z80 microprocessor
*
* Bruce Norskog 4/78
*
* Last modification 1-18-87 by cdk
* This assembler is modeled after the Intel 8080 macro cross-assembler
* for the Intel 8080 by Ken Borgendale. The major features are:
* 1. Full macro capabilities
* 2. Conditional assembly
* 3. A very flexible set of listing options and pseudo-ops
* 4. Symbol table output
* 5. Error report
* 6. Elimination of sequential searching
* 7. Commenting of source
* 8. Facilities for system definiton files
*
* Revision History:
*
* jrp 3-8-82 Converted to run on Vax, updated syntax to conform better
* to the Zilog standard.
*
* jrp 3-15-82 Added underscore as a character type in the lex table
* 'numpart' (0x5F).
*
* Changed maximum number of characters in a label to 15
* from 7. Note that 'putsymtab' uses this value inside
* of a quoted string, so we use 15.
*
* jrp 2-15-83 Fixed 'getlocal' to return better local labels. It used
* to crash after 6 invocations.
*
* jrp 6-7-83 Fixed bug in the ADD IX,... instruction.
*
* jrp 5-11-84 Added code to print unused labels out with the symbol table
* Also sped up the macro processor by using stdio.
*
* jrp 5-22-84 Added include files ala ormac
*
* jrp 8-27-84 Added PHASE/DEPHASE commands
*
* cdk 9-20-86 Converted to run on a Pyramid. This meant changing yylval
* to be a %union, and then putting in the appropriate
* typecasts where ints are pointers are used interchangeably.
* The current version still probably won't run on machines where
* sizeof(int) != sizeof(char *).
* Also changed emit() to use varargs, and got rid of the
* old style = in front of yacc action code.
* -Colin Kelley vu-vlsi!colin
*
* cdk 10-2-86 Added some more typecasts to keep lint a little happier.
* Removed several unused variables. Changed most vars
* declared as char to int, since many of them were being
* compared with -1! I still don't know what's going on with
* est[][] being malloc'd and free'd everywhere...it looks pretty
* fishy...
*
* cdk 1-18-87 Added MIO code to emulate 'mfile' using malloc()'d memory.
* This was needed to get the code to work when compiled under
* MSC 4.0 on a PC, and it's probably faster anyway.
*
* cdk 2-5-87 Added 'cmp' as a synonym for 'cp', 'jmp' as a synonym for
* 'jp', and added tolerance of accumulator specification for arithmetic
* and logical instructions. (For example, 'or a,12' is now accepted,
* same as 'or 12'.)
*
* gwp 12-29-08 Changes to allow compilation with modern C compiler and using bison
* as the .y to .c converter. assert, tstate pseudo-ops.
* t(), tilo(), tihi() functions. ==, <=, >=, !=, !, <, > operators.
* -c to turn cycle counts off in listing. Usage, -h and version.
*
* gwp 9-26-10 Add ocf() and setocf to track and set op code fetch counts.
* Add sett as an alias for tstate
*
* gwp 12-30-11 Add undocumented instructions sl1, pfix, pfiy, in (c), out (c),0
* bit/set/res (ixy+d),reg and ld/inc/dec ixylh.
*
* gwp 2-8-12 Increase MAXIFS massively due to massive lowt macro
*
* gwp 2-11-12 Support 32 bit constants. '%' alias for MOD. Add defd, dword.
* lo(x) and hi(x) for easy low and high byte extraction. Allow
* filenames longer than 15 characters. All output to "zout" subdirectory
* of source file.
*
* gwp 2-15-12 Perform multiple passes while equates are changing. Support
* .label for temporary label definitions and _label for file
* scoped labels. Allow '.' within labels. Assert listing bugfix.
*
* gwp 4-27-12 Implement $ prefixed hex constants and double-quoted strings.
*
* gwp 6-30-12 Minor changes to allow compilation with gcc.
*
* gwp 9-05-12 incbin added.
*
* gwp 11-24-12 Fix macro expansion bug when symbol larger than MAXSYMBOLSIZE
* due to file name prepending when symbol starts with '_'.
*
* gwp 12-04-12 Optional JR promotion and JP demotion errors. Output a warning
* if no execute address given. Output ".bds" file to enable easy
* simple source level debugging.
*
* gwp 4-14-13 Parens in expressions, else, .pseudo, full set of C operators
* with conventional precedence and various aliases and code
* changes to make source similar to zmac 1.3 on internet.
*
* gwp 5-5-13 .cmd,.cas,.lcas,.bin output. dc (both MACRO-80 and EDAS types).
* lo, hi renamed to low, high and make unary operators. Allow
* label::, placeholder public, extern declarations. Bug fixes
* in defs, t, ocf, tihi, tilo in phase mode. Initial support
* for -I include directories. 0x hex constants. --mras flag for
* limited MRAS compatibility (allows $ in labels, $? to start
* labels).
*
* gwp 4-6-13 --rel for .rel (Microsoft linker) output and extern, public,
* aseg, cseg, dseg in support (big emit + expression revamp).
* -I follows C preprocessor convention, output relative to
* current directory. 8080 mnemonics, .z80, .8080, -z, -8.
* Change .bin to .cim. Warn on labels not in first column.
*
* gwp 8-11-13 Allow $ to start identifiers and do '$' dropping when macro
* parsed so we no longer need to drop '$' in identifiers.
* Even $FCB allowed, with warning. Add --zmac for backwards
* compatibility with zmac. ` now used for joining in macros.
* Most reserved words can be used as labels or variables.
* Free-form title, name, comment, subttl parsing. Allow #arg
* for macro arguments (in --mras mode). Support <CR> delimited
* files. Add .ams output. Integrate documentation (--doc).
*
* gwp 3-12-14 Emit jr even if out of range. z80.lib support.
* Warning and bug fixes from Mark Galanger.
* Macros can override built-ins and are no longer listed
* in symbol table. A, B, C, D, E, H, L, M, PSW, SP are
* pre-defined values which can be used in data statements
* (db, dw, dd). Reserved words can be equated but are only
* accessbile in data. SET can be used in place of DEFL
* (MAC and MACRO-80 agree on this). '=' can be used in place
* of EQU. 'maclib file' includes 'file.lib'. Bug fix in "dw 0,$".
* Removed error flagging in expressions which could cause parse
* to fail from that point onwards.
* expression(ix) equivalent to (ix + expression).
* Macro expanded lines didn't go through the line analyzer.
* Empty macro arguments (e.g., mac 1,,2)
* Implemented rept, irp, irpc, exitm. Add more detail on phase
* errors. '' is an empty string in db/ascii/etc, 0 otherwise.
* Almost any name can be used as a macro parameter name.
* Allow 'if' in first column.
* Fix .rel mode bug in dc, incbin.
* Emit .bds output for dc, incbin.
* Allow assembly to wrap past end of memory.
* "pragma bds" for raw output to .bds file. Also output equates
* to .bds file.
* Macro bug fix from Sergey Erokhin.
*
* gwp 9-5-16 Allow ' on identifiers for manual prime register tracking.
* Improve MRAS support with *LIST, *INCLUDE and Pk=n parameters.
*
* gwp 20-9-16 Big MRAS compatibility fixes. Can do MRAS operator precedence,
* proper .operator. tokenization and agressive macro parameter
* substituion. Change Pk=n to -Pk=n. Add ++, += and variants
* for more compact variable adjustment than defl. First crack
* at .tap output for ZX Spectrum support.
*
* gwp 13-8-17 Add "import" for simple once-only inclusion of files.
* Track full path so relative includes work properly.
* Allow push af', pop af' for notational convenience.
* Add "bytes" as alias for "dc". Fix --rel output bugs in
* low(), high(), div and mod.
*
* gwp 12-3-18 250 baud .cas output and .wav format. Common blocks.
* --oo, --xo, --od to control output. Delete output on fail.
*
* gwp 2-6-18 1000 baud .cas ouput and .mds (MAME/MESS debug script) output.
*
* gwp 28-7-18 Double free of output files bug fix from Pedro Gimeno. Don't
* output SYSTEM files if no entry point thanks to Tim Halloran.
*/
#if defined(__GNUC__)
#pragma GCC diagnostic error "-Wreturn-type"
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <sys/stat.h>
#ifdef WIN32
#include <windows.h> // just for colouring the output
#include <direct.h> // _mkdir
#ifdef _MSC_VER
#define strdup _strdup
#define unlink _unlink
#endif
#endif
#if defined(__APPLE__) || defined(__linux__) || defined(EMSCRIPTEN)
#include <unistd.h> // just for unlink
#endif
#include "zi80dis.h"
#ifdef vax11c
#define unlink(filename) delete(filename)
#endif
#include "mio.h"
/*
* DEBUG turns on pass reporting.
* DBUG enables -d to allow yacc/bison yydebug increment (but must be passed
* on the command line)
* Macro debug and Token debug enables.
#define DEBUG
#define M_DEBUG
#define T_DEBUG
*/
#ifdef DBUG
#define YYDEBUG 1
#endif
#define ITEMTABLESIZE 100000
#define TEMPBUFSIZE (1000+MAXSYMBOLSIZE)
#define LINEBUFFERSIZE 1000
#define EMITBUFFERSIZE 200
#define MAXSYMBOLSIZE 40
#define IFSTACKSIZE 20
// GWP - I use lots of if's with my lowt macro
#define MAXIFS 65536
#define TITLELEN 50
#define BINPERLINE 16
#define PARMMAX 25
#define MAXEXP 25
#define SYMMAJIC 07203
#define NEST_IN 32
#define MAXPASS 32
#define MAXINCPATH 32
int iflist();
int yylex();
int phaseaddr(int addr);
int nextchar();
int getcol();
int skipline(int ac);
int tokenofitem(int deftoken, int keyexclude, int keyinclude);
int getm();
int counterr();
int countwarn();
int convert(char *buf, char *bufend, int *overflow);
void yyerror(char *err)
{} /* we will do our own error printing */
struct argparse {
char *arg; // output buffer for argument
int argsize; // size of output buffer
int (*getch)(struct argparse *); // get next character
int *peek; // pointer single item pushback buffer
int macarg; // working on macro arguments
char *user_ptr; // state for getch
int user_int; // state for getch
int user_peek; // state for getch
int didarg; // internal parsing state
int numarg; // internal parsing state
};
int getarg(struct argparse *ap);
struct item {
char *i_string;
int i_value;
int i_token;
int i_uses;
int i_scope;
int i_chain;
int i_pass;
};
void itemcpy(struct item *dst, struct item *src);
struct item *keyword(char *name);
#define SCOPE_NONE (0)
#define SCOPE_PROGRAM (1)
#define SCOPE_DATA (2)
#define SCOPE_COMMON (3)
#define SCOPE_PUBLIC (4)
#define SCOPE_EXTERNAL (8)
#define SCOPE_NORELOC (16)
#define SCOPE_BUILTIN (32) /* abuse */
#define SCOPE_COMBLOCK (64)
#define SCOPE_SEGMASK (3)
#define SCOPE_SEG(s) ((s) & SCOPE_SEGMASK)
struct expr {
int e_value;
int e_scope;
int e_token;
struct item *e_item;
struct expr *e_left;
struct expr *e_right;
};
#define EXPR_SEG(e) SCOPE_SEG(e->e_scope)
FILE *fout,
*fbuf,
*fbds,
*fcmd,
*fcas,
*flcas,
*flnwcas,
*ftcas,
*fcim,
*fams,
*frel,
*ftap,
*fmds,
*f1500wav,
*f1000wav,
*f500wav,
*f250wav,
*fin[NEST_IN],
*now_file ;
char *output_dir = "zout";
struct OutputFile {
char *suffix;
char *mode;
FILE **fpp;
int system; // A cassette SYSTEM file
int no_open;
int wanted; // output file has been explicitly selected
char *filename;
int temp;
}
outf[] = {
{ "lst", "w", &fout },
{ "hex", "w", &fbuf },
{ "bds", "w", &fbds },
{ "cmd", "wb", &fcmd },
{ "1500.wav", "wb", &f1500wav, 1 }, // must be 1 before 1500.cas
{ "1500.cas", "w+b", &fcas, 1 },
{ "1000.wav", "wb", &f1000wav, 1 }, // must be 1 before 1000.cas
{ "1000.cas", "w+b", &flnwcas, 1 },
{ "500.wav", "wb", &f500wav, 1 }, // must be 1 before 500.cas
{ "500.cas", "w+b", &flcas, 1 },
{ "250.wav", "wb", &f250wav }, // must be 1 before 250.cas
{ "250.cas", "w+b", &ftcas },
{ "cim", "wb", &fcim },
{ "ams", "wb", &fams },
{ "rel", "wb", &frel, 0, 1 },
{ "tap", "wb", &ftap },
{ "mds", "w", &fmds },
};
#define CNT_OUTF (sizeof outf / sizeof outf[0])
int getoptarg(int argc, char *argv[], int i);
void stop_all_outf();
void clean_outf();
void clean_outf_temp();
void suffix_list(char *sfx_lst, int no_open);
int pass2; /*set when pass one completed*/
int outpass; // set when we decide to stop doing passes */
int passfail; // set when an error means passes should not continue
int passretry; // set when an inconsistency will require another pass
int dollarsign ; /* location counter */
int olddollar ; /* kept to put out binary */
int oldothdollar; // output address of next .cmd/.cas/.lcas block
int emit_addr; // where code and data are being placed in memory
int tstates; // cumulative T-states
int ocf; // cumulative op code fetches
int llseq; // local label sequence number
int mras; // MRAS semi-compatibility mode
int trueval = 1; // Value returned for boolean true
int zcompat; // Original zmac compatibility mode
char modstr[8]; // Replacement string for '?' in labels when MRAS compatible
int relopt; // Only output .rel files and length of external symbols
char progname[8]; // Program name for .rel output
int note_depend; // Print names of files included
int firstcol;
int logcol;
int coloncnt;
int full_exprs; // expression parsing mode allowing almost any identifier
struct item *label, pristine_label; //
int list_dollarsign;// flag used to change list output for some operations
int list_addr; // address to display for operation if !list_dollarsign
// Include file search path
char *incpath[MAXINCPATH];
int incpath_cnt;
/* program counter save for PHASE/DEPHASE */
int phdollar, phbegin, phaseflag ;
char *src_name[NEST_IN] ;
int linein[NEST_IN] ;
int linepeek[NEST_IN];
int now_in ;
// These first 5 errors are singled out in lsterr1() for reasons I don't
// quite understand.
#define bflag 0 /* balance error */
#define eflag 1 /* expression error */
#define fflag 2 /* syntax error */
#define iflag 3 /* bad digits */
#define mflag 4 /* multiply defined */
#define pflag 5 /* phase error */
#define uflag 6 /* undeclared used */
#define vflag 7 /* value out of range */
#define oflag 8 /* phase/dephase error */
#define aflag 9 /* assert failed */
#define jflag 10 /* JP could be JR */
#define rflag 11 /* expression not relocatable */
#define gflag 12 /* incorrect register */
#define zflag 13 /* Z-80 instruction */
#define FIRSTWARN warn_hex
#define warn_hex 14
#define warn_notimpl 15
#define warn_general 16
#define FLAGS 17 /* number of errors and warnings */
char err[FLAGS];
int keeperr[FLAGS];
char errlet[FLAGS]="BEFIMPUVOAJRGZHNW";
char *errname[FLAGS]={
"Balance",
"Expression",
"Syntax",
"Digit",
"Mult. def.",
"Phase",
"Undeclared",
"Value",
"Phase/Dephase",
"Assertion failure",
"Use JR",
"Not relocatable",
"Register usage",
"Z-80 instruction in 8080 mode",
"$hex constant interpreted as symbol",
"Not implemented",
"General"
};
char errdetail[FLAGS][1024];
char detail[1024];
unsigned char inpbuf[LINEBUFFERSIZE];
unsigned char inpbuf_insert[LINEBUFFERSIZE];
unsigned char *inpptr;
char linebuf[LINEBUFFERSIZE];
char *lineptr;
char *linemax = linebuf+LINEBUFFERSIZE;
char outbin[BINPERLINE];
char *outbinp = outbin;
char *outbinm = outbin+BINPERLINE;
char outoth[256];
int outoth_cnt = 0;
unsigned char emitbuf[EMITBUFFERSIZE];
unsigned char *emitptr;
char ifstack[IFSTACKSIZE];
char *ifptr;
char *ifstmax = ifstack+IFSTACKSIZE-1;
char hexadec[] = "0123456789ABCDEF" ;
int nitems;
int linecnt;
int nbytes;
int invented;
int npass;
int njrpromo;
char tempbuf[TEMPBUFSIZE];
char *tempmax = tempbuf+TEMPBUFSIZE-1;
char arg_flag;
struct argparse arg_state;
void arg_start();
void arg_reset();
int str_getch(struct argparse *ap);
int mras_undecl_ok;
int mras_param[10];
char inmlex;
char mlex_list_on;
int parm_number;
int exp_number;
char symlong[] = "Symbol/number too long";
int raw;
int disp;
int param_parse;
#define PARAMTABSIZE (PARMMAX * 2)
struct item paramtab[PARAMTABSIZE];
#define FLOC PARMMAX
#define TEMPNUM PARMMAX+1
#define REPNUM PARMMAX+2
#define MSTART PARMMAX+3
#define MSTR PARMMAX+4
#define MARGP PARMMAX+5
#define MIF PARMMAX+6
#define PAREXT 7
union exprec {
char *param;
int value;
struct argparse *ap;
};
union exprec *est;
union exprec *est2;
union exprec *expstack[MAXEXP];
int expptr;
int floc;
int mfptr;
FILE *mfile;
char *writesyms;
char *title;
char titlespace[TITLELEN];
char *timp;
char *sourcef;
/* changed to cope with filenames longer than 14 chars -rjm 1998-12-15 */
char src[1024];
char bin[1024];
char listf[1024];
char oth[1024];
char copt = 1, /* cycle counts in listings by default */
edef = 1,
eopt = 1,
fdef = 0,
fopt = 0,
gdef = 1,
gopt = 1,
iopt = 0 , /* list include files */
jopt = 0,
JPopt = 0,
lstoff = 0,
lston = 0, /* flag to force listing on */
mdef = 0,
mopt = 0,
nopt = 1 , /* line numbers on as default */
popt = 1, /* form feed as default page eject */
sopt = 0, /* turn on symbol table listing */
topt = 1, /* terse, only error count to terminal */
printer_output = 0, // GWP - printer style output
z80,
saveopt;
char default_jopt, default_JPopt, default_z80 = 1;
char xeq_flag = 0;
int xeq;
time_t now;
int line;
int page = 1;
struct stab {
char t_name[MAXSYMBOLSIZE+1];
int t_value;
int t_token;
};
// GWP - support for cycle count tracking (and opens door for efficient .cmd, etc. output)
unsigned char memory[1 << 16];
char memflag[1 << 16];
enum {
MEM_DATA = 1,
MEM_INST = 2,
MEM_T_SET = 4
};
int tstatesum[1 << 16];
int ocfsum[1 << 16];
// GWP - expression handling extensions for .rel output.
void advance_segment(int step);
void expr_reloc_check(struct expr *ex);
void expr_number_check(struct expr *ex);
void expr_scope_same(struct expr *ex1, struct expr *ex2);
void expr_word_check(struct expr *ex);
int is_number(struct expr *ex);
int is_external(struct expr *ex);
struct expr *expr_num(int value);
struct expr *expr_alloc(void);
struct expr *expr_var(struct item *var);
struct expr *expr_mk(struct expr *left, int token, struct expr *right);
struct expr *expr_op(struct expr *left, int token, struct expr *right, int value);
struct expr *expr_op_sc(struct expr *left, int token, struct expr *right, int value);
void expr_free(struct expr *ex);
int can_extend_link(struct expr *ex);
void extend_link(struct expr *ex);
void putrelop(int op);
#define RELOP_BYTE (1)
#define RELOP_WORD (2)
#define RELOP_HIGH (3)
#define RELOP_LOW (4)
#define RELOP_NOT (5)
#define RELOP_NEG (6)
#define RELOP_SUB (7)
#define RELOP_ADD (8)
#define RELOP_MUL (9)
#define RELOP_DIV (10)
#define RELOP_MOD (11)
struct item *item_lookup(char *name, struct item *table, int table_size);
struct item *item_substr_lookup(char *name, int token, struct item *table, int table_size);
struct item *locate(char *name);
// Data descriptions for emit()
#define E_CODE (0)
#define E_DATA (1)
#define E_CODE8 (2)
#define E_CODE16 (3)
int segment;
#define SEGCHAR(s) " '\"!"[s]
#define SEG_ABS (0)
#define SEG_CODE (1)
#define SEG_DATA (2)
#define SEG_COMMON (3)
int seg_pos[4]; // may eventually support SEG_COMMON
int seg_size[4];
int rel_main;
int segchange;
struct item *cur_common;
void putout(int value);
int outrec;
int outlen;
unsigned char outbuf[1024 * 1024];
/*
* push back character
*/
#define NOPEEK (EOF - 1)
int peekc;
int nextline_peek;
/* function prototypes */
void error(char *as);
void usage(char *msg, char *param);
void help();
void list_out(int optarg, char *line_str, char type);
void erreport();
void errorprt(int errnum);
void errwarn(int errnum, char *message);
void mlex(char *look);
void popsi();
void suffix(char *str, char *suff);
char *basename(char *filename);
char *getsuffix(char *str);
void outpath(char *out, char *src, char *suff);
void casname(char *out, char *src, int maxlen);
void putm(int c);
void insymtab(char *name);
void outsymtab(char *name);
void compactsymtab();
void putsymtab();
void clear();
void setmem(int addr, int value, int type);
void setvars();
void flushbin();
void flushoth();
void lineout();
void puthex(int byte, FILE *buf);
void putcas(int byte);
void putrelbits(int count, int bits);
void putrel(int byte);
void putrelname(char *str);
void putrelextaddr(int extaddr);
void putrelcmd(int cmd);
void putrelsegref(int scope, int addr);
void flushrel(void);
void lsterr1();
void lsterr2(int lst);
void copyname(char *st1, char *st2);
void next_source(char *sp, int always);
void incbin(char *filename);
void dc(int count, int value);
char *getmraslocal();
void write_tap(int len, int org, unsigned char *data);
void write_250(int low, int high);
void writewavs(int pad250, int pad500, int pad1500);
void reset_import();
int imported(char *filename);
#define RELCMD_PUBLIC (0)
#define RELCMD_COMMON (1)
#define RELCMD_PROGNAME (2)
#define RELCMD_LIBLOOK (3)
#define RELCMD_EXTLINK (4)
#define RELCMD_COMSIZE (5)
#define RELCMD_EXTCHAIN (6)
#define RELCMD_PUBVALUE (7)
#define RELCMD_EXTMINUS (8)
#define RELCMD_EXTPLUS (9)
#define RELCMD_DATASIZE (10)
#define RELCMD_SETLOC (11)
#define RELCMD_CODESIZE (13)
#define RELCMD_ENDMOD (14)
#define RELCMD_ENDPROG (15)
/*
* add a character to the output line buffer
*/
void addtoline(int ac)
{
/* check for EOF from stdio */
if (ac == -1)
ac = 0 ;
if (lineptr >= linemax)
error("line buffer overflow");
*lineptr++ = ac;
}
int get_tstates(unsigned char *inst, int *low, int *high, int *fetch)
{
int len;
if (z80)
len = zi_tstates(inst, low, high, fetch, 0, 0);
else
len = zi_tstates(inst, 0, 0, fetch, low, high);
return len;
}
/*
* put values in buffer for outputing
*/
void emit(int bytes, int desc, struct expr *data, ...)
{
int type, i, args, dsize;
va_list ap;
if (relopt && segchange) {
segchange = 0;
putrelcmd(RELCMD_SETLOC);
putrelsegref(segment, seg_pos[segment]);
}
// External references only supported in .rel output.
if (!relopt && data && (data->e_scope & SCOPE_EXTERNAL)) {
sprintf(detail, "External references only allowed in .rel output\n");
errwarn(uflag, detail);
}
va_start(ap, data);
type = desc == E_DATA ? MEM_DATA : MEM_INST;
// Check emit is not adding instructions to the buffer.
if (desc != E_DATA && emitptr != emitbuf)
fprintf(stderr, "internal inconsistency in t-state counting\n");
dsize = 0;
args = bytes;
if (desc == E_DATA) {
args = 0;
dsize = bytes;
}
else if (desc == E_CODE16)
dsize = 2;
else if (desc == E_CODE8)
dsize = 1;
for (i = 0; i < args; i++)
{
if (emitptr >= &emitbuf[EMITBUFFERSIZE])
error("emit buffer overflow");
else {
int addr = (emit_addr + (emitptr - emitbuf)) & 0xffff;
*emitptr = va_arg(ap, int);
if (segment == SEG_CODE)
setmem(addr, *emitptr, type);
putrel(*emitptr);
putout(*emitptr);
emitptr++;
}
}
va_end(ap);
for (i = 0; i < dsize; i++) {
int addr = (emit_addr + (emitptr - emitbuf)) & 0xffff;
*emitptr = data->e_value >> (i * 8);
if (segment == SEG_CODE)
setmem(addr, *emitptr, type);
putout(*emitptr);
emitptr++;
}
if (desc != E_DATA)
{
int eaddr = emit_addr, low, fetch, addr_after;
// emitbuf is OK since this only happens with single emits
if (!z80) {
// Check for invalid 8080 instructions.
int op = emitbuf[0] & 0xff;
if (op == 0x08 || op == 0x10 || op == 0x18 ||
op == 0x20 || op == 0x28 || op == 0x30 ||
op == 0x38 || op == 0xCB || op == 0xD9 ||
op == 0xDD || op == 0xED || op == 0xFD)
{
err[zflag]++;
}
}
get_tstates(emitbuf, &low, 0, &fetch);
// Sanity check
if (low <= 0)
{
fprintf(stderr, "undefined instruction on %02x %02x (assembler or diassembler broken)\n",
emitbuf[0], emitbuf[1]);
}
// Special case to catch promotion of djnz to DEC B JP NZ
// Even update the tstatesum[] counter though that seems to
// me to be above and beyond.
if (emitbuf[0] == 5 && args == 2) {
tstatesum[eaddr] = tstates;
ocfsum[eaddr] = ocf;
memflag[eaddr] |= MEM_T_SET;
eaddr++;
tstates += low;
ocf += fetch;
low = 10;
// still 1 fetch
}
// Double setting of both sides of tstatesum[] seems like too
// much, but must be done in the isolated instruction case:
// org x ; inc a ; org y
tstatesum[eaddr] = tstates;
ocfsum[eaddr] = ocf;
memflag[eaddr] |= MEM_T_SET;
// Well, OK, should we default to high or low???
// Guess it should be whatever makes sense for you
// to get here which, generally, is the low.
// low it is.
tstates += low;
ocf += fetch;
addr_after = (emit_addr + (emitptr - emitbuf)) & 0xffff;
tstatesum[addr_after] = tstates;
ocfsum[addr_after] = ocf;
memflag[addr_after] |= MEM_T_SET;
}
if (relopt && outpass && dsize > 0) {
if (dsize == 1) {
if (is_number(data))
putrel(data->e_value);
else if (can_extend_link(data)) {
extend_link(data);
putrelop(RELOP_BYTE);
putrel(0);
}
else {
err[rflag]++;
putrel(0);
}
}
else if (dsize == 2) {
int handled = 0;
if (data->e_scope & SCOPE_EXTERNAL) {
struct item *var = 0;
int offset = 0;
// Simple external reference.
if (is_external(data))
var = data->e_item;
else if (is_external(data->e_left) &&
data->e_token == '+' &&
is_number(data->e_right))
{
var = data->e_left->e_item;
offset = data->e_right->e_value;
}
else if (is_number(data->e_left) &&
data->e_token == '+' &&
is_external(data->e_right))
{
offset = data->e_left->e_value;
var = data->e_right->e_item;
}
else if (is_external(data->e_left) &&
data->e_token == '-' &&
is_number(data->e_right))
{
var = data->e_left->e_item;
offset = data->e_right->e_value;
}
if (var && offset) {
putrelcmd(data->e_token == '-' ?
RELCMD_EXTMINUS : RELCMD_EXTPLUS);
// Theoretically we could put a
// program or data relative value here...
putrelsegref(SEG_ABS, offset);
}
if (var) {
if (var->i_chain == 0) {
putrel(0);
putrel(0);
}
else {
putrelbits(1, 1);
putrelextaddr(var->i_chain);
}
var->i_chain = (segment << 16) |
((dollarsign + args) & 0xffff);
handled = 1;
}
}
else if ((data->e_scope & ~SCOPE_PUBLIC) == 0) {
// nice constant value
putrel(data->e_value);
putrel(data->e_value >> 8);
handled = 1;
}
else if (!(data->e_scope & SCOPE_NORELOC)) {
// relocatable value
putrelbits(1, 1);
putrelbits(2, data->e_scope);
putrelbits(8, data->e_value);
putrelbits(8, data->e_value >> 8);
handled = 1;
}
if (!handled) {
if (can_extend_link(data)) {
extend_link(data);
putrelop(RELOP_WORD);
putrel(0);
putrel(0);
}
else {
err[rflag]++;
putrel(data->e_value);
putrel(data->e_value >> 8);
}
}
}
else if (dsize == 4) {
// Only numbers are allowed.
if (data->e_scope != 0) {
err[vflag]++;
if (data->e_scope & SCOPE_NORELOC)
err[rflag]++;
}
for (i = 0; i < dsize; i++)
putrel(data->e_value >> (i * 8));
}
else
error("internal dsize error");
}
}
#define ET_NOARG_DISP (0)