2 // coded by Ketmar // Invisible Vector
19 #include <sys/types.h>
27 #include "liburasm/liburasm.h"
28 #include "libfdc/libfdc.h"
29 #include "liburforth/urforth.h"
34 #define UR_FORCE_INLINE static inline __attribute__((always_inline))
35 #define UR_INLINE static inline
43 #define MAYBE_UNUSED __attribute__((unused))
45 #define lambda(return_type, body_and_args) ({ \
46 return_type __fn__ body_and_args \
51 // ////////////////////////////////////////////////////////////////////////// //
52 static const char *ur_assert_failure (const char *cond
, const char *fname
, int fline
,
55 for (const char *t
= fname
; *t
; ++t
) {
57 if (*t
== '/' || *t
== '\\') fname
= t
+1;
59 if (*t
== '/') fname
= t
+1;
63 fprintf(stderr
, "\n%s:%d: Assertion in `%s` failed: %s\n", fname
, fline
, func
, cond
);
68 #define ur_assert(cond_) do { if (__builtin_expect((!(cond_)), 0)) { ur_assert_failure(#cond_, __FILE__, __LINE__, __PRETTY_FUNCTION__); } } while (0)
71 //==========================================================================
75 //==========================================================================
76 static uint32_t joaatHashStr (const void *buf
) {
77 uint32_t hash
= 0x29a;
78 const uint8_t *s
= (const uint8_t *)buf
;
79 size_t len
= (s
!= NULL
? strlen((const char *)s
) : 0);
93 //==========================================================================
97 //==========================================================================
98 static uint32_t joaatHashStrCI (const void *buf
) {
99 uint32_t hash
= 0x29a;
100 const uint8_t *s
= (const uint8_t *)buf
;
101 size_t len
= (s
!= NULL
? strlen((const char *)s
) : 0);
103 hash
+= (*s
++)|0x20; // this converts ASCII capitals to locase (and destroys other, but who cares)
115 ////////////////////////////////////////////////////////////////////////////////
116 UR_FORCE_INLINE
int isSpace (char ch
) { return (ch
&& ((unsigned)(ch
&0xff) <= 32 || ch
== 127)); }
117 UR_FORCE_INLINE
int isAlpha (char ch
) { return ((ch
>= 'A' && ch
<= 'Z') || (ch
>= 'a' && ch
<= 'z')); }
118 UR_FORCE_INLINE
int isDigit (char ch
) { return (ch
>= '0' && ch
<= '9'); }
119 UR_FORCE_INLINE
int isHexDigit (char ch
) { return ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F') || (ch
>= 'a' && ch
<= 'f')); }
120 UR_FORCE_INLINE
int isAlphaDigit (char ch
) { return (isAlpha(ch
) || isDigit(ch
)); }
122 UR_FORCE_INLINE
char toUpper (char ch
) { return (ch
>= 'a' && ch
<= 'z' ? ch
-'a'+'A' : ch
); }
123 UR_FORCE_INLINE
char toLower (char ch
) { return (ch
>= 'A' && ch
<= 'Z' ? ch
-'A'+'a' : ch
); }
125 static int digitInBase (char ch
, int base
) {
126 if (ch
< '0') return -1;
128 if (ch
>= '0'+base
) return -1;
132 if (ch
<= '9') return ch
-'0';
133 if (ch
< 'A' || ch
> 'A'+base
-10) return -1;
135 return (ch
< base
? ch
: -1);
139 //==========================================================================
143 //==========================================================================
144 static int strEquCI (const char *s0
, const char *s1
) {
146 while (res
&& *s0
&& *s1
) {
147 char c0
= *s0
++; if (c0
>= 'A' && c0
<= 'Z') c0
= c0
- 'A' + 'a';
148 char c1
= *s1
++; if (c1
>= 'A' && c1
<= 'Z') c1
= c1
- 'A' + 'a';
151 return (res
&& s0
[0] == 0 && s1
[0] == 0);
155 ////////////////////////////////////////////////////////////////////////////////
156 static char *strprintfVA (const char *fmt
, va_list vaorig
) {
161 if (buf
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
167 olen
= vsnprintf(buf
, len
, fmt
, va
);
169 if (olen
>= 0 && olen
< len
) return buf
;
170 if (olen
< 0) olen
= len
*2-1;
171 nb
= realloc(buf
, olen
+1);
172 if (nb
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
179 static __attribute__((format(printf
,1,2))) char *strprintf (const char *fmt
, ...) {
184 buf
= strprintfVA(fmt
, va
);
190 ///////////////////////////////////////////////////////////////////////////////
195 AMODE_UFO
= 1, // scanning UrForth code
198 static int asmMode
= AMODE_NORMAL
;
200 static char *ufoIncludeDir
= NULL
;
201 static char *sysIncludeDir
= NULL
;
202 static char *refFileName
= NULL
;
203 static char *lastIncludePath
= NULL
;
204 static char *lastSysIncludePath
= NULL
;
205 static int urasm_dump_all_labels
= 1; // 0: do not write all-uppercase labels
207 #define MAX_LINE_SIZE (1024*1024)
208 static char currLine
[MAX_LINE_SIZE
]; /* current processing line; can be freely modified */
210 // set by `processLabel()`
211 static char currSeenLabel
[1024];
213 static uint32_t ufoMacroVocId
= 0;
216 ///////////////////////////////////////////////////////////////////////////////
219 static void initInclideDir (void) {
220 const char *id
= getenv("URASM_INCLUDE_DIR");
222 sysIncludeDir
= strdup(id
);
225 memset(myDir
, 0, sizeof(myDir
));
227 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
230 char *p
= (char *)strrchr(myDir
, '/');
231 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
234 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
235 char *p
= strrchr(myDir
, '\\');
236 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
238 strcat(myDir
, "/libs");
239 sysIncludeDir
= strdup(myDir
);
241 while (sysIncludeDir
[0] && sysIncludeDir
[strlen(sysIncludeDir
)-1] == '/') sysIncludeDir
[strlen(sysIncludeDir
)-1] = '\0';
242 if (!sysIncludeDir
[0]) strcpy(sysIncludeDir
, ".");
246 static void initUFEInclideDir (void) {
247 const char *id
= getenv("URASM_URFORTH_INCLUDE_DIR");
249 ufoIncludeDir
= strdup(id
);
252 memset(myDir
, 0, sizeof(myDir
));
254 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
257 char *p
= (char *)strrchr(myDir
, '/');
258 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
261 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
262 char *p
= strrchr(myDir
, '\\');
263 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
265 strcat(myDir
, "/urflibs");
266 ufoIncludeDir
= strdup(myDir
);
268 while (ufoIncludeDir
[0] && ufoIncludeDir
[strlen(ufoIncludeDir
)-1] == '/') ufoIncludeDir
[strlen(ufoIncludeDir
)-1] = '\0';
269 if (!ufoIncludeDir
[0]) strcpy(ufoIncludeDir
, ".");
273 ///////////////////////////////////////////////////////////////////////////////
276 /* trim trailing spaces and comments; normalize colons */
277 static void normalizeStr (char *s
) {
278 if (asmMode
== AMODE_UFO
) return; // do nothing
280 /* now skip all shit */
282 const char ch
= *p
++;
283 /* check for "af'" */
284 if (ch
== '\'' && p
-s
>= 2 && toLower(p
[-2] == 'a') && toLower(p
[-1] == 'f')) continue;
286 if (ch
== ';') { p
[-1] = 0; break; }
288 if (ch
== '"' || ch
== '\'') {
291 const char c1
= *p
++;
292 if (c1
== qch
) break;
293 if (c1
== '\\' && *p
) ++p
;
297 /* reduce and normalise colons */
299 --p
; /* back to colon */
300 /* remove spaces before colon */
302 while (t
!= s
&& isSpace(t
[-1])) --t
;
303 if (t
!= p
) memmove(t
, p
, strlen(p
)+1);
305 ur_assert(p
[0] == ':');
306 ++p
; /* skip colon */
307 /* remove following spaces and colons */
309 while (*t
== ':' || isSpace(*t
)) ++t
;
310 if (t
!= p
) memmove(p
, t
, strlen(t
)+1);
314 /* done; trim trailing spaces and colons */
315 size_t slen
= strlen(s
);
316 while (slen
> 0 && (isSpace(s
[slen
-1]) || s
[slen
-1] == ':')) --slen
;
321 /* check if string starts with the given command (case-insensitive) */
322 /* returns NULL or pointer to args */
323 /* skips spaces after command if any */
324 static char *strIsCommand (const char *command
, char *str
) {
325 for (int cnt
= 1; cnt
> 0; --cnt
) {
326 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
327 if (*str
== '$') ++str
;
328 for (; *command
&& *str
; ++command
, ++str
) {
329 if (toUpper(*command
) != toUpper(*str
)) return NULL
; // alas
331 if (*command
) return NULL
; // alas
332 if (*str
&& isAlphaDigit(*str
)) return NULL
; // alas
333 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
334 if (asmMode
!= AMODE_UFO
) {
335 if (*str
&& *str
== ':') break; // try again if we have a colon
343 /* parse string literal */
344 /* don't free() result */
345 /* skips trailing spaces */
346 static char *parseStr (char **str
, char endQ
, int *lenp
) {
347 static char buf
[MAX_LINE_SIZE
];
348 int len
= 0, n
, f
, base
;
350 memset(buf
, 0, sizeof(buf
));
356 case 'a': buf
[len
++] = '\a'; break;
357 case 'b': buf
[len
++] = '\b'; break;
358 case 'e': buf
[len
++] = '\x1b'; break;
359 case 'f': buf
[len
++] = '\f'; break;
360 case 'n': buf
[len
++] = '\n'; break;
361 case 'r': buf
[len
++] = '\r'; break;
362 case 't': buf
[len
++] = '\t'; break;
363 case 'v': buf
[len
++] = '\v'; break;
364 case 'z': buf
[len
++] = '\0'; break;
365 case 'x': case 'X': // hex
368 donum
: for (n
= 0; f
> 0; --f
) {
369 char ch
= digitInBase(*a
++, base
);
371 if (ch
< 0) { --a
; break; }
376 --a
; // return to the last digit, 'for' will skip it
381 case '1' ... '9': // decimal
384 default: buf
[len
++] = a
[0]; break; // others
387 if (*a
== endQ
) { ++a
; break; }
391 while (*a
&& isSpace(*a
)) ++a
; // skip trailing spaces
394 if (lenp
) *lenp
= len
;
399 ///////////////////////////////////////////////////////////////////////////////
400 // source file stack, reader, etc
402 typedef struct SourceLine
{
403 struct SourceLine
*next
;
410 static SourceLine
*asmText
= NULL
;
411 static SourceLine
*currSrcLine
= NULL
;
413 typedef struct FileInfo_s
{
414 struct FileInfo_s
*next
;
418 static FileInfo
*knownFiles
= NULL
;
421 static void clearKnownFiles (void) {
423 FileInfo
*fi
= knownFiles
;
424 knownFiles
= fi
->next
;
425 if (fi
->name
) free(fi
->name
);
431 static const char *appendKnownFileName (const char *fn
) {
432 if (!fn
|| !fn
[0]) return "<unknown>";
433 for (FileInfo
*fi
= knownFiles
; fi
; fi
= fi
->next
) {
434 if (strcmp(fi
->name
, fn
) == 0) return fi
->name
;
436 FileInfo
*fi
= calloc(1, sizeof(FileInfo
));
437 ur_assert((fi
->name
= strdup(fn
)) != NULL
);
438 fi
->next
= knownFiles
;
444 // ////////////////////////////////////////////////////////////////////////// //
445 #define MAX_MACRO_ARGS (32)
447 typedef struct MacroDef
{
448 struct MacroDef
*next
;
452 char *argdefaults
[MAX_MACRO_ARGS
]; // default values
453 char *argnames
[MAX_MACRO_ARGS
]; // argument names
458 char *argvals
[MAX_MACRO_ARGS
]; // argument values
461 static MacroDef
*maclist
= NULL
;
462 static CurMacroDef
*curmacro
= NULL
; // !NULL if we are not inserting macro
463 static int curmacronum
= 0;
466 static void freeLineList (SourceLine
*list
) {
468 SourceLine
*l
= list
;
477 static void clearMacros (void) {
479 MacroDef
*md
= maclist
;
481 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) {
482 if (md
->argdefaults
[f
]) free(md
->argdefaults
[f
]);
483 if (md
->argnames
[f
]) free(md
->argnames
[f
]);
486 freeLineList(md
->lines
);
492 static MacroDef
*findMacro (const char *name
) {
493 for (MacroDef
*mc
= maclist
; mc
!= NULL
; mc
= mc
->next
) if (strEquCI(name
, mc
->name
)) return mc
;
498 static void asmTextClear (void) {
499 freeLineList(asmText
);
505 #define currLineTrimBlanks() do { \
506 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
507 currLine[slen] = 0; \
510 #define currLineRemoveComment() do { \
513 while (pos < slen) { \
515 if (currLine[pos] == '\\' && pos+1 < slen) { \
517 } else if (currLine[pos] == instr) { \
521 if (currLine[pos] == ';') { slen = pos; break; } \
522 if (currLine[pos] == '"') { \
524 } else if (currLine[pos] == '\'') { \
526 (currLine[pos-1] != '_' && !isAlphaDigit(currLine[pos-1]))) \
528 instr = currLine[pos]; \
534 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
535 currLine[slen] = 0; \
539 static SourceLine
*loadTextFile (FILE *fl
, int system
, const char *fname
, int *error
) {
540 if (error
) *error
= 0;
542 fname
= appendKnownFileName(fname
);
543 int continuation
= 0;
545 SourceLine
*first
= NULL
;
546 SourceLine
*last
= NULL
;
550 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
552 currLine
[sizeof(currLine
)-1] = '\0';
553 size_t slen
= strlen(currLine
);
555 if (slen
== 0 || (currLine
[slen
-1] != '\n' && currLine
[slen
-1] != '\r')) {
557 if (error
) *error
= 1;
558 fprintf(stderr
, "ERROR: file %s, line %d: line too long\n", fname
, lineNo
);
564 currLineTrimBlanks();
567 if (slen
&& currLine
[slen
-1] == '\\') {
568 // this is a possible continuation line
569 if (slen
> 1 && currLine
[slen
-2] == '\\') {
576 currLineTrimBlanks();
580 //!normalizeStr(currLine);
582 // don't store empty lines
583 continuation
= new_cont
;
587 // remove comments from continuations
588 if (continuation
|| new_cont
) {
589 currLineRemoveComment();
594 // join continuation with the last line
596 size_t newlen
= slen
+strlen(last
->line
)+4;
597 if (newlen
> MAX_LINE_SIZE
-8) {
598 if (error
) *error
= 1;
599 fprintf(stderr
, "ERROR: too long continuation at line %d in file '%s'\n", lineNo
, fname
);
603 char *newbuf
= malloc(newlen
);
604 ur_assert(newbuf
); // who cares
605 snprintf(newbuf
, newlen
, "%s%s", last
->line
, currLine
);
610 SourceLine
*s
= calloc(1, sizeof(SourceLine
));
614 ur_assert((s
->line
= strdup(currLine
)) != NULL
);
616 if (last
) last
->next
= s
; else first
= s
;
620 continuation
= new_cont
;
627 static int asmTextLoad (const char *fname
, int system
) {
631 if (!(fl
= fopen(fname
, "r"))) {
632 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
635 printf("loading: %s\n", fname
);
637 SourceLine
*first
= loadTextFile(fl
, 0, fname
, &error
);
640 if (error
) return -1;
642 SourceLine
*last
= asmText
;
644 while (last
->next
) last
= last
->next
;
653 static char *extractFileDir (const char *s
) {
654 if (!s
|| !s
[0]) return strdup(".");
658 for (const char *ts
= s
; *ts
; ++ts
) {
659 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
661 if (slash
== s
&& (s
[0] == '/' || s
[0] == '\\') && (s
[1] == '/' || s
[1] == '\\')) slash
= NULL
;
663 slash
= strrchr(s
, '/');
665 if (!slash
) return strdup(".");
666 ptrdiff_t len
= (ptrdiff_t)(slash
-s
)+1;
667 char *res
= malloc(len
+1);
671 while (len
> 0 && (res
[len
-1] == '\\' || res
[len
-1] == '/')) --len
;
673 while (len
> 0 && res
[len
-1] == '/') --len
;
675 if (len
== 0) { free(res
); return strdup("."); }
681 static void loadCurSrcLine (void) {
683 strcpy(currLine
, currSrcLine
->line
);
684 /* macros will not change include dirs */
686 char **incpp
= (!currSrcLine
->system
? &lastIncludePath
: &lastSysIncludePath
);
687 if (*incpp
) free(*incpp
);
688 *incpp
= extractFileDir(currSrcLine
->fname
);
690 normalizeStr(currLine
);
697 UR_FORCE_INLINE SourceLine
*setCurSrcLine (SourceLine
*l
) {
704 UR_FORCE_INLINE SourceLine
*setUFOCurSrcLine (SourceLine
*l
) {
707 strcpy(currLine
, currSrcLine
->line
);
715 UR_FORCE_INLINE SourceLine
*nextSrcLine (void) {
716 return (currSrcLine
!= NULL
? setCurSrcLine(currSrcLine
->next
) : NULL
);
720 UR_FORCE_INLINE SourceLine
*nextUFOSrcLine (void) {
721 return (currSrcLine
!= NULL
? setUFOCurSrcLine(currSrcLine
->next
) : NULL
);
725 UR_FORCE_INLINE
int strHasPathDelim (const char *s
) {
726 if (!s
|| !s
[0]) return 0;
728 return (strchr(s
, '/') || strchr(s
, '\\') ? 1 : 0);
730 return (strchr(s
, '/') ? 1 : 0);
735 /* returns malloced string */
736 static char *createIncludeName (const char *fname
, int assystem
, const char *defaultmain
,
737 const char *lastIncPathUFO
) {
738 if (!fname
|| !fname
[0]) return NULL
;
740 if (fname
[0] != '/') {
743 incdir
= lastIncludePath
;
744 } else if (assystem
== -669) { // ufo normal
745 incdir
= lastIncPathUFO
;
746 } else if (assystem
== -666) { // ufo system
747 incdir
= lastIncPathUFO
;
748 if (!incdir
|| !incdir
[0]) incdir
= ufoIncludeDir
;
750 incdir
= lastSysIncludePath
;
751 if (!incdir
|| !incdir
[0]) incdir
= sysIncludeDir
;
753 if (incdir
== NULL
|| incdir
[0] == 0) incdir
= ".";
754 res
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
756 res
= strprintf("%s", fname
);
759 if (defaultmain
&& defaultmain
[0]) {
760 if (stat(res
, &st
) == 0) {
761 if (S_ISDIR(st
.st_mode
)) {
762 char *rs
= strprintf("%s/%s", res
, defaultmain
);
768 /* check if there is the disk file */
769 if (strHasPathDelim(fname
) && stat(res
, &st
) != 0) {
770 /* no file, try "root include" */
771 const char *incdir
= (!assystem
|| assystem
== -669 ? NULL
: assystem
== -666 ? ufoIncludeDir
: sysIncludeDir
);
772 char *rs
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
775 /* check for dir again */
776 if (defaultmain
&& defaultmain
[0]) {
777 if (stat(res
, &st
) == 0) {
778 if (S_ISDIR(st
.st_mode
)) {
779 char *rs
= strprintf("%s/%s", res
, defaultmain
);
786 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
791 static int includeCount
= 0;
794 // include file instead of the current line
795 // returns 0 on ok, <0 on error
796 static int asmTextInclude (const char *fname
, int system
, int softinclude
) {
797 char *fn
= createIncludeName(fname
, system
, "zzmain.zas", NULL
);
799 FILE *fl
= fopen(fn
, "r");
801 if (softinclude
) return 0;
802 fprintf(stderr
, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", currSrcLine
->fname
, currSrcLine
->lineNo
, currLine
);
807 if (includeCount
> 256) {
810 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", currSrcLine
->fname
, currSrcLine
->lineNo
);
815 printf("loading: %s\n", fn
);
818 SourceLine
*first
= loadTextFile(fl
, system
, fn
, &error
);
822 if (error
) return -1;
825 currSrcLine
->line
[0] = 0; // remove `include` directive
827 SourceLine
*last
= first
;
829 while (last
->next
) last
= last
->next
;
830 last
->next
= currSrcLine
->next
;
831 currSrcLine
->next
= first
;
837 ///////////////////////////////////////////////////////////////////////////////
840 static void processCurrentLine (void); // only one, will skip to next one
843 ///////////////////////////////////////////////////////////////////////////////
844 // error raisers, etc
846 static jmp_buf errJP
;
849 static void errorWriteFile (FILE *fo
) {
851 size_t slen
= strlen(currSrcLine
->line
);
852 fprintf(fo
, "at file %s, line %d\n%.70s%s\n*", currSrcLine
->fname
,
853 currSrcLine
->lineNo
, currSrcLine
->line
, (slen
> 70 ? " ..." : ""));
855 fprintf(fo
, "somewhere in time: ");
860 static void errorMsgV (const char *fmt
, va_list ap
) {
861 errorWriteFile(stderr
);
862 vfprintf(stderr
, fmt
, ap
);
869 __attribute__((format(printf
, 1, 2)))
870 static void warningMsg (const char *fmt
, ...) {
872 fprintf(stderr
, "WARNING ");
878 __attribute__((format(printf
, 1, 2)))
879 static void errorMsg (const char *fmt
, ...) {
881 fprintf(stderr
, "FATAL ");
887 __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2)))
888 static void fatal (const char *fmt
, ...) {
896 __attribute__((noreturn
))
897 static void fatalUrLib (int errcode
) {
898 errorMsg("%s", urasm_errormsg(errcode
));
903 ///////////////////////////////////////////////////////////////////////////////
905 static int optWriteType
= 't';
906 static int optWTChanged
= 0;
907 static int optWriteFixups
= 0;
908 static int optFixupType
= 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
909 static int optRunTape
= 1;
910 static int optRunDMB
= 1;
911 static int optRunSCL
= -1; /* -1: save cargador, but no boot; 1: boot and cargador */
912 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
913 static int optSNA48
= 1;
914 static char *optOutputDir
= NULL
;
915 static char *optOutputFile
= NULL
;
916 static int optOutputFileFixed
= 0;
919 static char *buildOutputFileName (const char *basename
, const char *ext
) {
922 if (ext
[0] == '.') ++ext
;
923 //fprintf(stderr, "********** <%s> <%s> <%s> %d <%s>\n", basename, ext, optOutputFile, optOutputFileFixed, optOutputDir);
925 if (!optOutputFileFixed
) {
926 optOutputFileFixed
= 1;
927 char *spp
= strrchr(optOutputFile
, '/');
929 char *spp1
= strrchr(optOutputFile
, '\\');
930 if (!spp
|| (spp
< spp1
)) spp
= spp1
;
932 if (!spp
) spp
= optOutputFile
; else ++spp
;
933 char *dotpos
= strrchr(spp
, '.');
934 if (dotpos
) *dotpos
= 0;
936 if (optOutputFile
&& optOutputFile
[0]) return strprintf("%s/%s.%s", optOutputDir
, optOutputFile
, ext
);
938 return strprintf("%s/%s.%s", optOutputDir
, basename
, ext
);
942 typedef struct OutDataFile_t
{
944 char type
; // 'C', 'B'
946 uint16_t start
; // start address, or basic start line
947 uint16_t size
; // data size (never 0)
949 struct OutDataFile_t
*next
;
952 static OutDataFile
*datafiles
= NULL
;
955 static OutDataFile
*appendDataData (const void *bytes
, uint16_t start
, uint16_t count
) {
956 if (!count
) return NULL
;
958 OutDataFile
*off
= malloc(sizeof(OutDataFile
));
964 off
->data
= malloc(count
);
965 if (bytes
) memcpy(off
->data
, bytes
, count
); else memset(off
->data
, 0, count
);
968 OutDataFile
*last
= datafiles
;
972 while (last
->next
) last
= last
->next
;
980 static OutDataFile
*appendDataFile (const char *fname
, long ofs
, int len
, int allowskip
) {
981 if (!fname
|| !fname
[0]) return NULL
;
983 FILE *fl
= fopen(fname
, "rb");
985 if (allowskip
) return NULL
;
986 fatal("cannot open data file '%s'", fname
);
988 if (fseek(fl
, 0, SEEK_END
) < 0) fatal("error reading data file '%s'", fname
);
989 long fsize
= ftell(fl
);
990 if (fsize
< 0) fatal("error reading data file '%s'", fname
);
994 if (ofs
< 0) fatal("invalid offset");
995 if (ofs
>= fsize
) ofs
= 0; else ofs
= fsize
-ofs
;
997 if (ofs
>= fsize
) { fclose(fl
); return NULL
; }
998 if (fseek(fl
, ofs
, SEEK_SET
) < 0) fatal("error reading data file '%s'", fname
);
1001 if (fsize
> 65535) fatal("data file '%s' too big", fname
);
1004 if (len
> 65535) fatal("data file '%s' too big", fname
);
1005 if (len
== 0) { fclose(fl
); return NULL
; }
1007 OutDataFile
*off
= malloc(sizeof(OutDataFile
));
1012 off
->size
= (uint16_t)len
;
1013 off
->data
= malloc(len
);
1015 if (fread(off
->data
, (unsigned)len
, 1, fl
) != 1) fatal("error reading data file '%s'", fname
);
1018 OutDataFile
*last
= datafiles
;
1022 while (last
->next
) last
= last
->next
;
1030 //////////////////////////////////////////////////////////////////////////////
1031 // operator management
1033 // return !0 to skip current line
1034 typedef int (*UrAsmOpFn
) (void);
1041 typedef struct UrAsmOp_t
{
1043 struct UrAsmOp_t
*hlink
;
1047 void *udata
; // user data (struct info for structs, for example)
1048 struct UrAsmOp_t
*next
;
1051 #define UR_OPER_BUCKETS (256)
1052 static UrAsmOp
*oplist
= NULL
;
1053 static UrAsmOp
*urCurrentOp
= NULL
;
1054 static UrAsmOp
*operBuckets
[UR_OPER_BUCKETS
];
1057 //==========================================================================
1061 //==========================================================================
1062 static UrAsmOp
*urAddOpEx (const char *name
, UrAsmOpFn fn
, char optpfx
) {
1063 ur_assert(name
!= NULL
&& name
[0] != 0);
1064 if (oplist
== NULL
) {
1065 //fprintf(stderr, "CLEAR OPBUCKETS!\n");
1066 for (int f
= 0; f
< UR_OPER_BUCKETS
; f
+= 1) operBuckets
[f
] = NULL
;
1068 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
1071 res
->name
= calloc(1, strlen(name
) + 2);
1072 ur_assert(res
->name
);
1073 res
->name
[0] = optpfx
;
1074 strcpy(res
->name
+ 1, name
);
1076 res
->name
= strdup(name
);
1077 ur_assert(res
->name
);
1083 // insert into hash table
1084 res
->hash
= joaatHashStrCI(res
->name
);
1085 const uint32_t bkt
= res
->hash
%UR_OPER_BUCKETS
;
1086 //fprintf(stderr, "NEWOP: 0x%08x (%u) : <%s>\n", res->hash, bkt, res->name);
1087 res
->hlink
= operBuckets
[bkt
];
1088 operBuckets
[bkt
] = res
;
1093 //==========================================================================
1097 //==========================================================================
1098 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
1099 return urAddOpEx(name
, fn
, 0);
1103 //==========================================================================
1107 //==========================================================================
1108 static void urAddOpAndDollar (const char *name
, UrAsmOpFn fn
) {
1109 (void)urAddOpEx(name
, fn
, 0);
1110 (void)urAddOpEx(name
, fn
, '$');
1114 //==========================================================================
1118 //==========================================================================
1119 static UrAsmOp
*urFindOp (const char *name
) {
1120 if (name
== NULL
|| name
[0] == 0) return NULL
;
1123 if (name
[0] == '$' && name
[1]) {
1124 for (res
= oplist
; res
; res
= res
->next
) {
1125 if (res
->name
[0] == '$') {
1126 if (strEquCI(name
, res
->name
)) break;
1128 if (strEquCI(name
+1, res
->name
)) break;
1132 for (res
= oplist
; res
; res
= res
->next
) if (strEquCI(name
, res
->name
)) break;
1136 const uint32_t hash
= joaatHashStrCI(name
);
1137 //fprintf(stderr, "LOOK: 0x%08x (%u) : <%s>\n", hash, hash%UR_OPER_BUCKETS, name);
1138 UrAsmOp
*res
= operBuckets
[hash
%UR_OPER_BUCKETS
];
1139 while (res
!= NULL
&& (res
->hash
!= hash
|| !strEquCI(name
, res
->name
))) {
1140 //fprintf(stderr, " REJECT: 0x%08x: <%s>\n", res->hash, res->name);
1148 //==========================================================================
1152 //==========================================================================
1153 static void urClearOps (void) {
1154 while (oplist
!= NULL
) {
1155 UrAsmOp
*c
= oplist
;
1156 oplist
= oplist
->next
;
1160 for (int f
= 0; f
< UR_OPER_BUCKETS
; f
+= 1) operBuckets
[f
] = NULL
;
1164 ///////////////////////////////////////////////////////////////////////////////
1168 LBL_TYPE_VERY_SPECIAL
= -42,
1169 LBL_TYPE_UNKNOWN
= -1,
1170 LBL_TYPE_ASS
= 0, // `=`
1171 LBL_TYPE_EQU
= 1, // equ
1172 LBL_TYPE_CODE
= 2, // ':', or "lbl opcode"
1173 LBL_TYPE_STOFS
= 3, // struct offset
1177 typedef struct UrLabelInfo_t
{
1179 struct UrLabelInfo_t
*hlink
;
1183 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
1184 int known
; /* !0: label value already known */
1185 int refLine
; /* first referenced line */
1186 int fixuptype
; /* UR_FIXUP_XXX */
1187 int modHidden
; /* hidden module label? hack for PLUS3BOOT */
1188 char savedFirstChar
; /* for hidden labels */
1190 struct UrLabelInfo_t
*next
;
1191 // previous temp label
1192 struct UrLabelInfo_t
*prevTemp
;
1198 #define UR_LABEL_BUCKETS (256)
1199 static UrLabelInfo
*labels
= NULL
;
1200 static UrLabelInfo
*labelsTail
= NULL
;
1201 static UrLabelInfo
*labelBuckets
[UR_LABEL_BUCKETS
];
1202 // list of all @@, from the last defined
1203 static UrLabelInfo
*tempLabels
= NULL
;
1204 static int tempLabelIdx
= 0;
1207 //==========================================================================
1211 //==========================================================================
1212 static void initLabelBuckets (void) {
1213 for (int f
= 0; f
< UR_LABEL_BUCKETS
; f
+= 1) labelBuckets
[f
] = NULL
;
1217 //==========================================================================
1221 //==========================================================================
1222 static void urClearLabels (void) {
1224 while ((c
= labels
) != NULL
) {
1226 if (c
->name
) free(c
->name
);
1227 if (c
->refFile
) free(c
->refFile
);
1235 //==========================================================================
1239 //==========================================================================
1240 static UrLabelInfo
*urFindLabel (const char *name
) {
1241 if (name
!= NULL
&& name
[0] != 0) {
1243 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
1244 if (strcmp(name
, c
->name
) == 0) return c
;
1247 const uint32_t hash
= joaatHashStr(name
);
1248 UrLabelInfo
*c
= labelBuckets
[hash
%UR_LABEL_BUCKETS
];
1250 if (c
->hash
== hash
&& strcmp(c
->name
, name
) == 0) return c
;
1259 //==========================================================================
1263 //==========================================================================
1264 static UrLabelInfo
*urAddLabel (const char *name
) {
1265 ur_assert(name
!= NULL
&& name
[0] != 0);
1266 UrLabelInfo
*c
= urFindLabel(name
);
1268 c
= calloc(1, sizeof(UrLabelInfo
));
1270 c
->name
= strdup(name
);
1272 c
->type
= LBL_TYPE_UNKNOWN
;
1273 c
->fixuptype
= UR_FIXUP_NONE
;
1275 c
->savedFirstChar
= 0;
1277 if (labelsTail
) labelsTail
->next
= c
; else labels
= c
;
1279 c
->hash
= joaatHashStr(c
->name
);
1280 const uint32_t bkt
= c
->hash
%UR_LABEL_BUCKETS
;
1281 c
->hlink
= labelBuckets
[bkt
];
1282 labelBuckets
[bkt
] = c
;
1288 //==========================================================================
1292 //==========================================================================
1293 static UrLabelInfo
*urAddTempLabel (void) {
1296 snprintf(name
, sizeof(name
), "@@_%05d", tempLabelIdx
);
1297 UrLabelInfo
*lbl
= urAddLabel(name
);
1298 lbl
->tempIdx
= tempLabelIdx
;
1299 lbl
->prevTemp
= tempLabels
;
1305 //==========================================================================
1309 //==========================================================================
1310 static UrLabelInfo
*urNextTempLabel (void) {
1313 snprintf(name
, sizeof(name
), "@@_%05d", tempLabelIdx
);
1314 UrLabelInfo
*lbl
= urFindLabel(name
);
1315 ur_assert(lbl
->tempIdx
== tempLabelIdx
);
1316 ur_assert(lbl
->prevTemp
== NULL
);
1317 lbl
->prevTemp
= tempLabels
;
1323 //==========================================================================
1325 // urResetTempLabels
1327 //==========================================================================
1328 static void urResetTempLabels (void) {
1329 while (tempLabels
!= NULL
) {
1330 UrLabelInfo
*lbl
= tempLabels
;
1331 tempLabels
= lbl
->prevTemp
;
1332 lbl
->prevTemp
= NULL
;
1339 //==========================================================================
1343 // add local label; it may shadow others
1345 //==========================================================================
1346 static UrLabelInfo
*urAddTempLocal (const char *name
) {
1347 UrLabelInfo
*c
= calloc(1, sizeof(UrLabelInfo
));
1349 c
->name
= strdup(name
);
1351 c
->type
= LBL_TYPE_EQU
;
1353 c
->fixuptype
= UR_FIXUP_NONE
;
1355 c
->savedFirstChar
= 0;
1358 if (!labelsTail
) labelsTail
= c
;
1359 c
->hash
= joaatHashStr(c
->name
);
1360 const uint32_t bkt
= c
->hash
%UR_LABEL_BUCKETS
;
1361 c
->hlink
= labelBuckets
[bkt
];
1362 labelBuckets
[bkt
] = c
;
1367 static void UFOZXLabelRemoved (UrLabelInfo
*lbl
);
1370 //==========================================================================
1372 // urRemoveTempLocal
1374 //==========================================================================
1375 static void urRemoveTempLocal (UrLabelInfo
*tmplbl
) {
1376 if (!tmplbl
) return;
1377 ur_assert(tmplbl
== labels
);
1378 UFOZXLabelRemoved(tmplbl
);
1379 labels
= tmplbl
->next
;
1380 if (!labels
) labelsTail
= NULL
;
1381 const uint32_t bkt
= tmplbl
->hash
%UR_LABEL_BUCKETS
;
1382 ur_assert(labelBuckets
[bkt
] == tmplbl
);
1383 labelBuckets
[bkt
] = tmplbl
->hlink
;
1389 ///////////////////////////////////////////////////////////////////////////////
1391 typedef struct StructField_s
{
1392 struct StructField_s
*next
; // next field
1393 char *name
; // field name
1394 uint16_t ofs
; // field offset in struct
1395 uint16_t size
; // field size in bytes (can be 0 for aliases)
1401 typedef struct StructInfo_s
{
1402 struct StructInfo_s
*next
; // next structure
1403 char *name
; // structure name
1404 StructField
*fields
;
1405 uint16_t size
; // total structure size in bytes
1410 // list of all structures
1411 static StructInfo
*structList
= NULL
;
1414 static void freeStructs (void) {
1415 while (structList
!= NULL
) {
1416 StructInfo
*sth
= structList
;
1417 structList
= sth
->next
;
1418 StructField
*fld
= sth
->fields
;
1420 StructField
*fc
= fld
;
1422 if (fc
->name
) free(fc
->name
);
1425 if (sth
->name
) free(sth
->name
);
1431 ///////////////////////////////////////////////////////////////////////////////
1433 typedef struct FixupItem_s
{
1434 struct FixupItem_s
*next
;
1435 uint16_t opdestaddr
;
1440 static FixupItem
*fixlisthead
= NULL
, *fixlisttail
= NULL
;
1443 static void clearFixups (void) {
1445 while ((c
= fixlisthead
) != NULL
) {
1446 fixlisthead
= c
->next
;
1449 fixlisthead
= fixlisttail
= NULL
;
1453 static void addFixup (uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
1454 FixupItem
*fx
= calloc(1, sizeof(FixupItem
));
1456 fx
->opdestaddr
= opdestaddr
;
1457 fx
->opaddr
= opaddr
;
1458 fx
->fixuptype
= fixuptype
;
1461 if (fixlisttail
!= NULL
) fixlisttail
->next
= fx
; else fixlisthead
= fx
;
1467 ///////////////////////////////////////////////////////////////////////////////
1468 // destination memory management
1470 static uint8_t memory
[65536];
1471 static uint8_t memused
[65536];
1472 static uint8_t memresv
[65536];
1473 static uint16_t start_pc
= 0x100; // viva CP/M!
1474 static uint16_t start_disp
= 0x100; // viva CP/M!
1475 static uint16_t start_ent
= 0x100; // viva CP/M!
1476 static uint16_t pc
= 0; /* current position to write */
1477 static uint16_t disp
= 0; /* current 'virtual PC' */
1478 static uint16_t ent
= 0; /* starting address */
1479 static uint16_t clrAddr
= 24999; /* address for CLEAR for cargador */
1480 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
1481 static int inTapeBlock
= 0;
1482 static uint8_t tapeXorB
= 0;
1484 static int findChunkFrom (int addr
, int *start
, int *len
);
1487 // find next free area starting from `addr`
1488 // returns address or -1
1489 static int reserveFindFreeFrom (uint16_t addr
) {
1490 for (uint32_t freeaddr
= addr
; freeaddr
<= 0xffffU
; ++freeaddr
) {
1491 if (!memresv
[freeaddr
]) return freeaddr
;
1497 // find next reserved area starting from `addr`
1498 // returns address or -1
1499 static int reserveFindReservedFrom (uint16_t addr
) {
1500 for (uint32_t freeaddr
= addr
; freeaddr
<= 0xffffU
; ++freeaddr
) {
1501 if (memresv
[freeaddr
]) return freeaddr
;
1507 static void reserveReleaseUsed (uint16_t addr
, int len
) {
1508 while (len
--) memused
[addr
++] = 0;
1513 static int reserveHit (uint16_t addr
, int len
) {
1514 if (len
<= 0) return 0;
1516 if (memresv
[addr
++]) return 1;
1522 // start from `addr`, look for free areas, and check
1523 // if we can insert jump from a pcaddr there
1524 // returns next free area or -1
1525 static int reservedFindNextJumpableFreeFrom (uint16_t pcaddr
, uint16_t addr
) {
1526 // check if there is enough room at least for a jr
1527 if (memresv
[pcaddr
] || memresv
[pcaddr
+1]) return -1;
1528 uint16_t caddr
= addr
;
1530 while (caddr
<= 0xffffU
&& memresv
[caddr
]) ++caddr
;
1531 if (caddr
> 0xffffU
) return -1; // alas
1533 uint32_t jrdist
= caddr
-(pcaddr
+2U);
1534 if (jrdist
< 128u) return (int)caddr
; // yay, we can JR there, this free area is good
1535 // cannot JR, check if we have room for JP
1536 if (memresv
[pcaddr
+2]) return -1; // alas
1542 // start from `addr`, look for free areas, and check
1543 // if we can insert jump from a pcaddr there, and if
1544 // that area is big enough to hold a next jump and at least one more byte
1545 // returns next free area or -1
1546 static int reservedFindNextSuitableFreeFrom (uint16_t pcaddr
, uint16_t addr
) {
1549 caddr
= reservedFindNextJumpableFreeFrom(pcaddr
, (uint16_t)caddr
);
1550 if (caddr
< 0) return -1; // alas
1551 // we can jump to naddr, now check if it is big enough
1552 // if it has less than 3 bytes, it is too small
1553 if (memresv
[(uint16_t)(caddr
+1)] || memresv
[(uint16_t)(caddr
+2)]) {
1558 // if we have more than 3 bytes, it is definitely ok
1559 if (!memresv
[(uint16_t)(caddr
+3)]) return caddr
; // ok
1560 // it has exactly 3 bytes, check if it is enough
1561 int xaddr
= reservedFindNextJumpableFreeFrom(caddr
+1, (uint16_t)caddr
+3);
1562 if (xaddr
>= 0) return caddr
; // ok
1564 caddr
= reserveFindReservedFrom(caddr
);
1565 if (caddr
< 0) return -1;
1566 caddr
= reserveFindFreeFrom(caddr
);
1567 if (caddr
< 0) return -1;
1571 UR_FORCE_INLINE
uint8_t getByte (uint16_t addr
) {
1572 return memory
[addr
];
1576 UR_FORCE_INLINE
uint16_t getWord (uint16_t addr
) {
1577 return ((uint16_t)memory
[addr
])|(((uint16_t)memory
[addr
+1])<<8);
1581 UR_FORCE_INLINE
void putByte (uint16_t addr
, uint8_t b
) {
1582 if (inTapeBlock
) tapeXorB
^= b
;
1588 UR_FORCE_INLINE
void putWord (uint16_t addr
, uint16_t w
) {
1589 putByte(addr
, w
&0xFFU
);
1590 putByte(addr
+1, (w
>>8)&0xFFU
);
1594 UR_FORCE_INLINE
void emitByte (uint8_t b
) {
1601 UR_FORCE_INLINE
void emitWord (uint16_t w
) {
1603 emitByte((w
>>8)&0xFFU
);
1607 UR_FORCE_INLINE
void emitRWord (uint16_t w
) {
1608 emitByte((w
>>8)&0xFFU
);
1613 static void prepareMemory (void) {
1614 memset(memory
, 0, sizeof(memory
));
1615 memset(memused
, 0, sizeof(memused
));
1616 memset(memresv
, 0, sizeof(memresv
));
1620 ///////////////////////////////////////////////////////////////////////////////
1621 // module list management
1623 typedef struct ModuleInfo_t
{
1625 char *fname
; // opened in this file
1626 int seen
; // !0: module already seen, skip other definitions from the same file
1627 struct ModuleInfo_t
*next
;
1629 struct ModuleInfo_t
*hlink
;
1632 #define UR_MODULE_BUCKETS (1024)
1633 static ModuleInfo
*modules
= NULL
;
1634 static ModuleInfo
*currModule
= NULL
;
1635 static ModuleInfo
*moduleBuckets
[UR_MODULE_BUCKETS
];
1637 static uint8_t mod_memory_saved
[65536];
1638 static uint8_t mod_memused_saved
[65536];
1639 static uint8_t mod_memresv_saved
[65536];
1642 static void modulesClear (void) {
1645 ModuleInfo
*c
= modules
;
1646 modules
= modules
->next
;
1651 for (int f
= 0; f
< UR_MODULE_BUCKETS
; f
+=1) moduleBuckets
[f
] = NULL
;
1655 //==========================================================================
1659 //==========================================================================
1660 static void modulesResetSeen (void) {
1661 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
1665 //==========================================================================
1669 //==========================================================================
1670 static ModuleInfo
*moduleFind (const char *name
) {
1671 if (!name
|| !name
[0]) return NULL
;
1673 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) return c
;
1675 const uint32_t hash
= joaatHashStr(name
);
1676 ModuleInfo
*c
= moduleBuckets
[hash
%UR_MODULE_BUCKETS
];
1678 if (c
->hash
== hash
&& strcmp(c
->name
, name
) == 0) return c
;
1686 //==========================================================================
1690 // special module name: PLUS3BOOT
1692 //==========================================================================
1693 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
1695 ur_assert(name
&& fname
&& name
[0] && fname
[0]);
1696 c
= calloc(1, sizeof(ModuleInfo
));
1698 c
->name
= strdup(name
);
1700 c
->fname
= strdup(fname
);
1701 ur_assert(c
->fname
);
1704 c
->hash
= joaatHashStr(name
);
1705 const uint32_t bkt
= c
->hash
%UR_MODULE_BUCKETS
;
1706 c
->hlink
= moduleBuckets
[bkt
];
1707 moduleBuckets
[bkt
] = c
;
1708 // +3DOS bootsector?
1709 if (strEquCI(c
->name
, "PLUS3BOOT")) {
1710 // save current memory state, because bootsector will be written as a separate independent file
1711 memcpy(mod_memory_saved
, memory
, sizeof(mod_memory_saved
));
1712 memcpy(mod_memused_saved
, memused
, sizeof(mod_memused_saved
));
1713 memcpy(mod_memresv_saved
, memresv
, sizeof(mod_memresv_saved
));
1719 //==========================================================================
1723 //==========================================================================
1724 static void moduleOpen (void) {
1725 //fprintf(stderr, "::: OPENING MODULE <%s> :::\n", currModule->name);
1726 // unhide +3DOS boot labels
1727 if (strEquCI(currModule
->name
, "PLUS3BOOT")) {
1728 for (UrLabelInfo
*lbl
= labels
; lbl
; lbl
= lbl
->next
) {
1729 if (lbl
->modHidden
== 1) {
1730 if (!lbl
->savedFirstChar
) fatal("internal module manager error");
1732 lbl
->name
[0] = lbl
->savedFirstChar
; // unhide it
1733 //fprintf(stderr, "***UN-HIDDEN: <%s>\n", lbl->name);
1740 //==========================================================================
1744 //==========================================================================
1745 static int isModuleLabel (const ModuleInfo
*mi
, const char *name
) {
1746 if (!mi
|| !name
|| !name
[0]) return 0;
1747 if (name
[0] == '.') ++name
;
1748 const size_t mnlen
= strlen(mi
->name
);
1749 if (strlen(name
) <= mnlen
|| name
[mnlen
] != '.') return 0;
1750 return (memcmp(name
, mi
->name
, mnlen
) == 0);
1754 //==========================================================================
1758 // close current module
1760 //==========================================================================
1761 static void moduleClose (const char *mname
) {
1762 if (mname
&& !mname
[0]) mname
= NULL
;
1764 if (!mname
) fatal("trying to close unopened module");
1765 fatal("trying to close unopened module '%s'", mname
);
1767 if (mname
&& strcmp(mname
, currModule
->name
)) fatal("invalid module name in ENDMODULE; got '%s', expected '%s'", mname
, currModule
->name
);
1769 // +3DOS bootsector?
1770 if (strEquCI(currModule
->name
, "PLUS3BOOT")) {
1771 // append bootsector chunk
1774 if (!findChunkFrom(start
, &start
, &len
)) fatal("no +3DOS bootsector chunk");
1775 if (start
!= 0xFE10) fatal("+3DOS bootsector chunk must be 'org'ed and #FE10");
1776 if (len
> 512-16) fatal("+3DOS bootsector chunk is too big");
1779 OutDataFile
*off
= appendDataData(memory
+(unsigned)start
, (unsigned)start
, (unsigned)len
);
1780 if (!off
) fatal("cannot append +3DOS bootsector");
1781 off
->name
= strdup("");
1782 off
->type
= 1; // special type
1785 if (findChunkFrom(start
, &start
, &len
)) fatal("only +3DOS bootsector chunk is allowed (found #%04X, %d)", (unsigned)start
, len
);
1789 memcpy(memory
, mod_memory_saved
, sizeof(memory
));
1790 memcpy(memused
, mod_memused_saved
, sizeof(memused
));
1791 memcpy(memresv
, mod_memresv_saved
, sizeof(memresv
));
1793 // remove module labels
1794 for (UrLabelInfo
*lbl
= labels
; lbl
; lbl
= lbl
->next
) {
1795 if (!isModuleLabel(currModule
, lbl
->name
)) continue;
1796 //fprintf(stderr, "***HIDDEN: <%s>\n", lbl->name);
1798 lbl
->savedFirstChar
= lbl
->name
[0];
1799 lbl
->name
[0] = '{'; // hide it
1807 ///////////////////////////////////////////////////////////////////////////////
1808 // label getter and utilities
1810 static char *lastSeenGlobalLabel
= NULL
; /* global; malloced */
1813 static char *mangleLabelName (const char *name
) {
1814 static char newname
[MAX_LINE_SIZE
*2+1024];
1815 if (!name
|| !name
[0]) {
1819 // local label or macro label?
1820 if (name
[0] == '.') {
1821 // two dots is a macro label
1822 if (name
[1] == '.') {
1823 if (!name
[2]) fatal("invalid label '%s'", name
);
1824 // this is macro label
1825 if (curmacro
== NULL
) fatal("macro label outside of macro: '%s'", name
);
1826 snprintf(newname
, sizeof(newname
), ".%s.MACLBL%d%s", curmacro
->mac
->name
, curmacronum
, name
+1);
1829 if (!name
[1]) fatal("invalid label '%s'", name
);
1831 if (!lastSeenGlobalLabel
) fatal("local label '%s' without a global", name
);
1832 snprintf(newname
, sizeof(newname
), ".%s%s", lastSeenGlobalLabel
, name
);
1836 // if global is prefixed with '@', it should not be mangled
1837 // this is to allow access to other labels from modules
1838 if (name
[0] == '@') {
1839 if (name
[1] == '@') fatal("double-prefixed label '%s'", name
);
1840 if (!name
[1]) fatal("invalid label '%s'", name
);
1841 snprintf(newname
, sizeof(newname
), "%s", name
+1);
1844 // if no module, nothing to do
1845 // also, do not mangle labels with dots inside:
1846 // those are either already mangled, or call to other modules
1847 if (!currModule
|| strchr(name
, '.')) {
1848 snprintf(newname
, sizeof(newname
), "%s", name
);
1851 // this is global unqualified label and we have a module; let's rename it
1852 snprintf(newname
, sizeof(newname
), "%s.%s", currModule
->name
, name
);
1857 static UrLabelInfo
*addTempLabel (void) {
1858 UrLabelInfo
*lbl
= tempLabels
;
1859 if (lbl
== NULL
|| !lbl
->tempFwd
) {
1861 lbl
= urAddTempLabel();
1863 lbl
= urNextTempLabel();
1866 if (!lbl
->refFile
) {
1867 lbl
->refLine
= currSrcLine
->lineNo
;
1868 lbl
->refFile
= strdup(currSrcLine
->fname
);
1875 static UrLabelInfo
*addFwdTempLabel (void) {
1876 return addTempLabel();
1880 // WARNING! this will be invalidated
1881 static char lastFindLabelName
[MAX_LINE_SIZE
*2+1024];
1884 static UrLabelInfo
*findLabel (const char *name
) {
1886 if (!name
|| !name
[0]) fatal("UrAsm internal error: empty name in `findLabel()`");
1888 if (name
[0] == '@') {
1889 if ((name
[1] == '@' && name
[2] == 'b' && name
[3] == 0) ||
1890 (name
[1] == 'b' && name
[2] == 0))
1892 // last defined temp
1894 while (lbl
!= NULL
&& lbl
->tempFwd
) lbl
= lbl
->prevTemp
;
1895 if (lbl
== NULL
) fatal("no temp labels defined yet");
1898 if ((name
[1] == '@' && name
[2] == 'f' && name
[3] == 0) ||
1899 (name
[1] == 'f' && name
[2] == 0))
1901 // next defined temp
1902 return addFwdTempLabel();
1906 if (name
[0] == '@' && name
[1] == '@' && name
[2] == '_') {
1907 //fprintf(stderr, "TEMP: <%s>\n", name);
1908 lbl
= urFindLabel(name
);
1909 if (lbl
) return lbl
;
1911 char *nn
= mangleLabelName(name
);
1912 strcpy(lastFindLabelName
, nn
);
1913 lbl
= urFindLabel(nn
);
1916 // try non-module label
1917 lbl = urFindLabel(ln);
1924 static UrLabelInfo
*findAddLabel (const char *name
) {
1925 if (!name
|| !name
[0]) fatal("UrAsm internal error: empty name in `findAddLabel()`");
1926 char *nn
= mangleLabelName(name
);
1927 strcpy(lastFindLabelName
, nn
);
1928 UrLabelInfo
*lbl
= urAddLabel(nn
);
1929 if (!lbl
->refFile
) {
1930 lbl
->refLine
= currSrcLine
->lineNo
;
1931 lbl
->refFile
= strdup(currSrcLine
->fname
);
1937 static int lblOptMakeU2
= 0;
1939 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
, int *fixuptype
) {
1941 if (strEquCI(name
, "__URASM_DEFFMT_ANY_DISK")) {
1944 *fixuptype
= UR_FIXUP_NONE
;
1945 return (optWriteType
== 'S' || optWriteType
== '3'); // SCL or +3DOS
1947 if (strEquCI(name
, "__URASM_DEFFMT_SCL")) {
1950 *fixuptype
= UR_FIXUP_NONE
;
1951 return (optWriteType
== 'S');
1953 if (strEquCI(name
, "__URASM_DEFFMT_SCL_MONO")) {
1956 *fixuptype
= UR_FIXUP_NONE
;
1957 return (optWriteType
== 'M');
1959 if (strEquCI(name
, "__URASM_DEFFMT_P3DOS")) {
1962 *fixuptype
= UR_FIXUP_NONE
;
1963 return (optWriteType
== '3');
1965 if (strEquCI(name
, "__URASM_DEFFMT_HOBETA")) {
1968 *fixuptype
= UR_FIXUP_NONE
;
1969 return (optWriteType
== 'H');
1971 if (strEquCI(name
, "__URASM_DEFFMT_TAPE")) {
1974 *fixuptype
= UR_FIXUP_NONE
;
1975 return (optWriteType
== 't');
1977 if (strEquCI(name
, "__URASM_DEFFMT_ANY_SNA")) {
1980 *fixuptype
= UR_FIXUP_NONE
;
1981 return (optWriteType
== 's');
1983 if (strEquCI(name
, "__URASM_DEFFMT_SNA48")) {
1986 *fixuptype
= UR_FIXUP_NONE
;
1987 return (optWriteType
== 's' && optSNA48
);
1989 if (strEquCI(name
, "__URASM_DEFFMT_SNA128")) {
1992 *fixuptype
= UR_FIXUP_NONE
;
1993 return (optWriteType
== 's' && !optSNA48
);
1995 if (strEquCI(name
, "__URASM_DEFFMT_RAW")) {
1998 *fixuptype
= UR_FIXUP_NONE
;
1999 return (optWriteType
== 'r');
2001 if (strEquCI(name
, "__URASM_DEFFMT_DMB")) {
2004 *fixuptype
= UR_FIXUP_NONE
;
2005 return (optWriteType
== 'd');
2008 UrLabelInfo
*lbl
= findLabel(name
);
2011 //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);
2012 errorMsg("using undefined label '%s'", name
);
2017 lbl
= urAddLabel(lastFindLabelName
);
2018 lbl
->type
= (lblOptMakeU2
? LBL_TYPE_VERY_SPECIAL
: LBL_TYPE_UNKNOWN
);
2020 lbl
->refLine
= currSrcLine
->lineNo
;
2021 lbl
->refFile
= strdup(currSrcLine
->fname
);
2022 //fprintf(stderr, "new label: [%s]\n", lbl->name);
2023 //fprintf(stderr, "new label: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
2025 //fprintf(stderr, "label reference: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
2029 *defined
= (lbl
->known
!= 0);
2030 *fixuptype
= lbl
->fixuptype
;
2045 static int isLabelDefinedOrKnown (const char *name
, uint16_t addr
, int qtype
) {
2046 UrLabelInfo
*lbl
= findLabel(name
);
2048 case UR_QTYPE_DEFINED
: return (lbl
? lbl
->known
!= 0 : 0);
2049 case UR_QTYPE_KNOWN
: return (lbl
!= NULL
);
2056 static void fixupOperandCB (const urasm_operand_t
*op
, uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
2058 //static const char *n[4] = {"none", "word", "low", "high"};
2059 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
2060 addFixup(opdestaddr
, opaddr
, fixuptype
, size
);
2065 static int checkLabels (void) {
2067 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
2068 //fprintf(stderr, ":LBL: <%s> (type=%d)\n", c->name, c->type);
2069 if (c
->type
== LBL_TYPE_UNKNOWN
) {
2070 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
2073 if (c
->type
== LBL_TYPE_ASS
) c
->known
= -1;
2075 //if (wasError) longjmp(errJP, 667);
2080 ///////////////////////////////////////////////////////////////////////////////
2081 // expression utils (aka string parsing)
2084 /* skip leading spaces */
2085 /* returns string with spaces skipped */
2086 UR_FORCE_INLINE
char *strSkipSpaces (const char *s
) {
2087 while (*s
&& isSpace(*s
)) ++s
;
2092 /* skip leading spaces */
2093 /* returns string with spaces skipped */
2094 UR_FORCE_INLINE
const char *strSkipSpacesConst (const char *s
) {
2095 while (*s
&& isSpace(*s
)) ++s
;
2100 /* remove trailing spaces from string */
2101 static void strTrimRight (char *s
) {
2102 if (!s
|| !s
[0]) return;
2103 size_t len
= strlen(s
);
2104 while (len
> 0 && isSpace(s
[len
-1])) --len
;
2109 /* skip leading spaces and colons */
2110 /* returns string with spaces skipped */
2111 UR_FORCE_INLINE
char *strSkipSpacesColons (char *s
) {
2112 while (*s
&& (isSpace(*s
) || *s
== ':')) ++s
;
2117 /* remove leading spaces from the current line */
2118 UR_FORCE_INLINE
void removeSpaces (void) {
2119 char *e
= strSkipSpaces(currLine
);
2120 if (e
!= currLine
) memmove(currLine
, e
, strlen(e
)+1);
2124 /* skip leading spaces, and argument (up to, but not including comma or colon) */
2125 /* correctly skip strings */
2126 /* returns string after skipped argument (with trailing spaces skipped) */
2127 static char *skipMacroArg (char *str
) {
2129 char *strstart
= str
;
2131 str
= strSkipSpaces(str
);
2132 if (!str
[0]) return str
;
2133 if (parens
== 0 && (str
[0] == ',' || str
[0] == ':')) return str
;
2134 /* check for "af'" */
2135 if (str
[0] == '\'' && str
-strstart
>= 2 && toLower(str
[-2] == 'a') && toLower(str
[-1] == 'f')) {
2139 if (str
[0] == '(') { ++parens
; str
+= 1; continue; }
2140 if (str
[0] == ')') { --parens
; if (parens
< 0) parens
= 0; str
+= 1; continue; }
2141 /* check for string */
2142 if (str
[0] == '"' || str
[0] == '\'') {
2143 const char qch
= *str
++;
2145 const char ch
= *str
++;
2146 if (ch
== qch
) break;
2147 if (ch
== '\\' && *str
) ++str
;
2156 /* evaluate next numeric expression in input string */
2157 /* returns expression value */
2158 static int32_t getExprArg (int *defined
, int *addr
) {
2160 char *a
= strSkipSpaces(currLine
);
2162 if (!a
[0]) fatal("expression expected");
2163 const char *ee
= urasm_expr(&res
, a
, disp
, defined
, addr
, &error
);
2164 if (error
) fatalUrLib(error
);
2166 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
2167 memmove(currLine
, ee
, strlen(ee
)+1);
2175 /* evaluate next string expression in input string */
2176 /* returns expression value */
2177 static char *getStrExprArgFmt (void) {
2179 int donteval
= 0, defined
= 0;
2180 static char resbuf
[256];
2181 char *a
= strSkipSpaces(currLine
);
2182 if (!a
[0]) fatal("expression expected");
2183 urasm_exprval_t res
;
2184 urasm_exprval_init(&res
);
2185 const char *ee
= urasm_expr_ex(&res
, a
, disp
, &donteval
, &defined
, &error
);
2186 if (error
) fatalUrLib(error
);
2188 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
2189 memmove(currLine
, ee
, strlen(ee
)+1);
2194 snprintf(resbuf
, sizeof(resbuf
), "%s", res
.str
);
2196 snprintf(resbuf
, sizeof(resbuf
), "%d", res
.val
);
2198 urasm_exprval_clear(&res
);
2203 /* evaluate next numeric expression in input string */
2204 /* there shoild be no other expressions in the string */
2205 /* returns expression value */
2206 static int32_t getOneExprArg (int *defined
, int *addr
) {
2207 int32_t res
= getExprArg(defined
, addr
);
2208 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
2213 /* is next expression a string literal? */
2214 UR_FORCE_INLINE
int isStrArg (void) {
2215 const char *s
= strSkipSpaces(currLine
);
2216 return (s
[0] == '"' || s
[0] == '\'');
2220 /* check of we reached end of operator */
2221 UR_FORCE_INLINE
int isOperatorEnd (void) {
2222 const char *s
= strSkipSpaces(currLine
);
2223 return (s
[0] == 0 || s
[0] == ':');
2227 /* check of we reached end of operator */
2228 UR_FORCE_INLINE
int isLineEnd (void) {
2229 const char *s
= strSkipSpaces(currLine
);
2230 return (s
[0] == 0 || s
[0] == ';');
2234 /* parse string argument from input string */
2235 /* returns parsed string */
2236 static char *getStrArg (int *lenp
) {
2238 char *a
= strSkipSpaces(currLine
);
2240 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
2241 res
= parseStr(&a
, qCh
, lenp
);
2243 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
2244 memmove(currLine
, a
, strlen(a
)+1);
2252 /* parse string argument from input string; allows math after the string */
2253 /* returns parsed string */
2254 /* returns NULL and math expr in `lenp` */
2255 static char *getStrExprArg (int *lenp
, int isWord
) {
2256 char *a
= strSkipSpaces(currLine
);
2257 const char qCh
= *a
++;
2258 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
2259 char *res
= parseStr(&a
, qCh
, lenp
);
2260 if (!res
[0]) fatal("invalid empty string");
2262 if (a
[0] != ',' && a
[0] != ':' && strlen(res
) <= 2) {
2263 memmove(currLine
, a
, strlen(a
)+1);
2264 // "ab" -- 'a' will be in low (first) byte
2265 // 'ab' -- 'a' will be in high (second) byte
2270 sval
= (uint8_t)(res
[0]&0xffU
)|(((uint8_t)(res
[1]&0xffU
))<<8);
2272 sval
= (uint8_t)(res
[1]&0xffU
)|(((uint8_t)(res
[0]&0xffU
))<<8);
2276 sval
= (uint8_t)(res
[0]&0xffU
);
2278 //fprintf(stderr, "SMATH:000: str=<%s> (sval=%d); <%s>\n", res, sval, currLine);
2280 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2281 int32_t xadd
= getExprArg(&defined
, &fixuptype
);
2282 //fprintf(stderr, "SMATH:001: str=<%s> (sval=%d; xadd=%d); <%s>\n", res, sval, xadd, currLine);
2283 if (pass
> 0 && !defined
) fatal("undefined operand");
2285 if (currLine
[0] && currLine
[0] != ',' && currLine
[0] != ':') fatal("bad string expression");
2287 if (lenp
) *lenp
= sval
;
2289 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
2290 memmove(currLine
, a
, strlen(a
)+1);
2299 /* get identifier (and lowercase it) */
2300 static char *getOneIdArgLo (void) {
2301 static char res
[MAX_LINE_SIZE
+128];
2303 char *a
= strSkipSpaces(currLine
);
2304 memset(res
, 0, sizeof(res
));
2305 if (!a
[0]) fatal("identifier expected");
2306 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
2307 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
2309 if (p
-res
> 120) fatal("identifier too long: %s", res
);
2310 while (*a
&& isSpace(*a
)) ++a
;
2312 memmove(currLine
, a
, strlen(a
)+1);
2313 if (currLine
[0] == ';') currLine
[0] = 0;
2314 if (currLine
[0]) fatal("extra arguments");
2318 if (!res
[0]) fatal("identifier expected");
2319 for (char *t
= res
; *t
; ++t
) *t
= toLower(*t
);
2324 /* get label argument */
2325 static char *getLabelArg (int checkdelim
) {
2326 static char res
[MAX_LINE_SIZE
+128];
2328 char *a
= strSkipSpaces(currLine
);
2329 memset(res
, 0, sizeof(res
));
2330 if (!a
[0]) fatal("label expected");
2331 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
2332 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
2334 if (p
-res
> 120) fatal("label name too long: %s", res
);
2335 while (*a
&& isSpace(*a
)) ++a
;
2337 if (checkdelim
&& a
[0] != ',' && a
[0] != ':') fatal("bad label expression");
2338 memmove(currLine
, a
, strlen(a
)+1);
2342 if (!res
[0]) fatal("label expected");
2347 /* get label argument, and ensure that it is the last one */
2348 static char *getOneLabelArg (void) {
2349 char *res
= getLabelArg(1);
2350 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
2355 /* returns ',' or 0 */
2356 static char eatComma (void) {
2357 char *a
= strSkipSpaces(currLine
);
2358 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
2359 if (a
[0] == ':') return 0;
2360 if (a
[0] != ',') fatal("invalid expression: ',' expected");
2361 for (++a
; *a
&& isSpace(*a
); ++a
) {}
2362 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
2363 memmove(currLine
, a
, strlen(a
)+1);
2368 static int checkDelim (char dm
) {
2369 char *a
= strSkipSpaces(currLine
);
2370 if (a
[0] != dm
) return 0;
2371 for (++a
; *a
&& isSpace(*a
); ++a
) {}
2372 if (!a
[0]) { currLine
[0] = '\0'; return 1; }
2373 memmove(currLine
, a
, strlen(a
)+1);
2378 static void matchDelim (char dm
) {
2379 if (!checkDelim(dm
)) fatal("invalid expression: '%c' expected", dm
);
2383 static int checkIdentCI (const char *ident
) {
2384 ur_assert(ident
&& ident
[0]);
2385 char *a
= strSkipSpaces(currLine
);
2386 while (*a
&& *ident
) {
2387 char c0
= *a
++; if (c0
>= 'A' && c0
<= 'Z') c0
= c0
- 'A' + 'a';
2388 char c1
= *ident
++; if (c1
>= 'A' && c1
<= 'Z') c1
= c1
- 'A' + 'a';
2389 if (c0
!= c1
) return 0;
2391 if (ident
[0] != 0) return 0;
2392 if (*a
== '_' || isAlphaDigit(*a
)) return 0;
2393 while (*a
&& isSpace(*a
)) a
+= 1;
2395 memmove(currLine
, a
, strlen(a
)+1);
2403 ///////////////////////////////////////////////////////////////////////////////
2406 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
2407 char *ep
= strSkipSpacesColons(currLine
);
2408 memmove(currLine
, ep
, strlen(ep
)+1);
2412 static void checkExprEnd (void) {
2413 char *ep
= strSkipSpaces(currLine
);
2414 memmove(currLine
, ep
, strlen(ep
)+1);
2415 if (currLine
[0] && currLine
[0] != ':') fatal("end of expression expected");
2419 static void checkOperatorEnd (void) {
2420 char *ep
= strSkipSpaces(currLine
);
2421 memmove(currLine
, ep
, strlen(ep
)+1);
2422 if (currLine
[0]) fatal("end of operator expected");
2426 /* remove label from curLine */
2427 static void removeLabel (void) {
2428 char *ep
= currLine
;
2429 if (ep
[0] && !isSpace(ep
[0])) for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {} // skip text
2430 // skip spaces and colons
2431 ep
= strSkipSpacesColons(ep
);
2432 memmove(currLine
, ep
, strlen(ep
)+1);
2436 static int labelDoEQU (const char *lblname
, const char *value
) {
2437 static char n2
[256];
2440 if (value
== NULL
|| lblname
== NULL
|| !lblname
[0] || strlen(lblname
) > 255 || strlen(value
) >= MAX_LINE_SIZE
) return -1;
2441 memset(n2
, 0, sizeof(n2
));
2442 strcpy(n2
, lblname
);
2443 if (!urasm_is_valid_name(n2
)) return -1; // this is not a valid label, get out of here
2444 // check if this can be an instruction
2445 lbl
= urAddLabel(lblname
);
2446 if (!lbl
->refFile
) {
2448 lbl
->refFile
= strdup("artificially-defined-label");
2450 //fprintf(stderr, "labelDoEQU: <%s>=<%s>\n", lblname, value);
2451 strcpy(currLine
, value
);
2453 int defined
= 1, addr
= UR_FIXUP_NONE
;
2454 int32_t res
= getOneExprArg(&defined
, &addr
);
2455 lbl
->type
= LBL_TYPE_EQU
;
2456 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
2461 return -1; //fatal("can't calculate label %s", lbl->name);
2468 static void processLabel (void) {
2471 static char n2
[256];
2473 int noLocAff
= 0, doEQU
= 0, isTemp
= 0;
2474 //fprintf(stderr, "LINE %5d: <%s> from <%s>\n", (currSrcLine ? currSrcLine->lineNo : 0), currLine, (currSrcLine ? currSrcLine->fname : "<nowhere>"));
2475 memset(n2
, 0, sizeof(n2
));
2477 currSeenLabel
[0] = 0;
2479 if (!currLine
[0] || isSpace(currLine
[0]) || currLine
[0] == ':') {
2480 // this may be " id = smth" or " id equ smth"
2483 while (isSpace(*ep
)) ++ep
;
2484 if (ep
[0] == ':' || !ep
[0] || (!isAlpha(ep
[0]) && ep
[0] != '_' && ep
[0] != '.' && ep
[0] != '@')) {
2485 removeLabel(); // removeLabel() removes any spaces, etc
2488 // this looks like a label; check for '=' or 'equ'
2489 while (isAlphaDigit(ep
[0]) || ep
[0] == '_' || ep
[0] == '.' || ep
[0] == '@') ++ep
;
2491 // skip trailing spaces
2492 while (isSpace(*ep
)) ++ep
;
2496 } else if (isSpace(*nn
)) {
2498 argstart
= strIsCommand("EQU", ep
);
2503 removeLabel(); // removeLabel() removes any spaces, etc
2506 // remove leading spaces from name
2510 while (isSpace(*ep
)) ++ep
;
2511 if (ep
>= nn
) fatal("internal compiler error");
2512 if (nn
-ep
> 120) fatal("label too long");
2513 memset(n2
, 0, sizeof(n2
));
2514 memmove(n2
, ep
, nn
-ep
);
2515 if (urFindOp(n2
) || !urasm_is_valid_name(n2
)) {
2516 //fatal("invalid label name");
2517 removeLabel(); // removeLabel() removes any spaces, etc
2520 // remove label name
2521 memmove(currLine
, argstart
, strlen(argstart
)+1);
2523 if (n2
[0] == '@' && n2
[1] == '@' && !n2
[2]) {
2524 // unnamed back/forward
2525 fatal("temporary label should end with ':'");
2528 lbl = addTempLabel();
2531 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
2532 lbl
= findAddLabel(n2
);
2534 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2535 if (doEQU
&& pass
== 0 && lbl
->type
!= LBL_TYPE_UNKNOWN
) {
2536 fatal("duplicate label '%s'", lbl
->name
);
2538 int defined
= 1, addr
= UR_FIXUP_NONE
;
2539 int32_t res
= getOneExprArg(&defined
, &addr
);
2540 lbl
->type
= (doEQU
? LBL_TYPE_EQU
: LBL_TYPE_ASS
);
2541 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
2546 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
2553 for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {}
2554 if (ep
-currLine
> 120) fatal("label too long");
2557 memset(n2
, 0, sizeof(n2
));
2558 memmove(n2
, currLine
, ep
-currLine
);
2560 ep
= strSkipSpaces(ep
);
2561 if (*ep
!= ':') return; // this must be an instruction, process it
2563 if (!urasm_is_valid_name(n2
)) return; // this is not a valid label, get out of here
2566 if (findMacro(n2
)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
2568 // check if this can be instruction
2569 //ep = strSkipSpaces(ep);
2570 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
2571 // ok, we got a good label
2573 if (n2
[0] == '@' && n2
[1] == '@' && !n2
[2]) {
2574 // unnamed back/forward
2575 noLocAff
= 1; isTemp
= 1;
2576 lbl
= addTempLabel();
2578 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
2579 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (currSrcLine ? currSrcLine->lineNo : 0));*/
2580 lbl
= findAddLabel(n2
);
2581 //printf("new: [%s]\n", lbl->name);
2585 if (currLine
[0] == '=') {
2586 if (isTemp
) fatal("cannot assign values to temp labels");
2588 argstart
= currLine
+1;
2591 argstart
= strIsCommand("EQU", currLine
);
2593 if (!argstart
|| doEQU
) {
2594 if (pass
== 0 && !isTemp
&& lbl
->type
!= LBL_TYPE_UNKNOWN
) {
2595 fatal("duplicate label '%s'", lbl
->name
);
2599 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2601 if (isTemp
) fatal("cannot assign values to temp labels");
2603 memmove(currLine
, argstart
, strlen(argstart
)+1);
2605 if (lbl
->type
!= LBL_TYPE_UNKNOWN
&& lbl
->type
!= LBL_TYPE_ASS
) {
2606 fatal("duplicate label '%s'", lbl
->name
);
2609 int defined
= 1, addr
= UR_FIXUP_NONE
;
2610 int32_t res
= getOneExprArg(&defined
, &addr
);
2611 lbl
->type
= (doEQU
? LBL_TYPE_EQU
: LBL_TYPE_ASS
);
2612 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
2617 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
2623 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d); disp=#%04X; pc=#%04X\n", lbl->name, doEQU, disp, pc);
2625 // update last seen global
2627 if (lbl
->name
[0] != '{' && lbl
->name
[0] != '.' && !noLocAff
) {
2628 //fprintf(stderr, "**: LASTGLOB: <%s>\n", lbl->name);
2629 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
2630 lastSeenGlobalLabel
= strdup(lbl
->name
);
2632 lbl
->type
= LBL_TYPE_CODE
;
2635 lbl
->fixuptype
= UR_FIXUP_WORD
;
2637 snprintf(currSeenLabel
, sizeof(currSeenLabel
), "%s", n2
);
2638 } else if (lbl
->type
!= LBL_TYPE_UNKNOWN
) {
2639 ur_assert(lbl
->type
== LBL_TYPE_CODE
);
2640 ur_assert(lbl
->value
== disp
);
2643 //fprintf(stderr, "FIXTEMP: %s\n", lbl->name);
2644 lbl
->type
= LBL_TYPE_CODE
;
2647 lbl
->fixuptype
= UR_FIXUP_WORD
;
2648 ur_assert(lbl
->tempIdx
!= 0);
2654 ///////////////////////////////////////////////////////////////////////////////
2655 // instruction finder (in source)
2657 /* array ends with NULL */
2658 /* returns line or NULL */
2659 /* iidx will be set to found instruction number */
2660 static __attribute__((sentinel
)) SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
2661 if (iidx
) *iidx
= -1;
2662 for (SourceLine
*cur
= currSrcLine
; cur
; cur
= cur
->next
) {
2664 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
2666 for (int f
= 0; ;++f
) {
2667 const char *name
= va_arg(ap
, const char *);
2670 if (strIsCommand(name
, cur
->line
)) {
2672 if (iidx
) *iidx
= f
;
2682 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
2683 return findNextInstructionFromList(NULL
, name
, NULL
);
2687 ///////////////////////////////////////////////////////////////////////////////
2690 ///////////////////////////////////////////////////////////////////////////////
2691 static void writeFixups (void) {
2692 void writeFixupList (FILE *fo
, int cnt
, const char *lbl
, int (*chk
)(const FixupItem
*)) {
2695 fprintf(fo
, "%s:\n", lbl
);
2696 if (optFixupType
== 1) fprintf(fo
, " dw %d ; count\n", cnt
);
2697 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
2699 fprintf(fo
, " dw #%04X\n", fx
->opaddr
-prevaddr
);
2700 if (optFixupType
== 2) {
2701 prevaddr
= fx
->opaddr
;
2705 if (optFixupType
== 2 || optFixupType
== 3) fprintf(fo
, " dw 0 ; end marker\n");
2709 if (optFixupType
== 0) {
2710 /* simple text file */
2711 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.txt");
2712 printf("writing fixups to '%s'...\n", fname
);
2713 FILE *fo
= fopen(fname
, "w");
2715 if (fo
== NULL
) fatal("can't write fixup file");
2716 fprintf(fo
, "; addr dadr sz\n");
2717 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
2718 static const char type
[4] = "NWLH";
2719 fprintf(fo
, "%c #%04x #%04x %d\n", type
[fx
->fixuptype
], fx
->opaddr
, fx
->opdestaddr
, fx
->size
);
2723 /* various asm formats */
2724 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.zas");
2725 printf("writing fixups to '%s'...\n", fname
);
2726 FILE *fo
= fopen(fname
, "w");
2727 int cntw
= 0, cntwl
= 0, cntwh
= 0, cntbl
= 0, cntbh
= 0;
2729 if (fo
== NULL
) fatal("can't write fixup file");
2730 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
2731 if (fx
->fixuptype
== UR_FIXUP_WORD
) { ++cntw
; continue; }
2732 switch (fx
->fixuptype
) {
2733 case UR_FIXUP_LOBYTE
: if (fx
->size
== 2) ++cntwl
; else ++cntbl
; break;
2734 case UR_FIXUP_HIBYTE
: if (fx
->size
== 2) ++cntwh
; else ++cntbh
; break;
2737 writeFixupList(fo
, cntw
, "fixup_table_w", lambda(int, (const FixupItem
*fx
) {
2738 return (fx
->fixuptype
== UR_FIXUP_WORD
);
2740 writeFixupList(fo
, cntwl
, "fixup_table_wl", lambda(int, (const FixupItem
*fx
) {
2741 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 2);
2743 writeFixupList(fo
, cntwh
, "fixup_table_wh", lambda(int, (const FixupItem
*fx
) {
2744 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 2);
2746 writeFixupList(fo
, cntbl
, "fixup_table_bl", lambda(int, (const FixupItem
*fx
) {
2747 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 1);
2749 writeFixupList(fo
, cntbh
, "fixup_table_bh", lambda(int, (const FixupItem
*fx
) {
2750 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 1);
2757 ///////////////////////////////////////////////////////////////////////////////
2758 /* return 'found' flag */
2759 static int findChunkFrom (int addr
, int *start
, int *len
) {
2760 if (addr
< 0) addr
= 0;
2761 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) {}
2762 if (addr
> 65535) return 0;
2764 for (; addr
<= 65535 && memused
[addr
]; ++addr
) {}
2765 *len
= addr
-(*start
);
2770 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
2771 for (; buflen
>= 2; buflen
-= 2) {
2773 uint8_t byte
= *buf
++;
2777 for (cnt
= byte
; cnt
>= 0; --cnt
, ++addr
, --buflen
, ++buf
) {
2778 if (!memused
[addr
]) putByte(addr
, *buf
);
2779 if (addr
== 0xffff) return;
2783 for (; cnt
> 0; --cnt
, ++addr
) {
2784 if (!memused
[addr
]) putByte(addr
, byte
);
2785 if (addr
== 0xffff) return;
2792 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
2793 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
2794 if (bxor
) *bxor
= (*bxor
)^b
;
2799 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
2800 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
2801 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
2806 ///////////////////////////////////////////////////////////////////////////////
2809 static int saveSna (const char *fname
, int as48
) {
2810 char *fn
;// = malloc(strlen(fname)+16);
2815 //fn = strprintf("%s/%s.sna", optOutputDir, fname);
2816 fn
= buildOutputFileName(fname
, "sna");
2817 fo
= fopen(fn
, "wb");
2819 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
2820 printf("out: %s.sna\n", fname
);
2821 memmove(regs
, ursna48
, 27);
2824 /* push new address */
2825 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
2827 putByte(sp
, ent
&0xFFU
);
2828 putByte(sp
+1, (ent
>>8)&0xFFU
);
2829 regs
[23] = sp
&0xFFU
;
2830 regs
[24] = (sp
>>8)&0xFFU
;
2832 fwrite(regs
, 27, 1, fo
);
2833 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
2834 fwrite(memory
+16384, 49152, 1, fo
);
2837 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
2839 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
2840 //fprintf(stderr, "%d\n", memused[sp]);
2841 if (memused
[sp
] || memused
[(sp
+1)&0xffff]) {
2842 fprintf(stderr
, "FATAL: can't save snapshot!\n");
2846 if (fwrite(ursna48
, 27, 1, fo
) != 1) goto error
;
2847 rleUnpack(16384, (const uint8_t *)ursna48
+27, sizeof(ursna48
)-27);
2848 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
2850 sprintf(abuf
, "%05d", clrAddr
);
2851 memcpy(memory
+23762, abuf
, 5);
2852 sprintf(abuf
, "%05d", ent
);
2853 memcpy(memory
+23773, abuf
, 5);
2855 if (fwrite(memory
+16384, 49152, 1, fo
) != 1) goto error
;
2858 int addr
= memory
[sp
]+256*memory
[(sp
+1)&0xffff];
2860 if (fWriteWord(fo
, addr
, NULL
) != 0) goto error
; // PC
2861 if (fWriteByte(fo
, 0x10, NULL
) != 0) goto error
; // 7FFD
2862 if (fWriteByte(fo
, 0, NULL
) != 0) goto error
; // TR-DOS inactive
2864 memset(memory
, 0, 16384);
2865 for (int f
= 1; f
< 8; ++f
) {
2866 if (f
!= 2 && f
!= 5) {
2867 if (fwrite(memory
, 16384, 1, fo
) != 1) goto error
;
2881 ///////////////////////////////////////////////////////////////////////////////
2884 static void saveRaw (const char *basename
) {
2885 char *fname
= NULL
;// = malloc(strlen(basename)+16);
2889 while (findChunkFrom(start
, &start
, &len
)) {
2890 if (fname
!= NULL
) free(fname
);
2891 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, (optTapExt
? "tap" : "bin"));
2892 fo
= fopen(fname
, "wb");
2894 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
2896 printf("out: %s\n", fname
);
2897 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
2898 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2907 if (fname
!= NULL
) free(fname
);
2911 ///////////////////////////////////////////////////////////////////////////////
2914 typedef struct __attribute__((packed
)) __attribute__((gcc_struct
)) {
2919 uint8_t zero
; // always zero
2920 uint8_t secLen
; // length in sectors
2925 static uint16_t calcHobSum (const void *hdr
) {
2926 const uint8_t *buf
= (const uint8_t *)hdr
;
2929 for (unsigned int f
= 0; f
< 15; ++f
) res
+= ((uint16_t)buf
[f
])*257+f
;
2934 static void saveHob (const char *basename
) {
2935 char *fname
= NULL
;//malloc(strlen(basename)+16);
2940 while (findChunkFrom(start
, &start
, &len
)) {
2941 if (fname
!= NULL
) free(fname
);
2942 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, "$C");
2943 fo
= fopen(fname
, "wb");
2945 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
2948 char tmpbuf
[sizeof(hdr
.name
)*2];
2949 printf("out: %s\n", fname
);
2950 memset(&hdr
, 0, sizeof(hdr
));
2951 memset(&hdr
.name
, 32, 8);
2952 snprintf(tmpbuf
, sizeof(tmpbuf
), "%c%04X", basename
[0], start
);
2953 while (strlen(tmpbuf
) < sizeof(hdr
.name
)) strcat(tmpbuf
, " "); /* sorry! */
2954 memcpy(hdr
.name
, tmpbuf
, sizeof(hdr
.name
));
2958 hdr
.secLen
= (len
+255)/256;
2959 hdr
.checksum
= calcHobSum(&hdr
);
2960 if (fwrite(&hdr
, sizeof(hdr
), 1, fo
) != 1) {
2961 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2966 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
2967 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2974 if (fwrite(&hdr
.zero
, 1, 1, fo
) != 1) {
2975 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
2981 //fprintf(stderr, ":%d\n", len%256);
2987 if (fname
!= NULL
) free(fname
);
2991 // ////////////////////////////////////////////////////////////////////////// //
2994 uint8_t dir
[14*256];
2996 uint32_t currfstartpos
;
3003 static const uint8_t monoldr_code
[169] = {
3004 0x00,0x00,0xff,0xf0,0xff,0xf0,0xff,0xf0,0xff,0xf3,0x01,0xfd,0x7f,0x3e,0x10,0xed,
3005 0x79,0xaf,0x21,0x00,0x58,0x11,0x01,0x58,0x01,0xff,0x02,0xfb,0x76,0xf3,0xd3,0xfe,
3006 0x36,0x00,0xed,0xb0,0x21,0x00,0x40,0x11,0x01,0x40,0x01,0x00,0x18,0x77,0xed,0xb0,
3007 0xfb,0x76,0xf3,0x3b,0x3b,0xe1,0xe5,0x11,0xce,0xff,0x19,0x7e,0x23,0x5e,0x23,0x56,
3008 0x23,0xc1,0xe5,0xd5,0x69,0x60,0x01,0x35,0x00,0x09,0x4f,0x87,0x81,0x01,0x43,0x00,
3009 0x81,0x4f,0x3e,0x00,0x88,0x47,0xed,0xb0,0xd1,0xe1,0xd5,0x01,0xff,0x03,0x13,0xed,
3010 0xa0,0xed,0xa0,0x13,0x10,0xf8,0xc9,0x31,0xa5,0xa5,0xfb,0x21,0x5a,0x5a,0xe5,0x21,
3011 0x9a,0x02,0xe5,0x76,0x3b,0x3b,0xe1,0x01,0x35,0x00,0x09,0x0e,0x00,0x7e,0xb7,0x28,
3012 0x23,0x23,0x5e,0x23,0x56,0x23,0xe5,0xeb,0x06,0x08,0xb8,0x30,0x01,0x47,0x90,0xf5,
3013 0xc5,0xe5,0xed,0x5b,0xf4,0x5c,0x0e,0x05,0xcd,0x13,0x3d,0xe1,0xc1,0x09,0xf1,0x20,
3014 0xe7,0xe1,0x18,0xd9,0xe1,0xd1,0xf9,0xeb,0xe9,
3018 static void sclInit (SCLFile
*scl
) {
3019 memset(scl
, 0, sizeof(*scl
));
3020 scl
->datasize
= 2*80*16*256; /* maximum disk size */
3021 scl
->data
= malloc(scl
->datasize
);
3022 memset(scl
->data
, 0, scl
->datasize
);
3023 scl
->currfstartpos
= 0xffffffffu
;
3027 static void sclClear (SCLFile
*scl
) {
3029 if (scl
->data
) free(scl
->data
);
3030 memset(scl
, 0, sizeof(*scl
));
3031 scl
->currfstartpos
= 0xffffffffu
;
3035 static void sclStartFile (SCLFile
*scl
, const char *name
, char type
, uint16_t v0
, uint16_t v1
) {
3036 if (scl
->fcount
== 255) {
3037 fprintf(stderr
, "FATAL: too many files in SCL!\n");
3040 if (scl
->currfstartpos
!= 0xffffffffu
) {
3041 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
3045 while (nlen
< 8 && name
&& name
[nlen
]) scl
->dir
[scl
->dirpos
++] = name
[nlen
++];
3046 while (nlen
< 8) { scl
->dir
[scl
->dirpos
++] = ' '; ++nlen
; }
3047 scl
->dir
[scl
->dirpos
++] = type
;
3048 scl
->dir
[scl
->dirpos
++] = v0
&0xff;
3049 scl
->dir
[scl
->dirpos
++] = (v0
>>8)&0xff;
3050 scl
->dir
[scl
->dirpos
++] = v1
&0xff;
3051 scl
->dir
[scl
->dirpos
++] = (v1
>>8)&0xff;
3052 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
3053 scl
->currfstartpos
= scl
->datapos
;
3057 static void sclEndFile (SCLFile
*scl
) {
3058 if (scl
->currfstartpos
== 0xffffffffu
) {
3059 fprintf(stderr
, "FATAL: last SCL file already finished!\n");
3062 /* align to sector */
3063 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
3064 const uint32_t fsz
= scl
->datapos
-scl
->currfstartpos
;
3065 if (fsz
> 255*256) {
3066 fprintf(stderr
, "FATAL: SCL file too big!\n");
3069 ur_assert((fsz
&255) == 0);
3070 scl
->dir
[scl
->dirpos
++] = fsz
/256; /* size in sectors */
3071 ur_assert(scl
->dirpos
%14 == 0);
3073 scl
->currfstartpos
= 0xffffffffu
;
3077 static void sclWriteDataForce (SCLFile
*scl
, const void *buf
, size_t len
) {
3079 if (len
> 255*256) {
3080 fprintf(stderr
, "FATAL: SCL file too big!\n");
3083 memcpy(scl
->data
+scl
->datapos
, buf
, len
);
3084 scl
->datapos
+= len
;
3088 static void sclWriteData (SCLFile
*scl
, const void *buf
, size_t len
) {
3089 if (scl
->currfstartpos
== 0xffffffffu
) {
3090 fprintf(stderr
, "FATAL: no open SCL file!\n");
3093 sclWriteDataForce(scl
, buf
, len
);
3097 static void sclWriteByte (SCLFile
*scl
, uint8_t b
) {
3098 sclWriteData(scl
, &b
, 1);
3102 static void sclWriteWord (SCLFile
*scl
, uint16_t w
) {
3104 sclWriteData(scl
, &b
, 1);
3106 sclWriteData(scl
, &b
, 1);
3110 static int sclFileWrite (FILE *fo
, const void *buf
, size_t count
, uint32_t *checksum
) {
3111 if (!count
) return 0;
3112 const uint8_t *p
= (const uint8_t *)buf
;
3113 if (checksum
) for (size_t n
= count
; n
--; ++p
) *checksum
+= (uint32_t)(*p
);
3114 if (fwrite(buf
, count
, 1, fo
) != 1) return -1;
3120 static int sclSaveToFile (FILE *fo
, SCLFile
*scl
) {
3121 if (scl
->currfstartpos
!= 0xffffffffu
) {
3122 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
3125 const char *sign
= "SINCLAIR";
3126 uint32_t checksum
= 0;
3128 if (sclFileWrite(fo
, sign
, 8, &checksum
) != 0) return -1;
3129 if (sclFileWrite(fo
, &scl
->fcount
, 1, &checksum
) != 0) return -1;
3131 if (sclFileWrite(fo
, scl
->dir
, scl
->dirpos
, &checksum
) != 0) return -1;
3133 if (sclFileWrite(fo
, scl
->data
, scl
->datapos
, &checksum
) != 0) return -1;
3135 for (unsigned f
= 0; f
< 4; ++f
) {
3136 const uint8_t b
= (checksum
>>(f
*8))&0xff;
3137 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
3144 // ////////////////////////////////////////////////////////////////////////// //
3145 static void saveSCLCargador (SCLFile
*scl
) {
3146 static uint8_t cargador
[16384]; // should be enough for everyone
3149 int start
= 0, len
, pos
;
3151 void putStr (const char *s
) {
3152 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3155 void putNum (int num
) {
3157 sprintf(buf
, "%d", num
);
3165 cargador
[pos
++] = (linenum
>>8)&0xff;
3166 cargador
[pos
++] = linenum
&0xff;
3167 // size (will be fixed later)
3168 cargador
[pos
++] = 0;
3169 cargador
[pos
++] = 0;
3173 if (linestart
>= 0) {
3174 const int size
= pos
-linestart
-4;
3175 cargador
[linestart
+2] = size
&0xff;
3176 cargador
[linestart
+3] = (size
>>8)&0xff;
3186 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
3190 while (findChunkFrom(start
, &start
, &len
)) {
3191 // :RANDOMIZE USR VAL "15619":REM:LOAD "
3192 if (cont
) { putStr(":"); cont
= 0; }
3193 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
3194 // generate chunk name
3195 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
3205 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3206 if (off
->type
== 1) continue; // skip +3DOS bootsector
3207 if (off
->type
!= 'C') continue;
3208 if (!off
->hasstart
) continue;
3209 // :RANDOMIZE USR VAL "15619":REM:LOAD "
3210 if (cont
) { putStr(":"); cont
= 0; }
3211 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
3212 // generate chunk name
3213 snprintf(cname
, sizeof(cname
), "%s", off
->name
);
3214 char *dotpos
= strchr(cname
, '.');
3216 if (dotpos
) { type
= toUpper(dotpos
[1]); *dotpos
= 0; }
3217 if (type
< 'A' || type
> 'Z') type
= 'C';
3218 for (char *s
= cname
; *s
; ++s
) *s
= toUpper(*s
);
3227 if (cont
) { putStr(":"); cont
= 0; }
3228 // RANDOMIZE USR VAL "xxx"
3229 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"");
3234 //putWord(1); // autostart line
3237 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
3238 sclWriteData(scl
, cargador
, (size_t)pos
);
3239 sclWriteByte(scl
, 0x80);
3240 sclWriteByte(scl
, 0xaa);
3241 sclWriteWord(scl
, 1);
3246 static void saveSCLCargadorMono (SCLFile
*scl
) {
3247 static uint8_t cargador
[16384]; // should be enough for everyone
3250 int start
= 0, len
, pos
;
3252 void putDB (uint8_t v
) {
3253 cargador
[pos
++] = v
;
3256 void putDW (uint16_t v
) {
3257 cargador
[pos
++] = v
&0xff;
3258 cargador
[pos
++] = (v
>>8)&0xff;
3261 void putStr (const char *s
) {
3262 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3265 void putNum (int num
) {
3267 sprintf(buf
, "%d", num
);
3274 cargador
[pos
++] = (linenum
>>8)&0xff;
3275 cargador
[pos
++] = linenum
&0xff;
3276 // size (will be fixed later)
3277 cargador
[pos
++] = 0;
3278 cargador
[pos
++] = 0;
3283 if (linestart
>= 0) {
3284 const int size
= pos
-linestart
-4;
3285 cargador
[linestart
+2] = size
&0xff;
3286 cargador
[linestart
+3] = (size
>>8)&0xff;
3296 // monoloader parameters:
3299 // dw prg_start_addr
3302 const int bcountpos
= pos
;
3304 putDW(0x4000); // to screen$
3305 putDW(0x4200); // stack
3306 putDW(ent
); // program start address
3307 putDW(0x0000); // stack
3309 for (uint32_t f
= 9; f
< (uint32_t)sizeof(monoldr_code
); ++f
) {
3310 putDB(monoldr_code
[f
]);
3313 while (findChunkFrom(start
, &start
, &len
)) {
3314 if (len
> 255*256) fatal("code chunk too big");
3316 putDB(len
/256+(len
%256 ? 1 : 0));
3321 ++cargador
[bcountpos
];
3324 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3325 if (off
->type
== 1) continue; // skip +3DOS bootsector
3326 if (off
->type
!= 'C') continue;
3327 if (!off
->hasstart
) continue;
3328 if (off
->size
> 255*256) fatal("data chunk too big");
3330 putDB(off
->size
/256+(off
->size
%256 ? 1 : 0));
3334 ++cargador
[bcountpos
];
3342 // CLEAR VAL "xxx":RANDOMIZE USR PEEK VAL "23635"+VAL "256"*PEEK VAL "23636"+VAL "14"
3343 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\":");
3344 putStr("\xf9\xc0(\xbe\xb0\"23635\"+\xb0\"256\"*\xbe\xb0\"23636\"+\xb0\"14\")\r");
3348 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
3349 sclWriteData(scl
, cargador
, (size_t)pos
);
3350 sclWriteByte(scl
, 0x80);
3351 sclWriteByte(scl
, 0xaa);
3352 sclWriteWord(scl
, 1);
3358 while (findChunkFrom(start
, &start
, &len
)) {
3359 ssz
+= len
/256+(len
%256 ? 1 : 0);
3360 sclWriteDataForce(scl
, memory
+start
, len
);
3361 /* align to sector */
3362 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
3365 for (unsigned dphase
= 0; dphase
< 2; ++dphase
) {
3367 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3368 if (off
->type
== 1) continue; // skip +3DOS bootsector
3369 if (off
->type
!= 'C') continue;
3371 if (!off
->hasstart
) continue;
3373 if (off
->hasstart
) continue;
3375 if (off
->size
> 255*256) fatal("data chunk too big");
3376 ssz
+= off
->size
/256+(off
->size
%256 ? 1 : 0);
3377 sclWriteDataForce(scl
, off
->data
, off
->size
);
3378 /* align to sector */
3379 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
3383 // fix basic file sector size
3384 scl
->dir
[scl
->dirpos
-1] += ssz
;
3388 static void saveSCL (const char *basename
) {
3392 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3393 char *fname
= buildOutputFileName(basename
, "scl");
3397 while (findChunkFrom(start
, &start
, &len
)) {
3402 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3403 if (off
->type
== 1) continue; // skip +3DOS bootsector
3404 if (off
->type
== 'C') ++fcount
;
3407 if (fcount
&& optRunSCL
) fcount
+= (optRunSCL
> 0 ? 2 : 1); // +loader and boot
3409 fprintf(stderr
, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname
, fcount
);
3413 // create output file
3414 FILE *fo
= fopen(fname
, "wb");
3416 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
3420 // initialise SCL writer
3423 if (fcount
&& optRunSCL
) {
3424 // create simple boot
3425 if (optRunSCL
> 0) {
3426 const uint8_t dasboot
[] = {
3427 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"
3429 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
3430 sclWriteWord(&scl
, 1); // line number
3431 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
3432 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
3434 sclWriteByte(&scl
, 0x80);
3435 sclWriteByte(&scl
, 0xaa);
3437 sclWriteWord(&scl
, 0);
3440 saveSCLCargador(&scl
);
3446 while (findChunkFrom(start
, &start
, &len
)) {
3447 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
3448 sclStartFile(&scl
, cname
, 'C', (unsigned)start
, (unsigned)len
);
3449 sclWriteData(&scl
, memory
+(unsigned)start
, (unsigned)len
);
3455 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3456 if (off
->type
== 1) continue; // skip +3DOS bootsector
3457 if (off
->type
!= 'C') {
3458 fprintf(stderr
, "skipping data file with invalid type '%C'\n", off
->type
);
3461 snprintf(cname
, sizeof(cname
), "%s", off
->name
);
3462 char *dotpos
= strchr(cname
, '.');
3464 if (dotpos
) { type
= toUpper(dotpos
[1]); *dotpos
= 0; }
3465 if (type
< 'A' || type
> 'Z') type
= 'C';
3466 for (char *s
= cname
; *s
; ++s
) *s
= toUpper(*s
);
3467 sclStartFile(&scl
, cname
, type
, (unsigned)off
->start
, (unsigned)off
->size
);
3468 sclWriteData(&scl
, off
->data
, (unsigned)off
->size
);
3472 if (sclSaveToFile(fo
, &scl
) < 0) {
3473 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
3480 printf("out: %s\n", fname
);
3481 if (fname
!= NULL
) free(fname
);
3485 static void saveSCLMono (const char *basename
) {
3488 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3489 char *fname
= buildOutputFileName(basename
, "scl");
3491 // count total size in sectors
3492 int secsize
= 1; // for loader
3494 while (findChunkFrom(start
, &start
, &len
)) {
3495 secsize
+= len
/256+(len
%256 ? 1 : 0);
3499 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3500 if (off
->type
== 1) continue; // skip +3DOS bootsector
3501 if (off
->type
== 'C') {
3502 secsize
+= off
->size
/256+(off
->size
%256 ? 1 : 0);
3506 if (secsize
> 254) {
3507 fprintf(stderr
, "ERROR: can't write file '%s' (too many sectors: %d)!\n", fname
, secsize
);
3511 // create output file
3512 FILE *fo
= fopen(fname
, "wb");
3514 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
3518 // initialise SCL writer
3522 // create simple boot
3523 if (optRunSCL
> 0) {
3524 const uint8_t dasboot
[] = {
3525 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"
3527 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
3528 sclWriteWord(&scl
, 1); // line number
3529 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
3530 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
3532 sclWriteByte(&scl
, 0x80);
3533 sclWriteByte(&scl
, 0xaa);
3535 sclWriteWord(&scl
, 0);
3539 saveSCLCargadorMono(&scl
);
3541 if (sclSaveToFile(fo
, &scl
) < 0) {
3542 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
3549 printf("out: %s\n", fname
);
3550 if (fname
!= NULL
) free(fname
);
3554 // ////////////////////////////////////////////////////////////////////////// //
3556 int optDskType
= P3DSK_PCW_SS
;
3559 static int saveDSKCargador (P3DiskInfo
*p3d
, int autorun
) {
3560 static uint8_t cargador
[16384]; // should be enough for everyone
3563 int start
= 0, len
, pos
;
3565 void putStr (const char *s
) {
3566 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3569 void putNum (int num
) {
3571 sprintf(buf
, "%d", num
);
3579 cargador
[pos
++] = (linenum
>>8)&0xff;
3580 cargador
[pos
++] = linenum
&0xff;
3581 // size (will be fixed later)
3582 cargador
[pos
++] = 0;
3583 cargador
[pos
++] = 0;
3587 if (linestart
>= 0) {
3588 const int size
= pos
-linestart
-4;
3589 cargador
[linestart
+2] = size
&0xff;
3590 cargador
[linestart
+3] = (size
>>8)&0xff;
3600 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
3602 while (findChunkFrom(start
, &start
, &len
)) {
3605 // generate chunk name
3606 snprintf(cname
, sizeof(cname
), "C%04X.BIN", (unsigned)start
);
3616 // :RANDOMIZE USR VAL "xxx"
3617 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
3621 // build cp/m 8.3 name
3623 if (autorun
) strcpy(pdfname
, "DISK"); else strcpy(pdfname
, "CARGADOR.BAS");
3625 int crerr
= p3dskCreateFile(p3d
, pdfname
);
3627 if (crerr
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3628 if (crerr
!= FLPERR_FILEEXIST
) { fprintf(stderr
, "+3DOS disk error (creating)\n"); return -1; }
3629 p3dskDeleteFiles(p3d
, pdfname
);
3630 crerr
= p3dskCreateFile(p3d
, pdfname
);
3631 if (crerr
< 0) { fprintf(stderr
, "+3DOS disk error (creating1)\n"); return -1; }
3635 if (p3dskOpenFile(p3d
, &nfo
, pdfname
) != FLPERR_OK
) { fprintf(stderr
, "cannot open created +3DOS file '%s'\n", pdfname
); return -1; }
3637 int wrres
= p3dskWriteFile(&nfo
, cargador
, 128, (int)pos
);
3638 if (wrres
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3639 if (wrres
== FLPERR_NOSPACE
) { fprintf(stderr
, "NO FREE SPACE (writing)!\n"); return -1; }
3640 if (wrres
< 0) { fprintf(stderr
, "+3DOS disk writing error (writing)\n"); return -1; }
3642 // write +3DOS header
3643 P3DskFileHeader hdr
;
3644 memset(&hdr
, 0, sizeof(P3DskFileHeader
));
3648 hdr
.filesize
= (uint32_t)(pos
+128);
3649 hdr
.bastype
= 0; // basic
3650 hdr
.baslength
= (uint16_t)pos
;
3652 hdr
.basvarsofs
= (uint16_t)pos
;
3653 if (p3dskWriteFileHeader(&nfo
, &hdr
) != FLPERR_OK
) { fprintf(stderr
, "error writing +3DOS file header\n"); return -1; }
3659 static int saveDSKChunk (P3DiskInfo
*p3d
, const char *name
, const void *data
, int start
, int len
, int p3header
) {
3661 // build cp/m 8.3 name
3663 if (p3dskNormaliseFileName(pdfname
, name
) != FLPERR_OK
) { fprintf(stderr
, "bad +3DOS file name '%s'\n", name
); return -1; }
3665 for (char *s
= pdfname
; *s
; ++s
) if (*s
>= 'a' && *s
<= 'z') s
[0] -= 32;
3667 int crerr
= p3dskCreateFile(p3d
, pdfname
);
3669 if (crerr
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3670 if (crerr
!= FLPERR_FILEEXIST
) { fprintf(stderr
, "+3DOS disk error (creating)\n"); return -1; }
3671 p3dskDeleteFiles(p3d
, pdfname
);
3672 crerr
= p3dskCreateFile(p3d
, pdfname
);
3673 if (crerr
< 0) { fprintf(stderr
, "+3DOS disk error (creating1)\n"); return -1; }
3677 if (p3dskOpenFile(p3d
, &nfo
, pdfname
) != FLPERR_OK
) { fprintf(stderr
, "cannot open created +3DOS file '%s'\n", pdfname
); return -1; }
3679 int wrres
= p3dskWriteFile(&nfo
, data
, (p3header
? 128 : 0), (int)len
);
3680 if (wrres
== FLPERR_MANYFILES
) { fprintf(stderr
, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3681 if (wrres
== FLPERR_NOSPACE
) { fprintf(stderr
, "NO FREE SPACE (writing)!\n"); return -1; }
3682 if (wrres
< 0) { fprintf(stderr
, "+3DOS disk writing error (writing)\n"); return -1; }
3684 // write +3DOS header
3686 P3DskFileHeader hdr
;
3687 memset(&hdr
, 0, sizeof(P3DskFileHeader
));
3691 hdr
.filesize
= (uint32_t)(len
+128);
3692 hdr
.bastype
= 3; // code
3693 hdr
.baslength
= (uint16_t)len
;
3694 hdr
.basaddr
= (uint16_t)start
;
3696 if (p3dskWriteFileHeader(&nfo
, &hdr
) != FLPERR_OK
) { fprintf(stderr
, "error writing +3DOS file header\n"); return -1; }
3703 static void saveDSK (const char *basename
) {
3704 // create and format the floppy
3705 Floppy
*flp
= flpCreate(0);
3706 if (p3dskFormatDisk(flp
, optDskType
) != FLPERR_OK
) {
3708 fprintf(stderr
, "ERROR: cannot format +3DOS disk\n");
3714 if (p3dskDetectGeom(p3d
.flp
, &p3d
.geom
) < 0) {
3716 fprintf(stderr
, "ERROR: not a +3DOS disk!\n");
3720 int hasbootsector
= 0;
3721 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3722 if (off
->type
== 1) { hasbootsector
= 1; break; }
3726 int haschunks
= findChunkFrom(start
, &start
, &len
);
3728 if (!hasbootsector
&& haschunks
) {
3729 if (saveDSKCargador(&p3d
, optRunDSK
) != 0) {
3737 while (findChunkFrom(start
, &start
, &len
)) {
3739 snprintf(cname
, sizeof(cname
), "C%04X.BIN", (unsigned)start
);
3740 if (saveDSKChunk(&p3d
, cname
, memory
+(unsigned)start
, start
, len
, 1) != 0) {
3748 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3749 if (off
->type
== 1) {
3751 if (!p3d
.geom
.restracks
) {
3752 fprintf(stderr
, "no room for +3DOS bootsector\n");
3755 uint8_t *bootsec
= malloc(512);
3756 if (flpGetSectorData(p3d
.flp
, 0, p3d
.geom
.firstsector
, bootsec
, 512) != FLPERR_OK
) {
3757 fprintf(stderr
, "cannot read +3DOS bootsector\n");
3760 size_t clen
= off
->size
;
3761 if (clen
> 512-16) clen
= 512-16;
3762 if (clen
) memcpy(bootsec
+16, off
->data
, clen
);
3763 if (flpPutSectorData(p3d
.flp
, 0, p3d
.geom
.firstsector
, bootsec
, 512) != FLPERR_OK
) fatal("cannot write +3DOS bootsector");
3764 if (p3dskWriteBootableChecksum(&p3d
) != FLPERR_OK
) fatal("cannot make +3DOS disk bootable");
3765 printf("written +3DOS bootsector (%u bytes of code)\n", off
->size
);
3769 if (off
->type
!= 'C') {
3770 fprintf(stderr
, "skipping data file with invalid type '%C'\n", off
->type
);
3774 if (p3dskNormaliseFileName(pdfname
, off
->name
) != FLPERR_OK
) {
3775 fprintf(stderr
, "skipping data file with invalid name '%s'\n", off
->name
);
3778 saveDSKChunk(&p3d
, pdfname
, off
->data
, off
->start
, off
->size
, off
->hasstart
);
3782 //char *fname = strprintf("%s/%s.dsk", optOutputDir, basename);
3783 char *fname
= buildOutputFileName(basename
, "dsk");
3784 FILE *fo
= fopen(fname
, "wb");
3785 if (!fo
) { fprintf(stderr
, "cannot create disk file '%s'\n", fname
); free(fname
); flpDestroy(flp
); return; }
3786 if (dskSaveDSK(flp
, fo
) != FLPERR_OK
) {
3788 fprintf(stderr
, "error writing disk file '%s'\n", fname
);
3794 printf("out: %s\n", fname
);
3800 ///////////////////////////////////////////////////////////////////////////////
3803 static int saveDMB (const char *fname
) {
3804 char *fn
;// = malloc(strlen(fname)+16);
3809 //fn = strprintf("%s/%s.dmb", optOutputDir, fname);
3810 fn
= buildOutputFileName(fname
, "dmb");
3811 fo
= fopen(fn
, "wb");
3813 if (!fo
) { fprintf(stderr
, "ERROR: can't write DMB file: %s.dmb\n", fname
); return -1; }
3815 while (findChunkFrom(start
, &start
, &len
)) { ++pcnt
; start
+= len
; }
3817 printf("out: %s.dmb\n", fname
);
3818 if (fwrite("DMB1", 4, 1, fo
) != 1) goto error
;
3819 if (fWriteWord(fo
, (optRunDMB
? ent
: 0), NULL
) != 0) goto error
;
3820 if (fWriteWord(fo
, clrAddr
, NULL
) != 0) goto error
;
3821 if (fWriteWord(fo
, pcnt
, NULL
) != 0) goto error
;
3824 while (findChunkFrom(start
, &start
, &len
)) {
3825 if (fWriteWord(fo
, len
, NULL
) != 0) goto error
;
3826 if (fWriteWord(fo
, start
, NULL
) != 0) goto error
;
3827 if (fwrite(memory
+start
, len
, 1, fo
) != 1) goto error
;
3835 fprintf(stderr
, "ERROR: error writing file %s.dmb!\n", fname
);
3840 ///////////////////////////////////////////////////////////////////////////////
3843 static char tapeLoaderName
[16];
3846 static void saveTapCargador (FILE *fo
) {
3848 static uint8_t cargador
[16384]; // should be enough for everyone
3849 int start
= 0, len
, pos
, f
;
3853 void putStr (const char *s
) {
3854 for (; *s
; ++s
) cargador
[pos
++] = *s
;
3857 void putNum (int num
) {
3859 sprintf(buf
, "%d", num
);
3866 cargador
[0] = 0; cargador
[1] = 10;
3867 // size (will be fixed later)
3868 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\""); // CLEAR VAL "xxx"
3870 while (findChunkFrom(start
, &start
, &len
)) {
3872 putStr(":\xef\"\"\xaf");
3875 // additional code files
3876 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3877 if (off
->type
== 1) continue; // skip +3DOS bootsector
3878 if (off
->type
!= 'C' || off
->size
> 65533 || !off
->hasstart
) {
3882 putStr(":\xef\"\"\xaf");
3886 // :RANDOMIZE USR VAL "xxx"
3887 putStr(":\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
3889 cargador
[2] = (pos
-4)&0xff;
3890 cargador
[3] = ((pos
-4)>>8)&0xff;
3892 fWriteWord(fo
, 19, NULL
); // length of header
3894 fWriteByte(fo
, 0, &bxor
); // header block
3895 fWriteByte(fo
, 0, &bxor
); // 'basic' flag
3896 if (tapeLoaderName
[0]) {
3897 for (int f
= 0; f
< 10; ++f
) fWriteByte(fo
, tapeLoaderName
[f
], &bxor
);
3899 fWriteByte(fo
, 'c', &bxor
);
3900 fWriteByte(fo
, 'a', &bxor
);
3901 fWriteByte(fo
, 'r', &bxor
);
3902 fWriteByte(fo
, 'g', &bxor
);
3903 fWriteByte(fo
, 'a', &bxor
);
3904 fWriteByte(fo
, 'd', &bxor
);
3905 fWriteByte(fo
, 'o', &bxor
);
3906 fWriteByte(fo
, 'r', &bxor
);
3907 fWriteByte(fo
, ' ', &bxor
);
3908 fWriteByte(fo
, ' ', &bxor
);
3910 fWriteWord(fo
, pos
, &bxor
); // length
3911 fWriteWord(fo
, 10, &bxor
); // start
3912 fWriteWord(fo
, pos
, &bxor
); // length2
3913 fWriteByte(fo
, bxor
, NULL
);
3915 fWriteWord(fo
, pos
+2, NULL
); // plus type and checkbyte
3917 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
3918 for (f
= 0; f
< pos
; ++f
) fWriteByte(fo
, cargador
[f
], &bxor
);
3919 fWriteByte(fo
, bxor
, NULL
);
3923 static void saveTap (const char *basename
) {
3924 char *fname
;// = malloc(strlen(basename)+16);
3926 int start
= 0, len
, f
;
3930 //fname = strprintf("%s/%s.tap", optOutputDir, basename);
3931 fname
= buildOutputFileName(basename
, "tap");
3932 fo
= fopen(fname
, "wb");
3934 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
3935 printf("out: %s.tap\n", basename
);
3936 if (optRunTape
) saveTapCargador(fo
);
3937 while (findChunkFrom(start
, &start
, &len
)) {
3939 if (tapeLoaderName
[0]) {
3940 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
3941 memcpy(blkname
, tapeLoaderName
, 10);
3943 sprintf(blkname
, "c%04X:%04X", start
, len
);
3945 //printf(" block: %s\n", blkname);
3946 fWriteWord(fo
, 19, NULL
); // length of header
3948 fWriteByte(fo
, 0, &bxor
); // header block
3949 fWriteByte(fo
, 3, &bxor
); // 'code' flag
3950 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
3951 fWriteWord(fo
, len
, &bxor
);
3952 fWriteWord(fo
, start
, &bxor
);
3953 fWriteWord(fo
, 32768, &bxor
);
3954 fWriteByte(fo
, bxor
, NULL
);
3956 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
3958 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
3959 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
3960 fWriteByte(fo
, bxor
, NULL
);
3964 for (int dfpass
= 0; dfpass
< 2; ++dfpass
) {
3966 for (OutDataFile
*off
= datafiles
; off
; off
= off
->next
) {
3967 if (off
->type
== 1) continue; // skip +3DOS bootsector
3968 if (off
->type
!= 'C' || off
->size
> 65533) {
3969 fprintf(stderr
, "skipping data file with invalid type '%C'\n", off
->type
);
3973 if (!off
->hasstart
) continue;
3975 if (off
->hasstart
) continue;
3977 snprintf(blkname
, 10, "%s", off
->name
);
3978 blkname
[10] = 0; // just in case
3979 while (strlen(blkname
) < 10) strcat(blkname
, " ");
3981 fWriteWord(fo
, 19, NULL
); // length of header
3983 fWriteByte(fo
, 0, &bxor
); // header block
3984 fWriteByte(fo
, 3, &bxor
); // 'code' flag
3985 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
3986 fWriteWord(fo
, off
->size
, &bxor
);
3987 fWriteWord(fo
, off
->start
, &bxor
);
3988 fWriteWord(fo
, 32768, &bxor
);
3989 fWriteByte(fo
, bxor
, NULL
);
3991 fWriteWord(fo
, off
->size
+2, NULL
); // plus type and checkbyte
3993 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
3994 for (f
= 0; f
< off
->size
; ++f
) fWriteByte(fo
, off
->data
[f
], &bxor
);
3995 fWriteByte(fo
, bxor
, NULL
);
4003 ///////////////////////////////////////////////////////////////////////////////
4004 // pseudoinstructions
4006 // note that processCurrentLine() will NOT skip to the next line before
4007 // calling pseudoinstruction handler!
4008 // note that processCurrentLine() will skip current line after calling
4009 // pseudoinstruction handler!
4011 static int wasOrg
= 0;
4012 static int wasClr
= 0;
4015 // ////////////////////////////////////////////////////////////////////////// //
4016 // print message using printf-like syntax
4017 // doesn't print new line
4018 static void processPrintf (FILE *fo
, const char *fmt
) {
4019 if (!fmt
|| !fmt
[0]) return;
4020 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
4021 char *tempstr
= NULL
;
4022 size_t tempsize
= 0;
4023 char *fmtcopy
= malloc(strlen(fmt
)+1);
4024 strcpy(fmtcopy
, fmt
);
4025 char *currfmt
= fmtcopy
;
4033 char *prcs
= strchr(currfmt
, '%');
4034 if (!prcs
|| !prcs
[1]) {
4035 /* no more formatting; print the tail and exit the loop */
4036 fprintf(fo
, "%s", currfmt
);
4041 if (prcs
[1] == '%') {
4045 /* print up to `prcs` */
4046 if (prcs
> currfmt
) {
4047 size_t partlen
= (ptrdiff_t)(prcs
-currfmt
);
4048 if (partlen
+1 > tempsize
) {
4049 tempsize
= ((partlen
+8)|0xff)+1;
4050 tempstr
= realloc(tempstr
, tempsize
);
4052 memcpy(tempstr
, currfmt
, partlen
);
4053 tempstr
[partlen
] = 0;
4054 fprintf(fo
, "%s", tempstr
);
4056 currfmt
= ++prcs
; /* skip percent */
4057 if (!docheck
) continue;
4063 if (*currfmt
== '+' || *currfmt
== '-') sign
= *currfmt
++;
4064 if (sign
!= '-' && *currfmt
== '0') zerofill
= 1;
4065 while (isDigit(*currfmt
)) { width
= width
*10+((*currfmt
)-'0'); ++currfmt
; }
4066 if (width
> 256) width
= 256;
4068 if (!ftype
) break; /* oops */
4069 if (!eatComma()) fatal("out of arguments for string format");
4071 case 's': /* string */
4073 switch (strSkipSpaces(currLine
)[0]) {
4074 case '"': case '\'': /* string literal? */
4075 strarg
= getStrArg(&stlen
);
4077 default: /* expression */
4078 strarg
= getStrExprArgFmt();
4079 stlen
= (int)strlen(strarg
);
4083 if (sign
!= '-' && stlen
< width
) {
4084 int padlen
= width
-stlen
;
4085 memset(tempbuf
, ' ', padlen
);
4086 tempbuf
[padlen
+1] = 0;
4087 fprintf(fo
, "%s", tempbuf
);
4089 fprintf(fo
, "%s", strarg
);
4091 if (sign
== '-' && stlen
< width
) {
4092 int padlen
= width
-stlen
;
4093 memset(tempbuf
, ' ', padlen
);
4094 tempbuf
[padlen
+1] = 0;
4095 fprintf(fo
, "%s", tempbuf
);
4098 case 'd': /* decimal */
4100 exprval
= getExprArg(&defined
, NULL
);
4101 if (width
&& zerofill
) {
4102 fprintf(fo
, "%0*d", width
, exprval
);
4104 fprintf(fo
, "%*d", (sign
!= '-' ? width
: -width
), exprval
);
4106 fprintf(fo
, "%d", exprval
);
4109 case 'c': /* char */
4111 exprval
= getExprArg(&defined
, NULL
);
4112 if (exprval
<= 0 || exprval
== '?' || exprval
> 255) exprval
= '?';
4114 fprintf(fo
, "%*c", (sign
!= '-' ? width
: -width
), exprval
);
4116 fprintf(fo
, "%c", exprval
);
4119 case 'u': /* unsigned */
4121 exprval
= getExprArg(&defined
, NULL
);
4122 if (width
&& zerofill
) {
4123 fprintf(fo
, "%0*u", width
, (unsigned)(exprval
&0xffff));
4125 fprintf(fo
, "%*u", (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
4127 fprintf(fo
, "%u", (unsigned)(exprval
&0xffff));
4130 case 'x': case 'X': /* hex */
4132 exprval
= getExprArg(&defined
, NULL
);
4133 if (width
&& zerofill
) {
4134 fprintf(fo
, (ftype
== 'x' ? "%0*x" : "%0*X"), width
, (unsigned)(exprval
&0xffff));
4136 fprintf(fo
, (ftype
== 'x' ? "%*x" : "%*X"), (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
4138 fprintf(fo
, (ftype
== 'x' ? "%x" : "%X"), (unsigned)(exprval
&0xffff));
4142 if (ftype
<= 0 || ftype
== 127) ftype
= '?';
4143 fatal("invalid format specifier: '%c'", ftype
);
4147 if (tempstr
) free(tempstr
);
4152 ///////////////////////////////////////////////////////////////////////////////
4155 static int piERROR (void) {
4157 char *res
= getStrArg(&len
);
4158 fprintf(stdout
, "*** USER ERROR: ");
4159 processPrintf(stdout
, res
);
4160 fputc('\n', stdout
);
4161 fatal("user error abort");
4162 return PI_SKIP_LINE
;
4166 static int piWARNING (void) {
4168 char *res
= getStrArg(&len
);
4169 fprintf(stdout
, "*** USER WARNING ");
4171 fprintf(stdout
, "at file %s, line %d: ", currSrcLine
->fname
, currSrcLine
->lineNo
);
4173 fprintf(stdout
, "somewhere in time: ");
4175 processPrintf(stdout
, res
);
4176 fputc('\n', stdout
);
4177 return PI_SKIP_LINE
;
4181 ///////////////////////////////////////////////////////////////////////////////
4184 static int piPrintfCommon (int passNo
) {
4185 if (passNo
< 0 || pass
== passNo
) {
4187 char *res
= getStrArg(&len
);
4188 processPrintf(stdout
, res
);
4189 fputc('\n', stdout
);
4191 return PI_SKIP_LINE
;
4195 static int piDISPLAYX (int passNo
, int asHex
) {
4199 char *res
= getStrArg(&len
);
4201 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
4204 int32_t v
= getExprArg(&defined
, NULL
);
4206 if (passNo
< 0 || pass
== passNo
) {
4207 if (asHex
) printf("%04X", (unsigned int)v
);
4208 else printf("%d", v
);
4211 if (!eatComma()) break;
4213 return PI_SKIP_LINE
;
4217 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
4218 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
4219 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
4220 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
4221 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
4222 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
4224 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
4225 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
4226 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
4229 ///////////////////////////////////////////////////////////////////////////////
4232 static int piORG (void) {
4234 int32_t res
= getOneExprArg(&defined
, NULL
);
4236 if (!defined
) fatal("sorry, ORG operand value must be known here");
4237 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
4238 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
4243 if (!wasClr
&& res
> 0) clrAddr
= res
-1;
4245 return PI_CONT_LINE
;
4249 static int piDISP (void) {
4251 int32_t res
= getOneExprArg(&defined
, NULL
);
4253 if (!defined
) fatal("sorry, DISP operand value must be known here");
4254 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
4255 //printf("DISP=%d\n", res);
4257 return PI_CONT_LINE
;
4261 static int piENDDISP (void) {
4262 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
4265 return PI_CONT_LINE
;
4269 static int piENT (void) {
4271 int32_t res
= getOneExprArg(&defined
, NULL
);
4273 //if (!defined) fatal("sorry, ENT operand value must be known here");
4274 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
4276 return PI_CONT_LINE
;
4280 static int piCLR (void) {
4282 int32_t res
= getOneExprArg(&defined
, NULL
);
4284 //if (!defined) fatal("sorry, CLR operand value must be known here");
4285 if (res
< 0 || res
> 65535) fatal("invalid CLR operand value: %d", res
);
4288 return PI_CONT_LINE
;
4292 // RESERVE start, count
4293 static int piRESERVE (void) {
4294 int defined
= 1, start
;
4295 int32_t res
= getExprArg(&defined
, NULL
);
4296 if (!defined
) fatal("sorry, RESERVE operand values must be known here");
4297 if (res
< 0 || res
> 65535) fatal("invalid RESERVE address value: %d", res
);
4299 if (!eatComma()) fatal("RESERVE needs 2 args");
4300 res
= getOneExprArg(&defined
, NULL
);
4301 if (!defined
) fatal("sorry, RESERVE operand values must be known here");
4302 if (res
< 0 || res
> 65535) fatal("invalid RESERVE length value: %d", res
);
4304 if (memused
[start
]) fatal("trying to reserve already used memory at #%04X", start
);
4305 memresv
[start
++] = 1;
4307 return PI_CONT_LINE
;
4311 static int piALIGN (void) {
4315 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
4316 res
= getOneExprArg(&defined
, NULL
);
4317 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
4318 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
4319 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
4320 if (res
> 0 && pc
%res
!= 0) {
4326 return PI_CONT_LINE
;
4330 static int piDISPALIGN (void) {
4332 int32_t res
= getOneExprArg(&defined
, NULL
);
4334 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
4335 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
4336 if (res
> 0 && disp
%res
!= 0) {
4341 return PI_CONT_LINE
;
4345 ///////////////////////////////////////////////////////////////////////////////
4348 // DEFINCR operations
4352 DFI_SAR
, // arith shift, extend bit 7
4353 DFI_SAL
, // arith shift, extend bit 0
4366 typedef struct DefIncrOp_s DefIncrOp
;
4367 struct DefIncrOp_s
{
4369 uint16_t operator; // DFI_xxx
4373 static DefIncrOp
*dfi_list
= NULL
;
4374 //static int defIncr = 0;
4377 static void dfi_free (void) {
4379 DefIncrOp
*c
= dfi_list
;
4386 //FIXME: make this faster
4387 #define DFI_XSHIFT(sop_) do { \
4388 for (uint16_t f = 0; f < opnd; ++f) { \
4389 val = ((val&xmask) sop_ 1)|(val&andmask ? ormask : 0); \
4394 static int32_t dfi_apply (int32_t val
, int isbyte
) {
4395 const uint16_t xmask
= (isbyte
? 0xff : 0xffff);
4397 uint16_t andmask
, ormask
;
4398 for (DefIncrOp
*dop
= dfi_list
; dop
; dop
= dop
->next
) {
4399 uint16_t opnd
= dop
->operand
;
4400 switch (dop
->operator) {
4403 if (opnd
> (isbyte
? 7 : 15)) { val
= 0; break; }
4404 val
= (val
&xmask
)>>opnd
;
4408 if (opnd
> (isbyte
? 7 : 15)) { val
= 0; break; }
4409 val
= (val
&xmask
)<<opnd
;
4414 if (opnd
> 7) { val
= (val
&0x80 ? 0xff : 0x00); break; }
4415 andmask
= ormask
= 0x80;
4417 if (opnd
> 15) { val
= (val
&0x8000 ? 0xffff : 0x0000); break; }
4418 andmask
= ormask
= 0x8000;
4424 if (opnd
> (isbyte
? 7 : 15)) { val
= (val
&0x01 ? 0xff : 0x00); break; }
4425 andmask
= ormask
= 0x01;
4429 opnd
&= (isbyte
? 7 : 15);
4432 ormask
= (isbyte
? 0x80 : 0x8000);
4436 opnd
&= (isbyte
? 7 : 15);
4438 andmask
= (isbyte
? 0x80 : 0x8000);
4461 if (opnd
) val
/= opnd
; else val
= 0;
4464 if (opnd
) val
%= opnd
; else val
= 0;
4473 static int piDEFINCR (void) {
4476 int32_t cnt
= getOneExprArg(&defined
, NULL
);
4477 if (!defined
) fatal("DEFINCR: increment must be defined");
4479 return PI_CONT_LINE
;
4484 char *rstr
= getStrArg(&len
);
4485 if (!rstr
|| !strEquCI(rstr
, "expr")) fatal("invalid DEFINCR command (\"expr\" expected)");
4486 DefIncrOp
*last
= NULL
;
4487 while (eatComma()) {
4489 rstr
= getStrArg(&len
);
4490 if (!rstr
|| len
!= 3) fatal("invalid DEFINCR operator");
4491 DefIncrOp
*dop
= malloc(sizeof(DefIncrOp
));
4492 if (strEquCI(rstr
, "SHR")) dop
->operator = DFI_SHR
;
4493 else if (strEquCI(rstr
, "SHL")) dop
->operator = DFI_SHL
;
4494 else if (strEquCI(rstr
, "SAR")) dop
->operator = DFI_SAR
;
4495 else if (strEquCI(rstr
, "SAL")) dop
->operator = DFI_SAL
;
4496 else if (strEquCI(rstr
, "ROR")) dop
->operator = DFI_ROR
;
4497 else if (strEquCI(rstr
, "ROL")) dop
->operator = DFI_ROL
;
4498 else if (strEquCI(rstr
, "AND")) dop
->operator = DFI_AND
;
4499 else if (strEquCI(rstr
, "XOR")) dop
->operator = DFI_XOR
;
4500 else if (strEquCI(rstr
, "OR")) dop
->operator = DFI_OR
;
4501 else if (strEquCI(rstr
, "ADD")) dop
->operator = DFI_ADD
;
4502 else if (strEquCI(rstr
, "SUB")) dop
->operator = DFI_SUB
;
4503 else if (strEquCI(rstr
, "MUL")) dop
->operator = DFI_MUL
;
4504 else if (strEquCI(rstr
, "DIV")) dop
->operator = DFI_DIV
;
4505 else if (strEquCI(rstr
, "MOD")) dop
->operator = DFI_MOD
;
4506 else fatal("invalid DEFINCR operator '%s'", rstr
);
4508 if (!eatComma()) fatal("DEFINCR: operand expected");
4510 int32_t res
= getExprArg(&defined
, NULL
);
4511 if (!defined
) fatal("DEFINCR: operand must be defined");
4512 if (res
< 0) fatal("DEFINCR: operand must be positive");
4514 dop
->operand
= (uint16_t)res
;
4515 if (last
) last
->next
= dop
; else dfi_list
= dop
;
4520 int32_t cnt
= getOneExprArg(&defined
, NULL
);
4521 if (!defined
) fatal("DEFINCR: increment must be defined");
4526 DefIncrOp
*dop
= malloc(sizeof(DefIncrOp
));
4528 dop
->operator = DFI_ADD
;
4533 cnt
&= 0xffff; //UB, i don't care
4535 DefIncrOp
*dop
= malloc(sizeof(DefIncrOp
));
4537 dop
->operator = DFI_SUB
;
4544 return PI_CONT_LINE
;
4548 static int piDEFBW (int isWord
) {
4551 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
4555 char *rstr
= getStrExprArg(&len
, isWord
);
4557 for (f
= 0; f
< len
; ++f
) {
4558 int32_t b
= (uint8_t)rstr
[f
];
4560 b
= dfi_apply(b
, 1/*isbyte*/);
4561 if (b
< -128 || b
> 255) fatal("invalid operand value: %d", b
);
4562 if (b
< 0) b
+= 256;
4565 if (!eatComma()) break;
4572 res
= getExprArg(&defined
, &fixuptype
);
4576 //int32_t res = getExprArg(&defined, &fixuptype);
4578 if (pass
> 0 && !defined
) fatal("undefined operand");
4580 res
= dfi_apply(res
, !isWord
/*isbyte*/);
4582 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
4583 if (res
< 0) res
+= 65536;
4585 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 2);
4589 switch (fixuptype
) {
4590 case UR_FIXUP_WORD
: /* swapped bytes */
4591 urasm_fixup_operand(NULL
, pc
, disp
, UR_FIXUP_HIBYTE
, 1);
4592 urasm_fixup_operand(NULL
, pc
, disp
+1, UR_FIXUP_LOBYTE
, 1);
4594 case UR_FIXUP_LOBYTE
:
4595 case UR_FIXUP_HIBYTE
:
4596 warningMsg("non-word fixup for reversed word; wtf?!");
4603 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
4604 if (fixuptype
!= UR_FIXUP_NONE
) {
4605 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
4606 urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
4608 if (res
< 0) res
+= 256;
4612 if (!eatComma()) break;
4614 return PI_CONT_LINE
;
4617 static int piDEFB (void) { return piDEFBW(0); }
4618 static int piDEFW (void) { return piDEFBW(1); }
4619 static int piDEFR (void) { return piDEFBW(2); }
4622 static int piDEFS (void) {
4625 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
4626 int32_t res
= getExprArg(&defined
, &fixuptype
);
4628 if (pass
> 0 && !defined
) fatal("undefined operand");
4629 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
4630 if (*currLine
&& currLine
[0] == ',') {
4632 bt
= getExprArg(&defined
, NULL
);
4633 if (pass
> 0 && !defined
) fatal("undefined operand");
4635 bt
= dfi_apply(bt
, 1/*isbyte*/);
4636 if (bt
< -128 || bt
> 255) fatal("invalid operand value: %d", res
);
4637 if (bt
< 0) bt
+= 256;
4638 if (fixuptype
!= UR_FIXUP_NONE
) {
4639 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
4641 for (f
= 0; f
< res
; ++f
) {
4642 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
4646 pc
+= res
; disp
+= res
;
4648 if (!eatComma()) break;
4650 return PI_CONT_LINE
;
4654 /* bit 0: put '\0' */
4655 /* bit 1: set bit 7 of last byte */
4656 /* bit 2: put length */
4657 static int piDEFSTR (int type
) {
4661 char *res
= getStrArg(&len
);
4664 if (len
> 255) fatal("string too long");
4667 for (f
= 0; f
< len
; ++f
) {
4668 uint8_t b
= (uint8_t)res
[f
];
4670 if ((type
&0x02) && f
== len
-1) b
|= 0x80;
4673 if (type
&0x01) emitByte(0);
4676 int32_t v
= getExprArg(&defined
, NULL
);
4678 if (pass
> 0 && !defined
) fatal("undefined expression");
4679 if (!defined
) v
= 0; else v
= dfi_apply(v
, 1/*isbyte*/);
4680 if (v
< -128 || v
> 255) fatal("invalid operand value: %d", v
);
4681 if (v
< 0) v
+= 256;
4684 if (!eatComma()) break;
4686 return PI_CONT_LINE
;
4690 static int piDEFM (void) { return piDEFSTR(0x00); }
4691 static int piDEFZ (void) { return piDEFSTR(0x01); }
4692 static int piDEFX (void) { return piDEFSTR(0x02); }
4693 static int piDEFC (void) { return piDEFSTR(0x04); }
4696 ///////////////////////////////////////////////////////////////////////////////
4699 /* INCBIN "name"[,maxlen[,offset]] */
4700 static int piINCBIN (void) {
4707 char *args
= currLine
;
4709 if (!currLine
[0]) fatal("INCBIN without file name");
4714 } else if (currLine
[0] == '<') {
4722 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4723 int softinclude
= 0;
4724 if (fn
[0] == '?') { softinclude
= 1; ++fn
; while (isSpace(*fn
)) ++fn
; }
4725 if (!fn
[0]) fatal("INCBIN: empty file name");
4727 char *fname
= createIncludeName(fn
, system
, NULL
, NULL
);
4728 memmove(currLine
, args
, strlen(args
)+1);
4731 if (currLine
[0] == ',') {
4734 maxlen
= getExprArg(&defined
, NULL
);
4735 if (!defined
) fatal("INCBIN: undefined maxlen");
4736 if (maxlen
< 1) { free(fname
); return 1; } // nothing to do
4739 // offset (negative: from the end)
4740 if (currLine
[0] == ',') {
4743 offset
= getOneExprArg(&defined
, NULL
);
4744 if (!defined
) fatal("INCBIN: undefined offset");
4746 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
4749 fl
= fopen(fname
, "rb");
4751 if (!softinclude
) fatal("INCBIN: file not found: '%s'", fname
);
4754 if (fseek(fl
, offset
, (offset
< 0 ? SEEK_END
: SEEK_SET
)) < 0) {
4756 fatal("INCBIN: error seeking to %d in file '%s'", offset
, fname
);
4759 while (maxlen
-- > 0) {
4760 int res
= fread(&bt
, 1, 1, fl
);
4762 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: '%s'", fname
); }
4769 return PI_SKIP_LINE
;
4773 /* DATABIN "name"[,len[,offset[,codeaddr]]] */
4774 /* DATABIN "name|dfname"[,len[,offset[,codeaddr]]] */
4775 static int piDATABINcommon (int ascode
) {
4776 if (pass
!= 1) return PI_SKIP_LINE
;
4782 int codeaddr
= 32768;
4784 char *args
= currLine
;
4786 if (!currLine
[0]) fatal("DATABIN without file name");
4791 } else if (currLine
[0] == '<') {
4799 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4801 if (fn
[0] == '?') { allowskip
= 1; ++fn
; while (isSpace(*fn
)) ++fn
; }
4802 if (!fn
[0]) fatal("DATABIN: empty file name");
4803 char *fnameup
= strdup(fn
);
4804 memmove(currLine
, args
, strlen(args
)+1);
4807 if (currLine
[0] == ',') {
4810 maxlen
= getExprArg(&defined
, NULL
);
4811 if (!defined
) fatal("DATABIN: undefined length");
4812 if (maxlen
< 1) { free(fnameup
); return 1; } // nothing to do
4815 // offset (negative: from the end)
4816 if (currLine
[0] == ',') {
4819 offset
= getExprArg(&defined
, NULL
);
4820 if (!defined
) fatal("DATABIN: undefined offset");
4824 if (currLine
[0] == ',') {
4827 codeaddr
= getOneExprArg(&defined
, NULL
);
4828 if (!defined
) fatal("DATABIN: undefined codeaddr");
4831 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
4834 // find and extract "disk name"
4837 char *pipeptr
= strchr(fnameup
, '|');
4839 if (!pipeptr
[1]) fatal("empty data file output name");
4840 diskname
= strdup(pipeptr
+1);
4843 // build output name from disk name
4844 char *slp
= strrchr(fnameup
, '/');
4846 char *slp1
= strrchr(fnameup
, '\\');
4847 if (slp1
&& (!slp
|| slp1
> slp
)) slp
= slp1
;
4851 if (!slp
[0]) fatal("empty data file output name");
4852 diskname
= strdup(slp
);
4854 diskname
= strdup(fnameup
);
4859 char *fname
= createIncludeName(fnameup
, system
, NULL
, NULL
);
4862 OutDataFile
*off
= appendDataFile(fname
, offset
, maxlen
, allowskip
);
4866 off
->name
= diskname
;
4867 off
->start
= codeaddr
&0xffffU
;
4868 off
->hasstart
= (hasstart
|| ascode
);
4869 //for (char *s = off->name; *s; ++s) *s = toUpper(*s);
4870 printf("data file: %s (start=%u; size=%u; std=%d)\n", off
->name
, off
->start
, off
->size
, off
->hasstart
);
4874 return PI_SKIP_LINE
;
4878 static int piDATABIN (void) { return piDATABINcommon(0); }
4879 static int piCODEBIN (void) { return piDATABINcommon(1); }
4882 ///////////////////////////////////////////////////////////////////////////////
4885 /* INCLUDE "name" */
4886 static int piINCLUDE (void) {
4889 char *args
= currLine
;
4890 if (!currLine
[0]) fatal("INCLUDE without file name");
4894 } else if (currLine
[0] == '<') {
4901 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
4903 int softinclude
= 0;
4904 if (fn
[0] == '?') { softinclude
= 1; ++fn
; while (isSpace(*fn
)) ++fn
; }
4905 if (!fn
[0]) fatal("INCLUDE: empty file name");
4907 if (asmTextInclude(fn
, system
, softinclude
) != 0) {
4908 if (!softinclude
) fatal("INCLUDE: some shit happens!");
4910 return PI_SKIP_LINE
;
4914 ///////////////////////////////////////////////////////////////////////////////
4915 // MODULE, ENDMODULE
4917 static int piENDMODULE (void) {
4918 if (!currModule
) fatal("ENDMODULE without MODULE");
4919 const char *mn
= NULL
;
4920 if (currLine
[0]) mn
= getOneLabelArg();
4922 return PI_SKIP_LINE
;
4926 static int piMODULE (void) {
4929 SourceLine
*ol
= currSrcLine
;
4931 if (currModule
) fatal("no nested modules allowed");
4932 mn
= getOneLabelArg();
4933 if (!urasm_is_valid_name(mn
)) fatal("invalid module name: %s", mn
);
4934 mi
= moduleFind(mn
);
4935 //fprintf(stderr, "+++MODULE: [%s] %p (seen=%d)\n", mn, mi, (mi ? mi->seen : -1));
4938 if (strcmp(mi
->fname
, currSrcLine
->fname
)) {
4939 fatal("duplicate module definition; previous was in %s", mi
->fname
);
4942 nextSrcLine(); /* skip "MODULE" line */
4943 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
4945 fatal("no ENDMODULE");
4947 if (inum
== 0) fatal("no nested modules allowed");
4950 //skipInstruction(); //k8:wtf?!
4951 return piENDMODULE();
4954 mi
= moduleAdd(mn
, currSrcLine
->fname
);
4959 return PI_SKIP_LINE
;
4964 static int piMODEL (void) {
4965 char *mn
= getOneIdArgLo();
4966 if (strSkipSpaces(currLine
)[0]) fatal("only one model name expected");
4967 if (strcmp(mn
, "z80") == 0) urasm_allow_zxnext
= 0;
4968 else if (strcmp(mn
, "z80a") == 0) urasm_allow_zxnext
= 0;
4969 else if (strcmp(mn
, "z80n") == 0) urasm_allow_zxnext
= 1;
4970 else if (strcmp(mn
, "z80next") == 0) urasm_allow_zxnext
= 1;
4971 else if (strcmp(mn
, "zxnext") == 0) urasm_allow_zxnext
= 1;
4972 else fatal("invalid model name: %s", mn
);
4973 return PI_SKIP_LINE
;
4977 ////////////////////////////////////////////////////////////////////////////////
4978 // $SAVECODE "filename",addr,size [,ldaddr [,options...]]
4981 // wipe -- wipe saved area (mark it as unused)
4983 static int piSAVECODE (void) {
4984 //if (pass != 1) return PI_SKIP_LINE;
4985 //char *fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4988 int optWipe
= 0, optData
= 0;
4990 char *args
= currLine
;
4992 if (!currLine
[0]) fatal("SAVECODE without file name");
4996 } else if (currLine
[0] == '<') {
5002 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
5003 if (!fn
[0]) fatal("DATABIN: empty file name");
5005 char *fname
= strdup(fn
);
5006 memmove(currLine
, args
, strlen(args
)+1);
5011 int staddr
= getExprArg(&defined
, NULL
);
5012 if (!defined
) fatal("SAVECODE: undefined start address");
5013 if (staddr
< 0 || staddr
> 65535) fatal("SAVECODE: invalid start address (%d)", staddr
);
5018 int size
= getExprArg(&defined
, NULL
);
5019 if (!defined
) fatal("SAVECODE: undefined size");
5020 if (size
< 0 || size
> 65535 || staddr
+size
> 65536) fatal("SAVECODE: invalid size");
5023 int ldaddr
= staddr
;
5024 if (checkDelim(',')) {
5026 ldaddr
= getExprArg(&defined
, NULL
);
5027 if (!defined
) fatal("SAVECODE: undefined load address");
5028 if (ldaddr
< 0 || ldaddr
> 65535) fatal("SAVECODE: invalid load address");
5032 while (checkDelim(',')) {
5033 char *nstr
= getLabelArg(0/*checkdelim*/);
5034 if (strEquCI(nstr
, "wipe")) { optWipe
= 1; continue; }
5035 if (strEquCI(nstr
, "data")) { optData
= 1; continue; }
5036 fatal("SAVECODE: unknown option '%s'", nstr
);
5038 if (!isLineEnd()) fatal("SAVECODE: unknown extra args");
5040 // save, if we are on the second pass
5041 if (pass
== 1 && size
> 0) {
5042 OutDataFile
*off
= appendDataData(memory
+(unsigned)staddr
, (unsigned)staddr
, (unsigned)size
);
5043 if (!off
) fatal("SAVECODE: out of memory");
5045 off
->hasstart
= !optData
;
5049 if (optWipe
) memset(memused
+staddr
, 0, size
);
5051 if (fname
) free(fname
);
5052 return PI_SKIP_LINE
;
5056 ////////////////////////////////////////////////////////////////////////////////
5059 static int piENDS (void) {
5060 fatal("ENDS without STRUCT");
5062 return PI_SKIP_LINE
;
5066 // this will be registered for each new struct
5067 // use `urCurrentOp` to check what structure we are creating
5068 // `currSeenLabel` contains current seen label
5069 static int piSTRUCT_Internal (void) {
5070 StructInfo
*sth
= urCurrentOp
->udata
;
5072 // reserve room, set default values, add labels
5073 uint16_t origPC
= pc
;
5074 uint16_t origDisp
= disp
;
5079 if (sth
->zerofill
) {
5080 for (uint16_t f
= 0; f
< sth
->size
; ++f
) {
5081 putByte(origPC
+f
, 0);
5085 for (StructField
*fld
= sth
->fields
; fld
; fld
= fld
->next
) {
5087 if (fld
->size
== 1) {
5088 const uint8_t v
= (fld
->initValue
< 0 ? 0x100+fld
->initValue
: fld
->initValue
)&0xff;
5089 putByte(origPC
+fld
->ofs
, v
);
5090 } else if (fld
->size
== 2) {
5091 const uint16_t v
= (fld
->initValue
< 0 ? 0x10000+fld
->initValue
: fld
->initValue
)&0xffff;
5092 putWord(origPC
+fld
->ofs
, v
);
5093 } else if (fld
->size
== 4) {
5095 memcpy(&v
, &fld
->initValue
, 4);
5096 putWord(origPC
+fld
->ofs
, v
&0xffff);
5097 putWord(origPC
+fld
->ofs
+2, (v
>>16)&0xffff);
5099 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth
->name
);
5105 //fprintf(stderr, "LBL: <%s>\n", currSeenLabel);
5106 // need to create a new label?
5107 if (!currSeenLabel
[0] || !fld
->name
|| !fld
->name
[0]) continue;
5109 char *nn
= mangleLabelName(currSeenLabel
);
5110 char *lbn
= malloc(strlen(nn
)+strlen(fld
->name
)+64);
5111 sprintf(lbn
, "%s.%s", nn
, fld
->name
);
5113 UrLabelInfo
*c
= urFindLabel(lbn
);
5114 if (!c
) c
= urAddLabel(lbn
);
5117 c
->type
= LBL_TYPE_STOFS
;
5118 c
->value
= origDisp
+fld
->ofs
;
5123 if (currSeenLabel
[0]) {
5124 char *nn
= mangleLabelName(currSeenLabel
);
5125 char *lbn
= malloc(strlen(nn
)+strlen("_sizeof")+64);
5126 sprintf(lbn
, "%s.%s", nn
, "_sizeof");
5128 UrLabelInfo
*c
= urFindLabel(lbn
);
5129 if (!c
) c
= urAddLabel(lbn
);
5132 c
->type
= LBL_TYPE_STOFS
;
5133 c
->value
= sth
->size
;
5137 // now parse initial values, if there are any
5139 const int isCurly
= checkDelim('{');
5140 SourceLine
*stline
= currSrcLine
;
5142 int wasNewLine
= 1; // don't require a comma
5146 setCurSrcLine(stline
);
5147 fatal("STRUCT: no closing curly bracket");
5152 if (!isCurly
) break;
5159 if (isCurly
&& checkDelim('}')) {
5161 if (!isLineEnd()) fatal("STRUCT: closing curly bracket must be alone on the line");
5171 char *nstr
= getLabelArg(0/*checkdelim*/);
5174 StructField
*fld
= sth
->fields
;
5175 for (; fld
; fld
= fld
->next
) if (fld
->name
&& strcmp(fld
->name
, nstr
) == 0) break;
5176 if (!fld
) fatal("unknown field `%s` in struct `%s`", nstr
, sth
->name
);
5178 // only `=` is allowed
5182 int32_t ival
= getExprArg(&defined
, NULL
);
5184 if (fld
->size
== 1) {
5185 if (defined
&& (ival
< -128 || ival
> 255)) {
5186 fatal("STRUCT: field `%s` has inivalid initial value: %d",
5187 (fld
->name
? fld
->name
: "<anonymous>"), ival
);
5189 if (ival
< -128) ival
= -128;
5190 const uint8_t v
= (ival
< 0 ? 0x100+ival
: ival
)&0xff;
5191 putByte(origPC
+fld
->ofs
, v
);
5192 } else if (fld
->size
== 2) {
5193 if (defined
&& (ival
< -32768 || ival
> 65535)) {
5194 fatal("STRUCT: field `%s` has inivalid initial value: %d",
5195 (fld
->name
? fld
->name
: "<anonymous>"), ival
);
5197 if (ival
< -32768) ival
= -32768;
5198 const uint16_t v
= (ival
< 0 ? 0x10000+ival
: ival
)&0xffff;
5199 putWord(origPC
+fld
->ofs
, v
);
5200 } else if (fld
->size
== 4) {
5202 memcpy(&v
, &ival
, 4);
5203 putWord(origPC
+fld
->ofs
, v
&0xffff);
5204 putWord(origPC
+fld
->ofs
+2, (v
>>16)&0xffff);
5206 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth
->name
);
5211 return PI_SKIP_LINE
;
5215 static int structCheckSizeDecl (const char *s
) {
5216 if (strEquCI(s
, "byte")) return 1;
5217 if (strEquCI(s
, "word")) return 2;
5218 if (strEquCI(s
, "address")) return 2;
5219 if (strEquCI(s
, "dword")) return 4;
5220 if (strEquCI(s
, "label")) return 0;
5221 if (strEquCI(s
, "defs")) return -666;
5222 if (strEquCI(s
, "ds")) return -666;
5227 // STRUCT name [,init_offset]
5228 static int piSTRUCT (void) {
5229 int zerofill
= 0, extend
= 0;
5230 int defined
= 1, inum
;
5233 //fprintf(stderr, "PASS=%d\n", pass);
5235 stline
= currSrcLine
;
5236 nextSrcLine(); // skip ourself
5237 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "ENDS", NULL
))) {
5238 setCurSrcLine(stline
);
5239 fatal("STRUCT: no ENDS");
5241 return PI_SKIP_LINE
;
5245 if (checkDelim('*')) extend = 1;
5246 else if (checkDelim('!')) extend = 2; // compatibility with the old code
5247 else if (checkDelim('+')) extend = 2;
5250 // flags: `[flag, flag...]`
5251 // currently, only `zero_fill` flag is defined
5252 if (checkDelim('[')) {
5253 while (!checkDelim(']')) {
5254 if (zerofill
== 0 && checkIdentCI("zero_fill")) zerofill
= 1;
5255 else fatal("invalid structure flag");
5256 if (!checkDelim(',')) {
5257 if (!checkDelim(']')) fatal("invalid structure flag list");
5263 // if `extend` is before a struct name, we are extending it without inheriting
5264 if (checkIdentCI("extend") || checkIdentCI("extends")) extend
= 1;
5267 char stname
[128], estname
[128];
5269 char *nstr
= getLabelArg(0/*checkdelim*/);
5270 if (!nstr
|| !nstr
[0]) fatal("structure name expected");
5271 if (strlen(nstr
) > 127) fatal("structure name too long");
5272 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr
)) {
5273 fatal("structure name `%s` is invalid", nstr
);
5275 strcpy(stname
, nstr
);
5277 StructInfo
*sth
= NULL
;
5279 for (sth
= structList
; sth
; sth
= sth
->next
) {
5280 if (strcmp(sth
->name
, stname
) == 0) {
5281 fatal("duplicate struct name `%s`!", stname
);
5284 if (urFindOp(stname
)) fatal("invalid struct name `%s`!", stname
);
5287 if (extend
|| checkIdentCI("extend") || checkIdentCI("extends")) {
5291 nstr
= getLabelArg(0/*checkdelim*/);
5292 if (!nstr
|| !nstr
[0]) fatal("parent structure name expected");
5293 if (strlen(nstr
) > 127) fatal("parent structure name too long");
5294 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr
)) {
5295 fatal("structure name `%s` is invalid", nstr
);
5297 strcpy(estname
, nstr
);
5298 for (sth
= structList
; sth
; sth
= sth
->next
) {
5299 if (strcmp(sth
->name
, estname
) == 0) break;
5301 if (!sth
) fatal("STRUCT: cannot extend unknown structure `%s`", estname
);
5304 for (sth
= structList
; sth
; sth
= sth
->next
) {
5305 if (strcmp(sth
->name
, stname
) == 0) break;
5307 if (!sth
) fatal("STRUCT: cannot extend unknown structure `%s`", stname
);
5311 StructInfo
*stnew
= calloc(1, sizeof(StructInfo
));
5312 stnew
->name
= strdup(stname
);
5313 stnew
->size
= sth
->size
;
5314 stnew
->zerofill
= (sth
->zerofill
|| zerofill
);
5317 StructField
*xlast
= NULL
;
5318 for (StructField
*fld
= sth
->fields
; fld
; fld
= fld
->next
) {
5319 StructField
*nf
= calloc(1, sizeof(StructField
));
5320 nf
->name
= (fld
->name
? strdup(fld
->name
) : NULL
);
5322 nf
->size
= fld
->size
;
5323 nf
->initValue
= fld
->initValue
;
5324 nf
->hasInit
= fld
->hasInit
;
5325 if (xlast
) xlast
->next
= nf
; else stnew
->fields
= nf
;
5332 // allocate struct header
5333 sth
= calloc(1, sizeof(StructInfo
));
5334 sth
->name
= strdup(stname
);
5335 sth
->zerofill
= zerofill
;
5338 int32_t currofs
= (sth
? sth
->size
: 0);
5339 // parse initial offset
5340 if (checkDelim(',')) {
5342 currofs
= getExprArg(&defined
, NULL
);
5343 if (!defined
) fatal("STRUCT: initial offset must be defined");
5344 if (currofs
< 0 || currofs
> 65535) fatal("STRUCT: invalid initial offset");
5348 if (!isLineEnd()) fatal("too many arguments for STRUCT");
5350 // for fast appending
5351 StructField
*fldlast
= (sth
? sth
->fields
: NULL
);
5352 StructField
*fldnew
= NULL
;
5355 while (fldlast
->next
) fldlast
= fldlast
->next
;
5358 if (extend
== 2) fldnew
= sth
->fields
;
5361 stline
= currSrcLine
;
5362 nextSrcLine(); // skip ourself
5363 //fprintf(stderr, "001: <%s>\n", currLine);
5365 while (currSrcLine
) {
5366 size_t slen
= strlen(currLine
);
5367 currLineRemoveComment();
5368 currLineTrimBlanks();
5369 if (!currLine
[0]) { nextSrcLine(); continue; }
5371 if (strIsCommand("ENDS", currSrcLine
->line
)) break;
5372 //fprintf(stderr, "112: <%s> (%d)\n", currLine, inum);
5376 char *fldname
= getLabelArg(0/*checkdelim*/);
5377 int fldsize
= structCheckSizeDecl(fldname
);
5378 if (fldsize
== -666) {
5381 fldsize
= getExprArg(&defined
, NULL
);
5382 if (!defined
) fatal("STRUCT: DEFS size must be defined");
5383 if (fldsize
< 0 || fldsize
> 65535) fatal("STRUCT: DEFS size is invalid");
5386 } else if (fldsize
< 0) {
5387 // this must be a label
5389 for (StructField
*cf
= sth
->fields
; cf
; cf
= cf
->next
) {
5390 if (cf
->name
&& cf
->name
[0] && strcmp(cf
->name
, fldname
) == 0) {
5395 if (found
) fatal("duplicate field name '%s'", fldname
);
5396 fldname
= strdup(fldname
);
5397 char *szdecl
= getLabelArg(0/*checkdelim*/);
5398 fldsize
= structCheckSizeDecl(szdecl
);
5399 if (fldsize
== -666) {
5402 fldsize
= getExprArg(&defined
, NULL
);
5403 if (!defined
) fatal("STRUCT: DEFS size must be defined");
5404 if (fldsize
< 0 || fldsize
> 65535) fatal("STRUCT: DEFS size is invalid");
5407 if (fldsize
< 0) fatal("field size definition expected");
5410 fldname
= NULL
; // no name
5413 // check for init value
5415 int32_t initval
= 0;
5417 if (!skipInit
&& checkDelim('=')) {
5419 initval
= getExprArg(&defined
, NULL
);
5420 if (!defined
) fatal("STRUCT: initial value must be defined");
5423 (fldsize
== 1 && (initval
< -128 || initval
> 255)) ||
5424 (fldsize
== 2 && (initval
< -32768 || initval
> 65535)))
5426 fatal("STRUCT: initial value is out of range");
5430 if (!isLineEnd()) fatal("STRUCT: extra field data");
5432 if (currofs
+fldsize
> 65535) fatal("STRUCT: too big");
5435 StructField
*fld
= calloc(1, sizeof(StructField
));
5436 fld
->name
= fldname
;
5438 fld
->size
= fldsize
;
5439 fld
->hasInit
= hasInit
;
5440 fld
->initValue
= initval
;
5441 if (fldlast
) fldlast
->next
= fld
; else sth
->fields
= fld
;
5445 if (!fldnew
) fldnew
= fld
;
5448 fprintf(stderr
, "FLD <%s>: ofs=%u; size=%u; hasinit=%d; init=%d\n", fld
->name
, fld
->ofs
,
5449 fld
->size
, fld
->hasInit
, fld
->initValue
);
5454 if (!currSrcLine
) { setCurSrcLine(stline
); fatal("no ENDS"); }
5455 if (currofs
> 65535) fatal("STRUCT: too big");
5456 sth
->size
= currofs
;
5458 //FIXME: structs must be local to module!
5460 // register new struct
5461 if (extend
== 0 || extend
== 2) {
5462 StructInfo
*slast
= structList
;
5464 while (slast
->next
) slast
= slast
->next
;
5469 UrAsmOp
*op
= urAddOp(sth
->name
, &piSTRUCT_Internal
);
5473 // add struct labels
5474 for (StructField
*fld
= fldnew
; fld
; fld
= fld
->next
) {
5475 if (!fld
->name
|| !fld
->name
[0]) continue;
5477 char *nn
= mangleLabelName(sth
->name
);
5478 char *lbn
= malloc(strlen(nn
)+strlen(fld
->name
)+64);
5479 sprintf(lbn
, "%s.%s", nn
, fld
->name
);
5481 //fprintf(stderr, "%d: <%s>\n", extend, lbn);
5483 UrLabelInfo
*c
= urFindLabel(lbn
);
5484 if (c
) fatal("STRUCT: field name '%s' conflict!", fld
->name
);
5486 c
= urAddLabel(lbn
);
5488 c
->type
= LBL_TYPE_STOFS
;
5489 c
->value
= fld
->ofs
;
5493 // add `structname.sizeof`
5495 char *nn
= mangleLabelName(sth
->name
);
5496 char *lbn
= malloc(strlen(nn
)+64);
5497 sprintf(lbn
, "%s.%s", nn
, "_sizeof");
5499 UrLabelInfo
*c
= urFindLabel(lbn
);
5501 if (!extend
) fatal("STRUCT: field name '%s' conflict!", "_sizeof");
5503 c
= urAddLabel(lbn
);
5507 c
->type
= LBL_TYPE_STOFS
;
5508 c
->value
= sth
->size
;
5512 return PI_SKIP_LINE
;
5516 // ////////////////////////////////////////////////////////////////////////// //
5517 static int piEND_PASMO (void) {
5519 int32_t res
= getOneExprArg(&defined
, NULL
);
5520 //if (!defined) fatal("sorry, ENT operand value must be known here");
5521 if (res
< 0 || res
> 65535) fatal("invalid END operand value: %d", res
);
5523 return PI_CONT_LINE
;
5527 ///////////////////////////////////////////////////////////////////////////////
5530 static int piEDUP (void) {
5531 fatal("EDUP without DUP");
5533 return PI_SKIP_LINE
;
5537 static int piENDR (void) {
5538 fatal("ENDR without REPT");
5540 return PI_SKIP_LINE
;
5544 // DUP count [,var [,initval [,incr]]]
5545 static int piDUPEx (int isrept
) {
5546 int defined
= 1, inum
;
5547 SourceLine
*stline
, *eline
= NULL
;
5549 //int32_t cnt = getOneExprArg(&defined, NULL);
5550 int32_t cnt
= getExprArg(&defined
, NULL
);
5551 if (!defined
) fatal("DUP: counter must be defined");
5552 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
5553 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
5555 int32_t initval
= 0;
5561 if (currLine
[0] == ',') {
5563 char *lbl
= getLabelArg(0/*checkdelim*/);
5565 if (strlen(lbl
) > 127) fatal("DUP counter variable name too long");
5566 if (urFindOp(lbl
) || !urasm_is_valid_name(lbl
)) fatal("DUP counter variable name is invalid");
5571 // initial variable value
5572 if (currLine
[0] == ',') {
5575 initval
= getExprArg(&defined
, NULL
);
5576 if (!defined
) fatal("DUP: initial value must be defined");
5580 if (currLine
[0] == ',') {
5583 incr
= getExprArg(&defined
, NULL
);
5584 if (!defined
) fatal("DUP: increment value must be defined");
5587 if (currLine
[0]) fatal("too many arguments for DUP");
5592 // now find corresponding EDUP
5593 // note that we should skip nested DUPs
5594 nextSrcLine(); // skip ourself
5595 stline
= currSrcLine
;
5596 while (currSrcLine
) {
5597 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "REPT", "EDUP", "ENDR", "ENDM", NULL
))) break;
5598 if (inum
== 0 || inum
== 1) {
5599 if (dupsp
>= 128) fatal("too many nested DUPs");
5600 dupstack
[dupsp
++] = isrept
;
5601 isrept
= (inum
== 1);
5602 nextSrcLine(); // skip DUP
5605 if (dupsp
== 0) { eline
= currSrcLine
; break; }
5607 if (inum
< 3) fatal("invalid REPT end directive");
5609 if (inum
>= 3) fatal("invalid DUP end directive");
5611 isrept
= dupstack
[--dupsp
];
5612 nextSrcLine(); // skip EDUP
5615 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
5617 // create counter local, if necessary
5618 UrLabelInfo
*lcnt
= (vname
[0] ? urAddTempLocal(vname
) : NULL
);
5619 if (lcnt
) lcnt
->value
= initval
;
5623 setCurSrcLine(stline
);
5624 while (currSrcLine
!= eline
) processCurrentLine();
5625 // increment counter
5626 if (lcnt
) lcnt
->value
+= incr
;
5630 urRemoveTempLocal(lcnt
);
5632 return PI_SKIP_LINE
;
5636 static int piDUP (void) { return piDUPEx(0); }
5637 static int piREPT (void) { return piDUPEx(1); }
5640 ///////////////////////////////////////////////////////////////////////////////
5643 static int ifCount
= 0;
5647 // -1: error (should not happen)
5648 // 0: successfully complete
5649 // 1: stopped *AT* "ELSE"
5650 // 2: stopped *AT* "ELSIF"
5651 static int ifSkipToEndIfOrElse (int wholeBody
) {
5652 int inum
, wasElse
= 0;
5655 while (currSrcLine
) {
5656 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "IF", "ELSE", "ENDIF",
5657 "ELSEIF", "MACRO", "ENDM", "IFX",
5664 nextSrcLine(); // skip IF
5665 ifSkipToEndIfOrElse(1); // and recurse
5666 nextSrcLine(); // skip ENDIF
5669 if (wasElse
) fatal("duplicate ELSE");
5670 if (!wholeBody
) return 1;
5672 nextSrcLine(); // skip ELSE
5673 break; // and continue
5675 return 0; // and exit
5676 case 3: /* elseif */
5677 case 7: /* elseifx */
5679 case 9: /* elsifx */
5680 if (wasElse
) fatal("ELSIF in ELSE");
5681 if (!wholeBody
) return 2;
5682 nextSrcLine(); // skip ELSIF
5683 break; // and continue
5685 // skip it as a whole
5686 nextSrcLine(); // skip MACRO
5688 oline
= currSrcLine
;
5689 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) { setCurSrcLine(oline
); fatal("invalid MACRO"); }
5690 if (inum
== 1) break;
5691 fatal("invalid nested MACRO");
5693 nextSrcLine(); // skip ENDM
5696 fatal("unexpected ENDM");
5699 fatal("IF without ENDIF");
5704 static int piENDIF (void) {
5705 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5706 if (--ifCount
< 0) fatal("ENDIF without IF");
5708 return PI_SKIP_LINE
;
5712 static int piELSE (void) {
5713 if (--ifCount
< 0) fatal("ELSE without IF");
5714 nextSrcLine(); // skip ELSE
5715 ifSkipToEndIfOrElse(1);
5716 return PI_SKIP_LINE
;
5719 static int piELSIF (void) {
5720 if (--ifCount
< 0) fatal("ELSIF without IF");
5721 nextSrcLine(); // skip ELSIF
5722 ifSkipToEndIfOrElse(1);
5723 return PI_SKIP_LINE
;
5727 static int piELSIFX (void) {
5728 if (--ifCount
< 0) fatal("ELSIFX without IF");
5729 nextSrcLine(); // skip ELSIFX
5730 ifSkipToEndIfOrElse(1);
5731 return PI_SKIP_LINE
;
5735 static int piIFAll (int isIfX
) {
5737 int ooo
= lblOptMakeU2
;
5739 //fprintf(stderr, "piIFALL: <%s>\n", currLine);
5740 lblOptMakeU2
= (isIfX
? 1 : 0);
5741 int32_t cond
= getOneExprArg(&defined
, NULL
);
5743 //fprintf(stderr, "piIFALL COND: <%d>\n", cond);
5746 if (!isIfX
) fatal("IF: condition must be defined");
5747 cond
= 0; // for IFX: 0 if there is any undefined label
5750 // ok, do it until ELSE/ELSIF/ENDIF
5752 return PI_SKIP_LINE
;
5758 nextSrcLine(); // skip last instruction
5759 // skip until ELSE/ELSIF/ENDIF
5760 r
= ifSkipToEndIfOrElse(0);
5761 /*!fprintf(stderr, "ELSIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5762 if (r
== 0) break; // ENDIF
5763 if (r
== 1) { ++ifCount
; break; } // ELSE
5764 // ELSIF, do condition
5765 args
= strIsCommand("ELSIF", currLine
);
5766 if (args
== NULL
) args
= strIsCommand("ELSEIF", currLine
);
5770 args
= strIsCommand("ELSIFX", currLine
);
5771 if (args
== NULL
) args
= strIsCommand("ELSEIFX", currLine
);
5772 if (args
== NULL
) fatal("internal error in conditionals"); // the thing that should not be
5775 memmove(currLine
, args
, strlen(args
)+1);
5776 /*!fprintf(stderr, "ELSIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5777 cond
= getOneExprArg(&defined
, NULL
);
5778 /*!fprintf(stderr, "ELSIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0), cond);*/
5780 if (!isIfX
) fatal("ELSIF: condition must be defined");
5781 cond
= 0; // for IFX: 0 if there is any undefined label
5783 if (cond
) { ++ifCount
; break; } // condition is true
5785 return PI_SKIP_LINE
;
5789 static int piIF (void) { return piIFAll(0); }
5790 static int piIFX (void) { return piIFAll(1); }
5793 ///////////////////////////////////////////////////////////////////////////////
5795 ///////////////////////////////////////////////////////////////////////////////
5797 what i did with MACRO is the brain-damaged cheating all the way.
5799 first, i will collect the MACRO body and remember it (removing it
5800 from the main source code, so second pass will not see it).
5802 second, when the macro is used, i will:
5803 * insert the whole macro body in place (label resolver will
5804 fix "..lbl" labels for us)
5805 * let the asm play with it
5807 this is not the best scheme, but it is fairly simple and it works.
5810 // will be called when parser encounters term starting with '=' or '*'
5811 // first term char will not be skipped
5812 // must return pointer to the first char after expression end
5813 static const char *getValueCB (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
5817 if (curmacro
== NULL
) fatal("'=' outside of macro");
5818 if (*expr
++ != '=') fatal("'=' expected!");
5820 expr
= strSkipSpaces(expr
);
5822 if (isAlphaDigit(*expr
) || *expr
== '_') {
5823 if (p
-name
> 250) fatal("id too long");
5831 expr
= strSkipSpaces(expr
);
5832 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
5833 if (strEquCI(name
, curmacro
->mac
->argnames
[f
])) {
5836 int l
= (int)strlen(curmacro
->argvals
[f
]);
5838 urasm_exprval_init(&v
);
5839 expr
= urasm_expr_ex(&v
, expr
, addr
, &donteval
, defined
, error
);
5840 if (*error
) return expr
;
5841 if (expr
== NULL
|| *expr
!= ']' || v
.str
!= NULL
) { *error
= UR_EXPRERR_FUNC
; return expr
; }
5843 if (v
.val
< 0) v
.val
+= l
;
5844 if (v
.val
< 0 || v
.val
>= l
) {
5847 res
->val
= (unsigned char)(curmacro
->argvals
[f
][v
.val
]);
5851 urasm_expr_ex(res
, curmacro
->argvals
[f
], addr
, &donteval
, defined
, error
);
5857 fatal("unknown macro variable: '%s'", name
);
5861 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
5862 // opr starts with '=' (invariant)
5863 static int expandCB (char *opr
, int oprlen
) {
5864 char name
[257], *p
= name
, *op
= opr
;
5866 if (curmacro
== NULL
) fatal("'=' outside of macro");
5867 if (*op
++ != '=') fatal("'=' expected!"); // just in case
5868 //fprintf(stderr, "expand: [%s]\n", opr);
5870 if (!isAlpha(op
[0])) return 0; // nothing to do
5872 // copy argument name
5874 if (isAlphaDigit(*op
) || *op
== '_') {
5875 if (p
-name
> 250) fatal("id too long");
5883 // expand argument? we only need to expand `=arg[n]`
5884 op
= strSkipSpaces(op
);
5885 if (op
[0] != '[') return 0;
5887 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
5888 if (strEquCI(name
, curmacro
->mac
->argnames
[f
])) {
5889 ur_assert(*op
== '[');
5890 // replace argument with character, or with literal value
5891 // used for `=regpair[0]` or `=regpair[]`
5894 const int l
= (int)strlen(curmacro
->argvals
[f
]);
5895 const int lleft
= (int)strlen(op
);
5896 char *tmp
= malloc(l
+lleft
+8);
5897 snprintf(tmp
, l
+lleft
+8, "%s%s", curmacro
->argvals
[f
], op
);
5898 if ((int)strlen(tmp
) > oprlen
) {
5907 int error
= 0, defined
= 1, donteval
= 0;
5909 urasm_exprval_init(&v
);
5910 op
= (char *)urasm_expr_ex(&v
, op
, pc
, &donteval
, &defined
, &error
);
5911 if (error
) return -1;
5912 // result should be a number
5913 if (op
== NULL
|| *op
!= ']' || v
.str
!= NULL
) return -1;
5915 // it is guaranteed to have more than one char in opr
5916 // so we can simply put char from argument value, and remove other expression chars
5917 const int l
= (int)strlen(curmacro
->argvals
[f
]);
5918 if (v
.val
< 0) v
.val
+= l
; // negative: indexing from the end
5919 if (v
.val
< 0 || v
.val
>= l
) fatal("index %d is out of bounds for macro argument '%s'", v
.val
, curmacro
->mac
->argnames
[f
]);
5921 opr
[0] = curmacro
->argvals
[f
][v
.val
];
5922 // remove other chars
5923 memmove(opr
+1, op
, strlen(op
)+1);
5929 fatal("unknown macro variable: '%s'", name
);
5933 // main macro expander
5934 static void processMacro (MacroDef
*mc
) {
5935 SourceLine
*oldcurline
= currSrcLine
;
5937 memset(&cm
, 0, sizeof(cm
));
5939 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
5940 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
5942 // parse macro arguments
5944 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) cm
.argvals
[f
] = NULL
;
5948 // do we have more arguments?
5949 if (!currLine
[0]) break;
5950 // check for argument name (this is purely cosmetic thing)
5951 // use like this: "mcall :argname=[value]" (value may be omited for default)
5952 if (currLine
[0] == ':' && isAlpha(currLine
[1])) {
5954 while (isAlphaDigit(currLine
[pos
]) || currLine
[pos
] == '_') ++pos
;
5956 const char svch
= currLine
[pos
];
5958 if (!strEquCI(currLine
+1, mc
->argnames
[currArg
])) {
5959 // out-of-order, find proper argument and rewind to it
5961 for (int c
= 0; c
< mc
->argc
; ++c
) {
5962 if (strEquCI(currLine
+1, mc
->argnames
[c
])) {
5967 if (currArg
< 0) fatal("macro '%s' has no argument named '%s'", mc
->name
, currLine
+1);
5969 currLine
[pos
] = svch
;
5970 // remove argument name
5971 memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
5974 if (currLine
[0] != '=') fatal("expected '=' after argument name");
5976 memmove(currLine
, currLine
+1, strlen(currLine
));
5980 if (currArg
>= mc
->argc
) fatal("too many arguments to macro '%s'", mc
->name
);
5981 // check for default value
5982 if (currLine
[0] == ',') {
5983 if (mc
->argdefaults
[currArg
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[currArg
]);
5984 cm
.argvals
[currArg
] = strdup(mc
->argdefaults
[currArg
]);
5985 memmove(currLine
, currLine
+1, strlen(currLine
));
5987 // skip argument (so we will know its length, and will be able to copy it)
5988 char *e
= skipMacroArg(currLine
);
5989 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
5990 const char ech
= *e
;
5992 if (ech
== ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc
->name
);
5993 if (ech
!= ',') fatal("invalid invocation of macro '%s'", mc
->name
);
5996 cm
.argvals
[currArg
] = strdup(currLine
);
5997 // strip trailing spaces
5998 strTrimRight(cm
.argvals
[currArg
]);
6001 memmove(currLine
, e
+1, strlen(e
));
6008 // check for line end
6010 if (currLine
[0]) fatal("invalid macro invocation");
6012 // setup default argument values
6013 for (int f
= 0; f
< mc
->argc
; ++f
) {
6014 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
6015 if (cm
.argvals
[f
]) continue;
6016 if (mc
->argdefaults
[f
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[f
]);
6017 cm
.argvals
[f
] = strdup(mc
->argdefaults
[f
]);
6018 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
6021 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
6023 // insert macro code into the source
6024 setCurSrcLine(mc
->lines
);
6027 while (currSrcLine
!= NULL
) {
6028 //fprintf(stderr, "*[%s] (%d)\n", currSrcLine->line, currSrcLine->lineNo);
6029 processCurrentLine();
6032 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) if (cm
.argvals
[f
]) free(cm
.argvals
[f
]);
6033 setCurSrcLine(oldcurline
);
6039 static int piMACRO (void) {
6042 char *argdefaults
[MAX_MACRO_ARGS
];
6043 char *argnames
[MAX_MACRO_ARGS
];
6044 SourceLine
*stline
, *eline
= NULL
;
6047 name
= strdup(getLabelArg(0));
6048 //fprintf(stderr, "[%s]\n", name);
6049 if (findMacro(name
) != NULL
) fatal("duplicate MACRO definition: '%s'", name
);
6050 if (currLine
[0] == ',') memmove(currLine
, currLine
+1, strlen(currLine
));
6053 while (currLine
[0]) {
6054 if (argc
>= MAX_MACRO_ARGS
) fatal("too many arguments in MACRO");
6055 if (!isAlpha(currLine
[0])) fatal("invalid MACRO definition");
6056 argnames
[argc
] = strdup(getLabelArg(0));
6057 if (currLine
[0] == '=') {
6059 char *e
= strchr(currLine
, ','), tch
;
6061 if (e
== NULL
) e
= currLine
+strlen(currLine
);
6064 argdefaults
[argc
] = strdup(currLine
+1);
6066 memmove(currLine
, e
, strlen(e
)+1);
6068 argdefaults
[argc
] = NULL
;
6071 if (currLine
[0] == ',') {
6072 memmove(currLine
, currLine
+1, strlen(currLine
));
6075 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
6079 // now find corresponding ENDM
6080 // note that we should skip nested DUPs
6081 stline
= currSrcLine
;
6082 nextSrcLine(); // skip ourself
6083 while (currSrcLine
) {
6086 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) break;
6087 // ok, we found something; what is it?
6090 fatal("no nested MACROs yet");
6093 eline
= currSrcLine
;
6096 nextSrcLine(); // skip ENDM
6100 if (!eline
) { setCurSrcLine(stline
); fatal("no ENDM"); }
6102 if ((mc
= calloc(1, sizeof(*mc
))) == NULL
) fatal("out of memory");
6105 for (int f
= 0; f
< argc
; ++f
) { mc
->argdefaults
[f
] = argdefaults
[f
]; mc
->argnames
[f
] = argnames
[f
]; }
6108 mc
->lines
= stline
->next
;
6109 stline
->next
= currSrcLine
;
6110 stline
->line
[0] = 0;
6114 setCurSrcLine(stline
);
6115 return PI_SKIP_LINE
;
6119 static int piENDM (void) {
6120 fatal("ENDM without MACRO");
6121 return PI_SKIP_LINE
;
6125 ///////////////////////////////////////////////////////////////////////////////
6127 static void piTapParseLoaderName (void) {
6130 if (!isStrArg()) fatal("loader name expected");
6131 char *fn
= getStrArg(&len
);
6132 if (len
> 10) fatal("loader name too long");
6133 memset(tapeLoaderName
, ' ', 10);
6134 for (int f
= 0; f
< len
; ++f
) tapeLoaderName
[f
] = fn
[f
];
6139 static int piMATHMODE (void) {
6140 char *name
= getOneLabelArg();
6141 if (strEquCI(name
, "OLD")) urasm_use_old_priorities
= 1;
6142 else if (strEquCI(name
, "NEW")) urasm_use_old_priorities
= 0;
6143 else fatal("invalid math mode; NEW or OLD expected");
6144 return PI_SKIP_LINE
;
6148 static int piCASTRATES (void) {
6149 char *name
= getOneLabelArg();
6150 if (strEquCI(name
, "YES")) urasm_allow_hex_castrates
= 1;
6151 else if (strEquCI(name
, "TAN")) urasm_allow_hex_castrates
= 1;
6152 else if (strEquCI(name
, "NO")) urasm_allow_hex_castrates
= 0;
6153 else if (strEquCI(name
, "ONA")) urasm_allow_hex_castrates
= 0;
6154 else fatal("yes/no value expected");
6155 return PI_SKIP_LINE
;
6159 static int piREFOPT (void) {
6160 char *name
= getOneLabelArg();
6161 if (strEquCI(name
, "ALLLABELS")) urasm_dump_all_labels
= 1;
6162 else if (strEquCI(name
, "ALL_LABELS")) urasm_dump_all_labels
= 1;
6163 else if (strEquCI(name
, "NOUPCASE")) urasm_dump_all_labels
= 0;
6164 else if (strEquCI(name
, "NO_UPCASE")) urasm_dump_all_labels
= 0;
6165 else if (strEquCI(name
, "DEFAULT")) urasm_dump_all_labels
= 1;
6166 else fatal("invalid refopt mode");
6167 return PI_SKIP_LINE
;
6171 static int piDEFFMT (void) {
6176 name
= getStrArg(&len
);
6178 name
= getLabelArg(1);
6180 if (optWTChanged
) return 1;
6182 optRunDMB
= optRunTape
= optRunSCL
= optRunDSK
= 0;
6185 if (strEquCI(name
, "SNA") || strEquCI(name
, "RUNSNA") || strEquCI(name
, "SNARUN")) {
6187 if (currLine
[0]) fatal("too many expressions");
6188 return PI_SKIP_LINE
;
6190 if (strEquCI(name
, "TAP") || strEquCI(name
, "TAPE")) {
6192 piTapParseLoaderName();
6193 return PI_SKIP_LINE
;
6195 if (strEquCI(name
, "RUNTAP") || strEquCI(name
, "RUNTAPE") || strEquCI(name
, "TAPERUN")) {
6198 piTapParseLoaderName();
6199 return PI_SKIP_LINE
;
6201 if (strEquCI(name
, "BIN") || strEquCI(name
, "RAW")) {
6203 if (currLine
[0]) fatal("too many expressions");
6204 return PI_SKIP_LINE
;
6206 if (strEquCI(name
, "DMB") || strEquCI(name
, "RUNDMB") || strEquCI(name
, "DMBRUN")) {
6207 optRunDMB
= (name
[3] != 0);
6209 if (currLine
[0]) fatal("too many expressions");
6210 return PI_SKIP_LINE
;
6212 if (strEquCI(name
, "NONE") || strEquCI(name
, "NOTHING")) {
6214 if (currLine
[0]) fatal("too many expressions");
6215 return PI_SKIP_LINE
;
6217 if (strEquCI(name
, "SCL") || strEquCI(name
, "RUNSCL") || strEquCI(name
, "SCLRUN")) {
6219 optRunSCL
= (name
[3] != 0 ? -1 : 0); /* no boot */
6220 piTapParseLoaderName();
6221 return PI_SKIP_LINE
;
6223 if (strEquCI(name
, "SCLMONO") || strEquCI(name
, "RUNSCLMONO") || strEquCI(name
, "SCLMONORUN")) {
6225 optRunSCL
= (name
[7] != 0 ? -1 : 0); /* no boot */
6226 piTapParseLoaderName();
6227 return PI_SKIP_LINE
;
6229 if (strEquCI(name
, "SCLBOOT") || strEquCI(name
, "BOOTSCL")) {
6231 optRunSCL
= 1; /* with boot */
6232 piTapParseLoaderName();
6233 return PI_SKIP_LINE
;
6235 if (strEquCI(name
, "SCLMONOBOOT") || strEquCI(name
, "BOOTSCLMONO")) {
6237 optRunSCL
= 1; /* with boot */
6238 piTapParseLoaderName();
6239 return PI_SKIP_LINE
;
6242 if (strEquCI(name
, "DSKBOOT") || strEquCI(name
, "BOOTDSK") ||
6243 strEquCI(name
, "DSK180BOOT") || strEquCI(name
, "BOOTDSK180"))
6246 optRunDSK
= 1; /* with boot */
6247 optDskType
= P3DSK_PCW_SS
; // +3DOS 180K disk
6248 piTapParseLoaderName();
6249 return PI_SKIP_LINE
;
6252 if (strEquCI(name
, "DSK720BOOT") || strEquCI(name
, "BOOTDSK720")) {
6254 optRunDSK
= 1; /* with boot */
6255 optDskType
= P3DSK_PCW_DS
; // +3DOS 720K disk
6256 piTapParseLoaderName();
6257 return PI_SKIP_LINE
;
6260 if (strEquCI(name
, "DSK") || strEquCI(name
, "DSK180")) {
6262 optRunDSK
= 0; /* without boot */
6263 optDskType
= P3DSK_PCW_SS
; // +3DOS 180K disk
6264 piTapParseLoaderName();
6265 return PI_SKIP_LINE
;
6268 if (strEquCI(name
, "DSK720")) {
6270 optRunDSK
= 0; /* without boot */
6271 optDskType
= P3DSK_PCW_DS
; // +3DOS 720K disk
6272 piTapParseLoaderName();
6273 return PI_SKIP_LINE
;
6276 fatal("invalid default output type: %s", name
);
6280 ///////////////////////////////////////////////////////////////////////////////
6283 static void processCurrentLine (void) {
6284 if (!currSrcLine
) return; // do nothing
6286 ur_assert(asmMode
!= AMODE_UFO
); // the thing that should not be
6287 //fprintf(stderr, "<%s>\n", currLine);
6290 char *str
, *ee
, name
[66];
6294 removeSpacesAndColons();
6295 // skip spaces and ':'
6296 if (!currLine
[0]) { nextSrcLine(); break; }
6297 // try to find and process command
6298 str
= currLine
; //while (*str && isSpace(*str)) ++str; // skip spaces
6300 for (ee
= str
; *ee
&& !isSpace(*ee
) && *ee
!= '"' && *ee
!= '\'' && *ee
!= ',' && *ee
!= ':'; ++ee
) {}
6301 // get command, if any
6302 if (ee
!= str
&& ee
-str
<= 64) {
6304 memset(name
, 0, sizeof(name
));
6305 memmove(name
, str
, ee
-str
);
6306 /* UrForth macro? */
6307 uint32_t fwmacro
= ufoFindWordInVocabulary(name
, ufoMacroVocId
);
6310 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
6311 memmove(currLine
, str
, strlen(str
)+1);
6312 ufoRunMacroWord(fwmacro
);
6313 /* only one macro per line! */
6314 nextSrcLine(); // skip it
6317 /* known command? */
6318 op
= urFindOp(name
);
6321 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
6322 memmove(currLine
, str
, strlen(str
)+1);
6326 nextSrcLine(); // skip it
6333 if ((mc
= findMacro(name
)) != NULL
) {
6335 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
6336 memmove(currLine
, str
, strlen(str
)+1);
6338 /* only one macro per line! */
6342 // with reservation management
6344 len
= urasm_opasm(currLine
, pc
, disp
, &errpos
);
6345 if (len
< 0) fatalUrLib(len
);
6346 // check for reservation
6348 if (reserveHit(pc
, len
)) {
6350 if (pc
!= disp
) fatal("trying to use reserved areas with phased code");
6351 reserveReleaseUsed(pc
, len
);
6352 int nextaddr
= reservedFindNextSuitableFreeFrom(pc
, pc
+len
);
6353 if (nextaddr
< 0) fatal("codegen started in reserved area #%04X", pc
);
6354 // insert jump to the new area, and retry
6355 fprintf(stderr
, "...inserting jump from #%04X to #%04X\n", (unsigned)pc
, (unsigned)nextaddr
);
6356 // can we insert a JP? it is faster than JR
6357 if (memresv
[pc
] || memresv
[pc
+1]) fatal("internal reserve area manager error at #%04X", pc
);
6358 if (!memresv
[pc
+2]) {
6360 emitByte(0xc3U
); // JP
6361 emitWord((uint16_t)nextaddr
);
6364 int jrdest
= nextaddr
-(pc
+2U);
6365 if (jrdest
>= 128) fatal("internal reserve area manager error at #%04X (jr dist)", pc
);
6366 emitByte(0x18U
); // JR
6367 emitByte((uint8_t)jrdest
);
6369 disp
= pc
= (uint16_t)nextaddr
;
6370 goto again_from_reserve
;
6372 // did we compiled JP/JR?
6373 if (getByte(pc
) == 0xc3U
|| getByte(pc
) == 0x18U
) {
6374 // no room for jump?
6375 if (memresv
[(pc
+len
)&0xffffU
] || memresv
[(pc
+len
+1)&0xffffU
] || memresv
[(pc
+len
+2)&0xffffU
]) {
6376 if (pc
!= disp
) fatal("trying to use reserved areas with phased code");
6377 // find next free area
6379 while (next
>= 0 && next
< 65536) {
6380 next
= reserveFindFreeFrom(next
);
6381 if (next
< 0) break;
6382 // has room for JP there?
6383 if (!memresv
[next
] && !memresv
[(next
+1)&0xffffU
] && !memresv
[(next
+2)&0xffffU
]) break;
6384 // no room for JP, has room for JP?
6385 if (!memresv
[next
] && !memresv
[(next
+1)&0xffffU
]) {
6386 // check if jr can fit
6387 const int nj
= reservedFindNextJumpableFreeFrom(next
, next
+2);
6390 next
= reserveFindReservedFrom(next
);
6392 if (next
>= 0) disp
= pc
= (next
-len
)&0xffffU
; // so "+len" will make PC right
6395 // check if we still have enough room for a possible jp/jr
6396 if (memresv
[(pc
+len
)&0xffffU
] || memresv
[(pc
+len
+1)&0xffffU
]) goto insert_jump
;
6397 // we have at least a room for JR, check if we have a room for JP
6398 if (!memresv
[(pc
+len
+2)&0xffffU
]) {
6399 // no room for JP, check if jr will be enough
6400 int next
= reservedFindNextSuitableFreeFrom(pc
, pc
+len
+2);
6401 if (next
>= 0 && next
-(pc
+len
+2u) >= 128) goto insert_jump
;
6408 if (len
>= 0 && errpos
) {
6409 memmove(currLine
, errpos
+1, strlen(errpos
));
6411 nextSrcLine(); // skip it
6418 ///////////////////////////////////////////////////////////////////////////////
6419 // start inline UrForth code
6421 static int piSTARTFORTH (void) {
6423 asmMode
= AMODE_UFO
;
6424 ufoRunInterpretLoop();
6425 asmMode
= AMODE_NORMAL
;
6426 return PI_SKIP_LINE
;
6430 ///////////////////////////////////////////////////////////////////////////////
6431 // setup instructions
6433 static void registerInstructions (void) {
6434 urAddOpAndDollar("DISPLAY", piDISPLAY
);
6435 urAddOpAndDollar("DISPLAY0", piDISPLAY0
);
6436 urAddOpAndDollar("DISPLAYA", piDISPLAYA
);
6437 urAddOpAndDollar("DISPHEX", piDISPHEX
);
6438 urAddOpAndDollar("DISPHEX0", piDISPHEX0
);
6439 urAddOpAndDollar("DISPHEXA", piDISPHEXA
);
6441 urAddOpAndDollar("DEFFMT", piDEFFMT
);
6442 urAddOp("$DEFFMT", piDEFFMT
);
6443 urAddOp("$MODEL", piMODEL
);
6444 urAddOp("$SAVECODE", piSAVECODE
);
6446 urAddOp("MACRO", piMACRO
);
6447 urAddOp("ENDM", piENDM
);
6449 urAddOp("ORG", piORG
);
6450 urAddOp("DISP", piDISP
);
6451 urAddOp("ENDDISP", piENDDISP
);
6452 urAddOp("PHASE", piDISP
);
6453 urAddOp("DEPHASE", piENDDISP
);
6454 urAddOp("UNPHASE", piENDDISP
);
6455 urAddOp("ALIGN", piALIGN
);
6456 urAddOp("DISPALIGN", piDISPALIGN
);
6457 urAddOp("PHASEALIGN", piDISPALIGN
);
6458 urAddOp("ENT", piENT
);
6459 urAddOp("CLR", piCLR
);
6460 urAddOp("RESERVE", piRESERVE
);
6462 urAddOpAndDollar("INCLUDE", piINCLUDE
);
6463 urAddOpAndDollar("INCBIN", piINCBIN
);
6464 urAddOpAndDollar("DATABIN", piDATABIN
);
6465 urAddOpAndDollar("CODEBIN", piCODEBIN
);
6467 urAddOp("MODULE", piMODULE
);
6468 urAddOp("ENDMODULE", piENDMODULE
);
6470 urAddOp("DUP", piDUP
);
6471 urAddOp("EDUP", piEDUP
);
6472 urAddOp("REPT", piREPT
);
6473 urAddOp("ENDR", piENDR
);
6475 urAddOp("STRUCT", piSTRUCT
);
6476 urAddOp("ENDS", piENDS
);
6478 urAddOp("END", piEND_PASMO
);
6480 urAddOpAndDollar("IF", piIF
);
6481 urAddOpAndDollar("IFX", piIFX
);
6482 urAddOpAndDollar("ELSE", piELSE
);
6483 urAddOpAndDollar("ELSIF", piELSIF
);
6484 urAddOpAndDollar("ELSIFX", piELSIFX
);
6485 urAddOpAndDollar("ELSEIF", piELSIF
);
6486 urAddOpAndDollar("ELSEIFX", piELSIFX
);
6487 urAddOpAndDollar("ENDIF", piENDIF
);
6489 urAddOp("DEFINCR", piDEFINCR
);
6490 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
6491 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
6492 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
6493 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
6494 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
6495 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
6496 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
6497 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
6499 urAddOp("$ERROR", piERROR
);
6500 urAddOp("$WARNING", piWARNING
);
6502 urAddOp("$PRINTF", piPRINTF
);
6503 urAddOp("$PRINTF0", piPRINTF0
);
6504 urAddOp("$PRINTFA", piPRINTFA
);
6506 urAddOp("$MATHMODE", piMATHMODE
);
6507 urAddOp("$REFOPT", piREFOPT
);
6508 urAddOp("$CASTRATES", piCASTRATES
);
6510 urAddOp("$START_FORTH", piSTARTFORTH
);
6514 ///////////////////////////////////////////////////////////////////////////////
6515 // !0: invalid label
6517 static inline void fnSkipSpaces (const char *expr) {
6518 while (*expr && isSpace(*expr)) ++expr;
6524 UR_FORCE_INLINE
char fnNextChar (const char *expr
) {
6525 while (*expr
&& isSpace(*expr
)) ++expr
;
6530 #define FN_SKIP_BLANKS do { \
6531 while (*expr && isSpace(*expr)) ++expr; \
6534 #define FN_CHECK_END do { \
6535 while (*expr && isSpace(*expr)) ++expr; \
6536 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
6541 #define FN_CHECK_COMMA do { \
6542 while (*expr && isSpace(*expr)) ++expr; \
6543 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
6548 static const char *readLabelName (char *buf
, const char *expr
) {
6551 while (*expr
&& isSpace(*expr
)) ++expr
;
6555 if (pos
>= 128) return NULL
;
6556 if (ch
== ')') { --expr
; break; }
6558 if (isAlphaDigit(ch
) || ch
== '$' || ch
== '.' || ch
== '_' || ch
== '@') {
6564 if (pos
< 1) return NULL
;
6566 if (!urasm_is_valid_name(buf
)) return NULL
;
6571 static const char *fnDefKn (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
, int qtype
) {
6574 if ((expr
= readLabelName(lbl
, expr
)) == NULL
) { *error
= UR_EXPRERR_LABEL
; return NULL
; }
6576 if (!donteval
) res
->val
= (isLabelDefinedOrKnown(lbl
, addr
, qtype
) != 0);
6580 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
); }
6581 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
); }
6584 static const char *fnAligned256 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6585 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6587 if (!donteval
) res
->val
= (res
->val
%256 ? 0 : 1);
6592 static const char *fnSameSeg (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6593 urasm_exprval_t v0
, v1
;
6595 urasm_exprval_init(&v0
);
6596 urasm_exprval_init(&v1
);
6597 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
6598 if (*error
) return expr
;
6600 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
6601 if (*error
) return expr
;
6603 if (!donteval
) res
->val
= (v0
.val
/256 == v1
.val
/256);
6608 static const char *fnAlign (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6609 urasm_exprval_t v0
, v1
;
6610 urasm_exprval_init(&v0
);
6611 urasm_exprval_init(&v1
);
6612 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
6613 if (*error
) return expr
;
6615 if (fnNextChar(expr
) == ',') {
6617 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
6618 if (*error
) return expr
;
6624 if (v1
.val
< 1) { *error
= UR_EXPRERR_FUNC
; return NULL
; }
6625 res
->val
= (v0
.val
+v1
.val
-1)/v1
.val
*v1
.val
;
6631 static const char *fnLow (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6632 const char *ee
= expr
;
6633 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6636 if (res
->fixuptype
== UR_FIXUP_HIBYTE
) {
6637 *error
= UR_EXPRERR_FUNC
; return ee
;
6639 res
->fixuptype
= UR_FIXUP_LOBYTE
;
6646 static const char *fnHigh (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6647 const char *ee
= expr
;
6648 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6651 if (res
->fixuptype
== UR_FIXUP_LOBYTE
) {
6652 *error
= UR_EXPRERR_FUNC
; return ee
;
6654 res
->fixuptype
= UR_FIXUP_HIBYTE
;
6655 res
->val
= (res
->val
>>8)&0xff;
6661 static const char *fnAbs (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6662 const char *ee
= expr
;
6663 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6666 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
6667 *error
= UR_EXPRERR_FUNC
; return ee
;
6669 res
->val
= abs(res
->val
);
6675 static const char *fnBSwap (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6676 const char *ee
= expr
;
6677 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6680 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
6681 *error
= UR_EXPRERR_FUNC
; return ee
;
6683 res
->val
= ((res
->val
>>8)&0xff)|((res
->val
&0xff)<<8);
6689 static const char *fnScrAddr8xn (int nmul
, urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6690 int32_t scrbase
= 0x4000;
6692 const char *ee
= expr
;
6693 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
6694 // first argument is `x`
6695 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6697 // second argument is `y`
6699 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6701 // optional third arg is screen base
6703 if (expr
[0] == ',') {
6705 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6706 scrbase
= res
->val
&0xffff;
6710 //urasm_exprval_clear(res);
6711 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
6712 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
6713 if (y
< 0 || y
> 191) fatal("invalid y coordinate: %d", y
/nmul
);
6719 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
6725 static const char *fnScrAddr8x1 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6726 return fnScrAddr8xn(1, res
, expr
, addr
, donteval
, defined
, error
);
6729 static const char *fnScrAddr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6730 return fnScrAddr8xn(8, res
, expr
, addr
, donteval
, defined
, error
);
6734 static const char *fnScrAttr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6735 int32_t scrbase
= 0x4000;
6737 const char *ee
= expr
;
6738 // first argument is `x`
6739 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6741 // second argument is `y`
6743 urasm_exprval_clear(res
);
6744 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6746 // optional third arg is screen base
6748 if (expr
[0] == ',') {
6750 urasm_exprval_clear(res
);
6751 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
6752 scrbase
= res
->val
&0xffff;
6754 urasm_exprval_clear(res
);
6757 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
6758 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
6759 if (y
< 0 || y
> 23) fatal("invalid y coordinate: %d", y
);
6760 res
->val
= scrbase
+6144+y
*32+x
;
6766 static const char *fnMArgToStr (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6767 const char *ee
= expr
;
6768 // argument is macro argument name
6769 expr
= strSkipSpacesConst(expr
);
6770 if (expr
[0] != '=') { *error
= UR_EXPRERR_FUNC
; return ee
; }
6772 if (!isAlpha(expr
[0]) && expr
[0] != '_') { *error
= UR_EXPRERR_FUNC
; return ee
; }
6773 const char *nend
= expr
+1;
6774 while (isAlphaDigit(*nend
) || *nend
== '_') ++nend
;
6775 if (nend
-expr
> 127) { *error
= UR_EXPRERR_FUNC
; return ee
; }
6777 memset(name
, 0, sizeof(name
));
6778 memcpy(name
, expr
, nend
-expr
);
6783 expr
= strSkipSpacesConst(expr
);
6784 if (expr
[0] == '[') {
6785 expr
= strSkipSpacesConst(expr
+1);
6786 if (expr
[0] != ']') {
6789 if (expr
[0] == '+') ++expr
;
6790 else if (expr
[0] == '-') { doneg
= 1; ++expr
; }
6792 while (isDigit(expr
[0])) {
6793 index
= index
*10+(expr
[0]-'0');
6794 if (index
>= 0x1fffffff) fatal("`margtostr` index too high");
6797 expr
= strSkipSpacesConst(expr
);
6798 if (doneg
) index
= -index
;
6800 if (expr
[0] != ']') fatal("`margtostr` invalid index syntax");
6805 if (curmacro
== NULL
) fatal("`margtostr` outside of macro");
6807 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
6808 if (strEquCI(name
, curmacro
->mac
->argnames
[f
])) {
6809 // found argument, convert it to string
6811 res
->str
= realloc(res
->str
, strlen(curmacro
->argvals
[f
])+1);
6812 strcpy(res
->str
, curmacro
->argvals
[f
]);
6814 const size_t slen
= strlen(curmacro
->argvals
[f
]);
6817 if (index
> slen
) fatal("index out of string");
6818 index
= (int)slen
-index
;
6820 if (index
>= slen
) fatal("index out of string");
6821 res
->str
= realloc(res
->str
, 2);
6822 res
->str
[0] = curmacro
->argvals
[f
][index
];
6826 for (char *s
= res
->str
; *s
; ++s
) *s
= toLower(*s
);
6827 //fprintf(stderr, "<%s>\n", res->str);
6830 res
->val
= ((unsigned char)res
->str
[0]);
6831 res
->val
|= ((unsigned char)res
->str
[1])<<8;
6833 res
->val
= (unsigned char)res
->str
[0];
6841 fatal("unknown macro argument '%s'", name
);
6847 static const char *fnStrLen (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
6848 expr
= strSkipSpacesConst(expr
);
6851 case '"': case '\'': /* string literal? */
6853 char *a
= (char *)expr
+1;
6854 (void)parseStr(&a
, expr
[0], &stlen
);
6855 expr
= (const char *)a
;
6858 default: /* expression */
6861 urasm_exprval_init(&r
);
6862 expr
= urasm_expr_ex(&r
, expr
, disp
, &donteval
, defined
, error
);
6863 if (*error
) fatalUrLib(*error
);
6864 if (!r
.str
) fatal("string expected for `strlen()`");
6865 stlen
= (int)strlen(r
.str
);
6866 urasm_exprval_clear(&r
);
6872 urasm_exprval_setint(res
, stlen
);
6878 static void registerFunctions (void) {
6879 urasm_expr_register_func("defined", fnDefined
);
6880 urasm_expr_register_func("known", fnKnown
);
6881 urasm_expr_register_func("aligned256", fnAligned256
);
6882 urasm_expr_register_func("align", fnAlign
);
6883 urasm_expr_register_func("sameseg", fnSameSeg
);
6884 urasm_expr_register_func("low", fnLow
);
6885 urasm_expr_register_func("high", fnHigh
);
6886 urasm_expr_register_func("abs", fnAbs
);
6887 urasm_expr_register_func("bswap", fnBSwap
);
6889 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8
);
6890 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1
);
6891 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8
);
6893 urasm_expr_register_func("marg2str", fnMArgToStr
);
6894 urasm_expr_register_func("strlen", fnStrLen
);
6898 ///////////////////////////////////////////////////////////////////////////////
6899 // preparing another pass
6901 static void initPass (void) {
6902 asmMode
= AMODE_NORMAL
;
6903 currSrcLine
= asmText
;
6917 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
6918 lastSeenGlobalLabel
= NULL
;
6919 //lastSeenGlobalLabel = strdup(" [MAIN] ");
6922 urasm_use_old_priorities
= 0;
6923 strcpy(lastFindLabelName
, "");
6924 strcpy(currSeenLabel
, "");
6925 urResetTempLabels();
6926 ufoClearIncludePath();
6930 static int postPass (void) {
6931 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
6932 lastSeenGlobalLabel
= NULL
;
6933 if (checkLabels()) return -1;
6934 if (ifCount
!= 0) fatal("unbalanced IFs");
6939 ///////////////////////////////////////////////////////////////////////////////
6940 static int labelCmp (const void *aa
, const void *bb
) {
6941 if (aa
== bb
) return 0;
6942 const UrLabelInfo
*a
= *(const UrLabelInfo
**)aa
;
6943 const UrLabelInfo
*b
= *(const UrLabelInfo
**)bb
;
6945 a
->value
< b
->value
? -1 :
6946 a
->value
> b
->value
? 1 :
6951 static __attribute__((unused
)) int strStartsWith (const char *s
, const char *pat
) {
6952 if (!pat
|| !pat
[0]) return 0;
6953 if (!s
|| !s
[0]) return 0;
6954 while (*s
&& *pat
) {
6955 if (*s
!= *pat
) return 0;
6963 static int isGoodLabelForRef (const UrLabelInfo
*ll
) {
6967 // do not output `=` labels
6968 if (ll->type == LBL_TYPE_ASS) return 0;
6969 // do not output struct labels
6970 if (ll->type == LBL_TYPE_STOFFS) return 0;
6973 if (urasm_dump_all_labels
) {
6974 if (strStartsWith(ll
->name
, "UFO_") || strStartsWith(ll
->name
, "USE_")) return 0;
6978 for (const char *s
= ll
->name
; *s
; ++s
) {
6980 if (ch
>= 'A' && ch
<= 'Z') continue;
6981 if (ch
>= '0' && ch
<= '9') continue;
6982 if (ch
== '_') continue;
6990 static void writeLabelsFile (const char *fname
) {
6991 if (!fname
|| !fname
[0]) return;
6995 for (const UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) {
6996 if (isGoodLabelForRef(ll
)) ++lcount
;
6999 UrLabelInfo
**larr
= NULL
;
7001 // create labels array
7002 larr
= (UrLabelInfo
**)malloc(sizeof(UrLabelInfo
*)*lcount
);
7004 for (UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) {
7005 if (isGoodLabelForRef(ll
)) larr
[lcount
++] = ll
;
7008 qsort(larr
, lcount
, sizeof(UrLabelInfo
*), &labelCmp
);
7012 FILE *fo
= fopen(fname
, "w");
7014 for (int f
= 0; f
< lcount
; ++f
) {
7015 UrLabelInfo
*ll
= larr
[f
];
7016 //if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
7017 if (!ll
->name
) continue; // just in case
7018 if (ll
->name
[0] == '{') continue; // we don't have these, but it is still reserved
7019 if (ll
->value
< 0) {
7020 fprintf(fo
, "%d %s", ll
->value
, ll
->name
);
7022 fprintf(fo
, "#%04X %s", (unsigned)ll
->value
, ll
->name
);
7026 case LBL_TYPE_VERY_SPECIAL
: fprintf(fo
, "WTF"); break;
7027 case LBL_TYPE_UNKNOWN
: fprintf(fo
, "UNKNOWN"); break;
7028 case LBL_TYPE_ASS
: fprintf(fo
, "assign"); break;
7029 case LBL_TYPE_EQU
: fprintf(fo
, "equ"); break;
7030 case LBL_TYPE_CODE
: fprintf(fo
, "code"); break;
7031 case LBL_TYPE_STOFS
: fprintf(fo
, "stofs"); break;
7032 case LBL_TYPE_DATA
: fprintf(fo
, "data"); break;
7033 default: fprintf(fo
, "WTF"); break;
7044 ///////////////////////////////////////////////////////////////////////////////
7047 static struct option longOpts
[] = {
7048 {"org", required_argument
, NULL
, 600},
7049 {"define", required_argument
, NULL
, 601},
7050 {"defzero", required_argument
, NULL
, 602},
7051 {"outdir", required_argument
, NULL
, 660},
7052 {"reffile", optional_argument
, NULL
, 669},
7053 {"outfile", required_argument
, NULL
, 665},
7054 {"castrates", optional_argument
, NULL
, 656},
7056 {"sna", 0, NULL
, 's'},
7057 {"sna128", 0, NULL
, 'S'},
7058 {"tap", 0, NULL
, 't'},
7059 {"autotap", 0, NULL
, 'T'},
7060 {"raw", 0, NULL
, 'r'},
7061 {"autodmb", 0, NULL
, 'B'},
7062 {"dmb", 0, NULL
, 'b'},
7063 {"none", 0, NULL
, 'n'},
7064 {"help", 0, NULL
, 'h'},
7065 {"hob", 0, NULL
, 'H'},
7066 {"scl", 0, NULL
, 'l'},
7067 {"autoscl", 0, NULL
, 'L'},
7068 {"monoscl", 0, NULL
, 'm'},
7069 {"autosclmono", 0, NULL
, 'M'},
7070 {"dsk", 0, NULL
, 690},
7071 {"dsk720", 0, NULL
, 691},
7072 {"adsk", 0, NULL
, 692},
7073 {"adsk720", 0, NULL
, 693},
7074 {"fixups", optional_argument
, NULL
, 'F'},
7080 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
7083 static void usage (const char *pname
) {
7085 "usage: %s [options] infile\n"
7086 "default infiles:", pname
);
7087 for (int f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
7090 " -s --sna write 48K .SNA file with autostart\n"
7091 " -S --sna128 write 148K .SNA file with autostart\n"
7092 " -t --tap write .tap file\n"
7093 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
7094 " -r --raw write raw file(s)\n"
7095 " -b --dmb write DMB file\n"
7096 " -B --autodmb write DMB file with autostart\n"
7097 " -H --hob write HoBeta code file(s)\n"
7098 " -l --scl write SCL TR-DOS archive\n"
7099 " -L --autoscl write autostarting SCL TR-DOS archive\n"
7100 " -m --monoscl write SCL with monoloader\n"
7101 /*" -M --monosclauto write autorun SCL with monoloader\n"*/
7102 " --dsk write +3DOS 180KB disk\n"
7103 " --dsk720 write +3DOS 720KB disk\n"
7104 " --adsk write +3DOS 180KB disk with autostart\n"
7105 " --adsk720 write +3DOS 720KB disk with autostart\n"
7106 " -n --none write nothing\n"
7107 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
7108 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
7109 " text: .txt file\n"
7110 " asm: address lists with counters\n"
7111 " asmdiff: 2nd and other addresses are relative, with counters\n"
7112 " asmz: address lists, with 0 end markers\n"
7113 " -h --help this help\n"
7115 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
7116 " --org xxx set ORG\n"
7117 " --define val perform 'val EQU 1'\n"
7118 " --defzero val perform 'val EQU 0'\n"
7119 " --outdir dir output dir for resulting files (default: current)\n"
7120 " --outfile fname output file name for image formats (default: main asm file name)\n"
7121 " --castrates allow '&abc' hex numbers (and disable '&b' prefix)\n"
7126 ///////////////////////////////////////////////////////////////////////////////
7127 //#include "urforth.c"
7130 UFO_ZX_LABEL_UNDEFINED
= -1,
7131 UFO_ZX_LABEL_UNKNOWN
= 0, // referenced, but not defined yet
7132 UFO_ZX_LABEL_VAR
= 1, // a = value
7133 UFO_ZX_LABEL_EQU
= 2, // a equ value
7134 UFO_ZX_LABEL_CODE
= 3,
7135 UFO_ZX_LABEL_DATA
= 4,
7136 UFO_ZX_LABEL_STOFS
= 5, // structure offset
7139 __attribute__((noreturn
))
7140 void ufoFatalError (void) {
7141 longjmp(errJP
, 666);
7144 static void ufoZXGetU8 (uint32_t mypfa
) {
7145 uint32_t addr
= ufoPopData();
7147 ufoPushData(getByte(addr
));
7150 static void ufoZXPutU8 (uint32_t mypfa
) {
7151 uint32_t addr
= ufoPopData();
7152 uint32_t v
= ufoPopData();
7158 static void ufoZXGetU16 (uint32_t mypfa
) {
7159 uint32_t addr
= ufoPopData();
7161 ufoPushData(getWord(addr
));
7164 static void ufoZXPutU16 (uint32_t mypfa
) {
7165 uint32_t addr
= ufoPopData();
7166 uint32_t v
= ufoPopData();
7172 static void ufoZXEmitU8 (uint32_t mypfa
) {
7173 emitByte(ufoPopData()&0xffU
);
7176 static void ufoZXEmitU16 (uint32_t mypfa
) {
7177 emitWord(ufoPopData()&0xffffU
);
7180 static void ufoZXGetReserved (uint32_t mypfa
) {
7181 uint32_t addr
= ufoPopData();
7182 ufoPushData(memresv
[addr
&0xffffU
]);
7185 static void ufoZXSetReserved (uint32_t mypfa
) {
7186 uint32_t addr
= ufoPopData();
7187 uint32_t resvflag
= ufoPopData();
7188 memresv
[addr
&0xffffU
] = resvflag
;
7191 static void ufoZXGetPass (uint32_t mypfa
) {
7195 static void ufoZXGetOrg (uint32_t mypfa
) {
7199 static void ufoZXSetOrg (uint32_t mypfa
) {
7200 uint32_t addr
= ufoPopData();
7201 if (addr
> 0xffff) ufoFatal("invalid ORG address: %u", addr
);
7202 pc
= disp
= (uint16_t)addr
;
7204 wasOrg
= 1; // so next `ORG` will not reset it
7205 ent
= (uint16_t)addr
;
7209 static void ufoZXGetDisp (uint32_t mypfa
) {
7213 static void ufoZXSetDisp (uint32_t mypfa
) {
7214 uint32_t addr
= ufoPopData();
7215 if (addr
> 0xffff) ufoFatal("invalid DISP address: %u", addr
);
7216 disp
= (uint16_t)addr
;
7219 static void ufoZXGetEnt (uint32_t mypfa
) {
7220 ufoPushData(wasOrg
? (uint32_t)ent
: ~0u);
7223 static void ufoZXSetEnt (uint32_t mypfa
) {
7224 uint32_t addr
= ufoPopData();
7225 if (addr
> 0xffff) ufoFatal("invalid ENT address: %u", addr
);
7226 wasOrg
= 1; // so next `ORG` will not reset it
7227 ent
= (uint16_t)addr
;
7230 static int ufoZXLType2UFO (int type
) {
7232 case LBL_TYPE_VERY_SPECIAL
: return UFO_ZX_LABEL_UNDEFINED
;
7233 case LBL_TYPE_UNKNOWN
: return UFO_ZX_LABEL_UNKNOWN
;
7234 case LBL_TYPE_ASS
: return UFO_ZX_LABEL_VAR
;
7235 case LBL_TYPE_EQU
: return UFO_ZX_LABEL_EQU
;
7236 case LBL_TYPE_CODE
: return UFO_ZX_LABEL_CODE
;
7237 case LBL_TYPE_DATA
: return UFO_ZX_LABEL_DATA
;
7238 case LBL_TYPE_STOFS
: return UFO_ZX_LABEL_STOFS
;
7240 ufoFatal("WTF?! unknown label type %d", type
);
7243 static char ufoStrLit
[256];
7245 static void ufoZXPopStrLit (void) {
7246 int32_t count
= (int32_t)ufoPopData();
7247 uint32_t addr
= ufoPopData();
7248 if (count
< 0) ufoFatal("invalid string");
7250 while (pos
< (uint32_t)sizeof(ufoStrLit
)) {
7251 uint8_t ch
= ufoPeekByte(addr
);
7252 ufoStrLit
[pos
] = (char)ch
;
7254 pos
+= 1u; addr
+= 1u;
7256 if (pos
== (uint32_t)sizeof(ufoStrLit
)) ufoFatal("string too long");
7259 static void ufoZXHasLabel (uint32_t mypfa
) {
7261 if (ufoStrLit
[0] == 0) {
7264 UrLabelInfo
*lbl
= findAddLabel(ufoStrLit
);
7265 if (lbl
== NULL
|| ufoZXLType2UFO(lbl
->type
) > UFO_ZX_LABEL_UNKNOWN
) {
7273 static void ufoZXGetLabelType (uint32_t mypfa
) {
7275 if (ufoStrLit
[0] == 0) {
7276 ufoPushData(UFO_ZX_LABEL_UNDEFINED
);
7278 UrLabelInfo
*lbl
= findAddLabel(ufoStrLit
);
7279 if (lbl
== NULL
) ufoPushData(UFO_ZX_LABEL_UNDEFINED
);
7280 else ufoPushData(ufoZXLType2UFO(lbl
->type
));
7284 static void ufoZXGetLabelValue (uint32_t mypfa
) {
7286 if (ufoStrLit
[0] == 0) ufoPushData(0);
7288 UrLabelInfo
*lbl
= findAddLabel(ufoStrLit
);
7289 ufoPushData(lbl
? lbl
->value
: 0);
7293 // this also creates labels
7294 // ( value addr count -- )
7295 static void urw_set_typed_label (int type
) {
7297 if (ufoStrLit
[0] == 0) ufoFatal("cannot set empty label");
7298 int32_t value
= (int32_t)ufoPopData();
7300 // convert label type
7302 case UFO_ZX_LABEL_UNDEFINED
: ufoFatal("cannot set label with undefined type");
7303 case UFO_ZX_LABEL_UNKNOWN
: ufoFatal("cannot set label with unknown type");
7304 case UFO_ZX_LABEL_VAR
: type
= LBL_TYPE_ASS
; break;
7305 case UFO_ZX_LABEL_EQU
: type
= LBL_TYPE_EQU
; break;
7306 case UFO_ZX_LABEL_CODE
: type
= LBL_TYPE_CODE
; break;
7307 case UFO_ZX_LABEL_DATA
: type
= LBL_TYPE_DATA
; break;
7308 case UFO_ZX_LABEL_STOFS
: type
= LBL_TYPE_STOFS
; break;
7309 default: ufoFatal("invalid label type %d", type
);
7312 UrLabelInfo
*lbl
= findAddLabel(ufoStrLit
);
7314 if (lbl
->type
!= LBL_TYPE_UNKNOWN
&& lbl
->type
!= type
) {
7315 ufoFatal("invalid label '%s' type", ufoStrLit
);
7317 if (type
!= LBL_TYPE_ASS
) {
7318 if (lbl
->type
>= 0 && lbl
->value
!= value
) {
7319 ufoFatal("invalid label '%s' value", ufoStrLit
);
7324 if (lbl
->type
== LBL_TYPE_UNKNOWN
) lbl
->type
= type
;
7327 static void ufoZXSetLabelVar (uint32_t mypfa
) { urw_set_typed_label(UFO_ZX_LABEL_VAR
); }
7328 static void ufoZXSetLabelEqu (uint32_t mypfa
) { urw_set_typed_label(UFO_ZX_LABEL_EQU
); }
7329 static void ufoZXSetLabelCode (uint32_t mypfa
) { urw_set_typed_label(UFO_ZX_LABEL_CODE
); }
7330 static void ufoZXSetLabelData (uint32_t mypfa
) { urw_set_typed_label(UFO_ZX_LABEL_DATA
); }
7331 static void ufoZXSetLabelStOfs (uint32_t mypfa
) { urw_set_typed_label(UFO_ZX_LABEL_STOFS
); }
7333 #define UFO_ZX_MAX_ITERS (16)
7335 typedef uint32_t UFOZXLabelIterator
;
7338 UFOZXLabelIterator id
;
7342 static UZXLabelIter ufoZXLblIters
[UFO_ZX_MAX_ITERS
];
7343 static uint32_t ufoZXLblIdLast
;
7345 static int UFOZXFindIter (UFOZXLabelIterator it
) {
7347 while (f
!= UFO_ZX_MAX_ITERS
&& ufoZXLblIters
[f
].id
!= it
) f
+= 1;
7348 if (f
== UFO_ZX_MAX_ITERS
) f
= -1;
7352 static UrLabelInfo
*UFOZXNormLabel (UrLabelInfo
*c
) {
7353 while (c
!= NULL
&& (c
->type
== LBL_TYPE_VERY_SPECIAL
|| c
->type
== LBL_TYPE_UNKNOWN
)) {
7359 static void UFOZXLabelRemoved (UrLabelInfo
*lbl
) {
7360 ur_assert(lbl
!= NULL
);
7361 UrLabelInfo
*c
= UFOZXNormLabel(lbl
->next
);
7362 for (int f
= 0; f
< UFO_ZX_MAX_ITERS
; f
+= 1) {
7363 if (ufoZXLblIters
[f
].lbl
== lbl
) {
7364 ufoZXLblIters
[f
].lbl
= c
;
7369 static UFOZXLabelIterator
ufoZXNewLabelIter (void) {
7370 UFOZXLabelIterator res
= 0;
7371 UrLabelInfo
*c
= UFOZXNormLabel(labels
);
7374 while (res
== 0 && f
!= UFO_ZX_MAX_ITERS
) {
7375 if (ufoZXLblIters
[f
].id
== 0) {
7376 ufoZXLblIdLast
+= 1;
7377 if (ufoZXLblIdLast
== 0) ufoZXLblIdLast
= 1;
7378 ufoZXLblIters
[f
].id
= ufoZXLblIdLast
;
7379 ufoZXLblIters
[f
].lbl
= c
;
7380 res
= ufoZXLblIdLast
;
7387 static int ufoZXLabelIterNext (UFOZXLabelIterator it
) {
7389 int f
= UFOZXFindIter(it
);
7391 UrLabelInfo
*c
= ufoZXLblIters
[f
].lbl
;
7392 if (c
!= NULL
) c
= UFOZXNormLabel(c
->next
);
7394 ufoZXLblIters
[f
].lbl
= c
;
7397 ufoZXLblIters
[f
].id
= 0;
7398 ufoZXLblIters
[f
].lbl
= NULL
;
7404 static void ufoZXLabelIterClose (UFOZXLabelIterator it
) {
7405 int f
= UFOZXFindIter(it
);
7407 ufoZXLblIters
[f
].id
= 0;
7408 ufoZXLblIters
[f
].lbl
= NULL
;
7412 static const char *ufoZXLabelIterGetName (UFOZXLabelIterator it
) {
7413 int f
= UFOZXFindIter(it
);
7415 UrLabelInfo
*c
= ufoZXLblIters
[f
].lbl
;
7416 if (c
!= NULL
) return c
->name
;
7421 static int ufoZXIterGetValue (UFOZXLabelIterator it
) {
7422 int f
= UFOZXFindIter(it
);
7424 UrLabelInfo
*c
= ufoZXLblIters
[f
].lbl
;
7425 if (c
!= NULL
) return c
->value
;
7430 static int ufoZXIterGetType (UFOZXLabelIterator it
) {
7431 int f
= UFOZXFindIter(it
);
7433 UrLabelInfo
*c
= ufoZXLblIters
[f
].lbl
;
7434 if (c
!= NULL
) ufoZXLType2UFO(c
->type
);
7436 return UFO_ZX_LABEL_UNDEFINED
;
7439 // UR-NEW-LABEL-ITER
7440 // ( -- iterid | 0 )
7441 static void ufoZXNewLabelIterWord (uint32_t mypfa
) {
7442 ufoPushData(ufoZXNewLabelIter());
7445 // UR-CLOSE-LABEL-ITER
7447 static void ufoZXCloseLabelIterWord (uint32_t mypfa
) {
7448 uint32_t id
= ufoPopData();
7449 ufoZXLabelIterClose(id
);
7452 // UR-LABEL-ITER-NEXT
7453 // ( iterid -- not-done? )
7454 static void ufoZXNextLabelIterWord (uint32_t mypfa
) {
7455 uint32_t id
= ufoPopData();
7456 ufoPushBoolData(ufoZXLabelIterNext(id
));
7459 // UR-LABEL-ITER-GET-NAME
7460 // ( iterid -- addr count )
7462 static void ufoZXGetLabelNameIterWord (uint32_t mypfa
) {
7463 uint32_t id
= ufoPopData();
7464 const char *name
= ufoZXLabelIterGetName(id
);
7465 if (name
== NULL
) name
= "";
7467 uint32_t pad
= ufoGetPad() + 4u;
7468 while (count
!= 1024 && *name
!= 0) {
7469 ufoPokeByte(pad
+ count
, ((const unsigned char *)name
)[count
]);
7470 count
+= 1u; name
+= 1u;
7472 if (count
== 1024) ufoFatal("label name too long");
7473 ufoPokeByte(pad
+ count
, 0); // just in case
7474 ufoPushData(pad
); ufoPushData(count
);
7477 // UR-LABEL-ITER-GET-VALUE
7478 // ( iterid -- value )
7479 static void ufoZXGetLabelValueIterWord (uint32_t mypfa
) {
7480 uint32_t id
= ufoPopData();
7481 ufoPushData((uint32_t)ufoZXIterGetValue(id
));
7484 // UR-LABEL-ITER-GET-TYPE
7485 // ( iterid -- type )
7486 static void ufoZXGetLabelTypeIterWord (uint32_t mypfa
) {
7487 uint32_t id
= ufoPopData();
7488 ufoPushData((uint32_t)ufoZXIterGetType(id
));
7492 static int ufoGetSrcLine (void *buf
, size_t bufsize
, const char **fname
, int *lnum
) {
7493 char *dest
= (char *)buf
;
7494 if (ufoIsInMacroMode()) {
7495 //fprintf(stderr, "!<%s>\n", currLine);
7496 if (strlen(currLine
) >= bufsize
) ufoFatal("user line too long");
7497 strcpy(buf
, currLine
);
7498 if (currSrcLine
!= NULL
) {
7499 *lnum
= currSrcLine
->lineNo
;
7500 *fname
= currSrcLine
->fname
;
7506 SourceLine
*sl
= nextUFOSrcLine();
7507 //fprintf(stderr, "!NL:%p:<%s>\n", sl, (sl && sl->line ? sl->line : "fuck!"));
7508 if (sl
== NULL
) return 0;
7509 if (sl
->line
== NULL
) {
7511 } else if (strlen(sl
->line
) >= bufsize
) {
7512 ufoFatal("user line too long");
7514 memcpy(buf
, sl
->line
, strlen(sl
->line
) + 1);
7522 char *ufoCreateIncludeName (const char *fname
, int assystem
, const char *lastIncPath
) {
7523 if (lastIncPath
== NULL
) {
7525 lastIncPath
= ufoIncludeDir
;
7530 char *res
= createIncludeName(fname
, (assystem
? -666 : -669), NULL
, lastIncPath
);
7533 if (res
== NULL
|| res
[0] == 0 || stat(res
, &st
) != 0) return res
;
7534 if (S_ISDIR(st
.st_mode
)) {
7535 tmp
= strprintf("%s/%s", res
, "00-main-loader.f");
7540 //return createIncludeName(fname, (assystem ? -666 : -669), NULL, lastIncPath);
7545 static void ufoZXEndForth (uint32_t mypfa
) {
7546 if (ufoIsInMacroMode()) ufoFatal("$END_FORTH in non-native mode");
7547 if (ufoIsCompiling()) ufoFatal("$END_FORTH: still compiling something");
7551 static void urw_declare_typed_label (int type
) {
7552 if (ufoIsCompiling()) ufoFatal("execution mode expected");
7555 if (ufoStrLit
[0] == 0) ufoFatal("label name expected");
7556 UrLabelInfo
*lbl
= findAddLabel(ufoStrLit
);
7557 if (lbl
->type
!= LBL_TYPE_UNKNOWN
&& lbl
->type
!= type
) {
7558 ufoFatal("invalid label '%s' type", ufoStrLit
);
7560 if (type
!= LBL_TYPE_ASS
) {
7561 if (lbl
->type
>= 0 && lbl
->value
!= pc
) {
7562 ufoFatal("invalid label '%s' value", ufoStrLit
);
7567 if (lbl
->type
== LBL_TYPE_UNKNOWN
) lbl
->type
= type
;
7570 // $LABEL-DATA: name
7571 static void ufoZXDefLabelData (uint32_t mypfa
) { urw_declare_typed_label(LBL_TYPE_DATA
); }
7572 // $LABEL-CODE: name
7573 static void ufoZXDefLabelCode (uint32_t mypfa
) { urw_declare_typed_label(LBL_TYPE_CODE
); }
7576 //==========================================================================
7580 //==========================================================================
7581 static void initUrForth (void) {
7582 for (int f
= 0; f
< UFO_ZX_MAX_ITERS
; f
+= 1) {
7583 ufoZXLblIters
[f
].id
= 0;
7584 ufoZXLblIters
[f
].lbl
= NULL
;
7588 ufoMacroVocId
= ufoCreateVoc("URASM-MACROS", 0, UFW_FLAG_PROTECTED
);
7590 ufoVocSetOnlyDefs(ufoCreateVoc("URASM", 0, UFW_FLAG_PROTECTED
));
7592 // UrAsm label types
7593 ufoRegisterConstant("LBL-TYPE-UNKNOWN", UFO_ZX_LABEL_UNKNOWN
, UFW_FLAG_PROTECTED
);
7594 ufoRegisterConstant("LBL-TYPE-VAR", UFO_ZX_LABEL_VAR
, UFW_FLAG_PROTECTED
);
7595 ufoRegisterConstant("LBL-TYPE-EQU", UFO_ZX_LABEL_EQU
, UFW_FLAG_PROTECTED
);
7596 ufoRegisterConstant("LBL-TYPE-CODE", UFO_ZX_LABEL_CODE
, UFW_FLAG_PROTECTED
);
7597 ufoRegisterConstant("LBL-TYPE-STOFS", UFO_ZX_LABEL_STOFS
, UFW_FLAG_PROTECTED
);
7598 ufoRegisterConstant("LBL-TYPE-DATA", UFO_ZX_LABEL_DATA
, UFW_FLAG_PROTECTED
);
7600 // ZX-C, ( val8 -- )
7601 // puts byte to zx dictionary
7602 ufoRegisterWord("C,", &ufoZXEmitU8
, UFW_FLAG_PROTECTED
);
7605 // puts word to zx dictionary
7606 ufoRegisterWord("W,", &ufoZXEmitU16
, UFW_FLAG_PROTECTED
);
7608 // ZX-C@ ( addr -- value8 )
7609 ufoRegisterWord("C@", &ufoZXGetU8
, UFW_FLAG_PROTECTED
);
7611 // ZX-C! ( val8 addr -- )
7612 ufoRegisterWord("C!", &ufoZXPutU8
, UFW_FLAG_PROTECTED
);
7614 // ZX-W@ ( addr -- value16 )
7615 ufoRegisterWord("W@", &ufoZXGetU16
, UFW_FLAG_PROTECTED
);
7617 // ZX-W! ( val16 addr -- )
7618 ufoRegisterWord("W!", &ufoZXPutU16
, UFW_FLAG_PROTECTED
);
7620 // ZX-RESERVED? ( addr -- bool )
7621 ufoRegisterWord("RESERVED?", &ufoZXGetReserved
, UFW_FLAG_PROTECTED
);
7623 // ZX-RESERVED! ( bool addr -- )
7624 ufoRegisterWord("RESERVED!", &ufoZXSetReserved
, UFW_FLAG_PROTECTED
);
7626 ufoRegisterWord("HAS-LABEL?", &ufoZXHasLabel
, UFW_FLAG_PROTECTED
);
7627 ufoRegisterWord("LABEL-TYPE?", &ufoZXGetLabelType
, UFW_FLAG_PROTECTED
);
7629 ufoRegisterWord("GET-LABEL", &ufoZXGetLabelValue
, UFW_FLAG_PROTECTED
);
7631 ufoRegisterWord("SET-LABEL-VAR", &ufoZXSetLabelVar
, UFW_FLAG_PROTECTED
);
7632 ufoRegisterWord("SET-LABEL-EQU", &ufoZXSetLabelEqu
, UFW_FLAG_PROTECTED
);
7633 ufoRegisterWord("SET-LABEL-CODE", &ufoZXSetLabelCode
, UFW_FLAG_PROTECTED
);
7634 ufoRegisterWord("SET-LABEL-STOFS", &ufoZXSetLabelStOfs
, UFW_FLAG_PROTECTED
);
7635 ufoRegisterWord("SET-LABEL-DATA", &ufoZXSetLabelData
, UFW_FLAG_PROTECTED
);
7637 ufoRegisterWord("NEW-LABEL-ITER", &ufoZXNewLabelIterWord
, UFW_FLAG_PROTECTED
);
7638 ufoRegisterWord("CLOSE-LABEL-ITER", &ufoZXCloseLabelIterWord
, UFW_FLAG_PROTECTED
);
7639 ufoRegisterWord("LABEL-ITER-NEXT", &ufoZXNextLabelIterWord
, UFW_FLAG_PROTECTED
);
7640 ufoRegisterWord("LABEL-ITER-GET-NAME", &ufoZXGetLabelNameIterWord
, UFW_FLAG_PROTECTED
);
7641 ufoRegisterWord("LABEL-ITER-GET-VALUE", &ufoZXGetLabelValueIterWord
, UFW_FLAG_PROTECTED
);
7642 ufoRegisterWord("LABEL-ITER-GET-TYPE", &ufoZXGetLabelTypeIterWord
, UFW_FLAG_PROTECTED
);
7644 ufoRegisterWord("PASS@", &ufoZXGetPass
, UFW_FLAG_PROTECTED
);
7646 //ufoRegisterWord("LOAD-DATA-FILE", ZX_LOAD_DATA_FILE);
7648 ufoRegisterWord("ORG@", &ufoZXGetOrg
, UFW_FLAG_PROTECTED
);
7649 ufoRegisterWord("DISP@", &ufoZXGetDisp
, UFW_FLAG_PROTECTED
);
7650 ufoRegisterWord("ENT@", &ufoZXGetEnt
, UFW_FLAG_PROTECTED
);
7651 ufoRegisterWord("ORG!", &ufoZXSetOrg
, UFW_FLAG_PROTECTED
);
7652 ufoRegisterWord("DISP!", &ufoZXSetEnt
, UFW_FLAG_PROTECTED
);
7653 ufoRegisterWord("ENT!", &ufoZXSetDisp
, UFW_FLAG_PROTECTED
);
7655 ufoVocSetOnlyDefs(ufoGetForthVocId());
7657 ufoRegisterWord("$LABEL-CODE:", &ufoZXDefLabelData
, UFW_FLAG_PROTECTED
| UFW_FLAG_IMMEDIATE
);
7658 ufoRegisterWord("$LABEL-DATA:", &ufoZXDefLabelCode
, UFW_FLAG_PROTECTED
| UFW_FLAG_IMMEDIATE
);
7660 ufoRegisterWord("$END-FORTH", &ufoZXEndForth
, UFW_FLAG_PROTECTED
| UFW_FLAG_IMMEDIATE
);
7661 ufoRegisterWord("$END_FORTH", &ufoZXEndForth
, UFW_FLAG_PROTECTED
| UFW_FLAG_IMMEDIATE
);
7665 ///////////////////////////////////////////////////////////////////////////////
7668 int main (int argc
, char *argv
[]) {
7670 const char *pname
= argv
[0];
7671 char *inFile
= NULL
;
7672 char **defines
= NULL
, **values
= NULL
;
7676 ufoSetUserPostInit(&initUrForth
);
7680 initUFEInclideDir();
7682 urasm_getbyte
= getByte
;
7683 urasm_putbyte
= putByte
;
7684 urasm_label_by_name
= findLabelCB
;
7685 urasm_getval
= getValueCB
;
7686 urasm_expand
= expandCB
;
7687 urasm_fixup_operand
= fixupOperandCB
;
7689 //strcpy(tapeLoaderName, "cargador ");
7690 tapeLoaderName
[0] = 0;
7692 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
7693 while ((c
= getopt_long(argc
, argv
, "sStTrBbnhHFLlMm", longOpts
, NULL
)) >= 0) {
7696 case 'S': /*optRunSNA = 1;*/ optSNA48
= 0; optWriteType
= 's'; optWTChanged
= 1; break;
7697 case 's': /*optRunSNA = 0;*/ optSNA48
= 1; optWriteType
= 's'; optWTChanged
= 1; break;
7698 case 'T': optRunTape
= 1; optWriteType
= 't'; optWTChanged
= 1; break;
7699 case 't': optRunTape
= 0; optWriteType
= 't'; optWTChanged
= 1; break;
7700 case 'L': optRunSCL
= 1; optWriteType
= 'S'; optWTChanged
= 1; break; /* with boot */
7701 case 'l': optRunSCL
= -1; optWriteType
= 'S'; optWTChanged
= 1; break; /* no boot */
7702 case 'm': optRunSCL
= -1; optWriteType
= 'M'; optWTChanged
= 1; break; /* no boot */
7703 case 'M': optRunSCL
= 1; optWriteType
= 'M'; optWTChanged
= 1; break; /* with boot */
7704 case 'r': case 'n': optWriteType
= c
; optWTChanged
= 1; break;
7705 case 'b': optRunTape
= 0; optRunDMB
= 0; optWriteType
= 'd'; optWTChanged
= 1; break;
7706 case 'B': optRunTape
= 0; optRunDMB
= 1; optWriteType
= 'd'; optWTChanged
= 1; break;
7707 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
7708 case 'H': optWriteType
= 'H'; optWTChanged
= 1; break;
7711 if (optarg
!= NULL
) {
7712 if (strcmp(optarg
, "asm") == 0 || strcmp(optarg
, "zas") == 0) {
7714 } else if (strcmp(optarg
, "asmdiff") == 0 || strcmp(optarg
, "zasdiff") == 0) {
7716 } else if (strcmp(optarg
, "asmz") == 0 || strcmp(optarg
, "zasz") == 0) {
7718 } else if (strcmp(optarg
, "text") == 0) {
7721 fprintf(stderr
, "FATAL: invalid fixup type: '%s'\n", optarg
);
7728 //fprintf(stderr, "ORG: %d\n", c);
7729 if (c
< 0 || c
> 65535) {
7730 fprintf(stderr
, "FATAL: invalid ORG: %d\n", c
);
7733 start_pc
= start_disp
= start_ent
= c
;
7736 //fprintf(stderr, "define: [%s]\n", optarg);
7737 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
7738 values
= realloc(values
, sizeof(char *)*(defcount
+1));
7739 defines
[defcount
] = strdup(optarg
);
7740 values
[defcount
] = strdup("1");
7743 case 602: // defzero
7744 //fprintf(stderr, "defzero: [%s]\n", optarg);
7745 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
7746 values
= realloc(values
, sizeof(char *)*(defcount
+1));
7747 defines
[defcount
] = strdup(optarg
);
7748 values
[defcount
] = strdup("0");
7752 if (optOutputDir
!= NULL
) free(optOutputDir
);
7753 optOutputDir
= strdup(optarg
);
7756 if (optOutputFile
!= NULL
) free(optOutputFile
);
7757 optOutputFile
= strdup(optarg
);
7759 case 669: // reffile
7760 if (refFileName
) free(refFileName
);
7761 refFileName
= (optarg
? strdup(optarg
) : NULL
);
7764 case 690: case 692: // +3DOS 180K disk
7765 optRunDSK
= (c
== 692);
7766 optDskType
= P3DSK_PCW_SS
;
7770 case 691: case 693: // +3DOS 720K disk
7771 optRunDSK
= (c
== 693);
7772 optDskType
= P3DSK_PCW_DS
;
7776 case 656: // hex castrates
7777 if (optarg
!= NULL
) {
7778 if (strcmp(optarg
, "tan") == 0 || strcmp(optarg
, "yes") == 0) {
7779 urasm_allow_hex_castrates
= 1;
7780 } else if (strcmp(optarg
, "ona") == 0 || strcmp(optarg
, "no") == 0) {
7781 urasm_allow_hex_castrates
= 0;
7783 fprintf(stderr
, "FATAL: i don't know what '%s' means.\n", optarg
);
7787 urasm_allow_hex_castrates
= 1;
7793 if (optind
>= argc
) {
7794 // try to find default input file
7795 for (int f
= 0; defInFiles
[f
]; ++f
) {
7796 if (!access(defInFiles
[f
], R_OK
)) {
7797 inFile
= strdup(defInFiles
[f
]);
7802 inFile
= strdup(argv
[optind
]);
7805 if (!inFile
|| !inFile
[0]) {
7807 fprintf(stderr
, "ERROR: no input file!\n");
7811 if (optOutputDir
== NULL
) optOutputDir
= strdup(".");
7813 for (char *ss
= optOutputDir
; *ss
; ++ss
) if (*ss
== '\\') *ss
= '/';
7816 char *ee
= strrchr(optOutputDir
, '/');
7817 if (ee
&& ee
!= optOutputDir
) *ee
= 0;
7819 if (!optOutputDir
[0]) { free(optOutputDir
); optOutputDir
= strdup("."); }
7821 registerInstructions();
7822 registerFunctions();
7824 res
= asmTextLoad(inFile
, 0);
7826 // init UrForth here, because why not?
7828 ufoFileReadLine
= &ufoGetSrcLine
;
7830 for (int f
= 0; f
< defcount
; ++f
) {
7831 if (labelDoEQU(defines
[f
], values
[f
]) != 0) {
7832 fprintf(stderr
, "FATAL: can't define label: '%s'\n", defines
[f
]);
7835 //fprintf(stderr, "****SET**** <%s> to <%s>\n", defines[f], values[f]);
7838 for (pass
= 0; pass
<= 1; ++pass
) {
7840 printf("pass %d\n", pass
);
7841 //fprintf(stderr, "===========pass %d===========\n", pass);
7842 setCurSrcLine(asmText
);
7843 if (setjmp(errJP
)) { res
= 1; break; }
7844 while (currSrcLine
) processCurrentLine();
7845 if (postPass()) { res
= 1; break; }
7850 char *oc
= strdup(inFile
);
7851 char *pd
= strrchr(oc
, '.');
7853 if (pd
&& !strchr(oc
, '/') && !strchr(oc
, '\\')) *pd
= '\0';
7855 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
7857 switch (optWriteType
) {
7858 case 's': saveSna(oc
, optSNA48
); break;
7859 case 't': saveTap(oc
); break;
7860 case 'r': saveRaw(oc
); break;
7861 case 'd': saveDMB(oc
); break;
7862 case 'H': saveHob(oc
); break;
7863 case 'S': saveSCL(oc
); break;
7864 case 'M': saveSCLMono(oc
); break;
7865 case '3': saveDSK(oc
); break;
7868 if (optWriteFixups
) writeFixups();
7870 /* build ref file name */
7871 if (!refFileName
|| !refFileName
[0]) {
7872 if (refFileName
) free(refFileName
);
7873 refFileName
= malloc(strlen(inFile
)+128);
7874 strcpy(refFileName
, inFile
);
7875 char *ext
= strrchr(refFileName
, '.');
7877 strcat(refFileName
, ".ref");
7881 for (char *ts
= refFileName
; *ts
; ++ts
) {
7882 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
7885 char *slash
= strrchr(refFileName
, '/');
7887 if (!slash
|| slash
< ext
) {
7888 strcpy(ext
, ".ref");
7890 strcat(refFileName
, ".ref");
7894 writeLabelsFile(refFileName
);
7895 printf("refs written to '%s'\n", refFileName
);
7900 fprintf(stderr
, "ERROR: loading error!\n");
7905 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
7916 if (inFile
) free(inFile
);
7917 if (sysIncludeDir
) free(sysIncludeDir
);
7918 if (ufoIncludeDir
) free(ufoIncludeDir
);
7919 if (lastIncludePath
) free(lastIncludePath
);
7920 if (lastSysIncludePath
) free(lastSysIncludePath
);
7921 for (int f
= defcount
-1; f
>= 0; --f
) {
7925 if (defines
!= NULL
) { free(values
); free(defines
); }
7926 if (optOutputFile
) free(optOutputFile
);
7927 if (optOutputDir
) free(optOutputDir
);
7929 return (res
? 1 : 0);