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 UR_FORCE_INLINE
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);
81 //hash += (uint8_t)locase1251(*s++);
82 hash
+= (*s
++);//|0x20; // this converts ASCII capitals to locase (and destroys other, but who cares)
94 ////////////////////////////////////////////////////////////////////////////////
95 UR_FORCE_INLINE
int isSpace (char ch
) { return (ch
&& ((unsigned)(ch
&0xff) <= 32 || ch
== 127)); }
96 UR_FORCE_INLINE
int isAlpha (char ch
) { return ((ch
>= 'A' && ch
<= 'Z') || (ch
>= 'a' && ch
<= 'z')); }
97 UR_FORCE_INLINE
int isDigit (char ch
) { return (ch
>= '0' && ch
<= '9'); }
98 UR_FORCE_INLINE
int isHexDigit (char ch
) { return ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F') || (ch
>= 'a' && ch
<= 'f')); }
99 UR_FORCE_INLINE
int isAlphaDigit (char ch
) { return (isAlpha(ch
) || isDigit(ch
)); }
101 UR_FORCE_INLINE
char toUpper (char ch
) { return (ch
>= 'a' && ch
<= 'z' ? ch
-'a'+'A' : ch
); }
102 UR_FORCE_INLINE
char toLower (char ch
) { return (ch
>= 'A' && ch
<= 'Z' ? ch
-'A'+'a' : ch
); }
104 static int digitInBase (char ch
, int base
) {
105 if (ch
< '0') return -1;
107 if (ch
>= '0'+base
) return -1;
111 if (ch
<= '9') return ch
-'0';
112 if (ch
< 'A' || ch
> 'A'+base
-10) return -1;
114 return (ch
< base
? ch
: -1);
118 //==========================================================================
122 //==========================================================================
123 static int strEquCI (const char *s0
, const char *s1
) {
125 while (res
&& *s0
&& *s1
) {
126 char c0
= *s0
++; if (c0
>= 'A' && c0
<= 'Z') c0
= c0
- 'A' + 'a';
127 char c1
= *s1
++; if (c1
>= 'A' && c1
<= 'Z') c1
= c1
- 'A' + 'a';
130 return (res
&& s0
[0] == 0 && s1
[0] == 0);
134 ////////////////////////////////////////////////////////////////////////////////
135 static char *strprintfVA (const char *fmt
, va_list vaorig
) {
140 if (buf
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
146 olen
= vsnprintf(buf
, len
, fmt
, va
);
148 if (olen
>= 0 && olen
< len
) return buf
;
149 if (olen
< 0) olen
= len
*2-1;
150 nb
= realloc(buf
, olen
+1);
151 if (nb
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
158 static __attribute__((format(printf
,1,2))) char *strprintf (const char *fmt
, ...) {
163 buf
= strprintfVA(fmt
, va
);
169 ///////////////////////////////////////////////////////////////////////////////
174 AMODE_UFO
= 1, // scanning UrForth code
177 static int asmMode
= AMODE_NORMAL
;
179 static char *ufoIncludeDir
= NULL
;
180 static char *sysIncludeDir
= NULL
;
181 static char *refFileName
= NULL
;
182 static char *lastIncludePath
= NULL
;
183 static char *lastSysIncludePath
= NULL
;
184 static int urasm_dump_all_labels
= 1; // 0: do not write all-uppercase labels
186 #define MAX_LINE_SIZE (1024*1024)
187 static char currLine
[MAX_LINE_SIZE
]; /* current processing line; can be freely modified */
189 // set by `processLabel()`
190 static char currSeenLabel
[1024];
193 ///////////////////////////////////////////////////////////////////////////////
196 static void initInclideDir (void) {
197 const char *id
= getenv("URASM_INCLUDE_DIR");
199 sysIncludeDir
= strdup(id
);
202 memset(myDir
, 0, sizeof(myDir
));
204 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
207 char *p
= (char *)strrchr(myDir
, '/');
208 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
211 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
212 char *p
= strrchr(myDir
, '\\');
213 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
215 strcat(myDir
, "/libs");
216 sysIncludeDir
= strdup(myDir
);
218 while (sysIncludeDir
[0] && sysIncludeDir
[strlen(sysIncludeDir
)-1] == '/') sysIncludeDir
[strlen(sysIncludeDir
)-1] = '\0';
219 if (!sysIncludeDir
[0]) strcpy(sysIncludeDir
, ".");
223 static void initUFEInclideDir (void) {
224 const char *id
= getenv("URASM_URFORTH_INCLUDE_DIR");
226 ufoIncludeDir
= strdup(id
);
229 memset(myDir
, 0, sizeof(myDir
));
231 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
234 char *p
= (char *)strrchr(myDir
, '/');
235 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
238 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
239 char *p
= strrchr(myDir
, '\\');
240 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
242 strcat(myDir
, "/urflibs");
243 ufoIncludeDir
= strdup(myDir
);
245 while (ufoIncludeDir
[0] && ufoIncludeDir
[strlen(ufoIncludeDir
)-1] == '/') ufoIncludeDir
[strlen(ufoIncludeDir
)-1] = '\0';
246 if (!ufoIncludeDir
[0]) strcpy(ufoIncludeDir
, ".");
250 ///////////////////////////////////////////////////////////////////////////////
253 /* trim trailing spaces and comments; normalize colons */
254 static void normalizeStr (char *s
) {
255 if (asmMode
== AMODE_UFO
) return; // do nothing
257 /* now skip all shit */
259 const char ch
= *p
++;
260 /* check for "af'" */
261 if (ch
== '\'' && p
-s
>= 2 && toLower(p
[-2] == 'a') && toLower(p
[-1] == 'f')) continue;
263 if (ch
== ';') { p
[-1] = 0; break; }
265 if (ch
== '"' || ch
== '\'') {
268 const char c1
= *p
++;
269 if (c1
== qch
) break;
270 if (c1
== '\\' && *p
) ++p
;
274 /* reduce and normalise colons */
276 --p
; /* back to colon */
277 /* remove spaces before colon */
279 while (t
!= s
&& isSpace(t
[-1])) --t
;
280 if (t
!= p
) memmove(t
, p
, strlen(p
)+1);
282 ur_assert(p
[0] == ':');
283 ++p
; /* skip colon */
284 /* remove following spaces and colons */
286 while (*t
== ':' || isSpace(*t
)) ++t
;
287 if (t
!= p
) memmove(p
, t
, strlen(t
)+1);
291 /* done; trim trailing spaces and colons */
292 size_t slen
= strlen(s
);
293 while (slen
> 0 && (isSpace(s
[slen
-1]) || s
[slen
-1] == ':')) --slen
;
298 /* check if string starts with the given command (case-insensitive) */
299 /* returns NULL or pointer to args */
300 /* skips spaces after command if any */
301 static char *strIsCommand (const char *command
, char *str
) {
302 for (int cnt
= 1; cnt
> 0; --cnt
) {
303 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
304 if (*str
== '$') ++str
;
305 for (; *command
&& *str
; ++command
, ++str
) {
306 if (toUpper(*command
) != toUpper(*str
)) return NULL
; // alas
308 if (*command
) return NULL
; // alas
309 if (*str
&& isAlphaDigit(*str
)) return NULL
; // alas
310 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
311 if (asmMode
!= AMODE_UFO
) {
312 if (*str
&& *str
== ':') break; // try again if we have a colon
320 /* parse string literal */
321 /* don't free() result */
322 /* skips trailing spaces */
323 static char *parseStr (char **str
, char endQ
, int *lenp
) {
324 static char buf
[MAX_LINE_SIZE
];
325 int len
= 0, n
, f
, base
;
327 memset(buf
, 0, sizeof(buf
));
333 case 'a': buf
[len
++] = '\a'; break;
334 case 'b': buf
[len
++] = '\b'; break;
335 case 'e': buf
[len
++] = '\x1b'; break;
336 case 'f': buf
[len
++] = '\f'; break;
337 case 'n': buf
[len
++] = '\n'; break;
338 case 'r': buf
[len
++] = '\r'; break;
339 case 't': buf
[len
++] = '\t'; break;
340 case 'v': buf
[len
++] = '\v'; break;
341 case 'z': buf
[len
++] = '\0'; break;
342 case 'x': case 'X': // hex
345 donum
: for (n
= 0; f
> 0; --f
) {
346 char ch
= digitInBase(*a
++, base
);
348 if (ch
< 0) { --a
; break; }
353 --a
; // return to the last digit, 'for' will skip it
358 case '1' ... '9': // decimal
361 default: buf
[len
++] = a
[0]; break; // others
364 if (*a
== endQ
) { ++a
; break; }
368 while (*a
&& isSpace(*a
)) ++a
; // skip trailing spaces
371 if (lenp
) *lenp
= len
;
376 ///////////////////////////////////////////////////////////////////////////////
377 // source file stack, reader, etc
379 typedef struct SourceLine
{
380 struct SourceLine
*next
;
387 static SourceLine
*asmText
= NULL
;
388 static SourceLine
*currSrcLine
= NULL
;
390 typedef struct FileInfo_s
{
391 struct FileInfo_s
*next
;
395 static FileInfo
*knownFiles
= NULL
;
398 static void clearKnownFiles (void) {
400 FileInfo
*fi
= knownFiles
;
401 knownFiles
= fi
->next
;
402 if (fi
->name
) free(fi
->name
);
408 static const char *appendKnownFileName (const char *fn
) {
409 if (!fn
|| !fn
[0]) return "<unknown>";
410 for (FileInfo
*fi
= knownFiles
; fi
; fi
= fi
->next
) {
411 if (strcmp(fi
->name
, fn
) == 0) return fi
->name
;
413 FileInfo
*fi
= calloc(1, sizeof(FileInfo
));
414 ur_assert((fi
->name
= strdup(fn
)) != NULL
);
415 fi
->next
= knownFiles
;
421 // ////////////////////////////////////////////////////////////////////////// //
422 #define MAX_MACRO_ARGS (32)
424 typedef struct MacroDef
{
425 struct MacroDef
*next
;
429 char *argdefaults
[MAX_MACRO_ARGS
]; // default values
430 char *argnames
[MAX_MACRO_ARGS
]; // argument names
435 char *argvals
[MAX_MACRO_ARGS
]; // argument values
438 static MacroDef
*maclist
= NULL
;
439 static CurMacroDef
*curmacro
= NULL
; // !NULL if we are not inserting macro
440 static int curmacronum
= 0;
443 static void freeLineList (SourceLine
*list
) {
445 SourceLine
*l
= list
;
454 static void clearMacros (void) {
456 MacroDef
*md
= maclist
;
458 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) {
459 if (md
->argdefaults
[f
]) free(md
->argdefaults
[f
]);
460 if (md
->argnames
[f
]) free(md
->argnames
[f
]);
463 freeLineList(md
->lines
);
469 static MacroDef
*findMacro (const char *name
) {
470 for (MacroDef
*mc
= maclist
; mc
!= NULL
; mc
= mc
->next
) if (strEquCI(name
, mc
->name
)) return mc
;
475 static void asmTextClear (void) {
476 freeLineList(asmText
);
482 #define currLineTrimBlanks() do { \
483 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
484 currLine[slen] = 0; \
487 #define currLineRemoveComment() do { \
490 while (pos < slen) { \
492 if (currLine[pos] == '\\' && pos+1 < slen) { \
494 } else if (currLine[pos] == instr) { \
498 if (currLine[pos] == ';') { slen = pos; break; } \
499 if (currLine[pos] == '"') { \
501 } else if (currLine[pos] == '\'') { \
503 (currLine[pos-1] != '_' && !isAlphaDigit(currLine[pos-1]))) \
505 instr = currLine[pos]; \
511 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
512 currLine[slen] = 0; \
516 static SourceLine
*loadTextFile (FILE *fl
, int system
, const char *fname
, int *error
) {
517 if (error
) *error
= 0;
519 fname
= appendKnownFileName(fname
);
520 int continuation
= 0;
522 SourceLine
*first
= NULL
;
523 SourceLine
*last
= NULL
;
527 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
529 currLine
[sizeof(currLine
)-1] = '\0';
530 size_t slen
= strlen(currLine
);
532 if (slen
== 0 || (currLine
[slen
-1] != '\n' && currLine
[slen
-1] != '\r')) {
534 if (error
) *error
= 1;
535 fprintf(stderr
, "ERROR: file %s, line %d: line too long\n", fname
, lineNo
);
541 currLineTrimBlanks();
544 if (slen
&& currLine
[slen
-1] == '\\') {
545 // this is a possible continuation line
546 if (slen
> 1 && currLine
[slen
-2] == '\\') {
553 currLineTrimBlanks();
557 //!normalizeStr(currLine);
559 // don't store empty lines
560 continuation
= new_cont
;
564 // remove comments from continuations
565 if (continuation
|| new_cont
) {
566 currLineRemoveComment();
571 // join continuation with the last line
573 size_t newlen
= slen
+strlen(last
->line
)+4;
574 if (newlen
> MAX_LINE_SIZE
-8) {
575 if (error
) *error
= 1;
576 fprintf(stderr
, "ERROR: too long continuation at line %d in file '%s'\n", lineNo
, fname
);
580 char *newbuf
= malloc(newlen
);
581 ur_assert(newbuf
); // who cares
582 snprintf(newbuf
, newlen
, "%s%s", last
->line
, currLine
);
587 SourceLine
*s
= calloc(1, sizeof(SourceLine
));
591 ur_assert((s
->line
= strdup(currLine
)) != NULL
);
593 if (last
) last
->next
= s
; else first
= s
;
597 continuation
= new_cont
;
604 static int asmTextLoad (const char *fname
, int system
) {
608 if (!(fl
= fopen(fname
, "r"))) {
609 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
612 printf("loading: %s\n", fname
);
614 SourceLine
*first
= loadTextFile(fl
, 0, fname
, &error
);
617 if (error
) return -1;
619 SourceLine
*last
= asmText
;
621 while (last
->next
) last
= last
->next
;
630 static char *extractFileDir (const char *s
) {
631 if (!s
|| !s
[0]) return strdup(".");
635 for (const char *ts
= s
; *ts
; ++ts
) {
636 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
638 if (slash
== s
&& (s
[0] == '/' || s
[0] == '\\') && (s
[1] == '/' || s
[1] == '\\')) slash
= NULL
;
640 slash
= strrchr(s
, '/');
642 if (!slash
) return strdup(".");
643 ptrdiff_t len
= (ptrdiff_t)(slash
-s
)+1;
644 char *res
= malloc(len
+1);
648 while (len
> 0 && (res
[len
-1] == '\\' || res
[len
-1] == '/')) --len
;
650 while (len
> 0 && res
[len
-1] == '/') --len
;
652 if (len
== 0) { free(res
); return strdup("."); }
658 static void loadCurSrcLine (void) {
660 strcpy(currLine
, currSrcLine
->line
);
661 /* macros will not change include dirs */
663 char **incpp
= (!currSrcLine
->system
? &lastIncludePath
: &lastSysIncludePath
);
664 if (*incpp
) free(*incpp
);
665 *incpp
= extractFileDir(currSrcLine
->fname
);
667 normalizeStr(currLine
);
674 UR_FORCE_INLINE SourceLine
*setCurSrcLine (SourceLine
*l
) {
681 UR_FORCE_INLINE SourceLine
*setUFOCurSrcLine (SourceLine
*l
) {
684 strcpy(currLine
, currSrcLine
->line
);
692 UR_FORCE_INLINE SourceLine
*nextSrcLine (void) {
693 return (currSrcLine
!= NULL
? setCurSrcLine(currSrcLine
->next
) : NULL
);
697 UR_FORCE_INLINE SourceLine
*nextUFOSrcLine (void) {
698 return (currSrcLine
!= NULL
? setUFOCurSrcLine(currSrcLine
->next
) : NULL
);
702 UR_FORCE_INLINE
int strHasPathDelim (const char *s
) {
703 if (!s
|| !s
[0]) return 0;
705 return (strchr(s
, '/') || strchr(s
, '\\') ? 1 : 0);
707 return (strchr(s
, '/') ? 1 : 0);
712 /* returns malloced string */
713 static char *createIncludeName (const char *fname
, int assystem
, const char *defaultmain
,
714 const char *lastIncPathUFO
) {
715 if (!fname
|| !fname
[0]) return NULL
;
717 if (fname
[0] != '/') {
720 incdir
= lastIncludePath
;
721 } else if (assystem
== -669) {
722 incdir
= lastIncPathUFO
;
723 } else if (assystem
== -666) {
724 incdir
= lastIncPathUFO
;
725 if (!incdir
|| !incdir
[0]) incdir
= ufoIncludeDir
;
727 incdir
= lastSysIncludePath
;
728 if (!incdir
|| !incdir
[0]) incdir
= sysIncludeDir
;
730 if (incdir
== NULL
|| incdir
[0] == 0) incdir
= ".";
731 res
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
733 res
= strprintf("%s", fname
);
736 if (defaultmain
&& defaultmain
[0]) {
737 if (stat(res
, &st
) == 0) {
738 if (S_ISDIR(st
.st_mode
)) {
739 char *rs
= strprintf("%s/%s", res
, defaultmain
);
745 /* check if there is the disk file */
746 if (strHasPathDelim(fname
) && stat(res
, &st
) != 0) {
747 /* no file, try "root include" */
748 const char *incdir
= (!assystem
? NULL
: assystem
== -666 ? ufoIncludeDir
: sysIncludeDir
);
749 char *rs
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
752 /* check for dir again */
753 if (defaultmain
&& defaultmain
[0]) {
754 if (stat(res
, &st
) == 0) {
755 if (S_ISDIR(st
.st_mode
)) {
756 char *rs
= strprintf("%s/%s", res
, defaultmain
);
763 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
768 static int includeCount
= 0;
771 // include file instead of the current line
772 // returns 0 on ok, <0 on error
773 static int asmTextInclude (const char *fname
, int system
, int softinclude
) {
774 char *fn
= createIncludeName(fname
, system
, "zzmain.zas", NULL
);
776 FILE *fl
= fopen(fn
, "r");
778 if (softinclude
) return 0;
779 fprintf(stderr
, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", currSrcLine
->fname
, currSrcLine
->lineNo
, currLine
);
784 if (includeCount
> 256) {
787 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", currSrcLine
->fname
, currSrcLine
->lineNo
);
792 printf("loading: %s\n", fn
);
795 SourceLine
*first
= loadTextFile(fl
, system
, fn
, &error
);
799 if (error
) return -1;
802 currSrcLine
->line
[0] = 0; // remove `include` directive
804 SourceLine
*last
= first
;
806 while (last
->next
) last
= last
->next
;
807 last
->next
= currSrcLine
->next
;
808 currSrcLine
->next
= first
;
814 ///////////////////////////////////////////////////////////////////////////////
817 static void processCurrentLine (void); // only one, will skip to next one
820 ///////////////////////////////////////////////////////////////////////////////
821 // error raisers, etc
823 static jmp_buf errJP
;
826 static void errorWriteFile (FILE *fo
) {
828 size_t slen
= strlen(currSrcLine
->line
);
829 fprintf(fo
, "at file %s, line %d\n%.70s%s\n*", currSrcLine
->fname
,
830 currSrcLine
->lineNo
, currSrcLine
->line
, (slen
> 70 ? " ..." : ""));
832 fprintf(fo
, "somewhere in time: ");
837 static void errorMsgV (const char *fmt
, va_list ap
) {
838 errorWriteFile(stderr
);
839 vfprintf(stderr
, fmt
, ap
);
846 __attribute__((format(printf
, 1, 2)))
847 static void warningMsg (const char *fmt
, ...) {
849 fprintf(stderr
, "WARNING ");
855 __attribute__((format(printf
, 1, 2)))
856 static void errorMsg (const char *fmt
, ...) {
858 fprintf(stderr
, "FATAL ");
864 __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2)))
865 static void fatal (const char *fmt
, ...) {
873 __attribute__((noreturn
))
874 static void fatalUrLib (int errcode
) {
875 errorMsg("%s", urasm_errormsg(errcode
));
880 ///////////////////////////////////////////////////////////////////////////////
882 static int optWriteType
= 't';
883 static int optWTChanged
= 0;
884 static int optWriteFixups
= 0;
885 static int optFixupType
= 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
886 static int optRunTape
= 1;
887 static int optRunDMB
= 1;
888 static int optRunSCL
= -1; /* -1: save cargador, but no boot; 1: boot and cargador */
889 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
890 static int optSNA48
= 1;
891 static char *optOutputDir
= NULL
;
892 static char *optOutputFile
= NULL
;
893 static int optOutputFileFixed
= 0;
896 static char *buildOutputFileName (const char *basename
, const char *ext
) {
899 if (ext
[0] == '.') ++ext
;
900 //fprintf(stderr, "********** <%s> <%s> <%s> %d <%s>\n", basename, ext, optOutputFile, optOutputFileFixed, optOutputDir);
902 if (!optOutputFileFixed
) {
903 optOutputFileFixed
= 1;
904 char *spp
= strrchr(optOutputFile
, '/');
906 char *spp1
= strrchr(optOutputFile
, '\\');
907 if (!spp
|| (spp
< spp1
)) spp
= spp1
;
909 if (!spp
) spp
= optOutputFile
; else ++spp
;
910 char *dotpos
= strrchr(spp
, '.');
911 if (dotpos
) *dotpos
= 0;
913 if (optOutputFile
&& optOutputFile
[0]) return strprintf("%s/%s.%s", optOutputDir
, optOutputFile
, ext
);
915 return strprintf("%s/%s.%s", optOutputDir
, basename
, ext
);
919 typedef struct OutDataFile_t
{
921 char type
; // 'C', 'B'
923 uint16_t start
; // start address, or basic start line
924 uint16_t size
; // data size (never 0)
926 struct OutDataFile_t
*next
;
929 static OutDataFile
*datafiles
= NULL
;
932 static OutDataFile
*appendDataData (const void *bytes
, uint16_t start
, uint16_t count
) {
933 if (!count
) return NULL
;
935 OutDataFile
*off
= malloc(sizeof(OutDataFile
));
941 off
->data
= malloc(count
);
942 if (bytes
) memcpy(off
->data
, bytes
, count
); else memset(off
->data
, 0, count
);
945 OutDataFile
*last
= datafiles
;
949 while (last
->next
) last
= last
->next
;
957 static OutDataFile
*appendDataFile (const char *fname
, long ofs
, int len
, int allowskip
) {
958 if (!fname
|| !fname
[0]) return NULL
;
960 FILE *fl
= fopen(fname
, "rb");
962 if (allowskip
) return NULL
;
963 fatal("cannot open data file '%s'", fname
);
965 if (fseek(fl
, 0, SEEK_END
) < 0) fatal("error reading data file '%s'", fname
);
966 long fsize
= ftell(fl
);
967 if (fsize
< 0) fatal("error reading data file '%s'", fname
);
971 if (ofs
< 0) fatal("invalid offset");
972 if (ofs
>= fsize
) ofs
= 0; else ofs
= fsize
-ofs
;
974 if (ofs
>= fsize
) { fclose(fl
); return NULL
; }
975 if (fseek(fl
, ofs
, SEEK_SET
) < 0) fatal("error reading data file '%s'", fname
);
978 if (fsize
> 65535) fatal("data file '%s' too big", fname
);
981 if (len
> 65535) fatal("data file '%s' too big", fname
);
982 if (len
== 0) { fclose(fl
); return NULL
; }
984 OutDataFile
*off
= malloc(sizeof(OutDataFile
));
989 off
->size
= (uint16_t)len
;
990 off
->data
= malloc(len
);
992 if (fread(off
->data
, (unsigned)len
, 1, fl
) != 1) fatal("error reading data file '%s'", fname
);
995 OutDataFile
*last
= datafiles
;
999 while (last
->next
) last
= last
->next
;
1007 //////////////////////////////////////////////////////////////////////////////
1008 // operator management
1010 // return !0 to skip current line
1011 typedef int (*UrAsmOpFn
) (void);
1018 typedef struct UrAsmOp
{
1021 void *udata
; // user data (struct info for structs, for example)
1022 struct UrAsmOp
*next
;
1025 static UrAsmOp
*oplist
= NULL
;
1026 static UrAsmOp
*urCurrentOp
= NULL
;
1029 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
1030 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
1032 res
->name
= strdup(name
);
1033 ur_assert(res
->name
);
1042 static UrAsmOp
*urFindOp (const char *name
) {
1043 if (!name
) return NULL
;
1045 if (name
[0] == '$' && name
[1]) {
1046 for (res
= oplist
; res
; res
= res
->next
) {
1047 if (res
->name
[0] == '$') {
1048 if (strEquCI(name
, res
->name
)) break;
1050 if (strEquCI(name
+1, res
->name
)) break;
1054 for (res
= oplist
; res
; res
= res
->next
) if (strEquCI(name
, res
->name
)) break;
1060 static void urClearOps (void) {
1062 UrAsmOp
*c
= oplist
;
1063 oplist
= oplist
->next
;
1070 ///////////////////////////////////////////////////////////////////////////////
1074 LBL_TYPE_VERY_SPECIAL
= -42,
1075 LBL_TYPE_UNKNOWN
= -1,
1076 LBL_TYPE_ASS
= 0, // `=`
1077 LBL_TYPE_EQU
= 1, // equ
1078 LBL_TYPE_CODE
= 2, // ':', or "lbl opcode"
1079 LBL_TYPE_STOFS
= 3, // struct offset
1083 typedef struct UrLabelInfo_s
{
1086 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
1087 int known
; /* !0: label value already known */
1088 int refLine
; /* first referenced line */
1089 int fixuptype
; /* UR_FIXUP_XXX */
1090 int modHidden
; /* hidden module label? hack for PLUS3BOOT */
1091 char savedFirstChar
; /* for hidden labels */
1093 struct UrLabelInfo_s
*next
;
1095 struct UrLabelInfo_s
*hlink
;
1099 #define UR_LABEL_BUCKETS (1024)
1101 static UrLabelInfo
*labels
= NULL
;
1102 static UrLabelInfo
*labelsTail
= NULL
;
1103 static UrLabelInfo
*labelBuckets
[UR_LABEL_BUCKETS
];
1106 //==========================================================================
1110 //==========================================================================
1111 static void initLabelBuckets (void) {
1112 for (int f
= 0; f
< UR_LABEL_BUCKETS
; f
+= 1) labelBuckets
[f
] = NULL
;
1116 //==========================================================================
1120 //==========================================================================
1121 static void urClearLabels (void) {
1123 while ((c
= labels
) != NULL
) {
1125 if (c
->name
) free(c
->name
);
1126 if (c
->refFile
) free(c
->refFile
);
1134 //==========================================================================
1138 //==========================================================================
1139 static UrLabelInfo
*urFindLabel (const char *name
) {
1140 if (name
!= NULL
&& name
[0] != 0) {
1142 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
1143 if (strcmp(name
, c
->name
) == 0) return c
;
1146 const uint32_t hash
= joaatHashStr(name
);
1147 UrLabelInfo
*c
= labelBuckets
[hash
%UR_LABEL_BUCKETS
];
1149 if (c
->hash
== hash
&& strcmp(c
->name
, name
) == 0) return c
;
1158 //==========================================================================
1162 //==========================================================================
1163 static UrLabelInfo
*urAddLabel (const char *name
) {
1164 ur_assert(name
!= NULL
&& name
[0] != 0);
1165 UrLabelInfo
*c
= urFindLabel(name
);
1167 c
= calloc(1, sizeof(UrLabelInfo
));
1169 c
->name
= strdup(name
);
1171 c
->type
= LBL_TYPE_UNKNOWN
;
1172 c
->fixuptype
= UR_FIXUP_NONE
;
1174 c
->savedFirstChar
= 0;
1176 if (labelsTail
) labelsTail
->next
= c
; else labels
= c
;
1178 c
->hash
= joaatHashStr(c
->name
);
1179 const uint32_t bkt
= c
->hash
%UR_LABEL_BUCKETS
;
1180 c
->hlink
= labelBuckets
[bkt
];
1181 labelBuckets
[bkt
] = c
;
1187 //==========================================================================
1191 // add local label; it may shadow others
1193 //==========================================================================
1194 static UrLabelInfo
*urAddTempLocal (const char *name
) {
1195 UrLabelInfo
*c
= calloc(1, sizeof(UrLabelInfo
));
1197 c
->name
= strdup(name
);
1199 c
->type
= LBL_TYPE_EQU
;
1201 c
->fixuptype
= UR_FIXUP_NONE
;
1203 c
->savedFirstChar
= 0;
1206 if (!labelsTail
) labelsTail
= c
;
1207 c
->hash
= joaatHashStr(c
->name
);
1208 const uint32_t bkt
= c
->hash
%UR_LABEL_BUCKETS
;
1209 c
->hlink
= labelBuckets
[bkt
];
1210 labelBuckets
[bkt
] = c
;
1215 static void urRemoveTempLocal (UrLabelInfo
*tmplbl
) {
1216 if (!tmplbl
) return;
1217 ur_assert(tmplbl
== labels
);
1218 labels
= tmplbl
->next
;
1219 if (!labels
) labelsTail
= NULL
;
1220 const uint32_t bkt
= tmplbl
->hash
%UR_LABEL_BUCKETS
;
1221 ur_assert(labelBuckets
[bkt
] == tmplbl
);
1222 labelBuckets
[bkt
] = tmplbl
->hlink
;
1228 ///////////////////////////////////////////////////////////////////////////////
1230 typedef struct StructField_s
{
1231 struct StructField_s
*next
; // next field
1232 char *name
; // field name
1233 uint16_t ofs
; // field offset in struct
1234 uint16_t size
; // field size in bytes (can be 0 for aliases)
1240 typedef struct StructInfo_s
{
1241 struct StructInfo_s
*next
; // next structure
1242 char *name
; // structure name
1243 StructField
*fields
;
1244 uint16_t size
; // total structure size in bytes
1249 // list of all structures
1250 static StructInfo
*structList
= NULL
;
1253 static void freeStructs (void) {
1254 while (structList
) {
1255 StructInfo
*sth
= structList
;
1256 structList
= sth
->next
;
1257 StructField
*fld
= sth
->fields
;
1259 StructField
*fc
= fld
;
1261 if (fc
->name
) free(fc
->name
);
1264 if (sth
->name
) free(sth
->name
);
1270 ///////////////////////////////////////////////////////////////////////////////
1272 typedef struct FixupItem_s
{
1273 struct FixupItem_s
*next
;
1274 uint16_t opdestaddr
;
1279 static FixupItem
*fixlisthead
= NULL
, *fixlisttail
= NULL
;
1282 static void clearFixups (void) {
1284 while ((c
= fixlisthead
) != NULL
) {
1285 fixlisthead
= c
->next
;
1288 fixlisthead
= fixlisttail
= NULL
;
1292 static void addFixup (uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
1293 FixupItem
*fx
= calloc(1, sizeof(FixupItem
));
1295 fx
->opdestaddr
= opdestaddr
;
1296 fx
->opaddr
= opaddr
;
1297 fx
->fixuptype
= fixuptype
;
1300 if (fixlisttail
!= NULL
) fixlisttail
->next
= fx
; else fixlisthead
= fx
;
1306 ///////////////////////////////////////////////////////////////////////////////
1307 // destination memory management
1309 static uint8_t memory
[65536];
1310 static uint8_t memused
[65536];
1311 static uint8_t memresv
[65536];
1312 static uint16_t start_pc
= 0x100; // viva CP/M!
1313 static uint16_t start_disp
= 0x100; // viva CP/M!
1314 static uint16_t start_ent
= 0x100; // viva CP/M!
1315 static uint16_t pc
= 0; /* current position to write */
1316 static uint16_t disp
= 0; /* current 'virtual PC' */
1317 static uint16_t ent
= 0; /* starting address */
1318 static uint16_t clrAddr
= 24999; /* address for CLEAR for cargador */
1319 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
1320 static int inTapeBlock
= 0;
1321 static uint8_t tapeXorB
= 0;
1323 static int findChunkFrom (int addr
, int *start
, int *len
);
1326 // find next free area starting from `addr`
1327 // returns address or -1
1328 static int reserveFindFreeFrom (uint16_t addr
) {
1329 for (uint32_t freeaddr
= addr
; freeaddr
<= 0xffffU
; ++freeaddr
) {
1330 if (!memresv
[freeaddr
]) return freeaddr
;
1336 // find next reserved area starting from `addr`
1337 // returns address or -1
1338 static int reserveFindReservedFrom (uint16_t addr
) {
1339 for (uint32_t freeaddr
= addr
; freeaddr
<= 0xffffU
; ++freeaddr
) {
1340 if (memresv
[freeaddr
]) return freeaddr
;
1346 static void reserveReleaseUsed (uint16_t addr
, int len
) {
1347 while (len
--) memused
[addr
++] = 0;
1352 static int reserveHit (uint16_t addr
, int len
) {
1353 if (len
<= 0) return 0;
1355 if (memresv
[addr
++]) return 1;
1361 // start from `addr`, look for free areas, and check
1362 // if we can insert jump from a pcaddr there
1363 // returns next free area or -1
1364 static int reservedFindNextJumpableFreeFrom (uint16_t pcaddr
, uint16_t addr
) {
1365 // check if there is enough room at least for a jr
1366 if (memresv
[pcaddr
] || memresv
[pcaddr
+1]) return -1;
1367 uint16_t caddr
= addr
;
1369 while (caddr
<= 0xffffU
&& memresv
[caddr
]) ++caddr
;
1370 if (caddr
> 0xffffU
) return -1; // alas
1372 uint32_t jrdist
= caddr
-(pcaddr
+2U);
1373 if (jrdist
< 128u) return (int)caddr
; // yay, we can JR there, this free area is good
1374 // cannot JR, check if we have room for JP
1375 if (memresv
[pcaddr
+2]) return -1; // alas
1381 // start from `addr`, look for free areas, and check
1382 // if we can insert jump from a pcaddr there, and if
1383 // that area is big enough to hold a next jump and at least one more byte
1384 // returns next free area or -1
1385 static int reservedFindNextSuitableFreeFrom (uint16_t pcaddr
, uint16_t addr
) {
1388 caddr
= reservedFindNextJumpableFreeFrom(pcaddr
, (uint16_t)caddr
);
1389 if (caddr
< 0) return -1; // alas
1390 // we can jump to naddr, now check if it is big enough
1391 // if it has less than 3 bytes, it is too small
1392 if (memresv
[(uint16_t)(caddr
+1)] || memresv
[(uint16_t)(caddr
+2)]) {
1397 // if we have more than 3 bytes, it is definitely ok
1398 if (!memresv
[(uint16_t)(caddr
+3)]) return caddr
; // ok
1399 // it has exactly 3 bytes, check if it is enough
1400 int xaddr
= reservedFindNextJumpableFreeFrom(caddr
+1, (uint16_t)caddr
+3);
1401 if (xaddr
>= 0) return caddr
; // ok
1403 caddr
= reserveFindReservedFrom(caddr
);
1404 if (caddr
< 0) return -1;
1405 caddr
= reserveFindFreeFrom(caddr
);
1406 if (caddr
< 0) return -1;
1410 UR_FORCE_INLINE
uint8_t getByte (uint16_t addr
) {
1411 return memory
[addr
];
1415 UR_FORCE_INLINE
uint16_t getWord (uint16_t addr
) {
1416 return ((uint16_t)memory
[addr
])|(((uint16_t)memory
[addr
+1])<<8);
1420 UR_FORCE_INLINE
void putByte (uint16_t addr
, uint8_t b
) {
1421 if (inTapeBlock
) tapeXorB
^= b
;
1427 UR_FORCE_INLINE
void putWord (uint16_t addr
, uint16_t w
) {
1428 putByte(addr
, w
&0xFFU
);
1429 putByte(addr
+1, (w
>>8)&0xFFU
);
1433 UR_FORCE_INLINE
void emitByte (uint8_t b
) {
1440 UR_FORCE_INLINE
void emitWord (uint16_t w
) {
1442 emitByte((w
>>8)&0xFFU
);
1446 UR_FORCE_INLINE
void emitRWord (uint16_t w
) {
1447 emitByte((w
>>8)&0xFFU
);
1452 static void prepareMemory (void) {
1453 memset(memory
, 0, sizeof(memory
));
1454 memset(memused
, 0, sizeof(memused
));
1455 memset(memresv
, 0, sizeof(memresv
));
1459 ///////////////////////////////////////////////////////////////////////////////
1460 // module list management
1462 typedef struct ModuleInfo_t
{
1464 char *fname
; // opened in this file
1465 int seen
; // !0: module already seen, skip other definitions from the same file
1466 struct ModuleInfo_t
*next
;
1468 struct ModuleInfo_t
*hlink
;
1471 #define UR_MODULE_BUCKETS (1024)
1472 static ModuleInfo
*modules
= NULL
;
1473 static ModuleInfo
*currModule
= NULL
;
1474 static ModuleInfo
*moduleBuckets
[UR_MODULE_BUCKETS
];
1476 static uint8_t mod_memory_saved
[65536];
1477 static uint8_t mod_memused_saved
[65536];
1478 static uint8_t mod_memresv_saved
[65536];
1481 static void modulesClear (void) {
1484 ModuleInfo
*c
= modules
;
1485 modules
= modules
->next
;
1490 for (int f
= 0; f
< UR_MODULE_BUCKETS
; f
+=1) moduleBuckets
[f
] = NULL
;
1494 //==========================================================================
1498 //==========================================================================
1499 static void modulesResetSeen (void) {
1500 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
1504 //==========================================================================
1508 //==========================================================================
1509 static ModuleInfo
*moduleFind (const char *name
) {
1510 if (!name
|| !name
[0]) return NULL
;
1512 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) return c
;
1514 const uint32_t hash
= joaatHashStr(name
);
1515 ModuleInfo
*c
= moduleBuckets
[hash
%UR_MODULE_BUCKETS
];
1517 if (c
->hash
== hash
&& strcmp(c
->name
, name
) == 0) return c
;
1525 //==========================================================================
1529 // special module name: PLUS3BOOT
1531 //==========================================================================
1532 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
1534 ur_assert(name
&& fname
&& name
[0] && fname
[0]);
1535 c
= calloc(1, sizeof(ModuleInfo
));
1537 c
->name
= strdup(name
);
1539 c
->fname
= strdup(fname
);
1540 ur_assert(c
->fname
);
1543 c
->hash
= joaatHashStr(name
);
1544 const uint32_t bkt
= c
->hash
%UR_MODULE_BUCKETS
;
1545 c
->hlink
= moduleBuckets
[bkt
];
1546 moduleBuckets
[bkt
] = c
;
1547 // +3DOS bootsector?
1548 if (strEquCI(c
->name
, "PLUS3BOOT")) {
1549 // save current memory state, because bootsector will be written as a separate independent file
1550 memcpy(mod_memory_saved
, memory
, sizeof(mod_memory_saved
));
1551 memcpy(mod_memused_saved
, memused
, sizeof(mod_memused_saved
));
1552 memcpy(mod_memresv_saved
, memresv
, sizeof(mod_memresv_saved
));
1558 //==========================================================================
1562 //==========================================================================
1563 static void moduleOpen (void) {
1564 //fprintf(stderr, "::: OPENING MODULE <%s> :::\n", currModule->name);
1565 // unhide +3DOS boot labels
1566 if (strEquCI(currModule
->name
, "PLUS3BOOT")) {
1567 for (UrLabelInfo
*lbl
= labels
; lbl
; lbl
= lbl
->next
) {
1568 if (lbl
->modHidden
== 1) {
1569 if (!lbl
->savedFirstChar
) fatal("internal module manager error");
1571 lbl
->name
[0] = lbl
->savedFirstChar
; // unhide it
1572 //fprintf(stderr, "***UN-HIDDEN: <%s>\n", lbl->name);
1579 //==========================================================================
1583 //==========================================================================
1584 static int isModuleLabel (const ModuleInfo
*mi
, const char *name
) {
1585 if (!mi
|| !name
|| !name
[0]) return 0;
1586 if (name
[0] == '.') ++name
;
1587 const size_t mnlen
= strlen(mi
->name
);
1588 if (strlen(name
) <= mnlen
|| name
[mnlen
] != '.') return 0;
1589 return (memcmp(name
, mi
->name
, mnlen
) == 0);
1593 //==========================================================================
1597 // close current module
1599 //==========================================================================
1600 static void moduleClose (const char *mname
) {
1601 if (mname
&& !mname
[0]) mname
= NULL
;
1603 if (!mname
) fatal("trying to close unopened module");
1604 fatal("trying to close unopened module '%s'", mname
);
1606 if (mname
&& strcmp(mname
, currModule
->name
)) fatal("invalid module name in ENDMODULE; got '%s', expected '%s'", mname
, currModule
->name
);
1608 // +3DOS bootsector?
1609 if (strEquCI(currModule
->name
, "PLUS3BOOT")) {
1610 // append bootsector chunk
1613 if (!findChunkFrom(start
, &start
, &len
)) fatal("no +3DOS bootsector chunk");
1614 if (start
!= 0xFE10) fatal("+3DOS bootsector chunk must be 'org'ed and #FE10");
1615 if (len
> 512-16) fatal("+3DOS bootsector chunk is too big");
1618 OutDataFile
*off
= appendDataData(memory
+(unsigned)start
, (unsigned)start
, (unsigned)len
);
1619 if (!off
) fatal("cannot append +3DOS bootsector");
1620 off
->name
= strdup("");
1621 off
->type
= 1; // special type
1624 if (findChunkFrom(start
, &start
, &len
)) fatal("only +3DOS bootsector chunk is allowed (found #%04X, %d)", (unsigned)start
, len
);
1628 memcpy(memory
, mod_memory_saved
, sizeof(memory
));
1629 memcpy(memused
, mod_memused_saved
, sizeof(memused
));
1630 memcpy(memresv
, mod_memresv_saved
, sizeof(memresv
));
1632 // remove module labels
1633 for (UrLabelInfo
*lbl
= labels
; lbl
; lbl
= lbl
->next
) {
1634 if (!isModuleLabel(currModule
, lbl
->name
)) continue;
1635 //fprintf(stderr, "***HIDDEN: <%s>\n", lbl->name);
1637 lbl
->savedFirstChar
= lbl
->name
[0];
1638 lbl
->name
[0] = '{'; // hide it
1646 ///////////////////////////////////////////////////////////////////////////////
1647 // label getter and utilities
1649 static char *lastSeenGlobalLabel
= NULL
; /* global; malloced */
1652 static char *mangleLabelName (const char *name
) {
1653 static char newname
[MAX_LINE_SIZE
*2+1024];
1654 if (!name
|| !name
[0]) {
1658 // local label or macro label?
1659 if (name
[0] == '.') {
1660 // two dots is a macro label
1661 if (name
[1] == '.') {
1662 if (!name
[2]) fatal("invalid label '%s'", name
);
1663 // this is macro label
1664 if (curmacro
== NULL
) fatal("macro label outside of macro: '%s'", name
);
1665 snprintf(newname
, sizeof(newname
), ".%s.MACLBL%d%s", curmacro
->mac
->name
, curmacronum
, name
+1);
1668 if (!name
[1]) fatal("invalid label '%s'", name
);
1670 if (!lastSeenGlobalLabel
) fatal("local label '%s' without a global", name
);
1671 snprintf(newname
, sizeof(newname
), ".%s%s", lastSeenGlobalLabel
, name
);
1675 // if global is prefixed with '@', it should not be mangled
1676 // this is to allow access to other labels from modules
1677 if (name
[0] == '@') {
1678 if (name
[1] == '@') fatal("double-prefixed label '%s'", name
);
1679 if (!name
[1]) fatal("invalid label '%s'", name
);
1680 snprintf(newname
, sizeof(newname
), "%s", name
+1);
1683 // if no module, nothing to do
1684 // also, do not mangle labels with dots inside:
1685 // those are either already mangled, or call to other modules
1686 if (!currModule
|| strchr(name
, '.')) {
1687 snprintf(newname
, sizeof(newname
), "%s", name
);
1690 // this is global unqualified label and we have a module; let's rename it
1691 snprintf(newname
, sizeof(newname
), "%s.%s", currModule
->name
, name
);
1696 // WARNING! this will be invalidated
1697 static char lastFindLabelName
[MAX_LINE_SIZE
*2+1024];
1699 static UrLabelInfo
*findLabel (const char *name
) {
1700 if (!name
|| !name
[0]) fatal("UrAsm internal error: empty name in `findLabel()`");
1701 char *nn
= mangleLabelName(name
);
1702 strcpy(lastFindLabelName
, nn
);
1703 UrLabelInfo
*lbl
= urFindLabel(nn
);
1706 // try non-module label
1707 lbl = urFindLabel(ln);
1714 static UrLabelInfo
*findAddLabel (const char *name
) {
1715 if (!name
|| !name
[0]) fatal("UrAsm internal error: empty name in `findAddLabel()`");
1716 char *nn
= mangleLabelName(name
);
1717 strcpy(lastFindLabelName
, nn
);
1718 UrLabelInfo
*lbl
= urAddLabel(nn
);
1719 if (!lbl
->refFile
) {
1720 lbl
->refLine
= currSrcLine
->lineNo
;
1721 lbl
->refFile
= strdup(currSrcLine
->fname
);
1727 static int lblOptMakeU2
= 0;
1729 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
, int *fixuptype
) {
1731 if (strEquCI(name
, "__URASM_DEFFMT_ANY_DISK")) {
1734 *fixuptype
= UR_FIXUP_NONE
;
1735 return (optWriteType
== 'S' || optWriteType
== '3'); // SCL or +3DOS
1737 if (strEquCI(name
, "__URASM_DEFFMT_SCL")) {
1740 *fixuptype
= UR_FIXUP_NONE
;
1741 return (optWriteType
== 'S');
1743 if (strEquCI(name
, "__URASM_DEFFMT_SCL_MONO")) {
1746 *fixuptype
= UR_FIXUP_NONE
;
1747 return (optWriteType
== 'M');
1749 if (strEquCI(name
, "__URASM_DEFFMT_P3DOS")) {
1752 *fixuptype
= UR_FIXUP_NONE
;
1753 return (optWriteType
== '3');
1755 if (strEquCI(name
, "__URASM_DEFFMT_HOBETA")) {
1758 *fixuptype
= UR_FIXUP_NONE
;
1759 return (optWriteType
== 'H');
1761 if (strEquCI(name
, "__URASM_DEFFMT_TAPE")) {
1764 *fixuptype
= UR_FIXUP_NONE
;
1765 return (optWriteType
== 't');
1767 if (strEquCI(name
, "__URASM_DEFFMT_ANY_SNA")) {
1770 *fixuptype
= UR_FIXUP_NONE
;
1771 return (optWriteType
== 's');
1773 if (strEquCI(name
, "__URASM_DEFFMT_SNA48")) {
1776 *fixuptype
= UR_FIXUP_NONE
;
1777 return (optWriteType
== 's' && optSNA48
);
1779 if (strEquCI(name
, "__URASM_DEFFMT_SNA128")) {
1782 *fixuptype
= UR_FIXUP_NONE
;
1783 return (optWriteType
== 's' && !optSNA48
);
1785 if (strEquCI(name
, "__URASM_DEFFMT_RAW")) {
1788 *fixuptype
= UR_FIXUP_NONE
;
1789 return (optWriteType
== 'r');
1791 if (strEquCI(name
, "__URASM_DEFFMT_DMB")) {
1794 *fixuptype
= UR_FIXUP_NONE
;
1795 return (optWriteType
== 'd');
1798 UrLabelInfo
*lbl
= findLabel(name
);
1801 //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);
1802 errorMsg("using undefined label '%s'", name
);
1807 lbl
= urAddLabel(lastFindLabelName
);
1808 lbl
->type
= (lblOptMakeU2
? LBL_TYPE_VERY_SPECIAL
: LBL_TYPE_UNKNOWN
);
1810 lbl
->refLine
= currSrcLine
->lineNo
;
1811 lbl
->refFile
= strdup(currSrcLine
->fname
);
1812 //fprintf(stderr, "new label: [%s]\n", lbl->name);
1813 //fprintf(stderr, "new label: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
1815 //fprintf(stderr, "label reference: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
1819 *defined
= (lbl
->known
!= 0);
1820 *fixuptype
= lbl
->fixuptype
;
1835 static int isLabelDefinedOrKnown (const char *name
, uint16_t addr
, int qtype
) {
1836 UrLabelInfo
*lbl
= findLabel(name
);
1838 case UR_QTYPE_DEFINED
: return (lbl
? lbl
->known
!= 0 : 0);
1839 case UR_QTYPE_KNOWN
: return (lbl
!= NULL
);
1846 static void fixupOperandCB (const urasm_operand_t
*op
, uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
1848 //static const char *n[4] = {"none", "word", "low", "high"};
1849 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
1850 addFixup(opdestaddr
, opaddr
, fixuptype
, size
);
1855 static int checkLabels (void) {
1857 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
1858 //fprintf(stderr, ":LBL: <%s> (type=%d)\n", c->name, c->type);
1859 if (c
->type
== LBL_TYPE_UNKNOWN
) {
1860 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
1863 if (c
->type
== LBL_TYPE_ASS
) c
->known
= -1;
1865 //if (wasError) longjmp(errJP, 667);
1870 ///////////////////////////////////////////////////////////////////////////////
1871 // expression utils (aka string parsing)
1874 /* skip leading spaces */
1875 /* returns string with spaces skipped */
1876 UR_FORCE_INLINE
char *strSkipSpaces (const char *s
) {
1877 while (*s
&& isSpace(*s
)) ++s
;
1882 /* skip leading spaces */
1883 /* returns string with spaces skipped */
1884 UR_FORCE_INLINE
const char *strSkipSpacesConst (const char *s
) {
1885 while (*s
&& isSpace(*s
)) ++s
;
1890 /* remove trailing spaces from string */
1891 static void strTrimRight (char *s
) {
1892 if (!s
|| !s
[0]) return;
1893 size_t len
= strlen(s
);
1894 while (len
> 0 && isSpace(s
[len
-1])) --len
;
1899 /* skip leading spaces and colons */
1900 /* returns string with spaces skipped */
1901 UR_FORCE_INLINE
char *strSkipSpacesColons (char *s
) {
1902 while (*s
&& (isSpace(*s
) || *s
== ':')) ++s
;
1907 /* remove leading spaces from the current line */
1908 UR_FORCE_INLINE
void removeSpaces (void) {
1909 char *e
= strSkipSpaces(currLine
);
1910 if (e
!= currLine
) memmove(currLine
, e
, strlen(e
)+1);
1914 /* skip leading spaces, and argument (up to, but not including comma or colon) */
1915 /* correctly skip strings */
1916 /* returns string after skipped argument (with trailing spaces skipped) */
1917 static char *skipMacroArg (char *str
) {
1919 char *strstart
= str
;
1921 str
= strSkipSpaces(str
);
1922 if (!str
[0]) return str
;
1923 if (parens
== 0 && (str
[0] == ',' || str
[0] == ':')) return str
;
1924 /* check for "af'" */
1925 if (str
[0] == '\'' && str
-strstart
>= 2 && toLower(str
[-2] == 'a') && toLower(str
[-1] == 'f')) {
1929 if (str
[0] == '(') { ++parens
; continue; }
1930 if (str
[0] == ')') { if (--parens
< 0) parens
= 0; continue; }
1931 /* check for string */
1932 if (str
[0] == '"' || str
[0] == '\'') {
1933 const char qch
= *str
++;
1935 const char ch
= *str
++;
1936 if (ch
== qch
) break;
1937 if (ch
== '\\' && *str
) ++str
;
1946 /* evaluate next numeric expression in input string */
1947 /* returns expression value */
1948 static int32_t getExprArg (int *defined
, int *addr
) {
1950 char *a
= strSkipSpaces(currLine
);
1952 if (!a
[0]) fatal("expression expected");
1953 const char *ee
= urasm_expr(&res
, a
, disp
, defined
, addr
, &error
);
1954 if (error
) fatalUrLib(error
);
1956 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
1957 memmove(currLine
, ee
, strlen(ee
)+1);
1965 /* evaluate next string expression in input string */
1966 /* returns expression value */
1967 static char *getStrExprArgFmt (void) {
1969 int donteval
= 0, defined
= 0;
1970 static char resbuf
[256];
1971 char *a
= strSkipSpaces(currLine
);
1972 if (!a
[0]) fatal("expression expected");
1973 urasm_exprval_t res
;
1974 urasm_exprval_init(&res
);
1975 const char *ee
= urasm_expr_ex(&res
, a
, disp
, &donteval
, &defined
, &error
);
1976 if (error
) fatalUrLib(error
);
1978 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
1979 memmove(currLine
, ee
, strlen(ee
)+1);
1984 snprintf(resbuf
, sizeof(resbuf
), "%s", res
.str
);
1986 snprintf(resbuf
, sizeof(resbuf
), "%d", res
.val
);
1988 urasm_exprval_clear(&res
);
1993 /* evaluate next numeric expression in input string */
1994 /* there shoild be no other expressions in the string */
1995 /* returns expression value */
1996 static int32_t getOneExprArg (int *defined
, int *addr
) {
1997 int32_t res
= getExprArg(defined
, addr
);
1998 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
2003 /* is next expression a string literal? */
2004 UR_FORCE_INLINE
int isStrArg (void) {
2005 const char *s
= strSkipSpaces(currLine
);
2006 return (s
[0] == '"' || s
[0] == '\'');
2010 /* check of we reached end of operator */
2011 UR_FORCE_INLINE
int isOperatorEnd (void) {
2012 const char *s
= strSkipSpaces(currLine
);
2013 return (s
[0] == 0 || s
[0] == ':');
2017 /* check of we reached end of operator */
2018 UR_FORCE_INLINE
int isLineEnd (void) {
2019 const char *s
= strSkipSpaces(currLine
);
2020 return (s
[0] == 0 || s
[0] == ';');
2024 /* parse string argument from input string */
2025 /* returns parsed string */
2026 static char *getStrArg (int *lenp
) {
2028 char *a
= strSkipSpaces(currLine
);
2030 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
2031 res
= parseStr(&a
, qCh
, lenp
);
2033 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
2034 memmove(currLine
, a
, strlen(a
)+1);
2042 /* parse string argument from input string; allows math after the string */
2043 /* returns parsed string */
2044 /* returns NULL and math expr in `lenp` */
2045 static char *getStrExprArg (int *lenp
, int isWord
) {
2046 char *a
= strSkipSpaces(currLine
);
2047 const char qCh
= *a
++;
2048 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
2049 char *res
= parseStr(&a
, qCh
, lenp
);
2050 if (!res
[0]) fatal("invalid empty string");
2052 if (a
[0] != ',' && a
[0] != ':' && strlen(res
) <= 2) {
2053 memmove(currLine
, a
, strlen(a
)+1);
2054 // "ab" -- 'a' will be in low (first) byte
2055 // 'ab' -- 'a' will be in high (second) byte
2060 sval
= (uint8_t)(res
[0]&0xffU
)|(((uint8_t)(res
[1]&0xffU
))<<8);
2062 sval
= (uint8_t)(res
[1]&0xffU
)|(((uint8_t)(res
[0]&0xffU
))<<8);
2066 sval
= (uint8_t)(res
[0]&0xffU
);
2068 //fprintf(stderr, "SMATH:000: str=<%s> (sval=%d); <%s>\n", res, sval, currLine);
2070 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2071 int32_t xadd
= getExprArg(&defined
, &fixuptype
);
2072 //fprintf(stderr, "SMATH:001: str=<%s> (sval=%d; xadd=%d); <%s>\n", res, sval, xadd, currLine);
2073 if (pass
> 0 && !defined
) fatal("undefined operand");
2075 if (currLine
[0] && currLine
[0] != ',' && currLine
[0] != ':') fatal("bad string expression");
2077 if (lenp
) *lenp
= sval
;
2079 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
2080 memmove(currLine
, a
, strlen(a
)+1);
2089 /* get identifier (and lowercase it) */
2090 static char *getOneIdArgLo (void) {
2091 static char res
[MAX_LINE_SIZE
+128];
2093 char *a
= strSkipSpaces(currLine
);
2094 memset(res
, 0, sizeof(res
));
2095 if (!a
[0]) fatal("identifier expected");
2096 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
2097 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
2099 if (p
-res
> 120) fatal("identifier too long: %s", res
);
2100 while (*a
&& isSpace(*a
)) ++a
;
2102 memmove(currLine
, a
, strlen(a
)+1);
2103 if (currLine
[0] == ';') currLine
[0] = 0;
2104 if (currLine
[0]) fatal("extra arguments");
2108 if (!res
[0]) fatal("identifier expected");
2109 for (char *t
= res
; *t
; ++t
) *t
= toLower(*t
);
2114 /* get label argument */
2115 static char *getLabelArg (int checkdelim
) {
2116 static char res
[MAX_LINE_SIZE
+128];
2118 char *a
= strSkipSpaces(currLine
);
2119 memset(res
, 0, sizeof(res
));
2120 if (!a
[0]) fatal("label expected");
2121 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
2122 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
2124 if (p
-res
> 120) fatal("label name too long: %s", res
);
2125 while (*a
&& isSpace(*a
)) ++a
;
2127 if (checkdelim
&& a
[0] != ',' && a
[0] != ':') fatal("bad label expression");
2128 memmove(currLine
, a
, strlen(a
)+1);
2132 if (!res
[0]) fatal("label expected");
2137 /* get label argument, and ensure that it is the last one */
2138 static char *getOneLabelArg (void) {
2139 char *res
= getLabelArg(1);
2140 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
2145 /* returns ',' or 0 */
2146 static char eatComma (void) {
2147 char *a
= strSkipSpaces(currLine
);
2148 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
2149 if (a
[0] == ':') return 0;
2150 if (a
[0] != ',') fatal("invalid expression: ',' expected");
2151 for (++a
; *a
&& isSpace(*a
); ++a
) {}
2152 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
2153 memmove(currLine
, a
, strlen(a
)+1);
2158 static int checkDelim (char dm
) {
2159 char *a
= strSkipSpaces(currLine
);
2160 if (a
[0] != dm
) return 0;
2161 for (++a
; *a
&& isSpace(*a
); ++a
) {}
2162 if (!a
[0]) { currLine
[0] = '\0'; return 1; }
2163 memmove(currLine
, a
, strlen(a
)+1);
2168 static void matchDelim (char dm
) {
2169 if (!checkDelim(dm
)) fatal("invalid expression: '%c' expected", dm
);
2173 static int checkIdentCI (const char *ident
) {
2174 ur_assert(ident
&& ident
[0]);
2175 char *a
= strSkipSpaces(currLine
);
2176 while (*a
&& *ident
) {
2177 char c0
= *a
++; if (c0
>= 'A' && c0
<= 'Z') c0
= c0
- 'A' + 'a';
2178 char c1
= *ident
++; if (c1
>= 'A' && c1
<= 'Z') c1
= c1
- 'A' + 'a';
2179 if (c0
!= c1
) return 0;
2181 if (ident
[0] != 0) return 0;
2182 if (*a
== '_' || isAlphaDigit(*a
)) return 0;
2183 while (*a
&& isSpace(*a
)) a
+= 1;
2185 memmove(currLine
, a
, strlen(a
)+1);
2193 ///////////////////////////////////////////////////////////////////////////////
2196 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
2197 char *ep
= strSkipSpacesColons(currLine
);
2198 memmove(currLine
, ep
, strlen(ep
)+1);
2202 static void checkExprEnd (void) {
2203 char *ep
= strSkipSpaces(currLine
);
2204 memmove(currLine
, ep
, strlen(ep
)+1);
2205 if (currLine
[0] && currLine
[0] != ':') fatal("end of expression expected");
2209 static void checkOperatorEnd (void) {
2210 char *ep
= strSkipSpaces(currLine
);
2211 memmove(currLine
, ep
, strlen(ep
)+1);
2212 if (currLine
[0]) fatal("end of operator expected");
2216 /* remove label from curLine */
2217 static void removeLabel (void) {
2218 char *ep
= currLine
;
2219 if (ep
[0] && !isSpace(ep
[0])) for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {} // skip text
2220 // skip spaces and colons
2221 ep
= strSkipSpacesColons(ep
);
2222 memmove(currLine
, ep
, strlen(ep
)+1);
2226 static int labelDoEQU (const char *lblname
, const char *value
) {
2227 static char n2
[256];
2230 if (value
== NULL
|| lblname
== NULL
|| !lblname
[0] || strlen(lblname
) > 255 || strlen(value
) >= MAX_LINE_SIZE
) return -1;
2231 memset(n2
, 0, sizeof(n2
));
2232 strcpy(n2
, lblname
);
2233 if (!urasm_is_valid_name(n2
)) return -1; // this is not a valid label, get out of here
2234 // check if this can be an instruction
2235 lbl
= urAddLabel(lblname
);
2236 if (!lbl
->refFile
) {
2238 lbl
->refFile
= strdup("artificially-defined-label");
2240 //fprintf(stderr, "labelDoEQU: <%s>=<%s>\n", lblname, value);
2241 strcpy(currLine
, value
);
2243 int defined
= 1, addr
= UR_FIXUP_NONE
;
2244 int32_t res
= getOneExprArg(&defined
, &addr
);
2245 lbl
->type
= LBL_TYPE_EQU
;
2246 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
2251 return -1; //fatal("can't calculate label %s", lbl->name);
2258 static void processLabel (void) {
2261 static char n2
[256];
2263 int noLocAff
= 0, doEQU
= 0;
2264 //fprintf(stderr, "LINE %5d: <%s> from <%s>\n", (currSrcLine ? currSrcLine->lineNo : 0), currLine, (currSrcLine ? currSrcLine->fname : "<nowhere>"));
2265 memset(n2
, 0, sizeof(n2
));
2267 currSeenLabel
[0] = 0;
2269 if (!currLine
[0] || isSpace(currLine
[0]) || currLine
[0] == ':') {
2270 // this may be " id = smth" or " id equ smth"
2273 while (isSpace(*ep
)) ++ep
;
2274 if (ep
[0] == ':' || !ep
[0] || (!isAlpha(ep
[0]) && ep
[0] != '_' && ep
[0] != '.' && ep
[0] != '@')) {
2275 removeLabel(); // removeLabel() removes any spaces, etc
2278 // this looks like a label; check for '=' or 'equ'
2279 while (isAlphaDigit(ep
[0]) || ep
[0] == '_' || ep
[0] == '.' || ep
[0] == '@') ++ep
;
2281 // skip trailing spaces
2282 while (isSpace(*ep
)) ++ep
;
2286 } else if (isSpace(*nn
)) {
2288 argstart
= strIsCommand("EQU", ep
);
2293 removeLabel(); // removeLabel() removes any spaces, etc
2296 // remove leading spaces from name
2300 while (isSpace(*ep
)) ++ep
;
2301 if (ep
>= nn
) fatal("internal compiler error");
2302 if (nn
-ep
> 120) fatal("label too long");
2303 memset(n2
, 0, sizeof(n2
));
2304 memmove(n2
, ep
, nn
-ep
);
2305 if (urFindOp(n2
) || !urasm_is_valid_name(n2
)) {
2306 //fatal("invalid label name");
2307 removeLabel(); // removeLabel() removes any spaces, etc
2310 // remove label name
2311 memmove(currLine
, argstart
, strlen(argstart
)+1);
2313 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
2314 lbl
= findAddLabel(n2
);
2315 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2316 if (doEQU
&& pass
== 0 && lbl
->type
!= LBL_TYPE_UNKNOWN
) {
2317 fatal("duplicate label '%s'", lbl
->name
);
2319 int defined
= 1, addr
= UR_FIXUP_NONE
;
2320 int32_t res
= getOneExprArg(&defined
, &addr
);
2321 lbl
->type
= (doEQU
? LBL_TYPE_EQU
: LBL_TYPE_ASS
);
2322 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
2327 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
2334 for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {}
2335 if (ep
-currLine
> 120) fatal("label too long");
2338 memset(n2
, 0, sizeof(n2
));
2339 memmove(n2
, currLine
, ep
-currLine
);
2341 ep
= strSkipSpaces(ep
);
2342 if (*ep
!= ':') return; // this must be an instruction, process it
2344 if (!urasm_is_valid_name(n2
)) return; // this is not a valid label, get out of here
2347 if (findMacro(n2
)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
2349 // check if this can be instruction
2350 //ep = strSkipSpaces(ep);
2351 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
2352 // ok, we got a good label
2354 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
2355 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (currSrcLine ? currSrcLine->lineNo : 0));*/
2356 lbl
= findAddLabel(n2
);
2357 //printf("new: [%s]\n", lbl->name);
2360 if (currLine
[0] == '=') {
2362 argstart
= currLine
+1;
2365 argstart
= strIsCommand("EQU", currLine
);
2367 if (!argstart
|| doEQU
) {
2368 if (pass
== 0 && lbl
->type
!= LBL_TYPE_UNKNOWN
) fatal("duplicate label '%s'", lbl
->name
);
2371 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2374 memmove(currLine
, argstart
, strlen(argstart
)+1);
2376 if (lbl
->type
!= LBL_TYPE_UNKNOWN
&& lbl
->type
!= LBL_TYPE_ASS
) {
2377 fatal("duplicate label '%s'", lbl
->name
);
2380 int defined
= 1, addr
= UR_FIXUP_NONE
;
2381 int32_t res
= getOneExprArg(&defined
, &addr
);
2382 lbl
->type
= (doEQU
? LBL_TYPE_EQU
: LBL_TYPE_ASS
);
2383 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
2388 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
2394 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d); disp=#%04X; pc=#%04X\n", lbl->name, doEQU, disp, pc);
2396 // update last seen global
2397 if (lbl
->name
[0] != '{' && lbl
->name
[0] != '.' && !noLocAff
) {
2398 //fprintf(stderr, "**: LASTGLOB: <%s>\n", lbl->name);
2399 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
2400 lastSeenGlobalLabel
= strdup(lbl
->name
);
2402 lbl
->type
= LBL_TYPE_CODE
;
2405 lbl
->fixuptype
= UR_FIXUP_WORD
;
2407 snprintf(currSeenLabel
, sizeof(currSeenLabel
), "%s", n2
);
2411 ///////////////////////////////////////////////////////////////////////////////
2412 // instruction finder (in source)
2414 /* array ends with NULL */
2415 /* returns line or NULL */
2416 /* iidx will be set to found instruction number */
2417 static __attribute__((sentinel
)) SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
2418 if (iidx
) *iidx
= -1;
2419 for (SourceLine
*cur
= currSrcLine
; cur
; cur
= cur
->next
) {
2421 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
2423 for (int f
= 0; ;++f
) {
2424 const char *name
= va_arg(ap
, const char *);
2427 if (strIsCommand(name
, cur
->line
)) {
2429 if (iidx
) *iidx
= f
;
2439 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
2440 return findNextInstructionFromList(NULL
, name
, NULL
);
2444 ///////////////////////////////////////////////////////////////////////////////
2447 ///////////////////////////////////////////////////////////////////////////////
2448 static void writeFixups (void) {
2449 void writeFixupList (FILE *fo
, int cnt
, const char *lbl
, int (*chk
)(const FixupItem
*)) {
2452 fprintf(fo
, "%s:\n", lbl
);
2453 if (optFixupType
== 1) fprintf(fo
, " dw %d ; count\n", cnt
);
2454 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
2456 fprintf(fo
, " dw #%04X\n", fx
->opaddr
-prevaddr
);
2457 if (optFixupType
== 2) {
2458 prevaddr
= fx
->opaddr
;
2462 if (optFixupType
== 2 || optFixupType
== 3) fprintf(fo
, " dw 0 ; end marker\n");
2466 if (optFixupType
== 0) {
2467 /* simple text file */
2468 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.txt");
2469 printf("writing fixups to '%s'...\n", fname
);
2470 FILE *fo
= fopen(fname
, "w");
2472 if (fo
== NULL
) fatal("can't write fixup file");
2473 fprintf(fo
, "; addr dadr sz\n");
2474 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
2475 static const char type
[4] = "NWLH";
2476 fprintf(fo
, "%c #%04x #%04x %d\n", type
[fx
->fixuptype
], fx
->opaddr
, fx
->opdestaddr
, fx
->size
);
2480 /* various asm formats */
2481 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.zas");
2482 printf("writing fixups to '%s'...\n", fname
);
2483 FILE *fo
= fopen(fname
, "w");
2484 int cntw
= 0, cntwl
= 0, cntwh
= 0, cntbl
= 0, cntbh
= 0;
2486 if (fo
== NULL
) fatal("can't write fixup file");
2487 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
2488 if (fx
->fixuptype
== UR_FIXUP_WORD
) { ++cntw
; continue; }
2489 switch (fx
->fixuptype
) {
2490 case UR_FIXUP_LOBYTE
: if (fx
->size
== 2) ++cntwl
; else ++cntbl
; break;
2491 case UR_FIXUP_HIBYTE
: if (fx
->size
== 2) ++cntwh
; else ++cntbh
; break;
2494 writeFixupList(fo
, cntw
, "fixup_table_w", lambda(int, (const FixupItem
*fx
) {
2495 return (fx
->fixuptype
== UR_FIXUP_WORD
);
2497 writeFixupList(fo
, cntwl
, "fixup_table_wl", lambda(int, (const FixupItem
*fx
) {
2498 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 2);
2500 writeFixupList(fo
, cntwh
, "fixup_table_wh", lambda(int, (const FixupItem
*fx
) {
2501 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 2);
2503 writeFixupList(fo
, cntbl
, "fixup_table_bl", lambda(int, (const FixupItem
*fx
) {
2504 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 1);
2506 writeFixupList(fo
, cntbh
, "fixup_table_bh", lambda(int, (const FixupItem
*fx
) {
2507 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 1);
2514 ///////////////////////////////////////////////////////////////////////////////
2515 /* return 'found' flag */
2516 static int findChunkFrom (int addr
, int *start
, int *len
) {
2517 if (addr
< 0) addr
= 0;
2518 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) {}
2519 if (addr
> 65535) return 0;
2521 for (; addr
<= 65535 && memused
[addr
]; ++addr
) {}
2522 *len
= addr
-(*start
);
2527 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
2528 for (; buflen
>= 2; buflen
-= 2) {
2530 uint8_t byte
= *buf
++;
2534 for (cnt
= byte
; cnt
>= 0; --cnt
, ++addr
, --buflen
, ++buf
) {
2535 if (!memused
[addr
]) putByte(addr
, *buf
);
2536 if (addr
== 0xffff) return;
2540 for (; cnt
> 0; --cnt
, ++addr
) {
2541 if (!memused
[addr
]) putByte(addr
, byte
);
2542 if (addr
== 0xffff) return;
2549 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
2550 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
2551 if (bxor
) *bxor
= (*bxor
)^b
;
2556 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
2557 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
2558 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
2563 ///////////////////////////////////////////////////////////////////////////////
2566 static int saveSna (const char *fname
, int as48
) {
2567 char *fn
;// = malloc(strlen(fname)+16);
2572 //fn = strprintf("%s/%s.sna", optOutputDir, fname);
2573 fn
= buildOutputFileName(fname
, "sna");
2574 fo
= fopen(fn
, "wb");
2576 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
2577 printf("out: %s.sna\n", fname
);
2578 memmove(regs
, ursna48
, 27);
2581 /* push new address */
2582 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
2584 putByte(sp
, ent
&0xFFU
);
2585 putByte(sp
+1, (ent
>>8)&0xFFU
);
2586 regs
[23] = sp
&0xFFU
;
2587 regs
[24] = (sp
>>8)&0xFFU
;
2589 fwrite(regs
, 27, 1, fo
);
2590 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
2591 fwrite(memory
+16384, 49152, 1, fo
);
2594 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
2596 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
2597 //fprintf(stderr, "%d\n", memused[sp]);
2598 if (memused
[sp
] || memused
[(sp
+1)&0xffff]) {
2599 fprintf(stderr
, "FATAL: can't save snapshot!\n");
2603 if (fwrite(ursna48
, 27, 1, fo
) != 1) goto error
;
2604 rleUnpack(16384, (const uint8_t *)ursna48
+27, sizeof(ursna48
)-27);
2605 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
2607 sprintf(abuf
, "%05d", clrAddr
);
2608 memcpy(memory
+23762, abuf
, 5);
2609 sprintf(abuf
, "%05d", ent
);
2610 memcpy(memory
+23773, abuf
, 5);
2612 if (fwrite(memory
+16384, 49152, 1, fo
) != 1) goto error
;
2615 int addr
= memory
[sp
]+256*memory
[(sp
+1)&0xffff];
2617 if (fWriteWord(fo
, addr
, NULL
) != 0) goto error
; // PC
2618 if (fWriteByte(fo
, 0x10, NULL
) != 0) goto error
; // 7FFD
2619 if (fWriteByte(fo
, 0, NULL
) != 0) goto error
; // TR-DOS inactive
2621 memset(memory
, 0, 16384);
2622 for (int f
= 1; f
< 8; ++f
) {
2623 if (f
!= 2 && f
!= 5) {
2624 if (fwrite(memory
, 16384, 1, fo
) != 1) goto error
;
2638 ///////////////////////////////////////////////////////////////////////////////
2641 static void saveRaw (const char *basename
) {
2642 char *fname
= NULL
;// = malloc(strlen(basename)+16);
2646 while (findChunkFrom(start
, &start
, &len
)) {
2647 if (fname
!= NULL
) free(fname
);
2648 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, (optTapExt
? "tap" : "bin"));
2649 fo
= fopen(fname
, "wb");
2651 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
2653 printf("out: %s\n", fname
);
2654 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
2655 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2664 if (fname
!= NULL
) free(fname
);
2668 ///////////////////////////////////////////////////////////////////////////////
2671 typedef struct __attribute__((packed
)) __attribute__((gcc_struct
)) {
2676 uint8_t zero
; // always zero
2677 uint8_t secLen
; // length in sectors
2682 static uint16_t calcHobSum (const void *hdr
) {
2683 const uint8_t *buf
= (const uint8_t *)hdr
;
2686 for (unsigned int f
= 0; f
< 15; ++f
) res
+= ((uint16_t)buf
[f
])*257+f
;
2691 static void saveHob (const char *basename
) {
2692 char *fname
= NULL
;//malloc(strlen(basename)+16);
2697 while (findChunkFrom(start
, &start
, &len
)) {
2698 if (fname
!= NULL
) free(fname
);
2699 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, "$C");
2700 fo
= fopen(fname
, "wb");
2702 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
2705 char tmpbuf
[sizeof(hdr
.name
)*2];
2706 printf("out: %s\n", fname
);
2707 memset(&hdr
, 0, sizeof(hdr
));
2708 memset(&hdr
.name
, 32, 8);
2709 snprintf(tmpbuf
, sizeof(tmpbuf
), "%c%04X", basename
[0], start
);
2710 while (strlen(tmpbuf
) < sizeof(hdr
.name
)) strcat(tmpbuf
, " "); /* sorry! */
2711 memcpy(hdr
.name
, tmpbuf
, sizeof(hdr
.name
));
2715 hdr
.secLen
= (len
+255)/256;
2716 hdr
.checksum
= calcHobSum(&hdr
);
2717 if (fwrite(&hdr
, sizeof(hdr
), 1, fo
) != 1) {
2718 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2723 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
2724 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2731 if (fwrite(&hdr
.zero
, 1, 1, fo
) != 1) {
2732 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2738 //fprintf(stderr, ":%d\n", len%256);
2744 if (fname
!= NULL
) free(fname
);
2748 // ////////////////////////////////////////////////////////////////////////// //
2751 uint8_t dir
[14*256];
2753 uint32_t currfstartpos
;
2760 static const uint8_t monoldr_code
[169] = {
2761 0x00,0x00,0xff,0xf0,0xff,0xf0,0xff,0xf0,0xff,0xf3,0x01,0xfd,0x7f,0x3e,0x10,0xed,
2762 0x79,0xaf,0x21,0x00,0x58,0x11,0x01,0x58,0x01,0xff,0x02,0xfb,0x76,0xf3,0xd3,0xfe,
2763 0x36,0x00,0xed,0xb0,0x21,0x00,0x40,0x11,0x01,0x40,0x01,0x00,0x18,0x77,0xed,0xb0,
2764 0xfb,0x76,0xf3,0x3b,0x3b,0xe1,0xe5,0x11,0xce,0xff,0x19,0x7e,0x23,0x5e,0x23,0x56,
2765 0x23,0xc1,0xe5,0xd5,0x69,0x60,0x01,0x35,0x00,0x09,0x4f,0x87,0x81,0x01,0x43,0x00,
2766 0x81,0x4f,0x3e,0x00,0x88,0x47,0xed,0xb0,0xd1,0xe1,0xd5,0x01,0xff,0x03,0x13,0xed,
2767 0xa0,0xed,0xa0,0x13,0x10,0xf8,0xc9,0x31,0xa5,0xa5,0xfb,0x21,0x5a,0x5a,0xe5,0x21,
2768 0x9a,0x02,0xe5,0x76,0x3b,0x3b,0xe1,0x01,0x35,0x00,0x09,0x0e,0x00,0x7e,0xb7,0x28,
2769 0x23,0x23,0x5e,0x23,0x56,0x23,0xe5,0xeb,0x06,0x08,0xb8,0x30,0x01,0x47,0x90,0xf5,
2770 0xc5,0xe5,0xed,0x5b,0xf4,0x5c,0x0e,0x05,0xcd,0x13,0x3d,0xe1,0xc1,0x09,0xf1,0x20,
2771 0xe7,0xe1,0x18,0xd9,0xe1,0xd1,0xf9,0xeb,0xe9,
2775 static void sclInit (SCLFile
*scl
) {
2776 memset(scl
, 0, sizeof(*scl
));
2777 scl
->datasize
= 2*80*16*256; /* maximum disk size */
2778 scl
->data
= malloc(scl
->datasize
);
2779 memset(scl
->data
, 0, scl
->datasize
);
2780 scl
->currfstartpos
= 0xffffffffu
;
2784 static void sclClear (SCLFile
*scl
) {
2786 if (scl
->data
) free(scl
->data
);
2787 memset(scl
, 0, sizeof(*scl
));
2788 scl
->currfstartpos
= 0xffffffffu
;
2792 static void sclStartFile (SCLFile
*scl
, const char *name
, char type
, uint16_t v0
, uint16_t v1
) {
2793 if (scl
->fcount
== 255) {
2794 fprintf(stderr
, "FATAL: too many files in SCL!\n");
2797 if (scl
->currfstartpos
!= 0xffffffffu
) {
2798 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
2802 while (nlen
< 8 && name
&& name
[nlen
]) scl
->dir
[scl
->dirpos
++] = name
[nlen
++];
2803 while (nlen
< 8) { scl
->dir
[scl
->dirpos
++] = ' '; ++nlen
; }
2804 scl
->dir
[scl
->dirpos
++] = type
;
2805 scl
->dir
[scl
->dirpos
++] = v0
&0xff;
2806 scl
->dir
[scl
->dirpos
++] = (v0
>>8)&0xff;
2807 scl
->dir
[scl
->dirpos
++] = v1
&0xff;
2808 scl
->dir
[scl
->dirpos
++] = (v1
>>8)&0xff;
2809 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
2810 scl
->currfstartpos
= scl
->datapos
;
2814 static void sclEndFile (SCLFile
*scl
) {
2815 if (scl
->currfstartpos
== 0xffffffffu
) {
2816 fprintf(stderr
, "FATAL: last SCL file already finished!\n");
2819 /* align to sector */
2820 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
2821 const uint32_t fsz
= scl
->datapos
-scl
->currfstartpos
;
2822 if (fsz
> 255*256) {
2823 fprintf(stderr
, "FATAL: SCL file too big!\n");
2826 ur_assert((fsz
&255) == 0);
2827 scl
->dir
[scl
->dirpos
++] = fsz
/256; /* size in sectors */
2828 ur_assert(scl
->dirpos
%14 == 0);
2830 scl
->currfstartpos
= 0xffffffffu
;
2834 static void sclWriteDataForce (SCLFile
*scl
, const void *buf
, size_t len
) {
2836 if (len
> 255*256) {
2837 fprintf(stderr
, "FATAL: SCL file too big!\n");
2840 memcpy(scl
->data
+scl
->datapos
, buf
, len
);
2841 scl
->datapos
+= len
;
2845 static void sclWriteData (SCLFile
*scl
, const void *buf
, size_t len
) {
2846 if (scl
->currfstartpos
== 0xffffffffu
) {
2847 fprintf(stderr
, "FATAL: no open SCL file!\n");
2850 sclWriteDataForce(scl
, buf
, len
);
2854 static void sclWriteByte (SCLFile
*scl
, uint8_t b
) {
2855 sclWriteData(scl
, &b
, 1);
2859 static void sclWriteWord (SCLFile
*scl
, uint16_t w
) {
2861 sclWriteData(scl
, &b
, 1);
2863 sclWriteData(scl
, &b
, 1);
2867 static int sclFileWrite (FILE *fo
, const void *buf
, size_t count
, uint32_t *checksum
) {
2868 if (!count
) return 0;
2869 const uint8_t *p
= (const uint8_t *)buf
;
2870 if (checksum
) for (size_t n
= count
; n
--; ++p
) *checksum
+= (uint32_t)(*p
);
2871 if (fwrite(buf
, count
, 1, fo
) != 1) return -1;
2877 static int sclSaveToFile (FILE *fo
, SCLFile
*scl
) {
2878 if (scl
->currfstartpos
!= 0xffffffffu
) {
2879 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
2882 const char *sign
= "SINCLAIR";
2883 uint32_t checksum
= 0;
2885 if (sclFileWrite(fo
, sign
, 8, &checksum
) != 0) return -1;
2886 if (sclFileWrite(fo
, &scl
->fcount
, 1, &checksum
) != 0) return -1;
2888 if (sclFileWrite(fo
, scl
->dir
, scl
->dirpos
, &checksum
) != 0) return -1;
2890 if (sclFileWrite(fo
, scl
->data
, scl
->datapos
, &checksum
) != 0) return -1;
2892 for (unsigned f
= 0; f
< 4; ++f
) {
2893 const uint8_t b
= (checksum
>>(f
*8))&0xff;
2894 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
2901 // ////////////////////////////////////////////////////////////////////////// //
2902 static void saveSCLCargador (SCLFile
*scl
) {
2903 static uint8_t cargador
[16384]; // should be enough for everyone
2906 int start
= 0, len
, pos
;
2908 void putStr (const char *s
) {
2909 for (; *s
; ++s
) cargador
[pos
++] = *s
;
2912 void putNum (int num
) {
2914 sprintf(buf
, "%d", num
);
2922 cargador
[pos
++] = (linenum
>>8)&0xff;
2923 cargador
[pos
++] = linenum
&0xff;
2924 // size (will be fixed later)
2925 cargador
[pos
++] = 0;
2926 cargador
[pos
++] = 0;
2930 if (linestart
>= 0) {
2931 const int size
= pos
-linestart
-4;
2932 cargador
[linestart
+2] = size
&0xff;
2933 cargador
[linestart
+3] = (size
>>8)&0xff;
2943 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
2947 while (findChunkFrom(start
, &start
, &len
)) {
2948 // :RANDOMIZE USR VAL "15619":REM:LOAD "
2949 if (cont
) { putStr(":"); cont
= 0; }
2950 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
2951 // generate chunk name
2952 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
2962 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
2963 if (off
->type
== 1) continue; // skip +3DOS bootsector
2964 if (off
->type
!= 'C') continue;
2965 if (!off
->hasstart
) continue;
2966 // :RANDOMIZE USR VAL "15619":REM:LOAD "
2967 if (cont
) { putStr(":"); cont
= 0; }
2968 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
2969 // generate chunk name
2970 snprintf(cname
, sizeof(cname
), "%s", off
->name
);
2971 char *dotpos
= strchr(cname
, '.');
2973 if (dotpos
) { type
= toUpper(dotpos
[1]); *dotpos
= 0; }
2974 if (type
< 'A' || type
> 'Z') type
= 'C';
2975 for (char *s
= cname
; *s
; ++s
) *s
= toUpper(*s
);
2984 if (cont
) { putStr(":"); cont
= 0; }
2985 // RANDOMIZE USR VAL "xxx"
2986 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"");
2991 //putWord(1); // autostart line
2994 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
2995 sclWriteData(scl
, cargador
, (size_t)pos
);
2996 sclWriteByte(scl
, 0x80);
2997 sclWriteByte(scl
, 0xaa);
2998 sclWriteWord(scl
, 1);
3003 static void saveSCLCargadorMono (SCLFile
*scl
) {
3004 static uint8_t cargador
[16384]; // should be enough for everyone
3007 int start
= 0, len
, pos
;
3009 void putDB (uint8_t v
) {
3010 cargador
[pos
++] = v
;
3013 void putDW (uint16_t v
) {
3014 cargador
[pos
++] = v
&0xff;
3015 cargador
[pos
++] = (v
>>8)&0xff;
3018 void putStr (const char *s
) {
3019 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3022 void putNum (int num
) {
3024 sprintf(buf
, "%d", num
);
3031 cargador
[pos
++] = (linenum
>>8)&0xff;
3032 cargador
[pos
++] = linenum
&0xff;
3033 // size (will be fixed later)
3034 cargador
[pos
++] = 0;
3035 cargador
[pos
++] = 0;
3040 if (linestart
>= 0) {
3041 const int size
= pos
-linestart
-4;
3042 cargador
[linestart
+2] = size
&0xff;
3043 cargador
[linestart
+3] = (size
>>8)&0xff;
3053 // monoloader parameters:
3056 // dw prg_start_addr
3059 const int bcountpos
= pos
;
3061 putDW(0x4000); // to screen$
3062 putDW(0x4200); // stack
3063 putDW(ent
); // program start address
3064 putDW(0x0000); // stack
3066 for (uint32_t f
= 9; f
< (uint32_t)sizeof(monoldr_code
); ++f
) {
3067 putDB(monoldr_code
[f
]);
3070 while (findChunkFrom(start
, &start
, &len
)) {
3071 if (len
> 255*256) fatal("code chunk too big");
3073 putDB(len
/256+(len
%256 ? 1 : 0));
3078 ++cargador
[bcountpos
];
3081 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3082 if (off
->type
== 1) continue; // skip +3DOS bootsector
3083 if (off
->type
!= 'C') continue;
3084 if (!off
->hasstart
) continue;
3085 if (off
->size
> 255*256) fatal("data chunk too big");
3087 putDB(off
->size
/256+(off
->size
%256 ? 1 : 0));
3091 ++cargador
[bcountpos
];
3099 // CLEAR VAL "xxx":RANDOMIZE USR PEEK VAL "23635"+VAL "256"*PEEK VAL "23636"+VAL "14"
3100 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\":");
3101 putStr("\xf9\xc0(\xbe\xb0\"23635\"+\xb0\"256\"*\xbe\xb0\"23636\"+\xb0\"14\")\r");
3105 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
3106 sclWriteData(scl
, cargador
, (size_t)pos
);
3107 sclWriteByte(scl
, 0x80);
3108 sclWriteByte(scl
, 0xaa);
3109 sclWriteWord(scl
, 1);
3115 while (findChunkFrom(start
, &start
, &len
)) {
3116 ssz
+= len
/256+(len
%256 ? 1 : 0);
3117 sclWriteDataForce(scl
, memory
+start
, len
);
3118 /* align to sector */
3119 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
3122 for (unsigned dphase
= 0; dphase
< 2; ++dphase
) {
3124 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3125 if (off
->type
== 1) continue; // skip +3DOS bootsector
3126 if (off
->type
!= 'C') continue;
3128 if (!off
->hasstart
) continue;
3130 if (off
->hasstart
) continue;
3132 if (off
->size
> 255*256) fatal("data chunk too big");
3133 ssz
+= off
->size
/256+(off
->size
%256 ? 1 : 0);
3134 sclWriteDataForce(scl
, off
->data
, off
->size
);
3135 /* align to sector */
3136 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
3140 // fix basic file sector size
3141 scl
->dir
[scl
->dirpos
-1] += ssz
;
3145 static void saveSCL (const char *basename
) {
3149 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3150 char *fname
= buildOutputFileName(basename
, "scl");
3154 while (findChunkFrom(start
, &start
, &len
)) {
3159 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3160 if (off
->type
== 1) continue; // skip +3DOS bootsector
3161 if (off
->type
== 'C') ++fcount
;
3164 if (fcount
&& optRunSCL
) fcount
+= (optRunSCL
> 0 ? 2 : 1); // +loader and boot
3166 fprintf(stderr
, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname
, fcount
);
3170 // create output file
3171 FILE *fo
= fopen(fname
, "wb");
3173 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
3177 // initialise SCL writer
3180 if (fcount
&& optRunSCL
) {
3181 // create simple boot
3182 if (optRunSCL
> 0) {
3183 const uint8_t dasboot
[] = {
3184 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"
3186 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
3187 sclWriteWord(&scl
, 1); // line number
3188 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
3189 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
3191 sclWriteByte(&scl
, 0x80);
3192 sclWriteByte(&scl
, 0xaa);
3194 sclWriteWord(&scl
, 0);
3197 saveSCLCargador(&scl
);
3203 while (findChunkFrom(start
, &start
, &len
)) {
3204 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
3205 sclStartFile(&scl
, cname
, 'C', (unsigned)start
, (unsigned)len
);
3206 sclWriteData(&scl
, memory
+(unsigned)start
, (unsigned)len
);
3212 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3213 if (off
->type
== 1) continue; // skip +3DOS bootsector
3214 if (off
->type
!= 'C') {
3215 fprintf(stderr
, "skipping data file with invalid type '%C'\n", off
->type
);
3218 snprintf(cname
, sizeof(cname
), "%s", off
->name
);
3219 char *dotpos
= strchr(cname
, '.');
3221 if (dotpos
) { type
= toUpper(dotpos
[1]); *dotpos
= 0; }
3222 if (type
< 'A' || type
> 'Z') type
= 'C';
3223 for (char *s
= cname
; *s
; ++s
) *s
= toUpper(*s
);
3224 sclStartFile(&scl
, cname
, type
, (unsigned)off
->start
, (unsigned)off
->size
);
3225 sclWriteData(&scl
, off
->data
, (unsigned)off
->size
);
3229 if (sclSaveToFile(fo
, &scl
) < 0) {
3230 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
3237 printf("out: %s\n", fname
);
3238 if (fname
!= NULL
) free(fname
);
3242 static void saveSCLMono (const char *basename
) {
3245 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3246 char *fname
= buildOutputFileName(basename
, "scl");
3248 // count total size in sectors
3249 int secsize
= 1; // for loader
3251 while (findChunkFrom(start
, &start
, &len
)) {
3252 secsize
+= len
/256+(len
%256 ? 1 : 0);
3256 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3257 if (off
->type
== 1) continue; // skip +3DOS bootsector
3258 if (off
->type
== 'C') {
3259 secsize
+= off
->size
/256+(off
->size
%256 ? 1 : 0);
3263 if (secsize
> 254) {
3264 fprintf(stderr
, "ERROR: can't write file '%s' (too many sectors: %d)!\n", fname
, secsize
);
3268 // create output file
3269 FILE *fo
= fopen(fname
, "wb");
3271 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
3275 // initialise SCL writer
3279 // create simple boot
3280 if (optRunSCL
> 0) {
3281 const uint8_t dasboot
[] = {
3282 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"
3284 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
3285 sclWriteWord(&scl
, 1); // line number
3286 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
3287 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
3289 sclWriteByte(&scl
, 0x80);
3290 sclWriteByte(&scl
, 0xaa);
3292 sclWriteWord(&scl
, 0);
3296 saveSCLCargadorMono(&scl
);
3298 if (sclSaveToFile(fo
, &scl
) < 0) {
3299 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
3306 printf("out: %s\n", fname
);
3307 if (fname
!= NULL
) free(fname
);
3311 // ////////////////////////////////////////////////////////////////////////// //
3313 int optDskType
= P3DSK_PCW_SS
;
3316 static int saveDSKCargador (P3DiskInfo
*p3d
, int autorun
) {
3317 static uint8_t cargador
[16384]; // should be enough for everyone
3320 int start
= 0, len
, pos
;
3322 void putStr (const char *s
) {
3323 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3326 void putNum (int num
) {
3328 sprintf(buf
, "%d", num
);
3336 cargador
[pos
++] = (linenum
>>8)&0xff;
3337 cargador
[pos
++] = linenum
&0xff;
3338 // size (will be fixed later)
3339 cargador
[pos
++] = 0;
3340 cargador
[pos
++] = 0;
3344 if (linestart
>= 0) {
3345 const int size
= pos
-linestart
-4;
3346 cargador
[linestart
+2] = size
&0xff;
3347 cargador
[linestart
+3] = (size
>>8)&0xff;
3357 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
3359 while (findChunkFrom(start
, &start
, &len
)) {
3362 // generate chunk name
3363 snprintf(cname
, sizeof(cname
), "C%04X.BIN", (unsigned)start
);
3373 // :RANDOMIZE USR VAL "xxx"
3374 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
3378 // build cp/m 8.3 name
3380 if (autorun
) strcpy(pdfname
, "DISK"); else strcpy(pdfname
, "CARGADOR.BAS");
3382 int crerr
= p3dskCreateFile(p3d
, pdfname
);
3384 if (crerr
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3385 if (crerr
!= FLPERR_FILEEXIST
) { fprintf(stderr
, "+3DOS disk error (creating)\n"); return -1; }
3386 p3dskDeleteFiles(p3d
, pdfname
);
3387 crerr
= p3dskCreateFile(p3d
, pdfname
);
3388 if (crerr
< 0) { fprintf(stderr
, "+3DOS disk error (creating1)\n"); return -1; }
3392 if (p3dskOpenFile(p3d
, &nfo
, pdfname
) != FLPERR_OK
) { fprintf(stderr
, "cannot open created +3DOS file '%s'\n", pdfname
); return -1; }
3394 int wrres
= p3dskWriteFile(&nfo
, cargador
, 128, (int)pos
);
3395 if (wrres
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3396 if (wrres
== FLPERR_NOSPACE
) { fprintf(stderr
, "NO FREE SPACE (writing)!\n"); return -1; }
3397 if (wrres
< 0) { fprintf(stderr
, "+3DOS disk writing error (writing)\n"); return -1; }
3399 // write +3DOS header
3400 P3DskFileHeader hdr
;
3401 memset(&hdr
, 0, sizeof(P3DskFileHeader
));
3405 hdr
.filesize
= (uint32_t)(pos
+128);
3406 hdr
.bastype
= 0; // basic
3407 hdr
.baslength
= (uint16_t)pos
;
3409 hdr
.basvarsofs
= (uint16_t)pos
;
3410 if (p3dskWriteFileHeader(&nfo
, &hdr
) != FLPERR_OK
) { fprintf(stderr
, "error writing +3DOS file header\n"); return -1; }
3416 static int saveDSKChunk (P3DiskInfo
*p3d
, const char *name
, const void *data
, int start
, int len
, int p3header
) {
3418 // build cp/m 8.3 name
3420 if (p3dskNormaliseFileName(pdfname
, name
) != FLPERR_OK
) { fprintf(stderr
, "bad +3DOS file name '%s'\n", name
); return -1; }
3422 for (char *s
= pdfname
; *s
; ++s
) if (*s
>= 'a' && *s
<= 'z') s
[0] -= 32;
3424 int crerr
= p3dskCreateFile(p3d
, pdfname
);
3426 if (crerr
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3427 if (crerr
!= FLPERR_FILEEXIST
) { fprintf(stderr
, "+3DOS disk error (creating)\n"); return -1; }
3428 p3dskDeleteFiles(p3d
, pdfname
);
3429 crerr
= p3dskCreateFile(p3d
, pdfname
);
3430 if (crerr
< 0) { fprintf(stderr
, "+3DOS disk error (creating1)\n"); return -1; }
3434 if (p3dskOpenFile(p3d
, &nfo
, pdfname
) != FLPERR_OK
) { fprintf(stderr
, "cannot open created +3DOS file '%s'\n", pdfname
); return -1; }
3436 int wrres
= p3dskWriteFile(&nfo
, data
, (p3header
? 128 : 0), (int)len
);
3437 if (wrres
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3438 if (wrres
== FLPERR_NOSPACE
) { fprintf(stderr
, "NO FREE SPACE (writing)!\n"); return -1; }
3439 if (wrres
< 0) { fprintf(stderr
, "+3DOS disk writing error (writing)\n"); return -1; }
3441 // write +3DOS header
3443 P3DskFileHeader hdr
;
3444 memset(&hdr
, 0, sizeof(P3DskFileHeader
));
3448 hdr
.filesize
= (uint32_t)(len
+128);
3449 hdr
.bastype
= 3; // code
3450 hdr
.baslength
= (uint16_t)len
;
3451 hdr
.basaddr
= (uint16_t)start
;
3453 if (p3dskWriteFileHeader(&nfo
, &hdr
) != FLPERR_OK
) { fprintf(stderr
, "error writing +3DOS file header\n"); return -1; }
3460 static void saveDSK (const char *basename
) {
3461 // create and format the floppy
3462 Floppy
*flp
= flpCreate(0);
3463 if (p3dskFormatDisk(flp
, optDskType
) != FLPERR_OK
) {
3465 fprintf(stderr
, "ERROR: cannot format +3DOS disk\n");
3471 if (p3dskDetectGeom(p3d
.flp
, &p3d
.geom
) < 0) {
3473 fprintf(stderr
, "ERROR: not a +3DOS disk!\n");
3477 int hasbootsector
= 0;
3478 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3479 if (off
->type
== 1) { hasbootsector
= 1; break; }
3483 int haschunks
= findChunkFrom(start
, &start
, &len
);
3485 if (!hasbootsector
&& haschunks
) {
3486 if (saveDSKCargador(&p3d
, optRunDSK
) != 0) {
3494 while (findChunkFrom(start
, &start
, &len
)) {
3496 snprintf(cname
, sizeof(cname
), "C%04X.BIN", (unsigned)start
);
3497 if (saveDSKChunk(&p3d
, cname
, memory
+(unsigned)start
, start
, len
, 1) != 0) {
3505 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3506 if (off
->type
== 1) {
3508 if (!p3d
.geom
.restracks
) {
3509 fprintf(stderr
, "no room for +3DOS bootsector\n");
3512 uint8_t *bootsec
= malloc(512);
3513 if (flpGetSectorData(p3d
.flp
, 0, p3d
.geom
.firstsector
, bootsec
, 512) != FLPERR_OK
) {
3514 fprintf(stderr
, "cannot read +3DOS bootsector\n");
3517 size_t clen
= off
->size
;
3518 if (clen
> 512-16) clen
= 512-16;
3519 if (clen
) memcpy(bootsec
+16, off
->data
, clen
);
3520 if (flpPutSectorData(p3d
.flp
, 0, p3d
.geom
.firstsector
, bootsec
, 512) != FLPERR_OK
) fatal("cannot write +3DOS bootsector");
3521 if (p3dskWriteBootableChecksum(&p3d
) != FLPERR_OK
) fatal("cannot make +3DOS disk bootable");
3522 printf("written +3DOS bootsector (%u bytes of code)\n", off
->size
);
3526 if (off
->type
!= 'C') {
3527 fprintf(stderr
, "skipping data file with invalid type '%C'\n", off
->type
);
3531 if (p3dskNormaliseFileName(pdfname
, off
->name
) != FLPERR_OK
) {
3532 fprintf(stderr
, "skipping data file with invalid name '%s'\n", off
->name
);
3535 saveDSKChunk(&p3d
, pdfname
, off
->data
, off
->start
, off
->size
, off
->hasstart
);
3539 //char *fname = strprintf("%s/%s.dsk", optOutputDir, basename);
3540 char *fname
= buildOutputFileName(basename
, "dsk");
3541 FILE *fo
= fopen(fname
, "wb");
3542 if (!fo
) { fprintf(stderr
, "cannot create disk file '%s'\n", fname
); free(fname
); flpDestroy(flp
); return; }
3543 if (dskSaveDSK(flp
, fo
) != FLPERR_OK
) {
3545 fprintf(stderr
, "error writing disk file '%s'\n", fname
);
3551 printf("out: %s\n", fname
);
3557 ///////////////////////////////////////////////////////////////////////////////
3560 static int saveDMB (const char *fname
) {
3561 char *fn
;// = malloc(strlen(fname)+16);
3566 //fn = strprintf("%s/%s.dmb", optOutputDir, fname);
3567 fn
= buildOutputFileName(fname
, "dmb");
3568 fo
= fopen(fn
, "wb");
3570 if (!fo
) { fprintf(stderr
, "ERROR: can't write DMB file: %s.dmb\n", fname
); return -1; }
3572 while (findChunkFrom(start
, &start
, &len
)) { ++pcnt
; start
+= len
; }
3574 printf("out: %s.dmb\n", fname
);
3575 if (fwrite("DMB1", 4, 1, fo
) != 1) goto error
;
3576 if (fWriteWord(fo
, (optRunDMB
? ent
: 0), NULL
) != 0) goto error
;
3577 if (fWriteWord(fo
, clrAddr
, NULL
) != 0) goto error
;
3578 if (fWriteWord(fo
, pcnt
, NULL
) != 0) goto error
;
3581 while (findChunkFrom(start
, &start
, &len
)) {
3582 if (fWriteWord(fo
, len
, NULL
) != 0) goto error
;
3583 if (fWriteWord(fo
, start
, NULL
) != 0) goto error
;
3584 if (fwrite(memory
+start
, len
, 1, fo
) != 1) goto error
;
3592 fprintf(stderr
, "ERROR: error writing file %s.dmb!\n", fname
);
3597 ///////////////////////////////////////////////////////////////////////////////
3600 static char tapeLoaderName
[16];
3603 static void saveTapCargador (FILE *fo
) {
3605 static uint8_t cargador
[16384]; // should be enough for everyone
3606 int start
= 0, len
, pos
, f
;
3610 void putStr (const char *s
) {
3611 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3614 void putNum (int num
) {
3616 sprintf(buf
, "%d", num
);
3623 cargador
[0] = 0; cargador
[1] = 10;
3624 // size (will be fixed later)
3625 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\""); // CLEAR VAL "xxx"
3627 while (findChunkFrom(start
, &start
, &len
)) {
3629 putStr(":\xef\"\"\xaf");
3632 // additional code files
3633 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3634 if (off
->type
== 1) continue; // skip +3DOS bootsector
3635 if (off
->type
!= 'C' || off
->size
> 65533 || !off
->hasstart
) {
3639 putStr(":\xef\"\"\xaf");
3643 // :RANDOMIZE USR VAL "xxx"
3644 putStr(":\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
3646 cargador
[2] = (pos
-4)&0xff;
3647 cargador
[3] = ((pos
-4)>>8)&0xff;
3649 fWriteWord(fo
, 19, NULL
); // length of header
3651 fWriteByte(fo
, 0, &bxor
); // header block
3652 fWriteByte(fo
, 0, &bxor
); // 'basic' flag
3653 if (tapeLoaderName
[0]) {
3654 for (int f
= 0; f
< 10; ++f
) fWriteByte(fo
, tapeLoaderName
[f
], &bxor
);
3656 fWriteByte(fo
, 'c', &bxor
);
3657 fWriteByte(fo
, 'a', &bxor
);
3658 fWriteByte(fo
, 'r', &bxor
);
3659 fWriteByte(fo
, 'g', &bxor
);
3660 fWriteByte(fo
, 'a', &bxor
);
3661 fWriteByte(fo
, 'd', &bxor
);
3662 fWriteByte(fo
, 'o', &bxor
);
3663 fWriteByte(fo
, 'r', &bxor
);
3664 fWriteByte(fo
, ' ', &bxor
);
3665 fWriteByte(fo
, ' ', &bxor
);
3667 fWriteWord(fo
, pos
, &bxor
); // length
3668 fWriteWord(fo
, 10, &bxor
); // start
3669 fWriteWord(fo
, pos
, &bxor
); // length2
3670 fWriteByte(fo
, bxor
, NULL
);
3672 fWriteWord(fo
, pos
+2, NULL
); // plus type and checkbyte
3674 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
3675 for (f
= 0; f
< pos
; ++f
) fWriteByte(fo
, cargador
[f
], &bxor
);
3676 fWriteByte(fo
, bxor
, NULL
);
3680 static void saveTap (const char *basename
) {
3681 char *fname
;// = malloc(strlen(basename)+16);
3683 int start
= 0, len
, f
;
3687 //fname = strprintf("%s/%s.tap", optOutputDir, basename);
3688 fname
= buildOutputFileName(basename
, "tap");
3689 fo
= fopen(fname
, "wb");
3691 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
3692 printf("out: %s.tap\n", basename
);
3693 if (optRunTape
) saveTapCargador(fo
);
3694 while (findChunkFrom(start
, &start
, &len
)) {
3696 if (tapeLoaderName
[0]) {
3697 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
3698 memcpy(blkname
, tapeLoaderName
, 10);
3700 sprintf(blkname
, "c%04X:%04X", start
, len
);
3702 //printf(" block: %s\n", blkname);
3703 fWriteWord(fo
, 19, NULL
); // length of header
3705 fWriteByte(fo
, 0, &bxor
); // header block
3706 fWriteByte(fo
, 3, &bxor
); // 'code' flag
3707 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
3708 fWriteWord(fo
, len
, &bxor
);
3709 fWriteWord(fo
, start
, &bxor
);
3710 fWriteWord(fo
, 32768, &bxor
);
3711 fWriteByte(fo
, bxor
, NULL
);
3713 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
3715 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
3716 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
3717 fWriteByte(fo
, bxor
, NULL
);
3721 for (int dfpass
= 0; dfpass
< 2; ++dfpass
) {
3723 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3724 if (off
->type
== 1) continue; // skip +3DOS bootsector
3725 if (off
->type
!= 'C' || off
->size
> 65533) {
3726 fprintf(stderr
, "skipping data file with invalid type '%C'\n", off
->type
);
3730 if (!off
->hasstart
) continue;
3732 if (off
->hasstart
) continue;
3734 snprintf(blkname
, 10, "%s", off
->name
);
3735 blkname
[10] = 0; // just in case
3736 while (strlen(blkname
) < 10) strcat(blkname
, " ");
3738 fWriteWord(fo
, 19, NULL
); // length of header
3740 fWriteByte(fo
, 0, &bxor
); // header block
3741 fWriteByte(fo
, 3, &bxor
); // 'code' flag
3742 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
3743 fWriteWord(fo
, off
->size
, &bxor
);
3744 fWriteWord(fo
, off
->start
, &bxor
);
3745 fWriteWord(fo
, 32768, &bxor
);
3746 fWriteByte(fo
, bxor
, NULL
);
3748 fWriteWord(fo
, off
->size
+2, NULL
); // plus type and checkbyte
3750 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
3751 for (f
= 0; f
< off
->size
; ++f
) fWriteByte(fo
, off
->data
[f
], &bxor
);
3752 fWriteByte(fo
, bxor
, NULL
);
3760 ///////////////////////////////////////////////////////////////////////////////
3761 // pseudoinstructions
3763 // note that processCurrentLine() will NOT skip to the next line before
3764 // calling pseudoinstruction handler!
3765 // note that processCurrentLine() will skip current line after calling
3766 // pseudoinstruction handler!
3768 static int wasOrg
= 0;
3769 static int wasClr
= 0;
3772 // ////////////////////////////////////////////////////////////////////////// //
3773 // print message using printf-like syntax
3774 // doesn't print new line
3775 static void processPrintf (FILE *fo
, const char *fmt
) {
3776 if (!fmt
|| !fmt
[0]) return;
3777 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
3778 char *tempstr
= NULL
;
3779 size_t tempsize
= 0;
3780 char *fmtcopy
= malloc(strlen(fmt
)+1);
3781 strcpy(fmtcopy
, fmt
);
3782 char *currfmt
= fmtcopy
;
3790 char *prcs
= strchr(currfmt
, '%');
3791 if (!prcs
|| !prcs
[1]) {
3792 /* no more formatting; print the tail and exit the loop */
3793 fprintf(fo
, "%s", currfmt
);
3798 if (prcs
[1] == '%') {
3802 /* print up to `prcs` */
3803 if (prcs
> currfmt
) {
3804 size_t partlen
= (ptrdiff_t)(prcs
-currfmt
);
3805 if (partlen
+1 > tempsize
) {
3806 tempsize
= ((partlen
+8)|0xff)+1;
3807 tempstr
= realloc(tempstr
, tempsize
);
3809 memcpy(tempstr
, currfmt
, partlen
);
3810 tempstr
[partlen
] = 0;
3811 fprintf(fo
, "%s", tempstr
);
3813 currfmt
= ++prcs
; /* skip percent */
3814 if (!docheck
) continue;
3820 if (*currfmt
== '+' || *currfmt
== '-') sign
= *currfmt
++;
3821 if (sign
!= '-' && *currfmt
== '0') zerofill
= 1;
3822 while (isDigit(*currfmt
)) { width
= width
*10+((*currfmt
)-'0'); ++currfmt
; }
3823 if (width
> 256) width
= 256;
3825 if (!ftype
) break; /* oops */
3826 if (!eatComma()) fatal("out of arguments for string format");
3828 case 's': /* string */
3830 switch (strSkipSpaces(currLine
)[0]) {
3831 case '"': case '\'': /* string literal? */
3832 strarg
= getStrArg(&stlen
);
3834 default: /* expression */
3835 strarg
= getStrExprArgFmt();
3836 stlen
= (int)strlen(strarg
);
3840 if (sign
!= '-' && stlen
< width
) {
3841 int padlen
= width
-stlen
;
3842 memset(tempbuf
, ' ', padlen
);
3843 tempbuf
[padlen
+1] = 0;
3844 fprintf(fo
, "%s", tempbuf
);
3846 fprintf(fo
, "%s", strarg
);
3848 if (sign
== '-' && stlen
< width
) {
3849 int padlen
= width
-stlen
;
3850 memset(tempbuf
, ' ', padlen
);
3851 tempbuf
[padlen
+1] = 0;
3852 fprintf(fo
, "%s", tempbuf
);
3855 case 'd': /* decimal */
3857 exprval
= getExprArg(&defined
, NULL
);
3858 if (width
&& zerofill
) {
3859 fprintf(fo
, "%0*d", width
, exprval
);
3861 fprintf(fo
, "%*d", (sign
!= '-' ? width
: -width
), exprval
);
3863 fprintf(fo
, "%d", exprval
);
3866 case 'c': /* char */
3868 exprval
= getExprArg(&defined
, NULL
);
3869 if (exprval
<= 0 || exprval
== '?' || exprval
> 255) exprval
= '?';
3871 fprintf(fo
, "%*c", (sign
!= '-' ? width
: -width
), exprval
);
3873 fprintf(fo
, "%c", exprval
);
3876 case 'u': /* unsigned */
3878 exprval
= getExprArg(&defined
, NULL
);
3879 if (width
&& zerofill
) {
3880 fprintf(fo
, "%0*u", width
, (unsigned)(exprval
&0xffff));
3882 fprintf(fo
, "%*u", (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
3884 fprintf(fo
, "%u", (unsigned)(exprval
&0xffff));
3887 case 'x': case 'X': /* hex */
3889 exprval
= getExprArg(&defined
, NULL
);
3890 if (width
&& zerofill
) {
3891 fprintf(fo
, (ftype
== 'x' ? "%0*x" : "%0*X"), width
, (unsigned)(exprval
&0xffff));
3893 fprintf(fo
, (ftype
== 'x' ? "%*x" : "%*X"), (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
3895 fprintf(fo
, (ftype
== 'x' ? "%x" : "%X"), (unsigned)(exprval
&0xffff));
3899 if (ftype
<= 0 || ftype
== 127) ftype
= '?';
3900 fatal("invalid format specifier: '%c'", ftype
);
3904 if (tempstr
) free(tempstr
);
3909 ///////////////////////////////////////////////////////////////////////////////
3912 static int piERROR (void) {
3914 char *res
= getStrArg(&len
);
3915 fprintf(stdout
, "*** USER ERROR: ");
3916 processPrintf(stdout
, res
);
3917 fputc('\n', stdout
);
3918 fatal("user error abort");
3919 return PI_SKIP_LINE
;
3923 static int piWARNING (void) {
3925 char *res
= getStrArg(&len
);
3926 fprintf(stdout
, "*** USER WARNING ");
3928 fprintf(stdout
, "at file %s, line %d: ", currSrcLine
->fname
, currSrcLine
->lineNo
);
3930 fprintf(stdout
, "somewhere in time: ");
3932 processPrintf(stdout
, res
);
3933 fputc('\n', stdout
);
3934 return PI_SKIP_LINE
;
3938 ///////////////////////////////////////////////////////////////////////////////
3941 static int piPrintfCommon (int passNo
) {
3942 if (passNo
< 0 || pass
== passNo
) {
3944 char *res
= getStrArg(&len
);
3945 processPrintf(stdout
, res
);
3946 fputc('\n', stdout
);
3948 return PI_SKIP_LINE
;
3952 static int piDISPLAYX (int passNo
, int asHex
) {
3956 char *res
= getStrArg(&len
);
3958 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
3961 int32_t v
= getExprArg(&defined
, NULL
);
3963 if (passNo
< 0 || pass
== passNo
) {
3964 if (asHex
) printf("%04X", (unsigned int)v
);
3965 else printf("%d", v
);
3968 if (!eatComma()) break;
3970 return PI_SKIP_LINE
;
3974 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
3975 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
3976 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
3977 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
3978 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
3979 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
3981 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
3982 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
3983 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
3986 ///////////////////////////////////////////////////////////////////////////////
3989 static int piORG (void) {
3991 int32_t res
= getOneExprArg(&defined
, NULL
);
3993 if (!defined
) fatal("sorry, ORG operand value must be known here");
3994 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
3995 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
4000 if (!wasClr
&& res
> 0) clrAddr
= res
-1;
4002 return PI_CONT_LINE
;
4006 static int piDISP (void) {
4008 int32_t res
= getOneExprArg(&defined
, NULL
);
4010 if (!defined
) fatal("sorry, DISP operand value must be known here");
4011 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
4012 //printf("DISP=%d\n", res);
4014 return PI_CONT_LINE
;
4018 static int piENDDISP (void) {
4019 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
4022 return PI_CONT_LINE
;
4026 static int piENT (void) {
4028 int32_t res
= getOneExprArg(&defined
, NULL
);
4030 //if (!defined) fatal("sorry, ENT operand value must be known here");
4031 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
4033 return PI_CONT_LINE
;
4037 static int piCLR (void) {
4039 int32_t res
= getOneExprArg(&defined
, NULL
);
4041 //if (!defined) fatal("sorry, CLR operand value must be known here");
4042 if (res
< 0 || res
> 65535) fatal("invalid CLR operand value: %d", res
);
4045 return PI_CONT_LINE
;
4049 // RESERVE start, count
4050 static int piRESERVE (void) {
4051 int defined
= 1, start
;
4052 int32_t res
= getExprArg(&defined
, NULL
);
4053 if (!defined
) fatal("sorry, RESERVE operand values must be known here");
4054 if (res
< 0 || res
> 65535) fatal("invalid RESERVE address value: %d", res
);
4056 if (!eatComma()) fatal("RESERVE needs 2 args");
4057 res
= getOneExprArg(&defined
, NULL
);
4058 if (!defined
) fatal("sorry, RESERVE operand values must be known here");
4059 if (res
< 0 || res
> 65535) fatal("invalid RESERVE length value: %d", res
);
4061 if (memused
[start
]) fatal("trying to reserve already used memory at #%04X", start
);
4062 memresv
[start
++] = 1;
4064 return PI_CONT_LINE
;
4068 static int piALIGN (void) {
4072 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
4073 res
= getOneExprArg(&defined
, NULL
);
4074 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
4075 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
4076 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
4077 if (res
> 0 && pc
%res
!= 0) {
4083 return PI_CONT_LINE
;
4087 static int piDISPALIGN (void) {
4089 int32_t res
= getOneExprArg(&defined
, NULL
);
4091 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
4092 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
4093 if (res
> 0 && disp
%res
!= 0) {
4098 return PI_CONT_LINE
;
4102 ///////////////////////////////////////////////////////////////////////////////
4105 // DEFINCR operations
4109 DFI_SAR
, // arith shift, extend bit 7
4110 DFI_SAL
, // arith shift, extend bit 0
4123 typedef struct DefIncrOp_s DefIncrOp
;
4124 struct DefIncrOp_s
{
4126 uint16_t operator; // DFI_xxx
4130 static DefIncrOp
*dfi_list
= NULL
;
4131 //static int defIncr = 0;
4134 static void dfi_free (void) {
4136 DefIncrOp
*c
= dfi_list
;
4143 //FIXME: make this faster
4144 #define DFI_XSHIFT(sop_) do { \
4145 for (uint16_t f = 0; f < opnd; ++f) { \
4146 val = ((val&xmask) sop_ 1)|(val&andmask ? ormask : 0); \
4151 static int32_t dfi_apply (int32_t val
, int isbyte
) {
4152 const uint16_t xmask
= (isbyte
? 0xff : 0xffff);
4154 uint16_t andmask
, ormask
;
4155 for (DefIncrOp
*dop
= dfi_list
; dop
; dop
= dop
->next
) {
4156 uint16_t opnd
= dop
->operand
;
4157 switch (dop
->operator) {
4160 if (opnd
> (isbyte
? 7 : 15)) { val
= 0; break; }
4161 val
= (val
&xmask
)>>opnd
;
4165 if (opnd
> (isbyte
? 7 : 15)) { val
= 0; break; }
4166 val
= (val
&xmask
)<<opnd
;
4171 if (opnd
> 7) { val
= (val
&0x80 ? 0xff : 0x00); break; }
4172 andmask
= ormask
= 0x80;
4174 if (opnd
> 15) { val
= (val
&0x8000 ? 0xffff : 0x0000); break; }
4175 andmask
= ormask
= 0x8000;
4181 if (opnd
> (isbyte
? 7 : 15)) { val
= (val
&0x01 ? 0xff : 0x00); break; }
4182 andmask
= ormask
= 0x01;
4186 opnd
&= (isbyte
? 7 : 15);
4189 ormask
= (isbyte
? 0x80 : 0x8000);
4193 opnd
&= (isbyte
? 7 : 15);
4195 andmask
= (isbyte
? 0x80 : 0x8000);
4218 if (opnd
) val
/= opnd
; else val
= 0;
4221 if (opnd
) val
%= opnd
; else val
= 0;
4230 static int piDEFINCR (void) {
4233 int32_t cnt
= getOneExprArg(&defined
, NULL
);
4234 if (!defined
) fatal("DEFINCR: increment must be defined");
4236 return PI_CONT_LINE
;
4241 char *rstr
= getStrArg(&len
);
4242 if (!rstr
|| !strEquCI(rstr
, "expr")) fatal("invalid DEFINCR command (\"expr\" expected)");
4243 DefIncrOp
*last
= NULL
;
4244 while (eatComma()) {
4246 rstr
= getStrArg(&len
);
4247 if (!rstr
|| len
!= 3) fatal("invalid DEFINCR operator");
4248 DefIncrOp
*dop
= malloc(sizeof(DefIncrOp
));
4249 if (strEquCI(rstr
, "SHR")) dop
->operator = DFI_SHR
;
4250 else if (strEquCI(rstr
, "SHL")) dop
->operator = DFI_SHL
;
4251 else if (strEquCI(rstr
, "SAR")) dop
->operator = DFI_SAR
;
4252 else if (strEquCI(rstr
, "SAL")) dop
->operator = DFI_SAL
;
4253 else if (strEquCI(rstr
, "ROR")) dop
->operator = DFI_ROR
;
4254 else if (strEquCI(rstr
, "ROL")) dop
->operator = DFI_ROL
;
4255 else if (strEquCI(rstr
, "AND")) dop
->operator = DFI_AND
;
4256 else if (strEquCI(rstr
, "XOR")) dop
->operator = DFI_XOR
;
4257 else if (strEquCI(rstr
, "OR")) dop
->operator = DFI_OR
;
4258 else if (strEquCI(rstr
, "ADD")) dop
->operator = DFI_ADD
;
4259 else if (strEquCI(rstr
, "SUB")) dop
->operator = DFI_SUB
;
4260 else if (strEquCI(rstr
, "MUL")) dop
->operator = DFI_MUL
;
4261 else if (strEquCI(rstr
, "DIV")) dop
->operator = DFI_DIV
;
4262 else if (strEquCI(rstr
, "MOD")) dop
->operator = DFI_MOD
;
4263 else fatal("invalid DEFINCR operator '%s'", rstr
);
4265 if (!eatComma()) fatal("DEFINCR: operand expected");
4267 int32_t res
= getExprArg(&defined
, NULL
);
4268 if (!defined
) fatal("DEFINCR: operand must be defined");
4269 if (res
< 0) fatal("DEFINCR: operand must be positive");
4271 dop
->operand
= (uint16_t)res
;
4272 if (last
) last
->next
= dop
; else dfi_list
= dop
;
4277 int32_t cnt
= getOneExprArg(&defined
, NULL
);
4278 if (!defined
) fatal("DEFINCR: increment must be defined");
4283 DefIncrOp
*dop
= malloc(sizeof(DefIncrOp
));
4285 dop
->operator = DFI_ADD
;
4290 cnt
&= 0xffff; //UB, i don't care
4292 DefIncrOp
*dop
= malloc(sizeof(DefIncrOp
));
4294 dop
->operator = DFI_SUB
;
4301 return PI_CONT_LINE
;
4305 static int piDEFBW (int isWord
) {
4308 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
4312 char *rstr
= getStrExprArg(&len
, isWord
);
4314 for (f
= 0; f
< len
; ++f
) {
4315 int32_t b
= (uint8_t)rstr
[f
];
4317 b
= dfi_apply(b
, 1/*isbyte*/);
4318 if (b
< -128 || b
> 255) fatal("invalid operand value: %d", b
);
4319 if (b
< 0) b
+= 256;
4322 if (!eatComma()) break;
4329 res
= getExprArg(&defined
, &fixuptype
);
4333 //int32_t res = getExprArg(&defined, &fixuptype);
4335 if (pass
> 0 && !defined
) fatal("undefined operand");
4337 res
= dfi_apply(res
, !isWord
/*isbyte*/);
4339 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
4340 if (res
< 0) res
+= 65536;
4342 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 2);
4346 switch (fixuptype
) {
4347 case UR_FIXUP_WORD
: /* swapped bytes */
4348 urasm_fixup_operand(NULL
, pc
, disp
, UR_FIXUP_HIBYTE
, 1);
4349 urasm_fixup_operand(NULL
, pc
, disp
+1, UR_FIXUP_LOBYTE
, 1);
4351 case UR_FIXUP_LOBYTE
:
4352 case UR_FIXUP_HIBYTE
:
4353 warningMsg("non-word fixup for reversed word; wtf?!");
4360 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
4361 if (fixuptype
!= UR_FIXUP_NONE
) {
4362 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
4363 urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
4365 if (res
< 0) res
+= 256;
4369 if (!eatComma()) break;
4371 return PI_CONT_LINE
;
4374 static int piDEFB (void) { return piDEFBW(0); }
4375 static int piDEFW (void) { return piDEFBW(1); }
4376 static int piDEFR (void) { return piDEFBW(2); }
4379 static int piDEFS (void) {
4382 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
4383 int32_t res
= getExprArg(&defined
, &fixuptype
);
4385 if (pass
> 0 && !defined
) fatal("undefined operand");
4386 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
4387 if (*currLine
&& currLine
[0] == ',') {
4389 bt
= getExprArg(&defined
, NULL
);
4390 if (pass
> 0 && !defined
) fatal("undefined operand");
4392 bt
= dfi_apply(bt
, 1/*isbyte*/);
4393 if (bt
< -128 || bt
> 255) fatal("invalid operand value: %d", res
);
4394 if (bt
< 0) bt
+= 256;
4395 if (fixuptype
!= UR_FIXUP_NONE
) {
4396 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
4398 for (f
= 0; f
< res
; ++f
) {
4399 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
4403 pc
+= res
; disp
+= res
;
4405 if (!eatComma()) break;
4407 return PI_CONT_LINE
;
4411 /* bit 0: put '\0' */
4412 /* bit 1: set bit 7 of last byte */
4413 /* bit 2: put length */
4414 static int piDEFSTR (int type
) {
4418 char *res
= getStrArg(&len
);
4421 if (len
> 255) fatal("string too long");
4424 for (f
= 0; f
< len
; ++f
) {
4425 uint8_t b
= (uint8_t)res
[f
];
4427 if ((type
&0x02) && f
== len
-1) b
|= 0x80;
4430 if (type
&0x01) emitByte(0);
4433 int32_t v
= getExprArg(&defined
, NULL
);
4435 if (pass
> 0 && !defined
) fatal("undefined expression");
4436 if (!defined
) v
= 0; else v
= dfi_apply(v
, 1/*isbyte*/);
4437 if (v
< -128 || v
> 255) fatal("invalid operand value: %d", v
);
4438 if (v
< 0) v
+= 256;
4441 if (!eatComma()) break;
4443 return PI_CONT_LINE
;
4447 static int piDEFM (void) { return piDEFSTR(0x00); }
4448 static int piDEFZ (void) { return piDEFSTR(0x01); }
4449 static int piDEFX (void) { return piDEFSTR(0x02); }
4450 static int piDEFC (void) { return piDEFSTR(0x04); }
4453 ///////////////////////////////////////////////////////////////////////////////
4456 /* INCBIN "name"[,maxlen[,offset]] */
4457 static int piINCBIN (void) {
4464 char *args
= currLine
;
4466 if (!currLine
[0]) fatal("INCBIN without file name");
4471 } else if (currLine
[0] == '<') {
4479 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4480 int softinclude
= 0;
4481 if (fn
[0] == '?') { softinclude
= 1; ++fn
; while (isSpace(*fn
)) ++fn
; }
4482 if (!fn
[0]) fatal("INCBIN: empty file name");
4484 char *fname
= createIncludeName(fn
, system
, NULL
, NULL
);
4485 memmove(currLine
, args
, strlen(args
)+1);
4488 if (currLine
[0] == ',') {
4491 maxlen
= getExprArg(&defined
, NULL
);
4492 if (!defined
) fatal("INCBIN: undefined maxlen");
4493 if (maxlen
< 1) { free(fname
); return 1; } // nothing to do
4496 // offset (negative: from the end)
4497 if (currLine
[0] == ',') {
4500 offset
= getOneExprArg(&defined
, NULL
);
4501 if (!defined
) fatal("INCBIN: undefined offset");
4503 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
4506 fl
= fopen(fname
, "rb");
4508 if (!softinclude
) fatal("INCBIN: file not found: '%s'", fname
);
4511 if (fseek(fl
, offset
, (offset
< 0 ? SEEK_END
: SEEK_SET
)) < 0) {
4513 fatal("INCBIN: error seeking to %d in file '%s'", offset
, fname
);
4516 while (maxlen
-- > 0) {
4517 int res
= fread(&bt
, 1, 1, fl
);
4519 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: '%s'", fname
); }
4526 return PI_SKIP_LINE
;
4530 /* DATABIN "name"[,len[,offset[,codeaddr]]] */
4531 /* DATABIN "name|dfname"[,len[,offset[,codeaddr]]] */
4532 static int piDATABINcommon (int ascode
) {
4533 if (pass
!= 1) return PI_SKIP_LINE
;
4539 int codeaddr
= 32768;
4541 char *args
= currLine
;
4543 if (!currLine
[0]) fatal("DATABIN without file name");
4548 } else if (currLine
[0] == '<') {
4556 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4558 if (fn
[0] == '?') { allowskip
= 1; ++fn
; while (isSpace(*fn
)) ++fn
; }
4559 if (!fn
[0]) fatal("DATABIN: empty file name");
4560 char *fnameup
= strdup(fn
);
4561 memmove(currLine
, args
, strlen(args
)+1);
4564 if (currLine
[0] == ',') {
4567 maxlen
= getExprArg(&defined
, NULL
);
4568 if (!defined
) fatal("DATABIN: undefined length");
4569 if (maxlen
< 1) { free(fnameup
); return 1; } // nothing to do
4572 // offset (negative: from the end)
4573 if (currLine
[0] == ',') {
4576 offset
= getExprArg(&defined
, NULL
);
4577 if (!defined
) fatal("DATABIN: undefined offset");
4581 if (currLine
[0] == ',') {
4584 codeaddr
= getOneExprArg(&defined
, NULL
);
4585 if (!defined
) fatal("DATABIN: undefined codeaddr");
4588 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
4591 // find and extract "disk name"
4594 char *pipeptr
= strchr(fnameup
, '|');
4596 if (!pipeptr
[1]) fatal("empty data file output name");
4597 diskname
= strdup(pipeptr
+1);
4600 // build output name from disk name
4601 char *slp
= strrchr(fnameup
, '/');
4603 char *slp1
= strrchr(fnameup
, '\\');
4604 if (slp1
&& (!slp
|| slp1
> slp
)) slp
= slp1
;
4608 if (!slp
[0]) fatal("empty data file output name");
4609 diskname
= strdup(slp
);
4611 diskname
= strdup(fnameup
);
4616 char *fname
= createIncludeName(fnameup
, system
, NULL
, NULL
);
4619 OutDataFile
*off
= appendDataFile(fname
, offset
, maxlen
, allowskip
);
4623 off
->name
= diskname
;
4624 off
->start
= codeaddr
&0xffffU
;
4625 off
->hasstart
= (hasstart
|| ascode
);
4626 //for (char *s = off->name; *s; ++s) *s = toUpper(*s);
4627 printf("data file: %s (start=%u; size=%u; std=%d)\n", off
->name
, off
->start
, off
->size
, off
->hasstart
);
4631 return PI_SKIP_LINE
;
4635 static int piDATABIN (void) { return piDATABINcommon(0); }
4636 static int piCODEBIN (void) { return piDATABINcommon(1); }
4639 ///////////////////////////////////////////////////////////////////////////////
4642 /* INCLUDE "name" */
4643 static int piINCLUDE (void) {
4646 char *args
= currLine
;
4647 if (!currLine
[0]) fatal("INCLUDE without file name");
4651 } else if (currLine
[0] == '<') {
4658 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4660 int softinclude
= 0;
4661 if (fn
[0] == '?') { softinclude
= 1; ++fn
; while (isSpace(*fn
)) ++fn
; }
4662 if (!fn
[0]) fatal("INCLUDE: empty file name");
4664 if (asmTextInclude(fn
, system
, softinclude
) != 0) {
4665 if (!softinclude
) fatal("INCLUDE: some shit happens!");
4667 return PI_SKIP_LINE
;
4671 ///////////////////////////////////////////////////////////////////////////////
4672 // MODULE, ENDMODULE
4674 static int piENDMODULE (void) {
4675 if (!currModule
) fatal("ENDMODULE without MODULE");
4676 const char *mn
= NULL
;
4677 if (currLine
[0]) mn
= getOneLabelArg();
4679 return PI_SKIP_LINE
;
4683 static int piMODULE (void) {
4686 SourceLine
*ol
= currSrcLine
;
4688 if (currModule
) fatal("no nested modules allowed");
4689 mn
= getOneLabelArg();
4690 if (!urasm_is_valid_name(mn
)) fatal("invalid module name: %s", mn
);
4691 mi
= moduleFind(mn
);
4692 //fprintf(stderr, "+++MODULE: [%s] %p (seen=%d)\n", mn, mi, (mi ? mi->seen : -1));
4695 if (strcmp(mi
->fname
, currSrcLine
->fname
)) fatal("duplicate module definition; previous was in %s", mi
->fname
);
4697 nextSrcLine(); /* skip "MODULE" line */
4698 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
4700 fatal("no ENDMODULE");
4702 if (inum
== 0) fatal("no nested modules allowed");
4705 //skipInstruction(); //k8:wtf?!
4706 return piENDMODULE();
4709 mi
= moduleAdd(mn
, currSrcLine
->fname
);
4714 return PI_SKIP_LINE
;
4719 static int piMODEL (void) {
4720 char *mn
= getOneIdArgLo();
4721 if (strSkipSpaces(currLine
)[0]) fatal("only one model name expected");
4722 if (strcmp(mn
, "z80") == 0) urasm_allow_zxnext
= 0;
4723 else if (strcmp(mn
, "z80a") == 0) urasm_allow_zxnext
= 0;
4724 else if (strcmp(mn
, "z80n") == 0) urasm_allow_zxnext
= 1;
4725 else if (strcmp(mn
, "z80next") == 0) urasm_allow_zxnext
= 1;
4726 else if (strcmp(mn
, "zxnext") == 0) urasm_allow_zxnext
= 1;
4727 else fatal("invalid model name: %s", mn
);
4728 return PI_SKIP_LINE
;
4732 ////////////////////////////////////////////////////////////////////////////////
4733 // $SAVECODE "filename",addr,size [,ldaddr [,options...]]
4736 // wipe -- wipe saved area (mark it as unused)
4738 static int piSAVECODE (void) {
4739 //if (pass != 1) return PI_SKIP_LINE;
4740 //char *fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4743 int optWipe
= 0, optData
= 0;
4745 char *args
= currLine
;
4747 if (!currLine
[0]) fatal("SAVECODE without file name");
4751 } else if (currLine
[0] == '<') {
4757 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4758 if (!fn
[0]) fatal("DATABIN: empty file name");
4760 char *fname
= strdup(fn
);
4761 memmove(currLine
, args
, strlen(args
)+1);
4766 int staddr
= getExprArg(&defined
, NULL
);
4767 if (!defined
) fatal("SAVECODE: undefined start address");
4768 if (staddr
< 0 || staddr
> 65535) fatal("SAVECODE: invalid start address (%d)", staddr
);
4773 int size
= getExprArg(&defined
, NULL
);
4774 if (!defined
) fatal("SAVECODE: undefined size");
4775 if (size
< 0 || size
> 65535 || staddr
+size
> 65536) fatal("SAVECODE: invalid size");
4778 int ldaddr
= staddr
;
4779 if (checkDelim(',')) {
4781 ldaddr
= getExprArg(&defined
, NULL
);
4782 if (!defined
) fatal("SAVECODE: undefined load address");
4783 if (ldaddr
< 0 || ldaddr
> 65535) fatal("SAVECODE: invalid load address");
4787 while (checkDelim(',')) {
4788 char *nstr
= getLabelArg(0/*checkdelim*/);
4789 if (strEquCI(nstr
, "wipe")) { optWipe
= 1; continue; }
4790 if (strEquCI(nstr
, "data")) { optData
= 1; continue; }
4791 fatal("SAVECODE: unknown option '%s'", nstr
);
4793 if (!isLineEnd()) fatal("SAVECODE: unknown extra args");
4795 // save, if we are on the second pass
4796 if (pass
== 1 && size
> 0) {
4797 OutDataFile
*off
= appendDataData(memory
+(unsigned)staddr
, (unsigned)staddr
, (unsigned)size
);
4798 if (!off
) fatal("SAVECODE: out of memory");
4800 off
->hasstart
= !optData
;
4804 if (optWipe
) memset(memused
+staddr
, 0, size
);
4806 if (fname
) free(fname
);
4807 return PI_SKIP_LINE
;
4811 ////////////////////////////////////////////////////////////////////////////////
4814 static int piENDS (void) {
4815 fatal("ENDS without STRUCT");
4817 return PI_SKIP_LINE
;
4821 // this will be registered for each new struct
4822 // use `urCurrentOp` to check what structure we are creating
4823 // `currSeenLabel` contains current seen label
4824 static int piSTRUCT_Internal (void) {
4825 StructInfo
*sth
= urCurrentOp
->udata
;
4827 // reserve room, set default values, add labels
4828 uint16_t origPC
= pc
;
4829 uint16_t origDisp
= disp
;
4834 if (sth
->zerofill
) {
4835 for (uint16_t f
= 0; f
< sth
->size
; ++f
) {
4836 putByte(origPC
+f
, 0);
4840 for (StructField
*fld
= sth
->fields
; fld
; fld
= fld
->next
) {
4842 if (fld
->size
== 1) {
4843 const uint8_t v
= (fld
->initValue
< 0 ? 0x100+fld
->initValue
: fld
->initValue
)&0xff;
4844 putByte(origPC
+fld
->ofs
, v
);
4845 } else if (fld
->size
== 2) {
4846 const uint16_t v
= (fld
->initValue
< 0 ? 0x10000+fld
->initValue
: fld
->initValue
)&0xffff;
4847 putWord(origPC
+fld
->ofs
, v
);
4848 } else if (fld
->size
== 4) {
4850 memcpy(&v
, &fld
->initValue
, 4);
4851 putWord(origPC
+fld
->ofs
, v
&0xffff);
4852 putWord(origPC
+fld
->ofs
+2, (v
>>16)&0xffff);
4854 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth
->name
);
4860 //fprintf(stderr, "LBL: <%s>\n", currSeenLabel);
4861 // need to create a new label?
4862 if (!currSeenLabel
[0] || !fld
->name
|| !fld
->name
[0]) continue;
4864 char *nn
= mangleLabelName(currSeenLabel
);
4865 char *lbn
= malloc(strlen(nn
)+strlen(fld
->name
)+64);
4866 sprintf(lbn
, "%s.%s", nn
, fld
->name
);
4868 UrLabelInfo
*c
= urFindLabel(lbn
);
4869 if (!c
) c
= urAddLabel(lbn
);
4872 c
->type
= LBL_TYPE_STOFS
;
4873 c
->value
= origDisp
+fld
->ofs
;
4878 if (currSeenLabel
[0]) {
4879 char *nn
= mangleLabelName(currSeenLabel
);
4880 char *lbn
= malloc(strlen(nn
)+strlen("_sizeof")+64);
4881 sprintf(lbn
, "%s.%s", nn
, "_sizeof");
4883 UrLabelInfo
*c
= urFindLabel(lbn
);
4884 if (!c
) c
= urAddLabel(lbn
);
4887 c
->type
= LBL_TYPE_STOFS
;
4888 c
->value
= sth
->size
;
4892 // now parse initial values, if there are any
4894 const int isCurly
= checkDelim('{');
4895 SourceLine
*stline
= currSrcLine
;
4897 int wasNewLine
= 1; // don't require a comma
4901 setCurSrcLine(stline
);
4902 fatal("STRUCT: no closing curly bracket");
4907 if (!isCurly
) break;
4914 if (isCurly
&& checkDelim('}')) {
4916 if (!isLineEnd()) fatal("STRUCT: closing curly bracket must be alone on the line");
4926 char *nstr
= getLabelArg(0/*checkdelim*/);
4929 StructField
*fld
= sth
->fields
;
4930 for (; fld
; fld
= fld
->next
) if (fld
->name
&& strcmp(fld
->name
, nstr
) == 0) break;
4931 if (!fld
) fatal("unknown field `%s` in struct `%s`", nstr
, sth
->name
);
4933 // only `=` is allowed
4937 int32_t ival
= getExprArg(&defined
, NULL
);
4939 if (fld
->size
== 1) {
4940 if (defined
&& (ival
< -128 || ival
> 255)) {
4941 fatal("STRUCT: field `%s` has inivalid initial value: %d",
4942 (fld
->name
? fld
->name
: "<anonymous>"), ival
);
4944 if (ival
< -128) ival
= -128;
4945 const uint8_t v
= (ival
< 0 ? 0x100+ival
: ival
)&0xff;
4946 putByte(origPC
+fld
->ofs
, v
);
4947 } else if (fld
->size
== 2) {
4948 if (defined
&& (ival
< -32768 || ival
> 65535)) {
4949 fatal("STRUCT: field `%s` has inivalid initial value: %d",
4950 (fld
->name
? fld
->name
: "<anonymous>"), ival
);
4952 if (ival
< -32768) ival
= -32768;
4953 const uint16_t v
= (ival
< 0 ? 0x10000+ival
: ival
)&0xffff;
4954 putWord(origPC
+fld
->ofs
, v
);
4955 } else if (fld
->size
== 4) {
4957 memcpy(&v
, &ival
, 4);
4958 putWord(origPC
+fld
->ofs
, v
&0xffff);
4959 putWord(origPC
+fld
->ofs
+2, (v
>>16)&0xffff);
4961 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth
->name
);
4966 return PI_SKIP_LINE
;
4970 static int structCheckSizeDecl (const char *s
) {
4971 if (strEquCI(s
, "byte")) return 1;
4972 if (strEquCI(s
, "word")) return 2;
4973 if (strEquCI(s
, "address")) return 2;
4974 if (strEquCI(s
, "dword")) return 4;
4975 if (strEquCI(s
, "label")) return 0;
4976 if (strEquCI(s
, "defs")) return -666;
4977 if (strEquCI(s
, "ds")) return -666;
4982 // STRUCT name [,init_offset]
4983 static int piSTRUCT (void) {
4984 int zerofill
= 0, extend
= 0;
4985 int defined
= 1, inum
;
4988 //fprintf(stderr, "PASS=%d\n", pass);
4990 stline
= currSrcLine
;
4991 nextSrcLine(); // skip ourself
4992 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "ENDS", NULL
))) {
4993 setCurSrcLine(stline
);
4994 fatal("STRUCT: no ENDS");
4996 return PI_SKIP_LINE
;
5000 if (checkDelim('*')) extend = 1;
5001 else if (checkDelim('!')) extend = 2; // compatibility with the old code
5002 else if (checkDelim('+')) extend = 2;
5005 // flags: `[flag, flag...]`
5006 // currently, only `zero_fill` flag is defined
5007 if (checkDelim('[')) {
5008 while (!checkDelim(']')) {
5009 if (zerofill
== 0 && checkIdentCI("zero_fill")) zerofill
= 1;
5010 else fatal("invalid structure flag");
5011 if (!checkDelim(',')) {
5012 if (!checkDelim(']')) fatal("invalid structure flag list");
5018 // if `extend` is before a struct name, we are extending it without inheriting
5019 if (checkIdentCI("extend") || checkIdentCI("extends")) extend
= 1;
5022 char stname
[128], estname
[128];
5024 char *nstr
= getLabelArg(0/*checkdelim*/);
5025 if (!nstr
|| !nstr
[0]) fatal("structure name expected");
5026 if (strlen(nstr
) > 127) fatal("structure name too long");
5027 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr
)) {
5028 fatal("structure name `%s` is invalid", nstr
);
5030 strcpy(stname
, nstr
);
5032 StructInfo
*sth
= NULL
;
5034 for (sth
= structList
; sth
; sth
= sth
->next
) {
5035 if (strcmp(sth
->name
, stname
) == 0) {
5036 fatal("duplicate struct name `%s`!", stname
);
5039 if (urFindOp(stname
)) fatal("invalid struct name `%s`!", stname
);
5042 if (extend
|| checkIdentCI("extend") || checkIdentCI("extends")) {
5046 nstr
= getLabelArg(0/*checkdelim*/);
5047 if (!nstr
|| !nstr
[0]) fatal("parent structure name expected");
5048 if (strlen(nstr
) > 127) fatal("parent structure name too long");
5049 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr
)) {
5050 fatal("structure name `%s` is invalid", nstr
);
5052 strcpy(estname
, nstr
);
5053 for (sth
= structList
; sth
; sth
= sth
->next
) {
5054 if (strcmp(sth
->name
, estname
) == 0) break;
5056 if (!sth
) fatal("STRUCT: cannot extend unknown structure `%s`", estname
);
5059 for (sth
= structList
; sth
; sth
= sth
->next
) {
5060 if (strcmp(sth
->name
, stname
) == 0) break;
5062 if (!sth
) fatal("STRUCT: cannot extend unknown structure `%s`", stname
);
5066 StructInfo
*stnew
= calloc(1, sizeof(StructInfo
));
5067 stnew
->name
= strdup(stname
);
5068 stnew
->size
= sth
->size
;
5069 stnew
->zerofill
= (sth
->zerofill
|| zerofill
);
5072 StructField
*xlast
= NULL
;
5073 for (StructField
*fld
= sth
->fields
; fld
; fld
= fld
->next
) {
5074 StructField
*nf
= calloc(1, sizeof(StructField
));
5075 nf
->name
= (fld
->name
? strdup(fld
->name
) : NULL
);
5077 nf
->size
= fld
->size
;
5078 nf
->initValue
= fld
->initValue
;
5079 nf
->hasInit
= fld
->hasInit
;
5080 if (xlast
) xlast
->next
= nf
; else stnew
->fields
= nf
;
5087 // allocate struct header
5088 sth
= calloc(1, sizeof(StructInfo
));
5089 sth
->name
= strdup(stname
);
5090 sth
->zerofill
= zerofill
;
5093 int32_t currofs
= (sth
? sth
->size
: 0);
5094 // parse initial offset
5095 if (checkDelim(',')) {
5097 currofs
= getExprArg(&defined
, NULL
);
5098 if (!defined
) fatal("STRUCT: initial offset must be defined");
5099 if (currofs
< 0 || currofs
> 65535) fatal("STRUCT: invalid initial offset");
5103 if (!isLineEnd()) fatal("too many arguments for STRUCT");
5105 // for fast appending
5106 StructField
*fldlast
= (sth
? sth
->fields
: NULL
);
5107 StructField
*fldnew
= NULL
;
5110 while (fldlast
->next
) fldlast
= fldlast
->next
;
5113 if (extend
== 2) fldnew
= sth
->fields
;
5116 stline
= currSrcLine
;
5117 nextSrcLine(); // skip ourself
5118 //fprintf(stderr, "001: <%s>\n", currLine);
5120 while (currSrcLine
) {
5121 size_t slen
= strlen(currLine
);
5122 currLineRemoveComment();
5123 currLineTrimBlanks();
5124 if (!currLine
[0]) { nextSrcLine(); continue; }
5126 if (strIsCommand("ENDS", currSrcLine
->line
)) break;
5127 //fprintf(stderr, "112: <%s> (%d)\n", currLine, inum);
5131 char *fldname
= getLabelArg(0/*checkdelim*/);
5132 int fldsize
= structCheckSizeDecl(fldname
);
5133 if (fldsize
== -666) {
5136 fldsize
= getExprArg(&defined
, NULL
);
5137 if (!defined
) fatal("STRUCT: DEFS size must be defined");
5138 if (fldsize
< 0 || fldsize
> 65535) fatal("STRUCT: DEFS size is invalid");
5141 } else if (fldsize
< 0) {
5142 // this must be a label
5144 for (StructField
*cf
= sth
->fields
; cf
; cf
= cf
->next
) {
5145 if (cf
->name
&& cf
->name
[0] && strcmp(cf
->name
, fldname
) == 0) {
5150 if (found
) fatal("duplicate field name '%s'", fldname
);
5151 fldname
= strdup(fldname
);
5152 char *szdecl
= getLabelArg(0/*checkdelim*/);
5153 fldsize
= structCheckSizeDecl(szdecl
);
5154 if (fldsize
== -666) {
5157 fldsize
= getExprArg(&defined
, NULL
);
5158 if (!defined
) fatal("STRUCT: DEFS size must be defined");
5159 if (fldsize
< 0 || fldsize
> 65535) fatal("STRUCT: DEFS size is invalid");
5162 if (fldsize
< 0) fatal("field size definition expected");
5165 fldname
= NULL
; // no name
5168 // check for init value
5170 int32_t initval
= 0;
5172 if (!skipInit
&& checkDelim('=')) {
5174 initval
= getExprArg(&defined
, NULL
);
5175 if (!defined
) fatal("STRUCT: initial value must be defined");
5178 (fldsize
== 1 && (initval
< -128 || initval
> 255)) ||
5179 (fldsize
== 2 && (initval
< -32768 || initval
> 65535)))
5181 fatal("STRUCT: initial value is out of range");
5185 if (!isLineEnd()) fatal("STRUCT: extra field data");
5187 if (currofs
+fldsize
> 65535) fatal("STRUCT: too big");
5190 StructField
*fld
= calloc(1, sizeof(StructField
));
5191 fld
->name
= fldname
;
5193 fld
->size
= fldsize
;
5194 fld
->hasInit
= hasInit
;
5195 fld
->initValue
= initval
;
5196 if (fldlast
) fldlast
->next
= fld
; else sth
->fields
= fld
;
5200 if (!fldnew
) fldnew
= fld
;
5203 fprintf(stderr
, "FLD <%s>: ofs=%u; size=%u; hasinit=%d; init=%d\n", fld
->name
, fld
->ofs
,
5204 fld
->size
, fld
->hasInit
, fld
->initValue
);
5209 if (!currSrcLine
) { setCurSrcLine(stline
); fatal("no ENDS"); }
5210 if (currofs
> 65535) fatal("STRUCT: too big");
5211 sth
->size
= currofs
;
5213 //FIXME: structs must be local to module!
5215 // register new struct
5216 if (extend
== 0 || extend
== 2) {
5217 StructInfo
*slast
= structList
;
5219 while (slast
->next
) slast
= slast
->next
;
5224 UrAsmOp
*op
= urAddOp(sth
->name
, &piSTRUCT_Internal
);
5228 // add struct labels
5229 for (StructField
*fld
= fldnew
; fld
; fld
= fld
->next
) {
5230 if (!fld
->name
|| !fld
->name
[0]) continue;
5232 char *nn
= mangleLabelName(sth
->name
);
5233 char *lbn
= malloc(strlen(nn
)+strlen(fld
->name
)+64);
5234 sprintf(lbn
, "%s.%s", nn
, fld
->name
);
5236 //fprintf(stderr, "%d: <%s>\n", extend, lbn);
5238 UrLabelInfo
*c
= urFindLabel(lbn
);
5239 if (c
) fatal("STRUCT: field name '%s' conflict!", fld
->name
);
5241 c
= urAddLabel(lbn
);
5243 c
->type
= LBL_TYPE_STOFS
;
5244 c
->value
= fld
->ofs
;
5248 // add `structname.sizeof`
5250 char *nn
= mangleLabelName(sth
->name
);
5251 char *lbn
= malloc(strlen(nn
)+64);
5252 sprintf(lbn
, "%s.%s", nn
, "_sizeof");
5254 UrLabelInfo
*c
= urFindLabel(lbn
);
5256 if (!extend
) fatal("STRUCT: field name '%s' conflict!", "_sizeof");
5258 c
= urAddLabel(lbn
);
5262 c
->type
= LBL_TYPE_STOFS
;
5263 c
->value
= sth
->size
;
5267 return PI_SKIP_LINE
;
5271 // ////////////////////////////////////////////////////////////////////////// //
5272 static int piEND_PASMO (void) {
5274 int32_t res
= getOneExprArg(&defined
, NULL
);
5275 //if (!defined) fatal("sorry, ENT operand value must be known here");
5276 if (res
< 0 || res
> 65535) fatal("invalid END operand value: %d", res
);
5278 return PI_CONT_LINE
;
5282 ///////////////////////////////////////////////////////////////////////////////
5285 static int piEDUP (void) {
5286 fatal("EDUP without DUP");
5288 return PI_SKIP_LINE
;
5292 static int piENDR (void) {
5293 fatal("ENDR without REPT");
5295 return PI_SKIP_LINE
;
5299 // DUP count [,var [,initval [,incr]]]
5300 static int piDUPEx (int isrept
) {
5301 int defined
= 1, inum
;
5302 SourceLine
*stline
, *eline
= NULL
;
5304 //int32_t cnt = getOneExprArg(&defined, NULL);
5305 int32_t cnt
= getExprArg(&defined
, NULL
);
5306 if (!defined
) fatal("DUP: counter must be defined");
5307 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
5308 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
5310 int32_t initval
= 0;
5316 if (currLine
[0] == ',') {
5318 char *lbl
= getLabelArg(0/*checkdelim*/);
5320 if (strlen(lbl
) > 127) fatal("DUP counter variable name too long");
5321 if (urFindOp(lbl
) || !urasm_is_valid_name(lbl
)) fatal("DUP counter variable name is invalid");
5326 // initial variable value
5327 if (currLine
[0] == ',') {
5330 initval
= getExprArg(&defined
, NULL
);
5331 if (!defined
) fatal("DUP: initial value must be defined");
5335 if (currLine
[0] == ',') {
5338 incr
= getExprArg(&defined
, NULL
);
5339 if (!defined
) fatal("DUP: increment value must be defined");
5342 if (currLine
[0]) fatal("too many arguments for DUP");
5347 // now find corresponding EDUP
5348 // note that we should skip nested DUPs
5349 nextSrcLine(); // skip ourself
5350 stline
= currSrcLine
;
5351 while (currSrcLine
) {
5352 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "REPT", "EDUP", "ENDR", "ENDM", NULL
))) break;
5353 if (inum
== 0 || inum
== 1) {
5354 if (dupsp
>= 128) fatal("too many nested DUPs");
5355 dupstack
[dupsp
++] = isrept
;
5356 isrept
= (inum
== 1);
5357 nextSrcLine(); // skip DUP
5360 if (dupsp
== 0) { eline
= currSrcLine
; break; }
5362 if (inum
< 3) fatal("invalid REPT end directive");
5364 if (inum
>= 3) fatal("invalid DUP end directive");
5366 isrept
= dupstack
[--dupsp
];
5367 nextSrcLine(); // skip EDUP
5370 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
5372 // create counter local, if necessary
5373 UrLabelInfo
*lcnt
= (vname
[0] ? urAddTempLocal(vname
) : NULL
);
5374 if (lcnt
) lcnt
->value
= initval
;
5378 setCurSrcLine(stline
);
5379 while (currSrcLine
!= eline
) processCurrentLine();
5380 // increment counter
5381 if (lcnt
) lcnt
->value
+= incr
;
5385 urRemoveTempLocal(lcnt
);
5387 return PI_SKIP_LINE
;
5391 static int piDUP (void) { return piDUPEx(0); }
5392 static int piREPT (void) { return piDUPEx(1); }
5395 ///////////////////////////////////////////////////////////////////////////////
5398 static int ifCount
= 0;
5402 // -1: error (should not happen)
5403 // 0: successfully complete
5404 // 1: stopped *AT* "ELSE"
5405 // 2: stopped *AT* "ELSIF"
5406 static int ifSkipToEndIfOrElse (int wholeBody
) {
5407 int inum
, wasElse
= 0;
5410 while (currSrcLine
) {
5411 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "IF", "ELSE", "ENDIF",
5412 "ELSEIF", "MACRO", "ENDM", "IFX",
5419 nextSrcLine(); // skip IF
5420 ifSkipToEndIfOrElse(1); // and recurse
5421 nextSrcLine(); // skip ENDIF
5424 if (wasElse
) fatal("duplicate ELSE");
5425 if (!wholeBody
) return 1;
5427 nextSrcLine(); // skip ELSE
5428 break; // and continue
5430 return 0; // and exit
5431 case 3: /* elseif */
5432 case 7: /* elseifx */
5434 case 9: /* elsifx */
5435 if (wasElse
) fatal("ELSIF in ELSE");
5436 if (!wholeBody
) return 2;
5437 nextSrcLine(); // skip ELSIF
5438 break; // and continue
5440 // skip it as a whole
5441 nextSrcLine(); // skip MACRO
5443 oline
= currSrcLine
;
5444 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) { setCurSrcLine(oline
); fatal("invalid MACRO"); }
5445 if (inum
== 1) break;
5446 fatal("invalid nested MACRO");
5448 nextSrcLine(); // skip ENDM
5451 fatal("unexpected ENDM");
5454 fatal("IF without ENDIF");
5459 static int piENDIF (void) {
5460 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5461 if (--ifCount
< 0) fatal("ENDIF without IF");
5463 return PI_SKIP_LINE
;
5467 static int piELSE (void) {
5468 if (--ifCount
< 0) fatal("ELSE without IF");
5469 nextSrcLine(); // skip ELSE
5470 ifSkipToEndIfOrElse(1);
5471 return PI_SKIP_LINE
;
5474 static int piELSIF (void) {
5475 if (--ifCount
< 0) fatal("ELSIF without IF");
5476 nextSrcLine(); // skip ELSIF
5477 ifSkipToEndIfOrElse(1);
5478 return PI_SKIP_LINE
;
5482 static int piELSIFX (void) {
5483 if (--ifCount
< 0) fatal("ELSIFX without IF");
5484 nextSrcLine(); // skip ELSIFX
5485 ifSkipToEndIfOrElse(1);
5486 return PI_SKIP_LINE
;
5490 static int piIFAll (int isIfX
) {
5492 int ooo
= lblOptMakeU2
;
5494 //fprintf(stderr, "piIFALL: <%s>\n", currLine);
5495 lblOptMakeU2
= (isIfX
? 1 : 0);
5496 int32_t cond
= getOneExprArg(&defined
, NULL
);
5498 //fprintf(stderr, "piIFALL COND: <%d>\n", cond);
5501 if (!isIfX
) fatal("IF: condition must be defined");
5502 cond
= 0; // for IFX: 0 if there is any undefined label
5505 // ok, do it until ELSE/ELSIF/ENDIF
5507 return PI_SKIP_LINE
;
5513 nextSrcLine(); // skip last instruction
5514 // skip until ELSE/ELSIF/ENDIF
5515 r
= ifSkipToEndIfOrElse(0);
5516 /*!fprintf(stderr, "ELSIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5517 if (r
== 0) break; // ENDIF
5518 if (r
== 1) { ++ifCount
; break; } // ELSE
5519 // ELSIF, do condition
5520 args
= strIsCommand("ELSIF", currLine
);
5521 if (args
== NULL
) args
= strIsCommand("ELSEIF", currLine
);
5525 args
= strIsCommand("ELSIFX", currLine
);
5526 if (args
== NULL
) args
= strIsCommand("ELSEIFX", currLine
);
5527 if (args
== NULL
) fatal("internal error in conditionals"); // the thing that should not be
5530 memmove(currLine
, args
, strlen(args
)+1);
5531 /*!fprintf(stderr, "ELSIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5532 cond
= getOneExprArg(&defined
, NULL
);
5533 /*!fprintf(stderr, "ELSIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0), cond);*/
5535 if (!isIfX
) fatal("ELSIF: condition must be defined");
5536 cond
= 0; // for IFX: 0 if there is any undefined label
5538 if (cond
) { ++ifCount
; break; } // condition is true
5540 return PI_SKIP_LINE
;
5544 static int piIF (void) { return piIFAll(0); }
5545 static int piIFX (void) { return piIFAll(1); }
5548 ///////////////////////////////////////////////////////////////////////////////
5550 ///////////////////////////////////////////////////////////////////////////////
5552 what i did with MACRO is the brain-damaged cheating all the way.
5554 first, i will collect the MACRO body and remember it (removing it
5555 from the main source code, so second pass will not see it).
5557 second, when the macro is used, i will:
5558 * insert the whole macro body in place (label resolver will
5559 fix "..lbl" labels for us)
5560 * let the asm play with it
5562 this is not the best scheme, but it is fairly simple and it works.
5565 // will be called when parser encounters term starting with '=' or '*'
5566 // first term char will not be skipped
5567 // must return pointer to the first char after expression end
5568 static const char *getValueCB (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
5572 if (curmacro
== NULL
) fatal("'=' outside of macro");
5573 if (*expr
++ != '=') fatal("'=' expected!");
5575 expr
= strSkipSpaces(expr
);
5577 if (isAlphaDigit(*expr
) || *expr
== '_') {
5578 if (p
-name
> 250) fatal("id too long");
5586 expr
= strSkipSpaces(expr
);
5587 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
5588 if (strEquCI(name
, curmacro
->mac
->argnames
[f
])) {
5591 int l
= (int)strlen(curmacro
->argvals
[f
]);
5593 urasm_exprval_init(&v
);
5594 expr
= urasm_expr_ex(&v
, expr
, addr
, &donteval
, defined
, error
);
5595 if (*error
) return expr
;
5596 if (expr
== NULL
|| *expr
!= ']' || v
.str
!= NULL
) { *error
= UR_EXPRERR_FUNC
; return expr
; }
5598 if (v
.val
< 0) v
.val
+= l
;
5599 if (v
.val
< 0 || v
.val
>= l
) {
5602 res
->val
= (unsigned char)(curmacro
->argvals
[f
][v
.val
]);
5606 urasm_expr_ex(res
, curmacro
->argvals
[f
], addr
, &donteval
, defined
, error
);
5612 fatal("unknown macro variable: '%s'", name
);
5616 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
5617 // opr starts with '=' (invariant)
5618 static int expandCB (char *opr
, int oprlen
) {
5619 char name
[257], *p
= name
, *op
= opr
;
5621 if (curmacro
== NULL
) fatal("'=' outside of macro");
5622 if (*op
++ != '=') fatal("'=' expected!"); // just in case
5623 //fprintf(stderr, "expand: [%s]\n", opr);
5625 if (!isAlpha(op
[0])) return 0; // nothing to do
5627 // copy argument name
5629 if (isAlphaDigit(*op
) || *op
== '_') {
5630 if (p
-name
> 250) fatal("id too long");
5638 // expand argument? we only need to expand `=arg[n]`
5639 op
= strSkipSpaces(op
);
5640 if (op
[0] != '[') return 0;
5642 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
5643 if (strEquCI(name
, curmacro
->mac
->argnames
[f
])) {
5644 ur_assert(*op
== '[');
5645 // replace argument with character, or with literal value
5646 // used for `=regpair[0]` or `=regpair[]`
5649 const int l
= (int)strlen(curmacro
->argvals
[f
]);
5650 const int lleft
= (int)strlen(op
);
5651 char *tmp
= malloc(l
+lleft
+8);
5652 snprintf(tmp
, l
+lleft
+8, "%s%s", curmacro
->argvals
[f
], op
);
5653 if ((int)strlen(tmp
) > oprlen
) {
5662 int error
= 0, defined
= 1, donteval
= 0;
5664 urasm_exprval_init(&v
);
5665 op
= (char *)urasm_expr_ex(&v
, op
, pc
, &donteval
, &defined
, &error
);
5666 if (error
) return -1;
5667 // result should be a number
5668 if (op
== NULL
|| *op
!= ']' || v
.str
!= NULL
) return -1;
5670 // it is guaranteed to have more than one char in opr
5671 // so we can simply put char from argument value, and remove other expression chars
5672 const int l
= (int)strlen(curmacro
->argvals
[f
]);
5673 if (v
.val
< 0) v
.val
+= l
; // negative: indexing from the end
5674 if (v
.val
< 0 || v
.val
>= l
) fatal("index %d is out of bounds for macro argument '%s'", v
.val
, curmacro
->mac
->argnames
[f
]);
5676 opr
[0] = curmacro
->argvals
[f
][v
.val
];
5677 // remove other chars
5678 memmove(opr
+1, op
, strlen(op
)+1);
5684 fatal("unknown macro variable: '%s'", name
);
5688 // main macro expander
5689 static void processMacro (MacroDef
*mc
) {
5690 SourceLine
*oldcurline
= currSrcLine
;
5692 memset(&cm
, 0, sizeof(cm
));
5694 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
5695 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
5697 // parse macro arguments
5699 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) cm
.argvals
[f
] = NULL
;
5703 // do we have more arguments?
5704 if (!currLine
[0]) break;
5705 // check for argument name (this is purely cosmetic thing)
5706 // use like this: "mcall :argname=[value]" (value may be omited for default)
5707 if (currLine
[0] == ':' && isAlpha(currLine
[1])) {
5709 while (isAlphaDigit(currLine
[pos
]) || currLine
[pos
] == '_') ++pos
;
5711 const char svch
= currLine
[pos
];
5713 if (!strEquCI(currLine
+1, mc
->argnames
[currArg
])) {
5714 // out-of-order, find proper argument and rewind to it
5716 for (int c
= 0; c
< mc
->argc
; ++c
) {
5717 if (strEquCI(currLine
+1, mc
->argnames
[c
])) {
5722 if (currArg
< 0) fatal("macro '%s' has no argument named '%s'", mc
->name
, currLine
+1);
5724 currLine
[pos
] = svch
;
5725 // remove argument name
5726 memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
5729 if (currLine
[0] != '=') fatal("expected '=' after argument name");
5731 memmove(currLine
, currLine
+1, strlen(currLine
));
5735 if (currArg
>= mc
->argc
) fatal("too many arguments to macro '%s'", mc
->name
);
5736 // check for default value
5737 if (currLine
[0] == ',') {
5738 if (mc
->argdefaults
[currArg
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[currArg
]);
5739 cm
.argvals
[currArg
] = strdup(mc
->argdefaults
[currArg
]);
5740 memmove(currLine
, currLine
+1, strlen(currLine
));
5742 // skip argument (so we will know its length, and will be able to copy it)
5743 char *e
= skipMacroArg(currLine
);
5744 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
5745 const char ech
= *e
;
5747 if (ech
== ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc
->name
);
5748 if (ech
!= ',') fatal("invalid invocation of macro '%s'", mc
->name
);
5751 cm
.argvals
[currArg
] = strdup(currLine
);
5752 // strip trailing spaces
5753 strTrimRight(cm
.argvals
[currArg
]);
5756 memmove(currLine
, e
+1, strlen(e
));
5763 // check for line end
5765 if (currLine
[0]) fatal("invalid macro invocation");
5767 // setup default argument values
5768 for (int f
= 0; f
< mc
->argc
; ++f
) {
5769 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
5770 if (cm
.argvals
[f
]) continue;
5771 if (mc
->argdefaults
[f
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[f
]);
5772 cm
.argvals
[f
] = strdup(mc
->argdefaults
[f
]);
5773 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
5776 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
5778 // insert macro code into the source
5779 setCurSrcLine(mc
->lines
);
5782 while (currSrcLine
!= NULL
) {
5783 //fprintf(stderr, "*[%s] (%d)\n", currSrcLine->line, currSrcLine->lineNo);
5784 processCurrentLine();
5787 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) if (cm
.argvals
[f
]) free(cm
.argvals
[f
]);
5788 setCurSrcLine(oldcurline
);
5794 static int piMACRO (void) {
5797 char *argdefaults
[MAX_MACRO_ARGS
];
5798 char *argnames
[MAX_MACRO_ARGS
];
5799 SourceLine
*stline
, *eline
= NULL
;
5802 name
= strdup(getLabelArg(0));
5803 //fprintf(stderr, "[%s]\n", name);
5804 if (findMacro(name
) != NULL
) fatal("duplicate MACRO definition: '%s'", name
);
5805 if (currLine
[0] == ',') memmove(currLine
, currLine
+1, strlen(currLine
));
5808 while (currLine
[0]) {
5809 if (argc
>= MAX_MACRO_ARGS
) fatal("too many arguments in MACRO");
5810 if (!isAlpha(currLine
[0])) fatal("invalid MACRO definition");
5811 argnames
[argc
] = strdup(getLabelArg(0));
5812 if (currLine
[0] == '=') {
5814 char *e
= strchr(currLine
, ','), tch
;
5816 if (e
== NULL
) e
= currLine
+strlen(currLine
);
5819 argdefaults
[argc
] = strdup(currLine
+1);
5821 memmove(currLine
, e
, strlen(e
)+1);
5823 argdefaults
[argc
] = NULL
;
5826 if (currLine
[0] == ',') {
5827 memmove(currLine
, currLine
+1, strlen(currLine
));
5830 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
5834 // now find corresponding ENDM
5835 // note that we should skip nested DUPs
5836 stline
= currSrcLine
;
5837 nextSrcLine(); // skip ourself
5838 while (currSrcLine
) {
5841 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) break;
5842 // ok, we found something; what is it?
5845 fatal("no nested MACROs yet");
5848 eline
= currSrcLine
;
5851 nextSrcLine(); // skip ENDM
5855 if (!eline
) { setCurSrcLine(stline
); fatal("no ENDM"); }
5857 if ((mc
= calloc(1, sizeof(*mc
))) == NULL
) fatal("out of memory");
5860 for (int f
= 0; f
< argc
; ++f
) { mc
->argdefaults
[f
] = argdefaults
[f
]; mc
->argnames
[f
] = argnames
[f
]; }
5863 mc
->lines
= stline
->next
;
5864 stline
->next
= currSrcLine
;
5865 stline
->line
[0] = 0;
5869 setCurSrcLine(stline
);
5870 return PI_SKIP_LINE
;
5874 static int piENDM (void) {
5875 fatal("ENDM without MACRO");
5876 return PI_SKIP_LINE
;
5880 ///////////////////////////////////////////////////////////////////////////////
5882 static void piTapParseLoaderName (void) {
5885 if (!isStrArg()) fatal("loader name expected");
5886 char *fn
= getStrArg(&len
);
5887 if (len
> 10) fatal("loader name too long");
5888 memset(tapeLoaderName
, ' ', 10);
5889 for (int f
= 0; f
< len
; ++f
) tapeLoaderName
[f
] = fn
[f
];
5894 static int piMATHMODE (void) {
5895 char *name
= getOneLabelArg();
5896 if (strEquCI(name
, "OLD")) urasm_use_old_priorities
= 1;
5897 else if (strEquCI(name
, "NEW")) urasm_use_old_priorities
= 0;
5898 else fatal("invalid math mode; NEW or OLD expected");
5899 return PI_SKIP_LINE
;
5903 static int piCASTRATES (void) {
5904 char *name
= getOneLabelArg();
5905 if (strEquCI(name
, "YES")) urasm_allow_hex_castrates
= 1;
5906 else if (strEquCI(name
, "TAN")) urasm_allow_hex_castrates
= 1;
5907 else if (strEquCI(name
, "NO")) urasm_allow_hex_castrates
= 0;
5908 else if (strEquCI(name
, "ONA")) urasm_allow_hex_castrates
= 0;
5909 else fatal("yes/no value expected");
5910 return PI_SKIP_LINE
;
5914 static int piREFOPT (void) {
5915 char *name
= getOneLabelArg();
5916 if (strEquCI(name
, "ALLLABELS")) urasm_dump_all_labels
= 1;
5917 else if (strEquCI(name
, "ALL_LABELS")) urasm_dump_all_labels
= 1;
5918 else if (strEquCI(name
, "NOUPCASE")) urasm_dump_all_labels
= 0;
5919 else if (strEquCI(name
, "NO_UPCASE")) urasm_dump_all_labels
= 0;
5920 else if (strEquCI(name
, "DEFAULT")) urasm_dump_all_labels
= 1;
5921 else fatal("invalid refopt mode");
5922 return PI_SKIP_LINE
;
5926 static int piDEFFMT (void) {
5931 name
= getStrArg(&len
);
5933 name
= getLabelArg(1);
5935 if (optWTChanged
) return 1;
5937 optRunDMB
= optRunTape
= optRunSCL
= optRunDSK
= 0;
5940 if (strEquCI(name
, "SNA") || strEquCI(name
, "RUNSNA") || strEquCI(name
, "SNARUN")) {
5942 if (currLine
[0]) fatal("too many expressions");
5943 return PI_SKIP_LINE
;
5945 if (strEquCI(name
, "TAP") || strEquCI(name
, "TAPE")) {
5947 piTapParseLoaderName();
5948 return PI_SKIP_LINE
;
5950 if (strEquCI(name
, "RUNTAP") || strEquCI(name
, "RUNTAPE") || strEquCI(name
, "TAPERUN")) {
5953 piTapParseLoaderName();
5954 return PI_SKIP_LINE
;
5956 if (strEquCI(name
, "BIN") || strEquCI(name
, "RAW")) {
5958 if (currLine
[0]) fatal("too many expressions");
5959 return PI_SKIP_LINE
;
5961 if (strEquCI(name
, "DMB") || strEquCI(name
, "RUNDMB") || strEquCI(name
, "DMBRUN")) {
5962 optRunDMB
= (name
[3] != 0);
5964 if (currLine
[0]) fatal("too many expressions");
5965 return PI_SKIP_LINE
;
5967 if (strEquCI(name
, "NONE") || strEquCI(name
, "NOTHING")) {
5969 if (currLine
[0]) fatal("too many expressions");
5970 return PI_SKIP_LINE
;
5972 if (strEquCI(name
, "SCL") || strEquCI(name
, "RUNSCL") || strEquCI(name
, "SCLRUN")) {
5974 optRunSCL
= (name
[3] != 0 ? -1 : 0); /* no boot */
5975 piTapParseLoaderName();
5976 return PI_SKIP_LINE
;
5978 if (strEquCI(name
, "SCLMONO") || strEquCI(name
, "RUNSCLMONO") || strEquCI(name
, "SCLMONORUN")) {
5980 optRunSCL
= (name
[7] != 0 ? -1 : 0); /* no boot */
5981 piTapParseLoaderName();
5982 return PI_SKIP_LINE
;
5984 if (strEquCI(name
, "SCLBOOT") || strEquCI(name
, "BOOTSCL")) {
5986 optRunSCL
= 1; /* with boot */
5987 piTapParseLoaderName();
5988 return PI_SKIP_LINE
;
5990 if (strEquCI(name
, "SCLMONOBOOT") || strEquCI(name
, "BOOTSCLMONO")) {
5992 optRunSCL
= 1; /* with boot */
5993 piTapParseLoaderName();
5994 return PI_SKIP_LINE
;
5997 if (strEquCI(name
, "DSKBOOT") || strEquCI(name
, "BOOTDSK") ||
5998 strEquCI(name
, "DSK180BOOT") || strEquCI(name
, "BOOTDSK180"))
6001 optRunDSK
= 1; /* with boot */
6002 optDskType
= P3DSK_PCW_SS
; // +3DOS 180K disk
6003 piTapParseLoaderName();
6004 return PI_SKIP_LINE
;
6007 if (strEquCI(name
, "DSK720BOOT") || strEquCI(name
, "BOOTDSK720")) {
6009 optRunDSK
= 1; /* with boot */
6010 optDskType
= P3DSK_PCW_DS
; // +3DOS 720K disk
6011 piTapParseLoaderName();
6012 return PI_SKIP_LINE
;
6015 if (strEquCI(name
, "DSK") || strEquCI(name
, "DSK180")) {
6017 optRunDSK
= 0; /* without boot */
6018 optDskType
= P3DSK_PCW_SS
; // +3DOS 180K disk
6019 piTapParseLoaderName();
6020 return PI_SKIP_LINE
;
6023 if (strEquCI(name
, "DSK720")) {
6025 optRunDSK
= 0; /* without boot */
6026 optDskType
= P3DSK_PCW_DS
; // +3DOS 720K disk
6027 piTapParseLoaderName();
6028 return PI_SKIP_LINE
;
6031 fatal("invalid default output type: %s", name
);
6035 ///////////////////////////////////////////////////////////////////////////////
6038 static void processCurrentLine (void) {
6039 if (!currSrcLine
) return; // do nothing
6041 ur_assert(asmMode
!= AMODE_UFO
); // the thing that should not be
6044 char *str
, *ee
, name
[66];
6048 removeSpacesAndColons();
6049 // skip spaces and ':'
6050 if (!currLine
[0]) { nextSrcLine(); break; }
6051 // try to find and process command
6052 str
= currLine
; //while (*str && isSpace(*str)) ++str; // skip spaces
6054 for (ee
= str
; *ee
&& !isSpace(*ee
) && *ee
!= '"' && *ee
!= '\'' && *ee
!= ',' && *ee
!= ':'; ++ee
) {}
6055 // get command, if any
6056 if (ee
!= str
&& ee
-str
<= 64) {
6058 memset(name
, 0, sizeof(name
));
6059 memmove(name
, str
, ee
-str
);
6060 /* UrForth macro? */
6061 uint32_t fwmacro
= ufoIsMacro(name
);
6064 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
6065 memmove(currLine
, str
, strlen(str
)+1);
6066 ufoMacroRun(fwmacro
, currLine
, currSrcLine
->fname
, currSrcLine
->lineNo
);
6067 /* only one macro per line! */
6068 nextSrcLine(); // skip it
6071 /* known command? */
6072 op
= urFindOp(name
);
6075 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
6076 memmove(currLine
, str
, strlen(str
)+1);
6080 nextSrcLine(); // skip it
6087 if ((mc
= findMacro(name
)) != NULL
) {
6089 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
6090 memmove(currLine
, str
, strlen(str
)+1);
6092 /* only one macro per line! */
6096 // with reservation management
6098 len
= urasm_opasm(currLine
, pc
, disp
, &errpos
);
6099 if (len
< 0) fatalUrLib(len
);
6100 // check for reservation
6102 if (reserveHit(pc
, len
)) {
6104 if (pc
!= disp
) fatal("trying to use reserved areas with phased code");
6105 reserveReleaseUsed(pc
, len
);
6106 int nextaddr
= reservedFindNextSuitableFreeFrom(pc
, pc
+len
);
6107 if (nextaddr
< 0) fatal("codegen started in reserved area #%04X", pc
);
6108 // insert jump to the new area, and retry
6109 fprintf(stderr
, "...inserting jump from #%04X to #%04X\n", (unsigned)pc
, (unsigned)nextaddr
);
6110 // can we insert a JP? it is faster than JR
6111 if (memresv
[pc
] || memresv
[pc
+1]) fatal("internal reserve area manager error at #%04X", pc
);
6112 if (!memresv
[pc
+2]) {
6114 emitByte(0xc3U
); // JP
6115 emitWord((uint16_t)nextaddr
);
6118 int jrdest
= nextaddr
-(pc
+2U);
6119 if (jrdest
>= 128) fatal("internal reserve area manager error at #%04X (jr dist)", pc
);
6120 emitByte(0x18U
); // JR
6121 emitByte((uint8_t)jrdest
);
6123 disp
= pc
= (uint16_t)nextaddr
;
6124 goto again_from_reserve
;
6126 // did we compiled JP/JR?
6127 if (getByte(pc
) == 0xc3U
|| getByte(pc
) == 0x18U
) {
6128 // no room for jump?
6129 if (memresv
[(pc
+len
)&0xffffU
] || memresv
[(pc
+len
+1)&0xffffU
] || memresv
[(pc
+len
+2)&0xffffU
]) {
6130 if (pc
!= disp
) fatal("trying to use reserved areas with phased code");
6131 // find next free area
6133 while (next
>= 0 && next
< 65536) {
6134 next
= reserveFindFreeFrom(next
);
6135 if (next
< 0) break;
6136 // has room for JP there?
6137 if (!memresv
[next
] && !memresv
[(next
+1)&0xffffU
] && !memresv
[(next
+2)&0xffffU
]) break;
6138 // no room for JP, has room for JP?
6139 if (!memresv
[next
] && !memresv
[(next
+1)&0xffffU
]) {
6140 // check if jr can fit
6141 const int nj
= reservedFindNextJumpableFreeFrom(next
, next
+2);
6144 next
= reserveFindReservedFrom(next
);
6146 if (next
>= 0) disp
= pc
= (next
-len
)&0xffffU
; // so "+len" will make PC right
6149 // check if we still have enough room for a possible jp/jr
6150 if (memresv
[(pc
+len
)&0xffffU
] || memresv
[(pc
+len
+1)&0xffffU
]) goto insert_jump
;
6151 // we have at least a room for JR, check if we have a room for JP
6152 if (!memresv
[(pc
+len
+2)&0xffffU
]) {
6153 // no room for JP, check if jr will be enough
6154 int next
= reservedFindNextSuitableFreeFrom(pc
, pc
+len
+2);
6155 if (next
>= 0 && next
-(pc
+len
+2u) >= 128) goto insert_jump
;
6162 if (len
>= 0 && errpos
) {
6163 memmove(currLine
, errpos
+1, strlen(errpos
));
6165 nextSrcLine(); // skip it
6172 ///////////////////////////////////////////////////////////////////////////////
6173 // start inline UrForth code
6175 static int piSTARTFORTH (void) {
6177 asmMode
= AMODE_UFO
;
6179 asmMode
= AMODE_NORMAL
;
6180 return PI_SKIP_LINE
;
6184 ///////////////////////////////////////////////////////////////////////////////
6185 // setup instructions
6187 static void registerInstructions (void) {
6188 urAddOp("DISPLAY", piDISPLAY
);
6189 urAddOp("DISPLAY0", piDISPLAY0
);
6190 urAddOp("DISPLAYA", piDISPLAYA
);
6191 urAddOp("DISPHEX", piDISPHEX
);
6192 urAddOp("DISPHEX0", piDISPHEX0
);
6193 urAddOp("DISPHEXA", piDISPHEXA
);
6195 urAddOp("DEFFMT", piDEFFMT
);
6196 urAddOp("$MODEL", piMODEL
);
6197 urAddOp("$SAVECODE", piSAVECODE
);
6199 urAddOp("MACRO", piMACRO
);
6200 urAddOp("ENDM", piENDM
);
6202 urAddOp("ORG", piORG
);
6203 urAddOp("DISP", piDISP
);
6204 urAddOp("ENDDISP", piENDDISP
);
6205 urAddOp("PHASE", piDISP
);
6206 urAddOp("DEPHASE", piENDDISP
);
6207 urAddOp("UNPHASE", piENDDISP
);
6208 urAddOp("ALIGN", piALIGN
);
6209 urAddOp("DISPALIGN", piDISPALIGN
);
6210 urAddOp("PHASEALIGN", piDISPALIGN
);
6211 urAddOp("ENT", piENT
);
6212 urAddOp("CLR", piCLR
);
6213 urAddOp("RESERVE", piRESERVE
);
6215 urAddOp("INCLUDE", piINCLUDE
);
6216 urAddOp("INCBIN", piINCBIN
);
6217 urAddOp("DATABIN", piDATABIN
);
6218 urAddOp("CODEBIN", piCODEBIN
);
6220 urAddOp("MODULE", piMODULE
);
6221 urAddOp("ENDMODULE", piENDMODULE
);
6223 urAddOp("DUP", piDUP
);
6224 urAddOp("EDUP", piEDUP
);
6225 urAddOp("REPT", piREPT
);
6226 urAddOp("ENDR", piENDR
);
6228 urAddOp("STRUCT", piSTRUCT
);
6229 urAddOp("ENDS", piENDS
);
6231 urAddOp("END", piEND_PASMO
);
6233 urAddOp("IF", piIF
);
6234 urAddOp("IFX", piIFX
);
6235 urAddOp("ELSE", piELSE
);
6236 urAddOp("ELSIF", piELSIF
);
6237 urAddOp("ELSIFX", piELSIFX
);
6238 urAddOp("ELSEIF", piELSIF
);
6239 urAddOp("ELSEIFX", piELSIFX
);
6240 urAddOp("ENDIF", piENDIF
);
6242 urAddOp("DEFINCR", piDEFINCR
);
6243 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
6244 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
6245 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
6246 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
6247 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
6248 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
6249 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
6250 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
6252 urAddOp("$ERROR", piERROR
);
6253 urAddOp("$WARNING", piWARNING
);
6255 urAddOp("$PRINTF", piPRINTF
);
6256 urAddOp("$PRINTF0", piPRINTF0
);
6257 urAddOp("$PRINTFA", piPRINTFA
);
6259 urAddOp("$MATHMODE", piMATHMODE
);
6260 urAddOp("$REFOPT", piREFOPT
);
6261 urAddOp("$CASTRATES", piCASTRATES
);
6263 urAddOp("$START_FORTH", piSTARTFORTH
);
6267 ///////////////////////////////////////////////////////////////////////////////
6268 // !0: invalid label
6270 static inline void fnSkipSpaces (const char *expr) {
6271 while (*expr && isSpace(*expr)) ++expr;
6277 UR_FORCE_INLINE
char fnNextChar (const char *expr
) {
6278 while (*expr
&& isSpace(*expr
)) ++expr
;
6283 #define FN_SKIP_BLANKS do { \
6284 while (*expr && isSpace(*expr)) ++expr; \
6287 #define FN_CHECK_END do { \
6288 while (*expr && isSpace(*expr)) ++expr; \
6289 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
6294 #define FN_CHECK_COMMA do { \
6295 while (*expr && isSpace(*expr)) ++expr; \
6296 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
6301 static const char *readLabelName (char *buf
, const char *expr
) {
6304 while (*expr
&& isSpace(*expr
)) ++expr
;
6308 if (pos
>= 128) return NULL
;
6309 if (ch
== ')') { --expr
; break; }
6311 if (isAlphaDigit(ch
) || ch
== '$' || ch
== '.' || ch
== '_' || ch
== '@') {
6317 if (pos
< 1) return NULL
;
6319 if (!urasm_is_valid_name(buf
)) return NULL
;
6324 static const char *fnDefKn (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
, int qtype
) {
6327 if ((expr
= readLabelName(lbl
, expr
)) == NULL
) { *error
= UR_EXPRERR_LABEL
; return NULL
; }
6329 if (!donteval
) res
->val
= (isLabelDefinedOrKnown(lbl
, addr
, qtype
) != 0);
6333 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
); }
6334 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
); }
6337 static const char *fnAligned256 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6338 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6340 if (!donteval
) res
->val
= (res
->val
%256 ? 0 : 1);
6345 static const char *fnSameSeg (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6346 urasm_exprval_t v0
, v1
;
6348 urasm_exprval_init(&v0
);
6349 urasm_exprval_init(&v1
);
6350 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
6351 if (*error
) return expr
;
6353 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
6354 if (*error
) return expr
;
6356 if (!donteval
) res
->val
= (v0
.val
/256 == v1
.val
/256);
6361 static const char *fnAlign (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6362 urasm_exprval_t v0
, v1
;
6363 urasm_exprval_init(&v0
);
6364 urasm_exprval_init(&v1
);
6365 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
6366 if (*error
) return expr
;
6368 if (fnNextChar(expr
) == ',') {
6370 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
6371 if (*error
) return expr
;
6377 if (v1
.val
< 1) { *error
= UR_EXPRERR_FUNC
; return NULL
; }
6378 res
->val
= (v0
.val
+v1
.val
-1)/v1
.val
*v1
.val
;
6384 static const char *fnLow (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6385 const char *ee
= expr
;
6386 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6389 if (res
->fixuptype
== UR_FIXUP_HIBYTE
) {
6390 *error
= UR_EXPRERR_FUNC
; return ee
;
6392 res
->fixuptype
= UR_FIXUP_LOBYTE
;
6399 static const char *fnHigh (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6400 const char *ee
= expr
;
6401 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6404 if (res
->fixuptype
== UR_FIXUP_LOBYTE
) {
6405 *error
= UR_EXPRERR_FUNC
; return ee
;
6407 res
->fixuptype
= UR_FIXUP_HIBYTE
;
6408 res
->val
= (res
->val
>>8)&0xff;
6414 static const char *fnAbs (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6415 const char *ee
= expr
;
6416 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6419 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
6420 *error
= UR_EXPRERR_FUNC
; return ee
;
6422 res
->val
= abs(res
->val
);
6428 static const char *fnBSwap (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6429 const char *ee
= expr
;
6430 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6433 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
6434 *error
= UR_EXPRERR_FUNC
; return ee
;
6436 res
->val
= ((res
->val
>>8)&0xff)|((res
->val
&0xff)<<8);
6442 static const char *fnScrAddr8xn (int nmul
, urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6443 int32_t scrbase
= 0x4000;
6445 const char *ee
= expr
;
6446 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
6447 // first argument is `x`
6448 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6450 // second argument is `y`
6452 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6454 // optional third arg is screen base
6456 if (expr
[0] == ',') {
6458 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6459 scrbase
= res
->val
&0xffff;
6463 //urasm_exprval_clear(res);
6464 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
6465 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
6466 if (y
< 0 || y
> 191) fatal("invalid y coordinate: %d", y
/nmul
);
6472 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
6478 static const char *fnScrAddr8x1 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6479 return fnScrAddr8xn(1, res
, expr
, addr
, donteval
, defined
, error
);
6482 static const char *fnScrAddr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6483 return fnScrAddr8xn(8, res
, expr
, addr
, donteval
, defined
, error
);
6487 static const char *fnScrAttr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6488 int32_t scrbase
= 0x4000;
6490 const char *ee
= expr
;
6491 // first argument is `x`
6492 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6494 // second argument is `y`
6496 urasm_exprval_clear(res
);
6497 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6499 // optional third arg is screen base
6501 if (expr
[0] == ',') {
6503 urasm_exprval_clear(res
);
6504 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6505 scrbase
= res
->val
&0xffff;
6507 urasm_exprval_clear(res
);
6510 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
6511 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
6512 if (y
< 0 || y
> 23) fatal("invalid y coordinate: %d", y
);
6513 res
->val
= scrbase
+6144+y
*32+x
;
6519 static const char *fnMArgToStr (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6520 const char *ee
= expr
;
6521 // argument is macro argument name
6522 expr
= strSkipSpacesConst(expr
);
6523 if (expr
[0] != '=') { *error
= UR_EXPRERR_FUNC
; return ee
; }
6525 if (!isAlpha(expr
[0]) && expr
[0] != '_') { *error
= UR_EXPRERR_FUNC
; return ee
; }
6526 const char *nend
= expr
+1;
6527 while (isAlphaDigit(*nend
) || *nend
== '_') ++nend
;
6528 if (nend
-expr
> 127) { *error
= UR_EXPRERR_FUNC
; return ee
; }
6530 memset(name
, 0, sizeof(name
));
6531 memcpy(name
, expr
, nend
-expr
);
6536 expr
= strSkipSpacesConst(expr
);
6537 if (expr
[0] == '[') {
6538 expr
= strSkipSpacesConst(expr
+1);
6539 if (expr
[0] != ']') {
6542 if (expr
[0] == '+') ++expr
;
6543 else if (expr
[0] == '-') { doneg
= 1; ++expr
; }
6545 while (isDigit(expr
[0])) {
6546 index
= index
*10+(expr
[0]-'0');
6547 if (index
>= 0x1fffffff) fatal("`margtostr` index too high");
6550 expr
= strSkipSpacesConst(expr
);
6551 if (doneg
) index
= -index
;
6553 if (expr
[0] != ']') fatal("`margtostr` invalid index syntax");
6558 if (curmacro
== NULL
) fatal("`margtostr` outside of macro");
6560 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
6561 if (strEquCI(name
, curmacro
->mac
->argnames
[f
])) {
6562 // found argument, convert it to string
6564 res
->str
= realloc(res
->str
, strlen(curmacro
->argvals
[f
])+1);
6565 strcpy(res
->str
, curmacro
->argvals
[f
]);
6567 const size_t slen
= strlen(curmacro
->argvals
[f
]);
6570 if (index
> slen
) fatal("index out of string");
6571 index
= (int)slen
-index
;
6573 if (index
>= slen
) fatal("index out of string");
6574 res
->str
= realloc(res
->str
, 2);
6575 res
->str
[0] = curmacro
->argvals
[f
][index
];
6579 for (char *s
= res
->str
; *s
; ++s
) *s
= toLower(*s
);
6580 //fprintf(stderr, "<%s>\n", res->str);
6583 res
->val
= ((unsigned char)res
->str
[0]);
6584 res
->val
|= ((unsigned char)res
->str
[1])<<8;
6586 res
->val
= (unsigned char)res
->str
[0];
6594 fatal("unknown macro argument '%s'", name
);
6600 static const char *fnStrLen (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6601 expr
= strSkipSpacesConst(expr
);
6604 case '"': case '\'': /* string literal? */
6606 char *a
= (char *)expr
+1;
6607 (void)parseStr(&a
, expr
[0], &stlen
);
6608 expr
= (const char *)a
;
6611 default: /* expression */
6614 urasm_exprval_init(&r
);
6615 expr
= urasm_expr_ex(&r
, expr
, disp
, &donteval
, defined
, error
);
6616 if (*error
) fatalUrLib(*error
);
6617 if (!r
.str
) fatal("string expected for `strlen()`");
6618 stlen
= (int)strlen(r
.str
);
6619 urasm_exprval_clear(&r
);
6625 urasm_exprval_setint(res
, stlen
);
6631 static void registerFunctions (void) {
6632 urasm_expr_register_func("defined", fnDefined
);
6633 urasm_expr_register_func("known", fnKnown
);
6634 urasm_expr_register_func("aligned256", fnAligned256
);
6635 urasm_expr_register_func("align", fnAlign
);
6636 urasm_expr_register_func("sameseg", fnSameSeg
);
6637 urasm_expr_register_func("low", fnLow
);
6638 urasm_expr_register_func("high", fnHigh
);
6639 urasm_expr_register_func("abs", fnAbs
);
6640 urasm_expr_register_func("bswap", fnBSwap
);
6642 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8
);
6643 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1
);
6644 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8
);
6646 urasm_expr_register_func("marg2str", fnMArgToStr
);
6647 urasm_expr_register_func("strlen", fnStrLen
);
6651 ///////////////////////////////////////////////////////////////////////////////
6652 // preparing another pass
6654 static void initPass (void) {
6655 asmMode
= AMODE_NORMAL
;
6656 currSrcLine
= asmText
;
6670 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
6671 lastSeenGlobalLabel
= NULL
;
6672 //lastSeenGlobalLabel = strdup(" [MAIN] ");
6675 urasm_use_old_priorities
= 0;
6676 strcpy(lastFindLabelName
, "");
6677 strcpy(currSeenLabel
, "");
6681 static int postPass (void) {
6682 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
6683 lastSeenGlobalLabel
= NULL
;
6684 if (checkLabels()) return -1;
6685 if (ifCount
!= 0) fatal("unbalanced IFs");
6690 ///////////////////////////////////////////////////////////////////////////////
6691 static int labelCmp (const void *aa
, const void *bb
) {
6692 if (aa
== bb
) return 0;
6693 const UrLabelInfo
*a
= *(const UrLabelInfo
**)aa
;
6694 const UrLabelInfo
*b
= *(const UrLabelInfo
**)bb
;
6696 a
->value
< b
->value
? -1 :
6697 a
->value
> b
->value
? 1 :
6702 static __attribute__((unused
)) int strStartsWith (const char *s
, const char *pat
) {
6703 if (!pat
|| !pat
[0]) return 0;
6704 if (!s
|| !s
[0]) return 0;
6705 while (*s
&& *pat
) {
6706 if (*s
!= *pat
) return 0;
6714 static int isGoodLabelForRef (const UrLabelInfo
*ll
) {
6718 // do not output `=` labels
6719 if (ll->type == LBL_TYPE_ASS) return 0;
6720 // do not output struct labels
6721 if (ll->type == LBL_TYPE_STOFFS) return 0;
6724 if (urasm_dump_all_labels
) {
6725 if (strStartsWith(ll
->name
, "UFO_") || strStartsWith(ll
->name
, "USE_")) return 0;
6729 for (const char *s
= ll
->name
; *s
; ++s
) {
6731 if (ch
>= 'A' && ch
<= 'Z') continue;
6732 if (ch
>= '0' && ch
<= '9') continue;
6733 if (ch
== '_') continue;
6741 static void writeLabelsFile (const char *fname
) {
6742 if (!fname
|| !fname
[0]) return;
6746 for (const UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) {
6747 if (isGoodLabelForRef(ll
)) ++lcount
;
6750 UrLabelInfo
**larr
= NULL
;
6752 // create labels array
6753 larr
= (UrLabelInfo
**)malloc(sizeof(UrLabelInfo
*)*lcount
);
6755 for (UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) {
6756 if (isGoodLabelForRef(ll
)) larr
[lcount
++] = ll
;
6759 qsort(larr
, lcount
, sizeof(UrLabelInfo
*), &labelCmp
);
6763 FILE *fo
= fopen(fname
, "w");
6765 for (int f
= 0; f
< lcount
; ++f
) {
6766 UrLabelInfo
*ll
= larr
[f
];
6767 //if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
6768 if (!ll
->name
) continue; // just in case
6769 if (ll
->name
[0] == '{') continue; // we don't have these, but it is still reserved
6770 if (ll
->value
< 0) {
6771 fprintf(fo
, "%d %s", ll
->value
, ll
->name
);
6773 fprintf(fo
, "#%04X %s", (unsigned)ll
->value
, ll
->name
);
6777 case LBL_TYPE_VERY_SPECIAL
: fprintf(fo
, "WTF"); break;
6778 case LBL_TYPE_UNKNOWN
: fprintf(fo
, "UNKNOWN"); break;
6779 case LBL_TYPE_ASS
: fprintf(fo
, "assign"); break;
6780 case LBL_TYPE_EQU
: fprintf(fo
, "equ"); break;
6781 case LBL_TYPE_CODE
: fprintf(fo
, "code"); break;
6782 case LBL_TYPE_STOFS
: fprintf(fo
, "stofs"); break;
6783 case LBL_TYPE_DATA
: fprintf(fo
, "data"); break;
6784 default: fprintf(fo
, "WTF"); break;
6795 ///////////////////////////////////////////////////////////////////////////////
6798 static struct option longOpts
[] = {
6799 {"org", required_argument
, NULL
, 600},
6800 {"define", required_argument
, NULL
, 601},
6801 {"defzero", required_argument
, NULL
, 602},
6802 {"outdir", required_argument
, NULL
, 660},
6803 {"reffile", optional_argument
, NULL
, 669},
6804 {"outfile", required_argument
, NULL
, 665},
6805 {"castrates", optional_argument
, NULL
, 656},
6807 {"sna", 0, NULL
, 's'},
6808 {"sna128", 0, NULL
, 'S'},
6809 {"tap", 0, NULL
, 't'},
6810 {"autotap", 0, NULL
, 'T'},
6811 {"raw", 0, NULL
, 'r'},
6812 {"autodmb", 0, NULL
, 'B'},
6813 {"dmb", 0, NULL
, 'b'},
6814 {"none", 0, NULL
, 'n'},
6815 {"help", 0, NULL
, 'h'},
6816 {"hob", 0, NULL
, 'H'},
6817 {"scl", 0, NULL
, 'l'},
6818 {"autoscl", 0, NULL
, 'L'},
6819 {"monoscl", 0, NULL
, 'm'},
6820 {"autosclmono", 0, NULL
, 'M'},
6821 {"dsk", 0, NULL
, 690},
6822 {"dsk720", 0, NULL
, 691},
6823 {"adsk", 0, NULL
, 692},
6824 {"adsk720", 0, NULL
, 693},
6825 {"fixups", optional_argument
, NULL
, 'F'},
6831 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
6834 static void usage (const char *pname
) {
6836 "usage: %s [options] infile\n"
6837 "default infiles:", pname
);
6838 for (int f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
6841 " -s --sna write 48K .SNA file with autostart\n"
6842 " -S --sna128 write 148K .SNA file with autostart\n"
6843 " -t --tap write .tap file\n"
6844 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
6845 " -r --raw write raw file(s)\n"
6846 " -b --dmb write DMB file\n"
6847 " -B --autodmb write DMB file with autostart\n"
6848 " -H --hob write HoBeta code file(s)\n"
6849 " -l --scl write SCL TR-DOS archive\n"
6850 " -L --autoscl write autostarting SCL TR-DOS archive\n"
6851 " -m --monoscl write SCL with monoloader\n"
6852 /*" -M --monosclauto write autorun SCL with monoloader\n"*/
6853 " --dsk write +3DOS 180KB disk\n"
6854 " --dsk720 write +3DOS 720KB disk\n"
6855 " --adsk write +3DOS 180KB disk with autostart\n"
6856 " --adsk720 write +3DOS 720KB disk with autostart\n"
6857 " -n --none write nothing\n"
6858 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
6859 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
6860 " text: .txt file\n"
6861 " asm: address lists with counters\n"
6862 " asmdiff: 2nd and other addresses are relative, with counters\n"
6863 " asmz: address lists, with 0 end markers\n"
6864 " -h --help this help\n"
6866 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
6867 " --org xxx set ORG\n"
6868 " --define val perform 'val EQU 1'\n"
6869 " --defzero val perform 'val EQU 0'\n"
6870 " --outdir dir output dir for resulting files (default: current)\n"
6871 " --outfile fname output file name for image formats (default: main asm file name)\n"
6872 " --castrates allow '&abc' hex numbers (and disable '&b' prefix)\n"
6877 ///////////////////////////////////////////////////////////////////////////////
6878 //#include "urforth.c"
6880 __attribute__((noreturn
))
6881 void ufoFatalError (void) {
6882 longjmp(errJP
, 666);
6885 uint32_t ufoZXGetU8 (uint32_t addr
) {
6887 return getByte(addr
);
6890 void ufoZXPutU8 (uint32_t addr
, uint32_t v
) {
6896 uint32_t ufoZXGetU16 (uint32_t addr
) {
6898 return getWord(addr
);
6901 void ufoZXPutU16 (uint32_t addr
, uint32_t v
) {
6907 void ufoZXEmitU8 (uint32_t v
) {
6908 //if (!zxlblLastByte) ufoFatal("label 'latest_byte' not found");
6912 void ufoZXEmitU16 (uint32_t v
) {
6913 //if (!zxlblLastByte) ufoFatal("label 'latest_byte' not found");
6914 emitWord(v
&0xffffU
);
6917 int ufoZXGetReserved (uint32_t addr
) {
6918 return memresv
[addr
&0xffffU
];
6921 void ufoZXSetReserved (uint32_t addr
, int resvflag
) {
6922 memresv
[addr
&0xffffU
] = resvflag
;
6925 int ufoZXGetPass (void) {
6929 uint32_t ufoZXGetOrg (void) {
6933 void ufoZXSetOrg (uint32_t addr
) {
6934 if (addr
> 0xffff) ufoFatal("invalid ORG address: %u", addr
);
6935 pc
= disp
= (uint16_t)addr
;
6937 wasOrg
= 1; // so next `ORG` will not reset it
6938 ent
= (uint16_t)addr
;
6942 uint32_t ufoZXGetDisp (void) {
6946 void ufoZXSetDisp (uint32_t addr
) {
6947 if (addr
> 0xffff) ufoFatal("invalid DISP address: %u", addr
);
6948 disp
= (uint16_t)addr
;
6951 uint32_t ufoZXGetEnt (void) {
6952 return (wasOrg
? (uint32_t)ent
: ~0u);
6955 void ufoZXSetEnt (uint32_t addr
) {
6956 if (addr
> 0xffff) ufoFatal("invalid ENT address: %u", addr
);
6957 wasOrg
= 1; // so next `ORG` will not reset it
6958 ent
= (uint16_t)addr
;
6961 static int ufoZXLType2UFO (int type
) {
6963 case LBL_TYPE_VERY_SPECIAL
: return UFO_ZX_LABEL_UNDEFINED
;
6964 case LBL_TYPE_UNKNOWN
: return UFO_ZX_LABEL_UNKNOWN
;
6965 case LBL_TYPE_ASS
: return UFO_ZX_LABEL_VAR
;
6966 case LBL_TYPE_EQU
: return UFO_ZX_LABEL_EQU
;
6967 case LBL_TYPE_CODE
: return UFO_ZX_LABEL_CODE
;
6968 case LBL_TYPE_DATA
: return UFO_ZX_LABEL_DATA
;
6969 case LBL_TYPE_STOFS
: return UFO_ZX_LABEL_STOFS
;
6971 ufoFatal("WTF?! unknown label type %d", type
);
6974 int ufoZXGetLabelType (const char *name
) {
6975 if (name
== NULL
|| name
[0] == 0) return UFO_ZX_LABEL_UNDEFINED
;
6976 UrLabelInfo
*lbl
= findAddLabel(name
);
6977 if (lbl
== NULL
) return UFO_ZX_LABEL_UNDEFINED
;
6978 return ufoZXLType2UFO(lbl
->type
);
6981 // return !0 to stop; returns cb result
6982 uint32_t ufoZXForeachLabel (uint32_t (*cb
) (const char *name
, int type
, int value
, void *udata
), void *udata
) {
6984 UrLabelInfo
*c
= labels
;
6985 while (res
== 0 && c
!= NULL
) {
6986 if (c
->type
!= LBL_TYPE_VERY_SPECIAL
&& c
->type
!= LBL_TYPE_UNKNOWN
) {
6987 res
= cb(c
->name
, c
->type
, c
->value
, udata
);
6993 // this also creates labels
6994 void ufoZXSetLabelValue (const char *name
, int type
, int value
) {
6995 if (name
== NULL
|| name
[0] == 0) ufoFatal("empty label name");
6997 // convert label type
6999 case UFO_ZX_LABEL_UNDEFINED
: ufoFatal("cannot set label with undefined type");
7000 case UFO_ZX_LABEL_UNKNOWN
: ufoFatal("cannot set label with unknown type");
7001 case UFO_ZX_LABEL_VAR
: type
= LBL_TYPE_ASS
; break;
7002 case UFO_ZX_LABEL_EQU
: type
= LBL_TYPE_EQU
; break;
7003 case UFO_ZX_LABEL_CODE
: type
= LBL_TYPE_CODE
; break;
7004 case UFO_ZX_LABEL_DATA
: type
= LBL_TYPE_DATA
; break;
7005 case UFO_ZX_LABEL_STOFS
: type
= LBL_TYPE_STOFS
; break;
7006 default: ufoFatal("invalid label type %d", type
);
7009 UrLabelInfo
*lbl
= findAddLabel(name
);
7011 if (lbl
->type
!= LBL_TYPE_UNKNOWN
&& lbl
->type
!= type
) {
7012 ufoFatal("invalid label '%s' type", name
);
7014 if (type
!= LBL_TYPE_ASS
) {
7015 if (lbl
->type
>= 0 && lbl
->value
!= value
) ufoFatal("invalid label '%s' value", name
);
7019 if (lbl
->type
== LBL_TYPE_UNKNOWN
) lbl
->type
= type
;
7022 int ufoZXGetLabelValue (const char *name
) {
7023 if (name
== NULL
|| name
[0] == 0) return 0;
7024 UrLabelInfo
*lbl
= findAddLabel(name
);
7025 return (lbl
? lbl
->value
: 0);
7028 const char *ufoGetSrcLine (const char **fname
, int *lnum
) {
7029 SourceLine
*sl
= nextUFOSrcLine();
7030 if (sl
== NULL
) return NULL
;
7033 return (sl
->line
!= NULL
? sl
->line
: "");
7036 char *ufoCreateIncludeName (const char *fname
, int assystem
, const char *lastIncPath
) {
7037 assystem
= (assystem
? -666 : -669);
7038 if (lastIncPath
== NULL
) lastIncPath
= ufoIncludeDir
;
7039 return createIncludeName(fname
, assystem
, NULL
, lastIncPath
);
7042 void ufoZXPostInit (void) {
7046 ///////////////////////////////////////////////////////////////////////////////
7049 int main (int argc
, char *argv
[]) {
7051 const char *pname
= argv
[0];
7052 char *inFile
= NULL
;
7053 char **defines
= NULL
, **values
= NULL
;
7060 initUFEInclideDir();
7062 urasm_getbyte
= getByte
;
7063 urasm_putbyte
= putByte
;
7064 urasm_label_by_name
= findLabelCB
;
7065 urasm_getval
= getValueCB
;
7066 urasm_expand
= expandCB
;
7067 urasm_fixup_operand
= fixupOperandCB
;
7069 //strcpy(tapeLoaderName, "cargador ");
7070 tapeLoaderName
[0] = 0;
7072 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
7073 while ((c
= getopt_long(argc
, argv
, "sStTrBbnhHFLlMm", longOpts
, NULL
)) >= 0) {
7076 case 'S': /*optRunSNA = 1;*/ optSNA48
= 0; optWriteType
= 's'; optWTChanged
= 1; break;
7077 case 's': /*optRunSNA = 0;*/ optSNA48
= 1; optWriteType
= 's'; optWTChanged
= 1; break;
7078 case 'T': optRunTape
= 1; optWriteType
= 't'; optWTChanged
= 1; break;
7079 case 't': optRunTape
= 0; optWriteType
= 't'; optWTChanged
= 1; break;
7080 case 'L': optRunSCL
= 1; optWriteType
= 'S'; optWTChanged
= 1; break; /* with boot */
7081 case 'l': optRunSCL
= -1; optWriteType
= 'S'; optWTChanged
= 1; break; /* no boot */
7082 case 'm': optRunSCL
= -1; optWriteType
= 'M'; optWTChanged
= 1; break; /* no boot */
7083 case 'M': optRunSCL
= 1; optWriteType
= 'M'; optWTChanged
= 1; break; /* with boot */
7084 case 'r': case 'n': optWriteType
= c
; optWTChanged
= 1; break;
7085 case 'b': optRunTape
= 0; optRunDMB
= 0; optWriteType
= 'd'; optWTChanged
= 1; break;
7086 case 'B': optRunTape
= 0; optRunDMB
= 1; optWriteType
= 'd'; optWTChanged
= 1; break;
7087 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
7088 case 'H': optWriteType
= 'H'; optWTChanged
= 1; break;
7091 if (optarg
!= NULL
) {
7092 if (strcmp(optarg
, "asm") == 0 || strcmp(optarg
, "zas") == 0) {
7094 } else if (strcmp(optarg
, "asmdiff") == 0 || strcmp(optarg
, "zasdiff") == 0) {
7096 } else if (strcmp(optarg
, "asmz") == 0 || strcmp(optarg
, "zasz") == 0) {
7098 } else if (strcmp(optarg
, "text") == 0) {
7101 fprintf(stderr
, "FATAL: invalid fixup type: '%s'\n", optarg
);
7108 //fprintf(stderr, "ORG: %d\n", c);
7109 if (c
< 0 || c
> 65535) {
7110 fprintf(stderr
, "FATAL: invalid ORG: %d\n", c
);
7113 start_pc
= start_disp
= start_ent
= c
;
7116 //fprintf(stderr, "define: [%s]\n", optarg);
7117 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
7118 values
= realloc(values
, sizeof(char *)*(defcount
+1));
7119 defines
[defcount
] = strdup(optarg
);
7120 values
[defcount
] = strdup("1");
7123 case 602: // defzero
7124 //fprintf(stderr, "defzero: [%s]\n", optarg);
7125 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
7126 values
= realloc(values
, sizeof(char *)*(defcount
+1));
7127 defines
[defcount
] = strdup(optarg
);
7128 values
[defcount
] = strdup("0");
7132 if (optOutputDir
!= NULL
) free(optOutputDir
);
7133 optOutputDir
= strdup(optarg
);
7136 if (optOutputFile
!= NULL
) free(optOutputFile
);
7137 optOutputFile
= strdup(optarg
);
7139 case 669: // reffile
7140 if (refFileName
) free(refFileName
);
7141 refFileName
= (optarg
? strdup(optarg
) : NULL
);
7144 case 690: case 692: // +3DOS 180K disk
7145 optRunDSK
= (c
== 692);
7146 optDskType
= P3DSK_PCW_SS
;
7150 case 691: case 693: // +3DOS 720K disk
7151 optRunDSK
= (c
== 693);
7152 optDskType
= P3DSK_PCW_DS
;
7156 case 656: // hex castrates
7157 if (optarg
!= NULL
) {
7158 if (strcmp(optarg
, "tan") == 0 || strcmp(optarg
, "yes") == 0) {
7159 urasm_allow_hex_castrates
= 1;
7160 } else if (strcmp(optarg
, "ona") == 0 || strcmp(optarg
, "no") == 0) {
7161 urasm_allow_hex_castrates
= 0;
7163 fprintf(stderr
, "FATAL: i don't know what '%s' means.\n", optarg
);
7167 urasm_allow_hex_castrates
= 1;
7173 if (optind
>= argc
) {
7174 // try to find default input file
7175 for (int f
= 0; defInFiles
[f
]; ++f
) {
7176 if (!access(defInFiles
[f
], R_OK
)) {
7177 inFile
= strdup(defInFiles
[f
]);
7182 inFile
= strdup(argv
[optind
]);
7185 if (!inFile
|| !inFile
[0]) {
7187 fprintf(stderr
, "ERROR: no input file!\n");
7191 if (optOutputDir
== NULL
) optOutputDir
= strdup(".");
7193 for (char *ss
= optOutputDir
; *ss
; ++ss
) if (*ss
== '\\') *ss
= '/';
7196 char *ee
= strrchr(optOutputDir
, '/');
7197 if (ee
&& ee
!= optOutputDir
) *ee
= 0;
7199 if (!optOutputDir
[0]) { free(optOutputDir
); optOutputDir
= strdup("."); }
7201 registerInstructions();
7202 registerFunctions();
7204 res
= asmTextLoad(inFile
, 0);
7206 for (int f
= 0; f
< defcount
; ++f
) {
7207 if (labelDoEQU(defines
[f
], values
[f
]) != 0) {
7208 fprintf(stderr
, "FATAL: can't define label: '%s'\n", defines
[f
]);
7211 //fprintf(stderr, "****SET**** <%s> to <%s>\n", defines[f], values[f]);
7214 for (pass
= 0; pass
<= 1; ++pass
) {
7216 printf("pass %d\n", pass
);
7217 //fprintf(stderr, "===========pass %d===========\n", pass);
7218 setCurSrcLine(asmText
);
7219 if (setjmp(errJP
)) { res
= 1; break; }
7220 while (currSrcLine
) processCurrentLine();
7221 if (postPass()) { res
= 1; break; }
7226 char *oc
= strdup(inFile
);
7227 char *pd
= strrchr(oc
, '.');
7229 if (pd
&& !strchr(oc
, '/') && !strchr(oc
, '\\')) *pd
= '\0';
7231 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
7233 switch (optWriteType
) {
7234 case 's': saveSna(oc
, optSNA48
); break;
7235 case 't': saveTap(oc
); break;
7236 case 'r': saveRaw(oc
); break;
7237 case 'd': saveDMB(oc
); break;
7238 case 'H': saveHob(oc
); break;
7239 case 'S': saveSCL(oc
); break;
7240 case 'M': saveSCLMono(oc
); break;
7241 case '3': saveDSK(oc
); break;
7244 if (optWriteFixups
) writeFixups();
7246 /* build ref file name */
7247 if (!refFileName
|| !refFileName
[0]) {
7248 if (refFileName
) free(refFileName
);
7249 refFileName
= malloc(strlen(inFile
)+128);
7250 strcpy(refFileName
, inFile
);
7251 char *ext
= strrchr(refFileName
, '.');
7253 strcat(refFileName
, ".ref");
7257 for (char *ts
= refFileName
; *ts
; ++ts
) {
7258 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
7261 char *slash
= strrchr(refFileName
, '/');
7263 if (!slash
|| slash
< ext
) {
7264 strcpy(ext
, ".ref");
7266 strcat(refFileName
, ".ref");
7270 writeLabelsFile(refFileName
);
7271 printf("refs written to '%s'\n", refFileName
);
7276 fprintf(stderr
, "ERROR: loading error!\n");
7281 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
7292 if (inFile
) free(inFile
);
7293 if (sysIncludeDir
) free(sysIncludeDir
);
7294 if (lastIncludePath
) free(lastIncludePath
);
7295 if (lastSysIncludePath
) free(lastSysIncludePath
);
7296 for (int f
= defcount
-1; f
>= 0; --f
) {
7300 if (defines
!= NULL
) { free(values
); free(defines
); }
7301 if (optOutputFile
) free(optOutputFile
);
7302 if (optOutputDir
) free(optOutputDir
);
7304 return (res
? 1 : 0);