2 // coded by Ketmar // Invisible Vector
19 #include <sys/types.h>
27 #include "liburasm/liburasm.h"
28 #include "libfdc/libfdc.h"
34 #define UR_FORCE_INLINE static inline __attribute__((always_inline))
35 #define UR_INLINE static inline
43 #define MAYBE_UNUSED __attribute__((unused))
45 #define lambda(return_type, body_and_args) ({ \
46 return_type __fn__ body_and_args \
51 // ////////////////////////////////////////////////////////////////////////// //
52 static const char *ur_assert_failure (const char *cond
, const char *fname
, int fline
,
55 for (const char *t
= fname
; *t
; ++t
) {
57 if (*t
== '/' || *t
== '\\') fname
= t
+1;
59 if (*t
== '/') fname
= t
+1;
63 fprintf(stderr
, "\n%s:%d: Assertion in `%s` failed: %s\n", fname
, fline
, func
, cond
);
68 #define ur_assert(cond_) do { if (__builtin_expect((!(cond_)), 0)) { ur_assert_failure(#cond_, __FILE__, __LINE__, __PRETTY_FUNCTION__); } } while (0)
71 //==========================================================================
75 //==========================================================================
76 static uint32_t joaatHashStr (const void *buf
) {
77 uint32_t hash
= 0x29a;
78 const uint8_t *s
= (const uint8_t *)buf
;
79 size_t len
= (s
!= NULL
? strlen((const char *)s
) : 0);
93 //==========================================================================
97 //==========================================================================
98 static uint32_t joaatHashStrCI (const void *buf
) {
99 uint32_t hash
= 0x29a;
100 const uint8_t *s
= (const uint8_t *)buf
;
101 size_t len
= (s
!= NULL
? strlen((const char *)s
) : 0);
103 hash
+= (*s
++)|0x20; // this converts ASCII capitals to locase (and destroys other, but who cares)
115 ////////////////////////////////////////////////////////////////////////////////
116 UR_FORCE_INLINE
int isSpace (char ch
) { return (ch
&& ((unsigned)(ch
&0xff) <= 32 || ch
== 127)); }
117 UR_FORCE_INLINE
int isAlpha (char ch
) { return ((ch
>= 'A' && ch
<= 'Z') || (ch
>= 'a' && ch
<= 'z')); }
118 UR_FORCE_INLINE
int isDigit (char ch
) { return (ch
>= '0' && ch
<= '9'); }
119 UR_FORCE_INLINE
int isHexDigit (char ch
) { return ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F') || (ch
>= 'a' && ch
<= 'f')); }
120 UR_FORCE_INLINE
int isAlphaDigit (char ch
) { return (isAlpha(ch
) || isDigit(ch
)); }
122 UR_FORCE_INLINE
char toUpper (char ch
) { return (ch
>= 'a' && ch
<= 'z' ? ch
-'a'+'A' : ch
); }
123 UR_FORCE_INLINE
char toLower (char ch
) { return (ch
>= 'A' && ch
<= 'Z' ? ch
-'A'+'a' : ch
); }
125 static int digitInBase (char ch
, int base
) {
126 if (ch
< '0') return -1;
128 if (ch
>= '0'+base
) return -1;
132 if (ch
<= '9') return ch
-'0';
133 if (ch
< 'A' || ch
> 'A'+base
-10) return -1;
135 return (ch
< base
? ch
: -1);
139 //==========================================================================
143 //==========================================================================
144 static int strEquCI (const char *s0
, const char *s1
) {
146 while (res
&& *s0
&& *s1
) {
147 char c0
= *s0
++; if (c0
>= 'A' && c0
<= 'Z') c0
= c0
- 'A' + 'a';
148 char c1
= *s1
++; if (c1
>= 'A' && c1
<= 'Z') c1
= c1
- 'A' + 'a';
151 return (res
&& s0
[0] == 0 && s1
[0] == 0);
155 ////////////////////////////////////////////////////////////////////////////////
156 static char *strprintfVA (const char *fmt
, va_list vaorig
) {
161 if (buf
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
167 olen
= vsnprintf(buf
, len
, fmt
, va
);
169 if (olen
>= 0 && olen
< len
) return buf
;
170 if (olen
< 0) olen
= len
*2-1;
171 nb
= realloc(buf
, olen
+1);
172 if (nb
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
179 static __attribute__((format(printf
,1,2))) char *strprintf (const char *fmt
, ...) {
184 buf
= strprintfVA(fmt
, va
);
190 ///////////////////////////////////////////////////////////////////////////////
195 AMODE_UFO
= 1, // scanning UrForth code
198 static int asmMode
= AMODE_NORMAL
;
200 static char *ufoIncludeDir
= NULL
;
201 static char *sysIncludeDir
= NULL
;
202 static char *refFileName
= NULL
;
203 static char *lastIncludePath
= NULL
;
204 static char *lastSysIncludePath
= NULL
;
205 static int urasm_dump_all_labels
= 1; // 0: do not write all-uppercase labels
207 #define MAX_LINE_SIZE (1024*1024)
208 static char currLine
[MAX_LINE_SIZE
]; /* current processing line; can be freely modified */
210 // set by `processLabel()`
211 static char currSeenLabel
[1024];
214 ///////////////////////////////////////////////////////////////////////////////
217 static void initInclideDir (void) {
218 const char *id
= getenv("URASM_INCLUDE_DIR");
220 sysIncludeDir
= strdup(id
);
223 memset(myDir
, 0, sizeof(myDir
));
225 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
228 char *p
= (char *)strrchr(myDir
, '/');
229 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
232 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
233 char *p
= strrchr(myDir
, '\\');
234 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
236 strcat(myDir
, "/libs");
237 sysIncludeDir
= strdup(myDir
);
239 while (sysIncludeDir
[0] && sysIncludeDir
[strlen(sysIncludeDir
)-1] == '/') sysIncludeDir
[strlen(sysIncludeDir
)-1] = '\0';
240 if (!sysIncludeDir
[0]) strcpy(sysIncludeDir
, ".");
244 static void initUFEInclideDir (void) {
245 const char *id
= getenv("URASM_URFORTH_INCLUDE_DIR");
247 ufoIncludeDir
= strdup(id
);
250 memset(myDir
, 0, sizeof(myDir
));
252 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
255 char *p
= (char *)strrchr(myDir
, '/');
256 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
259 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
260 char *p
= strrchr(myDir
, '\\');
261 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
263 strcat(myDir
, "/urflibs");
264 ufoIncludeDir
= strdup(myDir
);
266 while (ufoIncludeDir
[0] && ufoIncludeDir
[strlen(ufoIncludeDir
)-1] == '/') ufoIncludeDir
[strlen(ufoIncludeDir
)-1] = '\0';
267 if (!ufoIncludeDir
[0]) strcpy(ufoIncludeDir
, ".");
271 ///////////////////////////////////////////////////////////////////////////////
274 /* trim trailing spaces and comments; normalize colons */
275 static void normalizeStr (char *s
) {
276 if (asmMode
== AMODE_UFO
) return; // do nothing
278 /* now skip all shit */
280 const char ch
= *p
++;
281 /* check for "af'" */
282 if (ch
== '\'' && p
-s
>= 2 && toLower(p
[-2] == 'a') && toLower(p
[-1] == 'f')) continue;
284 if (ch
== ';') { p
[-1] = 0; break; }
286 if (ch
== '"' || ch
== '\'') {
289 const char c1
= *p
++;
290 if (c1
== qch
) break;
291 if (c1
== '\\' && *p
) ++p
;
295 /* reduce and normalise colons */
297 --p
; /* back to colon */
298 /* remove spaces before colon */
300 while (t
!= s
&& isSpace(t
[-1])) --t
;
301 if (t
!= p
) memmove(t
, p
, strlen(p
)+1);
303 ur_assert(p
[0] == ':');
304 ++p
; /* skip colon */
305 /* remove following spaces and colons */
307 while (*t
== ':' || isSpace(*t
)) ++t
;
308 if (t
!= p
) memmove(p
, t
, strlen(t
)+1);
312 /* done; trim trailing spaces and colons */
313 size_t slen
= strlen(s
);
314 while (slen
> 0 && (isSpace(s
[slen
-1]) || s
[slen
-1] == ':')) --slen
;
319 /* check if string starts with the given command (case-insensitive) */
320 /* returns NULL or pointer to args */
321 /* skips spaces after command if any */
322 static char *strIsCommand (const char *command
, char *str
) {
323 for (int cnt
= 1; cnt
> 0; --cnt
) {
324 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
325 if (*str
== '$') ++str
;
326 for (; *command
&& *str
; ++command
, ++str
) {
327 if (toUpper(*command
) != toUpper(*str
)) return NULL
; // alas
329 if (*command
) return NULL
; // alas
330 if (*str
&& isAlphaDigit(*str
)) return NULL
; // alas
331 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
332 if (asmMode
!= AMODE_UFO
) {
333 if (*str
&& *str
== ':') break; // try again if we have a colon
341 /* parse string literal */
342 /* don't free() result */
343 /* skips trailing spaces */
344 static char *parseStr (char **str
, char endQ
, int *lenp
) {
345 static char buf
[MAX_LINE_SIZE
];
346 int len
= 0, n
, f
, base
;
348 memset(buf
, 0, sizeof(buf
));
354 case 'a': buf
[len
++] = '\a'; break;
355 case 'b': buf
[len
++] = '\b'; break;
356 case 'e': buf
[len
++] = '\x1b'; break;
357 case 'f': buf
[len
++] = '\f'; break;
358 case 'n': buf
[len
++] = '\n'; break;
359 case 'r': buf
[len
++] = '\r'; break;
360 case 't': buf
[len
++] = '\t'; break;
361 case 'v': buf
[len
++] = '\v'; break;
362 case 'z': buf
[len
++] = '\0'; break;
363 case 'x': case 'X': // hex
366 donum
: for (n
= 0; f
> 0; --f
) {
367 char ch
= digitInBase(*a
++, base
);
369 if (ch
< 0) { --a
; break; }
374 --a
; // return to the last digit, 'for' will skip it
379 case '1' ... '9': // decimal
382 default: buf
[len
++] = a
[0]; break; // others
385 if (*a
== endQ
) { ++a
; break; }
389 while (*a
&& isSpace(*a
)) ++a
; // skip trailing spaces
392 if (lenp
) *lenp
= len
;
397 ///////////////////////////////////////////////////////////////////////////////
398 // source file stack, reader, etc
400 typedef struct SourceLine
{
401 struct SourceLine
*next
;
408 static SourceLine
*asmText
= NULL
;
409 static SourceLine
*currSrcLine
= NULL
;
411 typedef struct FileInfo_s
{
412 struct FileInfo_s
*next
;
416 static FileInfo
*knownFiles
= NULL
;
419 static void clearKnownFiles (void) {
421 FileInfo
*fi
= knownFiles
;
422 knownFiles
= fi
->next
;
423 if (fi
->name
) free(fi
->name
);
429 static const char *appendKnownFileName (const char *fn
) {
430 if (!fn
|| !fn
[0]) return "<unknown>";
431 for (FileInfo
*fi
= knownFiles
; fi
; fi
= fi
->next
) {
432 if (strcmp(fi
->name
, fn
) == 0) return fi
->name
;
434 FileInfo
*fi
= calloc(1, sizeof(FileInfo
));
435 ur_assert((fi
->name
= strdup(fn
)) != NULL
);
436 fi
->next
= knownFiles
;
442 // ////////////////////////////////////////////////////////////////////////// //
443 #define MAX_MACRO_ARGS (32)
445 typedef struct MacroDef
{
446 struct MacroDef
*next
;
450 char *argdefaults
[MAX_MACRO_ARGS
]; // default values
451 char *argnames
[MAX_MACRO_ARGS
]; // argument names
456 char *argvals
[MAX_MACRO_ARGS
]; // argument values
459 static MacroDef
*maclist
= NULL
;
460 static CurMacroDef
*curmacro
= NULL
; // !NULL if we are not inserting macro
461 static int curmacronum
= 0;
464 static void freeLineList (SourceLine
*list
) {
466 SourceLine
*l
= list
;
475 static void clearMacros (void) {
477 MacroDef
*md
= maclist
;
479 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) {
480 if (md
->argdefaults
[f
]) free(md
->argdefaults
[f
]);
481 if (md
->argnames
[f
]) free(md
->argnames
[f
]);
484 freeLineList(md
->lines
);
490 static MacroDef
*findMacro (const char *name
) {
491 for (MacroDef
*mc
= maclist
; mc
!= NULL
; mc
= mc
->next
) if (strEquCI(name
, mc
->name
)) return mc
;
496 static void asmTextClear (void) {
497 freeLineList(asmText
);
503 #define currLineTrimBlanks() do { \
504 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
505 currLine[slen] = 0; \
508 #define currLineRemoveComment() do { \
511 while (pos < slen) { \
513 if (currLine[pos] == '\\' && pos+1 < slen) { \
515 } else if (currLine[pos] == instr) { \
519 if (currLine[pos] == ';') { slen = pos; break; } \
520 if (currLine[pos] == '"') { \
522 } else if (currLine[pos] == '\'') { \
524 (currLine[pos-1] != '_' && !isAlphaDigit(currLine[pos-1]))) \
526 instr = currLine[pos]; \
532 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
533 currLine[slen] = 0; \
537 static SourceLine
*loadTextFile (FILE *fl
, int system
, const char *fname
, int *error
) {
538 if (error
) *error
= 0;
540 fname
= appendKnownFileName(fname
);
541 int continuation
= 0;
543 SourceLine
*first
= NULL
;
544 SourceLine
*last
= NULL
;
548 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
550 currLine
[sizeof(currLine
)-1] = '\0';
551 size_t slen
= strlen(currLine
);
553 if (slen
== 0 || (currLine
[slen
-1] != '\n' && currLine
[slen
-1] != '\r')) {
555 if (error
) *error
= 1;
556 fprintf(stderr
, "ERROR: file %s, line %d: line too long\n", fname
, lineNo
);
562 currLineTrimBlanks();
565 if (slen
&& currLine
[slen
-1] == '\\') {
566 // this is a possible continuation line
567 if (slen
> 1 && currLine
[slen
-2] == '\\') {
574 currLineTrimBlanks();
578 //!normalizeStr(currLine);
580 // don't store empty lines
581 continuation
= new_cont
;
585 // remove comments from continuations
586 if (continuation
|| new_cont
) {
587 currLineRemoveComment();
592 // join continuation with the last line
594 size_t newlen
= slen
+strlen(last
->line
)+4;
595 if (newlen
> MAX_LINE_SIZE
-8) {
596 if (error
) *error
= 1;
597 fprintf(stderr
, "ERROR: too long continuation at line %d in file '%s'\n", lineNo
, fname
);
601 char *newbuf
= malloc(newlen
);
602 ur_assert(newbuf
); // who cares
603 snprintf(newbuf
, newlen
, "%s%s", last
->line
, currLine
);
608 SourceLine
*s
= calloc(1, sizeof(SourceLine
));
612 ur_assert((s
->line
= strdup(currLine
)) != NULL
);
614 if (last
) last
->next
= s
; else first
= s
;
618 continuation
= new_cont
;
625 static int asmTextLoad (const char *fname
, int system
) {
629 if (!(fl
= fopen(fname
, "r"))) {
630 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
633 printf("loading: %s\n", fname
);
635 SourceLine
*first
= loadTextFile(fl
, 0, fname
, &error
);
638 if (error
) return -1;
640 SourceLine
*last
= asmText
;
642 while (last
->next
) last
= last
->next
;
651 static char *extractFileDir (const char *s
) {
652 if (!s
|| !s
[0]) return strdup(".");
656 for (const char *ts
= s
; *ts
; ++ts
) {
657 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
659 if (slash
== s
&& (s
[0] == '/' || s
[0] == '\\') && (s
[1] == '/' || s
[1] == '\\')) slash
= NULL
;
661 slash
= strrchr(s
, '/');
663 if (!slash
) return strdup(".");
664 ptrdiff_t len
= (ptrdiff_t)(slash
-s
)+1;
665 char *res
= malloc(len
+1);
669 while (len
> 0 && (res
[len
-1] == '\\' || res
[len
-1] == '/')) --len
;
671 while (len
> 0 && res
[len
-1] == '/') --len
;
673 if (len
== 0) { free(res
); return strdup("."); }
679 static void loadCurSrcLine (void) {
681 strcpy(currLine
, currSrcLine
->line
);
682 /* macros will not change include dirs */
684 char **incpp
= (!currSrcLine
->system
? &lastIncludePath
: &lastSysIncludePath
);
685 if (*incpp
) free(*incpp
);
686 *incpp
= extractFileDir(currSrcLine
->fname
);
688 normalizeStr(currLine
);
695 UR_FORCE_INLINE SourceLine
*setCurSrcLine (SourceLine
*l
) {
702 UR_FORCE_INLINE SourceLine
*setUFOCurSrcLine (SourceLine
*l
) {
705 strcpy(currLine
, currSrcLine
->line
);
713 UR_FORCE_INLINE SourceLine
*nextSrcLine (void) {
714 return (currSrcLine
!= NULL
? setCurSrcLine(currSrcLine
->next
) : NULL
);
718 UR_FORCE_INLINE SourceLine
*nextUFOSrcLine (void) {
719 return (currSrcLine
!= NULL
? setUFOCurSrcLine(currSrcLine
->next
) : NULL
);
723 UR_FORCE_INLINE
int strHasPathDelim (const char *s
) {
724 if (!s
|| !s
[0]) return 0;
726 return (strchr(s
, '/') || strchr(s
, '\\') ? 1 : 0);
728 return (strchr(s
, '/') ? 1 : 0);
733 /* returns malloced string */
734 static char *createIncludeName (const char *fname
, int assystem
, const char *defaultmain
,
735 const char *lastIncPathUFO
) {
736 if (!fname
|| !fname
[0]) return NULL
;
738 if (fname
[0] != '/') {
741 incdir
= lastIncludePath
;
742 } else if (assystem
== -669) {
743 incdir
= lastIncPathUFO
;
744 } else if (assystem
== -666) {
745 incdir
= lastIncPathUFO
;
746 if (!incdir
|| !incdir
[0]) incdir
= ufoIncludeDir
;
748 incdir
= lastSysIncludePath
;
749 if (!incdir
|| !incdir
[0]) incdir
= sysIncludeDir
;
751 if (incdir
== NULL
|| incdir
[0] == 0) incdir
= ".";
752 res
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
754 res
= strprintf("%s", fname
);
757 if (defaultmain
&& defaultmain
[0]) {
758 if (stat(res
, &st
) == 0) {
759 if (S_ISDIR(st
.st_mode
)) {
760 char *rs
= strprintf("%s/%s", res
, defaultmain
);
766 /* check if there is the disk file */
767 if (strHasPathDelim(fname
) && stat(res
, &st
) != 0) {
768 /* no file, try "root include" */
769 const char *incdir
= (!assystem
? NULL
: assystem
== -666 ? ufoIncludeDir
: sysIncludeDir
);
770 char *rs
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
773 /* check for dir again */
774 if (defaultmain
&& defaultmain
[0]) {
775 if (stat(res
, &st
) == 0) {
776 if (S_ISDIR(st
.st_mode
)) {
777 char *rs
= strprintf("%s/%s", res
, defaultmain
);
784 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
789 static int includeCount
= 0;
792 // include file instead of the current line
793 // returns 0 on ok, <0 on error
794 static int asmTextInclude (const char *fname
, int system
, int softinclude
) {
795 char *fn
= createIncludeName(fname
, system
, "zzmain.zas", NULL
);
797 FILE *fl
= fopen(fn
, "r");
799 if (softinclude
) return 0;
800 fprintf(stderr
, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", currSrcLine
->fname
, currSrcLine
->lineNo
, currLine
);
805 if (includeCount
> 256) {
808 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", currSrcLine
->fname
, currSrcLine
->lineNo
);
813 printf("loading: %s\n", fn
);
816 SourceLine
*first
= loadTextFile(fl
, system
, fn
, &error
);
820 if (error
) return -1;
823 currSrcLine
->line
[0] = 0; // remove `include` directive
825 SourceLine
*last
= first
;
827 while (last
->next
) last
= last
->next
;
828 last
->next
= currSrcLine
->next
;
829 currSrcLine
->next
= first
;
835 ///////////////////////////////////////////////////////////////////////////////
838 static void processCurrentLine (void); // only one, will skip to next one
841 ///////////////////////////////////////////////////////////////////////////////
842 // error raisers, etc
844 static jmp_buf errJP
;
847 static void errorWriteFile (FILE *fo
) {
849 size_t slen
= strlen(currSrcLine
->line
);
850 fprintf(fo
, "at file %s, line %d\n%.70s%s\n*", currSrcLine
->fname
,
851 currSrcLine
->lineNo
, currSrcLine
->line
, (slen
> 70 ? " ..." : ""));
853 fprintf(fo
, "somewhere in time: ");
858 static void errorMsgV (const char *fmt
, va_list ap
) {
859 errorWriteFile(stderr
);
860 vfprintf(stderr
, fmt
, ap
);
867 __attribute__((format(printf
, 1, 2)))
868 static void warningMsg (const char *fmt
, ...) {
870 fprintf(stderr
, "WARNING ");
876 __attribute__((format(printf
, 1, 2)))
877 static void errorMsg (const char *fmt
, ...) {
879 fprintf(stderr
, "FATAL ");
885 __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2)))
886 static void fatal (const char *fmt
, ...) {
894 __attribute__((noreturn
))
895 static void fatalUrLib (int errcode
) {
896 errorMsg("%s", urasm_errormsg(errcode
));
901 ///////////////////////////////////////////////////////////////////////////////
903 static int optWriteType
= 't';
904 static int optWTChanged
= 0;
905 static int optWriteFixups
= 0;
906 static int optFixupType
= 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
907 static int optRunTape
= 1;
908 static int optRunDMB
= 1;
909 static int optRunSCL
= -1; /* -1: save cargador, but no boot; 1: boot and cargador */
910 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
911 static int optSNA48
= 1;
912 static char *optOutputDir
= NULL
;
913 static char *optOutputFile
= NULL
;
914 static int optOutputFileFixed
= 0;
917 static char *buildOutputFileName (const char *basename
, const char *ext
) {
920 if (ext
[0] == '.') ++ext
;
921 //fprintf(stderr, "********** <%s> <%s> <%s> %d <%s>\n", basename, ext, optOutputFile, optOutputFileFixed, optOutputDir);
923 if (!optOutputFileFixed
) {
924 optOutputFileFixed
= 1;
925 char *spp
= strrchr(optOutputFile
, '/');
927 char *spp1
= strrchr(optOutputFile
, '\\');
928 if (!spp
|| (spp
< spp1
)) spp
= spp1
;
930 if (!spp
) spp
= optOutputFile
; else ++spp
;
931 char *dotpos
= strrchr(spp
, '.');
932 if (dotpos
) *dotpos
= 0;
934 if (optOutputFile
&& optOutputFile
[0]) return strprintf("%s/%s.%s", optOutputDir
, optOutputFile
, ext
);
936 return strprintf("%s/%s.%s", optOutputDir
, basename
, ext
);
940 typedef struct OutDataFile_t
{
942 char type
; // 'C', 'B'
944 uint16_t start
; // start address, or basic start line
945 uint16_t size
; // data size (never 0)
947 struct OutDataFile_t
*next
;
950 static OutDataFile
*datafiles
= NULL
;
953 static OutDataFile
*appendDataData (const void *bytes
, uint16_t start
, uint16_t count
) {
954 if (!count
) return NULL
;
956 OutDataFile
*off
= malloc(sizeof(OutDataFile
));
962 off
->data
= malloc(count
);
963 if (bytes
) memcpy(off
->data
, bytes
, count
); else memset(off
->data
, 0, count
);
966 OutDataFile
*last
= datafiles
;
970 while (last
->next
) last
= last
->next
;
978 static OutDataFile
*appendDataFile (const char *fname
, long ofs
, int len
, int allowskip
) {
979 if (!fname
|| !fname
[0]) return NULL
;
981 FILE *fl
= fopen(fname
, "rb");
983 if (allowskip
) return NULL
;
984 fatal("cannot open data file '%s'", fname
);
986 if (fseek(fl
, 0, SEEK_END
) < 0) fatal("error reading data file '%s'", fname
);
987 long fsize
= ftell(fl
);
988 if (fsize
< 0) fatal("error reading data file '%s'", fname
);
992 if (ofs
< 0) fatal("invalid offset");
993 if (ofs
>= fsize
) ofs
= 0; else ofs
= fsize
-ofs
;
995 if (ofs
>= fsize
) { fclose(fl
); return NULL
; }
996 if (fseek(fl
, ofs
, SEEK_SET
) < 0) fatal("error reading data file '%s'", fname
);
999 if (fsize
> 65535) fatal("data file '%s' too big", fname
);
1002 if (len
> 65535) fatal("data file '%s' too big", fname
);
1003 if (len
== 0) { fclose(fl
); return NULL
; }
1005 OutDataFile
*off
= malloc(sizeof(OutDataFile
));
1010 off
->size
= (uint16_t)len
;
1011 off
->data
= malloc(len
);
1013 if (fread(off
->data
, (unsigned)len
, 1, fl
) != 1) fatal("error reading data file '%s'", fname
);
1016 OutDataFile
*last
= datafiles
;
1020 while (last
->next
) last
= last
->next
;
1028 //////////////////////////////////////////////////////////////////////////////
1029 // operator management
1031 // return !0 to skip current line
1032 typedef int (*UrAsmOpFn
) (void);
1039 typedef struct UrAsmOp_t
{
1041 struct UrAsmOp_t
*hlink
;
1045 void *udata
; // user data (struct info for structs, for example)
1046 struct UrAsmOp_t
*next
;
1049 #define UR_OPER_BUCKETS (256)
1050 static UrAsmOp
*oplist
= NULL
;
1051 static UrAsmOp
*urCurrentOp
= NULL
;
1052 static UrAsmOp
*operBuckets
[UR_OPER_BUCKETS
];
1055 //==========================================================================
1059 //==========================================================================
1060 static UrAsmOp
*urAddOpEx (const char *name
, UrAsmOpFn fn
, char optpfx
) {
1061 ur_assert(name
!= NULL
&& name
[0] != 0);
1062 if (oplist
== NULL
) {
1063 //fprintf(stderr, "CLEAR OPBUCKETS!\n");
1064 for (int f
= 0; f
< UR_OPER_BUCKETS
; f
+= 1) operBuckets
[f
] = NULL
;
1066 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
1069 res
->name
= calloc(1, strlen(name
) + 2);
1070 ur_assert(res
->name
);
1071 res
->name
[0] = optpfx
;
1072 strcpy(res
->name
+ 1, name
);
1074 res
->name
= strdup(name
);
1075 ur_assert(res
->name
);
1081 // insert into hash table
1082 res
->hash
= joaatHashStrCI(res
->name
);
1083 const uint32_t bkt
= res
->hash
%UR_OPER_BUCKETS
;
1084 //fprintf(stderr, "NEWOP: 0x%08x (%u) : <%s>\n", res->hash, bkt, res->name);
1085 res
->hlink
= operBuckets
[bkt
];
1086 operBuckets
[bkt
] = res
;
1091 //==========================================================================
1095 //==========================================================================
1096 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
1097 return urAddOpEx(name
, fn
, 0);
1101 //==========================================================================
1105 //==========================================================================
1106 static void urAddOpAndDollar (const char *name
, UrAsmOpFn fn
) {
1107 (void)urAddOpEx(name
, fn
, 0);
1108 (void)urAddOpEx(name
, fn
, '$');
1112 //==========================================================================
1116 //==========================================================================
1117 static UrAsmOp
*urFindOp (const char *name
) {
1118 if (name
== NULL
|| name
[0] == 0) return NULL
;
1121 if (name
[0] == '$' && name
[1]) {
1122 for (res
= oplist
; res
; res
= res
->next
) {
1123 if (res
->name
[0] == '$') {
1124 if (strEquCI(name
, res
->name
)) break;
1126 if (strEquCI(name
+1, res
->name
)) break;
1130 for (res
= oplist
; res
; res
= res
->next
) if (strEquCI(name
, res
->name
)) break;
1134 const uint32_t hash
= joaatHashStrCI(name
);
1135 //fprintf(stderr, "LOOK: 0x%08x (%u) : <%s>\n", hash, hash%UR_OPER_BUCKETS, name);
1136 UrAsmOp
*res
= operBuckets
[hash
%UR_OPER_BUCKETS
];
1137 while (res
!= NULL
&& (res
->hash
!= hash
|| !strEquCI(name
, res
->name
))) {
1138 //fprintf(stderr, " REJECT: 0x%08x: <%s>\n", res->hash, res->name);
1146 //==========================================================================
1150 //==========================================================================
1151 static void urClearOps (void) {
1152 while (oplist
!= NULL
) {
1153 UrAsmOp
*c
= oplist
;
1154 oplist
= oplist
->next
;
1158 for (int f
= 0; f
< UR_OPER_BUCKETS
; f
+= 1) operBuckets
[f
] = NULL
;
1162 ///////////////////////////////////////////////////////////////////////////////
1166 LBL_TYPE_VERY_SPECIAL
= -42,
1167 LBL_TYPE_UNKNOWN
= -1,
1168 LBL_TYPE_ASS
= 0, // `=`
1169 LBL_TYPE_EQU
= 1, // equ
1170 LBL_TYPE_CODE
= 2, // ':', or "lbl opcode"
1171 LBL_TYPE_STOFS
= 3, // struct offset
1175 typedef struct UrLabelInfo_t
{
1177 struct UrLabelInfo_t
*hlink
;
1181 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
1182 int known
; /* !0: label value already known */
1183 int refLine
; /* first referenced line */
1184 int fixuptype
; /* UR_FIXUP_XXX */
1185 int modHidden
; /* hidden module label? hack for PLUS3BOOT */
1186 char savedFirstChar
; /* for hidden labels */
1188 struct UrLabelInfo_t
*next
;
1189 // previous temp label
1190 struct UrLabelInfo_t
*prevTemp
;
1196 #define UR_LABEL_BUCKETS (256)
1197 static UrLabelInfo
*labels
= NULL
;
1198 static UrLabelInfo
*labelsTail
= NULL
;
1199 static UrLabelInfo
*labelBuckets
[UR_LABEL_BUCKETS
];
1200 // list of all @@, from the last defined
1201 static UrLabelInfo
*tempLabels
= NULL
;
1202 static int tempLabelIdx
= 0;
1205 //==========================================================================
1209 //==========================================================================
1210 static void initLabelBuckets (void) {
1211 for (int f
= 0; f
< UR_LABEL_BUCKETS
; f
+= 1) labelBuckets
[f
] = NULL
;
1215 //==========================================================================
1219 //==========================================================================
1220 static void urClearLabels (void) {
1222 while ((c
= labels
) != NULL
) {
1224 if (c
->name
) free(c
->name
);
1225 if (c
->refFile
) free(c
->refFile
);
1233 //==========================================================================
1237 //==========================================================================
1238 static UrLabelInfo
*urFindLabel (const char *name
) {
1239 if (name
!= NULL
&& name
[0] != 0) {
1241 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
1242 if (strcmp(name
, c
->name
) == 0) return c
;
1245 const uint32_t hash
= joaatHashStr(name
);
1246 UrLabelInfo
*c
= labelBuckets
[hash
%UR_LABEL_BUCKETS
];
1248 if (c
->hash
== hash
&& strcmp(c
->name
, name
) == 0) return c
;
1257 //==========================================================================
1261 //==========================================================================
1262 static UrLabelInfo
*urAddLabel (const char *name
) {
1263 ur_assert(name
!= NULL
&& name
[0] != 0);
1264 UrLabelInfo
*c
= urFindLabel(name
);
1266 c
= calloc(1, sizeof(UrLabelInfo
));
1268 c
->name
= strdup(name
);
1270 c
->type
= LBL_TYPE_UNKNOWN
;
1271 c
->fixuptype
= UR_FIXUP_NONE
;
1273 c
->savedFirstChar
= 0;
1275 if (labelsTail
) labelsTail
->next
= c
; else labels
= c
;
1277 c
->hash
= joaatHashStr(c
->name
);
1278 const uint32_t bkt
= c
->hash
%UR_LABEL_BUCKETS
;
1279 c
->hlink
= labelBuckets
[bkt
];
1280 labelBuckets
[bkt
] = c
;
1286 //==========================================================================
1290 //==========================================================================
1291 static UrLabelInfo
*urAddTempLabel (void) {
1294 snprintf(name
, sizeof(name
), "@@_%05d", tempLabelIdx
);
1295 UrLabelInfo
*lbl
= urAddLabel(name
);
1296 lbl
->tempIdx
= tempLabelIdx
;
1297 lbl
->prevTemp
= tempLabels
;
1303 //==========================================================================
1307 //==========================================================================
1308 static UrLabelInfo
*urNextTempLabel (void) {
1311 snprintf(name
, sizeof(name
), "@@_%05d", tempLabelIdx
);
1312 UrLabelInfo
*lbl
= urFindLabel(name
);
1313 ur_assert(lbl
->tempIdx
== tempLabelIdx
);
1314 ur_assert(lbl
->prevTemp
== NULL
);
1315 lbl
->prevTemp
= tempLabels
;
1321 //==========================================================================
1323 // urResetTempLabels
1325 //==========================================================================
1326 static void urResetTempLabels (void) {
1327 while (tempLabels
!= NULL
) {
1328 UrLabelInfo
*lbl
= tempLabels
;
1329 tempLabels
= lbl
->prevTemp
;
1330 lbl
->prevTemp
= NULL
;
1337 //==========================================================================
1341 // add local label; it may shadow others
1343 //==========================================================================
1344 static UrLabelInfo
*urAddTempLocal (const char *name
) {
1345 UrLabelInfo
*c
= calloc(1, sizeof(UrLabelInfo
));
1347 c
->name
= strdup(name
);
1349 c
->type
= LBL_TYPE_EQU
;
1351 c
->fixuptype
= UR_FIXUP_NONE
;
1353 c
->savedFirstChar
= 0;
1356 if (!labelsTail
) labelsTail
= c
;
1357 c
->hash
= joaatHashStr(c
->name
);
1358 const uint32_t bkt
= c
->hash
%UR_LABEL_BUCKETS
;
1359 c
->hlink
= labelBuckets
[bkt
];
1360 labelBuckets
[bkt
] = c
;
1365 static void UFOZXLabelRemoved (UrLabelInfo
*lbl
);
1368 //==========================================================================
1370 // urRemoveTempLocal
1372 //==========================================================================
1373 static void urRemoveTempLocal (UrLabelInfo
*tmplbl
) {
1374 if (!tmplbl
) return;
1375 ur_assert(tmplbl
== labels
);
1376 UFOZXLabelRemoved(tmplbl
);
1377 labels
= tmplbl
->next
;
1378 if (!labels
) labelsTail
= NULL
;
1379 const uint32_t bkt
= tmplbl
->hash
%UR_LABEL_BUCKETS
;
1380 ur_assert(labelBuckets
[bkt
] == tmplbl
);
1381 labelBuckets
[bkt
] = tmplbl
->hlink
;
1387 ///////////////////////////////////////////////////////////////////////////////
1389 typedef struct StructField_s
{
1390 struct StructField_s
*next
; // next field
1391 char *name
; // field name
1392 uint16_t ofs
; // field offset in struct
1393 uint16_t size
; // field size in bytes (can be 0 for aliases)
1399 typedef struct StructInfo_s
{
1400 struct StructInfo_s
*next
; // next structure
1401 char *name
; // structure name
1402 StructField
*fields
;
1403 uint16_t size
; // total structure size in bytes
1408 // list of all structures
1409 static StructInfo
*structList
= NULL
;
1412 static void freeStructs (void) {
1413 while (structList
!= NULL
) {
1414 StructInfo
*sth
= structList
;
1415 structList
= sth
->next
;
1416 StructField
*fld
= sth
->fields
;
1418 StructField
*fc
= fld
;
1420 if (fc
->name
) free(fc
->name
);
1423 if (sth
->name
) free(sth
->name
);
1429 ///////////////////////////////////////////////////////////////////////////////
1431 typedef struct FixupItem_s
{
1432 struct FixupItem_s
*next
;
1433 uint16_t opdestaddr
;
1438 static FixupItem
*fixlisthead
= NULL
, *fixlisttail
= NULL
;
1441 static void clearFixups (void) {
1443 while ((c
= fixlisthead
) != NULL
) {
1444 fixlisthead
= c
->next
;
1447 fixlisthead
= fixlisttail
= NULL
;
1451 static void addFixup (uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
1452 FixupItem
*fx
= calloc(1, sizeof(FixupItem
));
1454 fx
->opdestaddr
= opdestaddr
;
1455 fx
->opaddr
= opaddr
;
1456 fx
->fixuptype
= fixuptype
;
1459 if (fixlisttail
!= NULL
) fixlisttail
->next
= fx
; else fixlisthead
= fx
;
1465 ///////////////////////////////////////////////////////////////////////////////
1466 // destination memory management
1468 static uint8_t memory
[65536];
1469 static uint8_t memused
[65536];
1470 static uint8_t memresv
[65536];
1471 static uint16_t start_pc
= 0x100; // viva CP/M!
1472 static uint16_t start_disp
= 0x100; // viva CP/M!
1473 static uint16_t start_ent
= 0x100; // viva CP/M!
1474 static uint16_t pc
= 0; /* current position to write */
1475 static uint16_t disp
= 0; /* current 'virtual PC' */
1476 static uint16_t ent
= 0; /* starting address */
1477 static uint16_t clrAddr
= 24999; /* address for CLEAR for cargador */
1478 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
1479 static int inTapeBlock
= 0;
1480 static uint8_t tapeXorB
= 0;
1482 static int findChunkFrom (int addr
, int *start
, int *len
);
1485 // find next free area starting from `addr`
1486 // returns address or -1
1487 static int reserveFindFreeFrom (uint16_t addr
) {
1488 for (uint32_t freeaddr
= addr
; freeaddr
<= 0xffffU
; ++freeaddr
) {
1489 if (!memresv
[freeaddr
]) return freeaddr
;
1495 // find next reserved area starting from `addr`
1496 // returns address or -1
1497 static int reserveFindReservedFrom (uint16_t addr
) {
1498 for (uint32_t freeaddr
= addr
; freeaddr
<= 0xffffU
; ++freeaddr
) {
1499 if (memresv
[freeaddr
]) return freeaddr
;
1505 static void reserveReleaseUsed (uint16_t addr
, int len
) {
1506 while (len
--) memused
[addr
++] = 0;
1511 static int reserveHit (uint16_t addr
, int len
) {
1512 if (len
<= 0) return 0;
1514 if (memresv
[addr
++]) return 1;
1520 // start from `addr`, look for free areas, and check
1521 // if we can insert jump from a pcaddr there
1522 // returns next free area or -1
1523 static int reservedFindNextJumpableFreeFrom (uint16_t pcaddr
, uint16_t addr
) {
1524 // check if there is enough room at least for a jr
1525 if (memresv
[pcaddr
] || memresv
[pcaddr
+1]) return -1;
1526 uint16_t caddr
= addr
;
1528 while (caddr
<= 0xffffU
&& memresv
[caddr
]) ++caddr
;
1529 if (caddr
> 0xffffU
) return -1; // alas
1531 uint32_t jrdist
= caddr
-(pcaddr
+2U);
1532 if (jrdist
< 128u) return (int)caddr
; // yay, we can JR there, this free area is good
1533 // cannot JR, check if we have room for JP
1534 if (memresv
[pcaddr
+2]) return -1; // alas
1540 // start from `addr`, look for free areas, and check
1541 // if we can insert jump from a pcaddr there, and if
1542 // that area is big enough to hold a next jump and at least one more byte
1543 // returns next free area or -1
1544 static int reservedFindNextSuitableFreeFrom (uint16_t pcaddr
, uint16_t addr
) {
1547 caddr
= reservedFindNextJumpableFreeFrom(pcaddr
, (uint16_t)caddr
);
1548 if (caddr
< 0) return -1; // alas
1549 // we can jump to naddr, now check if it is big enough
1550 // if it has less than 3 bytes, it is too small
1551 if (memresv
[(uint16_t)(caddr
+1)] || memresv
[(uint16_t)(caddr
+2)]) {
1556 // if we have more than 3 bytes, it is definitely ok
1557 if (!memresv
[(uint16_t)(caddr
+3)]) return caddr
; // ok
1558 // it has exactly 3 bytes, check if it is enough
1559 int xaddr
= reservedFindNextJumpableFreeFrom(caddr
+1, (uint16_t)caddr
+3);
1560 if (xaddr
>= 0) return caddr
; // ok
1562 caddr
= reserveFindReservedFrom(caddr
);
1563 if (caddr
< 0) return -1;
1564 caddr
= reserveFindFreeFrom(caddr
);
1565 if (caddr
< 0) return -1;
1569 UR_FORCE_INLINE
uint8_t getByte (uint16_t addr
) {
1570 return memory
[addr
];
1574 UR_FORCE_INLINE
uint16_t getWord (uint16_t addr
) {
1575 return ((uint16_t)memory
[addr
])|(((uint16_t)memory
[addr
+1])<<8);
1579 UR_FORCE_INLINE
void putByte (uint16_t addr
, uint8_t b
) {
1580 if (inTapeBlock
) tapeXorB
^= b
;
1586 UR_FORCE_INLINE
void putWord (uint16_t addr
, uint16_t w
) {
1587 putByte(addr
, w
&0xFFU
);
1588 putByte(addr
+1, (w
>>8)&0xFFU
);
1592 UR_FORCE_INLINE
void emitByte (uint8_t b
) {
1599 UR_FORCE_INLINE
void emitWord (uint16_t w
) {
1601 emitByte((w
>>8)&0xFFU
);
1605 UR_FORCE_INLINE
void emitRWord (uint16_t w
) {
1606 emitByte((w
>>8)&0xFFU
);
1611 static void prepareMemory (void) {
1612 memset(memory
, 0, sizeof(memory
));
1613 memset(memused
, 0, sizeof(memused
));
1614 memset(memresv
, 0, sizeof(memresv
));
1618 ///////////////////////////////////////////////////////////////////////////////
1619 // module list management
1621 typedef struct ModuleInfo_t
{
1623 char *fname
; // opened in this file
1624 int seen
; // !0: module already seen, skip other definitions from the same file
1625 struct ModuleInfo_t
*next
;
1627 struct ModuleInfo_t
*hlink
;
1630 #define UR_MODULE_BUCKETS (1024)
1631 static ModuleInfo
*modules
= NULL
;
1632 static ModuleInfo
*currModule
= NULL
;
1633 static ModuleInfo
*moduleBuckets
[UR_MODULE_BUCKETS
];
1635 static uint8_t mod_memory_saved
[65536];
1636 static uint8_t mod_memused_saved
[65536];
1637 static uint8_t mod_memresv_saved
[65536];
1640 static void modulesClear (void) {
1643 ModuleInfo
*c
= modules
;
1644 modules
= modules
->next
;
1649 for (int f
= 0; f
< UR_MODULE_BUCKETS
; f
+=1) moduleBuckets
[f
] = NULL
;
1653 //==========================================================================
1657 //==========================================================================
1658 static void modulesResetSeen (void) {
1659 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
1663 //==========================================================================
1667 //==========================================================================
1668 static ModuleInfo
*moduleFind (const char *name
) {
1669 if (!name
|| !name
[0]) return NULL
;
1671 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) return c
;
1673 const uint32_t hash
= joaatHashStr(name
);
1674 ModuleInfo
*c
= moduleBuckets
[hash
%UR_MODULE_BUCKETS
];
1676 if (c
->hash
== hash
&& strcmp(c
->name
, name
) == 0) return c
;
1684 //==========================================================================
1688 // special module name: PLUS3BOOT
1690 //==========================================================================
1691 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
1693 ur_assert(name
&& fname
&& name
[0] && fname
[0]);
1694 c
= calloc(1, sizeof(ModuleInfo
));
1696 c
->name
= strdup(name
);
1698 c
->fname
= strdup(fname
);
1699 ur_assert(c
->fname
);
1702 c
->hash
= joaatHashStr(name
);
1703 const uint32_t bkt
= c
->hash
%UR_MODULE_BUCKETS
;
1704 c
->hlink
= moduleBuckets
[bkt
];
1705 moduleBuckets
[bkt
] = c
;
1706 // +3DOS bootsector?
1707 if (strEquCI(c
->name
, "PLUS3BOOT")) {
1708 // save current memory state, because bootsector will be written as a separate independent file
1709 memcpy(mod_memory_saved
, memory
, sizeof(mod_memory_saved
));
1710 memcpy(mod_memused_saved
, memused
, sizeof(mod_memused_saved
));
1711 memcpy(mod_memresv_saved
, memresv
, sizeof(mod_memresv_saved
));
1717 //==========================================================================
1721 //==========================================================================
1722 static void moduleOpen (void) {
1723 //fprintf(stderr, "::: OPENING MODULE <%s> :::\n", currModule->name);
1724 // unhide +3DOS boot labels
1725 if (strEquCI(currModule
->name
, "PLUS3BOOT")) {
1726 for (UrLabelInfo
*lbl
= labels
; lbl
; lbl
= lbl
->next
) {
1727 if (lbl
->modHidden
== 1) {
1728 if (!lbl
->savedFirstChar
) fatal("internal module manager error");
1730 lbl
->name
[0] = lbl
->savedFirstChar
; // unhide it
1731 //fprintf(stderr, "***UN-HIDDEN: <%s>\n", lbl->name);
1738 //==========================================================================
1742 //==========================================================================
1743 static int isModuleLabel (const ModuleInfo
*mi
, const char *name
) {
1744 if (!mi
|| !name
|| !name
[0]) return 0;
1745 if (name
[0] == '.') ++name
;
1746 const size_t mnlen
= strlen(mi
->name
);
1747 if (strlen(name
) <= mnlen
|| name
[mnlen
] != '.') return 0;
1748 return (memcmp(name
, mi
->name
, mnlen
) == 0);
1752 //==========================================================================
1756 // close current module
1758 //==========================================================================
1759 static void moduleClose (const char *mname
) {
1760 if (mname
&& !mname
[0]) mname
= NULL
;
1762 if (!mname
) fatal("trying to close unopened module");
1763 fatal("trying to close unopened module '%s'", mname
);
1765 if (mname
&& strcmp(mname
, currModule
->name
)) fatal("invalid module name in ENDMODULE; got '%s', expected '%s'", mname
, currModule
->name
);
1767 // +3DOS bootsector?
1768 if (strEquCI(currModule
->name
, "PLUS3BOOT")) {
1769 // append bootsector chunk
1772 if (!findChunkFrom(start
, &start
, &len
)) fatal("no +3DOS bootsector chunk");
1773 if (start
!= 0xFE10) fatal("+3DOS bootsector chunk must be 'org'ed and #FE10");
1774 if (len
> 512-16) fatal("+3DOS bootsector chunk is too big");
1777 OutDataFile
*off
= appendDataData(memory
+(unsigned)start
, (unsigned)start
, (unsigned)len
);
1778 if (!off
) fatal("cannot append +3DOS bootsector");
1779 off
->name
= strdup("");
1780 off
->type
= 1; // special type
1783 if (findChunkFrom(start
, &start
, &len
)) fatal("only +3DOS bootsector chunk is allowed (found #%04X, %d)", (unsigned)start
, len
);
1787 memcpy(memory
, mod_memory_saved
, sizeof(memory
));
1788 memcpy(memused
, mod_memused_saved
, sizeof(memused
));
1789 memcpy(memresv
, mod_memresv_saved
, sizeof(memresv
));
1791 // remove module labels
1792 for (UrLabelInfo
*lbl
= labels
; lbl
; lbl
= lbl
->next
) {
1793 if (!isModuleLabel(currModule
, lbl
->name
)) continue;
1794 //fprintf(stderr, "***HIDDEN: <%s>\n", lbl->name);
1796 lbl
->savedFirstChar
= lbl
->name
[0];
1797 lbl
->name
[0] = '{'; // hide it
1805 ///////////////////////////////////////////////////////////////////////////////
1806 // label getter and utilities
1808 static char *lastSeenGlobalLabel
= NULL
; /* global; malloced */
1811 static char *mangleLabelName (const char *name
) {
1812 static char newname
[MAX_LINE_SIZE
*2+1024];
1813 if (!name
|| !name
[0]) {
1817 // local label or macro label?
1818 if (name
[0] == '.') {
1819 // two dots is a macro label
1820 if (name
[1] == '.') {
1821 if (!name
[2]) fatal("invalid label '%s'", name
);
1822 // this is macro label
1823 if (curmacro
== NULL
) fatal("macro label outside of macro: '%s'", name
);
1824 snprintf(newname
, sizeof(newname
), ".%s.MACLBL%d%s", curmacro
->mac
->name
, curmacronum
, name
+1);
1827 if (!name
[1]) fatal("invalid label '%s'", name
);
1829 if (!lastSeenGlobalLabel
) fatal("local label '%s' without a global", name
);
1830 snprintf(newname
, sizeof(newname
), ".%s%s", lastSeenGlobalLabel
, name
);
1834 // if global is prefixed with '@', it should not be mangled
1835 // this is to allow access to other labels from modules
1836 if (name
[0] == '@') {
1837 if (name
[1] == '@') fatal("double-prefixed label '%s'", name
);
1838 if (!name
[1]) fatal("invalid label '%s'", name
);
1839 snprintf(newname
, sizeof(newname
), "%s", name
+1);
1842 // if no module, nothing to do
1843 // also, do not mangle labels with dots inside:
1844 // those are either already mangled, or call to other modules
1845 if (!currModule
|| strchr(name
, '.')) {
1846 snprintf(newname
, sizeof(newname
), "%s", name
);
1849 // this is global unqualified label and we have a module; let's rename it
1850 snprintf(newname
, sizeof(newname
), "%s.%s", currModule
->name
, name
);
1855 static UrLabelInfo
*addTempLabel (void) {
1856 UrLabelInfo
*lbl
= tempLabels
;
1857 if (lbl
== NULL
|| !lbl
->tempFwd
) {
1859 lbl
= urAddTempLabel();
1861 lbl
= urNextTempLabel();
1864 if (!lbl
->refFile
) {
1865 lbl
->refLine
= currSrcLine
->lineNo
;
1866 lbl
->refFile
= strdup(currSrcLine
->fname
);
1873 static UrLabelInfo
*addFwdTempLabel (void) {
1874 return addTempLabel();
1878 // WARNING! this will be invalidated
1879 static char lastFindLabelName
[MAX_LINE_SIZE
*2+1024];
1882 static UrLabelInfo
*findLabel (const char *name
) {
1884 if (!name
|| !name
[0]) fatal("UrAsm internal error: empty name in `findLabel()`");
1886 if (name
[0] == '@') {
1887 if ((name
[1] == '@' && name
[2] == 'b' && name
[3] == 0) ||
1888 (name
[1] == 'b' && name
[2] == 0))
1890 // last defined temp
1892 while (lbl
!= NULL
&& lbl
->tempFwd
) lbl
= lbl
->prevTemp
;
1893 if (lbl
== NULL
) fatal("no temp labels defined yet");
1896 if ((name
[1] == '@' && name
[2] == 'f' && name
[3] == 0) ||
1897 (name
[1] == 'f' && name
[2] == 0))
1899 // next defined temp
1900 return addFwdTempLabel();
1904 if (name
[0] == '@' && name
[1] == '@' && name
[2] == '_') {
1905 //fprintf(stderr, "TEMP: <%s>\n", name);
1906 lbl
= urFindLabel(name
);
1907 if (lbl
) return lbl
;
1909 char *nn
= mangleLabelName(name
);
1910 strcpy(lastFindLabelName
, nn
);
1911 lbl
= urFindLabel(nn
);
1914 // try non-module label
1915 lbl = urFindLabel(ln);
1922 static UrLabelInfo
*findAddLabel (const char *name
) {
1923 if (!name
|| !name
[0]) fatal("UrAsm internal error: empty name in `findAddLabel()`");
1924 char *nn
= mangleLabelName(name
);
1925 strcpy(lastFindLabelName
, nn
);
1926 UrLabelInfo
*lbl
= urAddLabel(nn
);
1927 if (!lbl
->refFile
) {
1928 lbl
->refLine
= currSrcLine
->lineNo
;
1929 lbl
->refFile
= strdup(currSrcLine
->fname
);
1935 static int lblOptMakeU2
= 0;
1937 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
, int *fixuptype
) {
1939 if (strEquCI(name
, "__URASM_DEFFMT_ANY_DISK")) {
1942 *fixuptype
= UR_FIXUP_NONE
;
1943 return (optWriteType
== 'S' || optWriteType
== '3'); // SCL or +3DOS
1945 if (strEquCI(name
, "__URASM_DEFFMT_SCL")) {
1948 *fixuptype
= UR_FIXUP_NONE
;
1949 return (optWriteType
== 'S');
1951 if (strEquCI(name
, "__URASM_DEFFMT_SCL_MONO")) {
1954 *fixuptype
= UR_FIXUP_NONE
;
1955 return (optWriteType
== 'M');
1957 if (strEquCI(name
, "__URASM_DEFFMT_P3DOS")) {
1960 *fixuptype
= UR_FIXUP_NONE
;
1961 return (optWriteType
== '3');
1963 if (strEquCI(name
, "__URASM_DEFFMT_HOBETA")) {
1966 *fixuptype
= UR_FIXUP_NONE
;
1967 return (optWriteType
== 'H');
1969 if (strEquCI(name
, "__URASM_DEFFMT_TAPE")) {
1972 *fixuptype
= UR_FIXUP_NONE
;
1973 return (optWriteType
== 't');
1975 if (strEquCI(name
, "__URASM_DEFFMT_ANY_SNA")) {
1978 *fixuptype
= UR_FIXUP_NONE
;
1979 return (optWriteType
== 's');
1981 if (strEquCI(name
, "__URASM_DEFFMT_SNA48")) {
1984 *fixuptype
= UR_FIXUP_NONE
;
1985 return (optWriteType
== 's' && optSNA48
);
1987 if (strEquCI(name
, "__URASM_DEFFMT_SNA128")) {
1990 *fixuptype
= UR_FIXUP_NONE
;
1991 return (optWriteType
== 's' && !optSNA48
);
1993 if (strEquCI(name
, "__URASM_DEFFMT_RAW")) {
1996 *fixuptype
= UR_FIXUP_NONE
;
1997 return (optWriteType
== 'r');
1999 if (strEquCI(name
, "__URASM_DEFFMT_DMB")) {
2002 *fixuptype
= UR_FIXUP_NONE
;
2003 return (optWriteType
== 'd');
2006 UrLabelInfo
*lbl
= findLabel(name
);
2009 //fprintf(stderr, "====\n"); for (UrLabelInfo *l = labels; l; l = l->next) fprintf(stderr, " <%s>=%d (known=%d; type=%d)\n", l->name, l->value, l->known, l->type);
2010 errorMsg("using undefined label '%s'", name
);
2015 lbl
= urAddLabel(lastFindLabelName
);
2016 lbl
->type
= (lblOptMakeU2
? LBL_TYPE_VERY_SPECIAL
: LBL_TYPE_UNKNOWN
);
2018 lbl
->refLine
= currSrcLine
->lineNo
;
2019 lbl
->refFile
= strdup(currSrcLine
->fname
);
2020 //fprintf(stderr, "new label: [%s]\n", lbl->name);
2021 //fprintf(stderr, "new label: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
2023 //fprintf(stderr, "label reference: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
2027 *defined
= (lbl
->known
!= 0);
2028 *fixuptype
= lbl
->fixuptype
;
2043 static int isLabelDefinedOrKnown (const char *name
, uint16_t addr
, int qtype
) {
2044 UrLabelInfo
*lbl
= findLabel(name
);
2046 case UR_QTYPE_DEFINED
: return (lbl
? lbl
->known
!= 0 : 0);
2047 case UR_QTYPE_KNOWN
: return (lbl
!= NULL
);
2054 static void fixupOperandCB (const urasm_operand_t
*op
, uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
2056 //static const char *n[4] = {"none", "word", "low", "high"};
2057 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
2058 addFixup(opdestaddr
, opaddr
, fixuptype
, size
);
2063 static int checkLabels (void) {
2065 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
2066 //fprintf(stderr, ":LBL: <%s> (type=%d)\n", c->name, c->type);
2067 if (c
->type
== LBL_TYPE_UNKNOWN
) {
2068 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
2071 if (c
->type
== LBL_TYPE_ASS
) c
->known
= -1;
2073 //if (wasError) longjmp(errJP, 667);
2078 ///////////////////////////////////////////////////////////////////////////////
2079 // expression utils (aka string parsing)
2082 /* skip leading spaces */
2083 /* returns string with spaces skipped */
2084 UR_FORCE_INLINE
char *strSkipSpaces (const char *s
) {
2085 while (*s
&& isSpace(*s
)) ++s
;
2090 /* skip leading spaces */
2091 /* returns string with spaces skipped */
2092 UR_FORCE_INLINE
const char *strSkipSpacesConst (const char *s
) {
2093 while (*s
&& isSpace(*s
)) ++s
;
2098 /* remove trailing spaces from string */
2099 static void strTrimRight (char *s
) {
2100 if (!s
|| !s
[0]) return;
2101 size_t len
= strlen(s
);
2102 while (len
> 0 && isSpace(s
[len
-1])) --len
;
2107 /* skip leading spaces and colons */
2108 /* returns string with spaces skipped */
2109 UR_FORCE_INLINE
char *strSkipSpacesColons (char *s
) {
2110 while (*s
&& (isSpace(*s
) || *s
== ':')) ++s
;
2115 /* remove leading spaces from the current line */
2116 UR_FORCE_INLINE
void removeSpaces (void) {
2117 char *e
= strSkipSpaces(currLine
);
2118 if (e
!= currLine
) memmove(currLine
, e
, strlen(e
)+1);
2122 /* skip leading spaces, and argument (up to, but not including comma or colon) */
2123 /* correctly skip strings */
2124 /* returns string after skipped argument (with trailing spaces skipped) */
2125 static char *skipMacroArg (char *str
) {
2127 char *strstart
= str
;
2129 str
= strSkipSpaces(str
);
2130 if (!str
[0]) return str
;
2131 if (parens
== 0 && (str
[0] == ',' || str
[0] == ':')) return str
;
2132 /* check for "af'" */
2133 if (str
[0] == '\'' && str
-strstart
>= 2 && toLower(str
[-2] == 'a') && toLower(str
[-1] == 'f')) {
2137 if (str
[0] == '(') { ++parens
; str
+= 1; continue; }
2138 if (str
[0] == ')') { --parens
; if (parens
< 0) parens
= 0; str
+= 1; continue; }
2139 /* check for string */
2140 if (str
[0] == '"' || str
[0] == '\'') {
2141 const char qch
= *str
++;
2143 const char ch
= *str
++;
2144 if (ch
== qch
) break;
2145 if (ch
== '\\' && *str
) ++str
;
2154 /* evaluate next numeric expression in input string */
2155 /* returns expression value */
2156 static int32_t getExprArg (int *defined
, int *addr
) {
2158 char *a
= strSkipSpaces(currLine
);
2160 if (!a
[0]) fatal("expression expected");
2161 const char *ee
= urasm_expr(&res
, a
, disp
, defined
, addr
, &error
);
2162 if (error
) fatalUrLib(error
);
2164 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
2165 memmove(currLine
, ee
, strlen(ee
)+1);
2173 /* evaluate next string expression in input string */
2174 /* returns expression value */
2175 static char *getStrExprArgFmt (void) {
2177 int donteval
= 0, defined
= 0;
2178 static char resbuf
[256];
2179 char *a
= strSkipSpaces(currLine
);
2180 if (!a
[0]) fatal("expression expected");
2181 urasm_exprval_t res
;
2182 urasm_exprval_init(&res
);
2183 const char *ee
= urasm_expr_ex(&res
, a
, disp
, &donteval
, &defined
, &error
);
2184 if (error
) fatalUrLib(error
);
2186 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
2187 memmove(currLine
, ee
, strlen(ee
)+1);
2192 snprintf(resbuf
, sizeof(resbuf
), "%s", res
.str
);
2194 snprintf(resbuf
, sizeof(resbuf
), "%d", res
.val
);
2196 urasm_exprval_clear(&res
);
2201 /* evaluate next numeric expression in input string */
2202 /* there shoild be no other expressions in the string */
2203 /* returns expression value */
2204 static int32_t getOneExprArg (int *defined
, int *addr
) {
2205 int32_t res
= getExprArg(defined
, addr
);
2206 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
2211 /* is next expression a string literal? */
2212 UR_FORCE_INLINE
int isStrArg (void) {
2213 const char *s
= strSkipSpaces(currLine
);
2214 return (s
[0] == '"' || s
[0] == '\'');
2218 /* check of we reached end of operator */
2219 UR_FORCE_INLINE
int isOperatorEnd (void) {
2220 const char *s
= strSkipSpaces(currLine
);
2221 return (s
[0] == 0 || s
[0] == ':');
2225 /* check of we reached end of operator */
2226 UR_FORCE_INLINE
int isLineEnd (void) {
2227 const char *s
= strSkipSpaces(currLine
);
2228 return (s
[0] == 0 || s
[0] == ';');
2232 /* parse string argument from input string */
2233 /* returns parsed string */
2234 static char *getStrArg (int *lenp
) {
2236 char *a
= strSkipSpaces(currLine
);
2238 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
2239 res
= parseStr(&a
, qCh
, lenp
);
2241 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
2242 memmove(currLine
, a
, strlen(a
)+1);
2250 /* parse string argument from input string; allows math after the string */
2251 /* returns parsed string */
2252 /* returns NULL and math expr in `lenp` */
2253 static char *getStrExprArg (int *lenp
, int isWord
) {
2254 char *a
= strSkipSpaces(currLine
);
2255 const char qCh
= *a
++;
2256 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
2257 char *res
= parseStr(&a
, qCh
, lenp
);
2258 if (!res
[0]) fatal("invalid empty string");
2260 if (a
[0] != ',' && a
[0] != ':' && strlen(res
) <= 2) {
2261 memmove(currLine
, a
, strlen(a
)+1);
2262 // "ab" -- 'a' will be in low (first) byte
2263 // 'ab' -- 'a' will be in high (second) byte
2268 sval
= (uint8_t)(res
[0]&0xffU
)|(((uint8_t)(res
[1]&0xffU
))<<8);
2270 sval
= (uint8_t)(res
[1]&0xffU
)|(((uint8_t)(res
[0]&0xffU
))<<8);
2274 sval
= (uint8_t)(res
[0]&0xffU
);
2276 //fprintf(stderr, "SMATH:000: str=<%s> (sval=%d); <%s>\n", res, sval, currLine);
2278 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2279 int32_t xadd
= getExprArg(&defined
, &fixuptype
);
2280 //fprintf(stderr, "SMATH:001: str=<%s> (sval=%d; xadd=%d); <%s>\n", res, sval, xadd, currLine);
2281 if (pass
> 0 && !defined
) fatal("undefined operand");
2283 if (currLine
[0] && currLine
[0] != ',' && currLine
[0] != ':') fatal("bad string expression");
2285 if (lenp
) *lenp
= sval
;
2287 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
2288 memmove(currLine
, a
, strlen(a
)+1);
2297 /* get identifier (and lowercase it) */
2298 static char *getOneIdArgLo (void) {
2299 static char res
[MAX_LINE_SIZE
+128];
2301 char *a
= strSkipSpaces(currLine
);
2302 memset(res
, 0, sizeof(res
));
2303 if (!a
[0]) fatal("identifier expected");
2304 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
2305 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
2307 if (p
-res
> 120) fatal("identifier too long: %s", res
);
2308 while (*a
&& isSpace(*a
)) ++a
;
2310 memmove(currLine
, a
, strlen(a
)+1);
2311 if (currLine
[0] == ';') currLine
[0] = 0;
2312 if (currLine
[0]) fatal("extra arguments");
2316 if (!res
[0]) fatal("identifier expected");
2317 for (char *t
= res
; *t
; ++t
) *t
= toLower(*t
);
2322 /* get label argument */
2323 static char *getLabelArg (int checkdelim
) {
2324 static char res
[MAX_LINE_SIZE
+128];
2326 char *a
= strSkipSpaces(currLine
);
2327 memset(res
, 0, sizeof(res
));
2328 if (!a
[0]) fatal("label expected");
2329 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
2330 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
2332 if (p
-res
> 120) fatal("label name too long: %s", res
);
2333 while (*a
&& isSpace(*a
)) ++a
;
2335 if (checkdelim
&& a
[0] != ',' && a
[0] != ':') fatal("bad label expression");
2336 memmove(currLine
, a
, strlen(a
)+1);
2340 if (!res
[0]) fatal("label expected");
2345 /* get label argument, and ensure that it is the last one */
2346 static char *getOneLabelArg (void) {
2347 char *res
= getLabelArg(1);
2348 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
2353 /* returns ',' or 0 */
2354 static char eatComma (void) {
2355 char *a
= strSkipSpaces(currLine
);
2356 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
2357 if (a
[0] == ':') return 0;
2358 if (a
[0] != ',') fatal("invalid expression: ',' expected");
2359 for (++a
; *a
&& isSpace(*a
); ++a
) {}
2360 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
2361 memmove(currLine
, a
, strlen(a
)+1);
2366 static int checkDelim (char dm
) {
2367 char *a
= strSkipSpaces(currLine
);
2368 if (a
[0] != dm
) return 0;
2369 for (++a
; *a
&& isSpace(*a
); ++a
) {}
2370 if (!a
[0]) { currLine
[0] = '\0'; return 1; }
2371 memmove(currLine
, a
, strlen(a
)+1);
2376 static void matchDelim (char dm
) {
2377 if (!checkDelim(dm
)) fatal("invalid expression: '%c' expected", dm
);
2381 static int checkIdentCI (const char *ident
) {
2382 ur_assert(ident
&& ident
[0]);
2383 char *a
= strSkipSpaces(currLine
);
2384 while (*a
&& *ident
) {
2385 char c0
= *a
++; if (c0
>= 'A' && c0
<= 'Z') c0
= c0
- 'A' + 'a';
2386 char c1
= *ident
++; if (c1
>= 'A' && c1
<= 'Z') c1
= c1
- 'A' + 'a';
2387 if (c0
!= c1
) return 0;
2389 if (ident
[0] != 0) return 0;
2390 if (*a
== '_' || isAlphaDigit(*a
)) return 0;
2391 while (*a
&& isSpace(*a
)) a
+= 1;
2393 memmove(currLine
, a
, strlen(a
)+1);
2401 ///////////////////////////////////////////////////////////////////////////////
2404 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
2405 char *ep
= strSkipSpacesColons(currLine
);
2406 memmove(currLine
, ep
, strlen(ep
)+1);
2410 static void checkExprEnd (void) {
2411 char *ep
= strSkipSpaces(currLine
);
2412 memmove(currLine
, ep
, strlen(ep
)+1);
2413 if (currLine
[0] && currLine
[0] != ':') fatal("end of expression expected");
2417 static void checkOperatorEnd (void) {
2418 char *ep
= strSkipSpaces(currLine
);
2419 memmove(currLine
, ep
, strlen(ep
)+1);
2420 if (currLine
[0]) fatal("end of operator expected");
2424 /* remove label from curLine */
2425 static void removeLabel (void) {
2426 char *ep
= currLine
;
2427 if (ep
[0] && !isSpace(ep
[0])) for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {} // skip text
2428 // skip spaces and colons
2429 ep
= strSkipSpacesColons(ep
);
2430 memmove(currLine
, ep
, strlen(ep
)+1);
2434 static int labelDoEQU (const char *lblname
, const char *value
) {
2435 static char n2
[256];
2438 if (value
== NULL
|| lblname
== NULL
|| !lblname
[0] || strlen(lblname
) > 255 || strlen(value
) >= MAX_LINE_SIZE
) return -1;
2439 memset(n2
, 0, sizeof(n2
));
2440 strcpy(n2
, lblname
);
2441 if (!urasm_is_valid_name(n2
)) return -1; // this is not a valid label, get out of here
2442 // check if this can be an instruction
2443 lbl
= urAddLabel(lblname
);
2444 if (!lbl
->refFile
) {
2446 lbl
->refFile
= strdup("artificially-defined-label");
2448 //fprintf(stderr, "labelDoEQU: <%s>=<%s>\n", lblname, value);
2449 strcpy(currLine
, value
);
2451 int defined
= 1, addr
= UR_FIXUP_NONE
;
2452 int32_t res
= getOneExprArg(&defined
, &addr
);
2453 lbl
->type
= LBL_TYPE_EQU
;
2454 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
2459 return -1; //fatal("can't calculate label %s", lbl->name);
2466 static void processLabel (void) {
2469 static char n2
[256];
2471 int noLocAff
= 0, doEQU
= 0, isTemp
= 0;
2472 //fprintf(stderr, "LINE %5d: <%s> from <%s>\n", (currSrcLine ? currSrcLine->lineNo : 0), currLine, (currSrcLine ? currSrcLine->fname : "<nowhere>"));
2473 memset(n2
, 0, sizeof(n2
));
2475 currSeenLabel
[0] = 0;
2477 if (!currLine
[0] || isSpace(currLine
[0]) || currLine
[0] == ':') {
2478 // this may be " id = smth" or " id equ smth"
2481 while (isSpace(*ep
)) ++ep
;
2482 if (ep
[0] == ':' || !ep
[0] || (!isAlpha(ep
[0]) && ep
[0] != '_' && ep
[0] != '.' && ep
[0] != '@')) {
2483 removeLabel(); // removeLabel() removes any spaces, etc
2486 // this looks like a label; check for '=' or 'equ'
2487 while (isAlphaDigit(ep
[0]) || ep
[0] == '_' || ep
[0] == '.' || ep
[0] == '@') ++ep
;
2489 // skip trailing spaces
2490 while (isSpace(*ep
)) ++ep
;
2494 } else if (isSpace(*nn
)) {
2496 argstart
= strIsCommand("EQU", ep
);
2501 removeLabel(); // removeLabel() removes any spaces, etc
2504 // remove leading spaces from name
2508 while (isSpace(*ep
)) ++ep
;
2509 if (ep
>= nn
) fatal("internal compiler error");
2510 if (nn
-ep
> 120) fatal("label too long");
2511 memset(n2
, 0, sizeof(n2
));
2512 memmove(n2
, ep
, nn
-ep
);
2513 if (urFindOp(n2
) || !urasm_is_valid_name(n2
)) {
2514 //fatal("invalid label name");
2515 removeLabel(); // removeLabel() removes any spaces, etc
2518 // remove label name
2519 memmove(currLine
, argstart
, strlen(argstart
)+1);
2521 if (n2
[0] == '@' && n2
[1] == '@' && !n2
[2]) {
2522 // unnamed back/forward
2523 fatal("temporary label should end with ':'");
2526 lbl = addTempLabel();
2529 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
2530 lbl
= findAddLabel(n2
);
2532 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2533 if (doEQU
&& pass
== 0 && lbl
->type
!= LBL_TYPE_UNKNOWN
) {
2534 fatal("duplicate label '%s'", lbl
->name
);
2536 int defined
= 1, addr
= UR_FIXUP_NONE
;
2537 int32_t res
= getOneExprArg(&defined
, &addr
);
2538 lbl
->type
= (doEQU
? LBL_TYPE_EQU
: LBL_TYPE_ASS
);
2539 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
2544 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
2551 for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {}
2552 if (ep
-currLine
> 120) fatal("label too long");
2555 memset(n2
, 0, sizeof(n2
));
2556 memmove(n2
, currLine
, ep
-currLine
);
2558 ep
= strSkipSpaces(ep
);
2559 if (*ep
!= ':') return; // this must be an instruction, process it
2561 if (!urasm_is_valid_name(n2
)) return; // this is not a valid label, get out of here
2564 if (findMacro(n2
)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
2566 // check if this can be instruction
2567 //ep = strSkipSpaces(ep);
2568 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
2569 // ok, we got a good label
2571 if (n2
[0] == '@' && n2
[1] == '@' && !n2
[2]) {
2572 // unnamed back/forward
2573 noLocAff
= 1; isTemp
= 1;
2574 lbl
= addTempLabel();
2576 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
2577 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (currSrcLine ? currSrcLine->lineNo : 0));*/
2578 lbl
= findAddLabel(n2
);
2579 //printf("new: [%s]\n", lbl->name);
2583 if (currLine
[0] == '=') {
2584 if (isTemp
) fatal("cannot assign values to temp labels");
2586 argstart
= currLine
+1;
2589 argstart
= strIsCommand("EQU", currLine
);
2591 if (!argstart
|| doEQU
) {
2592 if (pass
== 0 && !isTemp
&& lbl
->type
!= LBL_TYPE_UNKNOWN
) {
2593 fatal("duplicate label '%s'", lbl
->name
);
2597 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2599 if (isTemp
) fatal("cannot assign values to temp labels");
2601 memmove(currLine
, argstart
, strlen(argstart
)+1);
2603 if (lbl
->type
!= LBL_TYPE_UNKNOWN
&& lbl
->type
!= LBL_TYPE_ASS
) {
2604 fatal("duplicate label '%s'", lbl
->name
);
2607 int defined
= 1, addr
= UR_FIXUP_NONE
;
2608 int32_t res
= getOneExprArg(&defined
, &addr
);
2609 lbl
->type
= (doEQU
? LBL_TYPE_EQU
: LBL_TYPE_ASS
);
2610 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
2615 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
2621 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d); disp=#%04X; pc=#%04X\n", lbl->name, doEQU, disp, pc);
2623 // update last seen global
2625 if (lbl
->name
[0] != '{' && lbl
->name
[0] != '.' && !noLocAff
) {
2626 //fprintf(stderr, "**: LASTGLOB: <%s>\n", lbl->name);
2627 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
2628 lastSeenGlobalLabel
= strdup(lbl
->name
);
2630 lbl
->type
= LBL_TYPE_CODE
;
2633 lbl
->fixuptype
= UR_FIXUP_WORD
;
2635 snprintf(currSeenLabel
, sizeof(currSeenLabel
), "%s", n2
);
2636 } else if (lbl
->type
!= LBL_TYPE_UNKNOWN
) {
2637 ur_assert(lbl
->type
== LBL_TYPE_CODE
);
2638 ur_assert(lbl
->value
== disp
);
2641 //fprintf(stderr, "FIXTEMP: %s\n", lbl->name);
2642 lbl
->type
= LBL_TYPE_CODE
;
2645 lbl
->fixuptype
= UR_FIXUP_WORD
;
2646 ur_assert(lbl
->tempIdx
!= 0);
2652 ///////////////////////////////////////////////////////////////////////////////
2653 // instruction finder (in source)
2655 /* array ends with NULL */
2656 /* returns line or NULL */
2657 /* iidx will be set to found instruction number */
2658 static __attribute__((sentinel
)) SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
2659 if (iidx
) *iidx
= -1;
2660 for (SourceLine
*cur
= currSrcLine
; cur
; cur
= cur
->next
) {
2662 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
2664 for (int f
= 0; ;++f
) {
2665 const char *name
= va_arg(ap
, const char *);
2668 if (strIsCommand(name
, cur
->line
)) {
2670 if (iidx
) *iidx
= f
;
2680 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
2681 return findNextInstructionFromList(NULL
, name
, NULL
);
2685 ///////////////////////////////////////////////////////////////////////////////
2688 ///////////////////////////////////////////////////////////////////////////////
2689 static void writeFixups (void) {
2690 void writeFixupList (FILE *fo
, int cnt
, const char *lbl
, int (*chk
)(const FixupItem
*)) {
2693 fprintf(fo
, "%s:\n", lbl
);
2694 if (optFixupType
== 1) fprintf(fo
, " dw %d ; count\n", cnt
);
2695 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
2697 fprintf(fo
, " dw #%04X\n", fx
->opaddr
-prevaddr
);
2698 if (optFixupType
== 2) {
2699 prevaddr
= fx
->opaddr
;
2703 if (optFixupType
== 2 || optFixupType
== 3) fprintf(fo
, " dw 0 ; end marker\n");
2707 if (optFixupType
== 0) {
2708 /* simple text file */
2709 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.txt");
2710 printf("writing fixups to '%s'...\n", fname
);
2711 FILE *fo
= fopen(fname
, "w");
2713 if (fo
== NULL
) fatal("can't write fixup file");
2714 fprintf(fo
, "; addr dadr sz\n");
2715 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
2716 static const char type
[4] = "NWLH";
2717 fprintf(fo
, "%c #%04x #%04x %d\n", type
[fx
->fixuptype
], fx
->opaddr
, fx
->opdestaddr
, fx
->size
);
2721 /* various asm formats */
2722 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.zas");
2723 printf("writing fixups to '%s'...\n", fname
);
2724 FILE *fo
= fopen(fname
, "w");
2725 int cntw
= 0, cntwl
= 0, cntwh
= 0, cntbl
= 0, cntbh
= 0;
2727 if (fo
== NULL
) fatal("can't write fixup file");
2728 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
2729 if (fx
->fixuptype
== UR_FIXUP_WORD
) { ++cntw
; continue; }
2730 switch (fx
->fixuptype
) {
2731 case UR_FIXUP_LOBYTE
: if (fx
->size
== 2) ++cntwl
; else ++cntbl
; break;
2732 case UR_FIXUP_HIBYTE
: if (fx
->size
== 2) ++cntwh
; else ++cntbh
; break;
2735 writeFixupList(fo
, cntw
, "fixup_table_w", lambda(int, (const FixupItem
*fx
) {
2736 return (fx
->fixuptype
== UR_FIXUP_WORD
);
2738 writeFixupList(fo
, cntwl
, "fixup_table_wl", lambda(int, (const FixupItem
*fx
) {
2739 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 2);
2741 writeFixupList(fo
, cntwh
, "fixup_table_wh", lambda(int, (const FixupItem
*fx
) {
2742 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 2);
2744 writeFixupList(fo
, cntbl
, "fixup_table_bl", lambda(int, (const FixupItem
*fx
) {
2745 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 1);
2747 writeFixupList(fo
, cntbh
, "fixup_table_bh", lambda(int, (const FixupItem
*fx
) {
2748 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 1);
2755 ///////////////////////////////////////////////////////////////////////////////
2756 /* return 'found' flag */
2757 static int findChunkFrom (int addr
, int *start
, int *len
) {
2758 if (addr
< 0) addr
= 0;
2759 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) {}
2760 if (addr
> 65535) return 0;
2762 for (; addr
<= 65535 && memused
[addr
]; ++addr
) {}
2763 *len
= addr
-(*start
);
2768 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
2769 for (; buflen
>= 2; buflen
-= 2) {
2771 uint8_t byte
= *buf
++;
2775 for (cnt
= byte
; cnt
>= 0; --cnt
, ++addr
, --buflen
, ++buf
) {
2776 if (!memused
[addr
]) putByte(addr
, *buf
);
2777 if (addr
== 0xffff) return;
2781 for (; cnt
> 0; --cnt
, ++addr
) {
2782 if (!memused
[addr
]) putByte(addr
, byte
);
2783 if (addr
== 0xffff) return;
2790 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
2791 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
2792 if (bxor
) *bxor
= (*bxor
)^b
;
2797 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
2798 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
2799 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
2804 ///////////////////////////////////////////////////////////////////////////////
2807 static int saveSna (const char *fname
, int as48
) {
2808 char *fn
;// = malloc(strlen(fname)+16);
2813 //fn = strprintf("%s/%s.sna", optOutputDir, fname);
2814 fn
= buildOutputFileName(fname
, "sna");
2815 fo
= fopen(fn
, "wb");
2817 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
2818 printf("out: %s.sna\n", fname
);
2819 memmove(regs
, ursna48
, 27);
2822 /* push new address */
2823 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
2825 putByte(sp
, ent
&0xFFU
);
2826 putByte(sp
+1, (ent
>>8)&0xFFU
);
2827 regs
[23] = sp
&0xFFU
;
2828 regs
[24] = (sp
>>8)&0xFFU
;
2830 fwrite(regs
, 27, 1, fo
);
2831 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
2832 fwrite(memory
+16384, 49152, 1, fo
);
2835 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
2837 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
2838 //fprintf(stderr, "%d\n", memused[sp]);
2839 if (memused
[sp
] || memused
[(sp
+1)&0xffff]) {
2840 fprintf(stderr
, "FATAL: can't save snapshot!\n");
2844 if (fwrite(ursna48
, 27, 1, fo
) != 1) goto error
;
2845 rleUnpack(16384, (const uint8_t *)ursna48
+27, sizeof(ursna48
)-27);
2846 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
2848 sprintf(abuf
, "%05d", clrAddr
);
2849 memcpy(memory
+23762, abuf
, 5);
2850 sprintf(abuf
, "%05d", ent
);
2851 memcpy(memory
+23773, abuf
, 5);
2853 if (fwrite(memory
+16384, 49152, 1, fo
) != 1) goto error
;
2856 int addr
= memory
[sp
]+256*memory
[(sp
+1)&0xffff];
2858 if (fWriteWord(fo
, addr
, NULL
) != 0) goto error
; // PC
2859 if (fWriteByte(fo
, 0x10, NULL
) != 0) goto error
; // 7FFD
2860 if (fWriteByte(fo
, 0, NULL
) != 0) goto error
; // TR-DOS inactive
2862 memset(memory
, 0, 16384);
2863 for (int f
= 1; f
< 8; ++f
) {
2864 if (f
!= 2 && f
!= 5) {
2865 if (fwrite(memory
, 16384, 1, fo
) != 1) goto error
;
2879 ///////////////////////////////////////////////////////////////////////////////
2882 static void saveRaw (const char *basename
) {
2883 char *fname
= NULL
;// = malloc(strlen(basename)+16);
2887 while (findChunkFrom(start
, &start
, &len
)) {
2888 if (fname
!= NULL
) free(fname
);
2889 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, (optTapExt
? "tap" : "bin"));
2890 fo
= fopen(fname
, "wb");
2892 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
2894 printf("out: %s\n", fname
);
2895 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
2896 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2905 if (fname
!= NULL
) free(fname
);
2909 ///////////////////////////////////////////////////////////////////////////////
2912 typedef struct __attribute__((packed
)) __attribute__((gcc_struct
)) {
2917 uint8_t zero
; // always zero
2918 uint8_t secLen
; // length in sectors
2923 static uint16_t calcHobSum (const void *hdr
) {
2924 const uint8_t *buf
= (const uint8_t *)hdr
;
2927 for (unsigned int f
= 0; f
< 15; ++f
) res
+= ((uint16_t)buf
[f
])*257+f
;
2932 static void saveHob (const char *basename
) {
2933 char *fname
= NULL
;//malloc(strlen(basename)+16);
2938 while (findChunkFrom(start
, &start
, &len
)) {
2939 if (fname
!= NULL
) free(fname
);
2940 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, "$C");
2941 fo
= fopen(fname
, "wb");
2943 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
2946 char tmpbuf
[sizeof(hdr
.name
)*2];
2947 printf("out: %s\n", fname
);
2948 memset(&hdr
, 0, sizeof(hdr
));
2949 memset(&hdr
.name
, 32, 8);
2950 snprintf(tmpbuf
, sizeof(tmpbuf
), "%c%04X", basename
[0], start
);
2951 while (strlen(tmpbuf
) < sizeof(hdr
.name
)) strcat(tmpbuf
, " "); /* sorry! */
2952 memcpy(hdr
.name
, tmpbuf
, sizeof(hdr
.name
));
2956 hdr
.secLen
= (len
+255)/256;
2957 hdr
.checksum
= calcHobSum(&hdr
);
2958 if (fwrite(&hdr
, sizeof(hdr
), 1, fo
) != 1) {
2959 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2964 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
2965 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2972 if (fwrite(&hdr
.zero
, 1, 1, fo
) != 1) {
2973 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2979 //fprintf(stderr, ":%d\n", len%256);
2985 if (fname
!= NULL
) free(fname
);
2989 // ////////////////////////////////////////////////////////////////////////// //
2992 uint8_t dir
[14*256];
2994 uint32_t currfstartpos
;
3001 static const uint8_t monoldr_code
[169] = {
3002 0x00,0x00,0xff,0xf0,0xff,0xf0,0xff,0xf0,0xff,0xf3,0x01,0xfd,0x7f,0x3e,0x10,0xed,
3003 0x79,0xaf,0x21,0x00,0x58,0x11,0x01,0x58,0x01,0xff,0x02,0xfb,0x76,0xf3,0xd3,0xfe,
3004 0x36,0x00,0xed,0xb0,0x21,0x00,0x40,0x11,0x01,0x40,0x01,0x00,0x18,0x77,0xed,0xb0,
3005 0xfb,0x76,0xf3,0x3b,0x3b,0xe1,0xe5,0x11,0xce,0xff,0x19,0x7e,0x23,0x5e,0x23,0x56,
3006 0x23,0xc1,0xe5,0xd5,0x69,0x60,0x01,0x35,0x00,0x09,0x4f,0x87,0x81,0x01,0x43,0x00,
3007 0x81,0x4f,0x3e,0x00,0x88,0x47,0xed,0xb0,0xd1,0xe1,0xd5,0x01,0xff,0x03,0x13,0xed,
3008 0xa0,0xed,0xa0,0x13,0x10,0xf8,0xc9,0x31,0xa5,0xa5,0xfb,0x21,0x5a,0x5a,0xe5,0x21,
3009 0x9a,0x02,0xe5,0x76,0x3b,0x3b,0xe1,0x01,0x35,0x00,0x09,0x0e,0x00,0x7e,0xb7,0x28,
3010 0x23,0x23,0x5e,0x23,0x56,0x23,0xe5,0xeb,0x06,0x08,0xb8,0x30,0x01,0x47,0x90,0xf5,
3011 0xc5,0xe5,0xed,0x5b,0xf4,0x5c,0x0e,0x05,0xcd,0x13,0x3d,0xe1,0xc1,0x09,0xf1,0x20,
3012 0xe7,0xe1,0x18,0xd9,0xe1,0xd1,0xf9,0xeb,0xe9,
3016 static void sclInit (SCLFile
*scl
) {
3017 memset(scl
, 0, sizeof(*scl
));
3018 scl
->datasize
= 2*80*16*256; /* maximum disk size */
3019 scl
->data
= malloc(scl
->datasize
);
3020 memset(scl
->data
, 0, scl
->datasize
);
3021 scl
->currfstartpos
= 0xffffffffu
;
3025 static void sclClear (SCLFile
*scl
) {
3027 if (scl
->data
) free(scl
->data
);
3028 memset(scl
, 0, sizeof(*scl
));
3029 scl
->currfstartpos
= 0xffffffffu
;
3033 static void sclStartFile (SCLFile
*scl
, const char *name
, char type
, uint16_t v0
, uint16_t v1
) {
3034 if (scl
->fcount
== 255) {
3035 fprintf(stderr
, "FATAL: too many files in SCL!\n");
3038 if (scl
->currfstartpos
!= 0xffffffffu
) {
3039 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
3043 while (nlen
< 8 && name
&& name
[nlen
]) scl
->dir
[scl
->dirpos
++] = name
[nlen
++];
3044 while (nlen
< 8) { scl
->dir
[scl
->dirpos
++] = ' '; ++nlen
; }
3045 scl
->dir
[scl
->dirpos
++] = type
;
3046 scl
->dir
[scl
->dirpos
++] = v0
&0xff;
3047 scl
->dir
[scl
->dirpos
++] = (v0
>>8)&0xff;
3048 scl
->dir
[scl
->dirpos
++] = v1
&0xff;
3049 scl
->dir
[scl
->dirpos
++] = (v1
>>8)&0xff;
3050 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
3051 scl
->currfstartpos
= scl
->datapos
;
3055 static void sclEndFile (SCLFile
*scl
) {
3056 if (scl
->currfstartpos
== 0xffffffffu
) {
3057 fprintf(stderr
, "FATAL: last SCL file already finished!\n");
3060 /* align to sector */
3061 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
3062 const uint32_t fsz
= scl
->datapos
-scl
->currfstartpos
;
3063 if (fsz
> 255*256) {
3064 fprintf(stderr
, "FATAL: SCL file too big!\n");
3067 ur_assert((fsz
&255) == 0);
3068 scl
->dir
[scl
->dirpos
++] = fsz
/256; /* size in sectors */
3069 ur_assert(scl
->dirpos
%14 == 0);
3071 scl
->currfstartpos
= 0xffffffffu
;
3075 static void sclWriteDataForce (SCLFile
*scl
, const void *buf
, size_t len
) {
3077 if (len
> 255*256) {
3078 fprintf(stderr
, "FATAL: SCL file too big!\n");
3081 memcpy(scl
->data
+scl
->datapos
, buf
, len
);
3082 scl
->datapos
+= len
;
3086 static void sclWriteData (SCLFile
*scl
, const void *buf
, size_t len
) {
3087 if (scl
->currfstartpos
== 0xffffffffu
) {
3088 fprintf(stderr
, "FATAL: no open SCL file!\n");
3091 sclWriteDataForce(scl
, buf
, len
);
3095 static void sclWriteByte (SCLFile
*scl
, uint8_t b
) {
3096 sclWriteData(scl
, &b
, 1);
3100 static void sclWriteWord (SCLFile
*scl
, uint16_t w
) {
3102 sclWriteData(scl
, &b
, 1);
3104 sclWriteData(scl
, &b
, 1);
3108 static int sclFileWrite (FILE *fo
, const void *buf
, size_t count
, uint32_t *checksum
) {
3109 if (!count
) return 0;
3110 const uint8_t *p
= (const uint8_t *)buf
;
3111 if (checksum
) for (size_t n
= count
; n
--; ++p
) *checksum
+= (uint32_t)(*p
);
3112 if (fwrite(buf
, count
, 1, fo
) != 1) return -1;
3118 static int sclSaveToFile (FILE *fo
, SCLFile
*scl
) {
3119 if (scl
->currfstartpos
!= 0xffffffffu
) {
3120 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
3123 const char *sign
= "SINCLAIR";
3124 uint32_t checksum
= 0;
3126 if (sclFileWrite(fo
, sign
, 8, &checksum
) != 0) return -1;
3127 if (sclFileWrite(fo
, &scl
->fcount
, 1, &checksum
) != 0) return -1;
3129 if (sclFileWrite(fo
, scl
->dir
, scl
->dirpos
, &checksum
) != 0) return -1;
3131 if (sclFileWrite(fo
, scl
->data
, scl
->datapos
, &checksum
) != 0) return -1;
3133 for (unsigned f
= 0; f
< 4; ++f
) {
3134 const uint8_t b
= (checksum
>>(f
*8))&0xff;
3135 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
3142 // ////////////////////////////////////////////////////////////////////////// //
3143 static void saveSCLCargador (SCLFile
*scl
) {
3144 static uint8_t cargador
[16384]; // should be enough for everyone
3147 int start
= 0, len
, pos
;
3149 void putStr (const char *s
) {
3150 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3153 void putNum (int num
) {
3155 sprintf(buf
, "%d", num
);
3163 cargador
[pos
++] = (linenum
>>8)&0xff;
3164 cargador
[pos
++] = linenum
&0xff;
3165 // size (will be fixed later)
3166 cargador
[pos
++] = 0;
3167 cargador
[pos
++] = 0;
3171 if (linestart
>= 0) {
3172 const int size
= pos
-linestart
-4;
3173 cargador
[linestart
+2] = size
&0xff;
3174 cargador
[linestart
+3] = (size
>>8)&0xff;
3184 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
3188 while (findChunkFrom(start
, &start
, &len
)) {
3189 // :RANDOMIZE USR VAL "15619":REM:LOAD "
3190 if (cont
) { putStr(":"); cont
= 0; }
3191 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
3192 // generate chunk name
3193 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
3203 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3204 if (off
->type
== 1) continue; // skip +3DOS bootsector
3205 if (off
->type
!= 'C') continue;
3206 if (!off
->hasstart
) continue;
3207 // :RANDOMIZE USR VAL "15619":REM:LOAD "
3208 if (cont
) { putStr(":"); cont
= 0; }
3209 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
3210 // generate chunk name
3211 snprintf(cname
, sizeof(cname
), "%s", off
->name
);
3212 char *dotpos
= strchr(cname
, '.');
3214 if (dotpos
) { type
= toUpper(dotpos
[1]); *dotpos
= 0; }
3215 if (type
< 'A' || type
> 'Z') type
= 'C';
3216 for (char *s
= cname
; *s
; ++s
) *s
= toUpper(*s
);
3225 if (cont
) { putStr(":"); cont
= 0; }
3226 // RANDOMIZE USR VAL "xxx"
3227 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"");
3232 //putWord(1); // autostart line
3235 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
3236 sclWriteData(scl
, cargador
, (size_t)pos
);
3237 sclWriteByte(scl
, 0x80);
3238 sclWriteByte(scl
, 0xaa);
3239 sclWriteWord(scl
, 1);
3244 static void saveSCLCargadorMono (SCLFile
*scl
) {
3245 static uint8_t cargador
[16384]; // should be enough for everyone
3248 int start
= 0, len
, pos
;
3250 void putDB (uint8_t v
) {
3251 cargador
[pos
++] = v
;
3254 void putDW (uint16_t v
) {
3255 cargador
[pos
++] = v
&0xff;
3256 cargador
[pos
++] = (v
>>8)&0xff;
3259 void putStr (const char *s
) {
3260 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3263 void putNum (int num
) {
3265 sprintf(buf
, "%d", num
);
3272 cargador
[pos
++] = (linenum
>>8)&0xff;
3273 cargador
[pos
++] = linenum
&0xff;
3274 // size (will be fixed later)
3275 cargador
[pos
++] = 0;
3276 cargador
[pos
++] = 0;
3281 if (linestart
>= 0) {
3282 const int size
= pos
-linestart
-4;
3283 cargador
[linestart
+2] = size
&0xff;
3284 cargador
[linestart
+3] = (size
>>8)&0xff;
3294 // monoloader parameters:
3297 // dw prg_start_addr
3300 const int bcountpos
= pos
;
3302 putDW(0x4000); // to screen$
3303 putDW(0x4200); // stack
3304 putDW(ent
); // program start address
3305 putDW(0x0000); // stack
3307 for (uint32_t f
= 9; f
< (uint32_t)sizeof(monoldr_code
); ++f
) {
3308 putDB(monoldr_code
[f
]);
3311 while (findChunkFrom(start
, &start
, &len
)) {
3312 if (len
> 255*256) fatal("code chunk too big");
3314 putDB(len
/256+(len
%256 ? 1 : 0));
3319 ++cargador
[bcountpos
];
3322 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3323 if (off
->type
== 1) continue; // skip +3DOS bootsector
3324 if (off
->type
!= 'C') continue;
3325 if (!off
->hasstart
) continue;
3326 if (off
->size
> 255*256) fatal("data chunk too big");
3328 putDB(off
->size
/256+(off
->size
%256 ? 1 : 0));
3332 ++cargador
[bcountpos
];
3340 // CLEAR VAL "xxx":RANDOMIZE USR PEEK VAL "23635"+VAL "256"*PEEK VAL "23636"+VAL "14"
3341 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\":");
3342 putStr("\xf9\xc0(\xbe\xb0\"23635\"+\xb0\"256\"*\xbe\xb0\"23636\"+\xb0\"14\")\r");
3346 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
3347 sclWriteData(scl
, cargador
, (size_t)pos
);
3348 sclWriteByte(scl
, 0x80);
3349 sclWriteByte(scl
, 0xaa);
3350 sclWriteWord(scl
, 1);
3356 while (findChunkFrom(start
, &start
, &len
)) {
3357 ssz
+= len
/256+(len
%256 ? 1 : 0);
3358 sclWriteDataForce(scl
, memory
+start
, len
);
3359 /* align to sector */
3360 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
3363 for (unsigned dphase
= 0; dphase
< 2; ++dphase
) {
3365 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3366 if (off
->type
== 1) continue; // skip +3DOS bootsector
3367 if (off
->type
!= 'C') continue;
3369 if (!off
->hasstart
) continue;
3371 if (off
->hasstart
) continue;
3373 if (off
->size
> 255*256) fatal("data chunk too big");
3374 ssz
+= off
->size
/256+(off
->size
%256 ? 1 : 0);
3375 sclWriteDataForce(scl
, off
->data
, off
->size
);
3376 /* align to sector */
3377 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
3381 // fix basic file sector size
3382 scl
->dir
[scl
->dirpos
-1] += ssz
;
3386 static void saveSCL (const char *basename
) {
3390 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3391 char *fname
= buildOutputFileName(basename
, "scl");
3395 while (findChunkFrom(start
, &start
, &len
)) {
3400 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3401 if (off
->type
== 1) continue; // skip +3DOS bootsector
3402 if (off
->type
== 'C') ++fcount
;
3405 if (fcount
&& optRunSCL
) fcount
+= (optRunSCL
> 0 ? 2 : 1); // +loader and boot
3407 fprintf(stderr
, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname
, fcount
);
3411 // create output file
3412 FILE *fo
= fopen(fname
, "wb");
3414 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
3418 // initialise SCL writer
3421 if (fcount
&& optRunSCL
) {
3422 // create simple boot
3423 if (optRunSCL
> 0) {
3424 const uint8_t dasboot
[] = {
3425 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"
3427 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
3428 sclWriteWord(&scl
, 1); // line number
3429 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
3430 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
3432 sclWriteByte(&scl
, 0x80);
3433 sclWriteByte(&scl
, 0xaa);
3435 sclWriteWord(&scl
, 0);
3438 saveSCLCargador(&scl
);
3444 while (findChunkFrom(start
, &start
, &len
)) {
3445 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
3446 sclStartFile(&scl
, cname
, 'C', (unsigned)start
, (unsigned)len
);
3447 sclWriteData(&scl
, memory
+(unsigned)start
, (unsigned)len
);
3453 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3454 if (off
->type
== 1) continue; // skip +3DOS bootsector
3455 if (off
->type
!= 'C') {
3456 fprintf(stderr
, "skipping data file with invalid type '%C'\n", off
->type
);
3459 snprintf(cname
, sizeof(cname
), "%s", off
->name
);
3460 char *dotpos
= strchr(cname
, '.');
3462 if (dotpos
) { type
= toUpper(dotpos
[1]); *dotpos
= 0; }
3463 if (type
< 'A' || type
> 'Z') type
= 'C';
3464 for (char *s
= cname
; *s
; ++s
) *s
= toUpper(*s
);
3465 sclStartFile(&scl
, cname
, type
, (unsigned)off
->start
, (unsigned)off
->size
);
3466 sclWriteData(&scl
, off
->data
, (unsigned)off
->size
);
3470 if (sclSaveToFile(fo
, &scl
) < 0) {
3471 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
3478 printf("out: %s\n", fname
);
3479 if (fname
!= NULL
) free(fname
);
3483 static void saveSCLMono (const char *basename
) {
3486 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3487 char *fname
= buildOutputFileName(basename
, "scl");
3489 // count total size in sectors
3490 int secsize
= 1; // for loader
3492 while (findChunkFrom(start
, &start
, &len
)) {
3493 secsize
+= len
/256+(len
%256 ? 1 : 0);
3497 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3498 if (off
->type
== 1) continue; // skip +3DOS bootsector
3499 if (off
->type
== 'C') {
3500 secsize
+= off
->size
/256+(off
->size
%256 ? 1 : 0);
3504 if (secsize
> 254) {
3505 fprintf(stderr
, "ERROR: can't write file '%s' (too many sectors: %d)!\n", fname
, secsize
);
3509 // create output file
3510 FILE *fo
= fopen(fname
, "wb");
3512 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
3516 // initialise SCL writer
3520 // create simple boot
3521 if (optRunSCL
> 0) {
3522 const uint8_t dasboot
[] = {
3523 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"
3525 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
3526 sclWriteWord(&scl
, 1); // line number
3527 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
3528 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
3530 sclWriteByte(&scl
, 0x80);
3531 sclWriteByte(&scl
, 0xaa);
3533 sclWriteWord(&scl
, 0);
3537 saveSCLCargadorMono(&scl
);
3539 if (sclSaveToFile(fo
, &scl
) < 0) {
3540 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
3547 printf("out: %s\n", fname
);
3548 if (fname
!= NULL
) free(fname
);
3552 // ////////////////////////////////////////////////////////////////////////// //
3554 int optDskType
= P3DSK_PCW_SS
;
3557 static int saveDSKCargador (P3DiskInfo
*p3d
, int autorun
) {
3558 static uint8_t cargador
[16384]; // should be enough for everyone
3561 int start
= 0, len
, pos
;
3563 void putStr (const char *s
) {
3564 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3567 void putNum (int num
) {
3569 sprintf(buf
, "%d", num
);
3577 cargador
[pos
++] = (linenum
>>8)&0xff;
3578 cargador
[pos
++] = linenum
&0xff;
3579 // size (will be fixed later)
3580 cargador
[pos
++] = 0;
3581 cargador
[pos
++] = 0;
3585 if (linestart
>= 0) {
3586 const int size
= pos
-linestart
-4;
3587 cargador
[linestart
+2] = size
&0xff;
3588 cargador
[linestart
+3] = (size
>>8)&0xff;
3598 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
3600 while (findChunkFrom(start
, &start
, &len
)) {
3603 // generate chunk name
3604 snprintf(cname
, sizeof(cname
), "C%04X.BIN", (unsigned)start
);
3614 // :RANDOMIZE USR VAL "xxx"
3615 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
3619 // build cp/m 8.3 name
3621 if (autorun
) strcpy(pdfname
, "DISK"); else strcpy(pdfname
, "CARGADOR.BAS");
3623 int crerr
= p3dskCreateFile(p3d
, pdfname
);
3625 if (crerr
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3626 if (crerr
!= FLPERR_FILEEXIST
) { fprintf(stderr
, "+3DOS disk error (creating)\n"); return -1; }
3627 p3dskDeleteFiles(p3d
, pdfname
);
3628 crerr
= p3dskCreateFile(p3d
, pdfname
);
3629 if (crerr
< 0) { fprintf(stderr
, "+3DOS disk error (creating1)\n"); return -1; }
3633 if (p3dskOpenFile(p3d
, &nfo
, pdfname
) != FLPERR_OK
) { fprintf(stderr
, "cannot open created +3DOS file '%s'\n", pdfname
); return -1; }
3635 int wrres
= p3dskWriteFile(&nfo
, cargador
, 128, (int)pos
);
3636 if (wrres
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3637 if (wrres
== FLPERR_NOSPACE
) { fprintf(stderr
, "NO FREE SPACE (writing)!\n"); return -1; }
3638 if (wrres
< 0) { fprintf(stderr
, "+3DOS disk writing error (writing)\n"); return -1; }
3640 // write +3DOS header
3641 P3DskFileHeader hdr
;
3642 memset(&hdr
, 0, sizeof(P3DskFileHeader
));
3646 hdr
.filesize
= (uint32_t)(pos
+128);
3647 hdr
.bastype
= 0; // basic
3648 hdr
.baslength
= (uint16_t)pos
;
3650 hdr
.basvarsofs
= (uint16_t)pos
;
3651 if (p3dskWriteFileHeader(&nfo
, &hdr
) != FLPERR_OK
) { fprintf(stderr
, "error writing +3DOS file header\n"); return -1; }
3657 static int saveDSKChunk (P3DiskInfo
*p3d
, const char *name
, const void *data
, int start
, int len
, int p3header
) {
3659 // build cp/m 8.3 name
3661 if (p3dskNormaliseFileName(pdfname
, name
) != FLPERR_OK
) { fprintf(stderr
, "bad +3DOS file name '%s'\n", name
); return -1; }
3663 for (char *s
= pdfname
; *s
; ++s
) if (*s
>= 'a' && *s
<= 'z') s
[0] -= 32;
3665 int crerr
= p3dskCreateFile(p3d
, pdfname
);
3667 if (crerr
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3668 if (crerr
!= FLPERR_FILEEXIST
) { fprintf(stderr
, "+3DOS disk error (creating)\n"); return -1; }
3669 p3dskDeleteFiles(p3d
, pdfname
);
3670 crerr
= p3dskCreateFile(p3d
, pdfname
);
3671 if (crerr
< 0) { fprintf(stderr
, "+3DOS disk error (creating1)\n"); return -1; }
3675 if (p3dskOpenFile(p3d
, &nfo
, pdfname
) != FLPERR_OK
) { fprintf(stderr
, "cannot open created +3DOS file '%s'\n", pdfname
); return -1; }
3677 int wrres
= p3dskWriteFile(&nfo
, data
, (p3header
? 128 : 0), (int)len
);
3678 if (wrres
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3679 if (wrres
== FLPERR_NOSPACE
) { fprintf(stderr
, "NO FREE SPACE (writing)!\n"); return -1; }
3680 if (wrres
< 0) { fprintf(stderr
, "+3DOS disk writing error (writing)\n"); return -1; }
3682 // write +3DOS header
3684 P3DskFileHeader hdr
;
3685 memset(&hdr
, 0, sizeof(P3DskFileHeader
));
3689 hdr
.filesize
= (uint32_t)(len
+128);
3690 hdr
.bastype
= 3; // code
3691 hdr
.baslength
= (uint16_t)len
;
3692 hdr
.basaddr
= (uint16_t)start
;
3694 if (p3dskWriteFileHeader(&nfo
, &hdr
) != FLPERR_OK
) { fprintf(stderr
, "error writing +3DOS file header\n"); return -1; }
3701 static void saveDSK (const char *basename
) {
3702 // create and format the floppy
3703 Floppy
*flp
= flpCreate(0);
3704 if (p3dskFormatDisk(flp
, optDskType
) != FLPERR_OK
) {
3706 fprintf(stderr
, "ERROR: cannot format +3DOS disk\n");
3712 if (p3dskDetectGeom(p3d
.flp
, &p3d
.geom
) < 0) {
3714 fprintf(stderr
, "ERROR: not a +3DOS disk!\n");
3718 int hasbootsector
= 0;
3719 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3720 if (off
->type
== 1) { hasbootsector
= 1; break; }
3724 int haschunks
= findChunkFrom(start
, &start
, &len
);
3726 if (!hasbootsector
&& haschunks
) {
3727 if (saveDSKCargador(&p3d
, optRunDSK
) != 0) {
3735 while (findChunkFrom(start
, &start
, &len
)) {
3737 snprintf(cname
, sizeof(cname
), "C%04X.BIN", (unsigned)start
);
3738 if (saveDSKChunk(&p3d
, cname
, memory
+(unsigned)start
, start
, len
, 1) != 0) {
3746 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3747 if (off
->type
== 1) {
3749 if (!p3d
.geom
.restracks
) {
3750 fprintf(stderr
, "no room for +3DOS bootsector\n");
3753 uint8_t *bootsec
= malloc(512);
3754 if (flpGetSectorData(p3d
.flp
, 0, p3d
.geom
.firstsector
, bootsec
, 512) != FLPERR_OK
) {
3755 fprintf(stderr
, "cannot read +3DOS bootsector\n");
3758 size_t clen
= off
->size
;
3759 if (clen
> 512-16) clen
= 512-16;
3760 if (clen
) memcpy(bootsec
+16, off
->data
, clen
);
3761 if (flpPutSectorData(p3d
.flp
, 0, p3d
.geom
.firstsector
, bootsec
, 512) != FLPERR_OK
) fatal("cannot write +3DOS bootsector");
3762 if (p3dskWriteBootableChecksum(&p3d
) != FLPERR_OK
) fatal("cannot make +3DOS disk bootable");
3763 printf("written +3DOS bootsector (%u bytes of code)\n", off
->size
);
3767 if (off
->type
!= 'C') {
3768 fprintf(stderr
, "skipping data file with invalid type '%C'\n", off
->type
);
3772 if (p3dskNormaliseFileName(pdfname
, off
->name
) != FLPERR_OK
) {
3773 fprintf(stderr
, "skipping data file with invalid name '%s'\n", off
->name
);
3776 saveDSKChunk(&p3d
, pdfname
, off
->data
, off
->start
, off
->size
, off
->hasstart
);
3780 //char *fname = strprintf("%s/%s.dsk", optOutputDir, basename);
3781 char *fname
= buildOutputFileName(basename
, "dsk");
3782 FILE *fo
= fopen(fname
, "wb");
3783 if (!fo
) { fprintf(stderr
, "cannot create disk file '%s'\n", fname
); free(fname
); flpDestroy(flp
); return; }
3784 if (dskSaveDSK(flp
, fo
) != FLPERR_OK
) {
3786 fprintf(stderr
, "error writing disk file '%s'\n", fname
);
3792 printf("out: %s\n", fname
);
3798 ///////////////////////////////////////////////////////////////////////////////
3801 static int saveDMB (const char *fname
) {
3802 char *fn
;// = malloc(strlen(fname)+16);
3807 //fn = strprintf("%s/%s.dmb", optOutputDir, fname);
3808 fn
= buildOutputFileName(fname
, "dmb");
3809 fo
= fopen(fn
, "wb");
3811 if (!fo
) { fprintf(stderr
, "ERROR: can't write DMB file: %s.dmb\n", fname
); return -1; }
3813 while (findChunkFrom(start
, &start
, &len
)) { ++pcnt
; start
+= len
; }
3815 printf("out: %s.dmb\n", fname
);
3816 if (fwrite("DMB1", 4, 1, fo
) != 1) goto error
;
3817 if (fWriteWord(fo
, (optRunDMB
? ent
: 0), NULL
) != 0) goto error
;
3818 if (fWriteWord(fo
, clrAddr
, NULL
) != 0) goto error
;
3819 if (fWriteWord(fo
, pcnt
, NULL
) != 0) goto error
;
3822 while (findChunkFrom(start
, &start
, &len
)) {
3823 if (fWriteWord(fo
, len
, NULL
) != 0) goto error
;
3824 if (fWriteWord(fo
, start
, NULL
) != 0) goto error
;
3825 if (fwrite(memory
+start
, len
, 1, fo
) != 1) goto error
;
3833 fprintf(stderr
, "ERROR: error writing file %s.dmb!\n", fname
);
3838 ///////////////////////////////////////////////////////////////////////////////
3841 static char tapeLoaderName
[16];
3844 static void saveTapCargador (FILE *fo
) {
3846 static uint8_t cargador
[16384]; // should be enough for everyone
3847 int start
= 0, len
, pos
, f
;
3851 void putStr (const char *s
) {
3852 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3855 void putNum (int num
) {
3857 sprintf(buf
, "%d", num
);
3864 cargador
[0] = 0; cargador
[1] = 10;
3865 // size (will be fixed later)
3866 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\""); // CLEAR VAL "xxx"
3868 while (findChunkFrom(start
, &start
, &len
)) {
3870 putStr(":\xef\"\"\xaf");
3873 // additional code files
3874 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3875 if (off
->type
== 1) continue; // skip +3DOS bootsector
3876 if (off
->type
!= 'C' || off
->size
> 65533 || !off
->hasstart
) {
3880 putStr(":\xef\"\"\xaf");
3884 // :RANDOMIZE USR VAL "xxx"
3885 putStr(":\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
3887 cargador
[2] = (pos
-4)&0xff;
3888 cargador
[3] = ((pos
-4)>>8)&0xff;
3890 fWriteWord(fo
, 19, NULL
); // length of header
3892 fWriteByte(fo
, 0, &bxor
); // header block
3893 fWriteByte(fo
, 0, &bxor
); // 'basic' flag
3894 if (tapeLoaderName
[0]) {
3895 for (int f
= 0; f
< 10; ++f
) fWriteByte(fo
, tapeLoaderName
[f
], &bxor
);
3897 fWriteByte(fo
, 'c', &bxor
);
3898 fWriteByte(fo
, 'a', &bxor
);
3899 fWriteByte(fo
, 'r', &bxor
);
3900 fWriteByte(fo
, 'g', &bxor
);
3901 fWriteByte(fo
, 'a', &bxor
);
3902 fWriteByte(fo
, 'd', &bxor
);
3903 fWriteByte(fo
, 'o', &bxor
);
3904 fWriteByte(fo
, 'r', &bxor
);
3905 fWriteByte(fo
, ' ', &bxor
);
3906 fWriteByte(fo
, ' ', &bxor
);
3908 fWriteWord(fo
, pos
, &bxor
); // length
3909 fWriteWord(fo
, 10, &bxor
); // start
3910 fWriteWord(fo
, pos
, &bxor
); // length2
3911 fWriteByte(fo
, bxor
, NULL
);
3913 fWriteWord(fo
, pos
+2, NULL
); // plus type and checkbyte
3915 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
3916 for (f
= 0; f
< pos
; ++f
) fWriteByte(fo
, cargador
[f
], &bxor
);
3917 fWriteByte(fo
, bxor
, NULL
);
3921 static void saveTap (const char *basename
) {
3922 char *fname
;// = malloc(strlen(basename)+16);
3924 int start
= 0, len
, f
;
3928 //fname = strprintf("%s/%s.tap", optOutputDir, basename);
3929 fname
= buildOutputFileName(basename
, "tap");
3930 fo
= fopen(fname
, "wb");
3932 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
3933 printf("out: %s.tap\n", basename
);
3934 if (optRunTape
) saveTapCargador(fo
);
3935 while (findChunkFrom(start
, &start
, &len
)) {
3937 if (tapeLoaderName
[0]) {
3938 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
3939 memcpy(blkname
, tapeLoaderName
, 10);
3941 sprintf(blkname
, "c%04X:%04X", start
, len
);
3943 //printf(" block: %s\n", blkname);
3944 fWriteWord(fo
, 19, NULL
); // length of header
3946 fWriteByte(fo
, 0, &bxor
); // header block
3947 fWriteByte(fo
, 3, &bxor
); // 'code' flag
3948 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
3949 fWriteWord(fo
, len
, &bxor
);
3950 fWriteWord(fo
, start
, &bxor
);
3951 fWriteWord(fo
, 32768, &bxor
);
3952 fWriteByte(fo
, bxor
, NULL
);
3954 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
3956 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
3957 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
3958 fWriteByte(fo
, bxor
, NULL
);
3962 for (int dfpass
= 0; dfpass
< 2; ++dfpass
) {
3964 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3965 if (off
->type
== 1) continue; // skip +3DOS bootsector
3966 if (off
->type
!= 'C' || off
->size
> 65533) {
3967 fprintf(stderr
, "skipping data file with invalid type '%C'\n", off
->type
);
3971 if (!off
->hasstart
) continue;
3973 if (off
->hasstart
) continue;
3975 snprintf(blkname
, 10, "%s", off
->name
);
3976 blkname
[10] = 0; // just in case
3977 while (strlen(blkname
) < 10) strcat(blkname
, " ");
3979 fWriteWord(fo
, 19, NULL
); // length of header
3981 fWriteByte(fo
, 0, &bxor
); // header block
3982 fWriteByte(fo
, 3, &bxor
); // 'code' flag
3983 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
3984 fWriteWord(fo
, off
->size
, &bxor
);
3985 fWriteWord(fo
, off
->start
, &bxor
);
3986 fWriteWord(fo
, 32768, &bxor
);
3987 fWriteByte(fo
, bxor
, NULL
);
3989 fWriteWord(fo
, off
->size
+2, NULL
); // plus type and checkbyte
3991 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
3992 for (f
= 0; f
< off
->size
; ++f
) fWriteByte(fo
, off
->data
[f
], &bxor
);
3993 fWriteByte(fo
, bxor
, NULL
);
4001 ///////////////////////////////////////////////////////////////////////////////
4002 // pseudoinstructions
4004 // note that processCurrentLine() will NOT skip to the next line before
4005 // calling pseudoinstruction handler!
4006 // note that processCurrentLine() will skip current line after calling
4007 // pseudoinstruction handler!
4009 static int wasOrg
= 0;
4010 static int wasClr
= 0;
4013 // ////////////////////////////////////////////////////////////////////////// //
4014 // print message using printf-like syntax
4015 // doesn't print new line
4016 static void processPrintf (FILE *fo
, const char *fmt
) {
4017 if (!fmt
|| !fmt
[0]) return;
4018 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
4019 char *tempstr
= NULL
;
4020 size_t tempsize
= 0;
4021 char *fmtcopy
= malloc(strlen(fmt
)+1);
4022 strcpy(fmtcopy
, fmt
);
4023 char *currfmt
= fmtcopy
;
4031 char *prcs
= strchr(currfmt
, '%');
4032 if (!prcs
|| !prcs
[1]) {
4033 /* no more formatting; print the tail and exit the loop */
4034 fprintf(fo
, "%s", currfmt
);
4039 if (prcs
[1] == '%') {
4043 /* print up to `prcs` */
4044 if (prcs
> currfmt
) {
4045 size_t partlen
= (ptrdiff_t)(prcs
-currfmt
);
4046 if (partlen
+1 > tempsize
) {
4047 tempsize
= ((partlen
+8)|0xff)+1;
4048 tempstr
= realloc(tempstr
, tempsize
);
4050 memcpy(tempstr
, currfmt
, partlen
);
4051 tempstr
[partlen
] = 0;
4052 fprintf(fo
, "%s", tempstr
);
4054 currfmt
= ++prcs
; /* skip percent */
4055 if (!docheck
) continue;
4061 if (*currfmt
== '+' || *currfmt
== '-') sign
= *currfmt
++;
4062 if (sign
!= '-' && *currfmt
== '0') zerofill
= 1;
4063 while (isDigit(*currfmt
)) { width
= width
*10+((*currfmt
)-'0'); ++currfmt
; }
4064 if (width
> 256) width
= 256;
4066 if (!ftype
) break; /* oops */
4067 if (!eatComma()) fatal("out of arguments for string format");
4069 case 's': /* string */
4071 switch (strSkipSpaces(currLine
)[0]) {
4072 case '"': case '\'': /* string literal? */
4073 strarg
= getStrArg(&stlen
);
4075 default: /* expression */
4076 strarg
= getStrExprArgFmt();
4077 stlen
= (int)strlen(strarg
);
4081 if (sign
!= '-' && stlen
< width
) {
4082 int padlen
= width
-stlen
;
4083 memset(tempbuf
, ' ', padlen
);
4084 tempbuf
[padlen
+1] = 0;
4085 fprintf(fo
, "%s", tempbuf
);
4087 fprintf(fo
, "%s", strarg
);
4089 if (sign
== '-' && stlen
< width
) {
4090 int padlen
= width
-stlen
;
4091 memset(tempbuf
, ' ', padlen
);
4092 tempbuf
[padlen
+1] = 0;
4093 fprintf(fo
, "%s", tempbuf
);
4096 case 'd': /* decimal */
4098 exprval
= getExprArg(&defined
, NULL
);
4099 if (width
&& zerofill
) {
4100 fprintf(fo
, "%0*d", width
, exprval
);
4102 fprintf(fo
, "%*d", (sign
!= '-' ? width
: -width
), exprval
);
4104 fprintf(fo
, "%d", exprval
);
4107 case 'c': /* char */
4109 exprval
= getExprArg(&defined
, NULL
);
4110 if (exprval
<= 0 || exprval
== '?' || exprval
> 255) exprval
= '?';
4112 fprintf(fo
, "%*c", (sign
!= '-' ? width
: -width
), exprval
);
4114 fprintf(fo
, "%c", exprval
);
4117 case 'u': /* unsigned */
4119 exprval
= getExprArg(&defined
, NULL
);
4120 if (width
&& zerofill
) {
4121 fprintf(fo
, "%0*u", width
, (unsigned)(exprval
&0xffff));
4123 fprintf(fo
, "%*u", (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
4125 fprintf(fo
, "%u", (unsigned)(exprval
&0xffff));
4128 case 'x': case 'X': /* hex */
4130 exprval
= getExprArg(&defined
, NULL
);
4131 if (width
&& zerofill
) {
4132 fprintf(fo
, (ftype
== 'x' ? "%0*x" : "%0*X"), width
, (unsigned)(exprval
&0xffff));
4134 fprintf(fo
, (ftype
== 'x' ? "%*x" : "%*X"), (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
4136 fprintf(fo
, (ftype
== 'x' ? "%x" : "%X"), (unsigned)(exprval
&0xffff));
4140 if (ftype
<= 0 || ftype
== 127) ftype
= '?';
4141 fatal("invalid format specifier: '%c'", ftype
);
4145 if (tempstr
) free(tempstr
);
4150 ///////////////////////////////////////////////////////////////////////////////
4153 static int piERROR (void) {
4155 char *res
= getStrArg(&len
);
4156 fprintf(stdout
, "*** USER ERROR: ");
4157 processPrintf(stdout
, res
);
4158 fputc('\n', stdout
);
4159 fatal("user error abort");
4160 return PI_SKIP_LINE
;
4164 static int piWARNING (void) {
4166 char *res
= getStrArg(&len
);
4167 fprintf(stdout
, "*** USER WARNING ");
4169 fprintf(stdout
, "at file %s, line %d: ", currSrcLine
->fname
, currSrcLine
->lineNo
);
4171 fprintf(stdout
, "somewhere in time: ");
4173 processPrintf(stdout
, res
);
4174 fputc('\n', stdout
);
4175 return PI_SKIP_LINE
;
4179 ///////////////////////////////////////////////////////////////////////////////
4182 static int piPrintfCommon (int passNo
) {
4183 if (passNo
< 0 || pass
== passNo
) {
4185 char *res
= getStrArg(&len
);
4186 processPrintf(stdout
, res
);
4187 fputc('\n', stdout
);
4189 return PI_SKIP_LINE
;
4193 static int piDISPLAYX (int passNo
, int asHex
) {
4197 char *res
= getStrArg(&len
);
4199 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
4202 int32_t v
= getExprArg(&defined
, NULL
);
4204 if (passNo
< 0 || pass
== passNo
) {
4205 if (asHex
) printf("%04X", (unsigned int)v
);
4206 else printf("%d", v
);
4209 if (!eatComma()) break;
4211 return PI_SKIP_LINE
;
4215 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
4216 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
4217 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
4218 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
4219 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
4220 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
4222 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
4223 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
4224 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
4227 ///////////////////////////////////////////////////////////////////////////////
4230 static int piORG (void) {
4232 int32_t res
= getOneExprArg(&defined
, NULL
);
4234 if (!defined
) fatal("sorry, ORG operand value must be known here");
4235 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
4236 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
4241 if (!wasClr
&& res
> 0) clrAddr
= res
-1;
4243 return PI_CONT_LINE
;
4247 static int piDISP (void) {
4249 int32_t res
= getOneExprArg(&defined
, NULL
);
4251 if (!defined
) fatal("sorry, DISP operand value must be known here");
4252 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
4253 //printf("DISP=%d\n", res);
4255 return PI_CONT_LINE
;
4259 static int piENDDISP (void) {
4260 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
4263 return PI_CONT_LINE
;
4267 static int piENT (void) {
4269 int32_t res
= getOneExprArg(&defined
, NULL
);
4271 //if (!defined) fatal("sorry, ENT operand value must be known here");
4272 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
4274 return PI_CONT_LINE
;
4278 static int piCLR (void) {
4280 int32_t res
= getOneExprArg(&defined
, NULL
);
4282 //if (!defined) fatal("sorry, CLR operand value must be known here");
4283 if (res
< 0 || res
> 65535) fatal("invalid CLR operand value: %d", res
);
4286 return PI_CONT_LINE
;
4290 // RESERVE start, count
4291 static int piRESERVE (void) {
4292 int defined
= 1, start
;
4293 int32_t res
= getExprArg(&defined
, NULL
);
4294 if (!defined
) fatal("sorry, RESERVE operand values must be known here");
4295 if (res
< 0 || res
> 65535) fatal("invalid RESERVE address value: %d", res
);
4297 if (!eatComma()) fatal("RESERVE needs 2 args");
4298 res
= getOneExprArg(&defined
, NULL
);
4299 if (!defined
) fatal("sorry, RESERVE operand values must be known here");
4300 if (res
< 0 || res
> 65535) fatal("invalid RESERVE length value: %d", res
);
4302 if (memused
[start
]) fatal("trying to reserve already used memory at #%04X", start
);
4303 memresv
[start
++] = 1;
4305 return PI_CONT_LINE
;
4309 static int piALIGN (void) {
4313 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
4314 res
= getOneExprArg(&defined
, NULL
);
4315 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
4316 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
4317 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
4318 if (res
> 0 && pc
%res
!= 0) {
4324 return PI_CONT_LINE
;
4328 static int piDISPALIGN (void) {
4330 int32_t res
= getOneExprArg(&defined
, NULL
);
4332 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
4333 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
4334 if (res
> 0 && disp
%res
!= 0) {
4339 return PI_CONT_LINE
;
4343 ///////////////////////////////////////////////////////////////////////////////
4346 // DEFINCR operations
4350 DFI_SAR
, // arith shift, extend bit 7
4351 DFI_SAL
, // arith shift, extend bit 0
4364 typedef struct DefIncrOp_s DefIncrOp
;
4365 struct DefIncrOp_s
{
4367 uint16_t operator; // DFI_xxx
4371 static DefIncrOp
*dfi_list
= NULL
;
4372 //static int defIncr = 0;
4375 static void dfi_free (void) {
4377 DefIncrOp
*c
= dfi_list
;
4384 //FIXME: make this faster
4385 #define DFI_XSHIFT(sop_) do { \
4386 for (uint16_t f = 0; f < opnd; ++f) { \
4387 val = ((val&xmask) sop_ 1)|(val&andmask ? ormask : 0); \
4392 static int32_t dfi_apply (int32_t val
, int isbyte
) {
4393 const uint16_t xmask
= (isbyte
? 0xff : 0xffff);
4395 uint16_t andmask
, ormask
;
4396 for (DefIncrOp
*dop
= dfi_list
; dop
; dop
= dop
->next
) {
4397 uint16_t opnd
= dop
->operand
;
4398 switch (dop
->operator) {
4401 if (opnd
> (isbyte
? 7 : 15)) { val
= 0; break; }
4402 val
= (val
&xmask
)>>opnd
;
4406 if (opnd
> (isbyte
? 7 : 15)) { val
= 0; break; }
4407 val
= (val
&xmask
)<<opnd
;
4412 if (opnd
> 7) { val
= (val
&0x80 ? 0xff : 0x00); break; }
4413 andmask
= ormask
= 0x80;
4415 if (opnd
> 15) { val
= (val
&0x8000 ? 0xffff : 0x0000); break; }
4416 andmask
= ormask
= 0x8000;
4422 if (opnd
> (isbyte
? 7 : 15)) { val
= (val
&0x01 ? 0xff : 0x00); break; }
4423 andmask
= ormask
= 0x01;
4427 opnd
&= (isbyte
? 7 : 15);
4430 ormask
= (isbyte
? 0x80 : 0x8000);
4434 opnd
&= (isbyte
? 7 : 15);
4436 andmask
= (isbyte
? 0x80 : 0x8000);
4459 if (opnd
) val
/= opnd
; else val
= 0;
4462 if (opnd
) val
%= opnd
; else val
= 0;
4471 static int piDEFINCR (void) {
4474 int32_t cnt
= getOneExprArg(&defined
, NULL
);
4475 if (!defined
) fatal("DEFINCR: increment must be defined");
4477 return PI_CONT_LINE
;
4482 char *rstr
= getStrArg(&len
);
4483 if (!rstr
|| !strEquCI(rstr
, "expr")) fatal("invalid DEFINCR command (\"expr\" expected)");
4484 DefIncrOp
*last
= NULL
;
4485 while (eatComma()) {
4487 rstr
= getStrArg(&len
);
4488 if (!rstr
|| len
!= 3) fatal("invalid DEFINCR operator");
4489 DefIncrOp
*dop
= malloc(sizeof(DefIncrOp
));
4490 if (strEquCI(rstr
, "SHR")) dop
->operator = DFI_SHR
;
4491 else if (strEquCI(rstr
, "SHL")) dop
->operator = DFI_SHL
;
4492 else if (strEquCI(rstr
, "SAR")) dop
->operator = DFI_SAR
;
4493 else if (strEquCI(rstr
, "SAL")) dop
->operator = DFI_SAL
;
4494 else if (strEquCI(rstr
, "ROR")) dop
->operator = DFI_ROR
;
4495 else if (strEquCI(rstr
, "ROL")) dop
->operator = DFI_ROL
;
4496 else if (strEquCI(rstr
, "AND")) dop
->operator = DFI_AND
;
4497 else if (strEquCI(rstr
, "XOR")) dop
->operator = DFI_XOR
;
4498 else if (strEquCI(rstr
, "OR")) dop
->operator = DFI_OR
;
4499 else if (strEquCI(rstr
, "ADD")) dop
->operator = DFI_ADD
;
4500 else if (strEquCI(rstr
, "SUB")) dop
->operator = DFI_SUB
;
4501 else if (strEquCI(rstr
, "MUL")) dop
->operator = DFI_MUL
;
4502 else if (strEquCI(rstr
, "DIV")) dop
->operator = DFI_DIV
;
4503 else if (strEquCI(rstr
, "MOD")) dop
->operator = DFI_MOD
;
4504 else fatal("invalid DEFINCR operator '%s'", rstr
);
4506 if (!eatComma()) fatal("DEFINCR: operand expected");
4508 int32_t res
= getExprArg(&defined
, NULL
);
4509 if (!defined
) fatal("DEFINCR: operand must be defined");
4510 if (res
< 0) fatal("DEFINCR: operand must be positive");
4512 dop
->operand
= (uint16_t)res
;
4513 if (last
) last
->next
= dop
; else dfi_list
= dop
;
4518 int32_t cnt
= getOneExprArg(&defined
, NULL
);
4519 if (!defined
) fatal("DEFINCR: increment must be defined");
4524 DefIncrOp
*dop
= malloc(sizeof(DefIncrOp
));
4526 dop
->operator = DFI_ADD
;
4531 cnt
&= 0xffff; //UB, i don't care
4533 DefIncrOp
*dop
= malloc(sizeof(DefIncrOp
));
4535 dop
->operator = DFI_SUB
;
4542 return PI_CONT_LINE
;
4546 static int piDEFBW (int isWord
) {
4549 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
4553 char *rstr
= getStrExprArg(&len
, isWord
);
4555 for (f
= 0; f
< len
; ++f
) {
4556 int32_t b
= (uint8_t)rstr
[f
];
4558 b
= dfi_apply(b
, 1/*isbyte*/);
4559 if (b
< -128 || b
> 255) fatal("invalid operand value: %d", b
);
4560 if (b
< 0) b
+= 256;
4563 if (!eatComma()) break;
4570 res
= getExprArg(&defined
, &fixuptype
);
4574 //int32_t res = getExprArg(&defined, &fixuptype);
4576 if (pass
> 0 && !defined
) fatal("undefined operand");
4578 res
= dfi_apply(res
, !isWord
/*isbyte*/);
4580 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
4581 if (res
< 0) res
+= 65536;
4583 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 2);
4587 switch (fixuptype
) {
4588 case UR_FIXUP_WORD
: /* swapped bytes */
4589 urasm_fixup_operand(NULL
, pc
, disp
, UR_FIXUP_HIBYTE
, 1);
4590 urasm_fixup_operand(NULL
, pc
, disp
+1, UR_FIXUP_LOBYTE
, 1);
4592 case UR_FIXUP_LOBYTE
:
4593 case UR_FIXUP_HIBYTE
:
4594 warningMsg("non-word fixup for reversed word; wtf?!");
4601 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
4602 if (fixuptype
!= UR_FIXUP_NONE
) {
4603 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
4604 urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
4606 if (res
< 0) res
+= 256;
4610 if (!eatComma()) break;
4612 return PI_CONT_LINE
;
4615 static int piDEFB (void) { return piDEFBW(0); }
4616 static int piDEFW (void) { return piDEFBW(1); }
4617 static int piDEFR (void) { return piDEFBW(2); }
4620 static int piDEFS (void) {
4623 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
4624 int32_t res
= getExprArg(&defined
, &fixuptype
);
4626 if (pass
> 0 && !defined
) fatal("undefined operand");
4627 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
4628 if (*currLine
&& currLine
[0] == ',') {
4630 bt
= getExprArg(&defined
, NULL
);
4631 if (pass
> 0 && !defined
) fatal("undefined operand");
4633 bt
= dfi_apply(bt
, 1/*isbyte*/);
4634 if (bt
< -128 || bt
> 255) fatal("invalid operand value: %d", res
);
4635 if (bt
< 0) bt
+= 256;
4636 if (fixuptype
!= UR_FIXUP_NONE
) {
4637 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
4639 for (f
= 0; f
< res
; ++f
) {
4640 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
4644 pc
+= res
; disp
+= res
;
4646 if (!eatComma()) break;
4648 return PI_CONT_LINE
;
4652 /* bit 0: put '\0' */
4653 /* bit 1: set bit 7 of last byte */
4654 /* bit 2: put length */
4655 static int piDEFSTR (int type
) {
4659 char *res
= getStrArg(&len
);
4662 if (len
> 255) fatal("string too long");
4665 for (f
= 0; f
< len
; ++f
) {
4666 uint8_t b
= (uint8_t)res
[f
];
4668 if ((type
&0x02) && f
== len
-1) b
|= 0x80;
4671 if (type
&0x01) emitByte(0);
4674 int32_t v
= getExprArg(&defined
, NULL
);
4676 if (pass
> 0 && !defined
) fatal("undefined expression");
4677 if (!defined
) v
= 0; else v
= dfi_apply(v
, 1/*isbyte*/);
4678 if (v
< -128 || v
> 255) fatal("invalid operand value: %d", v
);
4679 if (v
< 0) v
+= 256;
4682 if (!eatComma()) break;
4684 return PI_CONT_LINE
;
4688 static int piDEFM (void) { return piDEFSTR(0x00); }
4689 static int piDEFZ (void) { return piDEFSTR(0x01); }
4690 static int piDEFX (void) { return piDEFSTR(0x02); }
4691 static int piDEFC (void) { return piDEFSTR(0x04); }
4694 ///////////////////////////////////////////////////////////////////////////////
4697 /* INCBIN "name"[,maxlen[,offset]] */
4698 static int piINCBIN (void) {
4705 char *args
= currLine
;
4707 if (!currLine
[0]) fatal("INCBIN without file name");
4712 } else if (currLine
[0] == '<') {
4720 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4721 int softinclude
= 0;
4722 if (fn
[0] == '?') { softinclude
= 1; ++fn
; while (isSpace(*fn
)) ++fn
; }
4723 if (!fn
[0]) fatal("INCBIN: empty file name");
4725 char *fname
= createIncludeName(fn
, system
, NULL
, NULL
);
4726 memmove(currLine
, args
, strlen(args
)+1);
4729 if (currLine
[0] == ',') {
4732 maxlen
= getExprArg(&defined
, NULL
);
4733 if (!defined
) fatal("INCBIN: undefined maxlen");
4734 if (maxlen
< 1) { free(fname
); return 1; } // nothing to do
4737 // offset (negative: from the end)
4738 if (currLine
[0] == ',') {
4741 offset
= getOneExprArg(&defined
, NULL
);
4742 if (!defined
) fatal("INCBIN: undefined offset");
4744 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
4747 fl
= fopen(fname
, "rb");
4749 if (!softinclude
) fatal("INCBIN: file not found: '%s'", fname
);
4752 if (fseek(fl
, offset
, (offset
< 0 ? SEEK_END
: SEEK_SET
)) < 0) {
4754 fatal("INCBIN: error seeking to %d in file '%s'", offset
, fname
);
4757 while (maxlen
-- > 0) {
4758 int res
= fread(&bt
, 1, 1, fl
);
4760 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: '%s'", fname
); }
4767 return PI_SKIP_LINE
;
4771 /* DATABIN "name"[,len[,offset[,codeaddr]]] */
4772 /* DATABIN "name|dfname"[,len[,offset[,codeaddr]]] */
4773 static int piDATABINcommon (int ascode
) {
4774 if (pass
!= 1) return PI_SKIP_LINE
;
4780 int codeaddr
= 32768;
4782 char *args
= currLine
;
4784 if (!currLine
[0]) fatal("DATABIN without file name");
4789 } else if (currLine
[0] == '<') {
4797 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4799 if (fn
[0] == '?') { allowskip
= 1; ++fn
; while (isSpace(*fn
)) ++fn
; }
4800 if (!fn
[0]) fatal("DATABIN: empty file name");
4801 char *fnameup
= strdup(fn
);
4802 memmove(currLine
, args
, strlen(args
)+1);
4805 if (currLine
[0] == ',') {
4808 maxlen
= getExprArg(&defined
, NULL
);
4809 if (!defined
) fatal("DATABIN: undefined length");
4810 if (maxlen
< 1) { free(fnameup
); return 1; } // nothing to do
4813 // offset (negative: from the end)
4814 if (currLine
[0] == ',') {
4817 offset
= getExprArg(&defined
, NULL
);
4818 if (!defined
) fatal("DATABIN: undefined offset");
4822 if (currLine
[0] == ',') {
4825 codeaddr
= getOneExprArg(&defined
, NULL
);
4826 if (!defined
) fatal("DATABIN: undefined codeaddr");
4829 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
4832 // find and extract "disk name"
4835 char *pipeptr
= strchr(fnameup
, '|');
4837 if (!pipeptr
[1]) fatal("empty data file output name");
4838 diskname
= strdup(pipeptr
+1);
4841 // build output name from disk name
4842 char *slp
= strrchr(fnameup
, '/');
4844 char *slp1
= strrchr(fnameup
, '\\');
4845 if (slp1
&& (!slp
|| slp1
> slp
)) slp
= slp1
;
4849 if (!slp
[0]) fatal("empty data file output name");
4850 diskname
= strdup(slp
);
4852 diskname
= strdup(fnameup
);
4857 char *fname
= createIncludeName(fnameup
, system
, NULL
, NULL
);
4860 OutDataFile
*off
= appendDataFile(fname
, offset
, maxlen
, allowskip
);
4864 off
->name
= diskname
;
4865 off
->start
= codeaddr
&0xffffU
;
4866 off
->hasstart
= (hasstart
|| ascode
);
4867 //for (char *s = off->name; *s; ++s) *s = toUpper(*s);
4868 printf("data file: %s (start=%u; size=%u; std=%d)\n", off
->name
, off
->start
, off
->size
, off
->hasstart
);
4872 return PI_SKIP_LINE
;
4876 static int piDATABIN (void) { return piDATABINcommon(0); }
4877 static int piCODEBIN (void) { return piDATABINcommon(1); }
4880 ///////////////////////////////////////////////////////////////////////////////
4883 /* INCLUDE "name" */
4884 static int piINCLUDE (void) {
4887 char *args
= currLine
;
4888 if (!currLine
[0]) fatal("INCLUDE without file name");
4892 } else if (currLine
[0] == '<') {
4899 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4901 int softinclude
= 0;
4902 if (fn
[0] == '?') { softinclude
= 1; ++fn
; while (isSpace(*fn
)) ++fn
; }
4903 if (!fn
[0]) fatal("INCLUDE: empty file name");
4905 if (asmTextInclude(fn
, system
, softinclude
) != 0) {
4906 if (!softinclude
) fatal("INCLUDE: some shit happens!");
4908 return PI_SKIP_LINE
;
4912 ///////////////////////////////////////////////////////////////////////////////
4913 // MODULE, ENDMODULE
4915 static int piENDMODULE (void) {
4916 if (!currModule
) fatal("ENDMODULE without MODULE");
4917 const char *mn
= NULL
;
4918 if (currLine
[0]) mn
= getOneLabelArg();
4920 return PI_SKIP_LINE
;
4924 static int piMODULE (void) {
4927 SourceLine
*ol
= currSrcLine
;
4929 if (currModule
) fatal("no nested modules allowed");
4930 mn
= getOneLabelArg();
4931 if (!urasm_is_valid_name(mn
)) fatal("invalid module name: %s", mn
);
4932 mi
= moduleFind(mn
);
4933 //fprintf(stderr, "+++MODULE: [%s] %p (seen=%d)\n", mn, mi, (mi ? mi->seen : -1));
4936 if (strcmp(mi
->fname
, currSrcLine
->fname
)) {
4937 fatal("duplicate module definition; previous was in %s", mi
->fname
);
4940 nextSrcLine(); /* skip "MODULE" line */
4941 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
4943 fatal("no ENDMODULE");
4945 if (inum
== 0) fatal("no nested modules allowed");
4948 //skipInstruction(); //k8:wtf?!
4949 return piENDMODULE();
4952 mi
= moduleAdd(mn
, currSrcLine
->fname
);
4957 return PI_SKIP_LINE
;
4962 static int piMODEL (void) {
4963 char *mn
= getOneIdArgLo();
4964 if (strSkipSpaces(currLine
)[0]) fatal("only one model name expected");
4965 if (strcmp(mn
, "z80") == 0) urasm_allow_zxnext
= 0;
4966 else if (strcmp(mn
, "z80a") == 0) urasm_allow_zxnext
= 0;
4967 else if (strcmp(mn
, "z80n") == 0) urasm_allow_zxnext
= 1;
4968 else if (strcmp(mn
, "z80next") == 0) urasm_allow_zxnext
= 1;
4969 else if (strcmp(mn
, "zxnext") == 0) urasm_allow_zxnext
= 1;
4970 else fatal("invalid model name: %s", mn
);
4971 return PI_SKIP_LINE
;
4975 ////////////////////////////////////////////////////////////////////////////////
4976 // $SAVECODE "filename",addr,size [,ldaddr [,options...]]
4979 // wipe -- wipe saved area (mark it as unused)
4981 static int piSAVECODE (void) {
4982 //if (pass != 1) return PI_SKIP_LINE;
4983 //char *fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4986 int optWipe
= 0, optData
= 0;
4988 char *args
= currLine
;
4990 if (!currLine
[0]) fatal("SAVECODE without file name");
4994 } else if (currLine
[0] == '<') {
5000 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
5001 if (!fn
[0]) fatal("DATABIN: empty file name");
5003 char *fname
= strdup(fn
);
5004 memmove(currLine
, args
, strlen(args
)+1);
5009 int staddr
= getExprArg(&defined
, NULL
);
5010 if (!defined
) fatal("SAVECODE: undefined start address");
5011 if (staddr
< 0 || staddr
> 65535) fatal("SAVECODE: invalid start address (%d)", staddr
);
5016 int size
= getExprArg(&defined
, NULL
);
5017 if (!defined
) fatal("SAVECODE: undefined size");
5018 if (size
< 0 || size
> 65535 || staddr
+size
> 65536) fatal("SAVECODE: invalid size");
5021 int ldaddr
= staddr
;
5022 if (checkDelim(',')) {
5024 ldaddr
= getExprArg(&defined
, NULL
);
5025 if (!defined
) fatal("SAVECODE: undefined load address");
5026 if (ldaddr
< 0 || ldaddr
> 65535) fatal("SAVECODE: invalid load address");
5030 while (checkDelim(',')) {
5031 char *nstr
= getLabelArg(0/*checkdelim*/);
5032 if (strEquCI(nstr
, "wipe")) { optWipe
= 1; continue; }
5033 if (strEquCI(nstr
, "data")) { optData
= 1; continue; }
5034 fatal("SAVECODE: unknown option '%s'", nstr
);
5036 if (!isLineEnd()) fatal("SAVECODE: unknown extra args");
5038 // save, if we are on the second pass
5039 if (pass
== 1 && size
> 0) {
5040 OutDataFile
*off
= appendDataData(memory
+(unsigned)staddr
, (unsigned)staddr
, (unsigned)size
);
5041 if (!off
) fatal("SAVECODE: out of memory");
5043 off
->hasstart
= !optData
;
5047 if (optWipe
) memset(memused
+staddr
, 0, size
);
5049 if (fname
) free(fname
);
5050 return PI_SKIP_LINE
;
5054 ////////////////////////////////////////////////////////////////////////////////
5057 static int piENDS (void) {
5058 fatal("ENDS without STRUCT");
5060 return PI_SKIP_LINE
;
5064 // this will be registered for each new struct
5065 // use `urCurrentOp` to check what structure we are creating
5066 // `currSeenLabel` contains current seen label
5067 static int piSTRUCT_Internal (void) {
5068 StructInfo
*sth
= urCurrentOp
->udata
;
5070 // reserve room, set default values, add labels
5071 uint16_t origPC
= pc
;
5072 uint16_t origDisp
= disp
;
5077 if (sth
->zerofill
) {
5078 for (uint16_t f
= 0; f
< sth
->size
; ++f
) {
5079 putByte(origPC
+f
, 0);
5083 for (StructField
*fld
= sth
->fields
; fld
; fld
= fld
->next
) {
5085 if (fld
->size
== 1) {
5086 const uint8_t v
= (fld
->initValue
< 0 ? 0x100+fld
->initValue
: fld
->initValue
)&0xff;
5087 putByte(origPC
+fld
->ofs
, v
);
5088 } else if (fld
->size
== 2) {
5089 const uint16_t v
= (fld
->initValue
< 0 ? 0x10000+fld
->initValue
: fld
->initValue
)&0xffff;
5090 putWord(origPC
+fld
->ofs
, v
);
5091 } else if (fld
->size
== 4) {
5093 memcpy(&v
, &fld
->initValue
, 4);
5094 putWord(origPC
+fld
->ofs
, v
&0xffff);
5095 putWord(origPC
+fld
->ofs
+2, (v
>>16)&0xffff);
5097 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth
->name
);
5103 //fprintf(stderr, "LBL: <%s>\n", currSeenLabel);
5104 // need to create a new label?
5105 if (!currSeenLabel
[0] || !fld
->name
|| !fld
->name
[0]) continue;
5107 char *nn
= mangleLabelName(currSeenLabel
);
5108 char *lbn
= malloc(strlen(nn
)+strlen(fld
->name
)+64);
5109 sprintf(lbn
, "%s.%s", nn
, fld
->name
);
5111 UrLabelInfo
*c
= urFindLabel(lbn
);
5112 if (!c
) c
= urAddLabel(lbn
);
5115 c
->type
= LBL_TYPE_STOFS
;
5116 c
->value
= origDisp
+fld
->ofs
;
5121 if (currSeenLabel
[0]) {
5122 char *nn
= mangleLabelName(currSeenLabel
);
5123 char *lbn
= malloc(strlen(nn
)+strlen("_sizeof")+64);
5124 sprintf(lbn
, "%s.%s", nn
, "_sizeof");
5126 UrLabelInfo
*c
= urFindLabel(lbn
);
5127 if (!c
) c
= urAddLabel(lbn
);
5130 c
->type
= LBL_TYPE_STOFS
;
5131 c
->value
= sth
->size
;
5135 // now parse initial values, if there are any
5137 const int isCurly
= checkDelim('{');
5138 SourceLine
*stline
= currSrcLine
;
5140 int wasNewLine
= 1; // don't require a comma
5144 setCurSrcLine(stline
);
5145 fatal("STRUCT: no closing curly bracket");
5150 if (!isCurly
) break;
5157 if (isCurly
&& checkDelim('}')) {
5159 if (!isLineEnd()) fatal("STRUCT: closing curly bracket must be alone on the line");
5169 char *nstr
= getLabelArg(0/*checkdelim*/);
5172 StructField
*fld
= sth
->fields
;
5173 for (; fld
; fld
= fld
->next
) if (fld
->name
&& strcmp(fld
->name
, nstr
) == 0) break;
5174 if (!fld
) fatal("unknown field `%s` in struct `%s`", nstr
, sth
->name
);
5176 // only `=` is allowed
5180 int32_t ival
= getExprArg(&defined
, NULL
);
5182 if (fld
->size
== 1) {
5183 if (defined
&& (ival
< -128 || ival
> 255)) {
5184 fatal("STRUCT: field `%s` has inivalid initial value: %d",
5185 (fld
->name
? fld
->name
: "<anonymous>"), ival
);
5187 if (ival
< -128) ival
= -128;
5188 const uint8_t v
= (ival
< 0 ? 0x100+ival
: ival
)&0xff;
5189 putByte(origPC
+fld
->ofs
, v
);
5190 } else if (fld
->size
== 2) {
5191 if (defined
&& (ival
< -32768 || ival
> 65535)) {
5192 fatal("STRUCT: field `%s` has inivalid initial value: %d",
5193 (fld
->name
? fld
->name
: "<anonymous>"), ival
);
5195 if (ival
< -32768) ival
= -32768;
5196 const uint16_t v
= (ival
< 0 ? 0x10000+ival
: ival
)&0xffff;
5197 putWord(origPC
+fld
->ofs
, v
);
5198 } else if (fld
->size
== 4) {
5200 memcpy(&v
, &ival
, 4);
5201 putWord(origPC
+fld
->ofs
, v
&0xffff);
5202 putWord(origPC
+fld
->ofs
+2, (v
>>16)&0xffff);
5204 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth
->name
);
5209 return PI_SKIP_LINE
;
5213 static int structCheckSizeDecl (const char *s
) {
5214 if (strEquCI(s
, "byte")) return 1;
5215 if (strEquCI(s
, "word")) return 2;
5216 if (strEquCI(s
, "address")) return 2;
5217 if (strEquCI(s
, "dword")) return 4;
5218 if (strEquCI(s
, "label")) return 0;
5219 if (strEquCI(s
, "defs")) return -666;
5220 if (strEquCI(s
, "ds")) return -666;
5225 // STRUCT name [,init_offset]
5226 static int piSTRUCT (void) {
5227 int zerofill
= 0, extend
= 0;
5228 int defined
= 1, inum
;
5231 //fprintf(stderr, "PASS=%d\n", pass);
5233 stline
= currSrcLine
;
5234 nextSrcLine(); // skip ourself
5235 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "ENDS", NULL
))) {
5236 setCurSrcLine(stline
);
5237 fatal("STRUCT: no ENDS");
5239 return PI_SKIP_LINE
;
5243 if (checkDelim('*')) extend = 1;
5244 else if (checkDelim('!')) extend = 2; // compatibility with the old code
5245 else if (checkDelim('+')) extend = 2;
5248 // flags: `[flag, flag...]`
5249 // currently, only `zero_fill` flag is defined
5250 if (checkDelim('[')) {
5251 while (!checkDelim(']')) {
5252 if (zerofill
== 0 && checkIdentCI("zero_fill")) zerofill
= 1;
5253 else fatal("invalid structure flag");
5254 if (!checkDelim(',')) {
5255 if (!checkDelim(']')) fatal("invalid structure flag list");
5261 // if `extend` is before a struct name, we are extending it without inheriting
5262 if (checkIdentCI("extend") || checkIdentCI("extends")) extend
= 1;
5265 char stname
[128], estname
[128];
5267 char *nstr
= getLabelArg(0/*checkdelim*/);
5268 if (!nstr
|| !nstr
[0]) fatal("structure name expected");
5269 if (strlen(nstr
) > 127) fatal("structure name too long");
5270 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr
)) {
5271 fatal("structure name `%s` is invalid", nstr
);
5273 strcpy(stname
, nstr
);
5275 StructInfo
*sth
= NULL
;
5277 for (sth
= structList
; sth
; sth
= sth
->next
) {
5278 if (strcmp(sth
->name
, stname
) == 0) {
5279 fatal("duplicate struct name `%s`!", stname
);
5282 if (urFindOp(stname
)) fatal("invalid struct name `%s`!", stname
);
5285 if (extend
|| checkIdentCI("extend") || checkIdentCI("extends")) {
5289 nstr
= getLabelArg(0/*checkdelim*/);
5290 if (!nstr
|| !nstr
[0]) fatal("parent structure name expected");
5291 if (strlen(nstr
) > 127) fatal("parent structure name too long");
5292 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr
)) {
5293 fatal("structure name `%s` is invalid", nstr
);
5295 strcpy(estname
, nstr
);
5296 for (sth
= structList
; sth
; sth
= sth
->next
) {
5297 if (strcmp(sth
->name
, estname
) == 0) break;
5299 if (!sth
) fatal("STRUCT: cannot extend unknown structure `%s`", estname
);
5302 for (sth
= structList
; sth
; sth
= sth
->next
) {
5303 if (strcmp(sth
->name
, stname
) == 0) break;
5305 if (!sth
) fatal("STRUCT: cannot extend unknown structure `%s`", stname
);
5309 StructInfo
*stnew
= calloc(1, sizeof(StructInfo
));
5310 stnew
->name
= strdup(stname
);
5311 stnew
->size
= sth
->size
;
5312 stnew
->zerofill
= (sth
->zerofill
|| zerofill
);
5315 StructField
*xlast
= NULL
;
5316 for (StructField
*fld
= sth
->fields
; fld
; fld
= fld
->next
) {
5317 StructField
*nf
= calloc(1, sizeof(StructField
));
5318 nf
->name
= (fld
->name
? strdup(fld
->name
) : NULL
);
5320 nf
->size
= fld
->size
;
5321 nf
->initValue
= fld
->initValue
;
5322 nf
->hasInit
= fld
->hasInit
;
5323 if (xlast
) xlast
->next
= nf
; else stnew
->fields
= nf
;
5330 // allocate struct header
5331 sth
= calloc(1, sizeof(StructInfo
));
5332 sth
->name
= strdup(stname
);
5333 sth
->zerofill
= zerofill
;
5336 int32_t currofs
= (sth
? sth
->size
: 0);
5337 // parse initial offset
5338 if (checkDelim(',')) {
5340 currofs
= getExprArg(&defined
, NULL
);
5341 if (!defined
) fatal("STRUCT: initial offset must be defined");
5342 if (currofs
< 0 || currofs
> 65535) fatal("STRUCT: invalid initial offset");
5346 if (!isLineEnd()) fatal("too many arguments for STRUCT");
5348 // for fast appending
5349 StructField
*fldlast
= (sth
? sth
->fields
: NULL
);
5350 StructField
*fldnew
= NULL
;
5353 while (fldlast
->next
) fldlast
= fldlast
->next
;
5356 if (extend
== 2) fldnew
= sth
->fields
;
5359 stline
= currSrcLine
;
5360 nextSrcLine(); // skip ourself
5361 //fprintf(stderr, "001: <%s>\n", currLine);
5363 while (currSrcLine
) {
5364 size_t slen
= strlen(currLine
);
5365 currLineRemoveComment();
5366 currLineTrimBlanks();
5367 if (!currLine
[0]) { nextSrcLine(); continue; }
5369 if (strIsCommand("ENDS", currSrcLine
->line
)) break;
5370 //fprintf(stderr, "112: <%s> (%d)\n", currLine, inum);
5374 char *fldname
= getLabelArg(0/*checkdelim*/);
5375 int fldsize
= structCheckSizeDecl(fldname
);
5376 if (fldsize
== -666) {
5379 fldsize
= getExprArg(&defined
, NULL
);
5380 if (!defined
) fatal("STRUCT: DEFS size must be defined");
5381 if (fldsize
< 0 || fldsize
> 65535) fatal("STRUCT: DEFS size is invalid");
5384 } else if (fldsize
< 0) {
5385 // this must be a label
5387 for (StructField
*cf
= sth
->fields
; cf
; cf
= cf
->next
) {
5388 if (cf
->name
&& cf
->name
[0] && strcmp(cf
->name
, fldname
) == 0) {
5393 if (found
) fatal("duplicate field name '%s'", fldname
);
5394 fldname
= strdup(fldname
);
5395 char *szdecl
= getLabelArg(0/*checkdelim*/);
5396 fldsize
= structCheckSizeDecl(szdecl
);
5397 if (fldsize
== -666) {
5400 fldsize
= getExprArg(&defined
, NULL
);
5401 if (!defined
) fatal("STRUCT: DEFS size must be defined");
5402 if (fldsize
< 0 || fldsize
> 65535) fatal("STRUCT: DEFS size is invalid");
5405 if (fldsize
< 0) fatal("field size definition expected");
5408 fldname
= NULL
; // no name
5411 // check for init value
5413 int32_t initval
= 0;
5415 if (!skipInit
&& checkDelim('=')) {
5417 initval
= getExprArg(&defined
, NULL
);
5418 if (!defined
) fatal("STRUCT: initial value must be defined");
5421 (fldsize
== 1 && (initval
< -128 || initval
> 255)) ||
5422 (fldsize
== 2 && (initval
< -32768 || initval
> 65535)))
5424 fatal("STRUCT: initial value is out of range");
5428 if (!isLineEnd()) fatal("STRUCT: extra field data");
5430 if (currofs
+fldsize
> 65535) fatal("STRUCT: too big");
5433 StructField
*fld
= calloc(1, sizeof(StructField
));
5434 fld
->name
= fldname
;
5436 fld
->size
= fldsize
;
5437 fld
->hasInit
= hasInit
;
5438 fld
->initValue
= initval
;
5439 if (fldlast
) fldlast
->next
= fld
; else sth
->fields
= fld
;
5443 if (!fldnew
) fldnew
= fld
;
5446 fprintf(stderr
, "FLD <%s>: ofs=%u; size=%u; hasinit=%d; init=%d\n", fld
->name
, fld
->ofs
,
5447 fld
->size
, fld
->hasInit
, fld
->initValue
);
5452 if (!currSrcLine
) { setCurSrcLine(stline
); fatal("no ENDS"); }
5453 if (currofs
> 65535) fatal("STRUCT: too big");
5454 sth
->size
= currofs
;
5456 //FIXME: structs must be local to module!
5458 // register new struct
5459 if (extend
== 0 || extend
== 2) {
5460 StructInfo
*slast
= structList
;
5462 while (slast
->next
) slast
= slast
->next
;
5467 UrAsmOp
*op
= urAddOp(sth
->name
, &piSTRUCT_Internal
);
5471 // add struct labels
5472 for (StructField
*fld
= fldnew
; fld
; fld
= fld
->next
) {
5473 if (!fld
->name
|| !fld
->name
[0]) continue;
5475 char *nn
= mangleLabelName(sth
->name
);
5476 char *lbn
= malloc(strlen(nn
)+strlen(fld
->name
)+64);
5477 sprintf(lbn
, "%s.%s", nn
, fld
->name
);
5479 //fprintf(stderr, "%d: <%s>\n", extend, lbn);
5481 UrLabelInfo
*c
= urFindLabel(lbn
);
5482 if (c
) fatal("STRUCT: field name '%s' conflict!", fld
->name
);
5484 c
= urAddLabel(lbn
);
5486 c
->type
= LBL_TYPE_STOFS
;
5487 c
->value
= fld
->ofs
;
5491 // add `structname.sizeof`
5493 char *nn
= mangleLabelName(sth
->name
);
5494 char *lbn
= malloc(strlen(nn
)+64);
5495 sprintf(lbn
, "%s.%s", nn
, "_sizeof");
5497 UrLabelInfo
*c
= urFindLabel(lbn
);
5499 if (!extend
) fatal("STRUCT: field name '%s' conflict!", "_sizeof");
5501 c
= urAddLabel(lbn
);
5505 c
->type
= LBL_TYPE_STOFS
;
5506 c
->value
= sth
->size
;
5510 return PI_SKIP_LINE
;
5514 // ////////////////////////////////////////////////////////////////////////// //
5515 static int piEND_PASMO (void) {
5517 int32_t res
= getOneExprArg(&defined
, NULL
);
5518 //if (!defined) fatal("sorry, ENT operand value must be known here");
5519 if (res
< 0 || res
> 65535) fatal("invalid END operand value: %d", res
);
5521 return PI_CONT_LINE
;
5525 ///////////////////////////////////////////////////////////////////////////////
5528 static int piEDUP (void) {
5529 fatal("EDUP without DUP");
5531 return PI_SKIP_LINE
;
5535 static int piENDR (void) {
5536 fatal("ENDR without REPT");
5538 return PI_SKIP_LINE
;
5542 // DUP count [,var [,initval [,incr]]]
5543 static int piDUPEx (int isrept
) {
5544 int defined
= 1, inum
;
5545 SourceLine
*stline
, *eline
= NULL
;
5547 //int32_t cnt = getOneExprArg(&defined, NULL);
5548 int32_t cnt
= getExprArg(&defined
, NULL
);
5549 if (!defined
) fatal("DUP: counter must be defined");
5550 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
5551 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
5553 int32_t initval
= 0;
5559 if (currLine
[0] == ',') {
5561 char *lbl
= getLabelArg(0/*checkdelim*/);
5563 if (strlen(lbl
) > 127) fatal("DUP counter variable name too long");
5564 if (urFindOp(lbl
) || !urasm_is_valid_name(lbl
)) fatal("DUP counter variable name is invalid");
5569 // initial variable value
5570 if (currLine
[0] == ',') {
5573 initval
= getExprArg(&defined
, NULL
);
5574 if (!defined
) fatal("DUP: initial value must be defined");
5578 if (currLine
[0] == ',') {
5581 incr
= getExprArg(&defined
, NULL
);
5582 if (!defined
) fatal("DUP: increment value must be defined");
5585 if (currLine
[0]) fatal("too many arguments for DUP");
5590 // now find corresponding EDUP
5591 // note that we should skip nested DUPs
5592 nextSrcLine(); // skip ourself
5593 stline
= currSrcLine
;
5594 while (currSrcLine
) {
5595 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "REPT", "EDUP", "ENDR", "ENDM", NULL
))) break;
5596 if (inum
== 0 || inum
== 1) {
5597 if (dupsp
>= 128) fatal("too many nested DUPs");
5598 dupstack
[dupsp
++] = isrept
;
5599 isrept
= (inum
== 1);
5600 nextSrcLine(); // skip DUP
5603 if (dupsp
== 0) { eline
= currSrcLine
; break; }
5605 if (inum
< 3) fatal("invalid REPT end directive");
5607 if (inum
>= 3) fatal("invalid DUP end directive");
5609 isrept
= dupstack
[--dupsp
];
5610 nextSrcLine(); // skip EDUP
5613 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
5615 // create counter local, if necessary
5616 UrLabelInfo
*lcnt
= (vname
[0] ? urAddTempLocal(vname
) : NULL
);
5617 if (lcnt
) lcnt
->value
= initval
;
5621 setCurSrcLine(stline
);
5622 while (currSrcLine
!= eline
) processCurrentLine();
5623 // increment counter
5624 if (lcnt
) lcnt
->value
+= incr
;
5628 urRemoveTempLocal(lcnt
);
5630 return PI_SKIP_LINE
;
5634 static int piDUP (void) { return piDUPEx(0); }
5635 static int piREPT (void) { return piDUPEx(1); }
5638 ///////////////////////////////////////////////////////////////////////////////
5641 static int ifCount
= 0;
5645 // -1: error (should not happen)
5646 // 0: successfully complete
5647 // 1: stopped *AT* "ELSE"
5648 // 2: stopped *AT* "ELSIF"
5649 static int ifSkipToEndIfOrElse (int wholeBody
) {
5650 int inum
, wasElse
= 0;
5653 while (currSrcLine
) {
5654 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "IF", "ELSE", "ENDIF",
5655 "ELSEIF", "MACRO", "ENDM", "IFX",
5662 nextSrcLine(); // skip IF
5663 ifSkipToEndIfOrElse(1); // and recurse
5664 nextSrcLine(); // skip ENDIF
5667 if (wasElse
) fatal("duplicate ELSE");
5668 if (!wholeBody
) return 1;
5670 nextSrcLine(); // skip ELSE
5671 break; // and continue
5673 return 0; // and exit
5674 case 3: /* elseif */
5675 case 7: /* elseifx */
5677 case 9: /* elsifx */
5678 if (wasElse
) fatal("ELSIF in ELSE");
5679 if (!wholeBody
) return 2;
5680 nextSrcLine(); // skip ELSIF
5681 break; // and continue
5683 // skip it as a whole
5684 nextSrcLine(); // skip MACRO
5686 oline
= currSrcLine
;
5687 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) { setCurSrcLine(oline
); fatal("invalid MACRO"); }
5688 if (inum
== 1) break;
5689 fatal("invalid nested MACRO");
5691 nextSrcLine(); // skip ENDM
5694 fatal("unexpected ENDM");
5697 fatal("IF without ENDIF");
5702 static int piENDIF (void) {
5703 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5704 if (--ifCount
< 0) fatal("ENDIF without IF");
5706 return PI_SKIP_LINE
;
5710 static int piELSE (void) {
5711 if (--ifCount
< 0) fatal("ELSE without IF");
5712 nextSrcLine(); // skip ELSE
5713 ifSkipToEndIfOrElse(1);
5714 return PI_SKIP_LINE
;
5717 static int piELSIF (void) {
5718 if (--ifCount
< 0) fatal("ELSIF without IF");
5719 nextSrcLine(); // skip ELSIF
5720 ifSkipToEndIfOrElse(1);
5721 return PI_SKIP_LINE
;
5725 static int piELSIFX (void) {
5726 if (--ifCount
< 0) fatal("ELSIFX without IF");
5727 nextSrcLine(); // skip ELSIFX
5728 ifSkipToEndIfOrElse(1);
5729 return PI_SKIP_LINE
;
5733 static int piIFAll (int isIfX
) {
5735 int ooo
= lblOptMakeU2
;
5737 //fprintf(stderr, "piIFALL: <%s>\n", currLine);
5738 lblOptMakeU2
= (isIfX
? 1 : 0);
5739 int32_t cond
= getOneExprArg(&defined
, NULL
);
5741 //fprintf(stderr, "piIFALL COND: <%d>\n", cond);
5744 if (!isIfX
) fatal("IF: condition must be defined");
5745 cond
= 0; // for IFX: 0 if there is any undefined label
5748 // ok, do it until ELSE/ELSIF/ENDIF
5750 return PI_SKIP_LINE
;
5756 nextSrcLine(); // skip last instruction
5757 // skip until ELSE/ELSIF/ENDIF
5758 r
= ifSkipToEndIfOrElse(0);
5759 /*!fprintf(stderr, "ELSIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5760 if (r
== 0) break; // ENDIF
5761 if (r
== 1) { ++ifCount
; break; } // ELSE
5762 // ELSIF, do condition
5763 args
= strIsCommand("ELSIF", currLine
);
5764 if (args
== NULL
) args
= strIsCommand("ELSEIF", currLine
);
5768 args
= strIsCommand("ELSIFX", currLine
);
5769 if (args
== NULL
) args
= strIsCommand("ELSEIFX", currLine
);
5770 if (args
== NULL
) fatal("internal error in conditionals"); // the thing that should not be
5773 memmove(currLine
, args
, strlen(args
)+1);
5774 /*!fprintf(stderr, "ELSIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5775 cond
= getOneExprArg(&defined
, NULL
);
5776 /*!fprintf(stderr, "ELSIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0), cond);*/
5778 if (!isIfX
) fatal("ELSIF: condition must be defined");
5779 cond
= 0; // for IFX: 0 if there is any undefined label
5781 if (cond
) { ++ifCount
; break; } // condition is true
5783 return PI_SKIP_LINE
;
5787 static int piIF (void) { return piIFAll(0); }
5788 static int piIFX (void) { return piIFAll(1); }
5791 ///////////////////////////////////////////////////////////////////////////////
5793 ///////////////////////////////////////////////////////////////////////////////
5795 what i did with MACRO is the brain-damaged cheating all the way.
5797 first, i will collect the MACRO body and remember it (removing it
5798 from the main source code, so second pass will not see it).
5800 second, when the macro is used, i will:
5801 * insert the whole macro body in place (label resolver will
5802 fix "..lbl" labels for us)
5803 * let the asm play with it
5805 this is not the best scheme, but it is fairly simple and it works.
5808 // will be called when parser encounters term starting with '=' or '*'
5809 // first term char will not be skipped
5810 // must return pointer to the first char after expression end
5811 static const char *getValueCB (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
5815 if (curmacro
== NULL
) fatal("'=' outside of macro");
5816 if (*expr
++ != '=') fatal("'=' expected!");
5818 expr
= strSkipSpaces(expr
);
5820 if (isAlphaDigit(*expr
) || *expr
== '_') {
5821 if (p
-name
> 250) fatal("id too long");
5829 expr
= strSkipSpaces(expr
);
5830 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
5831 if (strEquCI(name
, curmacro
->mac
->argnames
[f
])) {
5834 int l
= (int)strlen(curmacro
->argvals
[f
]);
5836 urasm_exprval_init(&v
);
5837 expr
= urasm_expr_ex(&v
, expr
, addr
, &donteval
, defined
, error
);
5838 if (*error
) return expr
;
5839 if (expr
== NULL
|| *expr
!= ']' || v
.str
!= NULL
) { *error
= UR_EXPRERR_FUNC
; return expr
; }
5841 if (v
.val
< 0) v
.val
+= l
;
5842 if (v
.val
< 0 || v
.val
>= l
) {
5845 res
->val
= (unsigned char)(curmacro
->argvals
[f
][v
.val
]);
5849 urasm_expr_ex(res
, curmacro
->argvals
[f
], addr
, &donteval
, defined
, error
);
5855 fatal("unknown macro variable: '%s'", name
);
5859 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
5860 // opr starts with '=' (invariant)
5861 static int expandCB (char *opr
, int oprlen
) {
5862 char name
[257], *p
= name
, *op
= opr
;
5864 if (curmacro
== NULL
) fatal("'=' outside of macro");
5865 if (*op
++ != '=') fatal("'=' expected!"); // just in case
5866 //fprintf(stderr, "expand: [%s]\n", opr);
5868 if (!isAlpha(op
[0])) return 0; // nothing to do
5870 // copy argument name
5872 if (isAlphaDigit(*op
) || *op
== '_') {
5873 if (p
-name
> 250) fatal("id too long");
5881 // expand argument? we only need to expand `=arg[n]`
5882 op
= strSkipSpaces(op
);
5883 if (op
[0] != '[') return 0;
5885 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
5886 if (strEquCI(name
, curmacro
->mac
->argnames
[f
])) {
5887 ur_assert(*op
== '[');
5888 // replace argument with character, or with literal value
5889 // used for `=regpair[0]` or `=regpair[]`
5892 const int l
= (int)strlen(curmacro
->argvals
[f
]);
5893 const int lleft
= (int)strlen(op
);
5894 char *tmp
= malloc(l
+lleft
+8);
5895 snprintf(tmp
, l
+lleft
+8, "%s%s", curmacro
->argvals
[f
], op
);
5896 if ((int)strlen(tmp
) > oprlen
) {
5905 int error
= 0, defined
= 1, donteval
= 0;
5907 urasm_exprval_init(&v
);
5908 op
= (char *)urasm_expr_ex(&v
, op
, pc
, &donteval
, &defined
, &error
);
5909 if (error
) return -1;
5910 // result should be a number
5911 if (op
== NULL
|| *op
!= ']' || v
.str
!= NULL
) return -1;
5913 // it is guaranteed to have more than one char in opr
5914 // so we can simply put char from argument value, and remove other expression chars
5915 const int l
= (int)strlen(curmacro
->argvals
[f
]);
5916 if (v
.val
< 0) v
.val
+= l
; // negative: indexing from the end
5917 if (v
.val
< 0 || v
.val
>= l
) fatal("index %d is out of bounds for macro argument '%s'", v
.val
, curmacro
->mac
->argnames
[f
]);
5919 opr
[0] = curmacro
->argvals
[f
][v
.val
];
5920 // remove other chars
5921 memmove(opr
+1, op
, strlen(op
)+1);
5927 fatal("unknown macro variable: '%s'", name
);
5931 // main macro expander
5932 static void processMacro (MacroDef
*mc
) {
5933 SourceLine
*oldcurline
= currSrcLine
;
5935 memset(&cm
, 0, sizeof(cm
));
5937 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
5938 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
5940 // parse macro arguments
5942 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) cm
.argvals
[f
] = NULL
;
5946 // do we have more arguments?
5947 if (!currLine
[0]) break;
5948 // check for argument name (this is purely cosmetic thing)
5949 // use like this: "mcall :argname=[value]" (value may be omited for default)
5950 if (currLine
[0] == ':' && isAlpha(currLine
[1])) {
5952 while (isAlphaDigit(currLine
[pos
]) || currLine
[pos
] == '_') ++pos
;
5954 const char svch
= currLine
[pos
];
5956 if (!strEquCI(currLine
+1, mc
->argnames
[currArg
])) {
5957 // out-of-order, find proper argument and rewind to it
5959 for (int c
= 0; c
< mc
->argc
; ++c
) {
5960 if (strEquCI(currLine
+1, mc
->argnames
[c
])) {
5965 if (currArg
< 0) fatal("macro '%s' has no argument named '%s'", mc
->name
, currLine
+1);
5967 currLine
[pos
] = svch
;
5968 // remove argument name
5969 memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
5972 if (currLine
[0] != '=') fatal("expected '=' after argument name");
5974 memmove(currLine
, currLine
+1, strlen(currLine
));
5978 if (currArg
>= mc
->argc
) fatal("too many arguments to macro '%s'", mc
->name
);
5979 // check for default value
5980 if (currLine
[0] == ',') {
5981 if (mc
->argdefaults
[currArg
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[currArg
]);
5982 cm
.argvals
[currArg
] = strdup(mc
->argdefaults
[currArg
]);
5983 memmove(currLine
, currLine
+1, strlen(currLine
));
5985 // skip argument (so we will know its length, and will be able to copy it)
5986 char *e
= skipMacroArg(currLine
);
5987 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
5988 const char ech
= *e
;
5990 if (ech
== ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc
->name
);
5991 if (ech
!= ',') fatal("invalid invocation of macro '%s'", mc
->name
);
5994 cm
.argvals
[currArg
] = strdup(currLine
);
5995 // strip trailing spaces
5996 strTrimRight(cm
.argvals
[currArg
]);
5999 memmove(currLine
, e
+1, strlen(e
));
6006 // check for line end
6008 if (currLine
[0]) fatal("invalid macro invocation");
6010 // setup default argument values
6011 for (int f
= 0; f
< mc
->argc
; ++f
) {
6012 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
6013 if (cm
.argvals
[f
]) continue;
6014 if (mc
->argdefaults
[f
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[f
]);
6015 cm
.argvals
[f
] = strdup(mc
->argdefaults
[f
]);
6016 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
6019 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
6021 // insert macro code into the source
6022 setCurSrcLine(mc
->lines
);
6025 while (currSrcLine
!= NULL
) {
6026 //fprintf(stderr, "*[%s] (%d)\n", currSrcLine->line, currSrcLine->lineNo);
6027 processCurrentLine();
6030 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) if (cm
.argvals
[f
]) free(cm
.argvals
[f
]);
6031 setCurSrcLine(oldcurline
);
6037 static int piMACRO (void) {
6040 char *argdefaults
[MAX_MACRO_ARGS
];
6041 char *argnames
[MAX_MACRO_ARGS
];
6042 SourceLine
*stline
, *eline
= NULL
;
6045 name
= strdup(getLabelArg(0));
6046 //fprintf(stderr, "[%s]\n", name);
6047 if (findMacro(name
) != NULL
) fatal("duplicate MACRO definition: '%s'", name
);
6048 if (currLine
[0] == ',') memmove(currLine
, currLine
+1, strlen(currLine
));
6051 while (currLine
[0]) {
6052 if (argc
>= MAX_MACRO_ARGS
) fatal("too many arguments in MACRO");
6053 if (!isAlpha(currLine
[0])) fatal("invalid MACRO definition");
6054 argnames
[argc
] = strdup(getLabelArg(0));
6055 if (currLine
[0] == '=') {
6057 char *e
= strchr(currLine
, ','), tch
;
6059 if (e
== NULL
) e
= currLine
+strlen(currLine
);
6062 argdefaults
[argc
] = strdup(currLine
+1);
6064 memmove(currLine
, e
, strlen(e
)+1);
6066 argdefaults
[argc
] = NULL
;
6069 if (currLine
[0] == ',') {
6070 memmove(currLine
, currLine
+1, strlen(currLine
));
6073 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
6077 // now find corresponding ENDM
6078 // note that we should skip nested DUPs
6079 stline
= currSrcLine
;
6080 nextSrcLine(); // skip ourself
6081 while (currSrcLine
) {
6084 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) break;
6085 // ok, we found something; what is it?
6088 fatal("no nested MACROs yet");
6091 eline
= currSrcLine
;
6094 nextSrcLine(); // skip ENDM
6098 if (!eline
) { setCurSrcLine(stline
); fatal("no ENDM"); }
6100 if ((mc
= calloc(1, sizeof(*mc
))) == NULL
) fatal("out of memory");
6103 for (int f
= 0; f
< argc
; ++f
) { mc
->argdefaults
[f
] = argdefaults
[f
]; mc
->argnames
[f
] = argnames
[f
]; }
6106 mc
->lines
= stline
->next
;
6107 stline
->next
= currSrcLine
;
6108 stline
->line
[0] = 0;
6112 setCurSrcLine(stline
);
6113 return PI_SKIP_LINE
;
6117 static int piENDM (void) {
6118 fatal("ENDM without MACRO");
6119 return PI_SKIP_LINE
;
6123 ///////////////////////////////////////////////////////////////////////////////
6125 static void piTapParseLoaderName (void) {
6128 if (!isStrArg()) fatal("loader name expected");
6129 char *fn
= getStrArg(&len
);
6130 if (len
> 10) fatal("loader name too long");
6131 memset(tapeLoaderName
, ' ', 10);
6132 for (int f
= 0; f
< len
; ++f
) tapeLoaderName
[f
] = fn
[f
];
6137 static int piMATHMODE (void) {
6138 char *name
= getOneLabelArg();
6139 if (strEquCI(name
, "OLD")) urasm_use_old_priorities
= 1;
6140 else if (strEquCI(name
, "NEW")) urasm_use_old_priorities
= 0;
6141 else fatal("invalid math mode; NEW or OLD expected");
6142 return PI_SKIP_LINE
;
6146 static int piCASTRATES (void) {
6147 char *name
= getOneLabelArg();
6148 if (strEquCI(name
, "YES")) urasm_allow_hex_castrates
= 1;
6149 else if (strEquCI(name
, "TAN")) urasm_allow_hex_castrates
= 1;
6150 else if (strEquCI(name
, "NO")) urasm_allow_hex_castrates
= 0;
6151 else if (strEquCI(name
, "ONA")) urasm_allow_hex_castrates
= 0;
6152 else fatal("yes/no value expected");
6153 return PI_SKIP_LINE
;
6157 static int piREFOPT (void) {
6158 char *name
= getOneLabelArg();
6159 if (strEquCI(name
, "ALLLABELS")) urasm_dump_all_labels
= 1;
6160 else if (strEquCI(name
, "ALL_LABELS")) urasm_dump_all_labels
= 1;
6161 else if (strEquCI(name
, "NOUPCASE")) urasm_dump_all_labels
= 0;
6162 else if (strEquCI(name
, "NO_UPCASE")) urasm_dump_all_labels
= 0;
6163 else if (strEquCI(name
, "DEFAULT")) urasm_dump_all_labels
= 1;
6164 else fatal("invalid refopt mode");
6165 return PI_SKIP_LINE
;
6169 static int piDEFFMT (void) {
6174 name
= getStrArg(&len
);
6176 name
= getLabelArg(1);
6178 if (optWTChanged
) return 1;
6180 optRunDMB
= optRunTape
= optRunSCL
= optRunDSK
= 0;
6183 if (strEquCI(name
, "SNA") || strEquCI(name
, "RUNSNA") || strEquCI(name
, "SNARUN")) {
6185 if (currLine
[0]) fatal("too many expressions");
6186 return PI_SKIP_LINE
;
6188 if (strEquCI(name
, "TAP") || strEquCI(name
, "TAPE")) {
6190 piTapParseLoaderName();
6191 return PI_SKIP_LINE
;
6193 if (strEquCI(name
, "RUNTAP") || strEquCI(name
, "RUNTAPE") || strEquCI(name
, "TAPERUN")) {
6196 piTapParseLoaderName();
6197 return PI_SKIP_LINE
;
6199 if (strEquCI(name
, "BIN") || strEquCI(name
, "RAW")) {
6201 if (currLine
[0]) fatal("too many expressions");
6202 return PI_SKIP_LINE
;
6204 if (strEquCI(name
, "DMB") || strEquCI(name
, "RUNDMB") || strEquCI(name
, "DMBRUN")) {
6205 optRunDMB
= (name
[3] != 0);
6207 if (currLine
[0]) fatal("too many expressions");
6208 return PI_SKIP_LINE
;
6210 if (strEquCI(name
, "NONE") || strEquCI(name
, "NOTHING")) {
6212 if (currLine
[0]) fatal("too many expressions");
6213 return PI_SKIP_LINE
;
6215 if (strEquCI(name
, "SCL") || strEquCI(name
, "RUNSCL") || strEquCI(name
, "SCLRUN")) {
6217 optRunSCL
= (name
[3] != 0 ? -1 : 0); /* no boot */
6218 piTapParseLoaderName();
6219 return PI_SKIP_LINE
;
6221 if (strEquCI(name
, "SCLMONO") || strEquCI(name
, "RUNSCLMONO") || strEquCI(name
, "SCLMONORUN")) {
6223 optRunSCL
= (name
[7] != 0 ? -1 : 0); /* no boot */
6224 piTapParseLoaderName();
6225 return PI_SKIP_LINE
;
6227 if (strEquCI(name
, "SCLBOOT") || strEquCI(name
, "BOOTSCL")) {
6229 optRunSCL
= 1; /* with boot */
6230 piTapParseLoaderName();
6231 return PI_SKIP_LINE
;
6233 if (strEquCI(name
, "SCLMONOBOOT") || strEquCI(name
, "BOOTSCLMONO")) {
6235 optRunSCL
= 1; /* with boot */
6236 piTapParseLoaderName();
6237 return PI_SKIP_LINE
;
6240 if (strEquCI(name
, "DSKBOOT") || strEquCI(name
, "BOOTDSK") ||
6241 strEquCI(name
, "DSK180BOOT") || strEquCI(name
, "BOOTDSK180"))
6244 optRunDSK
= 1; /* with boot */
6245 optDskType
= P3DSK_PCW_SS
; // +3DOS 180K disk
6246 piTapParseLoaderName();
6247 return PI_SKIP_LINE
;
6250 if (strEquCI(name
, "DSK720BOOT") || strEquCI(name
, "BOOTDSK720")) {
6252 optRunDSK
= 1; /* with boot */
6253 optDskType
= P3DSK_PCW_DS
; // +3DOS 720K disk
6254 piTapParseLoaderName();
6255 return PI_SKIP_LINE
;
6258 if (strEquCI(name
, "DSK") || strEquCI(name
, "DSK180")) {
6260 optRunDSK
= 0; /* without boot */
6261 optDskType
= P3DSK_PCW_SS
; // +3DOS 180K disk
6262 piTapParseLoaderName();
6263 return PI_SKIP_LINE
;
6266 if (strEquCI(name
, "DSK720")) {
6268 optRunDSK
= 0; /* without boot */
6269 optDskType
= P3DSK_PCW_DS
; // +3DOS 720K disk
6270 piTapParseLoaderName();
6271 return PI_SKIP_LINE
;
6274 fatal("invalid default output type: %s", name
);
6278 ///////////////////////////////////////////////////////////////////////////////
6281 static void processCurrentLine (void) {
6282 if (!currSrcLine
) return; // do nothing
6284 ur_assert(asmMode
!= AMODE_UFO
); // the thing that should not be
6287 char *str
, *ee
, name
[66];
6291 removeSpacesAndColons();
6292 // skip spaces and ':'
6293 if (!currLine
[0]) { nextSrcLine(); break; }
6294 // try to find and process command
6295 str
= currLine
; //while (*str && isSpace(*str)) ++str; // skip spaces
6297 for (ee
= str
; *ee
&& !isSpace(*ee
) && *ee
!= '"' && *ee
!= '\'' && *ee
!= ',' && *ee
!= ':'; ++ee
) {}
6298 // get command, if any
6299 if (ee
!= str
&& ee
-str
<= 64) {
6301 memset(name
, 0, sizeof(name
));
6302 memmove(name
, str
, ee
-str
);
6303 /* UrForth macro? */
6304 uint32_t fwmacro
= ufoIsMacro(name
);
6307 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
6308 memmove(currLine
, str
, strlen(str
)+1);
6309 ufoMacroRun(fwmacro
, currLine
, currSrcLine
->fname
, currSrcLine
->lineNo
);
6310 /* only one macro per line! */
6311 nextSrcLine(); // skip it
6314 /* known command? */
6315 op
= urFindOp(name
);
6318 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
6319 memmove(currLine
, str
, strlen(str
)+1);
6323 nextSrcLine(); // skip it
6330 if ((mc
= findMacro(name
)) != NULL
) {
6332 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
6333 memmove(currLine
, str
, strlen(str
)+1);
6335 /* only one macro per line! */
6339 // with reservation management
6341 len
= urasm_opasm(currLine
, pc
, disp
, &errpos
);
6342 if (len
< 0) fatalUrLib(len
);
6343 // check for reservation
6345 if (reserveHit(pc
, len
)) {
6347 if (pc
!= disp
) fatal("trying to use reserved areas with phased code");
6348 reserveReleaseUsed(pc
, len
);
6349 int nextaddr
= reservedFindNextSuitableFreeFrom(pc
, pc
+len
);
6350 if (nextaddr
< 0) fatal("codegen started in reserved area #%04X", pc
);
6351 // insert jump to the new area, and retry
6352 fprintf(stderr
, "...inserting jump from #%04X to #%04X\n", (unsigned)pc
, (unsigned)nextaddr
);
6353 // can we insert a JP? it is faster than JR
6354 if (memresv
[pc
] || memresv
[pc
+1]) fatal("internal reserve area manager error at #%04X", pc
);
6355 if (!memresv
[pc
+2]) {
6357 emitByte(0xc3U
); // JP
6358 emitWord((uint16_t)nextaddr
);
6361 int jrdest
= nextaddr
-(pc
+2U);
6362 if (jrdest
>= 128) fatal("internal reserve area manager error at #%04X (jr dist)", pc
);
6363 emitByte(0x18U
); // JR
6364 emitByte((uint8_t)jrdest
);
6366 disp
= pc
= (uint16_t)nextaddr
;
6367 goto again_from_reserve
;
6369 // did we compiled JP/JR?
6370 if (getByte(pc
) == 0xc3U
|| getByte(pc
) == 0x18U
) {
6371 // no room for jump?
6372 if (memresv
[(pc
+len
)&0xffffU
] || memresv
[(pc
+len
+1)&0xffffU
] || memresv
[(pc
+len
+2)&0xffffU
]) {
6373 if (pc
!= disp
) fatal("trying to use reserved areas with phased code");
6374 // find next free area
6376 while (next
>= 0 && next
< 65536) {
6377 next
= reserveFindFreeFrom(next
);
6378 if (next
< 0) break;
6379 // has room for JP there?
6380 if (!memresv
[next
] && !memresv
[(next
+1)&0xffffU
] && !memresv
[(next
+2)&0xffffU
]) break;
6381 // no room for JP, has room for JP?
6382 if (!memresv
[next
] && !memresv
[(next
+1)&0xffffU
]) {
6383 // check if jr can fit
6384 const int nj
= reservedFindNextJumpableFreeFrom(next
, next
+2);
6387 next
= reserveFindReservedFrom(next
);
6389 if (next
>= 0) disp
= pc
= (next
-len
)&0xffffU
; // so "+len" will make PC right
6392 // check if we still have enough room for a possible jp/jr
6393 if (memresv
[(pc
+len
)&0xffffU
] || memresv
[(pc
+len
+1)&0xffffU
]) goto insert_jump
;
6394 // we have at least a room for JR, check if we have a room for JP
6395 if (!memresv
[(pc
+len
+2)&0xffffU
]) {
6396 // no room for JP, check if jr will be enough
6397 int next
= reservedFindNextSuitableFreeFrom(pc
, pc
+len
+2);
6398 if (next
>= 0 && next
-(pc
+len
+2u) >= 128) goto insert_jump
;
6405 if (len
>= 0 && errpos
) {
6406 memmove(currLine
, errpos
+1, strlen(errpos
));
6408 nextSrcLine(); // skip it
6415 ///////////////////////////////////////////////////////////////////////////////
6416 // start inline UrForth code
6418 static int piSTARTFORTH (void) {
6420 asmMode
= AMODE_UFO
;
6422 asmMode
= AMODE_NORMAL
;
6423 return PI_SKIP_LINE
;
6427 ///////////////////////////////////////////////////////////////////////////////
6428 // setup instructions
6430 static void registerInstructions (void) {
6431 urAddOpAndDollar("DISPLAY", piDISPLAY
);
6432 urAddOpAndDollar("DISPLAY0", piDISPLAY0
);
6433 urAddOpAndDollar("DISPLAYA", piDISPLAYA
);
6434 urAddOpAndDollar("DISPHEX", piDISPHEX
);
6435 urAddOpAndDollar("DISPHEX0", piDISPHEX0
);
6436 urAddOpAndDollar("DISPHEXA", piDISPHEXA
);
6438 urAddOpAndDollar("DEFFMT", piDEFFMT
);
6439 urAddOp("$DEFFMT", piDEFFMT
);
6440 urAddOp("$MODEL", piMODEL
);
6441 urAddOp("$SAVECODE", piSAVECODE
);
6443 urAddOp("MACRO", piMACRO
);
6444 urAddOp("ENDM", piENDM
);
6446 urAddOp("ORG", piORG
);
6447 urAddOp("DISP", piDISP
);
6448 urAddOp("ENDDISP", piENDDISP
);
6449 urAddOp("PHASE", piDISP
);
6450 urAddOp("DEPHASE", piENDDISP
);
6451 urAddOp("UNPHASE", piENDDISP
);
6452 urAddOp("ALIGN", piALIGN
);
6453 urAddOp("DISPALIGN", piDISPALIGN
);
6454 urAddOp("PHASEALIGN", piDISPALIGN
);
6455 urAddOp("ENT", piENT
);
6456 urAddOp("CLR", piCLR
);
6457 urAddOp("RESERVE", piRESERVE
);
6459 urAddOpAndDollar("INCLUDE", piINCLUDE
);
6460 urAddOpAndDollar("INCBIN", piINCBIN
);
6461 urAddOpAndDollar("DATABIN", piDATABIN
);
6462 urAddOpAndDollar("CODEBIN", piCODEBIN
);
6464 urAddOp("MODULE", piMODULE
);
6465 urAddOp("ENDMODULE", piENDMODULE
);
6467 urAddOp("DUP", piDUP
);
6468 urAddOp("EDUP", piEDUP
);
6469 urAddOp("REPT", piREPT
);
6470 urAddOp("ENDR", piENDR
);
6472 urAddOp("STRUCT", piSTRUCT
);
6473 urAddOp("ENDS", piENDS
);
6475 urAddOp("END", piEND_PASMO
);
6477 urAddOpAndDollar("IF", piIF
);
6478 urAddOpAndDollar("IFX", piIFX
);
6479 urAddOpAndDollar("ELSE", piELSE
);
6480 urAddOpAndDollar("ELSIF", piELSIF
);
6481 urAddOpAndDollar("ELSIFX", piELSIFX
);
6482 urAddOpAndDollar("ELSEIF", piELSIF
);
6483 urAddOpAndDollar("ELSEIFX", piELSIFX
);
6484 urAddOpAndDollar("ENDIF", piENDIF
);
6486 urAddOp("DEFINCR", piDEFINCR
);
6487 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
6488 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
6489 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
6490 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
6491 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
6492 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
6493 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
6494 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
6496 urAddOp("$ERROR", piERROR
);
6497 urAddOp("$WARNING", piWARNING
);
6499 urAddOp("$PRINTF", piPRINTF
);
6500 urAddOp("$PRINTF0", piPRINTF0
);
6501 urAddOp("$PRINTFA", piPRINTFA
);
6503 urAddOp("$MATHMODE", piMATHMODE
);
6504 urAddOp("$REFOPT", piREFOPT
);
6505 urAddOp("$CASTRATES", piCASTRATES
);
6507 urAddOp("$START_FORTH", piSTARTFORTH
);
6511 ///////////////////////////////////////////////////////////////////////////////
6512 // !0: invalid label
6514 static inline void fnSkipSpaces (const char *expr) {
6515 while (*expr && isSpace(*expr)) ++expr;
6521 UR_FORCE_INLINE
char fnNextChar (const char *expr
) {
6522 while (*expr
&& isSpace(*expr
)) ++expr
;
6527 #define FN_SKIP_BLANKS do { \
6528 while (*expr && isSpace(*expr)) ++expr; \
6531 #define FN_CHECK_END do { \
6532 while (*expr && isSpace(*expr)) ++expr; \
6533 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
6538 #define FN_CHECK_COMMA do { \
6539 while (*expr && isSpace(*expr)) ++expr; \
6540 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
6545 static const char *readLabelName (char *buf
, const char *expr
) {
6548 while (*expr
&& isSpace(*expr
)) ++expr
;
6552 if (pos
>= 128) return NULL
;
6553 if (ch
== ')') { --expr
; break; }
6555 if (isAlphaDigit(ch
) || ch
== '$' || ch
== '.' || ch
== '_' || ch
== '@') {
6561 if (pos
< 1) return NULL
;
6563 if (!urasm_is_valid_name(buf
)) return NULL
;
6568 static const char *fnDefKn (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
, int qtype
) {
6571 if ((expr
= readLabelName(lbl
, expr
)) == NULL
) { *error
= UR_EXPRERR_LABEL
; return NULL
; }
6573 if (!donteval
) res
->val
= (isLabelDefinedOrKnown(lbl
, addr
, qtype
) != 0);
6577 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
); }
6578 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
); }
6581 static const char *fnAligned256 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6582 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6584 if (!donteval
) res
->val
= (res
->val
%256 ? 0 : 1);
6589 static const char *fnSameSeg (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6590 urasm_exprval_t v0
, v1
;
6592 urasm_exprval_init(&v0
);
6593 urasm_exprval_init(&v1
);
6594 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
6595 if (*error
) return expr
;
6597 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
6598 if (*error
) return expr
;
6600 if (!donteval
) res
->val
= (v0
.val
/256 == v1
.val
/256);
6605 static const char *fnAlign (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6606 urasm_exprval_t v0
, v1
;
6607 urasm_exprval_init(&v0
);
6608 urasm_exprval_init(&v1
);
6609 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
6610 if (*error
) return expr
;
6612 if (fnNextChar(expr
) == ',') {
6614 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
6615 if (*error
) return expr
;
6621 if (v1
.val
< 1) { *error
= UR_EXPRERR_FUNC
; return NULL
; }
6622 res
->val
= (v0
.val
+v1
.val
-1)/v1
.val
*v1
.val
;
6628 static const char *fnLow (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6629 const char *ee
= expr
;
6630 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6633 if (res
->fixuptype
== UR_FIXUP_HIBYTE
) {
6634 *error
= UR_EXPRERR_FUNC
; return ee
;
6636 res
->fixuptype
= UR_FIXUP_LOBYTE
;
6643 static const char *fnHigh (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6644 const char *ee
= expr
;
6645 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6648 if (res
->fixuptype
== UR_FIXUP_LOBYTE
) {
6649 *error
= UR_EXPRERR_FUNC
; return ee
;
6651 res
->fixuptype
= UR_FIXUP_HIBYTE
;
6652 res
->val
= (res
->val
>>8)&0xff;
6658 static const char *fnAbs (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6659 const char *ee
= expr
;
6660 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6663 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
6664 *error
= UR_EXPRERR_FUNC
; return ee
;
6666 res
->val
= abs(res
->val
);
6672 static const char *fnBSwap (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6673 const char *ee
= expr
;
6674 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6677 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
6678 *error
= UR_EXPRERR_FUNC
; return ee
;
6680 res
->val
= ((res
->val
>>8)&0xff)|((res
->val
&0xff)<<8);
6686 static const char *fnScrAddr8xn (int nmul
, urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6687 int32_t scrbase
= 0x4000;
6689 const char *ee
= expr
;
6690 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
6691 // first argument is `x`
6692 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6694 // second argument is `y`
6696 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6698 // optional third arg is screen base
6700 if (expr
[0] == ',') {
6702 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6703 scrbase
= res
->val
&0xffff;
6707 //urasm_exprval_clear(res);
6708 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
6709 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
6710 if (y
< 0 || y
> 191) fatal("invalid y coordinate: %d", y
/nmul
);
6716 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
6722 static const char *fnScrAddr8x1 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6723 return fnScrAddr8xn(1, res
, expr
, addr
, donteval
, defined
, error
);
6726 static const char *fnScrAddr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6727 return fnScrAddr8xn(8, res
, expr
, addr
, donteval
, defined
, error
);
6731 static const char *fnScrAttr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6732 int32_t scrbase
= 0x4000;
6734 const char *ee
= expr
;
6735 // first argument is `x`
6736 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6738 // second argument is `y`
6740 urasm_exprval_clear(res
);
6741 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6743 // optional third arg is screen base
6745 if (expr
[0] == ',') {
6747 urasm_exprval_clear(res
);
6748 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6749 scrbase
= res
->val
&0xffff;
6751 urasm_exprval_clear(res
);
6754 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
6755 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
6756 if (y
< 0 || y
> 23) fatal("invalid y coordinate: %d", y
);
6757 res
->val
= scrbase
+6144+y
*32+x
;
6763 static const char *fnMArgToStr (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6764 const char *ee
= expr
;
6765 // argument is macro argument name
6766 expr
= strSkipSpacesConst(expr
);
6767 if (expr
[0] != '=') { *error
= UR_EXPRERR_FUNC
; return ee
; }
6769 if (!isAlpha(expr
[0]) && expr
[0] != '_') { *error
= UR_EXPRERR_FUNC
; return ee
; }
6770 const char *nend
= expr
+1;
6771 while (isAlphaDigit(*nend
) || *nend
== '_') ++nend
;
6772 if (nend
-expr
> 127) { *error
= UR_EXPRERR_FUNC
; return ee
; }
6774 memset(name
, 0, sizeof(name
));
6775 memcpy(name
, expr
, nend
-expr
);
6780 expr
= strSkipSpacesConst(expr
);
6781 if (expr
[0] == '[') {
6782 expr
= strSkipSpacesConst(expr
+1);
6783 if (expr
[0] != ']') {
6786 if (expr
[0] == '+') ++expr
;
6787 else if (expr
[0] == '-') { doneg
= 1; ++expr
; }
6789 while (isDigit(expr
[0])) {
6790 index
= index
*10+(expr
[0]-'0');
6791 if (index
>= 0x1fffffff) fatal("`margtostr` index too high");
6794 expr
= strSkipSpacesConst(expr
);
6795 if (doneg
) index
= -index
;
6797 if (expr
[0] != ']') fatal("`margtostr` invalid index syntax");
6802 if (curmacro
== NULL
) fatal("`margtostr` outside of macro");
6804 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
6805 if (strEquCI(name
, curmacro
->mac
->argnames
[f
])) {
6806 // found argument, convert it to string
6808 res
->str
= realloc(res
->str
, strlen(curmacro
->argvals
[f
])+1);
6809 strcpy(res
->str
, curmacro
->argvals
[f
]);
6811 const size_t slen
= strlen(curmacro
->argvals
[f
]);
6814 if (index
> slen
) fatal("index out of string");
6815 index
= (int)slen
-index
;
6817 if (index
>= slen
) fatal("index out of string");
6818 res
->str
= realloc(res
->str
, 2);
6819 res
->str
[0] = curmacro
->argvals
[f
][index
];
6823 for (char *s
= res
->str
; *s
; ++s
) *s
= toLower(*s
);
6824 //fprintf(stderr, "<%s>\n", res->str);
6827 res
->val
= ((unsigned char)res
->str
[0]);
6828 res
->val
|= ((unsigned char)res
->str
[1])<<8;
6830 res
->val
= (unsigned char)res
->str
[0];
6838 fatal("unknown macro argument '%s'", name
);
6844 static const char *fnStrLen (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6845 expr
= strSkipSpacesConst(expr
);
6848 case '"': case '\'': /* string literal? */
6850 char *a
= (char *)expr
+1;
6851 (void)parseStr(&a
, expr
[0], &stlen
);
6852 expr
= (const char *)a
;
6855 default: /* expression */
6858 urasm_exprval_init(&r
);
6859 expr
= urasm_expr_ex(&r
, expr
, disp
, &donteval
, defined
, error
);
6860 if (*error
) fatalUrLib(*error
);
6861 if (!r
.str
) fatal("string expected for `strlen()`");
6862 stlen
= (int)strlen(r
.str
);
6863 urasm_exprval_clear(&r
);
6869 urasm_exprval_setint(res
, stlen
);
6875 static void registerFunctions (void) {
6876 urasm_expr_register_func("defined", fnDefined
);
6877 urasm_expr_register_func("known", fnKnown
);
6878 urasm_expr_register_func("aligned256", fnAligned256
);
6879 urasm_expr_register_func("align", fnAlign
);
6880 urasm_expr_register_func("sameseg", fnSameSeg
);
6881 urasm_expr_register_func("low", fnLow
);
6882 urasm_expr_register_func("high", fnHigh
);
6883 urasm_expr_register_func("abs", fnAbs
);
6884 urasm_expr_register_func("bswap", fnBSwap
);
6886 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8
);
6887 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1
);
6888 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8
);
6890 urasm_expr_register_func("marg2str", fnMArgToStr
);
6891 urasm_expr_register_func("strlen", fnStrLen
);
6895 ///////////////////////////////////////////////////////////////////////////////
6896 // preparing another pass
6898 static void initPass (void) {
6899 asmMode
= AMODE_NORMAL
;
6900 currSrcLine
= asmText
;
6914 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
6915 lastSeenGlobalLabel
= NULL
;
6916 //lastSeenGlobalLabel = strdup(" [MAIN] ");
6919 urasm_use_old_priorities
= 0;
6920 strcpy(lastFindLabelName
, "");
6921 strcpy(currSeenLabel
, "");
6922 urResetTempLabels();
6926 static int postPass (void) {
6927 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
6928 lastSeenGlobalLabel
= NULL
;
6929 if (checkLabels()) return -1;
6930 if (ifCount
!= 0) fatal("unbalanced IFs");
6935 ///////////////////////////////////////////////////////////////////////////////
6936 static int labelCmp (const void *aa
, const void *bb
) {
6937 if (aa
== bb
) return 0;
6938 const UrLabelInfo
*a
= *(const UrLabelInfo
**)aa
;
6939 const UrLabelInfo
*b
= *(const UrLabelInfo
**)bb
;
6941 a
->value
< b
->value
? -1 :
6942 a
->value
> b
->value
? 1 :
6947 static __attribute__((unused
)) int strStartsWith (const char *s
, const char *pat
) {
6948 if (!pat
|| !pat
[0]) return 0;
6949 if (!s
|| !s
[0]) return 0;
6950 while (*s
&& *pat
) {
6951 if (*s
!= *pat
) return 0;
6959 static int isGoodLabelForRef (const UrLabelInfo
*ll
) {
6963 // do not output `=` labels
6964 if (ll->type == LBL_TYPE_ASS) return 0;
6965 // do not output struct labels
6966 if (ll->type == LBL_TYPE_STOFFS) return 0;
6969 if (urasm_dump_all_labels
) {
6970 if (strStartsWith(ll
->name
, "UFO_") || strStartsWith(ll
->name
, "USE_")) return 0;
6974 for (const char *s
= ll
->name
; *s
; ++s
) {
6976 if (ch
>= 'A' && ch
<= 'Z') continue;
6977 if (ch
>= '0' && ch
<= '9') continue;
6978 if (ch
== '_') continue;
6986 static void writeLabelsFile (const char *fname
) {
6987 if (!fname
|| !fname
[0]) return;
6991 for (const UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) {
6992 if (isGoodLabelForRef(ll
)) ++lcount
;
6995 UrLabelInfo
**larr
= NULL
;
6997 // create labels array
6998 larr
= (UrLabelInfo
**)malloc(sizeof(UrLabelInfo
*)*lcount
);
7000 for (UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) {
7001 if (isGoodLabelForRef(ll
)) larr
[lcount
++] = ll
;
7004 qsort(larr
, lcount
, sizeof(UrLabelInfo
*), &labelCmp
);
7008 FILE *fo
= fopen(fname
, "w");
7010 for (int f
= 0; f
< lcount
; ++f
) {
7011 UrLabelInfo
*ll
= larr
[f
];
7012 //if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
7013 if (!ll
->name
) continue; // just in case
7014 if (ll
->name
[0] == '{') continue; // we don't have these, but it is still reserved
7015 if (ll
->value
< 0) {
7016 fprintf(fo
, "%d %s", ll
->value
, ll
->name
);
7018 fprintf(fo
, "#%04X %s", (unsigned)ll
->value
, ll
->name
);
7022 case LBL_TYPE_VERY_SPECIAL
: fprintf(fo
, "WTF"); break;
7023 case LBL_TYPE_UNKNOWN
: fprintf(fo
, "UNKNOWN"); break;
7024 case LBL_TYPE_ASS
: fprintf(fo
, "assign"); break;
7025 case LBL_TYPE_EQU
: fprintf(fo
, "equ"); break;
7026 case LBL_TYPE_CODE
: fprintf(fo
, "code"); break;
7027 case LBL_TYPE_STOFS
: fprintf(fo
, "stofs"); break;
7028 case LBL_TYPE_DATA
: fprintf(fo
, "data"); break;
7029 default: fprintf(fo
, "WTF"); break;
7040 ///////////////////////////////////////////////////////////////////////////////
7043 static struct option longOpts
[] = {
7044 {"org", required_argument
, NULL
, 600},
7045 {"define", required_argument
, NULL
, 601},
7046 {"defzero", required_argument
, NULL
, 602},
7047 {"outdir", required_argument
, NULL
, 660},
7048 {"reffile", optional_argument
, NULL
, 669},
7049 {"outfile", required_argument
, NULL
, 665},
7050 {"castrates", optional_argument
, NULL
, 656},
7052 {"sna", 0, NULL
, 's'},
7053 {"sna128", 0, NULL
, 'S'},
7054 {"tap", 0, NULL
, 't'},
7055 {"autotap", 0, NULL
, 'T'},
7056 {"raw", 0, NULL
, 'r'},
7057 {"autodmb", 0, NULL
, 'B'},
7058 {"dmb", 0, NULL
, 'b'},
7059 {"none", 0, NULL
, 'n'},
7060 {"help", 0, NULL
, 'h'},
7061 {"hob", 0, NULL
, 'H'},
7062 {"scl", 0, NULL
, 'l'},
7063 {"autoscl", 0, NULL
, 'L'},
7064 {"monoscl", 0, NULL
, 'm'},
7065 {"autosclmono", 0, NULL
, 'M'},
7066 {"dsk", 0, NULL
, 690},
7067 {"dsk720", 0, NULL
, 691},
7068 {"adsk", 0, NULL
, 692},
7069 {"adsk720", 0, NULL
, 693},
7070 {"fixups", optional_argument
, NULL
, 'F'},
7076 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
7079 static void usage (const char *pname
) {
7081 "usage: %s [options] infile\n"
7082 "default infiles:", pname
);
7083 for (int f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
7086 " -s --sna write 48K .SNA file with autostart\n"
7087 " -S --sna128 write 148K .SNA file with autostart\n"
7088 " -t --tap write .tap file\n"
7089 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
7090 " -r --raw write raw file(s)\n"
7091 " -b --dmb write DMB file\n"
7092 " -B --autodmb write DMB file with autostart\n"
7093 " -H --hob write HoBeta code file(s)\n"
7094 " -l --scl write SCL TR-DOS archive\n"
7095 " -L --autoscl write autostarting SCL TR-DOS archive\n"
7096 " -m --monoscl write SCL with monoloader\n"
7097 /*" -M --monosclauto write autorun SCL with monoloader\n"*/
7098 " --dsk write +3DOS 180KB disk\n"
7099 " --dsk720 write +3DOS 720KB disk\n"
7100 " --adsk write +3DOS 180KB disk with autostart\n"
7101 " --adsk720 write +3DOS 720KB disk with autostart\n"
7102 " -n --none write nothing\n"
7103 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
7104 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
7105 " text: .txt file\n"
7106 " asm: address lists with counters\n"
7107 " asmdiff: 2nd and other addresses are relative, with counters\n"
7108 " asmz: address lists, with 0 end markers\n"
7109 " -h --help this help\n"
7111 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
7112 " --org xxx set ORG\n"
7113 " --define val perform 'val EQU 1'\n"
7114 " --defzero val perform 'val EQU 0'\n"
7115 " --outdir dir output dir for resulting files (default: current)\n"
7116 " --outfile fname output file name for image formats (default: main asm file name)\n"
7117 " --castrates allow '&abc' hex numbers (and disable '&b' prefix)\n"
7122 ///////////////////////////////////////////////////////////////////////////////
7123 //#include "urforth.c"
7125 __attribute__((noreturn
))
7126 void ufoFatalError (void) {
7127 longjmp(errJP
, 666);
7130 uint32_t ufoZXGetU8 (uint32_t addr
) {
7132 return getByte(addr
);
7135 void ufoZXPutU8 (uint32_t addr
, uint32_t v
) {
7141 uint32_t ufoZXGetU16 (uint32_t addr
) {
7143 return getWord(addr
);
7146 void ufoZXPutU16 (uint32_t addr
, uint32_t v
) {
7152 void ufoZXEmitU8 (uint32_t v
) {
7153 //if (!zxlblLastByte) ufoFatal("label 'latest_byte' not found");
7157 void ufoZXEmitU16 (uint32_t v
) {
7158 //if (!zxlblLastByte) ufoFatal("label 'latest_byte' not found");
7159 emitWord(v
&0xffffU
);
7162 int ufoZXGetReserved (uint32_t addr
) {
7163 return memresv
[addr
&0xffffU
];
7166 void ufoZXSetReserved (uint32_t addr
, int resvflag
) {
7167 memresv
[addr
&0xffffU
] = resvflag
;
7170 int ufoZXGetPass (void) {
7174 uint32_t ufoZXGetOrg (void) {
7178 void ufoZXSetOrg (uint32_t addr
) {
7179 if (addr
> 0xffff) ufoFatal("invalid ORG address: %u", addr
);
7180 pc
= disp
= (uint16_t)addr
;
7182 wasOrg
= 1; // so next `ORG` will not reset it
7183 ent
= (uint16_t)addr
;
7187 uint32_t ufoZXGetDisp (void) {
7191 void ufoZXSetDisp (uint32_t addr
) {
7192 if (addr
> 0xffff) ufoFatal("invalid DISP address: %u", addr
);
7193 disp
= (uint16_t)addr
;
7196 uint32_t ufoZXGetEnt (void) {
7197 return (wasOrg
? (uint32_t)ent
: ~0u);
7200 void ufoZXSetEnt (uint32_t addr
) {
7201 if (addr
> 0xffff) ufoFatal("invalid ENT address: %u", addr
);
7202 wasOrg
= 1; // so next `ORG` will not reset it
7203 ent
= (uint16_t)addr
;
7206 static int ufoZXLType2UFO (int type
) {
7208 case LBL_TYPE_VERY_SPECIAL
: return UFO_ZX_LABEL_UNDEFINED
;
7209 case LBL_TYPE_UNKNOWN
: return UFO_ZX_LABEL_UNKNOWN
;
7210 case LBL_TYPE_ASS
: return UFO_ZX_LABEL_VAR
;
7211 case LBL_TYPE_EQU
: return UFO_ZX_LABEL_EQU
;
7212 case LBL_TYPE_CODE
: return UFO_ZX_LABEL_CODE
;
7213 case LBL_TYPE_DATA
: return UFO_ZX_LABEL_DATA
;
7214 case LBL_TYPE_STOFS
: return UFO_ZX_LABEL_STOFS
;
7216 ufoFatal("WTF?! unknown label type %d", type
);
7219 int ufoZXGetLabelType (const char *name
) {
7220 if (name
== NULL
|| name
[0] == 0) return UFO_ZX_LABEL_UNDEFINED
;
7221 UrLabelInfo
*lbl
= findAddLabel(name
);
7222 if (lbl
== NULL
) return UFO_ZX_LABEL_UNDEFINED
;
7223 return ufoZXLType2UFO(lbl
->type
);
7226 int ufoZXGetLabelValue (const char *name
) {
7227 if (name
== NULL
|| name
[0] == 0) return 0;
7228 UrLabelInfo
*lbl
= findAddLabel(name
);
7229 return (lbl
? lbl
->value
: 0);
7232 // this also creates labels
7233 void ufoZXSetLabelValue (const char *name
, int type
, int value
) {
7234 if (name
== NULL
|| name
[0] == 0) ufoFatal("empty label name");
7236 // convert label type
7238 case UFO_ZX_LABEL_UNDEFINED
: ufoFatal("cannot set label with undefined type");
7239 case UFO_ZX_LABEL_UNKNOWN
: ufoFatal("cannot set label with unknown type");
7240 case UFO_ZX_LABEL_VAR
: type
= LBL_TYPE_ASS
; break;
7241 case UFO_ZX_LABEL_EQU
: type
= LBL_TYPE_EQU
; break;
7242 case UFO_ZX_LABEL_CODE
: type
= LBL_TYPE_CODE
; break;
7243 case UFO_ZX_LABEL_DATA
: type
= LBL_TYPE_DATA
; break;
7244 case UFO_ZX_LABEL_STOFS
: type
= LBL_TYPE_STOFS
; break;
7245 default: ufoFatal("invalid label type %d", type
);
7248 UrLabelInfo
*lbl
= findAddLabel(name
);
7250 if (lbl
->type
!= LBL_TYPE_UNKNOWN
&& lbl
->type
!= type
) {
7251 ufoFatal("invalid label '%s' type", name
);
7253 if (type
!= LBL_TYPE_ASS
) {
7254 if (lbl
->type
>= 0 && lbl
->value
!= value
) ufoFatal("invalid label '%s' value", name
);
7258 if (lbl
->type
== LBL_TYPE_UNKNOWN
) lbl
->type
= type
;
7261 #define UFO_ZX_MAX_ITERS (16)
7264 UFOZXLabelIterator id
;
7268 static UZXLabelIter ufoZXLblIters
[UFO_ZX_MAX_ITERS
];
7269 static uint32_t ufoZXLblIdLast
;
7271 static int UFOZXFindIter (UFOZXLabelIterator it
) {
7273 while (f
!= UFO_ZX_MAX_ITERS
&& ufoZXLblIters
[f
].id
!= it
) f
+= 1;
7274 if (f
== UFO_ZX_MAX_ITERS
) f
= -1;
7278 static UrLabelInfo
*UFOZXNormLabel (UrLabelInfo
*c
) {
7279 while (c
!= NULL
&& (c
->type
== LBL_TYPE_VERY_SPECIAL
|| c
->type
== LBL_TYPE_UNKNOWN
)) {
7285 static void UFOZXLabelRemoved (UrLabelInfo
*lbl
) {
7286 ur_assert(lbl
!= NULL
);
7287 UrLabelInfo
*c
= UFOZXNormLabel(lbl
->next
);
7288 for (int f
= 0; f
< UFO_ZX_MAX_ITERS
; f
+= 1) {
7289 if (ufoZXLblIters
[f
].lbl
== lbl
) {
7290 ufoZXLblIters
[f
].lbl
= c
;
7295 UFOZXLabelIterator
ufoZXNewLabelIter (void) {
7296 UFOZXLabelIterator res
= 0;
7297 UrLabelInfo
*c
= UFOZXNormLabel(labels
);
7300 while (res
== 0 && f
!= UFO_ZX_MAX_ITERS
) {
7301 if (ufoZXLblIters
[f
].id
== 0) {
7302 ufoZXLblIdLast
+= 1;
7303 if (ufoZXLblIdLast
== 0) ufoZXLblIdLast
= 1;
7304 ufoZXLblIters
[f
].id
= ufoZXLblIdLast
;
7305 ufoZXLblIters
[f
].lbl
= c
;
7306 res
= ufoZXLblIdLast
;
7313 int ufoZXLabelIterNext (UFOZXLabelIterator it
) {
7315 int f
= UFOZXFindIter(it
);
7317 UrLabelInfo
*c
= ufoZXLblIters
[f
].lbl
;
7318 if (c
!= NULL
) c
= UFOZXNormLabel(c
->next
);
7320 ufoZXLblIters
[f
].lbl
= c
;
7323 ufoZXLblIters
[f
].id
= 0;
7324 ufoZXLblIters
[f
].lbl
= NULL
;
7330 void ufoZXLabelIterClose (UFOZXLabelIterator it
) {
7331 int f
= UFOZXFindIter(it
);
7333 ufoZXLblIters
[f
].id
= 0;
7334 ufoZXLblIters
[f
].lbl
= NULL
;
7338 const char *ufoZXLabelIterGetName (UFOZXLabelIterator it
) {
7339 int f
= UFOZXFindIter(it
);
7341 UrLabelInfo
*c
= ufoZXLblIters
[f
].lbl
;
7342 if (c
!= NULL
) return c
->name
;
7347 int ufoZXIterGetValue (UFOZXLabelIterator it
) {
7348 int f
= UFOZXFindIter(it
);
7350 UrLabelInfo
*c
= ufoZXLblIters
[f
].lbl
;
7351 if (c
!= NULL
) return c
->value
;
7356 int ufoZXIterGetType (UFOZXLabelIterator it
) {
7357 int f
= UFOZXFindIter(it
);
7359 UrLabelInfo
*c
= ufoZXLblIters
[f
].lbl
;
7360 if (c
!= NULL
) ufoZXLType2UFO(c
->type
);
7362 return UFO_ZX_LABEL_UNDEFINED
;
7365 const char *ufoGetSrcLine (const char **fname
, int *lnum
) {
7366 SourceLine
*sl
= nextUFOSrcLine();
7367 if (sl
== NULL
) return NULL
;
7370 return (sl
->line
!= NULL
? sl
->line
: "");
7373 char *ufoCreateIncludeName (const char *fname
, int assystem
, const char *lastIncPath
) {
7374 assystem
= (assystem
? -666 : -669);
7375 if (lastIncPath
== NULL
) lastIncPath
= ufoIncludeDir
;
7376 return createIncludeName(fname
, assystem
, NULL
, lastIncPath
);
7379 void ufoZXPostInit (void) {
7380 for (int f
= 0; f
< UFO_ZX_MAX_ITERS
; f
+= 1) {
7381 ufoZXLblIters
[f
].id
= 0;
7382 ufoZXLblIters
[f
].lbl
= NULL
;
7388 ///////////////////////////////////////////////////////////////////////////////
7391 int main (int argc
, char *argv
[]) {
7393 const char *pname
= argv
[0];
7394 char *inFile
= NULL
;
7395 char **defines
= NULL
, **values
= NULL
;
7402 initUFEInclideDir();
7404 urasm_getbyte
= getByte
;
7405 urasm_putbyte
= putByte
;
7406 urasm_label_by_name
= findLabelCB
;
7407 urasm_getval
= getValueCB
;
7408 urasm_expand
= expandCB
;
7409 urasm_fixup_operand
= fixupOperandCB
;
7411 //strcpy(tapeLoaderName, "cargador ");
7412 tapeLoaderName
[0] = 0;
7414 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
7415 while ((c
= getopt_long(argc
, argv
, "sStTrBbnhHFLlMm", longOpts
, NULL
)) >= 0) {
7418 case 'S': /*optRunSNA = 1;*/ optSNA48
= 0; optWriteType
= 's'; optWTChanged
= 1; break;
7419 case 's': /*optRunSNA = 0;*/ optSNA48
= 1; optWriteType
= 's'; optWTChanged
= 1; break;
7420 case 'T': optRunTape
= 1; optWriteType
= 't'; optWTChanged
= 1; break;
7421 case 't': optRunTape
= 0; optWriteType
= 't'; optWTChanged
= 1; break;
7422 case 'L': optRunSCL
= 1; optWriteType
= 'S'; optWTChanged
= 1; break; /* with boot */
7423 case 'l': optRunSCL
= -1; optWriteType
= 'S'; optWTChanged
= 1; break; /* no boot */
7424 case 'm': optRunSCL
= -1; optWriteType
= 'M'; optWTChanged
= 1; break; /* no boot */
7425 case 'M': optRunSCL
= 1; optWriteType
= 'M'; optWTChanged
= 1; break; /* with boot */
7426 case 'r': case 'n': optWriteType
= c
; optWTChanged
= 1; break;
7427 case 'b': optRunTape
= 0; optRunDMB
= 0; optWriteType
= 'd'; optWTChanged
= 1; break;
7428 case 'B': optRunTape
= 0; optRunDMB
= 1; optWriteType
= 'd'; optWTChanged
= 1; break;
7429 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
7430 case 'H': optWriteType
= 'H'; optWTChanged
= 1; break;
7433 if (optarg
!= NULL
) {
7434 if (strcmp(optarg
, "asm") == 0 || strcmp(optarg
, "zas") == 0) {
7436 } else if (strcmp(optarg
, "asmdiff") == 0 || strcmp(optarg
, "zasdiff") == 0) {
7438 } else if (strcmp(optarg
, "asmz") == 0 || strcmp(optarg
, "zasz") == 0) {
7440 } else if (strcmp(optarg
, "text") == 0) {
7443 fprintf(stderr
, "FATAL: invalid fixup type: '%s'\n", optarg
);
7450 //fprintf(stderr, "ORG: %d\n", c);
7451 if (c
< 0 || c
> 65535) {
7452 fprintf(stderr
, "FATAL: invalid ORG: %d\n", c
);
7455 start_pc
= start_disp
= start_ent
= c
;
7458 //fprintf(stderr, "define: [%s]\n", optarg);
7459 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
7460 values
= realloc(values
, sizeof(char *)*(defcount
+1));
7461 defines
[defcount
] = strdup(optarg
);
7462 values
[defcount
] = strdup("1");
7465 case 602: // defzero
7466 //fprintf(stderr, "defzero: [%s]\n", optarg);
7467 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
7468 values
= realloc(values
, sizeof(char *)*(defcount
+1));
7469 defines
[defcount
] = strdup(optarg
);
7470 values
[defcount
] = strdup("0");
7474 if (optOutputDir
!= NULL
) free(optOutputDir
);
7475 optOutputDir
= strdup(optarg
);
7478 if (optOutputFile
!= NULL
) free(optOutputFile
);
7479 optOutputFile
= strdup(optarg
);
7481 case 669: // reffile
7482 if (refFileName
) free(refFileName
);
7483 refFileName
= (optarg
? strdup(optarg
) : NULL
);
7486 case 690: case 692: // +3DOS 180K disk
7487 optRunDSK
= (c
== 692);
7488 optDskType
= P3DSK_PCW_SS
;
7492 case 691: case 693: // +3DOS 720K disk
7493 optRunDSK
= (c
== 693);
7494 optDskType
= P3DSK_PCW_DS
;
7498 case 656: // hex castrates
7499 if (optarg
!= NULL
) {
7500 if (strcmp(optarg
, "tan") == 0 || strcmp(optarg
, "yes") == 0) {
7501 urasm_allow_hex_castrates
= 1;
7502 } else if (strcmp(optarg
, "ona") == 0 || strcmp(optarg
, "no") == 0) {
7503 urasm_allow_hex_castrates
= 0;
7505 fprintf(stderr
, "FATAL: i don't know what '%s' means.\n", optarg
);
7509 urasm_allow_hex_castrates
= 1;
7515 if (optind
>= argc
) {
7516 // try to find default input file
7517 for (int f
= 0; defInFiles
[f
]; ++f
) {
7518 if (!access(defInFiles
[f
], R_OK
)) {
7519 inFile
= strdup(defInFiles
[f
]);
7524 inFile
= strdup(argv
[optind
]);
7527 if (!inFile
|| !inFile
[0]) {
7529 fprintf(stderr
, "ERROR: no input file!\n");
7533 if (optOutputDir
== NULL
) optOutputDir
= strdup(".");
7535 for (char *ss
= optOutputDir
; *ss
; ++ss
) if (*ss
== '\\') *ss
= '/';
7538 char *ee
= strrchr(optOutputDir
, '/');
7539 if (ee
&& ee
!= optOutputDir
) *ee
= 0;
7541 if (!optOutputDir
[0]) { free(optOutputDir
); optOutputDir
= strdup("."); }
7543 registerInstructions();
7544 registerFunctions();
7546 res
= asmTextLoad(inFile
, 0);
7548 for (int f
= 0; f
< defcount
; ++f
) {
7549 if (labelDoEQU(defines
[f
], values
[f
]) != 0) {
7550 fprintf(stderr
, "FATAL: can't define label: '%s'\n", defines
[f
]);
7553 //fprintf(stderr, "****SET**** <%s> to <%s>\n", defines[f], values[f]);
7556 for (pass
= 0; pass
<= 1; ++pass
) {
7558 printf("pass %d\n", pass
);
7559 //fprintf(stderr, "===========pass %d===========\n", pass);
7560 setCurSrcLine(asmText
);
7561 if (setjmp(errJP
)) { res
= 1; break; }
7562 while (currSrcLine
) processCurrentLine();
7563 if (postPass()) { res
= 1; break; }
7568 char *oc
= strdup(inFile
);
7569 char *pd
= strrchr(oc
, '.');
7571 if (pd
&& !strchr(oc
, '/') && !strchr(oc
, '\\')) *pd
= '\0';
7573 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
7575 switch (optWriteType
) {
7576 case 's': saveSna(oc
, optSNA48
); break;
7577 case 't': saveTap(oc
); break;
7578 case 'r': saveRaw(oc
); break;
7579 case 'd': saveDMB(oc
); break;
7580 case 'H': saveHob(oc
); break;
7581 case 'S': saveSCL(oc
); break;
7582 case 'M': saveSCLMono(oc
); break;
7583 case '3': saveDSK(oc
); break;
7586 if (optWriteFixups
) writeFixups();
7588 /* build ref file name */
7589 if (!refFileName
|| !refFileName
[0]) {
7590 if (refFileName
) free(refFileName
);
7591 refFileName
= malloc(strlen(inFile
)+128);
7592 strcpy(refFileName
, inFile
);
7593 char *ext
= strrchr(refFileName
, '.');
7595 strcat(refFileName
, ".ref");
7599 for (char *ts
= refFileName
; *ts
; ++ts
) {
7600 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
7603 char *slash
= strrchr(refFileName
, '/');
7605 if (!slash
|| slash
< ext
) {
7606 strcpy(ext
, ".ref");
7608 strcat(refFileName
, ".ref");
7612 writeLabelsFile(refFileName
);
7613 printf("refs written to '%s'\n", refFileName
);
7618 fprintf(stderr
, "ERROR: loading error!\n");
7623 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
7634 if (inFile
) free(inFile
);
7635 if (sysIncludeDir
) free(sysIncludeDir
);
7636 if (ufoIncludeDir
) free(ufoIncludeDir
);
7637 if (lastIncludePath
) free(lastIncludePath
);
7638 if (lastSysIncludePath
) free(lastSysIncludePath
);
7639 for (int f
= defcount
-1; f
>= 0; --f
) {
7643 if (defines
!= NULL
) { free(values
); free(defines
); }
7644 if (optOutputFile
) free(optOutputFile
);
7645 if (optOutputDir
) free(optOutputDir
);
7647 return (res
? 1 : 0);