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
;
92 static char *lastIncludePath
= NULL
;
93 static char *lastSysIncludePath
= NULL
;
95 #define MAX_LINE_SIZE 16384
96 static char currLine
[MAX_LINE_SIZE
]; /* current processing line; can be freely modified */
99 ///////////////////////////////////////////////////////////////////////////////
102 static void initInclideDir (void) {
103 const char *id
= getenv("URASM_INCLUDE_DIR");
105 sysIncludeDir
= strdup(id
);
108 memset(myDir
, 0, sizeof(myDir
));
110 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
113 char *p
= (char *)strrchr(myDir
, '/');
114 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
117 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
118 char *p
= strrchr(myDir
, '\\');
119 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
121 strcat(myDir
, "/libs");
122 sysIncludeDir
= strdup(myDir
);
124 while (sysIncludeDir
[0] && sysIncludeDir
[strlen(sysIncludeDir
)-1] == '/') sysIncludeDir
[strlen(sysIncludeDir
)-1] = '\0';
125 if (!sysIncludeDir
[0]) strcpy(sysIncludeDir
, ".");
129 ///////////////////////////////////////////////////////////////////////////////
132 /* trim trailing spaces and comments; normalize colons */
133 static void normalizeStr (char *s
) {
138 const char ch
= *p
++;
139 /* check for "af'" */
140 if (ch
== '\'' && p
-s
>= 2 && toLower(p
[-2] == 'a') && toLower(p
[-1] == 'f')) continue;
142 //if (ch == '(') { ++paren; continue; }
143 //if (ch == ')') { if (--paren < 0) paren = 0; continue; }
145 if (ch
== ';') { p
[-1] = 0; break; }
147 if (ch
== '"' || ch
== '\'') {
150 const char c1
= *p
++;
151 if (c1
== qch
) break;
152 if (c1
== '\\' && *p
) ++p
;
156 /* reduce and normalise colons */
157 if (/*paren == 0 &&*/ ch
== ':') {
158 --p
; /* back to colon */
159 /* remove spaces before colon */
161 while (t
!= s
&& isSpace(t
[-1])) --t
;
162 if (t
!= p
) memmove(t
, p
, strlen(p
)+1);
164 if (p
[0] != ':') abort(); // assert
165 ++p
; /* skip colon */
166 /* remove following spaces and colons */
168 while (*t
== ':' || isSpace(*t
)) ++t
;
169 if (t
!= p
) memmove(p
, t
, strlen(t
)+1);
173 /* done; trim trailing spaces and colons */
174 size_t slen
= strlen(s
);
175 while (slen
> 0 && (isSpace(s
[slen
-1]) || s
[slen
-1] == ':')) --slen
;
180 /* check if string starts with the given command (case-insensitive) */
181 /* returns NULL or pointer to args */
182 /* skips spaces after command if any */
183 static char *strIsCommand (const char *command
, char *str
) {
184 for (int cnt
= 1; cnt
> 0; --cnt
) {
185 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
186 for (; *command
&& *str
; ++command
, ++str
) {
187 if (toUpper(*command
) != toUpper(*str
)) return NULL
; // alas
189 if (*command
) return NULL
; // alas
190 if (*str
&& isAlphaDigit(*str
)) return NULL
; // alas
191 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
192 if (*str
&& *str
== ':') break; // try again if we have a colon
199 /* parse string literal */
200 /* don't free() result */
201 /* skips trailing spaces */
202 static char *parseStr (char **str
, char endQ
, int *lenp
) {
203 static char buf
[MAX_LINE_SIZE
];
204 int len
= 0, n
, f
, base
;
207 int xDigit (char ch
, int base
) {
208 if (ch
< '0') return -1;
210 if (ch
>= '0'+base
) return -1;
214 if (ch
<= '9') return ch
-'0';
215 if (ch
< 'A' || ch
> 'A'+base
-10) return -1;
217 return (ch
< base
? ch
: -1);
220 memset(buf
, 0, sizeof(buf
));
226 case 'a': buf
[len
++] = '\a'; break;
227 case 'b': buf
[len
++] = '\b'; break;
228 case 'e': buf
[len
++] = '\x1b'; break;
229 case 'f': buf
[len
++] = '\f'; break;
230 case 'n': buf
[len
++] = '\n'; break;
231 case 'r': buf
[len
++] = '\r'; break;
232 case 't': buf
[len
++] = '\t'; break;
233 case 'v': buf
[len
++] = '\v'; break;
234 case 'z': buf
[len
++] = '\0'; break;
235 case 'x': case 'X': // hex
238 donum
: for (n
= 0; f
> 0; --f
) {
239 char ch
= xDigit(*a
++, base
);
241 if (ch
< 0) { --a
; break; }
246 --a
; // return to the last digit, 'for' will skip it
251 case '1' ... '9': // decimal
254 default: buf
[len
++] = a
[0]; break; // others
257 if (*a
== endQ
) { ++a
; break; }
261 while (*a
&& isSpace(*a
)) ++a
; // skip trailing spaces
264 if (lenp
) *lenp
= len
;
269 ///////////////////////////////////////////////////////////////////////////////
270 // source file stack, reader, etc
272 typedef struct SourceLine
{
273 struct SourceLine
*next
;
280 static SourceLine
*asmText
= NULL
;
281 static SourceLine
*asmTextLast
= NULL
;
282 static SourceLine
*curSrcLine
= NULL
;
284 #define MAX_MACRO_ARGS (32)
286 typedef struct MacroDef
{
287 struct MacroDef
*next
;
291 char *argdefaults
[MAX_MACRO_ARGS
]; // default values
292 char *argnames
[MAX_MACRO_ARGS
]; // argument names
297 char *argvals
[MAX_MACRO_ARGS
]; // argument values
300 static MacroDef
*maclist
= NULL
;
301 static CurMacroDef
*curmacro
= NULL
; // !NULL if we are not inserting macro
302 static int curmacronum
= 0;
305 static MacroDef
*findMacro (const char *name
) {
306 for (MacroDef
*mc
= maclist
; mc
!= NULL
; mc
= mc
->next
) if (strcasecmp(name
, mc
->name
) == 0) return mc
;
311 static void asmTextClear (void) {
313 SourceLine
*l
= asmText
;
315 asmText
= asmText
->next
;
320 asmTextLast
= curSrcLine
= NULL
;
324 static int asmTextLoad (const char *fname
, int system
) {
329 if (!(fl
= fopen(fname
, "r"))) {
330 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
333 printf("loading: %s\n", fname
);
335 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
337 currLine
[sizeof(currLine
)-1] = '\0';
338 normalizeStr(currLine
);
339 //fprintf(stderr, "*[%s]\n", curLine);
340 if (!currLine
[0]) continue; // don't store empty lines
342 if ((s
= calloc(1, sizeof(SourceLine
))) == NULL
) abort();
345 if ((s
->line
= strdup(currLine
)) == NULL
) abort();
346 if ((s
->fname
= strdup(fname
)) == NULL
) abort();
347 if (asmTextLast
) asmTextLast
->next
= s
; else asmText
= s
;
355 static char *extractFileDir (const char *s
) {
356 if (!s
|| !s
[0]) return strdup("");
360 for (const char *ts
= s
; *ts
; ++ts
) {
361 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
363 if (slash
== s
&& (s
[0] == '/' || s
[0] == '\\') && (s
[1] == '/' || s
[1] == '\\')) slash
= NULL
;
365 slash
= strrchr(s
, '/');
367 if (!slash
) return strdup("");
368 ptrdiff_t len
= (ptrdiff_t)(slash
-s
)+1;
369 char *res
= malloc(len
+1);
373 while (len
> 0 && (res
[len
-1] == '\\' || res
[len
-1] == '/')) --len
;
375 while (len
> 0 && res
[len
-1] == '/') --len
;
377 if (len
== 0) { free(res
); return strdup(""); }
383 static void loadCurSrcLine (void) {
385 strcpy(currLine
, curSrcLine
->line
);
386 /* macros will not change include dirs */
388 char **incpp
= (!curSrcLine
->system
? &lastIncludePath
: &lastSysIncludePath
);
389 if (*incpp
) free(*incpp
);
390 *incpp
= extractFileDir(curSrcLine
->fname
);
397 static inline SourceLine
*setCurSrcLine (SourceLine
*l
) { curSrcLine
= l
; loadCurSrcLine(); return l
; }
399 static inline SourceLine
*nextSrcLine (void) { return (curSrcLine
!= NULL
? setCurSrcLine(curSrcLine
->next
) : NULL
); }
402 static inline int strHasPathDelim (const char *s
) {
403 if (!s
|| !s
[0]) return 0;
405 return (strchr(s
, '/') || strchr(s
, '\\') ? 1 : 0);
407 return (strchr(s
, '/') ? 1 : 0);
412 /* returns malloced string */
413 static char *createIncludeName (const char *fname
, int assystem
, const char *defaultmain
) {
414 if (!fname
|| !fname
[0]) return NULL
;
416 if (fname
[0] != '/') {
419 incdir
= lastIncludePath
;
421 incdir
= lastSysIncludePath
;
422 if (!incdir
|| !incdir
[0]) incdir
= sysIncludeDir
;
424 res
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
426 res
= strprintf("%s", fname
);
429 if (defaultmain
&& defaultmain
[0]) {
430 if (stat(res
, &st
) == 0) {
431 if (S_ISDIR(st
.st_mode
)) {
432 char *rs
= strprintf("%s/%s", res
, defaultmain
);
438 /* check if there is disk file */
439 if (strHasPathDelim(fname
) && stat(res
, &st
) != 0) {
440 /* no file, try "root include" */
441 const char *incdir
= (!assystem
? NULL
: sysIncludeDir
);
442 char *rs
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
445 /* check for dir again */
446 if (defaultmain
&& defaultmain
[0]) {
447 if (stat(res
, &st
) == 0) {
448 if (S_ISDIR(st
.st_mode
)) {
449 char *rs
= strprintf("%s/%s", res
, defaultmain
);
456 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
461 static int includeCount
= 0;
464 // include file instead of the current line
465 static int asmTextInclude (const char *fname
, int system
) {
469 SourceLine
*first
= NULL
, *last
= NULL
, *s
= NULL
;
471 if (includeCount
> 256) {
472 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", curSrcLine
->fname
, curSrcLine
->lineNo
);
476 fn
= createIncludeName(fname
, system
, "zzmain.zas");
478 printf("loading: %s\n", fn
);
479 if ((fl
= fopen(fn
, "r")) == NULL
) {
480 fprintf(stderr
, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", curSrcLine
->fname
, curSrcLine
->lineNo
, currLine
);
485 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
487 currLine
[sizeof(currLine
)-1] = '\0';
488 const size_t slen
= strlen(currLine
);
489 if (slen
== 0 || (currLine
[slen
-1] != '\n' && currLine
[slen
-1] != '\r')) {
490 fprintf(stderr
, "ERROR: file %s, line %d: line too long\n", fn
, lineNo
);
494 normalizeStr(currLine
);
495 if (!currLine
[0]) continue; // don't store empty lines
497 if ((s
= calloc(1, sizeof(SourceLine
))) == NULL
) abort();
500 if ((s
->line
= strdup(currLine
)) == NULL
) abort();
501 if ((s
->fname
= strdup(fn
)) == NULL
) abort();
502 if (last
!= NULL
) last
->next
= s
; else first
= s
;
508 curSrcLine
->line
[0] = 0;
510 last
->next
= curSrcLine
->next
;
511 curSrcLine
->next
= first
;
517 ///////////////////////////////////////////////////////////////////////////////
520 static void processCurrentLine (void); // only one, will skip to next one
523 ///////////////////////////////////////////////////////////////////////////////
524 // error raisers, etc
526 static jmp_buf errJP
;
529 static void errorWriteFile (FILE *fo
) {
531 fprintf(fo
, "at file %s, line %d\n%s\n*", curSrcLine
->fname
, curSrcLine
->lineNo
, curSrcLine
->line
);
533 fprintf(fo
, "somewhere in time: ");
537 static void errorMsgV (const char *fmt
, va_list ap
) {
538 errorWriteFile(stderr
);
539 vfprintf(stderr
, fmt
, ap
);
546 static void __attribute__((format(printf
, 1, 2))) warningMsg (const char *fmt
, ...) {
548 fprintf(stderr
, "WARNING ");
554 static void __attribute__((format(printf
, 1, 2))) errorMsg (const char *fmt
, ...) {
556 fprintf(stderr
, "FATAL ");
562 static void __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2))) fatal (const char *fmt
, ...) {
570 static void __attribute__((noreturn
)) fatalUrLib (int errcode
) {
571 errorMsg("%s", urasm_errormsg(errcode
));
576 //////////////////////////////////////////////////////////////////////////////
577 // operator management
579 // return !0 to skip current line
580 typedef int (*UrAsmOpFn
) (void);
587 typedef struct UrAsmOp
{
590 struct UrAsmOp
*next
;
593 static UrAsmOp
*oplist
= NULL
;
596 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
597 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
599 res
->name
= strdup(name
);
607 static UrAsmOp
*urFindOp (const char *name
) {
609 for (res
= oplist
; res
; res
= res
->next
) if (!strcasecmp(name
, res
->name
)) break;
614 static void urClearOps (void) {
618 oplist
= oplist
->next
;
625 ///////////////////////////////////////////////////////////////////////////////
628 typedef struct UrLabelInfo
{
631 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
632 int known
; /* !0: label value already known */
633 int refLine
; /* first referenced line */
634 int fixuptype
; /* UR_FIXUP_XXX */
636 struct UrLabelInfo
*next
;
639 static UrLabelInfo
*labels
= NULL
;
642 static void urClearLabels (void) {
645 while ((c
= labels
) != NULL
) {
647 if (c
->name
) free(c
->name
);
648 if (c
->refFile
) free(c
->refFile
);
654 static UrLabelInfo
*urFindLabel (const char *name
) {
655 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) if (strcmp(name
, c
->name
) == 0) return c
;
660 static UrLabelInfo
*urAddLabel (const char *name
) {
661 UrLabelInfo
*c
= urFindLabel(name
);
666 for (p
= NULL
, c
= labels
; c
; p
= c
, c
= c
->next
) {}
667 c
= calloc(1, sizeof(UrLabelInfo
));
669 c
->name
= strdup(name
);
671 c
->fixuptype
= UR_FIXUP_NONE
;
672 if (p
) p
->next
= c
; else labels
= c
;
679 ///////////////////////////////////////////////////////////////////////////////
680 // module list management
682 typedef struct ModuleInfo
{
684 char *fname
; // opened in this file
685 int seen
; // !0: module already seen, skip other definitions from the same file
686 struct ModuleInfo
*next
;
689 static ModuleInfo
*modules
= NULL
;
690 static ModuleInfo
*curModule
= NULL
;
693 static void modulesClear (void) {
696 ModuleInfo
*c
= modules
;
698 modules
= modules
->next
;
706 static void modulesResetSeen (void) {
707 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
711 static ModuleInfo
*moduleFind (const char *name
) {
712 if (!name
|| !name
[0]) return NULL
;
713 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) return c
;
718 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
721 if (!name
|| !fname
|| !name
[0] || !fname
[0]) abort();
722 if ((c
= calloc(1, sizeof(ModuleInfo
))) == NULL
) abort();
723 if ((c
->name
= strdup(name
)) == NULL
) abort();
724 if ((c
->fname
= strdup(fname
)) == NULL
) abort();
726 return (modules
= c
);
730 ///////////////////////////////////////////////////////////////////////////////
732 typedef struct FixupItem
{
733 struct FixupItem
*next
;
739 static FixupItem
*fixlisthead
= NULL
, *fixlisttail
= NULL
;
742 static void clearFixups (void) {
745 while ((c
= fixlisthead
) != NULL
) {
746 fixlisthead
= c
->next
;
752 static void addFixup (uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
753 FixupItem
*fx
= calloc(1, sizeof(FixupItem
));
755 fx
->opdestaddr
= opdestaddr
;
757 fx
->fixuptype
= fixuptype
;
760 if (fixlisttail
!= NULL
) fixlisttail
->next
= fx
; else fixlisthead
= fx
;
766 ///////////////////////////////////////////////////////////////////////////////
767 // destination memory management
769 static uint8_t memory
[65536];
770 static char memused
[65536];
771 static char memresv
[65536];
772 static uint16_t start_pc
= 0x100; // viva CP/M!
773 static uint16_t start_disp
= 0x100; // viva CP/M!
774 static uint16_t start_ent
= 0x100; // viva CP/M!
775 static uint16_t pc
= 0; /* current position to write */
776 static uint16_t disp
= 0; /* current 'virtual PC' */
777 static uint16_t ent
= 0; /* starting address */
778 static uint16_t clrAddr
= 24999; /* address for CLEAR for cargador */
779 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
780 static int inTapeBlock
= 0;
781 static uint8_t tapeXorB
= 0;
784 static inline uint8_t getByte (uint16_t addr
) {
790 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
791 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
796 static inline void putByte (uint16_t addr
, uint8_t b
) {
797 if (inTapeBlock
) tapeXorB
^= b
;
803 static inline MAYBE_UNUSED
void putWord (uint16_t addr
, uint16_t w
) {
804 putByte(addr
, w
&0xFFU
);
805 putByte(addr
+1, (w
>>8)&0xFFU
);
809 static inline void emitByte (uint8_t b
) {
816 static inline void emitWord (uint16_t w
) {
818 emitByte((w
>>8)&0xFFU
);
822 static inline void emitRWord (uint16_t w
) {
823 emitByte((w
>>8)&0xFFU
);
828 static void prepareMemory (void) {
829 memset(memory
, 0, sizeof(memory
));
830 memset(memused
, 0, sizeof(memused
));
831 memset(memresv
, 0, sizeof(memresv
));
835 ///////////////////////////////////////////////////////////////////////////////
836 // label getter and utilities
838 static char *lastSeenGlobalLabel
= NULL
; /* global */
841 static char *fixLocalLabel (const char *name
) {
842 static char newname
[MAX_LINE_SIZE
*2+1024];
844 memset(newname
, 0, sizeof(newname
));
845 if (!name
|| !name
[0]) {
847 } else if (!lastSeenGlobalLabel
|| name
[0] != '.') {
848 strcpy(newname
, name
);
850 if (name
[0] == '.' && name
[1] == '.') {
851 // this is macro label
852 if (curmacro
== NULL
) fatal("macro label outside of macro: '%s'", name
);
853 sprintf(newname
, "{#MAC%d:%s:%s}", curmacronum
, curmacro
->mac
->name
, name
);
855 // this is local label, let's rename it
856 sprintf(newname
, "{%s%s}", lastSeenGlobalLabel
, name
);
858 /*!fprintf(stderr, "LABEL <%s> renamed to <%s> (%d)\n", name, newname, (curSrcLine ? curSrcLine->lineNo : 0));*/
864 static char *fixGlobalLabel (const char *name
) {
865 static char newname
[MAX_LINE_SIZE
*2+1024];
867 memset(newname
, 0, sizeof(newname
));
868 if (!name
|| !name
[0]) {
870 } else if (!curModule
|| name
[0] == '.' || name
[0] == '{' || name
[0] == '@' || strchr(name
, '.')) {
871 if (name
[0] == '@' && name
[1]) ++name
;
872 strcpy(newname
, name
);
874 // this is global unqualified label and we have a module; let's rename it
875 sprintf(newname
, "%s.%s", curModule
->name
, name
);
877 //printf("%s --> %s\n", name, newname);
882 static int lblOptMakeU2
= 0;
884 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
, int *fixuptype
) {
888 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
889 lbl
= urFindLabel(nn
);
891 // try non-module label
892 lbl
= urFindLabel(ln
);
896 errorMsg("using undefined label %s", ln
);
901 lbl
= urAddLabel(nn
);
902 lbl
->type
= (lblOptMakeU2
? -42 : -1);
904 lbl
->refLine
= curSrcLine
->lineNo
;
905 lbl
->refFile
= strdup(curSrcLine
->fname
);
906 //printf("new label: [%s]\n", lbl->name);
908 //printf("label reference: [%s]\n", lbl->name);
912 *defined
= lbl
->known
!=0;
913 *fixuptype
= lbl
->fixuptype
;
928 static int isLabelDefinedOrKnown (const char *name
, uint16_t addr
, int qtype
) {
932 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
933 lbl
= urFindLabel(nn
);
935 // try non-module label
936 lbl
= urFindLabel(ln
);
939 case UR_QTYPE_DEFINED
: return lbl
? lbl
->known
!=0 : 0;
940 case UR_QTYPE_KNOWN
: return lbl
!=NULL
;
947 static void fixupOperandCB (const urasm_operand_t
*op
, uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
949 //static const char *n[4] = {"none", "word", "low", "high"};
950 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
951 addFixup(opdestaddr
, opaddr
, fixuptype
, size
);
956 static int checkLabels (void) {
959 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
961 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
964 if (c
->type
== 0) c
->known
= -1;
966 //if (wasError) longjmp(errJP, 667);
971 ///////////////////////////////////////////////////////////////////////////////
972 // expression utils (aka string parsing)
975 /* skip leading spaces */
976 /* returns string with spaces skipped */
977 static inline char *strSkipSpaces (const char *s
) {
978 while (*s
&& isSpace(*s
)) ++s
;
983 /* skip leading spaces */
984 /* returns string with spaces skipped */
985 static inline const char *strSkipSpacesConst (const char *s
) {
986 while (*s
&& isSpace(*s
)) ++s
;
991 /* remove trailing spaces from string */
992 static void strTrimRight (char *s
) {
993 if (!s
|| !s
[0]) return;
994 size_t len
= strlen(s
);
995 while (len
> 0 && isSpace(s
[len
-1])) --len
;
1000 /* skip leading spaces and colons */
1001 /* returns string with spaces skipped */
1002 static inline char *strSkipSpacesColons (char *s
) {
1003 while (*s
&& (isSpace(*s
) || *s
== ':')) ++s
;
1008 /* remove leading spaces from the current line */
1009 static inline void removeSpaces (void) {
1010 char *e
= strSkipSpaces(currLine
);
1011 if (e
!= currLine
) memmove(currLine
, e
, strlen(e
)+1);
1015 /* skip leading spaces, and argument (up to, but not including comma or colon) */
1016 /* correctly skip strings */
1017 /* returns string after skipped argument (with trailing spaces skipped) */
1018 static char *skipMacroArg (char *str
) {
1020 char *strstart
= str
;
1022 str
= strSkipSpaces(str
);
1023 if (!str
[0]) return str
;
1024 if (parens
== 0 && (str
[0] == ',' || str
[0] == ':')) return str
;
1025 /* check for "af'" */
1026 if (str
[0] == '\'' && str
-strstart
>= 2 && toLower(str
[-2] == 'a') && toLower(str
[-1] == 'f')) {
1030 if (str
[0] == '(') { ++parens
; continue; }
1031 if (str
[0] == ')') { if (--parens
< 0) parens
= 0; continue; }
1032 /* check for string */
1033 if (str
[0] == '"' || str
[0] == '\'') {
1034 const char qch
= *str
++;
1036 const char ch
= *str
++;
1037 if (ch
== qch
) break;
1038 if (ch
== '\\' && *str
) ++str
;
1047 /* evaluate next numeric expression in input string */
1048 /* returns expression value */
1049 static int32_t getExprArg (int *defined
, int *addr
) {
1051 char *a
= strSkipSpaces(currLine
);
1053 if (!a
[0]) fatal("expression expected");
1054 const char *ee
= urasm_expr(&res
, a
, disp
, defined
, addr
, &error
);
1055 if (error
) fatalUrLib(error
);
1057 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
1058 memmove(currLine
, ee
, strlen(ee
)+1);
1066 /* evaluate next string expression in input string */
1067 /* returns expression value */
1068 static char *getStrExprArg (void) {
1070 int donteval
= 0, defined
= 0;
1071 static char resbuf
[256];
1072 char *a
= strSkipSpaces(currLine
);
1073 if (!a
[0]) fatal("expression expected");
1074 urasm_exprval_t res
;
1075 urasm_exprval_init(&res
);
1076 const char *ee
= urasm_expr_ex(&res
, a
, disp
, &donteval
, &defined
, &error
);
1077 if (error
) fatalUrLib(error
);
1079 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
1080 memmove(currLine
, ee
, strlen(ee
)+1);
1085 snprintf(resbuf
, sizeof(resbuf
), "%s", res
.str
);
1087 snprintf(resbuf
, sizeof(resbuf
), "%d", res
.val
);
1089 urasm_exprval_clear(&res
);
1094 /* evaluate next numeric expression in input string */
1095 /* there shoild be no other expressions in the string */
1096 /* returns expression value */
1097 static int32_t getOneExprArg (int *defined
, int *addr
) {
1098 int32_t res
= getExprArg(defined
, addr
);
1099 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
1104 /* is next expression a string literal? */
1105 static inline int isStrArg (void) {
1106 const char *s
= strSkipSpaces(currLine
);
1107 return (s
[0] == '"' || s
[0] == '\'');
1111 /* check of we reached end of operator */
1112 static __attribute__((unused
)) inline int isOperatorEnd (void) {
1113 const char *s
= strSkipSpaces(currLine
);
1114 return (s
[0] == 0 || s
[0] == ':');
1118 /* check of we reached end of operator */
1119 static __attribute__((unused
)) inline int isLineEnd (void) {
1120 const char *s
= strSkipSpaces(currLine
);
1125 /* parse string argument from input string */
1126 /* returns parsed string */
1127 static char *getStrArg (int *lenp
) {
1129 char *a
= strSkipSpaces(currLine
);
1131 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
1132 res
= parseStr(&a
, qCh
, lenp
);
1134 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
1135 memmove(currLine
, a
, strlen(a
)+1);
1143 /* get identifier (and lowercase it) */
1144 static char *getOneIdArgLo (void) {
1145 static char res
[MAX_LINE_SIZE
+128];
1147 char *a
= strSkipSpaces(currLine
);
1148 memset(res
, 0, sizeof(res
));
1149 if (!a
[0]) fatal("identifier expected");
1150 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
1151 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
1153 if (p
-res
> 120) fatal("identifier too long: %s", res
);
1154 while (*a
&& isSpace(*a
)) ++a
;
1156 memmove(currLine
, a
, strlen(a
)+1);
1157 if (currLine
[0] == ';') currLine
[0] = 0;
1158 if (currLine
[0]) fatal("extra arguments");
1162 for (char *t
= res
; *t
; ++t
) *t
= toLower(*t
);
1167 /* get label argument */
1168 static char *getLabelArg (int checkdelim
) {
1169 static char res
[MAX_LINE_SIZE
+128];
1171 char *a
= strSkipSpaces(currLine
);
1172 memset(res
, 0, sizeof(res
));
1173 if (!a
[0]) fatal("label expected");
1174 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
1175 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
1177 if (p
-res
> 120) fatal("label name too long: %s", res
);
1178 while (*a
&& isSpace(*a
)) ++a
;
1180 if (checkdelim
&& a
[0] != ',' && a
[0] != ':') fatal("bad label expression");
1181 memmove(currLine
, a
, strlen(a
)+1);
1189 /* get label argument, and ensure that it is the last one */
1190 static char *getOneLabelArg (void) {
1191 char *res
= getLabelArg(1);
1192 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
1197 /* returns ',' or 0 */
1198 static char eatComma (void) {
1199 char *a
= strSkipSpaces(currLine
);
1200 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
1201 if (a
[0] == ':') return 0;
1202 if (a
[0] != ',') fatal("invalid expression: ',' expected");
1203 for (++a
; *a
&& isSpace(*a
); ++a
) {}
1204 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
1205 memmove(currLine
, a
, strlen(a
)+1);
1210 ///////////////////////////////////////////////////////////////////////////////
1213 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
1214 char *ep
= strSkipSpacesColons(currLine
);
1215 memmove(currLine
, ep
, strlen(ep
)+1);
1219 static void checkExprEnd (void) {
1220 char *ep
= strSkipSpaces(currLine
);
1221 memmove(currLine
, ep
, strlen(ep
)+1);
1222 if (currLine
[0] && currLine
[0] != ':') fatal("end of expression expected");
1226 static void checkOperatorEnd (void) {
1227 char *ep
= strSkipSpaces(currLine
);
1228 memmove(currLine
, ep
, strlen(ep
)+1);
1229 if (currLine
[0]) fatal("end of operator expected");
1233 /* remove label from curLine */
1234 static void removeLabel (void) {
1235 char *ep
= currLine
;
1236 if (ep
[0] && !isSpace(ep
[0])) for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {} // skip text
1237 // skip spaces and colons
1238 ep
= strSkipSpacesColons(ep
);
1239 memmove(currLine
, ep
, strlen(ep
)+1);
1243 static int labelDoEQU (const char *lblname
, const char *value
) {
1244 static char n2
[256];
1247 if (value
== NULL
|| lblname
== NULL
|| !lblname
[0] || strlen(lblname
) > 255 || strlen(value
) >= MAX_LINE_SIZE
) return -1;
1248 memset(n2
, 0, sizeof(n2
));
1249 strcpy(n2
, lblname
);
1250 if (!urasm_is_valid_name(n2
)) return -1; // this is not a valid label, get out of here
1251 // check if this can be an instruction
1252 lbl
= urAddLabel(lblname
);
1253 if (!lbl
->refFile
) {
1255 lbl
->refFile
= strdup("artificially-defined-label");
1258 strcpy(currLine
, value
);
1261 int defined
= 1, addr
= UR_FIXUP_NONE
;
1262 int32_t res
= getOneExprArg(&defined
, &addr
);
1264 lbl
->type
= 1; // equ label
1265 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1270 return -1; //fatal("can't calculate label %s", lbl->name);
1277 static void processLabel (void) {
1280 static char n2
[256];
1282 int noLocAff
= 0, doEQU
= 0;
1283 /*!fprintf(stderr, "LINE <%s> (%d)\n", curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
1284 memset(n2
, 0, sizeof(n2
));
1285 if (!currLine
[0] || isSpace(currLine
[0]) || currLine
[0] == ':') {
1286 // this may be " id = smth" or " id equ smth"
1289 while (isSpace(*ep
)) ++ep
;
1290 if (ep
[0] == ':' || !ep
[0] || (!isAlpha(ep
[0]) && ep
[0] != '_' && ep
[0] != '.' && ep
[0] != '@')) {
1291 removeLabel(); // removeLabel() removes any spaces, etc
1294 // this looks like a label; check for '=' or 'equ'
1295 while (isAlphaDigit(ep
[0]) || ep
[0] == '_' || ep
[0] == '.' || ep
[0] == '@') ++ep
;
1297 // skip trailing spaces
1298 while (isSpace(*ep
)) ++ep
;
1302 } else if (isSpace(*nn
)) {
1304 argstart
= strIsCommand("EQU", ep
);
1309 removeLabel(); // removeLabel() removes any spaces, etc
1312 // remove leading spaces from name
1316 while (isSpace(*ep
)) ++ep
;
1317 if (ep
>= nn
) fatal("internal compiler error");
1318 if (nn
-ep
> 120) fatal("label too long");
1319 memset(n2
, 0, sizeof(n2
));
1320 memmove(n2
, ep
, nn
-ep
);
1321 if (urFindOp(n2
) || !urasm_is_valid_name(n2
)) {
1322 //fatal("invalid label name");
1323 removeLabel(); // removeLabel() removes any spaces, etc
1326 // remove label name
1327 memmove(currLine
, argstart
, strlen(argstart
)+1);
1329 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
1330 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
1331 lbl
= urAddLabel(nn
);
1332 if (!lbl
->refFile
) {
1333 lbl
->refLine
= curSrcLine
->lineNo
;
1334 lbl
->refFile
= strdup(curSrcLine
->fname
);
1336 if (doEQU
&& pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
1337 int defined
= 1, addr
= UR_FIXUP_NONE
;
1338 int32_t res
= getOneExprArg(&defined
, &addr
);
1340 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1345 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
1351 for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {}
1352 if (ep
-currLine
> 120) fatal("label too long");
1354 memset(n2
, 0, sizeof(n2
));
1355 memmove(n2
, currLine
, ep
-currLine
);
1357 ep
= strSkipSpaces(ep
);
1358 if (*ep
!= ':') return; // this must be an instruction, process it
1360 if (!urasm_is_valid_name(n2
)) return; // this is not a valid label, get out of here
1362 if (findMacro(n2
)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
1363 // check if this can be instruction
1364 //ep = strSkipSpaces(ep);
1365 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
1366 // ok, we got a good label
1368 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
1369 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (curSrcLine ? curSrcLine->lineNo : 0));*/
1370 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
1371 /*!fprintf(stderr, " RENAMED(ln) <%s>\n", ln);*/
1372 /*!fprintf(stderr, " RENAMED(nn) <%s>\n", nn);*/
1373 lbl
= urAddLabel(nn
);
1374 if (!lbl
->refFile
) {
1375 lbl
->refLine
= curSrcLine
->lineNo
;
1376 lbl
->refFile
= strdup(curSrcLine
->fname
);
1378 //printf("new: [%s]\n", lbl->name);
1380 if (currLine
[0] == '=') {
1382 argstart
= currLine
+1;
1385 argstart
= strIsCommand("EQU", currLine
);
1387 if (!argstart
|| doEQU
) {
1388 if (pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
1392 memmove(currLine
, argstart
, strlen(argstart
)+1);
1394 if (lbl
->type
!= -1 && lbl
->type
!= 0) fatal("duplicate label '%s'", lbl
->name
);
1396 int defined
= 1, addr
= UR_FIXUP_NONE
;
1397 int32_t res
= getOneExprArg(&defined
, &addr
);
1399 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1404 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
1410 if (lbl
->name
[0] != '{' && !noLocAff
) {
1411 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
1412 lastSeenGlobalLabel
= strdup(lbl
->name
);
1417 lbl
->fixuptype
= UR_FIXUP_WORD
;
1421 ///////////////////////////////////////////////////////////////////////////////
1422 // instruction finder (in source)
1424 /* array ends with NULL */
1425 /* returns line or NULL */
1426 /* iidx will be set to found instruction number */
1427 static __attribute__((sentinel
)) SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
1428 if (iidx
) *iidx
= -1;
1429 for (SourceLine
*cur
= curSrcLine
; cur
; cur
= cur
->next
) {
1431 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
1433 for (int f
= 0; ;++f
) {
1434 const char *name
= va_arg(ap
, const char *);
1437 if (strIsCommand(name
, cur
->line
)) {
1439 if (iidx
) *iidx
= f
;
1449 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
1450 return findNextInstructionFromList(NULL
, name
, NULL
);
1454 ///////////////////////////////////////////////////////////////////////////////
1457 static int optWriteFixups
= 0;
1458 static int optFixupType
= 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
1459 static int optRunTape
= 1;
1460 static int optRunDMB
= 1;
1461 static int optRunSCL
= 1;
1462 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1463 static int optSNA48
= 1;
1464 static char *optOutputDir
= NULL
;
1467 ///////////////////////////////////////////////////////////////////////////////
1468 static void writeFixups (void) {
1470 void writeFixupList (FILE *fo
, int cnt
, const char *lbl
, int (*chk
)(const FixupItem
*)) {
1473 fprintf(fo
, "%s:\n", lbl
);
1474 if (optFixupType
== 1) fprintf(fo
, " dw %d ; count\n", cnt
);
1475 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1477 fprintf(fo
, " dw #%04X\n", fx
->opaddr
-prevaddr
);
1478 if (optFixupType
== 2) {
1479 prevaddr
= fx
->opaddr
;
1483 if (optFixupType
== 2 || optFixupType
== 3) fprintf(fo
, " dw 0 ; end marker\n");
1487 if (optFixupType
== 0) {
1488 /* simple text file */
1489 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.txt");
1490 printf("writing fixups to '%s'...\n", fname
);
1491 FILE *fo
= fopen(fname
, "w");
1493 if (fo
== NULL
) fatal("can't write fixup file");
1494 fprintf(fo
, "; addr dadr sz\n");
1495 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1496 static const char type
[4] = "NWLH";
1497 fprintf(fo
, "%c #%04x #%04x %d\n", type
[fx
->fixuptype
], fx
->opaddr
, fx
->opdestaddr
, fx
->size
);
1501 /* various asm formats */
1502 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.zas");
1503 printf("writing fixups to '%s'...\n", fname
);
1504 FILE *fo
= fopen(fname
, "w");
1505 int cntw
= 0, cntwl
= 0, cntwh
= 0, cntbl
= 0, cntbh
= 0;
1507 if (fo
== NULL
) fatal("can't write fixup file");
1508 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1509 if (fx
->fixuptype
== UR_FIXUP_WORD
) { ++cntw
; continue; }
1510 switch (fx
->fixuptype
) {
1511 case UR_FIXUP_LOBYTE
: if (fx
->size
== 2) ++cntwl
; else ++cntbl
; break;
1512 case UR_FIXUP_HIBYTE
: if (fx
->size
== 2) ++cntwh
; else ++cntbh
; break;
1515 writeFixupList(fo
, cntw
, "fixup_table_w", lambda(int, (const FixupItem
*fx
) {
1516 return (fx
->fixuptype
== UR_FIXUP_WORD
);
1518 writeFixupList(fo
, cntwl
, "fixup_table_wl", lambda(int, (const FixupItem
*fx
) {
1519 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 2);
1521 writeFixupList(fo
, cntwh
, "fixup_table_wh", lambda(int, (const FixupItem
*fx
) {
1522 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 2);
1524 writeFixupList(fo
, cntbl
, "fixup_table_bl", lambda(int, (const FixupItem
*fx
) {
1525 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 1);
1527 writeFixupList(fo
, cntbh
, "fixup_table_bh", lambda(int, (const FixupItem
*fx
) {
1528 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 1);
1535 ///////////////////////////////////////////////////////////////////////////////
1536 /* return 'found' flag */
1537 static int findChunkFrom (int addr
, int *start
, int *len
) {
1538 if (addr
< 0) addr
= 0;
1539 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) {}
1540 if (addr
> 65535) return 0;
1542 for (; addr
<= 65535 && memused
[addr
]; ++addr
) {}
1543 *len
= addr
-(*start
);
1548 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
1549 for (; buflen
>= 2; buflen
-= 2) {
1551 uint8_t byte
= *buf
++;
1555 for (cnt
= byte
; cnt
>= 0; --cnt
, ++addr
, --buflen
, ++buf
) {
1556 if (!memused
[addr
]) putByte(addr
, *buf
);
1557 if (addr
== 0xffff) return;
1561 for (; cnt
> 0; --cnt
, ++addr
) {
1562 if (!memused
[addr
]) putByte(addr
, byte
);
1563 if (addr
== 0xffff) return;
1570 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
1571 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1572 if (bxor
) *bxor
= (*bxor
)^b
;
1577 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
1578 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
1579 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
1584 ///////////////////////////////////////////////////////////////////////////////
1587 static int saveSna (const char *fname
, int as48
) {
1588 char *fn
;// = malloc(strlen(fname)+16);
1593 fn
= strprintf("%s/%s.sna", optOutputDir
, fname
);
1594 fo
= fopen(fn
, "wb");
1596 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
1597 printf("out: %s.sna\n", fname
);
1598 memmove(regs
, ursna48
, 27);
1601 /* push new address */
1602 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1604 putByte(sp
, ent
&0xFFU
);
1605 putByte(sp
+1, (ent
>>8)&0xFFU
);
1606 regs
[23] = sp
&0xFFU
;
1607 regs
[24] = (sp
>>8)&0xFFU
;
1609 fwrite(regs
, 27, 1, fo
);
1610 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
1611 fwrite(memory
+16384, 49152, 1, fo
);
1614 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1616 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1617 //fprintf(stderr, "%d\n", memused[sp]);
1618 if (memused
[sp
] || memused
[(sp
+1)&0xffff]) {
1619 fprintf(stderr
, "FATAL: can't save snapshot!\n");
1623 if (fwrite(ursna48
, 27, 1, fo
) != 1) goto error
;
1624 rleUnpack(16384, (const uint8_t *)ursna48
+27, sizeof(ursna48
)-27);
1625 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1627 sprintf(abuf
, "%05d", clrAddr
);
1628 memcpy(memory
+23762, abuf
, 5);
1629 sprintf(abuf
, "%05d", ent
);
1630 memcpy(memory
+23773, abuf
, 5);
1632 if (fwrite(memory
+16384, 49152, 1, fo
) != 1) goto error
;
1635 int addr
= memory
[sp
]+256*memory
[(sp
+1)&0xffff];
1637 if (fWriteWord(fo
, addr
, NULL
) != 0) goto error
; // PC
1638 if (fWriteByte(fo
, 0x10, NULL
) != 0) goto error
; // 7FFD
1639 if (fWriteByte(fo
, 0, NULL
) != 0) goto error
; // TR-DOS inactive
1641 memset(memory
, 0, 16384);
1642 for (int f
= 1; f
< 8; ++f
) {
1643 if (f
!= 2 && f
!= 5) {
1644 if (fwrite(memory
, 16384, 1, fo
) != 1) goto error
;
1658 ///////////////////////////////////////////////////////////////////////////////
1661 static void saveRaw (const char *basename
) {
1662 char *fname
= NULL
;// = malloc(strlen(basename)+16);
1666 while (findChunkFrom(start
, &start
, &len
)) {
1667 if (fname
!= NULL
) free(fname
);
1668 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, (optTapExt
? "tap" : "bin"));
1669 fo
= fopen(fname
, "wb");
1671 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1673 printf("out: %s\n", fname
);
1674 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1675 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1684 if (fname
!= NULL
) free(fname
);
1688 ///////////////////////////////////////////////////////////////////////////////
1691 typedef struct __attribute__((packed
)) __attribute__((gcc_struct
)) {
1696 uint8_t zero
; // always zero
1697 uint8_t secLen
; // length in sectors
1702 static uint16_t calcHobSum (const void *hdr
) {
1703 const uint8_t *buf
= (const uint8_t *)hdr
;
1706 for (unsigned int f
= 0; f
< 15; ++f
) res
+= ((uint16_t)buf
[f
])*257+f
;
1711 static void saveHob (const char *basename
) {
1712 char *fname
= NULL
;//malloc(strlen(basename)+16);
1717 while (findChunkFrom(start
, &start
, &len
)) {
1718 if (fname
!= NULL
) free(fname
);
1719 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, "$C");
1720 fo
= fopen(fname
, "wb");
1722 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1725 char tmpbuf
[sizeof(hdr
.name
)*2];
1726 printf("out: %s\n", fname
);
1727 memset(&hdr
, 0, sizeof(hdr
));
1728 memset(&hdr
.name
, 32, 8);
1729 snprintf(tmpbuf
, sizeof(tmpbuf
), "%c%04X", basename
[0], start
);
1730 while (strlen(tmpbuf
) < sizeof(hdr
.name
)) strcat(tmpbuf
, " "); /* sorry! */
1731 memcpy(hdr
.name
, tmpbuf
, sizeof(hdr
.name
));
1735 hdr
.secLen
= (len
+255)/256;
1736 hdr
.checksum
= calcHobSum(&hdr
);
1737 if (fwrite(&hdr
, sizeof(hdr
), 1, fo
) != 1) {
1738 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1743 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1744 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1751 if (fwrite(&hdr
.zero
, 1, 1, fo
) != 1) {
1752 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1758 //fprintf(stderr, ":%d\n", len%256);
1764 if (fname
!= NULL
) free(fname
);
1768 // ////////////////////////////////////////////////////////////////////////// //
1771 uint8_t dir
[14*256];
1780 static void sclInit (SCLFile
*scl
) {
1781 memset(scl
, 0, sizeof(*scl
));
1782 scl
->datasize
= 2*80*16*256; /* maximum disk size */
1783 scl
->data
= malloc(scl
->datasize
);
1784 memset(scl
->data
, 0, scl
->datasize
);
1785 scl
->fpos
= 0xffffffffu
;
1789 static void sclFree (SCLFile
*scl
) {
1791 if (scl
->data
) free(scl
->data
);
1792 memset(scl
, 0, sizeof(*scl
));
1793 scl
->fpos
= 0xffffffffu
;
1797 static void sclStartFile (SCLFile
*scl
, const char *name
, char type
, uint16_t v0
, uint16_t v1
) {
1798 if (scl
->fcount
== 255) {
1799 fprintf(stderr
, "FATAL: too many files in SCL!\n");
1802 if (scl
->fpos
!= 0xffffffffu
) {
1803 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
1807 while (nlen
< 8 && name
&& name
[nlen
]) scl
->dir
[scl
->dirpos
++] = name
[nlen
++];
1808 while (nlen
< 8) { scl
->dir
[scl
->dirpos
++] = ' '; ++nlen
; }
1809 scl
->dir
[scl
->dirpos
++] = type
;
1810 scl
->dir
[scl
->dirpos
++] = v0
&0xff;
1811 scl
->dir
[scl
->dirpos
++] = (v0
>>8)&0xff;
1812 scl
->dir
[scl
->dirpos
++] = v1
&0xff;
1813 scl
->dir
[scl
->dirpos
++] = (v1
>>8)&0xff;
1814 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
1815 scl
->fpos
= scl
->datapos
;
1819 static void sclEndFile (SCLFile
*scl
) {
1820 if (scl
->fpos
== 0xffffffffu
) {
1821 fprintf(stderr
, "FATAL: last SCL file already finished!\n");
1824 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
1825 const uint32_t fsz
= scl
->datapos
-scl
->fpos
;
1826 if (fsz
> 255*256) {
1827 fprintf(stderr
, "FATAL: SCL file too big!\n");
1830 scl
->dir
[scl
->dirpos
++] = ((fsz
+255)/256)&0xff; /* size in sectors */
1832 scl
->fpos
= 0xffffffffu
;
1836 static void sclWriteData (SCLFile
*scl
, const void *buf
, size_t len
) {
1837 if (scl
->fpos
== 0xffffffffu
) {
1838 fprintf(stderr
, "FATAL: no open SCL file!\n");
1842 if (len
> 255*256) {
1843 fprintf(stderr
, "FATAL: SCL file too big!\n");
1846 memcpy(scl
->data
+scl
->datapos
, buf
, len
);
1847 scl
->datapos
+= len
;
1851 static void sclWriteByte (SCLFile
*scl
, uint8_t b
) {
1852 sclWriteData(scl
, &b
, 1);
1856 static void sclWriteWord (SCLFile
*scl
, uint16_t w
) {
1858 sclWriteData(scl
, &b
, 1);
1860 sclWriteData(scl
, &b
, 1);
1864 #define SCL_DO_WRITE(buf_,size_) do { \
1865 if ((size_) == 0) break; \
1866 const uint8_t *p = (const uint8_t *)(buf_); \
1867 for (size_t n = (size_t)(size_); n--; ++p) checksum += *p; \
1868 if (fwrite((buf_), (size_t)(size_), 1, fo) != 1) return -1; \
1872 static int sclSaveToFile (FILE *fo
, SCLFile
*scl
) {
1873 if (scl
->fpos
!= 0xffffffffu
) {
1874 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
1877 const char *sign
= "SINCLAIR";
1878 uint32_t checksum
= 0;
1880 SCL_DO_WRITE(sign
, 8);
1881 SCL_DO_WRITE(&scl
->fcount
, 1);
1883 SCL_DO_WRITE(scl
->dir
, scl
->dirpos
);
1885 SCL_DO_WRITE(scl
->data
, scl
->datapos
);
1887 for (unsigned f
= 0; f
< 4; ++f
) {
1888 const uint8_t b
= (checksum
>>(f
*8))&0xff;
1889 SCL_DO_WRITE(&b
, 1);
1896 // ////////////////////////////////////////////////////////////////////////// //
1897 static void saveSCLCargador (SCLFile
*scl
) {
1898 static uint8_t cargador
[16384]; // should be enough for everyone
1901 int start
= 0, len
, pos
;
1903 void putStr (const char *s
) {
1904 for (; *s
; ++s
) cargador
[pos
++] = *s
;
1907 void putNum (int num
) {
1909 sprintf(buf
, "%d", num
);
1917 cargador
[pos
++] = (linenum
>>8)&0xff;
1918 cargador
[pos
++] = linenum
&0xff;
1919 // size (will be fixed later)
1920 cargador
[pos
++] = 0;
1921 cargador
[pos
++] = 0;
1925 if (linestart
>= 0) {
1926 const int size
= pos
-linestart
-4;
1927 cargador
[linestart
+2] = size
&0xff;
1928 cargador
[linestart
+3] = (size
>>8)&0xff;
1938 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
1941 while (findChunkFrom(start
, &start
, &len
)) {
1942 // :RANDOMIZE USR VAL "15619":REM:LOAD "
1943 if (cont
) { putStr(":"); cont
= 0; }
1944 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
1945 // generate chunk name
1946 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
1955 if (cont
) { putStr(":"); cont
= 0; }
1956 // RANDOMIZE USR VAL "xxx"
1957 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"");
1962 //putWord(1); // autostart line
1965 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
1966 sclWriteData(scl
, cargador
, (size_t)pos
);
1967 sclWriteByte(scl
, 0x80);
1968 sclWriteByte(scl
, 0xaa);
1969 sclWriteWord(scl
, 1);
1974 static void saveSCL (const char *basename
) {
1978 char *fname
= strprintf("%s/%s.scl", optOutputDir
, basename
);
1981 while (findChunkFrom(start
, &start
, &len
)) {
1985 if (fcount
&& optRunSCL
) fcount
+= 2; // +loader and boot
1987 fprintf(stderr
, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname
, fcount
);
1990 // create output file
1991 FILE *fo
= fopen(fname
, "wb");
1993 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
1996 // initialise SCL writer
1999 if (fcount
&& optRunSCL
) {
2000 // create simple boot
2001 const uint8_t dasboot
[] = {
2002 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"
2004 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
2005 sclWriteWord(&scl
, 1); // line number
2006 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
2007 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
2009 sclWriteByte(&scl
, 0x80);
2010 sclWriteByte(&scl
, 0xaa);
2012 sclWriteWord(&scl
, 0);
2014 saveSCLCargador(&scl
);
2019 while (findChunkFrom(start
, &start
, &len
)) {
2020 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
2021 sclStartFile(&scl
, cname
, 'C', (unsigned)start
, (unsigned)len
);
2022 sclWriteData(&scl
, memory
+(unsigned)start
, (unsigned)len
);
2026 if (sclSaveToFile(fo
, &scl
) < 0) {
2027 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
2034 printf("out: %s\n", fname
);
2035 if (fname
!= NULL
) free(fname
);
2039 ///////////////////////////////////////////////////////////////////////////////
2042 static int saveDMB (const char *fname
) {
2043 char *fn
;// = malloc(strlen(fname)+16);
2048 fn
= strprintf("%s/%s.dmb", optOutputDir
, fname
);
2049 fo
= fopen(fn
, "wb");
2051 if (!fo
) { fprintf(stderr
, "ERROR: can't write DMB file: %s.dmb\n", fname
); return -1; }
2053 while (findChunkFrom(start
, &start
, &len
)) { ++pcnt
; start
+= len
; }
2055 printf("out: %s.dmb\n", fname
);
2056 if (fwrite("DMB1", 4, 1, fo
) != 1) goto error
;
2057 if (fWriteWord(fo
, (optRunDMB
? ent
: 0), NULL
) != 0) goto error
;
2058 if (fWriteWord(fo
, clrAddr
, NULL
) != 0) goto error
;
2059 if (fWriteWord(fo
, pcnt
, NULL
) != 0) goto error
;
2062 while (findChunkFrom(start
, &start
, &len
)) {
2063 if (fWriteWord(fo
, len
, NULL
) != 0) goto error
;
2064 if (fWriteWord(fo
, start
, NULL
) != 0) goto error
;
2065 if (fwrite(memory
+start
, len
, 1, fo
) != 1) goto error
;
2073 fprintf(stderr
, "ERROR: error writing file %s.dmb!\n", fname
);
2078 ///////////////////////////////////////////////////////////////////////////////
2081 static char tapeLoaderName
[16];
2084 static void saveTapCargador (FILE *fo
) {
2086 static uint8_t cargador
[16384]; // should be enough for everyone
2087 int start
= 0, len
, pos
, f
;
2091 void putStr (const char *s
) {
2092 for (; *s
; ++s
) cargador
[pos
++] = *s
;
2095 void putNum (int num
) {
2097 sprintf(buf
, "%d", num
);
2104 cargador
[0] = 0; cargador
[1] = 10;
2105 // size (will be fixed later)
2106 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\""); // CLEAR VAL "xxx"
2108 while (findChunkFrom(start
, &start
, &len
)) {
2110 putStr(":\xef\"\"\xaf");
2114 // :RANDOMIZE USR VAL "xxx"
2115 putStr(":\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
2117 cargador
[2] = (pos
-4)&0xff;
2118 cargador
[3] = ((pos
-4)>>8)&0xff;
2120 fWriteWord(fo
, 19, NULL
); // length of header
2122 fWriteByte(fo
, 0, &bxor
); // header block
2123 fWriteByte(fo
, 0, &bxor
); // 'basic' flag
2124 if (tapeLoaderName
[0]) {
2125 for (int f
= 0; f
< 10; ++f
) fWriteByte(fo
, tapeLoaderName
[f
], &bxor
);
2127 fWriteByte(fo
, 'c', &bxor
);
2128 fWriteByte(fo
, 'a', &bxor
);
2129 fWriteByte(fo
, 'r', &bxor
);
2130 fWriteByte(fo
, 'g', &bxor
);
2131 fWriteByte(fo
, 'a', &bxor
);
2132 fWriteByte(fo
, 'd', &bxor
);
2133 fWriteByte(fo
, 'o', &bxor
);
2134 fWriteByte(fo
, 'r', &bxor
);
2135 fWriteByte(fo
, ' ', &bxor
);
2136 fWriteByte(fo
, ' ', &bxor
);
2138 fWriteWord(fo
, pos
, &bxor
); // length
2139 fWriteWord(fo
, 10, &bxor
); // start
2140 fWriteWord(fo
, pos
, &bxor
); // length2
2141 fWriteByte(fo
, bxor
, NULL
);
2143 fWriteWord(fo
, pos
+2, NULL
); // plus type and checkbyte
2145 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
2146 for (f
= 0; f
< pos
; ++f
) fWriteByte(fo
, cargador
[f
], &bxor
);
2147 fWriteByte(fo
, bxor
, NULL
);
2151 static void saveTap (const char *basename
) {
2152 char *fname
;// = malloc(strlen(basename)+16);
2154 int start
= 0, len
, f
;
2158 fname
= strprintf("%s/%s.tap", optOutputDir
, basename
);
2159 fo
= fopen(fname
, "wb");
2161 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
2162 printf("out: %s.tap\n", basename
);
2163 if (optRunTape
) saveTapCargador(fo
);
2164 while (findChunkFrom(start
, &start
, &len
)) {
2166 if (tapeLoaderName
[0]) {
2167 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2168 memcpy(blkname
, tapeLoaderName
, 10);
2170 sprintf(blkname
, "c%04X:%04X", start
, len
);
2172 //printf(" block: %s\n", blkname);
2173 fWriteWord(fo
, 19, NULL
); // length of header
2175 fWriteByte(fo
, 0, &bxor
); // header block
2176 fWriteByte(fo
, 3, &bxor
); // 'code' flag
2177 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
2178 fWriteWord(fo
, len
, &bxor
);
2179 fWriteWord(fo
, start
, &bxor
);
2180 fWriteWord(fo
, 32768, &bxor
);
2181 fWriteByte(fo
, bxor
, NULL
);
2183 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
2185 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
2186 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
2187 fWriteByte(fo
, bxor
, NULL
);
2194 ///////////////////////////////////////////////////////////////////////////////
2195 // pseudoinstructions
2197 // note that processCurrentLine() will NOT skip to the next line before
2198 // calling pseudoinstruction handler!
2199 // note that processCurrentLine() will skip current line after calling
2200 // pseudoinstruction handler!
2202 static int wasOrg
= 0;
2203 static int wasClr
= 0;
2206 // ////////////////////////////////////////////////////////////////////////// //
2207 // print message using printf-like syntax
2208 // doesn't print new line
2209 static void processPrintf (FILE *fo
, const char *fmt
) {
2210 if (!fmt
|| !fmt
[0]) return;
2211 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
2212 char *tempstr
= NULL
;
2213 size_t tempsize
= 0;
2214 char *fmtcopy
= malloc(strlen(fmt
)+1);
2215 strcpy(fmtcopy
, fmt
);
2216 char *currfmt
= fmtcopy
;
2224 char *prcs
= strchr(currfmt
, '%');
2225 if (!prcs
|| !prcs
[1]) {
2226 /* no more formatting; print the tail and exit the loop */
2227 fprintf(fo
, "%s", currfmt
);
2232 if (prcs
[1] == '%') {
2236 /* print up to `prcs` */
2237 if (prcs
> currfmt
) {
2238 size_t partlen
= (ptrdiff_t)(prcs
-currfmt
);
2239 if (partlen
+1 > tempsize
) {
2240 tempsize
= ((partlen
+8)|0xff)+1;
2241 tempstr
= realloc(tempstr
, tempsize
);
2243 memcpy(tempstr
, currfmt
, partlen
);
2244 tempstr
[partlen
] = 0;
2245 fprintf(fo
, "%s", tempstr
);
2247 currfmt
= ++prcs
; /* skip percent */
2248 if (!docheck
) continue;
2254 if (*currfmt
== '+' || *currfmt
== '-') sign
= *currfmt
++;
2255 if (sign
!= '-' && *currfmt
== '0') zerofill
= 1;
2256 while (isDigit(*currfmt
)) { width
= width
*10+((*currfmt
)-'0'); ++currfmt
; }
2257 if (width
> 256) width
= 256;
2259 if (!ftype
) break; /* oops */
2260 if (!eatComma()) fatal("out of arguments for string format");
2262 case 's': /* string */
2264 switch (strSkipSpaces(currLine
)[0]) {
2265 case '"': case '\'': /* string literal? */
2266 strarg
= getStrArg(&stlen
);
2268 default: /* expression */
2269 strarg
= getStrExprArg();
2270 stlen
= (int)strlen(strarg
);
2274 if (sign
!= '-' && stlen
< width
) {
2275 int padlen
= width
-stlen
;
2276 memset(tempbuf
, ' ', padlen
);
2277 tempbuf
[padlen
+1] = 0;
2278 fprintf(fo
, "%s", tempbuf
);
2280 fprintf(fo
, "%s", strarg
);
2282 if (sign
== '-' && stlen
< width
) {
2283 int padlen
= width
-stlen
;
2284 memset(tempbuf
, ' ', padlen
);
2285 tempbuf
[padlen
+1] = 0;
2286 fprintf(fo
, "%s", tempbuf
);
2289 case 'd': /* decimal */
2291 exprval
= getExprArg(&defined
, NULL
);
2292 if (width
&& zerofill
) {
2293 fprintf(fo
, "%0*d", width
, exprval
);
2295 fprintf(fo
, "%*d", (sign
!= '-' ? width
: -width
), exprval
);
2297 fprintf(fo
, "%d", exprval
);
2300 case 'c': /* char */
2302 exprval
= getExprArg(&defined
, NULL
);
2303 if (exprval
<= 0 || exprval
== '?' || exprval
> 255) exprval
= '?';
2305 fprintf(fo
, "%*c", (sign
!= '-' ? width
: -width
), exprval
);
2307 fprintf(fo
, "%c", exprval
);
2310 case 'u': /* unsigned */
2312 exprval
= getExprArg(&defined
, NULL
);
2313 if (width
&& zerofill
) {
2314 fprintf(fo
, "%0*u", width
, (unsigned)(exprval
&0xffff));
2316 fprintf(fo
, "%*u", (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
2318 fprintf(fo
, "%u", (unsigned)(exprval
&0xffff));
2321 case 'x': case 'X': /* hex */
2323 exprval
= getExprArg(&defined
, NULL
);
2324 if (width
&& zerofill
) {
2325 fprintf(fo
, (ftype
== 'x' ? "%0*x" : "%0*X"), width
, (unsigned)(exprval
&0xffff));
2327 fprintf(fo
, (ftype
== 'x' ? "%*x" : "%*X"), (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
2329 fprintf(fo
, (ftype
== 'x' ? "%x" : "%X"), (unsigned)(exprval
&0xffff));
2333 if (ftype
<= 0 || ftype
== 127) ftype
= '?';
2334 fatal("invalid format specifier: '%c'", ftype
);
2338 if (tempstr
) free(tempstr
);
2343 ///////////////////////////////////////////////////////////////////////////////
2346 static int piERROR (void) {
2348 char *res
= getStrArg(&len
);
2349 fprintf(stdout
, "*** USER ERROR: ");
2350 processPrintf(stdout
, res
);
2351 fputc('\n', stdout
);
2352 fatal("user error abort");
2353 return PI_SKIP_LINE
;
2357 static int piWARNING (void) {
2359 char *res
= getStrArg(&len
);
2360 fprintf(stdout
, "*** USER WARNING ");
2362 fprintf(stdout
, "at file %s, line %d: ", curSrcLine
->fname
, curSrcLine
->lineNo
);
2364 fprintf(stdout
, "somewhere in time: ");
2366 processPrintf(stdout
, res
);
2367 fputc('\n', stdout
);
2368 return PI_SKIP_LINE
;
2372 ///////////////////////////////////////////////////////////////////////////////
2375 static int piPrintfCommon (int passNo
) {
2376 if (passNo
< 0 || pass
== passNo
) {
2378 char *res
= getStrArg(&len
);
2379 processPrintf(stdout
, res
);
2380 fputc('\n', stdout
);
2382 return PI_SKIP_LINE
;
2386 static int piDISPLAYX (int passNo
, int asHex
) {
2390 char *res
= getStrArg(&len
);
2392 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
2395 int32_t v
= getExprArg(&defined
, NULL
);
2397 if (passNo
< 0 || pass
== passNo
) {
2398 if (asHex
) printf("%04X", (unsigned int)v
);
2399 else printf("%d", v
);
2402 if (!eatComma()) break;
2404 return PI_SKIP_LINE
;
2408 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
2409 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
2410 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
2411 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
2412 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
2413 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
2415 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
2416 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
2417 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
2420 ///////////////////////////////////////////////////////////////////////////////
2423 static int piORG (void) {
2425 int32_t res
= getOneExprArg(&defined
, NULL
);
2427 if (!defined
) fatal("sorry, ORG operand value must be known here");
2428 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
2429 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
2434 if (!wasClr
&& res
> 0) clrAddr
= res
-1;
2436 return PI_CONT_LINE
;
2440 static int piDISP (void) {
2442 int32_t res
= getOneExprArg(&defined
, NULL
);
2444 if (!defined
) fatal("sorry, DISP operand value must be known here");
2445 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
2446 //printf("DISP=%d\n", res);
2448 return PI_CONT_LINE
;
2452 static int piENDDISP (void) {
2453 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
2456 return PI_CONT_LINE
;
2460 static int piENT (void) {
2462 int32_t res
= getOneExprArg(&defined
, NULL
);
2464 //if (!defined) fatal("sorry, ENT operand value must be known here");
2465 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
2467 return PI_CONT_LINE
;
2471 static int piCLR (void) {
2473 int32_t res
= getOneExprArg(&defined
, NULL
);
2475 //if (!defined) fatal("sorry, CLR operand value must be known here");
2476 if (res
< 0 || res
> 65535) fatal("invalid CLR operand value: %d", res
);
2479 return PI_CONT_LINE
;
2483 static int piRESERVE (void) {
2485 int defined = 1, start;
2486 int32_t res = getExprArg(&defined, NULL);
2487 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2488 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2490 if (!eatComma()) fatal("RESERVE needs 2 args");
2491 res = getOneExprArg(&defined, NULL);
2492 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2493 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2494 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
2496 fatal("RESERVE: not yet!");
2497 return PI_CONT_LINE
;
2501 static int piALIGN (void) {
2505 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
2506 res
= getOneExprArg(&defined
, NULL
);
2507 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
2508 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
2509 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
2510 if (res
> 0 && pc
%res
!= 0) {
2516 return PI_CONT_LINE
;
2520 static int piDISPALIGN (void) {
2522 int32_t res
= getOneExprArg(&defined
, NULL
);
2524 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
2525 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
2526 if (res
> 0 && disp
%res
!= 0) {
2531 return PI_CONT_LINE
;
2535 ///////////////////////////////////////////////////////////////////////////////
2538 static int defIncr
= 0;
2541 static int piDEFINCR (void) {
2543 int32_t cnt
= getOneExprArg(&defined
, NULL
);
2545 if (!defined
) fatal("DEFINCR: increment must be defined");
2547 return PI_CONT_LINE
;
2551 static int piDEFBW (int isWord
) {
2553 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2557 char *res
= getStrArg(&len
);
2559 for (f
= 0; f
< len
; ++f
) {
2560 int32_t b
= (uint8_t)res
[f
];
2562 if (b
< -128 || b
> 255) fatal("invalid operand value: %d", b
);
2563 if (b
< 0) b
+= 256;
2567 int32_t res
= getExprArg(&defined
, &fixuptype
);
2569 if (pass
> 0 && !defined
) fatal("undefined operand");
2572 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
2573 if (res
< 0) res
+= 65536;
2575 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 2);
2579 switch (fixuptype
) {
2580 case UR_FIXUP_WORD
: /* swapped bytes */
2581 urasm_fixup_operand(NULL
, pc
, disp
, UR_FIXUP_HIBYTE
, 1);
2582 urasm_fixup_operand(NULL
, pc
, disp
+1, UR_FIXUP_LOBYTE
, 1);
2584 case UR_FIXUP_LOBYTE
:
2585 case UR_FIXUP_HIBYTE
:
2586 warningMsg("non-word fixup for reversed word; wtf?!");
2593 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
2594 if (fixuptype
!= UR_FIXUP_NONE
) {
2595 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
2596 urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
2598 if (res
< 0) res
+= 256;
2602 if (!eatComma()) break;
2604 return PI_CONT_LINE
;
2607 static int piDEFB (void) { return piDEFBW(0); }
2608 static int piDEFW (void) { return piDEFBW(1); }
2609 static int piDEFR (void) { return piDEFBW(2); }
2612 static int piDEFS (void) {
2615 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2616 int32_t res
= getExprArg(&defined
, &fixuptype
);
2618 if (pass
> 0 && !defined
) fatal("undefined operand");
2619 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
2620 if (*currLine
&& currLine
[0] == ',') {
2622 bt
= getExprArg(&defined
, NULL
);
2623 if (pass
> 0 && !defined
) fatal("undefined operand");
2625 if (bt
< -128 || bt
> 255) fatal("invalid operand value: %d", res
);
2626 if (bt
< 0) bt
+= 256;
2627 if (fixuptype
!= UR_FIXUP_NONE
) {
2628 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
2630 for (f
= 0; f
< res
; ++f
) {
2631 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
2635 pc
+= res
; disp
+= res
;
2637 if (!eatComma()) break;
2639 return PI_CONT_LINE
;
2643 /* bit 0: put '\0' */
2644 /* bit 1: set bit 7 of last byte */
2645 /* bit 2: put length */
2646 static int piDEFSTR (int type
) {
2650 char *res
= getStrArg(&len
);
2653 if (len
> 255) fatal("string too long");
2656 for (f
= 0; f
< len
; ++f
) {
2657 uint8_t b
= (uint8_t)res
[f
];
2659 if ((type
&0x02) && f
== len
-1) b
|= 0x80;
2662 if (type
&0x01) emitByte(0);
2665 int32_t v
= getExprArg(&defined
, NULL
);
2667 if (pass
> 0 && !defined
) fatal("undefined expression");
2668 if (!defined
) v
= 0; else v
+= defIncr
;
2669 if (v
< -128 || v
> 255) fatal("invalid operand value: %d", v
);
2670 if (v
< 0) v
+= 256;
2673 if (!eatComma()) break;
2675 return PI_CONT_LINE
;
2679 static int piDEFM (void) { return piDEFSTR(0x00); }
2680 static int piDEFZ (void) { return piDEFSTR(0x01); }
2681 static int piDEFX (void) { return piDEFSTR(0x02); }
2682 static int piDEFC (void) { return piDEFSTR(0x04); }
2685 ///////////////////////////////////////////////////////////////////////////////
2688 /* INCBIN "name"[,maxlen] */
2689 static int piINCBIN (void) {
2695 char *args
= currLine
;
2697 if (!currLine
[0]) fatal("INCBIN without file name");
2701 } else if (currLine
[0] == '<') {
2708 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
2709 if (!fn
[0]) fatal("INCBIN: empty file name");
2710 memmove(currLine
, args
, strlen(args
)+1);
2712 if (currLine
[0] == ',') {
2715 maxlen
= getOneExprArg(&defined
, NULL
);
2716 if (!defined
) fatal("INCBIN: undefined maxlen");
2717 if (maxlen
< 1) return 1; // nothing to do
2720 char *fname
= createIncludeName(fn
, system
, NULL
);
2721 fl
= fopen(fname
, "rb");
2722 if (!fl
) fatal("INCBIN: file not found: '%s'", fname
);
2723 while (maxlen
-- > 0) {
2724 int res
= fread(&bt
, 1, 1, fl
);
2726 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: '%s'", fname
); }
2731 return PI_SKIP_LINE
;
2735 ///////////////////////////////////////////////////////////////////////////////
2738 /* INCLUDE "name" */
2739 static int piINCLUDE (void) {
2742 char *args
= currLine
;
2744 if (!currLine
[0]) fatal("INCLUDE without file name");
2748 } else if (currLine
[0] == '<') {
2755 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
2756 if (!fn
[0]) fatal("INCLUDE: empty file name");
2758 if (asmTextInclude(fn
, system
) != 0) fatal("INCLUDE: some shit happens!");
2759 return PI_SKIP_LINE
;
2763 ///////////////////////////////////////////////////////////////////////////////
2764 // MODULE, ENDMODULE
2766 static int piENDMODULE (void) {
2767 if (!curModule
) fatal("ENDMODULE without MODULE");
2769 char *mn
= getOneLabelArg();
2770 if (strcmp(mn
, curModule
->name
)) fatal("invalid module name in ENDMODULE: %s", mn
);
2773 return PI_SKIP_LINE
;
2777 static int piMODULE (void) {
2780 SourceLine
*ol
= curSrcLine
;
2783 if (curModule
) fatal("no nested modules allowed");
2784 mn
= getOneLabelArg();
2785 if (!urasm_is_valid_name(mn
)) fatal("invalid module name: %s", mn
);
2786 mi
= moduleFind(mn
);
2787 //printf("[%s] %p\n", mn, mi);
2790 if (strcmp(mi
->fname
, curSrcLine
->fname
)) fatal("duplicate module definition; previous was in %s", mi
->fname
);
2792 nextSrcLine(); /* skip "MODULE" line */
2793 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
2795 fatal("no ENDMODULE");
2797 if (inum
== 0) fatal("no nested modules allowed");
2799 //skipInstruction(); //k8:wtf?!
2800 return piENDMODULE();
2803 mi
= moduleAdd(mn
, curSrcLine
->fname
);
2807 return PI_SKIP_LINE
;
2812 static int piMODEL (void) {
2813 char *mn
= getOneIdArgLo();
2814 if (strSkipSpaces(currLine
)[0]) fatal("only one model name expected");
2815 if (strcmp(mn
, "z80") == 0) urasm_allow_zxnext
= 0;
2816 else if (strcmp(mn
, "z80a") == 0) urasm_allow_zxnext
= 0;
2817 else if (strcmp(mn
, "z80n") == 0) urasm_allow_zxnext
= 1;
2818 else if (strcmp(mn
, "z80next") == 0) urasm_allow_zxnext
= 1;
2819 else if (strcmp(mn
, "zxnext") == 0) urasm_allow_zxnext
= 1;
2820 else fatal("invalid model name: %s", mn
);
2821 return PI_SKIP_LINE
;
2825 ///////////////////////////////////////////////////////////////////////////////
2828 static int piEDUP (void) {
2829 fatal("EDUP without DUP");
2831 return PI_SKIP_LINE
;
2835 static int piDUP (void) {
2836 int defined
= 1, dupCnt
= 1, inum
;
2837 SourceLine
*stline
, *eline
= NULL
;
2838 int32_t cnt
= getOneExprArg(&defined
, NULL
);
2840 if (!defined
) fatal("DUP: counter must be defined");
2841 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
2842 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
2843 // now find corresponding EDUP
2844 // note that we should skip nested DUPs
2845 nextSrcLine(); // skip ourself
2846 stline
= curSrcLine
;
2847 while (curSrcLine
) {
2848 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "EDUP", NULL
))) break;
2849 // ok, we found something; what is it?
2853 nextSrcLine(); // skip DUP
2856 if (--dupCnt
== 0) {
2861 nextSrcLine(); // skip EDUP
2864 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
2865 // now repeat that lines
2867 setCurSrcLine(stline
);
2868 while (curSrcLine
!= eline
) processCurrentLine();
2870 return PI_SKIP_LINE
;
2874 ///////////////////////////////////////////////////////////////////////////////
2877 static int ifCount
= 0;
2881 // -1: error (should not happen)
2882 // 0: successfully complete
2883 // 1: stopped *AT* "ELSE"
2884 // 2: stopped *AT* "ELSEIF"
2885 static int ifSkipToEndIfOrElse (int wholeBody
) {
2886 int inum
, wasElse
= 0;
2889 while (curSrcLine
) {
2890 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL
))) break;
2894 nextSrcLine(); // skip IF
2895 ifSkipToEndIfOrElse(1); // and recurse
2896 nextSrcLine(); // skip ENDIF
2899 if (wasElse
) fatal("duplicate ELSE");
2900 if (!wholeBody
) return 1;
2902 nextSrcLine(); // skip ELSE
2903 break; // and continue
2905 return 0; // and exit
2908 if (wasElse
) fatal("ELSEIF in ELSE");
2909 if (!wholeBody
) return 2;
2910 nextSrcLine(); // skip ELSEIF
2911 break; // and continue
2913 // skip it as a whole
2914 nextSrcLine(); // skip MACRO
2917 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) { setCurSrcLine(oline
); fatal("invalid MACRO"); }
2918 if (inum
== 1) break;
2919 fatal("invalid nested MACRO");
2921 nextSrcLine(); // skip ENDM
2924 fatal("unexpected ENDM");
2927 fatal("IF without ENDIF");
2932 static int piENDIF (void) {
2933 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2934 if (--ifCount
< 0) fatal("ENDIF without IF");
2936 return PI_SKIP_LINE
;
2940 static int piELSE (void) {
2941 if (--ifCount
< 0) fatal("ELSE without IF");
2942 nextSrcLine(); // skip ELSE
2943 ifSkipToEndIfOrElse(1);
2944 return PI_SKIP_LINE
;
2947 static int piELSEIF (void) {
2948 if (--ifCount
< 0) fatal("ELSEIF without IF");
2949 nextSrcLine(); // skip ELSEIF
2950 ifSkipToEndIfOrElse(1);
2951 return PI_SKIP_LINE
;
2955 static int piELSEIFX (void) {
2956 if (--ifCount
< 0) fatal("ELSEIFX without IF");
2957 nextSrcLine(); // skip ELSEIFX
2958 ifSkipToEndIfOrElse(1);
2959 return PI_SKIP_LINE
;
2963 static int piIFAll (int isIfX
) {
2965 int ooo
= lblOptMakeU2
;
2966 lblOptMakeU2
= (isIfX
? 1 : 0);
2967 int32_t cond
= getOneExprArg(&defined
, NULL
);
2971 if (!isIfX
) fatal("IF: condition must be defined");
2972 cond
= 0; // for IFX: 0 if there is any undefined label
2975 // ok, do it until ELSE/ELSEIF/ENDIF
2977 return PI_SKIP_LINE
;
2983 nextSrcLine(); // skip last instruction
2984 // skip until ELSE/ELSEIF/ENDIF
2985 r
= ifSkipToEndIfOrElse(0);
2986 /*!fprintf(stderr, "ELSEIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2987 if (r
== 0) break; // ENDIF
2988 if (r
== 1) { ++ifCount
; break; } // ELSE
2989 // ELSEIF, do condition
2990 args
= strIsCommand("ELSEIF", currLine
);
2994 if ((args
= strIsCommand("ELSEIFX", currLine
)) == NULL
) fatal("internal error in conditionals"); // the thing that should not be
2997 memmove(currLine
, args
, strlen(args
)+1);
2998 /*!fprintf(stderr, "ELSEIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2999 cond
= getOneExprArg(&defined
, NULL
);
3000 /*!fprintf(stderr, "ELSEIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0), cond);*/
3002 if (!isIfX
) fatal("ELSEIF: condition must be defined");
3003 cond
= 0; // for IFX: 0 if there is any undefined label
3005 if (cond
) { ++ifCount
; break; } // condition is true
3007 return PI_SKIP_LINE
;
3011 static int piIF (void) { return piIFAll(0); }
3012 static int piIFX (void) { return piIFAll(1); }
3015 ///////////////////////////////////////////////////////////////////////////////
3017 ///////////////////////////////////////////////////////////////////////////////
3019 what i did with MACRO is the brain-damaged cheating all the way.
3021 first, i will collect the MACRO body and remember it (removing it
3022 from the main source code, so second pass will not see it).
3024 second, when the macro is used, i will:
3025 * insert the whole macro body in place (label resolver will
3026 fix "..lbl" labels for us)
3027 * let the asm play with it
3029 this is not the best scheme, but it is fairly simple and it works.
3032 // will be called when parser encounters term starting with '=' or '*'
3033 // first term char will not be skipped
3034 // must return pointer to the first char after expression end
3035 static const char *getValueCB (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3039 if (curmacro
== NULL
) fatal("'=' outside of macro");
3040 if (*expr
++ != '=') fatal("'=' expected!");
3042 expr
= strSkipSpaces(expr
);
3044 if (isAlphaDigit(*expr
) || *expr
== '_') {
3045 if (p
-name
> 250) fatal("id too long");
3053 expr
= strSkipSpaces(expr
);
3054 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
3055 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
3058 int l
= (int)strlen(curmacro
->argvals
[f
]);
3060 urasm_exprval_init(&v
);
3061 expr
= urasm_expr_ex(&v
, expr
, addr
, &donteval
, defined
, error
);
3062 if (*error
) return expr
;
3063 if (expr
== NULL
|| *expr
!= ']' || v
.str
!= NULL
) { *error
= UR_EXPRERR_FUNC
; return expr
; }
3065 if (v
.val
< 0) v
.val
+= l
;
3066 if (v
.val
< 0 || v
.val
>= l
) {
3069 res
->val
= (unsigned char)(curmacro
->argvals
[f
][v
.val
]);
3073 urasm_expr_ex(res
, curmacro
->argvals
[f
], addr
, &donteval
, defined
, error
);
3079 fatal("unknown macro variable: '%s'", name
);
3083 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
3084 // opr starts with '=' (invariant)
3085 static int expandCB (char *opr
, int oprlen
) {
3086 char name
[257], *p
= name
, *op
= opr
;
3088 if (curmacro
== NULL
) fatal("'=' outside of macro");
3089 if (*op
++ != '=') fatal("'=' expected!"); // just in case
3090 //fprintf(stderr, "expand: [%s]\n", opr);
3092 if (!isAlpha(op
[0])) return 0; // nothing to do
3094 // copy argument name
3096 if (isAlphaDigit(*op
) || *op
== '_') {
3097 if (p
-name
> 250) fatal("id too long");
3105 // expand argument? we only need to expand `=arg[n]`
3106 op
= strSkipSpaces(op
);
3107 if (op
[0] != '[') return 0;
3109 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
3110 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
3111 if (*op
!= '[') abort(); // assertion, just in case
3112 // replace argument with character, or with literal value
3113 // used for `=regpair[0]` or `=regpair[]`
3116 const int l
= (int)strlen(curmacro
->argvals
[f
]);
3117 const int lleft
= (int)strlen(op
);
3118 char *tmp
= malloc(l
+lleft
+8);
3119 snprintf(tmp
, l
+lleft
+8, "%s%s", curmacro
->argvals
[f
], op
);
3120 if ((int)strlen(tmp
) > oprlen
) {
3129 int error
= 0, defined
= 1, donteval
= 0;
3131 urasm_exprval_init(&v
);
3132 op
= (char *)urasm_expr_ex(&v
, op
, pc
, &donteval
, &defined
, &error
);
3133 if (error
) return -1;
3134 // result should be a number
3135 if (op
== NULL
|| *op
!= ']' || v
.str
!= NULL
) return -1;
3137 // it is guaranteed to have more than one char in opr
3138 // so we can simply put char from argument value, and remove other expression chars
3139 const int l
= (int)strlen(curmacro
->argvals
[f
]);
3140 if (v
.val
< 0) v
.val
+= l
; // negative: indexing from the end
3141 if (v
.val
< 0 || v
.val
>= l
) fatal("index %d is out of bounds for macro argument '%s'", v
.val
, curmacro
->mac
->argnames
[f
]);
3143 opr
[0] = curmacro
->argvals
[f
][v
.val
];
3144 // remove other chars
3145 memmove(opr
+1, op
, strlen(op
)+1);
3151 fatal("unknown macro variable: '%s'", name
);
3155 // main macro expander
3156 static void processMacro (MacroDef
*mc
) {
3157 SourceLine
*oldcurline
= curSrcLine
;
3160 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
3161 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
3163 // parse macro arguments
3165 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) cm
.argvals
[f
] = NULL
;
3169 // do we have more arguments?
3170 if (!currLine
[0]) break;
3171 // check for argument name (this is purely cosmetic thing)
3172 // use like this: "mcall :argname=[value]" (value may be omited for default)
3173 if (currLine
[0] == ':' && isAlpha(currLine
[1])) {
3175 while (isAlphaDigit(currLine
[pos
]) || currLine
[pos
] == '_') ++pos
;
3177 const char svch
= currLine
[pos
];
3179 if (strcasecmp(currLine
+1, mc
->argnames
[currArg
]) != 0) {
3180 // out-of-order, find proper argument and rewind to it
3182 for (int c
= 0; c
< mc
->argc
; ++c
) {
3183 if (strcasecmp(currLine
+1, mc
->argnames
[c
]) == 0) {
3188 if (currArg
< 0) fatal("macro '%s' has no argument named '%s'", mc
->name
, currLine
+1);
3190 currLine
[pos
] = svch
;
3191 // remove argument name
3192 memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
3195 if (currLine
[0] != '=') fatal("expected '=' after argument name");
3197 memmove(currLine
, currLine
+1, strlen(currLine
));
3201 if (currArg
>= mc
->argc
) fatal("too many arguments to macro '%s'", mc
->name
);
3202 // check for default value
3203 if (currLine
[0] == ',') {
3204 if (mc
->argdefaults
[currArg
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[currArg
]);
3205 cm
.argvals
[currArg
] = strdup(mc
->argdefaults
[currArg
]);
3206 memmove(currLine
, currLine
+1, strlen(currLine
));
3208 // skip argument (so we will know its length, and will be able to copy it)
3209 char *e
= skipMacroArg(currLine
);
3210 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
3211 const char ech
= *e
;
3213 if (ech
== ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc
->name
);
3214 if (ech
!= ',') fatal("invalid invocation of macro '%s'", mc
->name
);
3217 cm
.argvals
[currArg
] = strdup(currLine
);
3218 // strip trailing spaces
3219 strTrimRight(cm
.argvals
[currArg
]);
3222 memmove(currLine
, e
+1, strlen(e
));
3229 // check for line end
3231 if (currLine
[0]) fatal("invalid macro invocation");
3233 // setup default argument values
3234 for (int f
= 0; f
< mc
->argc
; ++f
) {
3235 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
3236 if (cm
.argvals
[f
]) continue;
3237 if (mc
->argdefaults
[f
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[f
]);
3238 cm
.argvals
[f
] = strdup(mc
->argdefaults
[f
]);
3239 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
3242 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
3244 // insert macro code into the source
3245 setCurSrcLine(mc
->lines
);
3248 while (curSrcLine
!= NULL
) {
3249 //fprintf(stderr, "*[%s] (%d)\n", curSrcLine->line, curSrcLine->lineNo);
3250 processCurrentLine();
3253 setCurSrcLine(oldcurline
);
3259 static int piMACRO (void) {
3262 char *argdefaults
[MAX_MACRO_ARGS
];
3263 char *argnames
[MAX_MACRO_ARGS
];
3264 SourceLine
*stline
, *eline
= NULL
;
3267 name
= strdup(getLabelArg(0));
3268 //fprintf(stderr, "[%s]\n", name);
3269 if (findMacro(name
) != NULL
) fatal("duplicate MACRO definition: '%s'", name
);
3270 if (currLine
[0] == ',') memmove(currLine
, currLine
+1, strlen(currLine
));
3273 while (currLine
[0]) {
3274 if (argc
>= MAX_MACRO_ARGS
) fatal("too many arguments in MACRO");
3275 if (!isAlpha(currLine
[0])) fatal("invalid MACRO definition");
3276 argnames
[argc
] = strdup(getLabelArg(0));
3277 if (currLine
[0] == '=') {
3279 char *e
= strchr(currLine
, ','), tch
;
3281 if (e
== NULL
) e
= currLine
+strlen(currLine
);
3284 argdefaults
[argc
] = strdup(currLine
+1);
3286 memmove(currLine
, e
, strlen(e
)+1);
3288 argdefaults
[argc
] = NULL
;
3291 if (currLine
[0] == ',') {
3292 memmove(currLine
, currLine
+1, strlen(currLine
));
3295 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
3299 // now find corresponding ENDM
3300 // note that we should skip nested DUPs
3301 stline
= curSrcLine
;
3302 nextSrcLine(); // skip ourself
3303 while (curSrcLine
) {
3306 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) break;
3307 // ok, we found something; what is it?
3310 fatal("no nested MACROs yet");
3316 nextSrcLine(); // skip ENDM
3320 if (!eline
) { setCurSrcLine(stline
); fatal("no ENDM"); }
3322 if ((mc
= calloc(1, sizeof(*mc
))) == NULL
) fatal("out of memory");
3325 for (int f
= 0; f
< argc
; ++f
) { mc
->argdefaults
[f
] = argdefaults
[f
]; mc
->argnames
[f
] = argnames
[f
]; }
3328 mc
->lines
= stline
->next
;
3329 stline
->next
= curSrcLine
;
3330 stline
->line
[0] = 0;
3334 setCurSrcLine(stline
);
3335 return PI_SKIP_LINE
;
3339 static int piENDM (void) {
3340 fatal("ENDM without MACRO");
3341 return PI_SKIP_LINE
;
3345 ///////////////////////////////////////////////////////////////////////////////
3347 static int optWriteType
= 't';
3348 static int optWTChanged
= 0;
3351 static void piTapParseLoaderName (void) {
3354 if (!isStrArg()) fatal("loader name expected");
3355 char *fn
= getStrArg(&len
);
3356 if (len
> 10) fatal("loader name too long");
3357 memset(tapeLoaderName
, ' ', 10);
3358 for (int f
= 0; f
< len
; ++f
) tapeLoaderName
[f
] = fn
[f
];
3363 static int piMATHMODE (void) {
3364 char *name
= getOneLabelArg();
3365 if (strcasecmp(name
, "OLD") == 0) urasm_use_old_priorities
= 1;
3366 else if (strcasecmp(name
, "NEW") == 0) urasm_use_old_priorities
= 0;
3367 else fatal("invalid math mode; NEW or OLD expected");
3368 return PI_SKIP_LINE
;
3372 static int piDEFFMT (void) {
3377 name
= getStrArg(&len
);
3379 name
= getLabelArg(1);
3381 if (optWTChanged
) return 1;
3383 optRunDMB
= optRunTape
= optRunSCL
= 0;
3386 if (!strcasecmp(name
, "SNA") || !strcasecmp(name
, "RUNSNA") || !strcasecmp(name
, "SNARUN")) {
3388 if (currLine
[0]) fatal("too many expressions");
3389 return PI_SKIP_LINE
;
3391 if (!strcasecmp(name
, "TAP") || !strcasecmp(name
, "TAPE")) {
3393 piTapParseLoaderName();
3394 return PI_SKIP_LINE
;
3396 if (!strcasecmp(name
, "RUNTAP") || !strcasecmp(name
, "RUNTAPE") || !strcasecmp(name
, "TAPERUN")) {
3399 piTapParseLoaderName();
3400 return PI_SKIP_LINE
;
3402 if (!strcasecmp(name
, "BIN") || !strcasecmp(name
, "RAW")) {
3404 if (currLine
[0]) fatal("too many expressions");
3405 return PI_SKIP_LINE
;
3407 if (!strcasecmp(name
, "DMB") || !strcasecmp(name
, "RUNDMB") || !strcasecmp(name
, "DMBRUN")) {
3408 optRunDMB
= (name
[3] != 0);
3410 if (currLine
[0]) fatal("too many expressions");
3411 return PI_SKIP_LINE
;
3413 if (!strcasecmp(name
, "NONE") || !strcasecmp(name
, "NOTHING")) {
3415 if (currLine
[0]) fatal("too many expressions");
3416 return PI_SKIP_LINE
;
3418 if (!strcasecmp(name
, "SCL") || !strcasecmp(name
, "RUNSCL") || !strcasecmp(name
, "SCLRUN")) {
3420 optRunSCL
= (name
[3] != 0);
3421 piTapParseLoaderName();
3422 return PI_SKIP_LINE
;
3424 fatal("invalid default output type: %s", name
);
3428 ///////////////////////////////////////////////////////////////////////////////
3431 static void processCurrentLine (void) {
3432 if (!curSrcLine
) return; // do nothing
3436 char *str
, *ee
, name
[66];
3441 removeSpacesAndColons();
3442 // skip spaces and ':'
3443 if (!currLine
[0]) { nextSrcLine(); break; }
3444 // try to find and process command
3445 str
= currLine
; //while (*str && isSpace(*str)) ++str; // skip spaces
3447 for (ee
= str
; *ee
&& !isSpace(*ee
) && *ee
!= '"' && *ee
!= '\'' && *ee
!= ',' && *ee
!= ':'; ++ee
) {}
3448 // get command, if any
3449 if (ee
!= str
&& ee
-str
<= 64) {
3451 memset(name
, 0, sizeof(name
));
3452 memmove(name
, str
, ee
-str
);
3453 /* known command? */
3454 op
= urFindOp(name
);
3457 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
3458 memmove(currLine
, str
, strlen(str
)+1);
3460 nextSrcLine(); // skip it
3466 if ((mc
= findMacro(name
)) != NULL
) {
3468 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
3469 memmove(currLine
, str
, strlen(str
)+1);
3471 /* only one macro per line! */
3476 len
= urasm_opasm(currLine
, pc
, disp
, &errpos
);
3477 if (len
< 0) fatalUrLib(len
);
3480 if (len
>= 0 && errpos
) {
3481 memmove(currLine
, errpos
+1, strlen(errpos
));
3483 nextSrcLine(); // skip it
3490 ///////////////////////////////////////////////////////////////////////////////
3491 // setup instructions
3493 static void registerInstructions (void) {
3494 urAddOp("DISPLAY", piDISPLAY
);
3495 urAddOp("DISPLAY0", piDISPLAY0
);
3496 urAddOp("DISPLAYA", piDISPLAYA
);
3497 urAddOp("DISPHEX", piDISPHEX
);
3498 urAddOp("DISPHEX0", piDISPHEX0
);
3499 urAddOp("DISPHEXA", piDISPHEXA
);
3501 urAddOp("DEFFMT", piDEFFMT
);
3502 urAddOp("$MODEL", piMODEL
);
3504 urAddOp("MACRO", piMACRO
);
3505 urAddOp("ENDM", piENDM
);
3507 urAddOp("ORG", piORG
);
3508 urAddOp("DISP", piDISP
);
3509 urAddOp("ENDDISP", piENDDISP
);
3510 urAddOp("PHASE", piDISP
);
3511 urAddOp("DEPHASE", piENDDISP
);
3512 urAddOp("UNPHASE", piENDDISP
);
3513 urAddOp("ALIGN", piALIGN
);
3514 urAddOp("DISPALIGN", piDISPALIGN
);
3515 urAddOp("PHASEALIGN", piDISPALIGN
);
3516 urAddOp("ENT", piENT
);
3517 urAddOp("CLR", piCLR
);
3518 urAddOp("RESERVE", piRESERVE
);
3520 urAddOp("INCLUDE", piINCLUDE
);
3521 urAddOp("INCBIN", piINCBIN
);
3523 urAddOp("MODULE", piMODULE
);
3524 urAddOp("ENDMODULE", piENDMODULE
);
3526 urAddOp("DUP", piDUP
);
3527 urAddOp("EDUP", piEDUP
);
3529 urAddOp("IF", piIF
);
3530 urAddOp("IFX", piIFX
);
3531 urAddOp("ELSE", piELSE
);
3532 urAddOp("ELSEIF", piELSEIF
);
3533 urAddOp("ELSEIFX", piELSEIFX
);
3534 urAddOp("ENDIF", piENDIF
);
3536 urAddOp("DEFINCR", piDEFINCR
);
3537 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
3538 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
3539 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
3540 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
3541 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
3542 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
3543 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
3544 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
3546 urAddOp("$ERROR", piERROR
);
3547 urAddOp("$WARNING", piWARNING
);
3549 urAddOp("$PRINTF", piPRINTF
);
3550 urAddOp("$PRINTF0", piPRINTF0
);
3551 urAddOp("$PRINTFA", piPRINTFA
);
3553 urAddOp("$MATHMODE", piMATHMODE
);
3557 ///////////////////////////////////////////////////////////////////////////////
3558 // !0: invalid label
3560 static inline void fnSkipSpaces (const char *expr) {
3561 while (*expr && isSpace(*expr)) ++expr;
3567 static inline char fnNextChar (const char *expr
) {
3568 while (*expr
&& isSpace(*expr
)) ++expr
;
3573 #define FN_SKIP_BLANKS do { \
3574 while (*expr && isSpace(*expr)) ++expr; \
3577 #define FN_CHECK_END do { \
3578 while (*expr && isSpace(*expr)) ++expr; \
3579 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
3584 #define FN_CHECK_COMMA do { \
3585 while (*expr && isSpace(*expr)) ++expr; \
3586 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
3591 static const char *readLabelName (char *buf
, const char *expr
) {
3594 while (*expr
&& isSpace(*expr
)) ++expr
;
3598 if (pos
>= 128) return NULL
;
3599 if (ch
== ')') { --expr
; break; }
3601 if (isAlphaDigit(ch
) || ch
== '$' || ch
== '.' || ch
== '_' || ch
== '@') {
3607 if (pos
< 1) return NULL
;
3609 if (!urasm_is_valid_name(buf
)) return NULL
;
3614 static const char *fnDefKn (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
, int qtype
) {
3617 if ((expr
= readLabelName(lbl
, expr
)) == NULL
) { *error
= UR_EXPRERR_LABEL
; return NULL
; }
3619 if (!donteval
) res
->val
= (isLabelDefinedOrKnown(lbl
, addr
, qtype
) != 0);
3623 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
); }
3624 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
); }
3627 static const char *fnAligned256 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3628 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3630 if (!donteval
) res
->val
= (res
->val
%256 ? 0 : 1);
3635 static const char *fnSameSeg (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3636 urasm_exprval_t v0
, v1
;
3638 urasm_exprval_init(&v0
);
3639 urasm_exprval_init(&v1
);
3640 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
3641 if (*error
) return expr
;
3643 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
3644 if (*error
) return expr
;
3646 if (!donteval
) res
->val
= (v0
.val
/256 == v1
.val
/256);
3651 static const char *fnAlign (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3652 urasm_exprval_t v0
, v1
;
3653 urasm_exprval_init(&v0
);
3654 urasm_exprval_init(&v1
);
3655 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
3656 if (*error
) return expr
;
3658 if (fnNextChar(expr
) == ',') {
3660 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
3661 if (*error
) return expr
;
3667 if (v1
.val
< 1) { *error
= UR_EXPRERR_FUNC
; return NULL
; }
3668 res
->val
= (v0
.val
+v1
.val
-1)/v1
.val
*v1
.val
;
3674 static const char *fnLow (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3675 const char *ee
= expr
;
3676 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3679 if (res
->fixuptype
== UR_FIXUP_HIBYTE
) {
3680 *error
= UR_EXPRERR_FUNC
; return ee
;
3682 res
->fixuptype
= UR_FIXUP_LOBYTE
;
3689 static const char *fnHigh (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3690 const char *ee
= expr
;
3691 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3694 if (res
->fixuptype
== UR_FIXUP_LOBYTE
) {
3695 *error
= UR_EXPRERR_FUNC
; return ee
;
3697 res
->fixuptype
= UR_FIXUP_HIBYTE
;
3698 res
->val
= (res
->val
>>8)&0xff;
3704 static const char *fnAbs (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3705 const char *ee
= expr
;
3706 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3709 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
3710 *error
= UR_EXPRERR_FUNC
; return ee
;
3712 res
->val
= abs(res
->val
);
3718 static const char *fnBSwap (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3719 const char *ee
= expr
;
3720 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3723 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
3724 *error
= UR_EXPRERR_FUNC
; return ee
;
3726 res
->val
= ((res
->val
>>8)&0xff)|((res
->val
&0xff)<<8);
3732 static const char *fnScrAddr8xn (int nmul
, urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3733 int32_t scrbase
= 0x4000;
3735 const char *ee
= expr
;
3736 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
3737 // first argument is `x`
3738 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3740 // second argument is `y`
3742 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3744 // optional third arg is screen base
3746 if (expr
[0] == ',') {
3748 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3749 scrbase
= res
->val
&0xffff;
3753 //urasm_exprval_clear(res);
3754 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
3755 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
3756 if (y
< 0 || y
> 191) fatal("invalid y coordinate: %d", y
/nmul
);
3762 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
3768 static const char *fnScrAddr8x1 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3769 return fnScrAddr8xn(1, res
, expr
, addr
, donteval
, defined
, error
);
3772 static const char *fnScrAddr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3773 return fnScrAddr8xn(8, res
, expr
, addr
, donteval
, defined
, error
);
3777 static const char *fnScrAttr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3778 int32_t scrbase
= 0x4000;
3780 const char *ee
= expr
;
3781 // first argument is `x`
3782 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3784 // second argument is `y`
3786 urasm_exprval_clear(res
);
3787 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3789 // optional third arg is screen base
3791 if (expr
[0] == ',') {
3793 urasm_exprval_clear(res
);
3794 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
3795 scrbase
= res
->val
&0xffff;
3797 urasm_exprval_clear(res
);
3800 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
3801 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
3802 if (y
< 0 || y
> 23) fatal("invalid y coordinate: %d", y
);
3803 res
->val
= scrbase
+6144+y
*32+x
;
3809 static const char *fnMArgToStr (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3810 const char *ee
= expr
;
3811 // argument is macro argument name
3812 expr
= strSkipSpacesConst(expr
);
3813 if (expr
[0] != '=') { *error
= UR_EXPRERR_FUNC
; return ee
; }
3815 if (!isAlpha(expr
[0]) && expr
[0] != '_') { *error
= UR_EXPRERR_FUNC
; return ee
; }
3816 const char *nend
= expr
+1;
3817 while (isAlphaDigit(*nend
) || *nend
== '_') ++nend
;
3818 if (nend
-expr
> 127) { *error
= UR_EXPRERR_FUNC
; return ee
; }
3820 memset(name
, 0, sizeof(name
));
3821 memcpy(name
, expr
, nend
-expr
);
3826 expr
= strSkipSpacesConst(expr
);
3827 if (expr
[0] == '[') {
3828 expr
= strSkipSpacesConst(expr
+1);
3829 if (expr
[0] != ']') {
3832 if (expr
[0] == '+') ++expr
;
3833 else if (expr
[0] == '-') { doneg
= 1; ++expr
; }
3835 while (isDigit(expr
[0])) {
3836 index
= index
*10+(expr
[0]-'0');
3837 if (index
>= 0x1fffffff) fatal("`margtostr` index too high");
3840 expr
= strSkipSpacesConst(expr
);
3841 if (doneg
) index
= -index
;
3843 if (expr
[0] != ']') fatal("`margtostr` invalid index syntax");
3848 if (curmacro
== NULL
) fatal("`margtostr` outside of macro");
3850 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
3851 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
3852 // found argument, convert it to string
3854 res
->str
= realloc(res
->str
, strlen(curmacro
->argvals
[f
])+1);
3855 strcpy(res
->str
, curmacro
->argvals
[f
]);
3857 const size_t slen
= strlen(curmacro
->argvals
[f
]);
3860 if (index
> slen
) fatal("index out of string");
3861 index
= (int)slen
-index
;
3863 if (index
>= slen
) fatal("index out of string");
3864 res
->str
= realloc(res
->str
, 2);
3865 res
->str
[0] = curmacro
->argvals
[f
][index
];
3869 for (char *s
= res
->str
; *s
; ++s
) *s
= toLower(*s
);
3870 //fprintf(stderr, "<%s>\n", res->str);
3873 res
->val
= ((unsigned char)res
->str
[0]);
3874 res
->val
|= ((unsigned char)res
->str
[1])<<8;
3876 res
->val
= (unsigned char)res
->str
[0];
3884 fatal("unknown macro argument '%s'", name
);
3890 static const char *fnStrLen (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3891 expr
= strSkipSpacesConst(expr
);
3894 case '"': case '\'': /* string literal? */
3896 char *a
= (char *)expr
+1;
3897 (void)parseStr(&a
, expr
[0], &stlen
);
3898 expr
= (const char *)a
;
3901 default: /* expression */
3904 urasm_exprval_init(&r
);
3905 expr
= urasm_expr_ex(&r
, expr
, disp
, &donteval
, defined
, error
);
3906 if (*error
) fatalUrLib(*error
);
3907 if (!r
.str
) fatal("string expected for `strlen()`");
3908 stlen
= (int)strlen(r
.str
);
3909 urasm_exprval_clear(&r
);
3915 urasm_exprval_setint(res
, stlen
);
3921 static void registerFunctions (void) {
3922 urasm_expr_register_func("defined", fnDefined
);
3923 urasm_expr_register_func("known", fnKnown
);
3924 urasm_expr_register_func("aligned256", fnAligned256
);
3925 urasm_expr_register_func("align", fnAlign
);
3926 urasm_expr_register_func("sameseg", fnSameSeg
);
3927 urasm_expr_register_func("low", fnLow
);
3928 urasm_expr_register_func("high", fnHigh
);
3929 urasm_expr_register_func("abs", fnAbs
);
3930 urasm_expr_register_func("bswap", fnBSwap
);
3932 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8
);
3933 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1
);
3934 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8
);
3936 urasm_expr_register_func("marg2str", fnMArgToStr
);
3937 urasm_expr_register_func("strlen", fnStrLen
);
3941 ///////////////////////////////////////////////////////////////////////////////
3942 // preparing another pass
3944 static void initPass (void) {
3945 curSrcLine
= asmText
;
3958 lastSeenGlobalLabel
= strdup(" [MAIN] ");
3961 urasm_use_old_priorities
= 0;
3965 static int posstPass (void) {
3966 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
); lastSeenGlobalLabel
= NULL
;
3967 if (checkLabels()) return -1;
3968 if (ifCount
!= 0) fatal("unbalanced IFs");
3973 ///////////////////////////////////////////////////////////////////////////////
3974 static int labelCmp (const void *aa
, const void *bb
) {
3975 if (aa
== bb
) return 0;
3976 const UrLabelInfo
*a
= *(const UrLabelInfo
**)aa
;
3977 const UrLabelInfo
*b
= *(const UrLabelInfo
**)bb
;
3979 a
->value
< b
->value
? -1 :
3980 a
->value
> b
->value
? 1 :
3985 static void writeLabelsFile (const char *fname
) {
3986 if (!fname
|| !fname
[0]) return;
3989 for (const UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) ++lcount
;
3990 UrLabelInfo
**larr
= NULL
;
3992 // create labels array
3993 larr
= (UrLabelInfo
**)malloc(sizeof(UrLabelInfo
*)*lcount
);
3995 for (UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
, ++lcount
) larr
[lcount
] = ll
;
3997 qsort(larr
, lcount
, sizeof(UrLabelInfo
*), &labelCmp
);
4000 FILE *fo
= fopen(fname
, "w");
4002 for (int f
= 0; f
< lcount
; ++f
) {
4003 UrLabelInfo
*ll
= larr
[f
];
4004 if (!ll
->name
|| (!isAlpha(ll
->name
[0]) && ll
->name
[0] != '@')) continue;
4005 if (ll
->value
< 0) {
4006 fprintf(fo
, "%d %s\n", ll
->value
, ll
->name
);
4008 fprintf(fo
, "#%04X %s\n", (unsigned)ll
->value
, ll
->name
);
4017 ///////////////////////////////////////////////////////////////////////////////
4020 static struct option longOpts
[] = {
4021 {"org", required_argument
, NULL
, 600},
4022 {"define", required_argument
, NULL
, 601},
4023 {"defzero", required_argument
, NULL
, 602},
4024 {"outdir", required_argument
, NULL
, 660},
4025 {"reffile", optional_argument
, NULL
, 669},
4027 {"sna", 0, NULL
, 's'},
4028 {"sna128", 0, NULL
, 'S'},
4029 {"tap", 0, NULL
, 't'},
4030 {"autotap", 0, NULL
, 'T'},
4031 {"raw", 0, NULL
, 'r'},
4032 {"autodmb", 0, NULL
, 'B'},
4033 {"dmb", 0, NULL
, 'b'},
4034 {"none", 0, NULL
, 'n'},
4035 {"help", 0, NULL
, 'h'},
4036 {"hob", 0, NULL
, 'H'},
4037 {"scl", 0, NULL
, 'l'},
4038 {"autoscl", 0, NULL
, 'L'},
4039 {"fixups", optional_argument
, NULL
, 'F'},
4045 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
4048 static void usage (const char *pname
) {
4050 "usage: %s [options] infile\n"
4051 "default infiles:", pname
);
4052 for (int f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
4055 " -s --sna write 48K .SNA file with autostart\n"
4056 " -S --sna128 write 148K .SNA file with autostart\n"
4057 " -t --tap write .tap file\n"
4058 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
4059 " -r --raw write raw file(s)\n"
4060 " -b --dmb write DMB file\n"
4061 " -B --autodmb write DMB file with autostart\n"
4062 " -H --hob write HoBeta code file(s)\n"
4063 " -l --scl write SCL TR-DOS archive\n"
4064 " -L --autoscl write autostarting SCL TR-DOS archive\n"
4065 " -n --none write nothing\n"
4066 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
4067 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
4068 " text: .txt file\n"
4069 " asm: address lists with counters\n"
4070 " asmdiff: 2nd and other addresses are relative, with counters\n"
4071 " asmz: address lists, with 0 end markers\n"
4072 " -h --help this help\n"
4074 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
4075 " --org xxx set ORG\n"
4076 " --define val perform 'val EQU 1'\n"
4077 " --defzero val perform 'val EQU 0'\n"
4078 " --outdir dir output dir for resulting files (default: current)\n"
4083 ///////////////////////////////////////////////////////////////////////////////
4086 int main (int argc
, char *argv
[]) {
4088 const char *pname
= argv
[0];
4089 char *inFile
= NULL
;
4090 char **defines
= NULL
, **values
= NULL
;
4096 urasm_getbyte
= getByte
;
4097 urasm_putbyte
= putByte
;
4098 urasm_label_by_name
= findLabelCB
;
4099 urasm_getval
= getValueCB
;
4100 urasm_expand
= expandCB
;
4101 urasm_fixup_operand
= fixupOperandCB
;
4103 //strcpy(tapeLoaderName, "cargador ");
4104 tapeLoaderName
[0] = 0;
4106 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
4107 while ((c
= getopt_long(argc
, argv
, "sStTrBbnhHFLl", longOpts
, NULL
)) >= 0) {
4110 case 'S': /*optRunSNA = 1;*/ optSNA48
= 0; optWriteType
= 's'; optWTChanged
= 1; break;
4111 case 's': /*optRunSNA = 0;*/ optSNA48
= 1; optWriteType
= 's'; optWTChanged
= 1; break;
4112 case 'T': optRunTape
= 1; optWriteType
= 't'; optWTChanged
= 1; break;
4113 case 't': optRunTape
= 0; optWriteType
= 't'; optWTChanged
= 1; break;
4114 case 'L': optRunSCL
= 1; optWriteType
= 'S'; optWTChanged
= 1; break;
4115 case 'l': optRunSCL
= 0; optWriteType
= 'S'; optWTChanged
= 1; break;
4116 case 'r': case 'n': optWriteType
= c
; optWTChanged
= 1; break;
4117 case 'b': optRunTape
= 0; optRunDMB
= 0; optWriteType
= 'd'; optWTChanged
= 1; break;
4118 case 'B': optRunTape
= 0; optRunDMB
= 1; optWriteType
= 'd'; optWTChanged
= 1; break;
4119 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
4120 case 'H': optWriteType
= 'H'; optWTChanged
= 1; break;
4123 if (optarg
!= NULL
) {
4124 if (strcmp(optarg
, "asm") == 0 || strcmp(optarg
, "zas") == 0) {
4126 } else if (strcmp(optarg
, "asmdiff") == 0 || strcmp(optarg
, "zasdiff") == 0) {
4128 } else if (strcmp(optarg
, "asmz") == 0 || strcmp(optarg
, "zasz") == 0) {
4130 } else if (strcmp(optarg
, "text") == 0) {
4133 fprintf(stderr
, "FATAL: invalid fixup type: '%s'\n", optarg
);
4140 //fprintf(stderr, "ORG: %d\n", c);
4141 if (c
< 0 || c
> 65535) {
4142 fprintf(stderr
, "FATAL: invalid ORG: %d\n", c
);
4145 start_pc
= start_disp
= start_ent
= c
;
4148 //fprintf(stderr, "define: [%s]\n", optarg);
4149 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
4150 values
= realloc(values
, sizeof(char *)*(defcount
+1));
4151 defines
[defcount
] = strdup(optarg
);
4152 values
[defcount
] = strdup("1");
4155 case 602: // defzero
4156 //fprintf(stderr, "defzero: [%s]\n", optarg);
4157 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
4158 values
= realloc(values
, sizeof(char *)*(defcount
+1));
4159 defines
[defcount
] = strdup(optarg
);
4160 values
[defcount
] = strdup("0");
4164 if (optOutputDir
!= NULL
) free(optOutputDir
);
4165 optOutputDir
= strdup(optarg
);
4167 case 669: // reffile
4168 if (refFileName
) free(refFileName
);
4169 refFileName
= (optarg
? strdup(optarg
) : NULL
);
4175 if (optind
>= argc
) {
4176 // try to find default input file
4177 for (int f
= 0; defInFiles
[f
]; ++f
) {
4178 if (!access(defInFiles
[f
], R_OK
)) {
4179 inFile
= strdup(defInFiles
[f
]);
4184 inFile
= strdup(argv
[optind
]);
4187 if (!inFile
|| !inFile
[0]) {
4189 fprintf(stderr
, "ERROR: no input file!\n");
4193 if (optOutputDir
== NULL
) optOutputDir
= strdup(".");
4195 registerInstructions();
4196 registerFunctions();
4198 res
= asmTextLoad(inFile
, 0);
4200 for (int f
= 0; f
< defcount
; ++f
) {
4201 if (labelDoEQU(defines
[f
], values
[f
]) != 0) {
4202 fprintf(stderr
, "FATAL: can't define label: '%s'\n", defines
[f
]);
4207 for (pass
= 0; pass
<= 1; ++pass
) {
4209 printf("pass %d\n", pass
);
4210 setCurSrcLine(asmText
);
4211 if (setjmp(errJP
)) { res
= 1; break; }
4212 while (curSrcLine
) processCurrentLine();
4213 if (posstPass()) { res
= 1; break; }
4218 char *oc
= strdup(inFile
);
4219 char *pd
= strrchr(oc
, '.');
4220 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
4221 switch (optWriteType
) {
4222 case 's': saveSna(oc
, optSNA48
); break;
4223 case 't': saveTap(oc
); break;
4224 case 'r': saveRaw(oc
); break;
4225 case 'd': saveDMB(oc
); break;
4226 case 'H': saveHob(oc
); break;
4227 case 'S': saveSCL(oc
); break;
4230 if (optWriteFixups
) writeFixups();
4232 /* build ref file name */
4233 if (!refFileName
|| !refFileName
[0]) {
4234 if (refFileName
) free(refFileName
);
4235 refFileName
= malloc(strlen(inFile
)+128);
4236 strcpy(refFileName
, inFile
);
4237 char *ext
= strrchr(refFileName
, '.');
4239 strcat(refFileName
, ".ref");
4243 for (char *ts
= refFileName
; *ts
; ++ts
) {
4244 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
4247 char *slash
= strrchr(refFileName
, '/');
4249 if (!slash
|| slash
< ext
) {
4250 strcpy(ext
, ".ref");
4252 strcat(refFileName
, ".ref");
4256 writeLabelsFile(refFileName
);
4257 printf("refs written to '%s'\n", refFileName
);
4262 fprintf(stderr
, "ERROR: loading error!\n");
4266 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
4274 if (inFile
) free(inFile
);
4275 if (sysIncludeDir
) free(sysIncludeDir
);
4276 for (int f
= defcount
-1; f
>= 0; --f
) {
4280 if (defines
!= NULL
) { free(values
); free(defines
); }
4281 if (optOutputDir
!= NULL
) free(optOutputDir
);
4282 return (res
? 1 : 0);