2 // coded by Ketmar // Vamprile Avalon
15 #include "urasmlib/urasmlib.h"
24 #define MAYBE_UNUSED __attribute__((unused))
26 ///////////////////////////////////////////////////////////////////////////////
29 static char *sysIncludeDir
= NULL
;
31 #define MAX_LINE_SIZE 16384
32 static char curLine
[MAX_LINE_SIZE
]; /* current processing line; can be freely modified */
35 ///////////////////////////////////////////////////////////////////////////////
38 static void initInclideDir (void) {
39 const char *id
= getenv("URASM_INCLUDE_DIR");
41 sysIncludeDir
= strdup(id
);
43 char buf
[128], myDir
[4096];
45 sprintf(buf
, "/proc/%u/exe", (unsigned int)pid
);
46 memset(myDir
, 0, sizeof(myDir
));
47 if (readlink(buf
, myDir
, sizeof(myDir
)-1) < 0) strcpy(myDir
, ".");
49 char *p
= (char *)strrchr(myDir
, '/');
50 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
52 strcat(myDir
, "/libs");
53 sysIncludeDir
= strdup(myDir
);
55 while (sysIncludeDir
[0] && sysIncludeDir
[strlen(sysIncludeDir
)-1] == '/') sysIncludeDir
[strlen(sysIncludeDir
)-1] = '\0';
56 if (!sysIncludeDir
[0]) strcpy(sysIncludeDir
, ".");
60 ///////////////////////////////////////////////////////////////////////////////
63 /*TODO: expression parser should understant escapes in strings!*/
64 /* trim trailing spaces and comments */
65 static void normalizeStr (char *s
) {
69 for (p
= s
; *p
; ++p
) {
74 case 'x': case 'X': // hex code
76 for (f
= 0; f
< 2; ++f
) if (!isxdigit(*p
)) break;
77 --p
; // last digit will be skiped by 'for'
79 case '0': // octal code
80 for (f
= 0; f
< 4; ++f
) if (!isdigit(*p
) || *p
> '7') break;
81 --p
; // last digit will be skiped by 'for'
83 case '1' ... '9': // decimal code
84 for (f
= 0; f
< 3; ++f
) if (!isdigit(*p
)) break;
85 --p
; // last digit will be skiped by 'for'
87 default: ; // other escapes, do nothing
90 if (*p
== inQ
) inQ
= 0;
96 case '"': case '\'': // string catched
99 case ';': // end of line, comment follows
100 *p
-- = '\0'; // strip it and quit
102 default: ; // do nothing
106 for (p
= s
+strlen(s
)-1; p
>= s
&& isspace(*p
); --p
) ;
111 /* returns NULL or pointer to args */
112 /* skips spaces after command if any */
113 static char *strIsCommand (const char *command
, char *str
) {
116 for (cnt
= 1; cnt
> 0; --cnt
) {
117 while (*str
&& isspace(*str
)) ++str
; // skip spaces
118 for (; *command
&& *str
; ++command
, ++str
) {
119 if (toupper(*command
) != toupper(*str
)) return NULL
; // alas
121 if (*command
) return NULL
; // alas
122 if (*str
&& isalnum(*str
)) return NULL
; // alas
123 while (*str
&& isspace(*str
)) ++str
; // skip spaces
124 if (*str
&& *str
== ':') break; // try again if we have a colon
131 /* don't free() result */
132 /* skips trailing spaces */
133 static char *parseStr (char **str
, char endQ
, int *lenp
) {
134 static char buf
[MAX_LINE_SIZE
];
135 int len
= 0, n
, f
, base
;
138 int xDigit (char ch
, int base
) {
139 if (ch
< '0') return -1;
141 if (ch
>= '0'+base
) return -1;
145 if (ch
<= '9') return ch
-'0';
146 if (ch
< 'A' || ch
> 'A'+base
-10) return -1;
148 return ch
<base
? ch
: -1;
156 case 'a': buf
[len
++] = '\a'; break;
157 case 'b': buf
[len
++] = '\b'; break;
158 case 'f': buf
[len
++] = '\f'; break;
159 case 'n': buf
[len
++] = '\n'; break;
160 case 'r': buf
[len
++] = '\r'; break;
161 case 't': buf
[len
++] = '\t'; break;
162 case 'v': buf
[len
++] = '\v'; break;
163 case 'z': buf
[len
++] = '\0'; break;
164 case 'x': case 'X': // hex
167 donum
: for (n
= 0; f
> 0; --f
) {
168 char ch
= xDigit(*a
++, base
);
174 --a
; // return to the last digit, 'for' will skip it
179 case '1' ... '9': // decimal
182 default: buf
[len
++] = a
[0]; break; // others
185 if (*a
== endQ
) { ++a
; break; }
189 while (*a
&& isspace(*a
)) ++a
; // skip trailing spaces
192 if (lenp
) *lenp
= len
;
197 ///////////////////////////////////////////////////////////////////////////////
198 // source file stack, reader, etc
200 typedef struct SourceLine SourceLine
;
208 static SourceLine
*asmText
= NULL
;
209 static SourceLine
*asmTextLast
= NULL
;
210 static SourceLine
*curSrcLine
= NULL
;
213 static void asmTextClear (void) {
215 SourceLine
*l
= asmText
;
216 asmText
= asmText
->next
;
221 asmTextLast
= curSrcLine
= NULL
;
225 static int asmTextLoad (const char *fname
) {
230 static int includeCount
= 0;
232 if (!(fl
= fopen(fname
, "r"))) {
233 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
236 printf("loading: %s\n", fname
);
238 while (fgets(curLine
, sizeof(curLine
)-1, fl
)) {
240 curLine
[sizeof(curLine
)-1] = '\0';
241 normalizeStr(curLine
);
242 if (!curLine
[0]) continue; // don't store empty lines
243 // find specials, if any
244 if (isspace(curLine
[0])) {
245 if ((args
= strIsCommand("INCLUDE", curLine
)) != NULL
) {
252 fprintf(stderr
, "ERROR: file %s, line %d: invalid INCLUDE!\n", fname
, lineNo
);
255 if (args
[0] == '"' || args
[0] == '\'') qCh
= *args
++;
256 else if (args
[0] == '<') { qCh
= '>'; ++args
; system
= 1; }
257 char *fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
260 fprintf(stderr
, "ERROR: file %s, line %d: can't INCLUDE nothing!\n", fname
, lineNo
);
265 fprintf(stderr
, "ERROR: file %s, line %d: extra args after INCLUDE filename!\n", fname
, lineNo
);
268 if (includeCount
> 256) {
270 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", fname
, lineNo
);
273 if (system
) sprintf(curLine
, "%s/%s", sysIncludeDir
, fn
); else sprintf(curLine
, "%s", fn
);
274 fn
= strdup(curLine
); if (!fn
) abort();
276 system
= asmTextLoad(fn
);
279 if (system
) { fclose(fl
); return system
; }
284 s
= calloc(1, sizeof(SourceLine
));
287 s
->line
= strdup(curLine
); if (!s
->line
) abort();
288 s
->fname
= strdup(fname
); if (!s
->fname
) abort();
289 if (asmTextLast
) asmTextLast
->next
= s
; else asmText
= s
;
297 static inline void loadCurSrcLine (void) { if (curSrcLine
) strcpy(curLine
, curSrcLine
?curSrcLine
->line
:""); }
298 static inline SourceLine
*setCurSrcLine (SourceLine
*l
) { curSrcLine
= l
; loadCurSrcLine(); return l
; }
299 static inline SourceLine
*nextSrcLine (void) { return curSrcLine
? setCurSrcLine(curSrcLine
->next
) : NULL
; }
302 ///////////////////////////////////////////////////////////////////////////////
305 static void processCurrentLine (void); // only one, will skip to next one
308 ///////////////////////////////////////////////////////////////////////////////
309 // error raisers, etc
311 static jmp_buf errJP
;
314 static void errorWriteFile (void) {
316 fprintf(stderr
, "at file %s, line %d: ", curSrcLine
->fname
, curSrcLine
->lineNo
);
318 fprintf(stderr
, "somewhere in time: ");
322 static void errorMsgV (const char *fmt
, va_list ap
) {
323 vfprintf(stderr
, fmt
, ap
);
330 static void __attribute__((format(printf
, 1, 2))) warningMsg (const char *fmt
, ...) {
332 fprintf(stderr
, "WARNING ");
339 static void __attribute__((format(printf
, 1, 2))) errorMsg (const char *fmt
, ...) {
341 fprintf(stderr
, "FATAL ");
348 static void __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2))) fatal (const char *fmt
, ...) {
356 static void __attribute__((noreturn
)) fatalUrLib (int errcode
) {
357 errorMsg("%s", urErrorMessage(errcode
));
362 //////////////////////////////////////////////////////////////////////////////
363 // operator management
365 typedef void (*UrAsmOpFn
) (void);
367 typedef struct UrAsmOp UrAsmOp
;
374 static UrAsmOp
*oplist
= NULL
;
377 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
378 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
380 res
->name
= strdup(name
);
388 static UrAsmOp
*urFindOp (const char *name
) {
390 for (res
= oplist
; res
; res
= res
->next
) if (!strcasecmp(name
, res
->name
)) break;
395 static void urClearOps (void) {
398 c
= oplist
; oplist
= oplist
->next
;
405 ///////////////////////////////////////////////////////////////////////////////
408 typedef struct UrLabelInfo UrLabelInfo
;
412 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
413 int known
; /* !0: label value already known */
414 int refLine
; /* first referenced line */
419 static UrLabelInfo
*labels
= NULL
;
421 static void urClearLabels (void) {
423 while ((c
= labels
)) {
424 labels
= labels
->next
;
425 if (c
->name
) free(c
->name
);
426 if (c
->refFile
) free(c
->refFile
);
432 static UrLabelInfo
*urFindLabel (const char *name
) {
434 for (c
= labels
; c
; c
= c
->next
) if (!strcmp(name
, c
->name
)) break;
439 static UrLabelInfo
*urAddLabel (const char *name
) {
440 UrLabelInfo
*c
= urFindLabel(name
);
443 for (p
= NULL
, c
= labels
; c
; p
= c
, c
= c
->next
) ;
444 c
= calloc(1, sizeof(UrLabelInfo
));
446 c
->name
= strdup(name
);
448 if (p
) p
->next
= c
; else labels
= c
;
455 ///////////////////////////////////////////////////////////////////////////////
456 // module list management
458 typedef struct ModuleInfo ModuleInfo
;
461 char *fname
; // opened in this file
462 int seen
; // !0: module already seen, skip other definitions from the same file
465 static ModuleInfo
*modules
= NULL
;
466 static ModuleInfo
*curModule
= NULL
;
469 static void modulesClear (void) {
472 ModuleInfo
*c
= modules
;
473 modules
= modules
->next
;
481 static void modulesResetSeen (void) {
483 for (c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
487 static ModuleInfo
*moduleFind (const char *name
) {
489 if (!name
|| !name
[0]) return NULL
;
490 for (c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) break;
495 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
497 if (!name
|| !fname
|| !name
[0] || !fname
[0]) abort();
498 c
= calloc(1, sizeof(ModuleInfo
));
500 c
->name
= strdup(name
); if (!c
->name
) abort();
501 c
->fname
= strdup(fname
); if (!c
->fname
) abort();
508 ///////////////////////////////////////////////////////////////////////////////
509 // destination memory management
511 static uint8_t memory
[65536];
512 static char memused
[65536];
513 static uint16_t pc
= 0; /* current position to write */
514 static uint16_t disp
= 0; /* current 'virtual PC' */
515 static uint16_t ent
= 0; /* eng */
516 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
517 static int inTapeBlock
= 0;
518 static uint8_t tapeXorB
= 0;
521 static uint8_t getByte (uint16_t addr
) {
526 static __attribute__((unused
)) uint16_t getWord (uint16_t addr
) {
527 return ((uint16_t)memory
[addr
])|(((uint16_t)memory
[addr
+1])<<8);
531 static void putByte (uint16_t addr
, uint8_t b
) {
532 if (inTapeBlock
) tapeXorB
^= b
;
538 static MAYBE_UNUSED
void putWord (uint16_t addr
, uint16_t w
) {
539 putByte(addr
, w
&0xFFU
);
540 putByte(addr
+1, (w
>>8)&0xFFU
);
544 static void emitByte (uint8_t b
) {
550 static void emitWord (uint16_t w
) {
552 emitByte((w
>>8)&0xFFU
);
556 static void emitRWord (uint16_t w
) {
557 emitByte((w
>>8)&0xFFU
);
562 static void prepareMemory (void) {
563 memset(memory
, 0, sizeof(memory
));
564 memset(memused
, 0, sizeof(memused
));
568 ///////////////////////////////////////////////////////////////////////////////
569 // label getter and utilities
571 static char *lastSeenGlobalLabel
= NULL
; /* global */
574 static char *fixLocalLabel (const char *name
) {
575 static char newname
[MAX_LINE_SIZE
*2+1024];
577 if (!name
|| !name
[0]) newname
[0] = '\0';
578 else if (!lastSeenGlobalLabel
|| name
[0] != '.') strcpy(newname
, name
);
580 // this is local label, let's rename it
581 sprintf(newname
, "{%s%s}", lastSeenGlobalLabel
, name
);
587 static char *fixGlobalLabel (const char *name
) {
588 static char newname
[MAX_LINE_SIZE
*2+1024];
590 if (!name
|| !name
[0]) newname
[0] = '\0';
591 else if (!curModule
|| name
[0] == '.' || name
[0] == '{' || strchr(name
, '.')) strcpy(newname
, name
);
593 // this is global unqualified label and we have a module; let's rename it
594 sprintf(newname
, "%s.%s", curModule
->name
, name
);
600 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
) {
604 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
605 lbl
= urFindLabel(nn
);
608 errorMsg("using undefined label %s", ln
);
613 lbl
= urAddLabel(nn
);
616 lbl
->refLine
= curSrcLine
->lineNo
;
617 lbl
->refFile
= strdup(curSrcLine
->fname
);
618 //printf("new label: [%s]\n", lbl->name);
620 //printf("label reference: [%s]\n", lbl->name);
624 *defined
= lbl
->known
!=0;
633 static int checkLabels (void) {
637 for (c
= labels
; c
; c
= c
->next
) {
639 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
642 if (c
->type
== 0) c
->known
= -1;
644 //if (wasError) longjmp(errJP, 667);
649 ///////////////////////////////////////////////////////////////////////////////
652 static int32_t getExprArg (int *defined
) {
657 while (*a
&& isspace(*a
)) ++a
;
658 if (!a
[0]) fatal("expression expected");
659 const char *ee
= urExpression(&res
, a
, disp
, defined
, &error
);
660 if (error
) fatalUrLib(error
);
662 if (ee
[0] != ',') fatal("bad expression");
663 memmove(curLine
, ee
, strlen(ee
)+1);
671 static int32_t getOneExprArg (int *defined
) {
672 int32_t res
= getExprArg(defined
);
673 if (curLine
[0]) fatal("too many expressions");
678 static int isStrArg (void) { return curLine
[0]=='"' || curLine
[0] == '\''; }
681 static char *getStrArg (int *lenp
) {
685 while (*a
&& isspace(*a
)) ++a
;
687 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
688 res
= parseStr(&a
, qCh
, lenp
);
690 if (a
[0] != ',') fatal("bad string expression");
691 memmove(curLine
, a
, strlen(a
)+1);
699 static MAYBE_UNUSED
char *getOneStrArg (int *lenp
) {
700 char *res
= getStrArg(lenp
);
701 if (curLine
[0]) fatal("too many expressions");
706 static char *getLabelArg (void) {
707 static char res
[MAX_LINE_SIZE
+128], *p
= res
;
710 while (*a
&& isspace(*a
)) ++a
;
711 if (!a
[0]) fatal("label expected");
712 for (; *a
&& a
[0] != ','; ++a
) *p
++ = *a
;
713 for (; p
> res
&& isspace(p
[-1]); --p
) ;
715 if (p
-res
> 120) fatal("label name too long: %s", res
);
716 while (*a
&& isspace(*a
)) ++a
;
718 if (a
[0] != ',') fatal("bad string expression");
719 memmove(curLine
, a
, strlen(a
)+1);
727 static char *getOneLabelArg (void) {
728 char *res
= getLabelArg();
729 if (curLine
[0]) fatal("too many expressions");
734 ///////////////////////////////////////////////////////////////////////////////
737 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
739 while (*ep
&& (isspace(*ep
) || *ep
== ':')) ++ep
;
740 memmove(curLine
, ep
, strlen(ep
)+1);
744 /* remove label from curLine */
745 static void removeLabel (void) {
748 if (ep
[0] && !isspace(ep
[0])) for (ep
= curLine
; *ep
&& !isspace(*ep
) && *ep
!= ':'; ++ep
) ; // skip text
749 // skip spaces and colons
750 while (*ep
&& (isspace(*ep
) || *ep
== ':')) ++ep
;
751 memmove(curLine
, ep
, strlen(ep
)+1);
755 static void processLabel (void) {
760 int noLocAff
= 0, doEQU
= 0;
762 if (!curLine
[0] || isspace(curLine
[0]) || curLine
[0] == ':') { removeLabel(); return; } // removeLabel() removes any spaces, etc
764 for (ep
= curLine
; *ep
&& !isspace(*ep
) && *ep
!= ':'; ++ep
) ;
765 if (ep
-curLine
> 120) fatal("label too long");
767 memset(n2
, 0, sizeof(n2
));
768 memmove(n2
, curLine
, ep
-curLine
);
769 if (!urIsValidLabelName(n2
)) return; // this is not a valid label, get out of here
770 // check if this can be instruction
771 while (*ep
&& isspace(*ep
)) ++ep
;
772 if (*ep
!= ':' && urFindOp(n2
)) return; // this must be and instruction, process it
773 // ok, we got a good label
775 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
776 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
777 lbl
= urAddLabel(nn
);
779 lbl
->refLine
= curSrcLine
->lineNo
;
780 lbl
->refFile
= strdup(curSrcLine
->fname
);
782 //printf("%d: new: [%s]\n", localLbl, lbl->name);
784 if (curLine
[0] == '=') {
786 argstart
= curLine
+1;
789 argstart
= strIsCommand("EQU", curLine
);
791 if (!argstart
|| doEQU
) {
792 if (pass
== 0 && lbl
->type
!= -1) fatal("duplicate label %s", lbl
->name
);
796 memmove(curLine
, argstart
, strlen(argstart
)+1);
798 if (lbl
->type
!= -1 && lbl
->type
!= 0) fatal("duplicate label %s", lbl
->name
);
801 int32_t res
= getOneExprArg(&defined
);
807 if (pass
!= 0) fatal("can't calculate label %s", lbl
->name
);
813 if (lbl
->name
[0] != '{' && !noLocAff
) {
814 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
815 lastSeenGlobalLabel
= strdup(lbl
->name
);
823 ///////////////////////////////////////////////////////////////////////////////
824 // instruction finder (in source)
826 /* array ends with NULL */
827 /* returns line or NULL */
828 /* iidx will be set to found instruction number */
829 static SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
831 SourceLine
*cur
= curSrcLine
;
833 if (iidx
) *iidx
= -1;
834 for (; cur
; cur
= cur
->next
) {
836 if (!isspace(cur
->line
[0])) continue; // fuck labeled strings
839 const char *name
= va_arg(ap
, const char *);
841 if (strIsCommand(name
, cur
->line
)) {
853 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
854 return findNextInstructionFromList(NULL
, name
, NULL
);
858 ///////////////////////////////////////////////////////////////////////////////
861 static int optRunSNA
= 0;
862 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
865 /* return 'found' flag */
866 static int findChunkFrom (int addr
, int *start
, int *len
) {
867 if (addr
< 0) addr
= 0;
868 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) ;
869 if (addr
> 65535) return 0;
871 for (; addr
<= 65535 && memused
[addr
]; ++addr
) ;
872 *len
= addr
-(*start
);
877 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
878 for (; buflen
>= 2; buflen
-= 2) {
880 uint8_t byte
= *buf
++;
881 //printf("%d %u %u\n", cnt+1, byte, addr);
882 for (; cnt
>= 0; --cnt
, ++addr
) if (!memused
[addr
]) putByte(addr
, byte
);
887 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
888 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
889 if (bxor
) *bxor
= (*bxor
)^b
;
894 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
895 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
896 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
901 ///////////////////////////////////////////////////////////////////////////////
904 static int saveSna (const char *fname
) {
905 char *fn
= malloc(strlen(fname
)+16);
909 sprintf(fn
, "%s.sna", fname
);
910 fo
= fopen(fn
, "wb");
912 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
913 memmove(regs
, ursna48
, 27);
915 /* push new address */
916 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
918 putByte(sp
, ent
&0xFFU
);
919 putByte(sp
+1, (ent
>>8)&0xFFU
);
921 regs
[24] = (sp
>>8)&0xFFU
;
923 fwrite(regs
, 27, 1, fo
);
924 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
925 fwrite(memory
+16384, 49152, 1, fo
);
931 ///////////////////////////////////////////////////////////////////////////////
934 static void saveRaw (const char *basename
) {
935 char *fname
= malloc(strlen(basename
)+16);
939 while (findChunkFrom(start
, &start
, &len
)) {
940 sprintf(fname
, "%s_%04X.%s", basename
, start
, optTapExt
?"tap":"bin");
941 fo
= fopen(fname
, "wb");
943 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
945 printf("out: %s\n", fname
);
946 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
947 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
960 ///////////////////////////////////////////////////////////////////////////////
963 static void saveTap (const char *basename
) {
964 char *fname
= malloc(strlen(basename
)+16);
966 int start
= 0, len
, f
;
970 sprintf(fname
, "%s.tap", basename
);
971 fo
= fopen(fname
, "wb");
973 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
974 while (findChunkFrom(start
, &start
, &len
)) {
976 sprintf(blkname
, "c%04X:%04X", start
, len
);
977 printf(" block: %s\n", blkname
);
978 fWriteWord(fo
, 19, NULL
); // length of header
980 fWriteByte(fo
, 0, &bxor
); // header block
981 fWriteByte(fo
, 3, &bxor
); // 'code' flag
982 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
983 fWriteWord(fo
, len
, &bxor
);
984 fWriteWord(fo
, start
, &bxor
);
985 fWriteWord(fo
, 32768, &bxor
);
986 fWriteByte(fo
, bxor
, NULL
);
988 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
990 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
991 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
992 fWriteByte(fo
, bxor
, NULL
);
998 ///////////////////////////////////////////////////////////////////////////////
999 // pseudoinstructions
1001 // note that processCurrentLine() will NOT skip to the next line before
1002 // calling pseudoinstruction handler!
1003 // note that processCurrentLine() will skip current line after calling
1004 // pseudoinstruction handler!
1006 static int wasOrg
= 0;
1009 ///////////////////////////////////////////////////////////////////////////////
1012 static void piDISPLAYX (int passNo
) {
1016 char *res
= getStrArg(&len
);
1017 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
1020 int32_t v
= getExprArg(&defined
);
1021 if (passNo
< 0 || pass
== passNo
) printf("%d", v
);
1023 if (!curLine
[0]) break;
1024 if (curLine
[0] != ',') fatal("invalid expression");
1025 memmove(curLine
, curLine
+1, strlen(curLine
));
1030 static void piDISPLAY (void) { piDISPLAYX(1); }
1031 static void piDISPLAY0 (void) { piDISPLAYX(0); }
1032 static void piDISPLAYA (void) { piDISPLAYX(-1); }
1035 ///////////////////////////////////////////////////////////////////////////////
1038 static void piORG (void) {
1040 int32_t res
= getOneExprArg(&defined
);
1041 if (!defined
) fatal("sorry, ORG operand value must be known here");
1042 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
1043 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
1052 static void piDISP (void) {
1054 int32_t res
= getOneExprArg(&defined
);
1055 if (!defined
) fatal("sorry, DISP operand value must be known here");
1056 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
1057 //printf("DISP=%d\n", res);
1062 static void piENDDISP (void) {
1063 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
1068 static void piENT (void) {
1070 int32_t res
= getOneExprArg(&defined
);
1071 if (!defined
) fatal("sorry, ENT operand value must be known here");
1072 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
1077 static void piALIGN (void) {
1079 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
1080 int32_t res
= getOneExprArg(&defined
);
1081 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
1082 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
1083 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
1084 if (res
> 0 && pc
%res
!= 0) {
1093 static void piDISPALIGN (void) {
1095 int32_t res
= getOneExprArg(&defined
);
1096 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
1097 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
1098 if (res
> 0 && disp
%res
!= 0) {
1106 ///////////////////////////////////////////////////////////////////////////////
1109 static void piDEFBW (int isWord
) {
1113 int32_t res
= getExprArg(&defined
);
1114 if (pass
> 0 && !defined
) fatal("undefined operand");
1116 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
1117 if (res
< 0) res
+= 65536;
1118 if (isWord
== 1) emitWord(res
); else emitRWord(res
);
1120 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
1121 if (res
< 0) res
+= 256;
1124 if (!curLine
[0]) break;
1125 if (curLine
[0] != ',') fatal("invalid operand: ',' missing");
1126 memmove(curLine
, curLine
+1, strlen(curLine
));
1130 static void piDEFB (void) { piDEFBW(0); }
1131 static void piDEFW (void) { piDEFBW(1); }
1132 static void piDEFR (void) { piDEFBW(2); }
1135 static void piDEFS (void) {
1139 int32_t res
= getExprArg(&defined
);
1140 if (pass
> 0 && !defined
) fatal("undefined operand");
1141 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
1142 if (*curLine
&& curLine
[0] == ',') {
1143 memmove(curLine
, curLine
+1, strlen(curLine
));
1144 bt
= getExprArg(&defined
);
1145 if (pass
> 0 && !defined
) fatal("undefined operand");
1146 if (bt
< -127 || bt
> 255) fatal("invalid operand value: %d", res
);
1147 if (bt
< 0) bt
+= 256;
1148 for (f
= 0; f
< res
; ++f
) emitByte(bt
);
1150 pc
+= res
; disp
+= res
;
1152 if (!curLine
[0]) break;
1153 if (curLine
[0] != ',') fatal("invalid operand: ',' missing");
1154 memmove(curLine
, curLine
+1, strlen(curLine
));
1159 /* bit 0: put '\0' */
1160 /* bit 1: set bit 7 of last byte */
1161 /* bit 2: put length */
1162 static void piDEFSTR (int type
) {
1166 char *res
= getStrArg(&len
);
1168 if (len
> 255) fatal("string too long");
1171 for (f
= 0; f
< len
; ++f
) {
1172 uint8_t b
= (uint8_t)res
[f
];
1173 if ((type
&0x01) && f
== len
-1) b
|= 0x80;
1176 if (type
&0x01) emitByte(0);
1179 int32_t v
= getExprArg(&defined
);
1180 if (pass
> 0 && !defined
) fatal("undefined expression");
1181 if (!defined
) v
= 0;
1184 if (!curLine
[0]) break;
1185 if (curLine
[0] != ',') fatal("invalid expression");
1186 memmove(curLine
, curLine
+1, strlen(curLine
));
1191 static void piDEFM (void) { piDEFSTR(0x00); }
1192 static void piDEFZ (void) { piDEFSTR(0x01); }
1193 static void piDEFX (void) { piDEFSTR(0x02); }
1194 static void piDEFC (void) { piDEFSTR(0x04); }
1197 ///////////////////////////////////////////////////////////////////////////////
1200 /* INCBIN "name"[,maxlen] */
1201 static void piINCBIN (void) {
1207 char *args
= curLine
;
1209 if (!curLine
[0]) fatal("INCBIN without file name");
1213 } else if (curLine
[0] == '<') {
1220 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
1221 if (!fn
[0]) fatal("INCBIN: empty file name");
1222 memmove(curLine
, args
, strlen(args
)+1);
1224 if (curLine
[0] == ',') {
1225 memmove(curLine
, curLine
+1, strlen(curLine
));
1227 maxlen
= getOneExprArg(&defined
);
1228 if (!defined
) fatal("INCBIN: undefined maxlen");
1229 if (maxlen
< 1) return; // nothing to do
1233 sprintf(curLine
, "%s/%s", sysIncludeDir
, fn
);
1235 sprintf(curLine
, "%s", fn
);
1238 fl
= fopen(curLine
, "rb");
1239 if (!fl
) fatal("INCBIN: file not found: %s", curLine
);
1240 while (maxlen
-- > 0) {
1241 int res
= fread(&bt
, 1, 1, fl
);
1243 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: %s", curLine
); }
1250 ///////////////////////////////////////////////////////////////////////////////
1251 // MODULE, ENDMODULE
1253 static void piENDMODULE (void) {
1254 if (!curModule
) fatal("ENDMODULE without MODULE");
1256 char *mn
= getOneLabelArg();
1257 if (strcmp(mn
, curModule
->name
)) fatal("invalid module name in ENDMODULE");
1263 static void piMODULE (void) {
1266 SourceLine
*ol
= curSrcLine
;
1269 if (curModule
) fatal("no nested modules allowed");
1270 mn
= getOneLabelArg();
1271 if (!urIsValidLabelName(mn
)) fatal("invalid module name: %s", mn
);
1272 mi
= moduleFind(mn
);
1275 if (strcmp(mi
->fname
, curSrcLine
->fname
)) fatal("duplicate module definition; previous was in %s", mi
->fname
);
1277 nextSrcLine(); // skip ourself
1278 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
1280 fatal("no ENDMODULE");
1282 if (inum
== 0) fatal("no nested modules allowed");
1288 mi
= moduleAdd(mn
, curSrcLine
->fname
);
1295 ///////////////////////////////////////////////////////////////////////////////
1298 static void piDUP (void) {
1299 int defined
= 1, dupCnt
= 1, inum
;
1300 SourceLine
*stline
, *eline
= NULL
;
1301 int32_t cnt
= getOneExprArg(&defined
);
1302 if (!defined
) fatal("DUP: counter must be defined");
1303 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
1304 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
1305 // now find corresponding EDUP
1306 // note that we should skip nested DUPs
1307 nextSrcLine(); // skip ourself
1308 stline
= curSrcLine
;
1309 while (curSrcLine
) {
1310 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "EDUP", NULL
))) break;
1311 // ok, we found something; what is it?
1315 nextSrcLine(); // skip DUP
1318 if (--dupCnt
== 0) {
1323 nextSrcLine(); // skip EDUP
1326 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
1327 // now repeat that lines
1329 setCurSrcLine(stline
);
1330 while (curSrcLine
!= eline
) processCurrentLine();
1335 static void piEDUP (void) {
1336 fatal("EDUP withoud DUP");
1340 ///////////////////////////////////////////////////////////////////////////////
1343 static void processCurrentLine (void) {
1344 char *str
, *ee
, name
[66];
1347 if (!curSrcLine
) return; // do nothing
1349 //printf("0: [%s]\n", curLine);
1351 //printf("1: [%s]\n", curLine);
1352 // try to find and process command
1353 str
= curLine
; while (*str
&& isspace(*str
)) ++str
; // skip spaces
1354 //printf("2: [%s]\n", str);
1356 for (ee
= str
; *ee
&& !isspace(*ee
) && *ee
!= '"' && *ee
!= '\''; ++ee
) ;
1357 //printf("3: [%s]\n", ee);
1358 if (ee
!= str
&& ee
-str
<= 64) {
1359 memset(name
, 0, sizeof(name
));
1360 memmove(name
, str
, ee
-str
);
1362 op
= urFindOp(name
);
1365 str
= ee
; while (*str
&& isspace(*str
)) ++str
; // skip spaces
1366 memmove(curLine
, str
, strlen(str
)+1);
1368 nextSrcLine(); // skip it
1372 // try to compile this
1376 // skip spaces and ':'
1377 str
= curLine
; while (*str
&& (isspace(*str
) || *str
== ':')) ++str
;
1378 memmove(curLine
, str
, strlen(str
)+1);
1379 if (!curLine
[0]) break;
1381 len
= urAssembleOne(curLine
, pc
, disp
, &errpos
);
1382 if (len
< 0) fatalUrLib(len
);
1383 pc
+= len
; disp
+= len
;
1384 if (len
>= 0 && errpos
) {
1385 memmove(curLine
, errpos
+1, strlen(errpos
));
1393 ///////////////////////////////////////////////////////////////////////////////
1394 // setup instructions
1396 static void registerInstructions (void) {
1397 urAddOp("DISPLAY", piDISPLAY
);
1398 urAddOp("DISPLAY0", piDISPLAY0
);
1399 urAddOp("DISPLAYA", piDISPLAYA
);
1401 urAddOp("ORG", piORG
);
1402 urAddOp("DISP", piDISP
);
1403 urAddOp("ENDDISP", piENDDISP
);
1404 urAddOp("ALIGN", piALIGN
);
1405 urAddOp("DISPALIGN", piDISPALIGN
);
1406 urAddOp("ENT", piENT
);
1408 urAddOp("INCBIN", piINCBIN
);
1410 urAddOp("MODULE", piMODULE
);
1411 urAddOp("ENDMODULE", piENDMODULE
);
1413 urAddOp("DUP", piDUP
);
1414 urAddOp("EDUP", piEDUP
);
1416 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
1417 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
1418 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
1419 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
1420 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
1421 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
1422 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
1423 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
1428 ///////////////////////////////////////////////////////////////////////////////
1429 // preparing another pass
1431 static void initPass (void) {
1432 curSrcLine
= asmText
;
1434 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
); lastSeenGlobalLabel
= NULL
;
1443 static int posstPass (void) {
1444 if (checkLabels()) return -1;
1449 ///////////////////////////////////////////////////////////////////////////////
1452 static struct option longOpts
[] = {
1453 {"sna", 0, NULL
, 's'},
1454 {"autosna", 0, NULL
, 'S'},
1455 {"tap", 0, NULL
, 't'},
1456 /*{"rawtap", 0, NULL, 'T'},*/
1457 {"raw", 0, NULL
, 'r'},
1458 /*{"dmb", 0, NULL, 'b'},*/
1459 {"none", 0, NULL
, 'n'},
1460 {"help", 0, NULL
, 'h'},
1465 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
1468 static void usage (const char *pname
) {
1470 /*" -T --rawtap write raw file with '.tap' extension\n"*/
1471 /*" -b --dmb write .dmb file\n"*/
1473 "usage: %s [options] infile\n"
1474 "default infiles:", pname
);
1475 for (f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
1478 " -s --sna write 48K .SNA file\n"
1479 " -S --autosna write 48K .SNA file with autostart\n"
1480 " -t --tap write .tap file\n"
1481 " -r --raw write raw file(s)\n"
1482 " -n --none write nothing\n"
1483 " -h --help this help\n");
1487 static int optWriteType
= 'r';
1490 ///////////////////////////////////////////////////////////////////////////////
1493 int main (int argc
, char *argv
[]) {
1495 const char *pname
= argv
[0];
1496 char *inFile
= NULL
;
1499 urGetByte
= getByte
;
1500 urPutByte
= putByte
;
1501 urFindLabelByName
= findLabelCB
;
1503 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
1504 while ((c
= getopt_long(argc
, argv
, "sStrnh", longOpts
, NULL
)) >= 0) {
1506 case 'S': optRunSNA
= 1; optWriteType
= 's'; break;
1507 case 's': case 't': case 'r': case 'n': optRunSNA
= 0; optWriteType
= c
; break;
1508 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
1511 if (optind
>= argc
) {
1512 // try to find default input file
1514 for (f
= 0; defInFiles
[f
]; ++f
) {
1515 if (!access(defInFiles
[f
], R_OK
)) {
1516 inFile
= strdup(defInFiles
[f
]);
1521 inFile
= strdup(argv
[optind
]);
1523 if (!inFile
|| !inFile
[0]) {
1525 fprintf(stderr
, "ERROR: no input file!\n");
1529 registerInstructions();
1531 res
= asmTextLoad(argc
>1?argv
[1]:"ztest.saz");
1534 printf("dumping...\n");
1535 FILE *fo = fopen("z000.out", "w");
1538 for (c = asmText; c; c = c->next) fprintf(fo, "%s ; %s: %d\n", c->line, c->fname, c->lineNo);
1542 for (pass
= 0; pass
<= 1; ++pass
) {
1544 printf("pass #%d\n", pass
);
1545 setCurSrcLine(asmText
);
1546 if (setjmp(errJP
)) { res
= 1; break; }
1547 while (curSrcLine
) processCurrentLine();
1548 if (posstPass()) { res
= 1; break; }
1552 char *oc
= strdup(inFile
);
1553 char *pd
= strrchr(oc
, '.');
1554 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
1555 switch (optWriteType
) {
1556 case 's': saveSna(oc
); break;
1557 case 't': saveTap(oc
); break;
1558 case 'r': saveRaw(oc
); break;
1563 fprintf(stderr
, "ERROR: loading error!\n");
1566 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
1572 if (inFile
) free(inFile
);
1573 if (sysIncludeDir
) free(sysIncludeDir
);