2 // coded by Ketmar // Invisible Vector
15 #include <sys/types.h>
23 #include "liburasm/liburasm.h"
33 #define MAYBE_UNUSED __attribute__((unused))
35 #define lambda(return_type, body_and_args) ({ \
36 return_type __fn__ body_and_args \
41 ////////////////////////////////////////////////////////////////////////////////
42 static inline int isSpace (char ch
) { return (ch
&& ((unsigned)(ch
&0xff) <= 32 || ch
== 127)); }
43 static inline int isAlpha (char ch
) { return ((ch
>= 'A' && ch
<= 'Z') || (ch
>= 'a' && ch
<= 'z')); }
44 static inline int isDigit (char ch
) { return (ch
>= '0' && ch
<= '9'); }
45 static inline int isHexDigit (char ch
) { return ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F') || (ch
>= 'a' && ch
<= 'f')); }
46 static inline int isAlphaDigit (char ch
) { return (isAlpha(ch
) || isDigit(ch
)); }
48 static inline char toUpper (char ch
) { return (ch
>= 'a' && ch
<= 'z' ? ch
-'a'+'A' : ch
); }
49 static inline char toLower (char ch
) { return (ch
>= 'A' && ch
<= 'Z' ? ch
-'A'+'a' : ch
); }
52 ////////////////////////////////////////////////////////////////////////////////
53 static char *strprintfVA (const char *fmt
, va_list vaorig
) {
58 if (buf
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
64 olen
= vsnprintf(buf
, len
, fmt
, va
);
66 if (olen
>= 0 && olen
< len
) return buf
;
67 if (olen
< 0) olen
= len
*2-1;
68 nb
= realloc(buf
, olen
+1);
69 if (nb
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
76 static __attribute((format(printf
,1,2))) char *strprintf (const char *fmt
, ...) {
81 buf
= strprintfVA(fmt
, va
);
87 ///////////////////////////////////////////////////////////////////////////////
90 static char *sysIncludeDir
= NULL
;
91 static char *refFileName
= NULL
;
93 #define MAX_LINE_SIZE 16384
94 static char currLine
[MAX_LINE_SIZE
]; /* current processing line; can be freely modified */
97 ///////////////////////////////////////////////////////////////////////////////
100 static void initInclideDir (void) {
101 const char *id
= getenv("URASM_INCLUDE_DIR");
103 sysIncludeDir
= strdup(id
);
106 memset(myDir
, 0, sizeof(myDir
));
108 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
111 char *p
= (char *)strrchr(myDir
, '/');
112 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
115 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
116 char *p
= strrchr(myDir
, '\\');
117 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
119 strcat(myDir
, "/libs");
120 sysIncludeDir
= strdup(myDir
);
122 while (sysIncludeDir
[0] && sysIncludeDir
[strlen(sysIncludeDir
)-1] == '/') sysIncludeDir
[strlen(sysIncludeDir
)-1] = '\0';
123 if (!sysIncludeDir
[0]) strcpy(sysIncludeDir
, ".");
127 ///////////////////////////////////////////////////////////////////////////////
130 /* trim trailing spaces and comments; normalize colons */
131 static void normalizeStr (char *s
) {
136 const char ch
= *p
++;
137 /* check for "af'" */
138 if (ch
== '\'' && p
-s
>= 2 && toLower(p
[-2] == 'a') && toLower(p
[-1] == 'f')) continue;
140 //if (ch == '(') { ++paren; continue; }
141 //if (ch == ')') { if (--paren < 0) paren = 0; continue; }
143 if (ch
== ';') { p
[-1] = 0; break; }
145 if (ch
== '"' || ch
== '\'') {
148 const char c1
= *p
++;
149 if (c1
== qch
) break;
150 if (c1
== '\\' && *p
) ++p
;
154 /* reduce and normalise colons */
155 if (/*paren == 0 &&*/ ch
== ':') {
156 --p
; /* back to colon */
157 /* remove spaces before colon */
159 while (t
!= s
&& isSpace(t
[-1])) --t
;
160 if (t
!= p
) memmove(t
, p
, strlen(p
)+1);
162 if (p
[0] != ':') abort(); // assert
163 ++p
; /* skip colon */
164 /* remove following spaces and colons */
166 while (*t
== ':' || isSpace(*t
)) ++t
;
167 if (t
!= p
) memmove(p
, t
, strlen(t
)+1);
171 /* done; trim trailing spaces and colons */
172 size_t slen
= strlen(s
);
173 while (slen
> 0 && (isSpace(s
[slen
-1]) || s
[slen
-1] == ':')) --slen
;
178 /* check if string starts with the given command (case-insensitive) */
179 /* returns NULL or pointer to args */
180 /* skips spaces after command if any */
181 static char *strIsCommand (const char *command
, char *str
) {
182 for (int cnt
= 1; cnt
> 0; --cnt
) {
183 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
184 for (; *command
&& *str
; ++command
, ++str
) {
185 if (toUpper(*command
) != toUpper(*str
)) return NULL
; // alas
187 if (*command
) return NULL
; // alas
188 if (*str
&& isAlphaDigit(*str
)) return NULL
; // alas
189 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
190 if (*str
&& *str
== ':') break; // try again if we have a colon
197 /* parse string literal */
198 /* don't free() result */
199 /* skips trailing spaces */
200 static char *parseStr (char **str
, char endQ
, int *lenp
) {
201 static char buf
[MAX_LINE_SIZE
];
202 int len
= 0, n
, f
, base
;
205 int xDigit (char ch
, int base
) {
206 if (ch
< '0') return -1;
208 if (ch
>= '0'+base
) return -1;
212 if (ch
<= '9') return ch
-'0';
213 if (ch
< 'A' || ch
> 'A'+base
-10) return -1;
215 return (ch
< base
? ch
: -1);
218 memset(buf
, 0, sizeof(buf
));
224 case 'a': buf
[len
++] = '\a'; break;
225 case 'b': buf
[len
++] = '\b'; break;
226 case 'e': buf
[len
++] = '\x1b'; break;
227 case 'f': buf
[len
++] = '\f'; break;
228 case 'n': buf
[len
++] = '\n'; break;
229 case 'r': buf
[len
++] = '\r'; break;
230 case 't': buf
[len
++] = '\t'; break;
231 case 'v': buf
[len
++] = '\v'; break;
232 case 'z': buf
[len
++] = '\0'; break;
233 case 'x': case 'X': // hex
236 donum
: for (n
= 0; f
> 0; --f
) {
237 char ch
= xDigit(*a
++, base
);
239 if (ch
< 0) { --a
; break; }
244 --a
; // return to the last digit, 'for' will skip it
249 case '1' ... '9': // decimal
252 default: buf
[len
++] = a
[0]; break; // others
255 if (*a
== endQ
) { ++a
; break; }
259 while (*a
&& isSpace(*a
)) ++a
; // skip trailing spaces
262 if (lenp
) *lenp
= len
;
267 ///////////////////////////////////////////////////////////////////////////////
268 // source file stack, reader, etc
270 typedef struct SourceLine
{
271 struct SourceLine
*next
;
277 static SourceLine
*asmText
= NULL
;
278 static SourceLine
*asmTextLast
= NULL
;
279 static SourceLine
*curSrcLine
= NULL
;
281 #define MAX_MACRO_ARGS (32)
283 typedef struct MacroDef
{
284 struct MacroDef
*next
;
288 char *argdefaults
[MAX_MACRO_ARGS
]; // default values
289 char *argnames
[MAX_MACRO_ARGS
]; // argument names
294 char *argvals
[MAX_MACRO_ARGS
]; // argument values
297 static MacroDef
*maclist
= NULL
;
298 static CurMacroDef
*curmacro
= NULL
; // !NULL if we are not inserting macro
299 static int curmacronum
= 0;
302 static MacroDef
*findMacro (const char *name
) {
303 for (MacroDef
*mc
= maclist
; mc
!= NULL
; mc
= mc
->next
) if (strcasecmp(name
, mc
->name
) == 0) return mc
;
308 static void asmTextClear (void) {
310 SourceLine
*l
= asmText
;
312 asmText
= asmText
->next
;
317 asmTextLast
= curSrcLine
= NULL
;
321 static int asmTextLoad (const char *fname
) {
326 if (!(fl
= fopen(fname
, "r"))) {
327 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
330 printf("loading: %s\n", fname
);
332 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
334 currLine
[sizeof(currLine
)-1] = '\0';
335 normalizeStr(currLine
);
336 //fprintf(stderr, "*[%s]\n", curLine);
337 if (!currLine
[0]) continue; // don't store empty lines
339 if ((s
= calloc(1, sizeof(SourceLine
))) == NULL
) abort();
341 if ((s
->line
= strdup(currLine
)) == NULL
) abort();
342 if ((s
->fname
= strdup(fname
)) == NULL
) abort();
343 if (asmTextLast
) asmTextLast
->next
= s
; else asmText
= s
;
351 static inline void loadCurSrcLine (void) { if (curSrcLine
) strcpy(currLine
, (curSrcLine
!= NULL
? curSrcLine
->line
: "")); }
352 static inline SourceLine
*setCurSrcLine (SourceLine
*l
) { curSrcLine
= l
; loadCurSrcLine(); return l
; }
353 static inline SourceLine
*nextSrcLine (void) { return (curSrcLine
!= NULL
? setCurSrcLine(curSrcLine
->next
) : NULL
); }
356 static int includeCount
= 0;
359 static void normIncName (char *dest
, const char *fn
, int system
) {
362 if (system
) sprintf(dest
, "%s/%s", sysIncludeDir
, fn
); else sprintf(dest
, "%s", fn
);
363 if (stat(dest
, &st
)) return;
364 if (S_ISDIR(st
.st_mode
)) strcat(dest
, "/zzmain.zas");
369 // include file instead of the current line
370 static int asmTextInclude (const char *fname
, int system
) {
374 SourceLine
*first
= NULL
, *last
= NULL
, *s
= NULL
;
376 if (includeCount
> 256) {
377 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", curSrcLine
->fname
, curSrcLine
->lineNo
);
382 normIncName(currLine
, fn
, system
);
384 fn
= alloca(strlen(currLine
+1));
385 strcpy(fn
, currLine
);
387 printf("loading: %s\n", fn
);
388 if ((fl
= fopen(fn
, "r")) == NULL
) {
389 fprintf(stderr
, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", curSrcLine
->fname
, curSrcLine
->lineNo
, currLine
);
393 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
395 currLine
[sizeof(currLine
)-1] = '\0';
396 normalizeStr(currLine
);
397 if (!currLine
[0]) continue; // don't store empty lines
399 if ((s
= calloc(1, sizeof(SourceLine
))) == NULL
) abort();
401 if ((s
->line
= strdup(currLine
)) == NULL
) abort();
402 if ((s
->fname
= strdup(fn
)) == NULL
) abort();
403 if (last
!= NULL
) last
->next
= s
; else first
= s
;
408 // now replace current line
410 free(curSrcLine->line);
411 free(curSrcLine->fname);
413 curSrcLine->line = first->line;
414 curSrcLine->fname = first->fname;
415 curSrcLine->lineNo = first->lineNo;
416 if ((first = first->next) != NULL) {
417 // more than one line
418 last->next = curSrcLine->next;
419 curSrcLine->next = first;
423 curSrcLine
->line
[0] = 0;
424 last
->next
= curSrcLine
->next
;
425 curSrcLine
->next
= first
;
430 ///////////////////////////////////////////////////////////////////////////////
433 static void processCurrentLine (void); // only one, will skip to next one
436 ///////////////////////////////////////////////////////////////////////////////
437 // error raisers, etc
439 static jmp_buf errJP
;
442 static void errorWriteFile (FILE *fo
) {
444 fprintf(fo
, "at file %s, line %d\n%s\n*", curSrcLine
->fname
, curSrcLine
->lineNo
, curSrcLine
->line
);
446 fprintf(fo
, "somewhere in time: ");
450 static void errorMsgV (const char *fmt
, va_list ap
) {
451 errorWriteFile(stderr
);
452 vfprintf(stderr
, fmt
, ap
);
459 static void __attribute__((format(printf
, 1, 2))) warningMsg (const char *fmt
, ...) {
461 fprintf(stderr
, "WARNING ");
467 static void __attribute__((format(printf
, 1, 2))) errorMsg (const char *fmt
, ...) {
469 fprintf(stderr
, "FATAL ");
475 static void __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2))) fatal (const char *fmt
, ...) {
483 static void __attribute__((noreturn
)) fatalUrLib (int errcode
) {
484 errorMsg("%s", urasm_errormsg(errcode
));
489 //////////////////////////////////////////////////////////////////////////////
490 // operator management
492 // return !0 to skip current line
493 typedef int (*UrAsmOpFn
) (void);
500 typedef struct UrAsmOp
{
503 struct UrAsmOp
*next
;
506 static UrAsmOp
*oplist
= NULL
;
509 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
510 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
512 res
->name
= strdup(name
);
520 static UrAsmOp
*urFindOp (const char *name
) {
522 for (res
= oplist
; res
; res
= res
->next
) if (!strcasecmp(name
, res
->name
)) break;
527 static void urClearOps (void) {
531 oplist
= oplist
->next
;
538 ///////////////////////////////////////////////////////////////////////////////
541 typedef struct UrLabelInfo
{
544 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
545 int known
; /* !0: label value already known */
546 int refLine
; /* first referenced line */
547 int fixuptype
; /* UR_FIXUP_XXX */
549 struct UrLabelInfo
*next
;
552 static UrLabelInfo
*labels
= NULL
;
555 static void urClearLabels (void) {
558 while ((c
= labels
) != NULL
) {
560 if (c
->name
) free(c
->name
);
561 if (c
->refFile
) free(c
->refFile
);
567 static UrLabelInfo
*urFindLabel (const char *name
) {
568 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) if (strcmp(name
, c
->name
) == 0) return c
;
573 static UrLabelInfo
*urAddLabel (const char *name
) {
574 UrLabelInfo
*c
= urFindLabel(name
);
579 for (p
= NULL
, c
= labels
; c
; p
= c
, c
= c
->next
) {}
580 c
= calloc(1, sizeof(UrLabelInfo
));
582 c
->name
= strdup(name
);
584 c
->fixuptype
= UR_FIXUP_NONE
;
585 if (p
) p
->next
= c
; else labels
= c
;
592 ///////////////////////////////////////////////////////////////////////////////
593 // module list management
595 typedef struct ModuleInfo
{
597 char *fname
; // opened in this file
598 int seen
; // !0: module already seen, skip other definitions from the same file
599 struct ModuleInfo
*next
;
602 static ModuleInfo
*modules
= NULL
;
603 static ModuleInfo
*curModule
= NULL
;
606 static void modulesClear (void) {
609 ModuleInfo
*c
= modules
;
611 modules
= modules
->next
;
619 static void modulesResetSeen (void) {
620 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
624 static ModuleInfo
*moduleFind (const char *name
) {
625 if (!name
|| !name
[0]) return NULL
;
626 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) return c
;
631 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
634 if (!name
|| !fname
|| !name
[0] || !fname
[0]) abort();
635 if ((c
= calloc(1, sizeof(ModuleInfo
))) == NULL
) abort();
636 if ((c
->name
= strdup(name
)) == NULL
) abort();
637 if ((c
->fname
= strdup(fname
)) == NULL
) abort();
639 return (modules
= c
);
643 ///////////////////////////////////////////////////////////////////////////////
645 typedef struct FixupItem
{
646 struct FixupItem
*next
;
652 static FixupItem
*fixlisthead
= NULL
, *fixlisttail
= NULL
;
655 static void clearFixups (void) {
658 while ((c
= fixlisthead
) != NULL
) {
659 fixlisthead
= c
->next
;
665 static void addFixup (uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
666 FixupItem
*fx
= calloc(1, sizeof(FixupItem
));
668 fx
->opdestaddr
= opdestaddr
;
670 fx
->fixuptype
= fixuptype
;
673 if (fixlisttail
!= NULL
) fixlisttail
->next
= fx
; else fixlisthead
= fx
;
679 ///////////////////////////////////////////////////////////////////////////////
680 // destination memory management
682 static uint8_t memory
[65536];
683 static char memused
[65536];
684 static char memresv
[65536];
685 static uint16_t start_pc
= 0x100; // viva CP/M!
686 static uint16_t start_disp
= 0x100; // viva CP/M!
687 static uint16_t start_ent
= 0x100; // viva CP/M!
688 static uint16_t pc
= 0; /* current position to write */
689 static uint16_t disp
= 0; /* current 'virtual PC' */
690 static uint16_t ent
= 0; /* starting address */
691 static uint16_t clrAddr
= 24999; /* address for CLEAR for cargador */
692 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
693 static int inTapeBlock
= 0;
694 static uint8_t tapeXorB
= 0;
697 static inline uint8_t getByte (uint16_t addr
) {
703 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
704 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
709 static inline void putByte (uint16_t addr
, uint8_t b
) {
710 if (inTapeBlock
) tapeXorB
^= b
;
716 static inline MAYBE_UNUSED
void putWord (uint16_t addr
, uint16_t w
) {
717 putByte(addr
, w
&0xFFU
);
718 putByte(addr
+1, (w
>>8)&0xFFU
);
722 static inline void emitByte (uint8_t b
) {
729 static inline void emitWord (uint16_t w
) {
731 emitByte((w
>>8)&0xFFU
);
735 static inline void emitRWord (uint16_t w
) {
736 emitByte((w
>>8)&0xFFU
);
741 static void prepareMemory (void) {
742 memset(memory
, 0, sizeof(memory
));
743 memset(memused
, 0, sizeof(memused
));
744 memset(memresv
, 0, sizeof(memresv
));
748 ///////////////////////////////////////////////////////////////////////////////
749 // label getter and utilities
751 static char *lastSeenGlobalLabel
= NULL
; /* global */
754 static char *fixLocalLabel (const char *name
) {
755 static char newname
[MAX_LINE_SIZE
*2+1024];
757 memset(newname
, 0, sizeof(newname
));
758 if (!name
|| !name
[0]) {
760 } else if (!lastSeenGlobalLabel
|| name
[0] != '.') {
761 strcpy(newname
, name
);
763 if (name
[0] == '.' && name
[1] == '.') {
764 // this is macro label
765 if (curmacro
== NULL
) fatal("macro label outside of macro: '%s'", name
);
766 sprintf(newname
, "{#MAC%d:%s:%s}", curmacronum
, curmacro
->mac
->name
, name
);
768 // this is local label, let's rename it
769 sprintf(newname
, "{%s%s}", lastSeenGlobalLabel
, name
);
771 /*!fprintf(stderr, "LABEL <%s> renamed to <%s> (%d)\n", name, newname, (curSrcLine ? curSrcLine->lineNo : 0));*/
777 static char *fixGlobalLabel (const char *name
) {
778 static char newname
[MAX_LINE_SIZE
*2+1024];
780 memset(newname
, 0, sizeof(newname
));
781 if (!name
|| !name
[0]) {
783 } else if (!curModule
|| name
[0] == '.' || name
[0] == '{' || name
[0] == '@' || strchr(name
, '.')) {
784 if (name
[0] == '@' && name
[1]) ++name
;
785 strcpy(newname
, name
);
787 // this is global unqualified label and we have a module; let's rename it
788 sprintf(newname
, "%s.%s", curModule
->name
, name
);
790 //printf("%s --> %s\n", name, newname);
795 static int lblOptMakeU2
= 0;
797 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
, int *fixuptype
) {
801 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
802 lbl
= urFindLabel(nn
);
804 // try non-module label
805 lbl
= urFindLabel(ln
);
809 errorMsg("using undefined label %s", ln
);
814 lbl
= urAddLabel(nn
);
815 lbl
->type
= (lblOptMakeU2
? -42 : -1);
817 lbl
->refLine
= curSrcLine
->lineNo
;
818 lbl
->refFile
= strdup(curSrcLine
->fname
);
819 //printf("new label: [%s]\n", lbl->name);
821 //printf("label reference: [%s]\n", lbl->name);
825 *defined
= lbl
->known
!=0;
826 *fixuptype
= lbl
->fixuptype
;
841 static int isLabelDefinedOrKnown (const char *name
, uint16_t addr
, int qtype
) {
845 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
846 lbl
= urFindLabel(nn
);
848 // try non-module label
849 lbl
= urFindLabel(ln
);
852 case UR_QTYPE_DEFINED
: return lbl
? lbl
->known
!=0 : 0;
853 case UR_QTYPE_KNOWN
: return lbl
!=NULL
;
860 static void fixupOperandCB (const urasm_operand_t
*op
, uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
862 //static const char *n[4] = {"none", "word", "low", "high"};
863 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
864 addFixup(opdestaddr
, opaddr
, fixuptype
, size
);
869 static int checkLabels (void) {
872 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
874 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
877 if (c
->type
== 0) c
->known
= -1;
879 //if (wasError) longjmp(errJP, 667);
884 ///////////////////////////////////////////////////////////////////////////////
885 // expression utils (aka string parsing)
888 /* skip leading spaces */
889 /* returns string with spaces skipped */
890 static inline char *strSkipSpaces (const char *s
) {
891 while (*s
&& isSpace(*s
)) ++s
;
896 /* skip leading spaces */
897 /* returns string with spaces skipped */
898 static inline const char *strSkipSpacesConst (const char *s
) {
899 while (*s
&& isSpace(*s
)) ++s
;
904 /* remove trailing spaces from string */
905 static void strTrimRight (char *s
) {
906 if (!s
|| !s
[0]) return;
907 size_t len
= strlen(s
);
908 while (len
> 0 && isSpace(s
[len
-1])) --len
;
913 /* skip leading spaces and colons */
914 /* returns string with spaces skipped */
915 static inline char *strSkipSpacesColons (char *s
) {
916 while (*s
&& (isSpace(*s
) || *s
== ':')) ++s
;
921 /* remove leading spaces from the current line */
922 static inline void removeSpaces (void) {
923 char *e
= strSkipSpaces(currLine
);
924 if (e
!= currLine
) memmove(currLine
, e
, strlen(e
)+1);
928 /* skip leading spaces, and argument (up to, but not including comma or colon) */
929 /* correctly skip strings */
930 /* returns string after skipped argument (with trailing spaces skipped) */
931 static char *skipMacroArg (char *str
) {
933 char *strstart
= str
;
935 str
= strSkipSpaces(str
);
936 if (!str
[0]) return str
;
937 if (parens
== 0 && (str
[0] == ',' || str
[0] == ':')) return str
;
938 /* check for "af'" */
939 if (str
[0] == '\'' && str
-strstart
>= 2 && toLower(str
[-2] == 'a') && toLower(str
[-1] == 'f')) {
943 if (str
[0] == '(') { ++parens
; continue; }
944 if (str
[0] == ')') { if (--parens
< 0) parens
= 0; continue; }
945 /* check for string */
946 if (str
[0] == '"' || str
[0] == '\'') {
947 const char qch
= *str
++;
949 const char ch
= *str
++;
950 if (ch
== qch
) break;
951 if (ch
== '\\' && *str
) ++str
;
960 /* evaluate next numeric expression in input string */
961 /* returns expression value */
962 static int32_t getExprArg (int *defined
, int *addr
) {
964 char *a
= strSkipSpaces(currLine
);
966 if (!a
[0]) fatal("expression expected");
967 const char *ee
= urasm_expr(&res
, a
, disp
, defined
, addr
, &error
);
968 if (error
) fatalUrLib(error
);
970 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
971 memmove(currLine
, ee
, strlen(ee
)+1);
979 /* evaluate next string expression in input string */
980 /* returns expression value */
981 static char *getStrExprArg (void) {
983 int donteval
= 0, defined
= 0;
984 static char resbuf
[256];
985 char *a
= strSkipSpaces(currLine
);
986 if (!a
[0]) fatal("expression expected");
988 urasm_exprval_init(&res
);
989 const char *ee
= urasm_expr_ex(&res
, a
, disp
, &donteval
, &defined
, &error
);
990 if (error
) fatalUrLib(error
);
992 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
993 memmove(currLine
, ee
, strlen(ee
)+1);
998 snprintf(resbuf
, sizeof(resbuf
), "%s", res
.str
);
1000 snprintf(resbuf
, sizeof(resbuf
), "%d", res
.val
);
1002 urasm_exprval_clear(&res
);
1007 /* evaluate next numeric expression in input string */
1008 /* there shoild be no other expressions in the string */
1009 /* returns expression value */
1010 static int32_t getOneExprArg (int *defined
, int *addr
) {
1011 int32_t res
= getExprArg(defined
, addr
);
1012 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
1017 /* is next expression a string literal? */
1018 static inline int isStrArg (void) {
1019 const char *s
= strSkipSpaces(currLine
);
1020 return (s
[0] == '"' || s
[0] == '\'');
1024 /* check of we reached end of operator */
1025 static __attribute__((unused
)) inline int isOperatorEnd (void) {
1026 const char *s
= strSkipSpaces(currLine
);
1027 return (s
[0] == 0 || s
[0] == ':');
1031 /* check of we reached end of operator */
1032 static __attribute__((unused
)) inline int isLineEnd (void) {
1033 const char *s
= strSkipSpaces(currLine
);
1038 /* parse string argument from input string */
1039 /* returns parsed string */
1040 static char *getStrArg (int *lenp
) {
1042 char *a
= strSkipSpaces(currLine
);
1044 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
1045 res
= parseStr(&a
, qCh
, lenp
);
1047 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
1048 memmove(currLine
, a
, strlen(a
)+1);
1056 /* get identifier (and lowercase it) */
1057 static char *getOneIdArgLo (void) {
1058 static char res
[MAX_LINE_SIZE
+128];
1060 char *a
= strSkipSpaces(currLine
);
1061 memset(res
, 0, sizeof(res
));
1062 if (!a
[0]) fatal("identifier expected");
1063 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
1064 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
1066 if (p
-res
> 120) fatal("identifier too long: %s", res
);
1067 while (*a
&& isSpace(*a
)) ++a
;
1069 memmove(currLine
, a
, strlen(a
)+1);
1070 if (currLine
[0] == ';') currLine
[0] = 0;
1071 if (currLine
[0]) fatal("extra arguments");
1075 for (char *t
= res
; *t
; ++t
) *t
= toLower(*t
);
1080 /* get label argument */
1081 static char *getLabelArg (int checkdelim
) {
1082 static char res
[MAX_LINE_SIZE
+128];
1084 char *a
= strSkipSpaces(currLine
);
1085 memset(res
, 0, sizeof(res
));
1086 if (!a
[0]) fatal("label expected");
1087 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
1088 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
1090 if (p
-res
> 120) fatal("label name too long: %s", res
);
1091 while (*a
&& isSpace(*a
)) ++a
;
1093 if (checkdelim
&& a
[0] != ',' && a
[0] != ':') fatal("bad label expression");
1094 memmove(currLine
, a
, strlen(a
)+1);
1102 /* get label argument, and ensure that it is the last one */
1103 static char *getOneLabelArg (void) {
1104 char *res
= getLabelArg(1);
1105 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
1110 /* returns ',' or 0 */
1111 static char eatComma (void) {
1112 char *a
= strSkipSpaces(currLine
);
1113 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
1114 if (a
[0] == ':') return 0;
1115 if (a
[0] != ',') fatal("invalid expression: ',' expected");
1116 for (++a
; *a
&& isSpace(*a
); ++a
) {}
1117 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
1118 memmove(currLine
, a
, strlen(a
)+1);
1123 ///////////////////////////////////////////////////////////////////////////////
1126 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
1127 char *ep
= strSkipSpacesColons(currLine
);
1128 memmove(currLine
, ep
, strlen(ep
)+1);
1132 static void checkExprEnd (void) {
1133 char *ep
= strSkipSpaces(currLine
);
1134 memmove(currLine
, ep
, strlen(ep
)+1);
1135 if (currLine
[0] && currLine
[0] != ':') fatal("end of expression expected");
1139 static void checkOperatorEnd (void) {
1140 char *ep
= strSkipSpaces(currLine
);
1141 memmove(currLine
, ep
, strlen(ep
)+1);
1142 if (currLine
[0]) fatal("end of operator expected");
1146 /* remove label from curLine */
1147 static void removeLabel (void) {
1148 char *ep
= currLine
;
1149 if (ep
[0] && !isSpace(ep
[0])) for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {} // skip text
1150 // skip spaces and colons
1151 ep
= strSkipSpacesColons(ep
);
1152 memmove(currLine
, ep
, strlen(ep
)+1);
1156 static int labelDoEQU (const char *lblname
, const char *value
) {
1157 static char n2
[256];
1160 if (value
== NULL
|| lblname
== NULL
|| !lblname
[0] || strlen(lblname
) > 255 || strlen(value
) >= MAX_LINE_SIZE
) return -1;
1161 memset(n2
, 0, sizeof(n2
));
1162 strcpy(n2
, lblname
);
1163 if (!urasm_is_valid_name(n2
)) return -1; // this is not a valid label, get out of here
1164 // check if this can be an instruction
1165 lbl
= urAddLabel(lblname
);
1166 if (!lbl
->refFile
) {
1168 lbl
->refFile
= strdup("artificially-defined-label");
1171 strcpy(currLine
, value
);
1174 int defined
= 1, addr
= UR_FIXUP_NONE
;
1175 int32_t res
= getOneExprArg(&defined
, &addr
);
1177 lbl
->type
= 1; // equ label
1178 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1183 return -1; //fatal("can't calculate label %s", lbl->name);
1190 static void processLabel (void) {
1193 static char n2
[256];
1195 int noLocAff
= 0, doEQU
= 0;
1196 /*!fprintf(stderr, "LINE <%s> (%d)\n", curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
1197 memset(n2
, 0, sizeof(n2
));
1198 if (!currLine
[0] || isSpace(currLine
[0]) || currLine
[0] == ':') {
1199 // this may be " id = smth" or " id equ smth"
1202 while (isSpace(*ep
)) ++ep
;
1203 if (ep
[0] == ':' || !ep
[0] || (!isAlpha(ep
[0]) && ep
[0] != '_' && ep
[0] != '.' && ep
[0] != '@')) {
1204 removeLabel(); // removeLabel() removes any spaces, etc
1207 // this looks like a label; check for '=' or 'equ'
1208 while (isAlphaDigit(ep
[0]) || ep
[0] == '_' || ep
[0] == '.' || ep
[0] == '@') ++ep
;
1210 // skip trailing spaces
1211 while (isSpace(*ep
)) ++ep
;
1215 } else if (isSpace(*nn
)) {
1217 argstart
= strIsCommand("EQU", ep
);
1222 removeLabel(); // removeLabel() removes any spaces, etc
1225 // remove leading spaces from name
1229 while (isSpace(*ep
)) ++ep
;
1230 if (ep
>= nn
) fatal("internal compiler error");
1231 if (nn
-ep
> 120) fatal("label too long");
1232 memset(n2
, 0, sizeof(n2
));
1233 memmove(n2
, ep
, nn
-ep
);
1234 if (urFindOp(n2
) || !urasm_is_valid_name(n2
)) {
1235 //fatal("invalid label name");
1236 removeLabel(); // removeLabel() removes any spaces, etc
1239 // remove label name
1240 memmove(currLine
, argstart
, strlen(argstart
)+1);
1242 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
1243 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
1244 lbl
= urAddLabel(nn
);
1245 if (!lbl
->refFile
) {
1246 lbl
->refLine
= curSrcLine
->lineNo
;
1247 lbl
->refFile
= strdup(curSrcLine
->fname
);
1249 if (doEQU
&& pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
1250 int defined
= 1, addr
= UR_FIXUP_NONE
;
1251 int32_t res
= getOneExprArg(&defined
, &addr
);
1253 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1258 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
1264 for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {}
1265 if (ep
-currLine
> 120) fatal("label too long");
1267 memset(n2
, 0, sizeof(n2
));
1268 memmove(n2
, currLine
, ep
-currLine
);
1270 ep
= strSkipSpaces(ep
);
1271 if (*ep
!= ':') return; // this must be an instruction, process it
1273 if (!urasm_is_valid_name(n2
)) return; // this is not a valid label, get out of here
1275 if (findMacro(n2
)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
1276 // check if this can be instruction
1277 //ep = strSkipSpaces(ep);
1278 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
1279 // ok, we got a good label
1281 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
1282 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (curSrcLine ? curSrcLine->lineNo : 0));*/
1283 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
1284 /*!fprintf(stderr, " RENAMED(ln) <%s>\n", ln);*/
1285 /*!fprintf(stderr, " RENAMED(nn) <%s>\n", nn);*/
1286 lbl
= urAddLabel(nn
);
1287 if (!lbl
->refFile
) {
1288 lbl
->refLine
= curSrcLine
->lineNo
;
1289 lbl
->refFile
= strdup(curSrcLine
->fname
);
1291 //printf("new: [%s]\n", lbl->name);
1293 if (currLine
[0] == '=') {
1295 argstart
= currLine
+1;
1298 argstart
= strIsCommand("EQU", currLine
);
1300 if (!argstart
|| doEQU
) {
1301 if (pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
1305 memmove(currLine
, argstart
, strlen(argstart
)+1);
1307 if (lbl
->type
!= -1 && lbl
->type
!= 0) fatal("duplicate label '%s'", lbl
->name
);
1309 int defined
= 1, addr
= UR_FIXUP_NONE
;
1310 int32_t res
= getOneExprArg(&defined
, &addr
);
1312 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1317 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
1323 if (lbl
->name
[0] != '{' && !noLocAff
) {
1324 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
1325 lastSeenGlobalLabel
= strdup(lbl
->name
);
1330 lbl
->fixuptype
= UR_FIXUP_WORD
;
1334 ///////////////////////////////////////////////////////////////////////////////
1335 // instruction finder (in source)
1337 /* array ends with NULL */
1338 /* returns line or NULL */
1339 /* iidx will be set to found instruction number */
1340 static __attribute__((sentinel
)) SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
1341 if (iidx
) *iidx
= -1;
1342 for (SourceLine
*cur
= curSrcLine
; cur
; cur
= cur
->next
) {
1344 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
1346 for (int f
= 0; ;++f
) {
1347 const char *name
= va_arg(ap
, const char *);
1350 if (strIsCommand(name
, cur
->line
)) {
1352 if (iidx
) *iidx
= f
;
1362 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
1363 return findNextInstructionFromList(NULL
, name
, NULL
);
1367 ///////////////////////////////////////////////////////////////////////////////
1370 static int optWriteFixups
= 0;
1371 static int optFixupType
= 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
1372 static int optRunTape
= 1;
1373 static int optRunDMB
= 1;
1374 static int optRunSCL
= 1;
1375 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1376 static int optSNA48
= 1;
1377 static char *optOutputDir
= NULL
;
1380 ///////////////////////////////////////////////////////////////////////////////
1381 static void writeFixups (void) {
1383 void writeFixupList (FILE *fo
, int cnt
, const char *lbl
, int (*chk
)(const FixupItem
*)) {
1386 fprintf(fo
, "%s:\n", lbl
);
1387 if (optFixupType
== 1) fprintf(fo
, " dw %d ; count\n", cnt
);
1388 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1390 fprintf(fo
, " dw #%04X\n", fx
->opaddr
-prevaddr
);
1391 if (optFixupType
== 2) {
1392 prevaddr
= fx
->opaddr
;
1396 if (optFixupType
== 2 || optFixupType
== 3) fprintf(fo
, " dw 0 ; end marker\n");
1400 if (optFixupType
== 0) {
1401 /* simple text file */
1402 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.txt");
1403 printf("writing fixups to '%s'...\n", fname
);
1404 FILE *fo
= fopen(fname
, "w");
1406 if (fo
== NULL
) fatal("can't write fixup file");
1407 fprintf(fo
, "; addr dadr sz\n");
1408 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1409 static const char type
[4] = "NWLH";
1410 fprintf(fo
, "%c #%04x #%04x %d\n", type
[fx
->fixuptype
], fx
->opaddr
, fx
->opdestaddr
, fx
->size
);
1414 /* various asm formats */
1415 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.zas");
1416 printf("writing fixups to '%s'...\n", fname
);
1417 FILE *fo
= fopen(fname
, "w");
1418 int cntw
= 0, cntwl
= 0, cntwh
= 0, cntbl
= 0, cntbh
= 0;
1420 if (fo
== NULL
) fatal("can't write fixup file");
1421 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1422 if (fx
->fixuptype
== UR_FIXUP_WORD
) { ++cntw
; continue; }
1423 switch (fx
->fixuptype
) {
1424 case UR_FIXUP_LOBYTE
: if (fx
->size
== 2) ++cntwl
; else ++cntbl
; break;
1425 case UR_FIXUP_HIBYTE
: if (fx
->size
== 2) ++cntwh
; else ++cntbh
; break;
1428 writeFixupList(fo
, cntw
, "fixup_table_w", lambda(int, (const FixupItem
*fx
) {
1429 return (fx
->fixuptype
== UR_FIXUP_WORD
);
1431 writeFixupList(fo
, cntwl
, "fixup_table_wl", lambda(int, (const FixupItem
*fx
) {
1432 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 2);
1434 writeFixupList(fo
, cntwh
, "fixup_table_wh", lambda(int, (const FixupItem
*fx
) {
1435 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 2);
1437 writeFixupList(fo
, cntbl
, "fixup_table_bl", lambda(int, (const FixupItem
*fx
) {
1438 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 1);
1440 writeFixupList(fo
, cntbh
, "fixup_table_bh", lambda(int, (const FixupItem
*fx
) {
1441 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 1);
1448 ///////////////////////////////////////////////////////////////////////////////
1449 /* return 'found' flag */
1450 static int findChunkFrom (int addr
, int *start
, int *len
) {
1451 if (addr
< 0) addr
= 0;
1452 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) {}
1453 if (addr
> 65535) return 0;
1455 for (; addr
<= 65535 && memused
[addr
]; ++addr
) {}
1456 *len
= addr
-(*start
);
1461 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
1462 for (; buflen
>= 2; buflen
-= 2) {
1464 uint8_t byte
= *buf
++;
1468 for (cnt
= byte
; cnt
>= 0; --cnt
, ++addr
, --buflen
, ++buf
) {
1469 if (!memused
[addr
]) putByte(addr
, *buf
);
1470 if (addr
== 0xffff) return;
1474 for (; cnt
> 0; --cnt
, ++addr
) {
1475 if (!memused
[addr
]) putByte(addr
, byte
);
1476 if (addr
== 0xffff) return;
1483 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
1484 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1485 if (bxor
) *bxor
= (*bxor
)^b
;
1490 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
1491 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
1492 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
1497 ///////////////////////////////////////////////////////////////////////////////
1500 static int saveSna (const char *fname
, int as48
) {
1501 char *fn
;// = malloc(strlen(fname)+16);
1506 fn
= strprintf("%s/%s.sna", optOutputDir
, fname
);
1507 fo
= fopen(fn
, "wb");
1509 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
1510 printf("out: %s.sna\n", fname
);
1511 memmove(regs
, ursna48
, 27);
1514 /* push new address */
1515 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1517 putByte(sp
, ent
&0xFFU
);
1518 putByte(sp
+1, (ent
>>8)&0xFFU
);
1519 regs
[23] = sp
&0xFFU
;
1520 regs
[24] = (sp
>>8)&0xFFU
;
1522 fwrite(regs
, 27, 1, fo
);
1523 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
1524 fwrite(memory
+16384, 49152, 1, fo
);
1527 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1529 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1530 //fprintf(stderr, "%d\n", memused[sp]);
1531 if (memused
[sp
] || memused
[(sp
+1)&0xffff]) {
1532 fprintf(stderr
, "FATAL: can't save snapshot!\n");
1536 if (fwrite(ursna48
, 27, 1, fo
) != 1) goto error
;
1537 rleUnpack(16384, (const uint8_t *)ursna48
+27, sizeof(ursna48
)-27);
1538 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1540 sprintf(abuf
, "%05d", clrAddr
);
1541 memcpy(memory
+23762, abuf
, 5);
1542 sprintf(abuf
, "%05d", ent
);
1543 memcpy(memory
+23773, abuf
, 5);
1545 if (fwrite(memory
+16384, 49152, 1, fo
) != 1) goto error
;
1548 int addr
= memory
[sp
]+256*memory
[(sp
+1)&0xffff];
1550 if (fWriteWord(fo
, addr
, NULL
) != 0) goto error
; // PC
1551 if (fWriteByte(fo
, 0x10, NULL
) != 0) goto error
; // 7FFD
1552 if (fWriteByte(fo
, 0, NULL
) != 0) goto error
; // TR-DOS inactive
1554 memset(memory
, 0, 16384);
1555 for (int f
= 1; f
< 8; ++f
) {
1556 if (f
!= 2 && f
!= 5) {
1557 if (fwrite(memory
, 16384, 1, fo
) != 1) goto error
;
1571 ///////////////////////////////////////////////////////////////////////////////
1574 static void saveRaw (const char *basename
) {
1575 char *fname
= NULL
;// = malloc(strlen(basename)+16);
1579 while (findChunkFrom(start
, &start
, &len
)) {
1580 if (fname
!= NULL
) free(fname
);
1581 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, (optTapExt
? "tap" : "bin"));
1582 fo
= fopen(fname
, "wb");
1584 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1586 printf("out: %s\n", fname
);
1587 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1588 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1597 if (fname
!= NULL
) free(fname
);
1601 ///////////////////////////////////////////////////////////////////////////////
1604 typedef struct __attribute__((packed
)) __attribute__((gcc_struct
)) {
1609 uint8_t zero
; // always zero
1610 uint8_t secLen
; // length in sectors
1615 static uint16_t calcHobSum (const void *hdr
) {
1616 const uint8_t *buf
= (const uint8_t *)hdr
;
1619 for (unsigned int f
= 0; f
< 15; ++f
) res
+= ((uint16_t)buf
[f
])*257+f
;
1624 static void saveHob (const char *basename
) {
1625 char *fname
= NULL
;//malloc(strlen(basename)+16);
1630 while (findChunkFrom(start
, &start
, &len
)) {
1631 if (fname
!= NULL
) free(fname
);
1632 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, "$C");
1633 fo
= fopen(fname
, "wb");
1635 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1638 char tmpbuf
[sizeof(hdr
.name
)*2];
1639 printf("out: %s\n", fname
);
1640 memset(&hdr
, 0, sizeof(hdr
));
1641 memset(&hdr
.name
, 32, 8);
1642 snprintf(tmpbuf
, sizeof(tmpbuf
), "%c%04X", basename
[0], start
);
1643 while (strlen(tmpbuf
) < sizeof(hdr
.name
)) strcat(tmpbuf
, " "); /* sorry! */
1644 memcpy(hdr
.name
, tmpbuf
, sizeof(hdr
.name
));
1648 hdr
.secLen
= (len
+255)/256;
1649 hdr
.checksum
= calcHobSum(&hdr
);
1650 if (fwrite(&hdr
, sizeof(hdr
), 1, fo
) != 1) {
1651 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1656 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1657 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1664 if (fwrite(&hdr
.zero
, 1, 1, fo
) != 1) {
1665 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1671 //fprintf(stderr, ":%d\n", len%256);
1677 if (fname
!= NULL
) free(fname
);
1681 // ////////////////////////////////////////////////////////////////////////// //
1684 uint8_t dir
[14*256];
1693 static void sclInit (SCLFile
*scl
) {
1694 memset(scl
, 0, sizeof(*scl
));
1695 scl
->datasize
= 2*80*16*256; /* maximum disk size */
1696 scl
->data
= malloc(scl
->datasize
);
1697 memset(scl
->data
, 0, scl
->datasize
);
1698 scl
->fpos
= 0xffffffffu
;
1702 static void sclFree (SCLFile
*scl
) {
1704 if (scl
->data
) free(scl
->data
);
1705 memset(scl
, 0, sizeof(*scl
));
1706 scl
->fpos
= 0xffffffffu
;
1710 static void sclStartFile (SCLFile
*scl
, const char *name
, char type
, uint16_t v0
, uint16_t v1
) {
1711 if (scl
->fcount
== 255) {
1712 fprintf(stderr
, "FATAL: too many files in SCL!\n");
1715 if (scl
->fpos
!= 0xffffffffu
) {
1716 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
1720 while (nlen
< 8 && name
&& name
[nlen
]) scl
->dir
[scl
->dirpos
++] = name
[nlen
++];
1721 while (nlen
< 8) { scl
->dir
[scl
->dirpos
++] = ' '; ++nlen
; }
1722 scl
->dir
[scl
->dirpos
++] = type
;
1723 scl
->dir
[scl
->dirpos
++] = v0
&0xff;
1724 scl
->dir
[scl
->dirpos
++] = (v0
>>8)&0xff;
1725 scl
->dir
[scl
->dirpos
++] = v1
&0xff;
1726 scl
->dir
[scl
->dirpos
++] = (v1
>>8)&0xff;
1727 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
1728 scl
->fpos
= scl
->datapos
;
1732 static void sclEndFile (SCLFile
*scl
) {
1733 if (scl
->fpos
== 0xffffffffu
) {
1734 fprintf(stderr
, "FATAL: last SCL file already finished!\n");
1737 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
1738 const uint32_t fsz
= scl
->datapos
-scl
->fpos
;
1739 if (fsz
> 255*256) {
1740 fprintf(stderr
, "FATAL: SCL file too big!\n");
1743 scl
->dir
[scl
->dirpos
++] = ((fsz
+255)/256)&0xff; /* size in sectors */
1745 scl
->fpos
= 0xffffffffu
;
1749 static void sclWriteData (SCLFile
*scl
, const void *buf
, size_t len
) {
1750 if (scl
->fpos
== 0xffffffffu
) {
1751 fprintf(stderr
, "FATAL: no open SCL file!\n");
1755 if (len
> 255*256) {
1756 fprintf(stderr
, "FATAL: SCL file too big!\n");
1759 memcpy(scl
->data
+scl
->datapos
, buf
, len
);
1760 scl
->datapos
+= len
;
1764 static void sclWriteByte (SCLFile
*scl
, uint8_t b
) {
1765 sclWriteData(scl
, &b
, 1);
1769 static void sclWriteWord (SCLFile
*scl
, uint16_t w
) {
1771 sclWriteData(scl
, &b
, 1);
1773 sclWriteData(scl
, &b
, 1);
1777 #define SCL_DO_WRITE(buf_,size_) do { \
1778 if ((size_) == 0) break; \
1779 const uint8_t *p = (const uint8_t *)(buf_); \
1780 for (size_t n = (size_t)(size_); n--; ++p) checksum += *p; \
1781 if (fwrite((buf_), (size_t)(size_), 1, fo) != 1) return -1; \
1785 static int sclSaveToFile (FILE *fo
, SCLFile
*scl
) {
1786 if (scl
->fpos
!= 0xffffffffu
) {
1787 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
1790 const char *sign
= "SINCLAIR";
1791 uint32_t checksum
= 0;
1793 SCL_DO_WRITE(sign
, 8);
1794 SCL_DO_WRITE(&scl
->fcount
, 1);
1796 SCL_DO_WRITE(scl
->dir
, scl
->dirpos
);
1798 SCL_DO_WRITE(scl
->data
, scl
->datapos
);
1800 for (unsigned f
= 0; f
< 4; ++f
) {
1801 const uint8_t b
= (checksum
>>(f
*8))&0xff;
1802 SCL_DO_WRITE(&b
, 1);
1809 // ////////////////////////////////////////////////////////////////////////// //
1810 static void saveSCLCargador (SCLFile
*scl
) {
1811 static uint8_t cargador
[16384]; // should be enough for everyone
1814 int start
= 0, len
, pos
;
1816 void putStr (const char *s
) {
1817 for (; *s
; ++s
) cargador
[pos
++] = *s
;
1820 void putNum (int num
) {
1822 sprintf(buf
, "%d", num
);
1830 cargador
[pos
++] = (linenum
>>8)&0xff;
1831 cargador
[pos
++] = linenum
&0xff;
1832 // size (will be fixed later)
1833 cargador
[pos
++] = 0;
1834 cargador
[pos
++] = 0;
1838 if (linestart
>= 0) {
1839 const int size
= pos
-linestart
-4;
1840 cargador
[linestart
+2] = size
&0xff;
1841 cargador
[linestart
+3] = (size
>>8)&0xff;
1851 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
1854 while (findChunkFrom(start
, &start
, &len
)) {
1855 // :RANDOMIZE USR VAL "15619":REM:LOAD "
1856 if (cont
) { putStr(":"); cont
= 0; }
1857 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
1858 // generate chunk name
1859 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
1868 if (cont
) { putStr(":"); cont
= 0; }
1869 // RANDOMIZE USR VAL "xxx"
1870 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"");
1875 //putWord(1); // autostart line
1878 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
1879 sclWriteData(scl
, cargador
, (size_t)pos
);
1880 sclWriteByte(scl
, 0x80);
1881 sclWriteByte(scl
, 0xaa);
1882 sclWriteWord(scl
, 1);
1887 static void saveSCL (const char *basename
) {
1891 char *fname
= strprintf("%s/%s.scl", optOutputDir
, basename
);
1894 while (findChunkFrom(start
, &start
, &len
)) {
1898 if (fcount
&& optRunSCL
) fcount
+= 2; // +loader and boot
1900 fprintf(stderr
, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname
, fcount
);
1903 // create output file
1904 FILE *fo
= fopen(fname
, "wb");
1906 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
1909 // initialise SCL writer
1912 if (fcount
&& optRunSCL
) {
1913 // create simple boot
1914 const uint8_t dasboot
[] = {
1915 0xf9,0xc0,0xb0,'"','1','5','6','1','9','"',':',0xea,':',0xef,'"','C','A','R','G','A','D','O','R','"','\r', //RANDOMIZE USR VAL "15619":REM:LOAD "CARGADOR"
1917 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
1918 sclWriteWord(&scl
, 1); // line number
1919 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
1920 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
1922 sclWriteByte(&scl
, 0x80);
1923 sclWriteByte(&scl
, 0xaa);
1925 sclWriteWord(&scl
, 0);
1927 saveSCLCargador(&scl
);
1932 while (findChunkFrom(start
, &start
, &len
)) {
1933 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
1934 sclStartFile(&scl
, cname
, 'C', (unsigned)start
, (unsigned)len
);
1935 sclWriteData(&scl
, memory
+(unsigned)start
, (unsigned)len
);
1939 if (sclSaveToFile(fo
, &scl
) < 0) {
1940 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
1947 printf("out: %s\n", fname
);
1948 if (fname
!= NULL
) free(fname
);
1952 ///////////////////////////////////////////////////////////////////////////////
1955 static int saveDMB (const char *fname
) {
1956 char *fn
;// = malloc(strlen(fname)+16);
1961 fn
= strprintf("%s/%s.dmb", optOutputDir
, fname
);
1962 fo
= fopen(fn
, "wb");
1964 if (!fo
) { fprintf(stderr
, "ERROR: can't write DMB file: %s.dmb\n", fname
); return -1; }
1966 while (findChunkFrom(start
, &start
, &len
)) { ++pcnt
; start
+= len
; }
1968 printf("out: %s.dmb\n", fname
);
1969 if (fwrite("DMB1", 4, 1, fo
) != 1) goto error
;
1970 if (fWriteWord(fo
, (optRunDMB
? ent
: 0), NULL
) != 0) goto error
;
1971 if (fWriteWord(fo
, clrAddr
, NULL
) != 0) goto error
;
1972 if (fWriteWord(fo
, pcnt
, NULL
) != 0) goto error
;
1975 while (findChunkFrom(start
, &start
, &len
)) {
1976 if (fWriteWord(fo
, len
, NULL
) != 0) goto error
;
1977 if (fWriteWord(fo
, start
, NULL
) != 0) goto error
;
1978 if (fwrite(memory
+start
, len
, 1, fo
) != 1) goto error
;
1986 fprintf(stderr
, "ERROR: error writing file %s.dmb!\n", fname
);
1991 ///////////////////////////////////////////////////////////////////////////////
1994 static char tapeLoaderName
[16];
1997 static void saveTapCargador (FILE *fo
) {
1999 static uint8_t cargador
[16384]; // should be enough for everyone
2000 int start
= 0, len
, pos
, f
;
2004 void putStr (const char *s
) {
2005 for (; *s
; ++s
) cargador
[pos
++] = *s
;
2008 void putNum (int num
) {
2010 sprintf(buf
, "%d", num
);
2017 cargador
[0] = 0; cargador
[1] = 10;
2018 // size (will be fixed later)
2019 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\""); // CLEAR VAL "xxx"
2021 while (findChunkFrom(start
, &start
, &len
)) {
2023 putStr(":\xef\"\"\xaf");
2027 // :RANDOMIZE USR VAL "xxx"
2028 putStr(":\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
2030 cargador
[2] = (pos
-4)&0xff;
2031 cargador
[3] = ((pos
-4)>>8)&0xff;
2033 fWriteWord(fo
, 19, NULL
); // length of header
2035 fWriteByte(fo
, 0, &bxor
); // header block
2036 fWriteByte(fo
, 0, &bxor
); // 'basic' flag
2037 if (tapeLoaderName
[0]) {
2038 for (int f
= 0; f
< 10; ++f
) fWriteByte(fo
, tapeLoaderName
[f
], &bxor
);
2040 fWriteByte(fo
, 'c', &bxor
);
2041 fWriteByte(fo
, 'a', &bxor
);
2042 fWriteByte(fo
, 'r', &bxor
);
2043 fWriteByte(fo
, 'g', &bxor
);
2044 fWriteByte(fo
, 'a', &bxor
);
2045 fWriteByte(fo
, 'd', &bxor
);
2046 fWriteByte(fo
, 'o', &bxor
);
2047 fWriteByte(fo
, 'r', &bxor
);
2048 fWriteByte(fo
, ' ', &bxor
);
2049 fWriteByte(fo
, ' ', &bxor
);
2051 fWriteWord(fo
, pos
, &bxor
); // length
2052 fWriteWord(fo
, 10, &bxor
); // start
2053 fWriteWord(fo
, pos
, &bxor
); // length2
2054 fWriteByte(fo
, bxor
, NULL
);
2056 fWriteWord(fo
, pos
+2, NULL
); // plus type and checkbyte
2058 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
2059 for (f
= 0; f
< pos
; ++f
) fWriteByte(fo
, cargador
[f
], &bxor
);
2060 fWriteByte(fo
, bxor
, NULL
);
2064 static void saveTap (const char *basename
) {
2065 char *fname
;// = malloc(strlen(basename)+16);
2067 int start
= 0, len
, f
;
2071 fname
= strprintf("%s/%s.tap", optOutputDir
, basename
);
2072 fo
= fopen(fname
, "wb");
2074 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
2075 printf("out: %s.tap\n", basename
);
2076 if (optRunTape
) saveTapCargador(fo
);
2077 while (findChunkFrom(start
, &start
, &len
)) {
2079 if (tapeLoaderName
[0]) {
2080 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2081 memcpy(blkname
, tapeLoaderName
, 10);
2083 sprintf(blkname
, "c%04X:%04X", start
, len
);
2085 //printf(" block: %s\n", blkname);
2086 fWriteWord(fo
, 19, NULL
); // length of header
2088 fWriteByte(fo
, 0, &bxor
); // header block
2089 fWriteByte(fo
, 3, &bxor
); // 'code' flag
2090 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
2091 fWriteWord(fo
, len
, &bxor
);
2092 fWriteWord(fo
, start
, &bxor
);
2093 fWriteWord(fo
, 32768, &bxor
);
2094 fWriteByte(fo
, bxor
, NULL
);
2096 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
2098 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
2099 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
2100 fWriteByte(fo
, bxor
, NULL
);
2107 ///////////////////////////////////////////////////////////////////////////////
2108 // pseudoinstructions
2110 // note that processCurrentLine() will NOT skip to the next line before
2111 // calling pseudoinstruction handler!
2112 // note that processCurrentLine() will skip current line after calling
2113 // pseudoinstruction handler!
2115 static int wasOrg
= 0;
2116 static int wasClr
= 0;
2119 // ////////////////////////////////////////////////////////////////////////// //
2120 // print message using printf-like syntax
2121 // doesn't print new line
2122 static void processPrintf (FILE *fo
, const char *fmt
) {
2123 if (!fmt
|| !fmt
[0]) return;
2124 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
2125 char *tempstr
= NULL
;
2126 size_t tempsize
= 0;
2127 char *fmtcopy
= malloc(strlen(fmt
)+1);
2128 strcpy(fmtcopy
, fmt
);
2129 char *currfmt
= fmtcopy
;
2137 char *prcs
= strchr(currfmt
, '%');
2138 if (!prcs
|| !prcs
[1]) {
2139 /* no more formatting; print the tail and exit the loop */
2140 fprintf(fo
, "%s", currfmt
);
2145 if (prcs
[1] == '%') {
2149 /* print up to `prcs` */
2150 if (prcs
> currfmt
) {
2151 size_t partlen
= (ptrdiff_t)(prcs
-currfmt
);
2152 if (partlen
+1 > tempsize
) {
2153 tempsize
= ((partlen
+8)|0xff)+1;
2154 tempstr
= realloc(tempstr
, tempsize
);
2156 memcpy(tempstr
, currfmt
, partlen
);
2157 tempstr
[partlen
] = 0;
2158 fprintf(fo
, "%s", tempstr
);
2160 currfmt
= ++prcs
; /* skip percent */
2161 if (!docheck
) continue;
2167 if (*currfmt
== '+' || *currfmt
== '-') sign
= *currfmt
++;
2168 if (sign
!= '-' && *currfmt
== '0') zerofill
= 1;
2169 while (isDigit(*currfmt
)) { width
= width
*10+((*currfmt
)-'0'); ++currfmt
; }
2170 if (width
> 256) width
= 256;
2172 if (!ftype
) break; /* oops */
2173 if (!eatComma()) fatal("out of arguments for string format");
2175 case 's': /* string */
2177 switch (strSkipSpaces(currLine
)[0]) {
2178 case '"': case '\'': /* string literal? */
2179 strarg
= getStrArg(&stlen
);
2181 default: /* expression */
2182 strarg
= getStrExprArg();
2183 stlen
= (int)strlen(strarg
);
2187 if (sign
!= '-' && stlen
< width
) {
2188 int padlen
= width
-stlen
;
2189 memset(tempbuf
, ' ', padlen
);
2190 tempbuf
[padlen
+1] = 0;
2191 fprintf(fo
, "%s", tempbuf
);
2193 fprintf(fo
, "%s", strarg
);
2195 if (sign
== '-' && stlen
< width
) {
2196 int padlen
= width
-stlen
;
2197 memset(tempbuf
, ' ', padlen
);
2198 tempbuf
[padlen
+1] = 0;
2199 fprintf(fo
, "%s", tempbuf
);
2202 case 'd': /* decimal */
2204 exprval
= getExprArg(&defined
, NULL
);
2205 if (width
&& zerofill
) {
2206 fprintf(fo
, "%0*d", width
, exprval
);
2208 fprintf(fo
, "%*d", (sign
!= '-' ? width
: -width
), exprval
);
2210 fprintf(fo
, "%d", exprval
);
2213 case 'c': /* char */
2215 exprval
= getExprArg(&defined
, NULL
);
2216 if (exprval
<= 0 || exprval
== '?' || exprval
> 255) exprval
= '?';
2218 fprintf(fo
, "%*c", (sign
!= '-' ? width
: -width
), exprval
);
2220 fprintf(fo
, "%c", exprval
);
2223 case 'u': /* unsigned */
2225 exprval
= getExprArg(&defined
, NULL
);
2226 if (width
&& zerofill
) {
2227 fprintf(fo
, "%0*u", width
, (unsigned)(exprval
&0xffff));
2229 fprintf(fo
, "%*u", (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
2231 fprintf(fo
, "%u", (unsigned)(exprval
&0xffff));
2234 case 'x': case 'X': /* hex */
2236 exprval
= getExprArg(&defined
, NULL
);
2237 if (width
&& zerofill
) {
2238 fprintf(fo
, (ftype
== 'x' ? "%0*x" : "%0*X"), width
, (unsigned)(exprval
&0xffff));
2240 fprintf(fo
, (ftype
== 'x' ? "%*x" : "%*X"), (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
2242 fprintf(fo
, (ftype
== 'x' ? "%x" : "%X"), (unsigned)(exprval
&0xffff));
2246 if (ftype
<= 0 || ftype
== 127) ftype
= '?';
2247 fatal("invalid format specifier: '%c'", ftype
);
2251 if (tempstr
) free(tempstr
);
2256 ///////////////////////////////////////////////////////////////////////////////
2259 static int piERROR (void) {
2261 char *res
= getStrArg(&len
);
2262 fprintf(stdout
, "*** USER ERROR: ");
2263 processPrintf(stdout
, res
);
2264 fputc('\n', stdout
);
2265 fatal("user error abort");
2266 return PI_SKIP_LINE
;
2270 static int piWARNING (void) {
2272 char *res
= getStrArg(&len
);
2273 fprintf(stdout
, "*** USER WARNING ");
2275 fprintf(stdout
, "at file %s, line %d: ", curSrcLine
->fname
, curSrcLine
->lineNo
);
2277 fprintf(stdout
, "somewhere in time: ");
2279 processPrintf(stdout
, res
);
2280 fputc('\n', stdout
);
2281 return PI_SKIP_LINE
;
2285 ///////////////////////////////////////////////////////////////////////////////
2288 static int piPrintfCommon (int passNo
) {
2289 if (passNo
< 0 || pass
== passNo
) {
2291 char *res
= getStrArg(&len
);
2292 processPrintf(stdout
, res
);
2293 fputc('\n', stdout
);
2295 return PI_SKIP_LINE
;
2299 static int piDISPLAYX (int passNo
, int asHex
) {
2303 char *res
= getStrArg(&len
);
2305 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
2308 int32_t v
= getExprArg(&defined
, NULL
);
2310 if (passNo
< 0 || pass
== passNo
) {
2311 if (asHex
) printf("%04X", (unsigned int)v
);
2312 else printf("%d", v
);
2315 if (!eatComma()) break;
2317 return PI_SKIP_LINE
;
2321 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
2322 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
2323 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
2324 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
2325 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
2326 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
2328 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
2329 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
2330 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
2333 ///////////////////////////////////////////////////////////////////////////////
2336 static int piORG (void) {
2338 int32_t res
= getOneExprArg(&defined
, NULL
);
2340 if (!defined
) fatal("sorry, ORG operand value must be known here");
2341 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
2342 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
2347 if (!wasClr
&& res
> 0) clrAddr
= res
-1;
2349 return PI_CONT_LINE
;
2353 static int piDISP (void) {
2355 int32_t res
= getOneExprArg(&defined
, NULL
);
2357 if (!defined
) fatal("sorry, DISP operand value must be known here");
2358 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
2359 //printf("DISP=%d\n", res);
2361 return PI_CONT_LINE
;
2365 static int piENDDISP (void) {
2366 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
2369 return PI_CONT_LINE
;
2373 static int piENT (void) {
2375 int32_t res
= getOneExprArg(&defined
, NULL
);
2377 //if (!defined) fatal("sorry, ENT operand value must be known here");
2378 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
2380 return PI_CONT_LINE
;
2384 static int piCLR (void) {
2386 int32_t res
= getOneExprArg(&defined
, NULL
);
2388 //if (!defined) fatal("sorry, CLR operand value must be known here");
2389 if (res
< 0 || res
> 65535) fatal("invalid CLR operand value: %d", res
);
2392 return PI_CONT_LINE
;
2396 static int piRESERVE (void) {
2398 int defined = 1, start;
2399 int32_t res = getExprArg(&defined, NULL);
2400 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2401 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2403 if (!eatComma()) fatal("RESERVE needs 2 args");
2404 res = getOneExprArg(&defined, NULL);
2405 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2406 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2407 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
2409 fatal("RESERVE: not yet!");
2410 return PI_CONT_LINE
;
2414 static int piALIGN (void) {
2418 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
2419 res
= getOneExprArg(&defined
, NULL
);
2420 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
2421 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
2422 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
2423 if (res
> 0 && pc
%res
!= 0) {
2429 return PI_CONT_LINE
;
2433 static int piDISPALIGN (void) {
2435 int32_t res
= getOneExprArg(&defined
, NULL
);
2437 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
2438 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
2439 if (res
> 0 && disp
%res
!= 0) {
2444 return PI_CONT_LINE
;
2448 ///////////////////////////////////////////////////////////////////////////////
2451 static int defIncr
= 0;
2454 static int piDEFINCR (void) {
2456 int32_t cnt
= getOneExprArg(&defined
, NULL
);
2458 if (!defined
) fatal("DEFINCR: increment must be defined");
2460 return PI_CONT_LINE
;
2464 static int piDEFBW (int isWord
) {
2466 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2470 char *res
= getStrArg(&len
);
2472 for (f
= 0; f
< len
; ++f
) {
2473 int32_t b
= (uint8_t)res
[f
];
2475 if (b
< -128 || b
> 255) fatal("invalid operand value: %d", b
);
2476 if (b
< 0) b
+= 256;
2480 int32_t res
= getExprArg(&defined
, &fixuptype
);
2482 if (pass
> 0 && !defined
) fatal("undefined operand");
2485 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
2486 if (res
< 0) res
+= 65536;
2488 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 2);
2492 switch (fixuptype
) {
2493 case UR_FIXUP_WORD
: /* swapped bytes */
2494 urasm_fixup_operand(NULL
, pc
, disp
, UR_FIXUP_HIBYTE
, 1);
2495 urasm_fixup_operand(NULL
, pc
, disp
+1, UR_FIXUP_LOBYTE
, 1);
2497 case UR_FIXUP_LOBYTE
:
2498 case UR_FIXUP_HIBYTE
:
2499 warningMsg("non-word fixup for reversed word; wtf?!");
2506 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
2507 if (fixuptype
!= UR_FIXUP_NONE
) {
2508 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
2509 urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
2511 if (res
< 0) res
+= 256;
2515 if (!eatComma()) break;
2517 return PI_CONT_LINE
;
2520 static int piDEFB (void) { return piDEFBW(0); }
2521 static int piDEFW (void) { return piDEFBW(1); }
2522 static int piDEFR (void) { return piDEFBW(2); }
2525 static int piDEFS (void) {
2528 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2529 int32_t res
= getExprArg(&defined
, &fixuptype
);
2531 if (pass
> 0 && !defined
) fatal("undefined operand");
2532 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
2533 if (*currLine
&& currLine
[0] == ',') {
2535 bt
= getExprArg(&defined
, NULL
);
2536 if (pass
> 0 && !defined
) fatal("undefined operand");
2538 if (bt
< -128 || bt
> 255) fatal("invalid operand value: %d", res
);
2539 if (bt
< 0) bt
+= 256;
2540 if (fixuptype
!= UR_FIXUP_NONE
) {
2541 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
2543 for (f
= 0; f
< res
; ++f
) {
2544 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
2548 pc
+= res
; disp
+= res
;
2550 if (!eatComma()) break;
2552 return PI_CONT_LINE
;
2556 /* bit 0: put '\0' */
2557 /* bit 1: set bit 7 of last byte */
2558 /* bit 2: put length */
2559 static int piDEFSTR (int type
) {
2563 char *res
= getStrArg(&len
);
2566 if (len
> 255) fatal("string too long");
2569 for (f
= 0; f
< len
; ++f
) {
2570 uint8_t b
= (uint8_t)res
[f
];
2572 if ((type
&0x02) && f
== len
-1) b
|= 0x80;
2575 if (type
&0x01) emitByte(0);
2578 int32_t v
= getExprArg(&defined
, NULL
);
2580 if (pass
> 0 && !defined
) fatal("undefined expression");
2581 if (!defined
) v
= 0; else v
+= defIncr
;
2582 if (v
< -128 || v
> 255) fatal("invalid operand value: %d", v
);
2583 if (v
< 0) v
+= 256;
2586 if (!eatComma()) break;
2588 return PI_CONT_LINE
;
2592 static int piDEFM (void) { return piDEFSTR(0x00); }
2593 static int piDEFZ (void) { return piDEFSTR(0x01); }
2594 static int piDEFX (void) { return piDEFSTR(0x02); }
2595 static int piDEFC (void) { return piDEFSTR(0x04); }
2598 ///////////////////////////////////////////////////////////////////////////////
2601 /* INCBIN "name"[,maxlen] */
2602 static int piINCBIN (void) {
2608 char *args
= currLine
;
2610 if (!currLine
[0]) fatal("INCBIN without file name");
2614 } else if (currLine
[0] == '<') {
2621 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
2622 if (!fn
[0]) fatal("INCBIN: empty file name");
2623 memmove(currLine
, args
, strlen(args
)+1);
2625 if (currLine
[0] == ',') {
2628 maxlen
= getOneExprArg(&defined
, NULL
);
2629 if (!defined
) fatal("INCBIN: undefined maxlen");
2630 if (maxlen
< 1) return 1; // nothing to do
2634 sprintf(currLine
, "%s/%s", sysIncludeDir
, fn
);
2636 sprintf(currLine
, "%s", fn
);
2639 fl
= fopen(currLine
, "rb");
2640 if (!fl
) fatal("INCBIN: file not found: %s", currLine
);
2641 while (maxlen
-- > 0) {
2642 int res
= fread(&bt
, 1, 1, fl
);
2645 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: %s", currLine
); }
2649 return PI_SKIP_LINE
;
2653 ///////////////////////////////////////////////////////////////////////////////
2656 /* INCLUDE "name" */
2657 static int piINCLUDE (void) {
2660 char *args
= currLine
;
2662 if (!currLine
[0]) fatal("INCLUDE without file name");
2666 } else if (currLine
[0] == '<') {
2673 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
2674 if (!fn
[0]) fatal("INCLUDE: empty file name");
2676 if (asmTextInclude(fn
, system
) != 0) fatal("INCLUDE: some shit happens!");
2677 return PI_SKIP_LINE
;
2681 ///////////////////////////////////////////////////////////////////////////////
2682 // MODULE, ENDMODULE
2684 static int piENDMODULE (void) {
2685 if (!curModule
) fatal("ENDMODULE without MODULE");
2687 char *mn
= getOneLabelArg();
2688 if (strcmp(mn
, curModule
->name
)) fatal("invalid module name in ENDMODULE: %s", mn
);
2691 return PI_SKIP_LINE
;
2695 static int piMODULE (void) {
2698 SourceLine
*ol
= curSrcLine
;
2701 if (curModule
) fatal("no nested modules allowed");
2702 mn
= getOneLabelArg();
2703 if (!urasm_is_valid_name(mn
)) fatal("invalid module name: %s", mn
);
2704 mi
= moduleFind(mn
);
2705 //printf("[%s] %p\n", mn, mi);
2708 if (strcmp(mi
->fname
, curSrcLine
->fname
)) fatal("duplicate module definition; previous was in %s", mi
->fname
);
2710 nextSrcLine(); /* skip "MODULE" line */
2711 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
2713 fatal("no ENDMODULE");
2715 if (inum
== 0) fatal("no nested modules allowed");
2717 //skipInstruction(); //k8:wtf?!
2718 return piENDMODULE();
2721 mi
= moduleAdd(mn
, curSrcLine
->fname
);
2725 return PI_SKIP_LINE
;
2730 static int piMODEL (void) {
2731 char *mn
= getOneIdArgLo();
2732 if (strSkipSpaces(currLine
)[0]) fatal("only one model name expected");
2733 if (strcmp(mn
, "z80") == 0) urasm_allow_zxnext
= 0;
2734 else if (strcmp(mn
, "z80a") == 0) urasm_allow_zxnext
= 0;
2735 else if (strcmp(mn
, "z80n") == 0) urasm_allow_zxnext
= 1;
2736 else if (strcmp(mn
, "z80next") == 0) urasm_allow_zxnext
= 1;
2737 else if (strcmp(mn
, "zxnext") == 0) urasm_allow_zxnext
= 1;
2738 else fatal("invalid model name: %s", mn
);
2739 return PI_SKIP_LINE
;
2743 ///////////////////////////////////////////////////////////////////////////////
2746 static int piEDUP (void) {
2747 fatal("EDUP without DUP");
2749 return PI_SKIP_LINE
;
2753 static int piDUP (void) {
2754 int defined
= 1, dupCnt
= 1, inum
;
2755 SourceLine
*stline
, *eline
= NULL
;
2756 int32_t cnt
= getOneExprArg(&defined
, NULL
);
2758 if (!defined
) fatal("DUP: counter must be defined");
2759 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
2760 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
2761 // now find corresponding EDUP
2762 // note that we should skip nested DUPs
2763 nextSrcLine(); // skip ourself
2764 stline
= curSrcLine
;
2765 while (curSrcLine
) {
2766 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "EDUP", NULL
))) break;
2767 // ok, we found something; what is it?
2771 nextSrcLine(); // skip DUP
2774 if (--dupCnt
== 0) {
2779 nextSrcLine(); // skip EDUP
2782 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
2783 // now repeat that lines
2785 setCurSrcLine(stline
);
2786 while (curSrcLine
!= eline
) processCurrentLine();
2788 return PI_SKIP_LINE
;
2792 ///////////////////////////////////////////////////////////////////////////////
2795 static int ifCount
= 0;
2799 // -1: error (should not happen)
2800 // 0: successfully complete
2801 // 1: stopped *AT* "ELSE"
2802 // 2: stopped *AT* "ELSEIF"
2803 static int ifSkipToEndIfOrElse (int wholeBody
) {
2804 int inum
, wasElse
= 0;
2807 while (curSrcLine
) {
2808 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL
))) break;
2812 nextSrcLine(); // skip IF
2813 ifSkipToEndIfOrElse(1); // and recurse
2814 nextSrcLine(); // skip ENDIF
2817 if (wasElse
) fatal("duplicate ELSE");
2818 if (!wholeBody
) return 1;
2820 nextSrcLine(); // skip ELSE
2821 break; // and continue
2823 return 0; // and exit
2826 if (wasElse
) fatal("ELSEIF in ELSE");
2827 if (!wholeBody
) return 2;
2828 nextSrcLine(); // skip ELSEIF
2829 break; // and continue
2831 // skip it as a whole
2832 nextSrcLine(); // skip MACRO
2835 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) { setCurSrcLine(oline
); fatal("invalid MACRO"); }
2836 if (inum
== 1) break;
2837 fatal("invalid nested MACRO");
2839 nextSrcLine(); // skip ENDM
2842 fatal("unexpected ENDM");
2845 fatal("IF without ENDIF");
2850 static int piENDIF (void) {
2851 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2852 if (--ifCount
< 0) fatal("ENDIF without IF");
2854 return PI_SKIP_LINE
;
2858 static int piELSE (void) {
2859 if (--ifCount
< 0) fatal("ELSE without IF");
2860 nextSrcLine(); // skip ELSE
2861 ifSkipToEndIfOrElse(1);
2862 return PI_SKIP_LINE
;
2865 static int piELSEIF (void) {
2866 if (--ifCount
< 0) fatal("ELSEIF without IF");
2867 nextSrcLine(); // skip ELSEIF
2868 ifSkipToEndIfOrElse(1);
2869 return PI_SKIP_LINE
;
2873 static int piELSEIFX (void) {
2874 if (--ifCount
< 0) fatal("ELSEIFX without IF");
2875 nextSrcLine(); // skip ELSEIFX
2876 ifSkipToEndIfOrElse(1);
2877 return PI_SKIP_LINE
;
2881 static int piIFAll (int isIfX
) {
2883 int ooo
= lblOptMakeU2
;
2884 lblOptMakeU2
= (isIfX
? 1 : 0);
2885 int32_t cond
= getOneExprArg(&defined
, NULL
);
2889 if (!isIfX
) fatal("IF: condition must be defined");
2890 cond
= 0; // for IFX: 0 if there is any undefined label
2893 // ok, do it until ELSE/ELSEIF/ENDIF
2895 return PI_SKIP_LINE
;
2901 nextSrcLine(); // skip last instruction
2902 // skip until ELSE/ELSEIF/ENDIF
2903 r
= ifSkipToEndIfOrElse(0);
2904 /*!fprintf(stderr, "ELSEIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2905 if (r
== 0) break; // ENDIF
2906 if (r
== 1) { ++ifCount
; break; } // ELSE
2907 // ELSEIF, do condition
2908 args
= strIsCommand("ELSEIF", currLine
);
2912 if ((args
= strIsCommand("ELSEIFX", currLine
)) == NULL
) fatal("internal error in conditionals"); // the thing that should not be
2915 memmove(currLine
, args
, strlen(args
)+1);
2916 /*!fprintf(stderr, "ELSEIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2917 cond
= getOneExprArg(&defined
, NULL
);
2918 /*!fprintf(stderr, "ELSEIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0), cond);*/
2920 if (!isIfX
) fatal("ELSEIF: condition must be defined");
2921 cond
= 0; // for IFX: 0 if there is any undefined label
2923 if (cond
) { ++ifCount
; break; } // condition is true
2925 return PI_SKIP_LINE
;
2929 static int piIF (void) { return piIFAll(0); }
2930 static int piIFX (void) { return piIFAll(1); }
2933 ///////////////////////////////////////////////////////////////////////////////
2935 ///////////////////////////////////////////////////////////////////////////////
2937 what i did with MACRO is the brain-damaged cheating all the way.
2939 first, i will collect the MACRO body and remember it (removing it
2940 from the main source code, so second pass will not see it).
2942 second, when the macro is used, i will:
2943 * insert the whole macro body in place (label resolver will
2944 fix "..lbl" labels for us)
2945 * let the asm play with it
2947 this is not the best scheme, but it is fairly simple and it works.
2950 // will be called when parser encounters term starting with '=' or '*'
2951 // first term char will not be skipped
2952 // must return pointer to the first char after expression end
2953 static const char *getValueCB (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2957 if (curmacro
== NULL
) fatal("'=' outside of macro");
2958 if (*expr
++ != '=') fatal("'=' expected!");
2960 expr
= strSkipSpaces(expr
);
2962 if (isAlphaDigit(*expr
) || *expr
== '_') {
2963 if (p
-name
> 250) fatal("id too long");
2971 expr
= strSkipSpaces(expr
);
2972 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
2973 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
2976 int l
= (int)strlen(curmacro
->argvals
[f
]);
2978 urasm_exprval_init(&v
);
2979 expr
= urasm_expr_ex(&v
, expr
, addr
, &donteval
, defined
, error
);
2980 if (*error
) return expr
;
2981 if (expr
== NULL
|| *expr
!= ']' || v
.str
!= NULL
) { *error
= UR_EXPRERR_FUNC
; return expr
; }
2983 if (v
.val
< 0) v
.val
+= l
;
2984 if (v
.val
< 0 || v
.val
>= l
) {
2987 res
->val
= (unsigned char)(curmacro
->argvals
[f
][v
.val
]);
2991 urasm_expr_ex(res
, curmacro
->argvals
[f
], addr
, &donteval
, defined
, error
);
2997 fatal("unknown macro variable: '%s'", name
);
3001 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
3002 // opr starts with '=' (invariant)
3003 static int expandCB (char *opr
, int oprlen
) {
3004 char name
[257], *p
= name
, *op
= opr
;
3006 if (curmacro
== NULL
) fatal("'=' outside of macro");
3007 if (*op
++ != '=') fatal("'=' expected!"); // just in case
3008 //fprintf(stderr, "expand: [%s]\n", opr);
3010 if (!isAlpha(op
[0])) return 0; // nothing to do
3012 // copy argument name
3014 if (isAlphaDigit(*op
) || *op
== '_') {
3015 if (p
-name
> 250) fatal("id too long");
3023 // expand argument? we only need to expand `=arg[n]`
3024 op
= strSkipSpaces(op
);
3025 if (op
[0] != '[') return 0;
3027 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
3028 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
3029 if (*op
!= '[') abort(); // assertion, just in case
3030 // replace argument with character, or with literal value
3031 // used for `=regpair[0]` or `=regpair[]`
3034 const int l
= (int)strlen(curmacro
->argvals
[f
]);
3035 const int lleft
= (int)strlen(op
);
3036 char *tmp
= malloc(l
+lleft
+8);
3037 snprintf(tmp
, l
+lleft
+8, "%s%s", curmacro
->argvals
[f
], op
);
3038 if ((int)strlen(tmp
) > oprlen
) {
3047 int error
= 0, defined
= 1, donteval
= 0;
3049 urasm_exprval_init(&v
);
3050 op
= (char *)urasm_expr_ex(&v
, op
, pc
, &donteval
, &defined
, &error
);
3051 if (error
) return -1;
3052 // result should be a number
3053 if (op
== NULL
|| *op
!= ']' || v
.str
!= NULL
) return -1;
3055 // it is guaranteed to have more than one char in opr
3056 // so we can simply put char from argument value, and remove other expression chars
3057 const int l
= (int)strlen(curmacro
->argvals
[f
]);
3058 if (v
.val
< 0) v
.val
+= l
; // negative: indexing from the end
3059 if (v
.val
< 0 || v
.val
>= l
) fatal("index %d is out of bounds for macro argument '%s'", v
.val
, curmacro
->mac
->argnames
[f
]);
3061 opr
[0] = curmacro
->argvals
[f
][v
.val
];
3062 // remove other chars
3063 memmove(opr
+1, op
, strlen(op
)+1);
3069 fatal("unknown macro variable: '%s'", name
);
3073 // main macro expander
3074 static void processMacro (MacroDef
*mc
) {
3075 SourceLine
*oldcurline
= curSrcLine
;
3078 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
3079 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
3081 // parse macro arguments
3083 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) cm
.argvals
[f
] = NULL
;
3087 // do we have more arguments?
3088 if (!currLine
[0]) break;
3089 // check for argument name (this is purely cosmetic thing)
3090 // use like this: "mcall :argname=[value]" (value may be omited for default)
3091 if (currLine
[0] == ':' && isAlpha(currLine
[1])) {
3093 while (isAlphaDigit(currLine
[pos
]) || currLine
[pos
] == '_') ++pos
;
3095 const char svch
= currLine
[pos
];
3097 if (strcasecmp(currLine
+1, mc
->argnames
[currArg
]) != 0) {
3098 // out-of-order, find proper argument and rewind to it
3100 for (int c
= 0; c
< mc
->argc
; ++c
) {
3101 if (strcasecmp(currLine
+1, mc
->argnames
[c
]) == 0) {
3106 if (currArg
< 0) fatal("macro '%s' has no argument named '%s'", mc
->name
, currLine
+1);
3108 currLine
[pos
] = svch
;
3109 // remove argument name
3110 memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
3113 if (currLine
[0] != '=') fatal("expected '=' after argument name");
3115 memmove(currLine
, currLine
+1, strlen(currLine
));
3119 if (currArg
>= mc
->argc
) fatal("too many arguments to macro '%s'", mc
->name
);
3120 // check for default value
3121 if (currLine
[0] == ',') {
3122 if (mc
->argdefaults
[currArg
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[currArg
]);
3123 cm
.argvals
[currArg
] = strdup(mc
->argdefaults
[currArg
]);
3124 memmove(currLine
, currLine
+1, strlen(currLine
));
3126 // skip argument (so we will know its length, and will be able to copy it)
3127 char *e
= skipMacroArg(currLine
);
3128 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
3129 const char ech
= *e
;
3131 if (ech
== ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc
->name
);
3132 if (ech
!= ',') fatal("invalid invocation of macro '%s'", mc
->name
);
3135 cm
.argvals
[currArg
] = strdup(currLine
);
3136 // strip trailing spaces
3137 strTrimRight(cm
.argvals
[currArg
]);
3140 memmove(currLine
, e
+1, strlen(e
));
3147 // check for line end
3149 if (currLine
[0]) fatal("invalid macro invocation");
3151 // setup default argument values
3152 for (int f
= 0; f
< mc
->argc
; ++f
) {
3153 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
3154 if (cm
.argvals
[f
]) continue;
3155 if (mc
->argdefaults
[f
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[f
]);
3156 cm
.argvals
[f
] = strdup(mc
->argdefaults
[f
]);
3157 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
3160 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
3162 // insert macro code into the source
3163 setCurSrcLine(mc
->lines
);
3166 while (curSrcLine
!= NULL
) {
3167 //fprintf(stderr, "*[%s] (%d)\n", curSrcLine->line, curSrcLine->lineNo);
3168 processCurrentLine();
3171 setCurSrcLine(oldcurline
);
3177 static int piMACRO (void) {
3180 char *argdefaults
[MAX_MACRO_ARGS
];
3181 char *argnames
[MAX_MACRO_ARGS
];
3182 SourceLine
*stline
, *eline
= NULL
;
3185 name
= strdup(getLabelArg(0));
3186 //fprintf(stderr, "[%s]\n", name);
3187 if (findMacro(name
) != NULL
) fatal("duplicate MACRO definition: '%s'", name
);
3188 if (currLine
[0] == ',') memmove(currLine
, currLine
+1, strlen(currLine
));
3191 while (currLine
[0]) {
3192 if (argc
>= MAX_MACRO_ARGS
) fatal("too many arguments in MACRO");
3193 if (!isAlpha(currLine
[0])) fatal("invalid MACRO definition");
3194 argnames
[argc
] = strdup(getLabelArg(0));
3195 if (currLine
[0] == '=') {
3197 char *e
= strchr(currLine
, ','), tch
;
3199 if (e
== NULL
) e
= currLine
+strlen(currLine
);
3202 argdefaults
[argc
] = strdup(currLine
+1);
3204 memmove(currLine
, e
, strlen(e
)+1);
3206 argdefaults
[argc
] = NULL
;
3209 if (currLine
[0] == ',') {
3210 memmove(currLine
, currLine
+1, strlen(currLine
));
3213 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
3217 // now find corresponding ENDM
3218 // note that we should skip nested DUPs
3219 stline
= curSrcLine
;
3220 nextSrcLine(); // skip ourself
3221 while (curSrcLine
) {
3224 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) break;
3225 // ok, we found something; what is it?
3228 fatal("no nested MACROs yet");
3234 nextSrcLine(); // skip ENDM
3238 if (!eline
) { setCurSrcLine(stline
); fatal("no ENDM"); }
3240 if ((mc
= calloc(1, sizeof(*mc
))) == NULL
) fatal("out of memory");
3243 for (int f
= 0; f
< argc
; ++f
) { mc
->argdefaults
[f
] = argdefaults
[f
]; mc
->argnames
[f
] = argnames
[f
]; }
3246 mc
->lines
= stline
->next
;
3247 stline
->next
= curSrcLine
;
3248 stline
->line
[0] = 0;
3252 setCurSrcLine(stline
);
3253 return PI_SKIP_LINE
;
3257 static int piENDM (void) {
3258 fatal("ENDM without MACRO");
3259 return PI_SKIP_LINE
;
3263 ///////////////////////////////////////////////////////////////////////////////
3265 static int optWriteType
= 't';
3266 static int optWTChanged
= 0;
3269 static void piTapParseLoaderName (void) {
3272 if (!isStrArg()) fatal("loader name expected");
3273 char *fn
= getStrArg(&len
);
3274 if (len
> 10) fatal("loader name too long");
3275 memset(tapeLoaderName
, ' ', 10);
3276 for (int f
= 0; f
< len
; ++f
) tapeLoaderName
[f
] = fn
[f
];
3281 static int piMATHMODE (void) {
3282 char *name
= getOneLabelArg();
3283 if (strcasecmp(name
, "OLD") == 0) urasm_use_old_priorities
= 1;
3284 else if (strcasecmp(name
, "NEW") == 0) urasm_use_old_priorities
= 0;
3285 else fatal("invalid math mode; NEW or OLD expected");
3286 return PI_SKIP_LINE
;
3290 static int piDEFFMT (void) {
3295 name
= getStrArg(&len
);
3297 name
= getLabelArg(1);
3299 if (optWTChanged
) return 1;
3301 optRunDMB
= optRunTape
= optRunSCL
= 0;
3304 if (!strcasecmp(name
, "SNA") || !strcasecmp(name
, "RUNSNA") || !strcasecmp(name
, "SNARUN")) {
3306 if (currLine
[0]) fatal("too many expressions");
3307 return PI_SKIP_LINE
;
3309 if (!strcasecmp(name
, "TAP") || !strcasecmp(name
, "TAPE")) {
3311 piTapParseLoaderName();
3312 return PI_SKIP_LINE
;
3314 if (!strcasecmp(name
, "RUNTAP") || !strcasecmp(name
, "RUNTAPE") || !strcasecmp(name
, "TAPERUN")) {
3317 piTapParseLoaderName();
3318 return PI_SKIP_LINE
;
3320 if (!strcasecmp(name
, "BIN") || !strcasecmp(name
, "RAW")) {
3322 if (currLine
[0]) fatal("too many expressions");
3323 return PI_SKIP_LINE
;
3325 if (!strcasecmp(name
, "DMB") || !strcasecmp(name
, "RUNDMB") || !strcasecmp(name
, "DMBRUN")) {
3326 optRunDMB
= (name
[3] != 0);
3328 if (currLine
[0]) fatal("too many expressions");
3329 return PI_SKIP_LINE
;
3331 if (!strcasecmp(name
, "NONE") || !strcasecmp(name
, "NOTHING")) {
3333 if (currLine
[0]) fatal("too many expressions");
3334 return PI_SKIP_LINE
;
3336 if (!strcasecmp(name
, "SCL") || !strcasecmp(name
, "RUNSCL") || !strcasecmp(name
, "SCLRUN")) {
3338 optRunSCL
= (name
[3] != 0);
3339 piTapParseLoaderName();
3340 return PI_SKIP_LINE
;
3342 fatal("invalid default output type: %s", name
);
3346 ///////////////////////////////////////////////////////////////////////////////
3349 static void processCurrentLine (void) {
3350 if (!curSrcLine
) return; // do nothing
3354 char *str
, *ee
, name
[66];
3359 removeSpacesAndColons();
3360 // skip spaces and ':'
3361 if (!currLine
[0]) { nextSrcLine(); break; }
3362 // try to find and process command
3363 str
= currLine
; //while (*str && isSpace(*str)) ++str; // skip spaces
3365 for (ee
= str
; *ee
&& !isSpace(*ee
) && *ee
!= '"' && *ee
!= '\'' && *ee
!= ',' && *ee
!= ':'; ++ee
) {}
3366 // get command, if any
3367 if (ee
!= str
&& ee
-str
<= 64) {
3369 memset(name
, 0, sizeof(name
));
3370 memmove(name
, str
, ee
-str
);
3371 /* known command? */
3372 op
= urFindOp(name
);
3375 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
3376 memmove(currLine
, str
, strlen(str
)+1);
3378 nextSrcLine(); // skip it
3384 if ((mc
= findMacro(name
)) != NULL
) {
3386 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
3387 memmove(currLine
, str
, strlen(str
)+1);
3389 /* only one macro per line! */
3394 len
= urasm_opasm(currLine
, pc
, disp
, &errpos
);
3395 if (len
< 0) fatalUrLib(len
);
3398 if (len
>= 0 && errpos
) {
3399 memmove(currLine
, errpos
+1, strlen(errpos
));
3401 nextSrcLine(); // skip it
3408 ///////////////////////////////////////////////////////////////////////////////
3409 // setup instructions
3411 static void registerInstructions (void) {
3412 urAddOp("DISPLAY", piDISPLAY
);
3413 urAddOp("DISPLAY0", piDISPLAY0
);
3414 urAddOp("DISPLAYA", piDISPLAYA
);
3415 urAddOp("DISPHEX", piDISPHEX
);
3416 urAddOp("DISPHEX0", piDISPHEX0
);
3417 urAddOp("DISPHEXA", piDISPHEXA
);
3419 urAddOp("DEFFMT", piDEFFMT
);
3420 urAddOp("$MODEL", piMODEL
);
3422 urAddOp("MACRO", piMACRO
);
3423 urAddOp("ENDM", piENDM
);
3425 urAddOp("ORG", piORG
);
3426 urAddOp("DISP", piDISP
);
3427 urAddOp("ENDDISP", piENDDISP
);
3428 urAddOp("PHASE", piDISP
);
3429 urAddOp("DEPHASE", piENDDISP
);
3430 urAddOp("UNPHASE", piENDDISP
);
3431 urAddOp("ALIGN", piALIGN
);
3432 urAddOp("DISPALIGN", piDISPALIGN
);
3433 urAddOp("PHASEALIGN", piDISPALIGN
);
3434 urAddOp("ENT", piENT
);
3435 urAddOp("CLR", piCLR
);
3436 urAddOp("RESERVE", piRESERVE
);
3438 urAddOp("INCLUDE", piINCLUDE
);
3439 urAddOp("INCBIN", piINCBIN
);
3441 urAddOp("MODULE", piMODULE
);
3442 urAddOp("ENDMODULE", piENDMODULE
);
3444 urAddOp("DUP", piDUP
);
3445 urAddOp("EDUP", piEDUP
);
3447 urAddOp("IF", piIF
);
3448 urAddOp("IFX", piIFX
);
3449 urAddOp("ELSE", piELSE
);
3450 urAddOp("ELSEIF", piELSEIF
);
3451 urAddOp("ELSEIFX", piELSEIFX
);
3452 urAddOp("ENDIF", piENDIF
);
3454 urAddOp("DEFINCR", piDEFINCR
);
3455 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
3456 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
3457 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
3458 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
3459 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
3460 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
3461 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
3462 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
3464 urAddOp("$ERROR", piERROR
);
3465 urAddOp("$WARNING", piWARNING
);
3467 urAddOp("$PRINTF", piPRINTF
);
3468 urAddOp("$PRINTF0", piPRINTF0
);
3469 urAddOp("$PRINTFA", piPRINTFA
);
3471 urAddOp("$MATHMODE", piMATHMODE
);
3475 ///////////////////////////////////////////////////////////////////////////////
3476 // !0: invalid label
3478 static inline void fnSkipSpaces (const char *expr) {
3479 while (*expr && isSpace(*expr)) ++expr;
3485 static inline char fnNextChar (const char *expr
) {
3486 while (*expr
&& isSpace(*expr
)) ++expr
;
3491 #define FN_SKIP_BLANKS do { \
3492 while (*expr && isSpace(*expr)) ++expr; \
3495 #define FN_CHECK_END do { \
3496 while (*expr && isSpace(*expr)) ++expr; \
3497 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
3502 #define FN_CHECK_COMMA do { \
3503 while (*expr && isSpace(*expr)) ++expr; \
3504 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
3509 static const char *readLabelName (char *buf
, const char *expr
) {
3512 while (*expr
&& isSpace(*expr
)) ++expr
;
3516 if (pos
>= 128) return NULL
;
3517 if (ch
== ')') { --expr
; break; }
3519 if (isAlphaDigit(ch
) || ch
== '$' || ch
== '.' || ch
== '_' || ch
== '@') {
3525 if (pos
< 1) return NULL
;
3527 if (!urasm_is_valid_name(buf
)) return NULL
;
3532 static const char *fnDefKn (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
, int qtype
) {
3535 if ((expr
= readLabelName(lbl
, expr
)) == NULL
) { *error
= UR_EXPRERR_LABEL
; return NULL
; }
3537 if (!donteval
) res
->val
= (isLabelDefinedOrKnown(lbl
, addr
, qtype
) != 0);
3541 static const char *fnDefined (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) { return fnDefKn(res
, expr
, addr
, donteval
, defined
, error
, UR_QTYPE_DEFINED
); }
3542 static const char *fnKnown (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) { return fnDefKn(res
, expr
, addr
, donteval
, defined
, error
, UR_QTYPE_KNOWN
); }
3545 static const char *fnAligned256 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3546 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3548 if (!donteval
) res
->val
= (res
->val
%256 ? 0 : 1);
3553 static const char *fnSameSeg (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3554 urasm_exprval_t v0
, v1
;
3556 urasm_exprval_init(&v0
);
3557 urasm_exprval_init(&v1
);
3558 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
3559 if (*error
) return expr
;
3561 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
3562 if (*error
) return expr
;
3564 if (!donteval
) res
->val
= (v0
.val
/256 == v1
.val
/256);
3569 static const char *fnAlign (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3570 urasm_exprval_t v0
, v1
;
3571 urasm_exprval_init(&v0
);
3572 urasm_exprval_init(&v1
);
3573 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
3574 if (*error
) return expr
;
3576 if (fnNextChar(expr
) == ',') {
3578 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
3579 if (*error
) return expr
;
3585 if (v1
.val
< 1) { *error
= UR_EXPRERR_FUNC
; return NULL
; }
3586 res
->val
= (v0
.val
+v1
.val
-1)/v1
.val
*v1
.val
;
3592 static const char *fnLow (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3593 const char *ee
= expr
;
3594 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3597 if (res
->fixuptype
== UR_FIXUP_HIBYTE
) {
3598 *error
= UR_EXPRERR_FUNC
; return ee
;
3600 res
->fixuptype
= UR_FIXUP_LOBYTE
;
3607 static const char *fnHigh (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3608 const char *ee
= expr
;
3609 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3612 if (res
->fixuptype
== UR_FIXUP_LOBYTE
) {
3613 *error
= UR_EXPRERR_FUNC
; return ee
;
3615 res
->fixuptype
= UR_FIXUP_HIBYTE
;
3616 res
->val
= (res
->val
>>8)&0xff;
3622 static const char *fnAbs (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3623 const char *ee
= expr
;
3624 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3627 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
3628 *error
= UR_EXPRERR_FUNC
; return ee
;
3630 res
->val
= abs(res
->val
);
3636 static const char *fnBSwap (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3637 const char *ee
= expr
;
3638 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3641 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
3642 *error
= UR_EXPRERR_FUNC
; return ee
;
3644 res
->val
= ((res
->val
>>8)&0xff)|((res
->val
&0xff)<<8);
3650 static const char *fnScrAddr8xn (int nmul
, urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3651 int32_t scrbase
= 0x4000;
3653 const char *ee
= expr
;
3654 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
3655 // first argument is `x`
3656 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3658 // second argument is `y`
3660 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3662 // optional third arg is screen base
3664 if (expr
[0] == ',') {
3666 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3667 scrbase
= res
->val
&0xffff;
3671 //urasm_exprval_clear(res);
3672 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
3673 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
3674 if (y
< 0 || y
> 191) fatal("invalid y coordinate: %d", y
/nmul
);
3680 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
3686 static const char *fnScrAddr8x1 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3687 return fnScrAddr8xn(1, res
, expr
, addr
, donteval
, defined
, error
);
3690 static const char *fnScrAddr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3691 return fnScrAddr8xn(8, res
, expr
, addr
, donteval
, defined
, error
);
3695 static const char *fnScrAttr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3696 int32_t scrbase
= 0x4000;
3698 const char *ee
= expr
;
3699 // first argument is `x`
3700 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3702 // second argument is `y`
3704 urasm_exprval_clear(res
);
3705 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3707 // optional third arg is screen base
3709 if (expr
[0] == ',') {
3711 urasm_exprval_clear(res
);
3712 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3713 scrbase
= res
->val
&0xffff;
3715 urasm_exprval_clear(res
);
3718 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
3719 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
3720 if (y
< 0 || y
> 23) fatal("invalid y coordinate: %d", y
);
3721 res
->val
= scrbase
+6144+y
*32+x
;
3727 static const char *fnMArgToStr (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3728 const char *ee
= expr
;
3729 // argument is macro argument name
3730 expr
= strSkipSpacesConst(expr
);
3731 if (expr
[0] != '=') { *error
= UR_EXPRERR_FUNC
; return ee
; }
3733 if (!isAlpha(expr
[0]) && expr
[0] != '_') { *error
= UR_EXPRERR_FUNC
; return ee
; }
3734 const char *nend
= expr
+1;
3735 while (isAlphaDigit(*nend
) || *nend
== '_') ++nend
;
3736 if (nend
-expr
> 127) { *error
= UR_EXPRERR_FUNC
; return ee
; }
3738 memset(name
, 0, sizeof(name
));
3739 memcpy(name
, expr
, nend
-expr
);
3744 expr
= strSkipSpacesConst(expr
);
3745 if (expr
[0] == '[') {
3746 expr
= strSkipSpacesConst(expr
+1);
3747 if (expr
[0] != ']') {
3750 if (expr
[0] == '+') ++expr
;
3751 else if (expr
[0] == '-') { doneg
= 1; ++expr
; }
3753 while (isDigit(expr
[0])) {
3754 index
= index
*10+(expr
[0]-'0');
3755 if (index
>= 0x1fffffff) fatal("`margtostr` index too high");
3758 expr
= strSkipSpacesConst(expr
);
3759 if (doneg
) index
= -index
;
3761 if (expr
[0] != ']') fatal("`margtostr` invalid index syntax");
3766 if (curmacro
== NULL
) fatal("`margtostr` outside of macro");
3768 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
3769 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
3770 // found argument, convert it to string
3772 res
->str
= realloc(res
->str
, strlen(curmacro
->argvals
[f
])+1);
3773 strcpy(res
->str
, curmacro
->argvals
[f
]);
3775 const size_t slen
= strlen(curmacro
->argvals
[f
]);
3778 if (index
> slen
) fatal("index out of string");
3779 index
= (int)slen
-index
;
3781 if (index
>= slen
) fatal("index out of string");
3782 res
->str
= realloc(res
->str
, 2);
3783 res
->str
[0] = curmacro
->argvals
[f
][index
];
3787 for (char *s
= res
->str
; *s
; ++s
) *s
= toLower(*s
);
3788 //fprintf(stderr, "<%s>\n", res->str);
3791 res
->val
= ((unsigned char)res
->str
[0]);
3792 res
->val
|= ((unsigned char)res
->str
[1])<<8;
3794 res
->val
= (unsigned char)res
->str
[0];
3802 fatal("unknown macro argument '%s'", name
);
3808 static const char *fnStrLen (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3809 expr
= strSkipSpacesConst(expr
);
3812 case '"': case '\'': /* string literal? */
3814 char *a
= (char *)expr
+1;
3815 (void)parseStr(&a
, expr
[0], &stlen
);
3816 expr
= (const char *)a
;
3819 default: /* expression */
3822 urasm_exprval_init(&r
);
3823 expr
= urasm_expr_ex(&r
, expr
, disp
, &donteval
, defined
, error
);
3824 if (*error
) fatalUrLib(*error
);
3825 if (!r
.str
) fatal("string expected for `strlen()`");
3826 stlen
= (int)strlen(r
.str
);
3827 urasm_exprval_clear(&r
);
3833 urasm_exprval_setint(res
, stlen
);
3839 static void registerFunctions (void) {
3840 urasm_expr_register_func("defined", fnDefined
);
3841 urasm_expr_register_func("known", fnKnown
);
3842 urasm_expr_register_func("aligned256", fnAligned256
);
3843 urasm_expr_register_func("align", fnAlign
);
3844 urasm_expr_register_func("sameseg", fnSameSeg
);
3845 urasm_expr_register_func("low", fnLow
);
3846 urasm_expr_register_func("high", fnHigh
);
3847 urasm_expr_register_func("abs", fnAbs
);
3848 urasm_expr_register_func("bswap", fnBSwap
);
3850 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8
);
3851 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1
);
3852 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8
);
3854 urasm_expr_register_func("marg2str", fnMArgToStr
);
3855 urasm_expr_register_func("strlen", fnStrLen
);
3859 ///////////////////////////////////////////////////////////////////////////////
3860 // preparing another pass
3862 static void initPass (void) {
3863 curSrcLine
= asmText
;
3876 lastSeenGlobalLabel
= strdup(" [MAIN] ");
3879 urasm_use_old_priorities
= 0;
3883 static int posstPass (void) {
3884 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
); lastSeenGlobalLabel
= NULL
;
3885 if (checkLabels()) return -1;
3886 if (ifCount
!= 0) fatal("unbalanced IFs");
3891 ///////////////////////////////////////////////////////////////////////////////
3892 static int labelCmp (const void *aa
, const void *bb
) {
3893 if (aa
== bb
) return 0;
3894 const UrLabelInfo
*a
= *(const UrLabelInfo
**)aa
;
3895 const UrLabelInfo
*b
= *(const UrLabelInfo
**)bb
;
3897 a
->value
< b
->value
? -1 :
3898 a
->value
> b
->value
? 1 :
3903 static void writeLabelsFile (const char *fname
) {
3904 if (!fname
|| !fname
[0]) return;
3907 for (const UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) ++lcount
;
3908 UrLabelInfo
**larr
= NULL
;
3910 // create labels array
3911 larr
= (UrLabelInfo
**)malloc(sizeof(UrLabelInfo
*)*lcount
);
3913 for (UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
, ++lcount
) larr
[lcount
] = ll
;
3915 qsort(larr
, lcount
, sizeof(UrLabelInfo
*), &labelCmp
);
3918 FILE *fo
= fopen(fname
, "w");
3920 for (int f
= 0; f
< lcount
; ++f
) {
3921 UrLabelInfo
*ll
= larr
[f
];
3922 if (!ll
->name
|| (!isAlpha(ll
->name
[0]) && ll
->name
[0] != '@')) continue;
3923 if (ll
->value
< 0) {
3924 fprintf(fo
, "%d %s\n", ll
->value
, ll
->name
);
3926 fprintf(fo
, "#%04X %s\n", (unsigned)ll
->value
, ll
->name
);
3935 ///////////////////////////////////////////////////////////////////////////////
3938 static struct option longOpts
[] = {
3939 {"org", required_argument
, NULL
, 600},
3940 {"define", required_argument
, NULL
, 601},
3941 {"defzero", required_argument
, NULL
, 602},
3942 {"outdir", required_argument
, NULL
, 660},
3943 {"reffile", optional_argument
, NULL
, 669},
3945 {"sna", 0, NULL
, 's'},
3946 {"sna128", 0, NULL
, 'S'},
3947 {"tap", 0, NULL
, 't'},
3948 {"autotap", 0, NULL
, 'T'},
3949 {"raw", 0, NULL
, 'r'},
3950 {"autodmb", 0, NULL
, 'B'},
3951 {"dmb", 0, NULL
, 'b'},
3952 {"none", 0, NULL
, 'n'},
3953 {"help", 0, NULL
, 'h'},
3954 {"hob", 0, NULL
, 'H'},
3955 {"scl", 0, NULL
, 'l'},
3956 {"autoscl", 0, NULL
, 'L'},
3957 {"fixups", optional_argument
, NULL
, 'F'},
3963 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
3966 static void usage (const char *pname
) {
3968 "usage: %s [options] infile\n"
3969 "default infiles:", pname
);
3970 for (int f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
3973 " -s --sna write 48K .SNA file with autostart\n"
3974 " -S --sna128 write 148K .SNA file with autostart\n"
3975 " -t --tap write .tap file\n"
3976 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
3977 " -r --raw write raw file(s)\n"
3978 " -b --dmb write DMB file\n"
3979 " -B --autodmb write DMB file with autostart\n"
3980 " -H --hob write HoBeta code file(s)\n"
3981 " -l --scl write SCL TR-DOS archive\n"
3982 " -L --autoscl write autostarting SCL TR-DOS archive\n"
3983 " -n --none write nothing\n"
3984 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
3985 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
3986 " text: .txt file\n"
3987 " asm: address lists with counters\n"
3988 " asmdiff: 2nd and other addresses are relative, with counters\n"
3989 " asmz: address lists, with 0 end markers\n"
3990 " -h --help this help\n"
3992 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
3993 " --org xxx set ORG\n"
3994 " --define val perform 'val EQU 1'\n"
3995 " --defzero val perform 'val EQU 0'\n"
3996 " --outdir dir output dir for resulting files (default: current)\n"
4001 ///////////////////////////////////////////////////////////////////////////////
4004 int main (int argc
, char *argv
[]) {
4006 const char *pname
= argv
[0];
4007 char *inFile
= NULL
;
4008 char **defines
= NULL
, **values
= NULL
;
4014 urasm_getbyte
= getByte
;
4015 urasm_putbyte
= putByte
;
4016 urasm_label_by_name
= findLabelCB
;
4017 urasm_getval
= getValueCB
;
4018 urasm_expand
= expandCB
;
4019 urasm_fixup_operand
= fixupOperandCB
;
4021 //strcpy(tapeLoaderName, "cargador ");
4022 tapeLoaderName
[0] = 0;
4024 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
4025 while ((c
= getopt_long(argc
, argv
, "sStTrBbnhHFLl", longOpts
, NULL
)) >= 0) {
4028 case 'S': /*optRunSNA = 1;*/ optSNA48
= 0; optWriteType
= 's'; optWTChanged
= 1; break;
4029 case 's': /*optRunSNA = 0;*/ optSNA48
= 1; optWriteType
= 's'; optWTChanged
= 1; break;
4030 case 'T': optRunTape
= 1; optWriteType
= 't'; optWTChanged
= 1; break;
4031 case 't': optRunTape
= 0; optWriteType
= 't'; optWTChanged
= 1; break;
4032 case 'L': optRunSCL
= 1; optWriteType
= 'S'; optWTChanged
= 1; break;
4033 case 'l': optRunSCL
= 0; optWriteType
= 'S'; optWTChanged
= 1; break;
4034 case 'r': case 'n': optWriteType
= c
; optWTChanged
= 1; break;
4035 case 'b': optRunTape
= 0; optRunDMB
= 0; optWriteType
= 'd'; optWTChanged
= 1; break;
4036 case 'B': optRunTape
= 0; optRunDMB
= 1; optWriteType
= 'd'; optWTChanged
= 1; break;
4037 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
4038 case 'H': optWriteType
= 'H'; optWTChanged
= 1; break;
4041 if (optarg
!= NULL
) {
4042 if (strcmp(optarg
, "asm") == 0 || strcmp(optarg
, "zas") == 0) {
4044 } else if (strcmp(optarg
, "asmdiff") == 0 || strcmp(optarg
, "zasdiff") == 0) {
4046 } else if (strcmp(optarg
, "asmz") == 0 || strcmp(optarg
, "zasz") == 0) {
4048 } else if (strcmp(optarg
, "text") == 0) {
4051 fprintf(stderr
, "FATAL: invalid fixup type: '%s'\n", optarg
);
4058 //fprintf(stderr, "ORG: %d\n", c);
4059 if (c
< 0 || c
> 65535) {
4060 fprintf(stderr
, "FATAL: invalid ORG: %d\n", c
);
4063 start_pc
= start_disp
= start_ent
= c
;
4066 //fprintf(stderr, "define: [%s]\n", optarg);
4067 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
4068 values
= realloc(values
, sizeof(char *)*(defcount
+1));
4069 defines
[defcount
] = strdup(optarg
);
4070 values
[defcount
] = strdup("1");
4073 case 602: // defzero
4074 //fprintf(stderr, "defzero: [%s]\n", optarg);
4075 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
4076 values
= realloc(values
, sizeof(char *)*(defcount
+1));
4077 defines
[defcount
] = strdup(optarg
);
4078 values
[defcount
] = strdup("0");
4082 if (optOutputDir
!= NULL
) free(optOutputDir
);
4083 optOutputDir
= strdup(optarg
);
4085 case 669: // reffile
4086 if (refFileName
) free(refFileName
);
4087 refFileName
= (optarg
? strdup(optarg
) : NULL
);
4093 if (optind
>= argc
) {
4094 // try to find default input file
4095 for (int f
= 0; defInFiles
[f
]; ++f
) {
4096 if (!access(defInFiles
[f
], R_OK
)) {
4097 inFile
= strdup(defInFiles
[f
]);
4102 inFile
= strdup(argv
[optind
]);
4105 if (!inFile
|| !inFile
[0]) {
4107 fprintf(stderr
, "ERROR: no input file!\n");
4111 if (optOutputDir
== NULL
) optOutputDir
= strdup(".");
4113 registerInstructions();
4114 registerFunctions();
4116 res
= asmTextLoad(inFile
);
4118 for (int f
= 0; f
< defcount
; ++f
) {
4119 if (labelDoEQU(defines
[f
], values
[f
]) != 0) {
4120 fprintf(stderr
, "FATAL: can't define label: '%s'\n", defines
[f
]);
4125 for (pass
= 0; pass
<= 1; ++pass
) {
4127 printf("pass %d\n", pass
);
4128 setCurSrcLine(asmText
);
4129 if (setjmp(errJP
)) { res
= 1; break; }
4130 while (curSrcLine
) processCurrentLine();
4131 if (posstPass()) { res
= 1; break; }
4136 char *oc
= strdup(inFile
);
4137 char *pd
= strrchr(oc
, '.');
4138 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
4139 switch (optWriteType
) {
4140 case 's': saveSna(oc
, optSNA48
); break;
4141 case 't': saveTap(oc
); break;
4142 case 'r': saveRaw(oc
); break;
4143 case 'd': saveDMB(oc
); break;
4144 case 'H': saveHob(oc
); break;
4145 case 'S': saveSCL(oc
); break;
4148 if (optWriteFixups
) writeFixups();
4150 /* build ref file name */
4151 if (!refFileName
|| !refFileName
[0]) {
4152 if (refFileName
) free(refFileName
);
4153 refFileName
= malloc(strlen(inFile
)+128);
4154 strcpy(refFileName
, inFile
);
4155 char *ext
= strrchr(refFileName
, '.');
4157 strcat(refFileName
, ".ref");
4161 for (char *ts
= refFileName
; *ts
; ++ts
) {
4162 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
4165 char *slash
= strrchr(refFileName
, '/');
4167 if (!slash
|| slash
< ext
) {
4168 strcpy(ext
, ".ref");
4170 strcat(refFileName
, ".ref");
4174 writeLabelsFile(refFileName
);
4175 printf("refs written to '%s'\n", refFileName
);
4180 fprintf(stderr
, "ERROR: loading error!\n");
4184 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
4192 if (inFile
) free(inFile
);
4193 if (sysIncludeDir
) free(sysIncludeDir
);
4194 for (int f
= defcount
-1; f
>= 0; --f
) {
4198 if (defines
!= NULL
) { free(values
); free(defines
); }
4199 if (optOutputDir
!= NULL
) free(optOutputDir
);
4200 return (res
? 1 : 0);