2 // coded by Ketmar // Invisible Vector
19 #include <sys/types.h>
27 #include "liburasm/liburasm.h"
37 #define MAYBE_UNUSED __attribute__((unused))
39 #define lambda(return_type, body_and_args) ({ \
40 return_type __fn__ body_and_args \
45 ////////////////////////////////////////////////////////////////////////////////
46 static inline int isSpace (char ch
) { return (ch
&& ((unsigned)(ch
&0xff) <= 32 || ch
== 127)); }
47 static inline int isAlpha (char ch
) { return ((ch
>= 'A' && ch
<= 'Z') || (ch
>= 'a' && ch
<= 'z')); }
48 static inline int isDigit (char ch
) { return (ch
>= '0' && ch
<= '9'); }
49 static inline int isHexDigit (char ch
) { return ((ch
>= '0' && ch
<= '9') || (ch
>= 'A' && ch
<= 'F') || (ch
>= 'a' && ch
<= 'f')); }
50 static inline int isAlphaDigit (char ch
) { return (isAlpha(ch
) || isDigit(ch
)); }
52 static inline char toUpper (char ch
) { return (ch
>= 'a' && ch
<= 'z' ? ch
-'a'+'A' : ch
); }
53 static inline char toLower (char ch
) { return (ch
>= 'A' && ch
<= 'Z' ? ch
-'A'+'a' : ch
); }
55 static int digitInBase (char ch
, int base
) {
56 if (ch
< '0') return -1;
58 if (ch
>= '0'+base
) return -1;
62 if (ch
<= '9') return ch
-'0';
63 if (ch
< 'A' || ch
> 'A'+base
-10) return -1;
65 return (ch
< base
? ch
: -1);
70 static const char *strcasestr (const char *s
, const char *pat
) {
71 if (!s
|| !pat
|| !pat
[0] || !s
[0]) return NULL
;
74 for (size_t f
= 0; pat
[f
]; ++f
) {
75 if (toLower(s
[f
]) != toLower(pat
[f
])) { ok
= 0; break; }
85 ////////////////////////////////////////////////////////////////////////////////
86 static char *strprintfVA (const char *fmt
, va_list vaorig
) {
91 if (buf
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
97 olen
= vsnprintf(buf
, len
, fmt
, va
);
99 if (olen
>= 0 && olen
< len
) return buf
;
100 if (olen
< 0) olen
= len
*2-1;
101 nb
= realloc(buf
, olen
+1);
102 if (nb
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
109 static __attribute((format(printf
,1,2))) char *strprintf (const char *fmt
, ...) {
114 buf
= strprintfVA(fmt
, va
);
120 ///////////////////////////////////////////////////////////////////////////////
125 AMODE_FCODE
= 1, // forth assembler code word
126 AMODE_FWORD
= 2, // forth threaded code word
129 static int asmMode
= 0;
131 static char *sysIncludeDir
= NULL
;
132 static char *refFileName
= NULL
;
133 static char *lastIncludePath
= NULL
;
134 static char *lastSysIncludePath
= NULL
;
136 #define MAX_LINE_SIZE 16384
137 static char currLine
[MAX_LINE_SIZE
]; /* current processing line; can be freely modified */
140 ///////////////////////////////////////////////////////////////////////////////
143 static void initInclideDir (void) {
144 const char *id
= getenv("URASM_INCLUDE_DIR");
146 sysIncludeDir
= strdup(id
);
149 memset(myDir
, 0, sizeof(myDir
));
151 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
154 char *p
= (char *)strrchr(myDir
, '/');
155 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
158 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
159 char *p
= strrchr(myDir
, '\\');
160 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
162 strcat(myDir
, "/libs");
163 sysIncludeDir
= strdup(myDir
);
165 while (sysIncludeDir
[0] && sysIncludeDir
[strlen(sysIncludeDir
)-1] == '/') sysIncludeDir
[strlen(sysIncludeDir
)-1] = '\0';
166 if (!sysIncludeDir
[0]) strcpy(sysIncludeDir
, ".");
170 ///////////////////////////////////////////////////////////////////////////////
173 /* trim trailing spaces and comments; normalize colons */
174 static void normalizeForthStr (char *s
) {
176 /* now skip all shit */
178 const char ch
= *p
++;
180 if (ch
== ';' && p
[0] == ';') { p
[-1] = 0; break; }
182 /* done; trim trailing spaces and colons */
183 size_t slen
= strlen(s
);
184 while (slen
> 0 && isSpace(s
[slen
-1])) --slen
;
189 /* trim trailing spaces and comments; normalize colons */
190 static void normalizeStr (char *s
) {
191 if (asmMode
== AMODE_FWORD
) return normalizeForthStr(s
);
192 if (strcasestr(s
, "$FORTH")) return normalizeForthStr(s
);
194 /* now skip all shit */
196 const char ch
= *p
++;
197 /* check for "af'" */
198 if (ch
== '\'' && p
-s
>= 2 && toLower(p
[-2] == 'a') && toLower(p
[-1] == 'f')) continue;
200 if (ch
== ';') { p
[-1] = 0; break; }
202 if (ch
== '"' || ch
== '\'') {
205 const char c1
= *p
++;
206 if (c1
== qch
) break;
207 if (c1
== '\\' && *p
) ++p
;
211 /* reduce and normalise colons */
213 --p
; /* back to colon */
214 /* remove spaces before colon */
216 while (t
!= s
&& isSpace(t
[-1])) --t
;
217 if (t
!= p
) memmove(t
, p
, strlen(p
)+1);
219 if (p
[0] != ':') abort(); // assert
220 ++p
; /* skip colon */
221 /* remove following spaces and colons */
223 while (*t
== ':' || isSpace(*t
)) ++t
;
224 if (t
!= p
) memmove(p
, t
, strlen(t
)+1);
228 /* done; trim trailing spaces and colons */
229 size_t slen
= strlen(s
);
230 while (slen
> 0 && (isSpace(s
[slen
-1]) || s
[slen
-1] == ':')) --slen
;
235 /* check if string starts with the given command (case-insensitive) */
236 /* returns NULL or pointer to args */
237 /* skips spaces after command if any */
238 static char *strIsCommand (const char *command
, char *str
) {
239 for (int cnt
= 1; cnt
> 0; --cnt
) {
240 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
241 for (; *command
&& *str
; ++command
, ++str
) {
242 if (toUpper(*command
) != toUpper(*str
)) return NULL
; // alas
244 if (*command
) return NULL
; // alas
245 if (*str
&& isAlphaDigit(*str
)) return NULL
; // alas
246 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
247 if (*str
&& *str
== ':') break; // try again if we have a colon
254 /* parse string literal */
255 /* don't free() result */
256 /* skips trailing spaces */
257 static char *parseStr (char **str
, char endQ
, int *lenp
) {
258 static char buf
[MAX_LINE_SIZE
];
259 int len
= 0, n
, f
, base
;
261 memset(buf
, 0, sizeof(buf
));
267 case 'a': buf
[len
++] = '\a'; break;
268 case 'b': buf
[len
++] = '\b'; break;
269 case 'e': buf
[len
++] = '\x1b'; break;
270 case 'f': buf
[len
++] = '\f'; break;
271 case 'n': buf
[len
++] = '\n'; break;
272 case 'r': buf
[len
++] = '\r'; break;
273 case 't': buf
[len
++] = '\t'; break;
274 case 'v': buf
[len
++] = '\v'; break;
275 case 'z': buf
[len
++] = '\0'; break;
276 case 'x': case 'X': // hex
279 donum
: for (n
= 0; f
> 0; --f
) {
280 char ch
= digitInBase(*a
++, base
);
282 if (ch
< 0) { --a
; break; }
287 --a
; // return to the last digit, 'for' will skip it
292 case '1' ... '9': // decimal
295 default: buf
[len
++] = a
[0]; break; // others
298 if (*a
== endQ
) { ++a
; break; }
302 while (*a
&& isSpace(*a
)) ++a
; // skip trailing spaces
305 if (lenp
) *lenp
= len
;
310 ///////////////////////////////////////////////////////////////////////////////
311 // source file stack, reader, etc
313 typedef struct SourceLine
{
314 struct SourceLine
*next
;
321 static SourceLine
*asmText
= NULL
;
322 static SourceLine
*asmTextLast
= NULL
;
323 static SourceLine
*curSrcLine
= NULL
;
325 #define MAX_MACRO_ARGS (32)
327 typedef struct MacroDef
{
328 struct MacroDef
*next
;
332 char *argdefaults
[MAX_MACRO_ARGS
]; // default values
333 char *argnames
[MAX_MACRO_ARGS
]; // argument names
338 char *argvals
[MAX_MACRO_ARGS
]; // argument values
341 static MacroDef
*maclist
= NULL
;
342 static CurMacroDef
*curmacro
= NULL
; // !NULL if we are not inserting macro
343 static int curmacronum
= 0;
346 static MacroDef
*findMacro (const char *name
) {
347 for (MacroDef
*mc
= maclist
; mc
!= NULL
; mc
= mc
->next
) if (strcasecmp(name
, mc
->name
) == 0) return mc
;
352 static void asmTextClear (void) {
354 SourceLine
*l
= asmText
;
356 asmText
= asmText
->next
;
361 asmTextLast
= curSrcLine
= NULL
;
365 static int asmTextLoad (const char *fname
, int system
) {
370 if (!(fl
= fopen(fname
, "r"))) {
371 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
374 printf("loading: %s\n", fname
);
376 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
378 currLine
[sizeof(currLine
)-1] = '\0';
379 //!normalizeStr(currLine);
380 //fprintf(stderr, "*[%s]\n", curLine);
381 if (!currLine
[0]) continue; // don't store empty lines
383 if ((s
= calloc(1, sizeof(SourceLine
))) == NULL
) abort();
386 if ((s
->line
= strdup(currLine
)) == NULL
) abort();
387 if ((s
->fname
= strdup(fname
)) == NULL
) abort();
388 if (asmTextLast
) asmTextLast
->next
= s
; else asmText
= s
;
396 static char *extractFileDir (const char *s
) {
397 if (!s
|| !s
[0]) return strdup("");
401 for (const char *ts
= s
; *ts
; ++ts
) {
402 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
404 if (slash
== s
&& (s
[0] == '/' || s
[0] == '\\') && (s
[1] == '/' || s
[1] == '\\')) slash
= NULL
;
406 slash
= strrchr(s
, '/');
408 if (!slash
) return strdup("");
409 ptrdiff_t len
= (ptrdiff_t)(slash
-s
)+1;
410 char *res
= malloc(len
+1);
414 while (len
> 0 && (res
[len
-1] == '\\' || res
[len
-1] == '/')) --len
;
416 while (len
> 0 && res
[len
-1] == '/') --len
;
418 if (len
== 0) { free(res
); return strdup(""); }
424 static void loadCurSrcLine (void) {
426 strcpy(currLine
, curSrcLine
->line
);
427 /* macros will not change include dirs */
429 char **incpp
= (!curSrcLine
->system
? &lastIncludePath
: &lastSysIncludePath
);
430 if (*incpp
) free(*incpp
);
431 *incpp
= extractFileDir(curSrcLine
->fname
);
433 normalizeStr(currLine
);
439 static inline SourceLine
*setCurSrcLine (SourceLine
*l
) { curSrcLine
= l
; loadCurSrcLine(); return l
; }
441 static inline SourceLine
*nextSrcLine (void) { return (curSrcLine
!= NULL
? setCurSrcLine(curSrcLine
->next
) : NULL
); }
444 static inline int strHasPathDelim (const char *s
) {
445 if (!s
|| !s
[0]) return 0;
447 return (strchr(s
, '/') || strchr(s
, '\\') ? 1 : 0);
449 return (strchr(s
, '/') ? 1 : 0);
454 /* returns malloced string */
455 static char *createIncludeName (const char *fname
, int assystem
, const char *defaultmain
) {
456 if (!fname
|| !fname
[0]) return NULL
;
458 if (fname
[0] != '/') {
461 incdir
= lastIncludePath
;
463 incdir
= lastSysIncludePath
;
464 if (!incdir
|| !incdir
[0]) incdir
= sysIncludeDir
;
466 res
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
468 res
= strprintf("%s", fname
);
471 if (defaultmain
&& defaultmain
[0]) {
472 if (stat(res
, &st
) == 0) {
473 if (S_ISDIR(st
.st_mode
)) {
474 char *rs
= strprintf("%s/%s", res
, defaultmain
);
480 /* check if there is disk file */
481 if (strHasPathDelim(fname
) && stat(res
, &st
) != 0) {
482 /* no file, try "root include" */
483 const char *incdir
= (!assystem
? NULL
: sysIncludeDir
);
484 char *rs
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
487 /* check for dir again */
488 if (defaultmain
&& defaultmain
[0]) {
489 if (stat(res
, &st
) == 0) {
490 if (S_ISDIR(st
.st_mode
)) {
491 char *rs
= strprintf("%s/%s", res
, defaultmain
);
498 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
503 static int includeCount
= 0;
506 // include file instead of the current line
507 static int asmTextInclude (const char *fname
, int system
) {
511 SourceLine
*first
= NULL
, *last
= NULL
, *s
= NULL
;
513 if (includeCount
> 256) {
514 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", curSrcLine
->fname
, curSrcLine
->lineNo
);
518 fn
= createIncludeName(fname
, system
, "zzmain.zas");
520 printf("loading: %s\n", fn
);
521 if ((fl
= fopen(fn
, "r")) == NULL
) {
522 fprintf(stderr
, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", curSrcLine
->fname
, curSrcLine
->lineNo
, currLine
);
527 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
529 currLine
[sizeof(currLine
)-1] = '\0';
530 const size_t slen
= strlen(currLine
);
531 if (slen
== 0 || (currLine
[slen
-1] != '\n' && currLine
[slen
-1] != '\r')) {
532 fprintf(stderr
, "ERROR: file %s, line %d: line too long\n", fn
, lineNo
);
536 //!normalizeStr(currLine);
537 if (!currLine
[0]) continue; // don't store empty lines
539 if ((s
= calloc(1, sizeof(SourceLine
))) == NULL
) abort();
542 if ((s
->line
= strdup(currLine
)) == NULL
) abort();
543 if ((s
->fname
= strdup(fn
)) == NULL
) abort();
544 if (last
!= NULL
) last
->next
= s
; else first
= s
;
550 curSrcLine
->line
[0] = 0;
552 last
->next
= curSrcLine
->next
;
553 curSrcLine
->next
= first
;
559 ///////////////////////////////////////////////////////////////////////////////
562 static void processCurrentLine (void); // only one, will skip to next one
563 static void processForthWordLine (void); // only one, will skip to next one
566 ///////////////////////////////////////////////////////////////////////////////
567 // error raisers, etc
569 static jmp_buf errJP
;
572 static void errorWriteFile (FILE *fo
) {
574 fprintf(fo
, "at file %s, line %d\n%s\n*", curSrcLine
->fname
, curSrcLine
->lineNo
, curSrcLine
->line
);
576 fprintf(fo
, "somewhere in time: ");
580 static void errorMsgV (const char *fmt
, va_list ap
) {
581 errorWriteFile(stderr
);
582 vfprintf(stderr
, fmt
, ap
);
589 static void __attribute__((format(printf
, 1, 2))) warningMsg (const char *fmt
, ...) {
591 fprintf(stderr
, "WARNING ");
597 static void __attribute__((format(printf
, 1, 2))) errorMsg (const char *fmt
, ...) {
599 fprintf(stderr
, "FATAL ");
605 static void __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2))) fatal (const char *fmt
, ...) {
613 static void __attribute__((noreturn
)) fatalUrLib (int errcode
) {
614 errorMsg("%s", urasm_errormsg(errcode
));
619 //////////////////////////////////////////////////////////////////////////////
620 // operator management
622 // return !0 to skip current line
623 typedef int (*UrAsmOpFn
) (void);
630 typedef struct UrAsmOp
{
633 struct UrAsmOp
*next
;
636 static UrAsmOp
*oplist
= NULL
;
639 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
640 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
642 res
->name
= strdup(name
);
650 static UrAsmOp
*urFindOp (const char *name
) {
652 for (res
= oplist
; res
; res
= res
->next
) if (!strcasecmp(name
, res
->name
)) break;
657 static void urClearOps (void) {
661 oplist
= oplist
->next
;
668 ///////////////////////////////////////////////////////////////////////////////
671 typedef struct UrLabelInfo
{
674 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
675 int known
; /* !0: label value already known */
676 int refLine
; /* first referenced line */
677 int fixuptype
; /* UR_FIXUP_XXX */
679 struct UrLabelInfo
*next
;
682 static UrLabelInfo
*labels
= NULL
;
683 static UrLabelInfo
*labelsTail
= NULL
;
686 static void urClearLabels (void) {
688 while ((c
= labels
) != NULL
) {
690 if (c
->name
) free(c
->name
);
691 if (c
->refFile
) free(c
->refFile
);
698 static UrLabelInfo
*urFindLabel (const char *name
) {
699 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) if (strcmp(name
, c
->name
) == 0) return c
;
704 static UrLabelInfo
*urAddLabel (const char *name
) {
705 UrLabelInfo
*c
= urFindLabel(name
);
707 c
= calloc(1, sizeof(UrLabelInfo
));
709 c
->name
= strdup(name
);
711 c
->fixuptype
= UR_FIXUP_NONE
;
713 if (labelsTail
) labelsTail
->next
= c
; else labels
= c
;
720 ///////////////////////////////////////////////////////////////////////////////
721 // module list management
723 typedef struct ModuleInfo
{
725 char *fname
; // opened in this file
726 int seen
; // !0: module already seen, skip other definitions from the same file
727 struct ModuleInfo
*next
;
730 static ModuleInfo
*modules
= NULL
;
731 static ModuleInfo
*curModule
= NULL
;
734 static void modulesClear (void) {
737 ModuleInfo
*c
= modules
;
739 modules
= modules
->next
;
747 static void modulesResetSeen (void) {
748 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
752 static ModuleInfo
*moduleFind (const char *name
) {
753 if (!name
|| !name
[0]) return NULL
;
754 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) return c
;
759 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
762 if (!name
|| !fname
|| !name
[0] || !fname
[0]) abort();
763 if ((c
= calloc(1, sizeof(ModuleInfo
))) == NULL
) abort();
764 if ((c
->name
= strdup(name
)) == NULL
) abort();
765 if ((c
->fname
= strdup(fname
)) == NULL
) abort();
767 return (modules
= c
);
771 ///////////////////////////////////////////////////////////////////////////////
773 typedef struct FixupItem
{
774 struct FixupItem
*next
;
780 static FixupItem
*fixlisthead
= NULL
, *fixlisttail
= NULL
;
783 static void clearFixups (void) {
786 while ((c
= fixlisthead
) != NULL
) {
787 fixlisthead
= c
->next
;
793 static void addFixup (uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
794 FixupItem
*fx
= calloc(1, sizeof(FixupItem
));
796 fx
->opdestaddr
= opdestaddr
;
798 fx
->fixuptype
= fixuptype
;
801 if (fixlisttail
!= NULL
) fixlisttail
->next
= fx
; else fixlisthead
= fx
;
807 ///////////////////////////////////////////////////////////////////////////////
808 // destination memory management
810 static uint8_t memory
[65536];
811 static char memused
[65536];
812 static char memresv
[65536];
813 static uint16_t start_pc
= 0x100; // viva CP/M!
814 static uint16_t start_disp
= 0x100; // viva CP/M!
815 static uint16_t start_ent
= 0x100; // viva CP/M!
816 static uint16_t pc
= 0; /* current position to write */
817 static uint16_t disp
= 0; /* current 'virtual PC' */
818 static uint16_t ent
= 0; /* starting address */
819 static uint16_t clrAddr
= 24999; /* address for CLEAR for cargador */
820 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
821 static int inTapeBlock
= 0;
822 static uint8_t tapeXorB
= 0;
825 static inline uint8_t getByte (uint16_t addr
) {
831 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
832 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
837 static inline void putByte (uint16_t addr
, uint8_t b
) {
838 if (inTapeBlock
) tapeXorB
^= b
;
844 static inline MAYBE_UNUSED
void putWord (uint16_t addr
, uint16_t w
) {
845 putByte(addr
, w
&0xFFU
);
846 putByte(addr
+1, (w
>>8)&0xFFU
);
850 static inline void emitByte (uint8_t b
) {
857 static inline void emitWord (uint16_t w
) {
859 emitByte((w
>>8)&0xFFU
);
863 static inline void emitRWord (uint16_t w
) {
864 emitByte((w
>>8)&0xFFU
);
869 static void prepareMemory (void) {
870 memset(memory
, 0, sizeof(memory
));
871 memset(memused
, 0, sizeof(memused
));
872 memset(memresv
, 0, sizeof(memresv
));
876 ///////////////////////////////////////////////////////////////////////////////
877 // label getter and utilities
879 static char *lastSeenGlobalLabel
= NULL
; /* global */
882 static char *fixLocalLabel (const char *name
) {
883 static char newname
[MAX_LINE_SIZE
*2+1024];
885 memset(newname
, 0, sizeof(newname
));
886 if (!name
|| !name
[0]) {
888 } else if (!lastSeenGlobalLabel
|| name
[0] != '.') {
889 strcpy(newname
, name
);
891 if (name
[0] == '.' && name
[1] == '.') {
892 // this is macro label
893 if (curmacro
== NULL
) fatal("macro label outside of macro: '%s'", name
);
894 sprintf(newname
, "{#MAC%d:%s:%s}", curmacronum
, curmacro
->mac
->name
, name
);
896 // this is local label, let's rename it
897 sprintf(newname
, "{%s%s}", lastSeenGlobalLabel
, name
);
899 /*!fprintf(stderr, "LABEL <%s> renamed to <%s> (%d)\n", name, newname, (curSrcLine ? curSrcLine->lineNo : 0));*/
905 static char *fixGlobalLabel (const char *name
) {
906 static char newname
[MAX_LINE_SIZE
*2+1024];
908 memset(newname
, 0, sizeof(newname
));
909 if (!name
|| !name
[0]) {
911 } else if (!curModule
|| name
[0] == '.' || name
[0] == '{' || name
[0] == '@' || strchr(name
, '.')) {
912 if (name
[0] == '@' && name
[1]) ++name
;
913 strcpy(newname
, name
);
915 // this is global unqualified label and we have a module; let's rename it
916 sprintf(newname
, "%s.%s", curModule
->name
, name
);
918 //printf("%s --> %s\n", name, newname);
923 static int lblOptMakeU2
= 0;
925 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
, int *fixuptype
) {
929 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
930 lbl
= urFindLabel(nn
);
932 // try non-module label
933 lbl
= urFindLabel(ln
);
937 errorMsg("using undefined label %s", ln
);
942 lbl
= urAddLabel(nn
);
943 lbl
->type
= (lblOptMakeU2
? -42 : -1);
945 lbl
->refLine
= curSrcLine
->lineNo
;
946 lbl
->refFile
= strdup(curSrcLine
->fname
);
947 //fprintf(stderr, "new label: [%s]\n", lbl->name);
948 //fprintf(stderr, "new label: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
950 //fprintf(stderr, "label reference: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
954 *defined
= lbl
->known
!=0;
955 *fixuptype
= lbl
->fixuptype
;
970 static int isLabelDefinedOrKnown (const char *name
, uint16_t addr
, int qtype
) {
974 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
975 lbl
= urFindLabel(nn
);
977 // try non-module label
978 lbl
= urFindLabel(ln
);
981 case UR_QTYPE_DEFINED
: return lbl
? lbl
->known
!=0 : 0;
982 case UR_QTYPE_KNOWN
: return lbl
!=NULL
;
989 static void fixupOperandCB (const urasm_operand_t
*op
, uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
991 //static const char *n[4] = {"none", "word", "low", "high"};
992 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
993 addFixup(opdestaddr
, opaddr
, fixuptype
, size
);
998 static int checkLabels (void) {
1000 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
1001 //fprintf(stderr, ":LBL: <%s> (type=%d)\n", c->name, c->type);
1002 if (c
->type
== -1) {
1003 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
1006 if (c
->type
== 0) c
->known
= -1;
1008 //if (wasError) longjmp(errJP, 667);
1013 ///////////////////////////////////////////////////////////////////////////////
1014 // expression utils (aka string parsing)
1017 /* skip leading spaces */
1018 /* returns string with spaces skipped */
1019 static inline char *strSkipSpaces (const char *s
) {
1020 while (*s
&& isSpace(*s
)) ++s
;
1025 /* skip leading spaces */
1026 /* returns string with spaces skipped */
1027 static inline const char *strSkipSpacesConst (const char *s
) {
1028 while (*s
&& isSpace(*s
)) ++s
;
1033 /* remove trailing spaces from string */
1034 static void strTrimRight (char *s
) {
1035 if (!s
|| !s
[0]) return;
1036 size_t len
= strlen(s
);
1037 while (len
> 0 && isSpace(s
[len
-1])) --len
;
1042 /* skip leading spaces and colons */
1043 /* returns string with spaces skipped */
1044 static inline char *strSkipSpacesColons (char *s
) {
1045 while (*s
&& (isSpace(*s
) || *s
== ':')) ++s
;
1050 /* remove leading spaces from the current line */
1051 static inline void removeSpaces (void) {
1052 char *e
= strSkipSpaces(currLine
);
1053 if (e
!= currLine
) memmove(currLine
, e
, strlen(e
)+1);
1057 /* skip leading spaces, and argument (up to, but not including comma or colon) */
1058 /* correctly skip strings */
1059 /* returns string after skipped argument (with trailing spaces skipped) */
1060 static char *skipMacroArg (char *str
) {
1062 char *strstart
= str
;
1064 str
= strSkipSpaces(str
);
1065 if (!str
[0]) return str
;
1066 if (parens
== 0 && (str
[0] == ',' || str
[0] == ':')) return str
;
1067 /* check for "af'" */
1068 if (str
[0] == '\'' && str
-strstart
>= 2 && toLower(str
[-2] == 'a') && toLower(str
[-1] == 'f')) {
1072 if (str
[0] == '(') { ++parens
; continue; }
1073 if (str
[0] == ')') { if (--parens
< 0) parens
= 0; continue; }
1074 /* check for string */
1075 if (str
[0] == '"' || str
[0] == '\'') {
1076 const char qch
= *str
++;
1078 const char ch
= *str
++;
1079 if (ch
== qch
) break;
1080 if (ch
== '\\' && *str
) ++str
;
1089 /* evaluate next numeric expression in input string */
1090 /* returns expression value */
1091 static int32_t getExprArg (int *defined
, int *addr
) {
1093 char *a
= strSkipSpaces(currLine
);
1095 if (!a
[0]) fatal("expression expected");
1096 const char *ee
= urasm_expr(&res
, a
, disp
, defined
, addr
, &error
);
1097 if (error
) fatalUrLib(error
);
1099 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
1100 memmove(currLine
, ee
, strlen(ee
)+1);
1108 /* evaluate next string expression in input string */
1109 /* returns expression value */
1110 static char *getStrExprArg (void) {
1112 int donteval
= 0, defined
= 0;
1113 static char resbuf
[256];
1114 char *a
= strSkipSpaces(currLine
);
1115 if (!a
[0]) fatal("expression expected");
1116 urasm_exprval_t res
;
1117 urasm_exprval_init(&res
);
1118 const char *ee
= urasm_expr_ex(&res
, a
, disp
, &donteval
, &defined
, &error
);
1119 if (error
) fatalUrLib(error
);
1121 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
1122 memmove(currLine
, ee
, strlen(ee
)+1);
1127 snprintf(resbuf
, sizeof(resbuf
), "%s", res
.str
);
1129 snprintf(resbuf
, sizeof(resbuf
), "%d", res
.val
);
1131 urasm_exprval_clear(&res
);
1136 /* evaluate next numeric expression in input string */
1137 /* there shoild be no other expressions in the string */
1138 /* returns expression value */
1139 static int32_t getOneExprArg (int *defined
, int *addr
) {
1140 int32_t res
= getExprArg(defined
, addr
);
1141 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
1146 /* is next expression a string literal? */
1147 static inline int isStrArg (void) {
1148 const char *s
= strSkipSpaces(currLine
);
1149 return (s
[0] == '"' || s
[0] == '\'');
1153 /* check of we reached end of operator */
1154 static __attribute__((unused
)) inline int isOperatorEnd (void) {
1155 const char *s
= strSkipSpaces(currLine
);
1156 return (s
[0] == 0 || s
[0] == ':');
1160 /* check of we reached end of operator */
1161 static __attribute__((unused
)) inline int isLineEnd (void) {
1162 const char *s
= strSkipSpaces(currLine
);
1167 /* parse string argument from input string */
1168 /* returns parsed string */
1169 static char *getStrArg (int *lenp
) {
1171 char *a
= strSkipSpaces(currLine
);
1173 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
1174 res
= parseStr(&a
, qCh
, lenp
);
1176 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
1177 memmove(currLine
, a
, strlen(a
)+1);
1185 /* get identifier (and lowercase it) */
1186 static char *getOneIdArgLo (void) {
1187 static char res
[MAX_LINE_SIZE
+128];
1189 char *a
= strSkipSpaces(currLine
);
1190 memset(res
, 0, sizeof(res
));
1191 if (!a
[0]) fatal("identifier expected");
1192 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
1193 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
1195 if (p
-res
> 120) fatal("identifier too long: %s", res
);
1196 while (*a
&& isSpace(*a
)) ++a
;
1198 memmove(currLine
, a
, strlen(a
)+1);
1199 if (currLine
[0] == ';') currLine
[0] = 0;
1200 if (currLine
[0]) fatal("extra arguments");
1204 for (char *t
= res
; *t
; ++t
) *t
= toLower(*t
);
1209 /* get label argument */
1210 static char *getLabelArg (int checkdelim
) {
1211 static char res
[MAX_LINE_SIZE
+128];
1213 char *a
= strSkipSpaces(currLine
);
1214 memset(res
, 0, sizeof(res
));
1215 if (!a
[0]) fatal("label expected");
1216 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
1217 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
1219 if (p
-res
> 120) fatal("label name too long: %s", res
);
1220 while (*a
&& isSpace(*a
)) ++a
;
1222 if (checkdelim
&& a
[0] != ',' && a
[0] != ':') fatal("bad label expression");
1223 memmove(currLine
, a
, strlen(a
)+1);
1231 /* get label argument, and ensure that it is the last one */
1232 static char *getOneLabelArg (void) {
1233 char *res
= getLabelArg(1);
1234 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
1239 /* returns ',' or 0 */
1240 static char eatComma (void) {
1241 char *a
= strSkipSpaces(currLine
);
1242 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
1243 if (a
[0] == ':') return 0;
1244 if (a
[0] != ',') fatal("invalid expression: ',' expected");
1245 for (++a
; *a
&& isSpace(*a
); ++a
) {}
1246 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
1247 memmove(currLine
, a
, strlen(a
)+1);
1252 ///////////////////////////////////////////////////////////////////////////////
1255 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
1256 char *ep
= strSkipSpacesColons(currLine
);
1257 memmove(currLine
, ep
, strlen(ep
)+1);
1261 static void checkExprEnd (void) {
1262 char *ep
= strSkipSpaces(currLine
);
1263 memmove(currLine
, ep
, strlen(ep
)+1);
1264 if (currLine
[0] && currLine
[0] != ':') fatal("end of expression expected");
1268 static void checkOperatorEnd (void) {
1269 char *ep
= strSkipSpaces(currLine
);
1270 memmove(currLine
, ep
, strlen(ep
)+1);
1271 if (currLine
[0]) fatal("end of operator expected");
1275 /* remove label from curLine */
1276 static void removeLabel (void) {
1277 char *ep
= currLine
;
1278 if (ep
[0] && !isSpace(ep
[0])) for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {} // skip text
1279 // skip spaces and colons
1280 ep
= strSkipSpacesColons(ep
);
1281 memmove(currLine
, ep
, strlen(ep
)+1);
1285 static int labelDoEQU (const char *lblname
, const char *value
) {
1286 static char n2
[256];
1289 if (value
== NULL
|| lblname
== NULL
|| !lblname
[0] || strlen(lblname
) > 255 || strlen(value
) >= MAX_LINE_SIZE
) return -1;
1290 memset(n2
, 0, sizeof(n2
));
1291 strcpy(n2
, lblname
);
1292 if (!urasm_is_valid_name(n2
)) return -1; // this is not a valid label, get out of here
1293 // check if this can be an instruction
1294 lbl
= urAddLabel(lblname
);
1295 if (!lbl
->refFile
) {
1297 lbl
->refFile
= strdup("artificially-defined-label");
1299 strcpy(currLine
, value
);
1301 int defined
= 1, addr
= UR_FIXUP_NONE
;
1302 int32_t res
= getOneExprArg(&defined
, &addr
);
1303 lbl
->type
= 1; // equ label
1304 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1309 return -1; //fatal("can't calculate label %s", lbl->name);
1316 static void processLabel (void) {
1319 static char n2
[256];
1321 int noLocAff
= 0, doEQU
= 0;
1322 /*!fprintf(stderr, "LINE <%s> (%d)\n", curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
1323 memset(n2
, 0, sizeof(n2
));
1324 if (!currLine
[0] || isSpace(currLine
[0]) || currLine
[0] == ':') {
1325 // this may be " id = smth" or " id equ smth"
1328 while (isSpace(*ep
)) ++ep
;
1329 if (ep
[0] == ':' || !ep
[0] || (!isAlpha(ep
[0]) && ep
[0] != '_' && ep
[0] != '.' && ep
[0] != '@')) {
1330 removeLabel(); // removeLabel() removes any spaces, etc
1333 // this looks like a label; check for '=' or 'equ'
1334 while (isAlphaDigit(ep
[0]) || ep
[0] == '_' || ep
[0] == '.' || ep
[0] == '@') ++ep
;
1336 // skip trailing spaces
1337 while (isSpace(*ep
)) ++ep
;
1341 } else if (isSpace(*nn
)) {
1343 argstart
= strIsCommand("EQU", ep
);
1348 removeLabel(); // removeLabel() removes any spaces, etc
1351 // remove leading spaces from name
1355 while (isSpace(*ep
)) ++ep
;
1356 if (ep
>= nn
) fatal("internal compiler error");
1357 if (nn
-ep
> 120) fatal("label too long");
1358 memset(n2
, 0, sizeof(n2
));
1359 memmove(n2
, ep
, nn
-ep
);
1360 if (urFindOp(n2
) || !urasm_is_valid_name(n2
)) {
1361 //fatal("invalid label name");
1362 removeLabel(); // removeLabel() removes any spaces, etc
1365 // remove label name
1366 memmove(currLine
, argstart
, strlen(argstart
)+1);
1368 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
1369 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
1370 lbl
= urAddLabel(nn
);
1371 if (!lbl
->refFile
) {
1372 lbl
->refLine
= curSrcLine
->lineNo
;
1373 lbl
->refFile
= strdup(curSrcLine
->fname
);
1375 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
1376 if (doEQU
&& pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
1377 int defined
= 1, addr
= UR_FIXUP_NONE
;
1378 int32_t res
= getOneExprArg(&defined
, &addr
);
1380 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1385 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
1391 for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {}
1392 if (ep
-currLine
> 120) fatal("label too long");
1394 memset(n2
, 0, sizeof(n2
));
1395 memmove(n2
, currLine
, ep
-currLine
);
1397 ep
= strSkipSpaces(ep
);
1398 if (*ep
!= ':') return; // this must be an instruction, process it
1400 if (!urasm_is_valid_name(n2
)) return; // this is not a valid label, get out of here
1402 if (findMacro(n2
)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
1403 // check if this can be instruction
1404 //ep = strSkipSpaces(ep);
1405 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
1406 // ok, we got a good label
1408 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
1409 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (curSrcLine ? curSrcLine->lineNo : 0));*/
1410 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
1411 /*!fprintf(stderr, " RENAMED(ln) <%s>\n", ln);*/
1412 /*!fprintf(stderr, " RENAMED(nn) <%s>\n", nn);*/
1413 lbl
= urAddLabel(nn
);
1414 if (!lbl
->refFile
) {
1415 lbl
->refLine
= curSrcLine
->lineNo
;
1416 lbl
->refFile
= strdup(curSrcLine
->fname
);
1418 //printf("new: [%s]\n", lbl->name);
1420 if (currLine
[0] == '=') {
1422 argstart
= currLine
+1;
1425 argstart
= strIsCommand("EQU", currLine
);
1427 if (!argstart
|| doEQU
) {
1428 if (pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
1430 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
1433 memmove(currLine
, argstart
, strlen(argstart
)+1);
1435 if (lbl
->type
!= -1 && lbl
->type
!= 0) fatal("duplicate label '%s'", lbl
->name
);
1437 int defined
= 1, addr
= UR_FIXUP_NONE
;
1438 int32_t res
= getOneExprArg(&defined
, &addr
);
1440 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1445 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
1450 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d); disp=#%04X; pc=#%04X\n", lbl->name, doEQU, disp, pc);
1452 if (lbl
->name
[0] != '{' && !noLocAff
) {
1453 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
1454 lastSeenGlobalLabel
= strdup(lbl
->name
);
1459 lbl
->fixuptype
= UR_FIXUP_WORD
;
1463 ///////////////////////////////////////////////////////////////////////////////
1464 // instruction finder (in source)
1466 /* array ends with NULL */
1467 /* returns line or NULL */
1468 /* iidx will be set to found instruction number */
1469 static __attribute__((sentinel
)) SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
1470 if (iidx
) *iidx
= -1;
1471 for (SourceLine
*cur
= curSrcLine
; cur
; cur
= cur
->next
) {
1473 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
1475 for (int f
= 0; ;++f
) {
1476 const char *name
= va_arg(ap
, const char *);
1479 if (strIsCommand(name
, cur
->line
)) {
1481 if (iidx
) *iidx
= f
;
1491 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
1492 return findNextInstructionFromList(NULL
, name
, NULL
);
1496 ///////////////////////////////////////////////////////////////////////////////
1499 static int optWriteFixups
= 0;
1500 static int optFixupType
= 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
1501 static int optRunTape
= 1;
1502 static int optRunDMB
= 1;
1503 static int optRunSCL
= -1; /* -1: save cargador, but no boot; 1: boot and cargador */
1504 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1505 static int optSNA48
= 1;
1506 static char *optOutputDir
= NULL
;
1509 ///////////////////////////////////////////////////////////////////////////////
1510 static void writeFixups (void) {
1512 void writeFixupList (FILE *fo
, int cnt
, const char *lbl
, int (*chk
)(const FixupItem
*)) {
1515 fprintf(fo
, "%s:\n", lbl
);
1516 if (optFixupType
== 1) fprintf(fo
, " dw %d ; count\n", cnt
);
1517 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1519 fprintf(fo
, " dw #%04X\n", fx
->opaddr
-prevaddr
);
1520 if (optFixupType
== 2) {
1521 prevaddr
= fx
->opaddr
;
1525 if (optFixupType
== 2 || optFixupType
== 3) fprintf(fo
, " dw 0 ; end marker\n");
1529 if (optFixupType
== 0) {
1530 /* simple text file */
1531 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.txt");
1532 printf("writing fixups to '%s'...\n", fname
);
1533 FILE *fo
= fopen(fname
, "w");
1535 if (fo
== NULL
) fatal("can't write fixup file");
1536 fprintf(fo
, "; addr dadr sz\n");
1537 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1538 static const char type
[4] = "NWLH";
1539 fprintf(fo
, "%c #%04x #%04x %d\n", type
[fx
->fixuptype
], fx
->opaddr
, fx
->opdestaddr
, fx
->size
);
1543 /* various asm formats */
1544 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.zas");
1545 printf("writing fixups to '%s'...\n", fname
);
1546 FILE *fo
= fopen(fname
, "w");
1547 int cntw
= 0, cntwl
= 0, cntwh
= 0, cntbl
= 0, cntbh
= 0;
1549 if (fo
== NULL
) fatal("can't write fixup file");
1550 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1551 if (fx
->fixuptype
== UR_FIXUP_WORD
) { ++cntw
; continue; }
1552 switch (fx
->fixuptype
) {
1553 case UR_FIXUP_LOBYTE
: if (fx
->size
== 2) ++cntwl
; else ++cntbl
; break;
1554 case UR_FIXUP_HIBYTE
: if (fx
->size
== 2) ++cntwh
; else ++cntbh
; break;
1557 writeFixupList(fo
, cntw
, "fixup_table_w", lambda(int, (const FixupItem
*fx
) {
1558 return (fx
->fixuptype
== UR_FIXUP_WORD
);
1560 writeFixupList(fo
, cntwl
, "fixup_table_wl", lambda(int, (const FixupItem
*fx
) {
1561 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 2);
1563 writeFixupList(fo
, cntwh
, "fixup_table_wh", lambda(int, (const FixupItem
*fx
) {
1564 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 2);
1566 writeFixupList(fo
, cntbl
, "fixup_table_bl", lambda(int, (const FixupItem
*fx
) {
1567 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 1);
1569 writeFixupList(fo
, cntbh
, "fixup_table_bh", lambda(int, (const FixupItem
*fx
) {
1570 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 1);
1577 ///////////////////////////////////////////////////////////////////////////////
1578 /* return 'found' flag */
1579 static int findChunkFrom (int addr
, int *start
, int *len
) {
1580 if (addr
< 0) addr
= 0;
1581 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) {}
1582 if (addr
> 65535) return 0;
1584 for (; addr
<= 65535 && memused
[addr
]; ++addr
) {}
1585 *len
= addr
-(*start
);
1590 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
1591 for (; buflen
>= 2; buflen
-= 2) {
1593 uint8_t byte
= *buf
++;
1597 for (cnt
= byte
; cnt
>= 0; --cnt
, ++addr
, --buflen
, ++buf
) {
1598 if (!memused
[addr
]) putByte(addr
, *buf
);
1599 if (addr
== 0xffff) return;
1603 for (; cnt
> 0; --cnt
, ++addr
) {
1604 if (!memused
[addr
]) putByte(addr
, byte
);
1605 if (addr
== 0xffff) return;
1612 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
1613 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1614 if (bxor
) *bxor
= (*bxor
)^b
;
1619 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
1620 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
1621 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
1626 ///////////////////////////////////////////////////////////////////////////////
1629 static int saveSna (const char *fname
, int as48
) {
1630 char *fn
;// = malloc(strlen(fname)+16);
1635 fn
= strprintf("%s/%s.sna", optOutputDir
, fname
);
1636 fo
= fopen(fn
, "wb");
1638 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
1639 printf("out: %s.sna\n", fname
);
1640 memmove(regs
, ursna48
, 27);
1643 /* push new address */
1644 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1646 putByte(sp
, ent
&0xFFU
);
1647 putByte(sp
+1, (ent
>>8)&0xFFU
);
1648 regs
[23] = sp
&0xFFU
;
1649 regs
[24] = (sp
>>8)&0xFFU
;
1651 fwrite(regs
, 27, 1, fo
);
1652 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
1653 fwrite(memory
+16384, 49152, 1, fo
);
1656 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1658 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1659 //fprintf(stderr, "%d\n", memused[sp]);
1660 if (memused
[sp
] || memused
[(sp
+1)&0xffff]) {
1661 fprintf(stderr
, "FATAL: can't save snapshot!\n");
1665 if (fwrite(ursna48
, 27, 1, fo
) != 1) goto error
;
1666 rleUnpack(16384, (const uint8_t *)ursna48
+27, sizeof(ursna48
)-27);
1667 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1669 sprintf(abuf
, "%05d", clrAddr
);
1670 memcpy(memory
+23762, abuf
, 5);
1671 sprintf(abuf
, "%05d", ent
);
1672 memcpy(memory
+23773, abuf
, 5);
1674 if (fwrite(memory
+16384, 49152, 1, fo
) != 1) goto error
;
1677 int addr
= memory
[sp
]+256*memory
[(sp
+1)&0xffff];
1679 if (fWriteWord(fo
, addr
, NULL
) != 0) goto error
; // PC
1680 if (fWriteByte(fo
, 0x10, NULL
) != 0) goto error
; // 7FFD
1681 if (fWriteByte(fo
, 0, NULL
) != 0) goto error
; // TR-DOS inactive
1683 memset(memory
, 0, 16384);
1684 for (int f
= 1; f
< 8; ++f
) {
1685 if (f
!= 2 && f
!= 5) {
1686 if (fwrite(memory
, 16384, 1, fo
) != 1) goto error
;
1700 ///////////////////////////////////////////////////////////////////////////////
1703 static void saveRaw (const char *basename
) {
1704 char *fname
= NULL
;// = malloc(strlen(basename)+16);
1708 while (findChunkFrom(start
, &start
, &len
)) {
1709 if (fname
!= NULL
) free(fname
);
1710 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, (optTapExt
? "tap" : "bin"));
1711 fo
= fopen(fname
, "wb");
1713 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1715 printf("out: %s\n", fname
);
1716 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1717 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1726 if (fname
!= NULL
) free(fname
);
1730 ///////////////////////////////////////////////////////////////////////////////
1733 typedef struct __attribute__((packed
)) __attribute__((gcc_struct
)) {
1738 uint8_t zero
; // always zero
1739 uint8_t secLen
; // length in sectors
1744 static uint16_t calcHobSum (const void *hdr
) {
1745 const uint8_t *buf
= (const uint8_t *)hdr
;
1748 for (unsigned int f
= 0; f
< 15; ++f
) res
+= ((uint16_t)buf
[f
])*257+f
;
1753 static void saveHob (const char *basename
) {
1754 char *fname
= NULL
;//malloc(strlen(basename)+16);
1759 while (findChunkFrom(start
, &start
, &len
)) {
1760 if (fname
!= NULL
) free(fname
);
1761 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, "$C");
1762 fo
= fopen(fname
, "wb");
1764 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1767 char tmpbuf
[sizeof(hdr
.name
)*2];
1768 printf("out: %s\n", fname
);
1769 memset(&hdr
, 0, sizeof(hdr
));
1770 memset(&hdr
.name
, 32, 8);
1771 snprintf(tmpbuf
, sizeof(tmpbuf
), "%c%04X", basename
[0], start
);
1772 while (strlen(tmpbuf
) < sizeof(hdr
.name
)) strcat(tmpbuf
, " "); /* sorry! */
1773 memcpy(hdr
.name
, tmpbuf
, sizeof(hdr
.name
));
1777 hdr
.secLen
= (len
+255)/256;
1778 hdr
.checksum
= calcHobSum(&hdr
);
1779 if (fwrite(&hdr
, sizeof(hdr
), 1, fo
) != 1) {
1780 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1785 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1786 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1793 if (fwrite(&hdr
.zero
, 1, 1, fo
) != 1) {
1794 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1800 //fprintf(stderr, ":%d\n", len%256);
1806 if (fname
!= NULL
) free(fname
);
1810 // ////////////////////////////////////////////////////////////////////////// //
1813 uint8_t dir
[14*256];
1815 uint32_t currfstartpos
;
1822 static void sclInit (SCLFile
*scl
) {
1823 memset(scl
, 0, sizeof(*scl
));
1824 scl
->datasize
= 2*80*16*256; /* maximum disk size */
1825 scl
->data
= malloc(scl
->datasize
);
1826 memset(scl
->data
, 0, scl
->datasize
);
1827 scl
->currfstartpos
= 0xffffffffu
;
1831 static void sclClear (SCLFile
*scl
) {
1833 if (scl
->data
) free(scl
->data
);
1834 memset(scl
, 0, sizeof(*scl
));
1835 scl
->currfstartpos
= 0xffffffffu
;
1839 static void sclStartFile (SCLFile
*scl
, const char *name
, char type
, uint16_t v0
, uint16_t v1
) {
1840 if (scl
->fcount
== 255) {
1841 fprintf(stderr
, "FATAL: too many files in SCL!\n");
1844 if (scl
->currfstartpos
!= 0xffffffffu
) {
1845 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
1849 while (nlen
< 8 && name
&& name
[nlen
]) scl
->dir
[scl
->dirpos
++] = name
[nlen
++];
1850 while (nlen
< 8) { scl
->dir
[scl
->dirpos
++] = ' '; ++nlen
; }
1851 scl
->dir
[scl
->dirpos
++] = type
;
1852 scl
->dir
[scl
->dirpos
++] = v0
&0xff;
1853 scl
->dir
[scl
->dirpos
++] = (v0
>>8)&0xff;
1854 scl
->dir
[scl
->dirpos
++] = v1
&0xff;
1855 scl
->dir
[scl
->dirpos
++] = (v1
>>8)&0xff;
1856 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
1857 scl
->currfstartpos
= scl
->datapos
;
1861 static void sclEndFile (SCLFile
*scl
) {
1862 if (scl
->currfstartpos
== 0xffffffffu
) {
1863 fprintf(stderr
, "FATAL: last SCL file already finished!\n");
1866 /* align to sector */
1867 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
1868 const uint32_t fsz
= scl
->datapos
-scl
->currfstartpos
;
1869 if (fsz
> 255*256) {
1870 fprintf(stderr
, "FATAL: SCL file too big!\n");
1873 if (fsz
&255) abort();
1874 scl
->dir
[scl
->dirpos
++] = fsz
/256; /* size in sectors */
1875 if (scl
->dirpos
%14) abort();
1877 scl
->currfstartpos
= 0xffffffffu
;
1881 static void sclWriteData (SCLFile
*scl
, const void *buf
, size_t len
) {
1882 if (scl
->currfstartpos
== 0xffffffffu
) {
1883 fprintf(stderr
, "FATAL: no open SCL file!\n");
1887 if (len
> 255*256) {
1888 fprintf(stderr
, "FATAL: SCL file too big!\n");
1891 memcpy(scl
->data
+scl
->datapos
, buf
, len
);
1892 scl
->datapos
+= len
;
1896 static void sclWriteByte (SCLFile
*scl
, uint8_t b
) {
1897 sclWriteData(scl
, &b
, 1);
1901 static void sclWriteWord (SCLFile
*scl
, uint16_t w
) {
1903 sclWriteData(scl
, &b
, 1);
1905 sclWriteData(scl
, &b
, 1);
1909 static int sclFileWrite (FILE *fo
, const void *buf
, size_t count
, uint32_t *checksum
) {
1910 if (!count
) return 0;
1911 const uint8_t *p
= (const uint8_t *)buf
;
1912 if (checksum
) for (size_t n
= count
; n
--; ++p
) *checksum
+= (uint32_t)(*p
);
1913 if (fwrite(buf
, count
, 1, fo
) != 1) return -1;
1919 static int sclSaveToFile (FILE *fo
, SCLFile
*scl
) {
1920 if (scl
->currfstartpos
!= 0xffffffffu
) {
1921 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
1924 const char *sign
= "SINCLAIR";
1925 uint32_t checksum
= 0;
1927 if (sclFileWrite(fo
, sign
, 8, &checksum
) != 0) return -1;
1928 if (sclFileWrite(fo
, &scl
->fcount
, 1, &checksum
) != 0) return -1;
1930 if (sclFileWrite(fo
, scl
->dir
, scl
->dirpos
, &checksum
) != 0) return -1;
1932 if (sclFileWrite(fo
, scl
->data
, scl
->datapos
, &checksum
) != 0) return -1;
1934 for (unsigned f
= 0; f
< 4; ++f
) {
1935 const uint8_t b
= (checksum
>>(f
*8))&0xff;
1936 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1943 // ////////////////////////////////////////////////////////////////////////// //
1944 static void saveSCLCargador (SCLFile
*scl
) {
1945 static uint8_t cargador
[16384]; // should be enough for everyone
1948 int start
= 0, len
, pos
;
1950 void putStr (const char *s
) {
1951 for (; *s
; ++s
) cargador
[pos
++] = *s
;
1954 void putNum (int num
) {
1956 sprintf(buf
, "%d", num
);
1964 cargador
[pos
++] = (linenum
>>8)&0xff;
1965 cargador
[pos
++] = linenum
&0xff;
1966 // size (will be fixed later)
1967 cargador
[pos
++] = 0;
1968 cargador
[pos
++] = 0;
1972 if (linestart
>= 0) {
1973 const int size
= pos
-linestart
-4;
1974 cargador
[linestart
+2] = size
&0xff;
1975 cargador
[linestart
+3] = (size
>>8)&0xff;
1985 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
1988 while (findChunkFrom(start
, &start
, &len
)) {
1989 // :RANDOMIZE USR VAL "15619":REM:LOAD "
1990 if (cont
) { putStr(":"); cont
= 0; }
1991 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
1992 // generate chunk name
1993 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
2002 if (cont
) { putStr(":"); cont
= 0; }
2003 // RANDOMIZE USR VAL "xxx"
2004 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"");
2009 //putWord(1); // autostart line
2012 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
2013 sclWriteData(scl
, cargador
, (size_t)pos
);
2014 sclWriteByte(scl
, 0x80);
2015 sclWriteByte(scl
, 0xaa);
2016 sclWriteWord(scl
, 1);
2021 static void saveSCL (const char *basename
) {
2025 char *fname
= strprintf("%s/%s.scl", optOutputDir
, basename
);
2028 while (findChunkFrom(start
, &start
, &len
)) {
2032 if (fcount
&& optRunSCL
) fcount
+= (optRunSCL
> 0 ? 2 : 1); // +loader and boot
2034 fprintf(stderr
, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname
, fcount
);
2037 // create output file
2038 FILE *fo
= fopen(fname
, "wb");
2040 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
2043 // initialise SCL writer
2046 if (fcount
&& optRunSCL
) {
2047 // create simple boot
2048 if (optRunSCL
> 0) {
2049 const uint8_t dasboot
[] = {
2050 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"
2052 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
2053 sclWriteWord(&scl
, 1); // line number
2054 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
2055 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
2057 sclWriteByte(&scl
, 0x80);
2058 sclWriteByte(&scl
, 0xaa);
2060 sclWriteWord(&scl
, 0);
2063 saveSCLCargador(&scl
);
2068 while (findChunkFrom(start
, &start
, &len
)) {
2069 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
2070 sclStartFile(&scl
, cname
, 'C', (unsigned)start
, (unsigned)len
);
2071 sclWriteData(&scl
, memory
+(unsigned)start
, (unsigned)len
);
2075 if (sclSaveToFile(fo
, &scl
) < 0) {
2076 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
2083 printf("out: %s\n", fname
);
2084 if (fname
!= NULL
) free(fname
);
2088 ///////////////////////////////////////////////////////////////////////////////
2091 static int saveDMB (const char *fname
) {
2092 char *fn
;// = malloc(strlen(fname)+16);
2097 fn
= strprintf("%s/%s.dmb", optOutputDir
, fname
);
2098 fo
= fopen(fn
, "wb");
2100 if (!fo
) { fprintf(stderr
, "ERROR: can't write DMB file: %s.dmb\n", fname
); return -1; }
2102 while (findChunkFrom(start
, &start
, &len
)) { ++pcnt
; start
+= len
; }
2104 printf("out: %s.dmb\n", fname
);
2105 if (fwrite("DMB1", 4, 1, fo
) != 1) goto error
;
2106 if (fWriteWord(fo
, (optRunDMB
? ent
: 0), NULL
) != 0) goto error
;
2107 if (fWriteWord(fo
, clrAddr
, NULL
) != 0) goto error
;
2108 if (fWriteWord(fo
, pcnt
, NULL
) != 0) goto error
;
2111 while (findChunkFrom(start
, &start
, &len
)) {
2112 if (fWriteWord(fo
, len
, NULL
) != 0) goto error
;
2113 if (fWriteWord(fo
, start
, NULL
) != 0) goto error
;
2114 if (fwrite(memory
+start
, len
, 1, fo
) != 1) goto error
;
2122 fprintf(stderr
, "ERROR: error writing file %s.dmb!\n", fname
);
2127 ///////////////////////////////////////////////////////////////////////////////
2130 static char tapeLoaderName
[16];
2133 static void saveTapCargador (FILE *fo
) {
2135 static uint8_t cargador
[16384]; // should be enough for everyone
2136 int start
= 0, len
, pos
, f
;
2140 void putStr (const char *s
) {
2141 for (; *s
; ++s
) cargador
[pos
++] = *s
;
2144 void putNum (int num
) {
2146 sprintf(buf
, "%d", num
);
2153 cargador
[0] = 0; cargador
[1] = 10;
2154 // size (will be fixed later)
2155 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\""); // CLEAR VAL "xxx"
2157 while (findChunkFrom(start
, &start
, &len
)) {
2159 putStr(":\xef\"\"\xaf");
2163 // :RANDOMIZE USR VAL "xxx"
2164 putStr(":\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
2166 cargador
[2] = (pos
-4)&0xff;
2167 cargador
[3] = ((pos
-4)>>8)&0xff;
2169 fWriteWord(fo
, 19, NULL
); // length of header
2171 fWriteByte(fo
, 0, &bxor
); // header block
2172 fWriteByte(fo
, 0, &bxor
); // 'basic' flag
2173 if (tapeLoaderName
[0]) {
2174 for (int f
= 0; f
< 10; ++f
) fWriteByte(fo
, tapeLoaderName
[f
], &bxor
);
2176 fWriteByte(fo
, 'c', &bxor
);
2177 fWriteByte(fo
, 'a', &bxor
);
2178 fWriteByte(fo
, 'r', &bxor
);
2179 fWriteByte(fo
, 'g', &bxor
);
2180 fWriteByte(fo
, 'a', &bxor
);
2181 fWriteByte(fo
, 'd', &bxor
);
2182 fWriteByte(fo
, 'o', &bxor
);
2183 fWriteByte(fo
, 'r', &bxor
);
2184 fWriteByte(fo
, ' ', &bxor
);
2185 fWriteByte(fo
, ' ', &bxor
);
2187 fWriteWord(fo
, pos
, &bxor
); // length
2188 fWriteWord(fo
, 10, &bxor
); // start
2189 fWriteWord(fo
, pos
, &bxor
); // length2
2190 fWriteByte(fo
, bxor
, NULL
);
2192 fWriteWord(fo
, pos
+2, NULL
); // plus type and checkbyte
2194 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
2195 for (f
= 0; f
< pos
; ++f
) fWriteByte(fo
, cargador
[f
], &bxor
);
2196 fWriteByte(fo
, bxor
, NULL
);
2200 static void saveTap (const char *basename
) {
2201 char *fname
;// = malloc(strlen(basename)+16);
2203 int start
= 0, len
, f
;
2207 fname
= strprintf("%s/%s.tap", optOutputDir
, basename
);
2208 fo
= fopen(fname
, "wb");
2210 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
2211 printf("out: %s.tap\n", basename
);
2212 if (optRunTape
) saveTapCargador(fo
);
2213 while (findChunkFrom(start
, &start
, &len
)) {
2215 if (tapeLoaderName
[0]) {
2216 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2217 memcpy(blkname
, tapeLoaderName
, 10);
2219 sprintf(blkname
, "c%04X:%04X", start
, len
);
2221 //printf(" block: %s\n", blkname);
2222 fWriteWord(fo
, 19, NULL
); // length of header
2224 fWriteByte(fo
, 0, &bxor
); // header block
2225 fWriteByte(fo
, 3, &bxor
); // 'code' flag
2226 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
2227 fWriteWord(fo
, len
, &bxor
);
2228 fWriteWord(fo
, start
, &bxor
);
2229 fWriteWord(fo
, 32768, &bxor
);
2230 fWriteByte(fo
, bxor
, NULL
);
2232 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
2234 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
2235 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
2236 fWriteByte(fo
, bxor
, NULL
);
2243 ///////////////////////////////////////////////////////////////////////////////
2244 // pseudoinstructions
2246 // note that processCurrentLine() will NOT skip to the next line before
2247 // calling pseudoinstruction handler!
2248 // note that processCurrentLine() will skip current line after calling
2249 // pseudoinstruction handler!
2251 static int wasOrg
= 0;
2252 static int wasClr
= 0;
2255 // ////////////////////////////////////////////////////////////////////////// //
2256 // print message using printf-like syntax
2257 // doesn't print new line
2258 static void processPrintf (FILE *fo
, const char *fmt
) {
2259 if (!fmt
|| !fmt
[0]) return;
2260 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
2261 char *tempstr
= NULL
;
2262 size_t tempsize
= 0;
2263 char *fmtcopy
= malloc(strlen(fmt
)+1);
2264 strcpy(fmtcopy
, fmt
);
2265 char *currfmt
= fmtcopy
;
2273 char *prcs
= strchr(currfmt
, '%');
2274 if (!prcs
|| !prcs
[1]) {
2275 /* no more formatting; print the tail and exit the loop */
2276 fprintf(fo
, "%s", currfmt
);
2281 if (prcs
[1] == '%') {
2285 /* print up to `prcs` */
2286 if (prcs
> currfmt
) {
2287 size_t partlen
= (ptrdiff_t)(prcs
-currfmt
);
2288 if (partlen
+1 > tempsize
) {
2289 tempsize
= ((partlen
+8)|0xff)+1;
2290 tempstr
= realloc(tempstr
, tempsize
);
2292 memcpy(tempstr
, currfmt
, partlen
);
2293 tempstr
[partlen
] = 0;
2294 fprintf(fo
, "%s", tempstr
);
2296 currfmt
= ++prcs
; /* skip percent */
2297 if (!docheck
) continue;
2303 if (*currfmt
== '+' || *currfmt
== '-') sign
= *currfmt
++;
2304 if (sign
!= '-' && *currfmt
== '0') zerofill
= 1;
2305 while (isDigit(*currfmt
)) { width
= width
*10+((*currfmt
)-'0'); ++currfmt
; }
2306 if (width
> 256) width
= 256;
2308 if (!ftype
) break; /* oops */
2309 if (!eatComma()) fatal("out of arguments for string format");
2311 case 's': /* string */
2313 switch (strSkipSpaces(currLine
)[0]) {
2314 case '"': case '\'': /* string literal? */
2315 strarg
= getStrArg(&stlen
);
2317 default: /* expression */
2318 strarg
= getStrExprArg();
2319 stlen
= (int)strlen(strarg
);
2323 if (sign
!= '-' && stlen
< width
) {
2324 int padlen
= width
-stlen
;
2325 memset(tempbuf
, ' ', padlen
);
2326 tempbuf
[padlen
+1] = 0;
2327 fprintf(fo
, "%s", tempbuf
);
2329 fprintf(fo
, "%s", strarg
);
2331 if (sign
== '-' && stlen
< width
) {
2332 int padlen
= width
-stlen
;
2333 memset(tempbuf
, ' ', padlen
);
2334 tempbuf
[padlen
+1] = 0;
2335 fprintf(fo
, "%s", tempbuf
);
2338 case 'd': /* decimal */
2340 exprval
= getExprArg(&defined
, NULL
);
2341 if (width
&& zerofill
) {
2342 fprintf(fo
, "%0*d", width
, exprval
);
2344 fprintf(fo
, "%*d", (sign
!= '-' ? width
: -width
), exprval
);
2346 fprintf(fo
, "%d", exprval
);
2349 case 'c': /* char */
2351 exprval
= getExprArg(&defined
, NULL
);
2352 if (exprval
<= 0 || exprval
== '?' || exprval
> 255) exprval
= '?';
2354 fprintf(fo
, "%*c", (sign
!= '-' ? width
: -width
), exprval
);
2356 fprintf(fo
, "%c", exprval
);
2359 case 'u': /* unsigned */
2361 exprval
= getExprArg(&defined
, NULL
);
2362 if (width
&& zerofill
) {
2363 fprintf(fo
, "%0*u", width
, (unsigned)(exprval
&0xffff));
2365 fprintf(fo
, "%*u", (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
2367 fprintf(fo
, "%u", (unsigned)(exprval
&0xffff));
2370 case 'x': case 'X': /* hex */
2372 exprval
= getExprArg(&defined
, NULL
);
2373 if (width
&& zerofill
) {
2374 fprintf(fo
, (ftype
== 'x' ? "%0*x" : "%0*X"), width
, (unsigned)(exprval
&0xffff));
2376 fprintf(fo
, (ftype
== 'x' ? "%*x" : "%*X"), (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
2378 fprintf(fo
, (ftype
== 'x' ? "%x" : "%X"), (unsigned)(exprval
&0xffff));
2382 if (ftype
<= 0 || ftype
== 127) ftype
= '?';
2383 fatal("invalid format specifier: '%c'", ftype
);
2387 if (tempstr
) free(tempstr
);
2392 ///////////////////////////////////////////////////////////////////////////////
2395 static int piERROR (void) {
2397 char *res
= getStrArg(&len
);
2398 fprintf(stdout
, "*** USER ERROR: ");
2399 processPrintf(stdout
, res
);
2400 fputc('\n', stdout
);
2401 fatal("user error abort");
2402 return PI_SKIP_LINE
;
2406 static int piWARNING (void) {
2408 char *res
= getStrArg(&len
);
2409 fprintf(stdout
, "*** USER WARNING ");
2411 fprintf(stdout
, "at file %s, line %d: ", curSrcLine
->fname
, curSrcLine
->lineNo
);
2413 fprintf(stdout
, "somewhere in time: ");
2415 processPrintf(stdout
, res
);
2416 fputc('\n', stdout
);
2417 return PI_SKIP_LINE
;
2421 ///////////////////////////////////////////////////////////////////////////////
2424 static int piPrintfCommon (int passNo
) {
2425 if (passNo
< 0 || pass
== passNo
) {
2427 char *res
= getStrArg(&len
);
2428 processPrintf(stdout
, res
);
2429 fputc('\n', stdout
);
2431 return PI_SKIP_LINE
;
2435 static int piDISPLAYX (int passNo
, int asHex
) {
2439 char *res
= getStrArg(&len
);
2441 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
2444 int32_t v
= getExprArg(&defined
, NULL
);
2446 if (passNo
< 0 || pass
== passNo
) {
2447 if (asHex
) printf("%04X", (unsigned int)v
);
2448 else printf("%d", v
);
2451 if (!eatComma()) break;
2453 return PI_SKIP_LINE
;
2457 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
2458 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
2459 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
2460 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
2461 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
2462 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
2464 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
2465 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
2466 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
2469 ///////////////////////////////////////////////////////////////////////////////
2472 static int piORG (void) {
2474 int32_t res
= getOneExprArg(&defined
, NULL
);
2476 if (!defined
) fatal("sorry, ORG operand value must be known here");
2477 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
2478 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
2483 if (!wasClr
&& res
> 0) clrAddr
= res
-1;
2485 return PI_CONT_LINE
;
2489 static int piDISP (void) {
2491 int32_t res
= getOneExprArg(&defined
, NULL
);
2493 if (!defined
) fatal("sorry, DISP operand value must be known here");
2494 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
2495 //printf("DISP=%d\n", res);
2497 return PI_CONT_LINE
;
2501 static int piENDDISP (void) {
2502 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
2505 return PI_CONT_LINE
;
2509 static int piENT (void) {
2511 int32_t res
= getOneExprArg(&defined
, NULL
);
2513 //if (!defined) fatal("sorry, ENT operand value must be known here");
2514 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
2516 return PI_CONT_LINE
;
2520 static int piCLR (void) {
2522 int32_t res
= getOneExprArg(&defined
, NULL
);
2524 //if (!defined) fatal("sorry, CLR operand value must be known here");
2525 if (res
< 0 || res
> 65535) fatal("invalid CLR operand value: %d", res
);
2528 return PI_CONT_LINE
;
2532 static int piRESERVE (void) {
2534 int defined = 1, start;
2535 int32_t res = getExprArg(&defined, NULL);
2536 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2537 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2539 if (!eatComma()) fatal("RESERVE needs 2 args");
2540 res = getOneExprArg(&defined, NULL);
2541 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2542 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2543 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
2545 fatal("RESERVE: not yet!");
2546 return PI_CONT_LINE
;
2550 static int piALIGN (void) {
2554 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
2555 res
= getOneExprArg(&defined
, NULL
);
2556 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
2557 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
2558 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
2559 if (res
> 0 && pc
%res
!= 0) {
2565 return PI_CONT_LINE
;
2569 static int piDISPALIGN (void) {
2571 int32_t res
= getOneExprArg(&defined
, NULL
);
2573 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
2574 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
2575 if (res
> 0 && disp
%res
!= 0) {
2580 return PI_CONT_LINE
;
2584 ///////////////////////////////////////////////////////////////////////////////
2587 static int defIncr
= 0;
2590 static int piDEFINCR (void) {
2592 int32_t cnt
= getOneExprArg(&defined
, NULL
);
2594 if (!defined
) fatal("DEFINCR: increment must be defined");
2596 return PI_CONT_LINE
;
2600 static int piDEFBW (int isWord
) {
2602 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2606 char *res
= getStrArg(&len
);
2608 for (f
= 0; f
< len
; ++f
) {
2609 int32_t b
= (uint8_t)res
[f
];
2611 if (b
< -128 || b
> 255) fatal("invalid operand value: %d", b
);
2612 if (b
< 0) b
+= 256;
2616 int32_t res
= getExprArg(&defined
, &fixuptype
);
2618 if (pass
> 0 && !defined
) fatal("undefined operand");
2621 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
2622 if (res
< 0) res
+= 65536;
2624 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 2);
2628 switch (fixuptype
) {
2629 case UR_FIXUP_WORD
: /* swapped bytes */
2630 urasm_fixup_operand(NULL
, pc
, disp
, UR_FIXUP_HIBYTE
, 1);
2631 urasm_fixup_operand(NULL
, pc
, disp
+1, UR_FIXUP_LOBYTE
, 1);
2633 case UR_FIXUP_LOBYTE
:
2634 case UR_FIXUP_HIBYTE
:
2635 warningMsg("non-word fixup for reversed word; wtf?!");
2642 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
2643 if (fixuptype
!= UR_FIXUP_NONE
) {
2644 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
2645 urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
2647 if (res
< 0) res
+= 256;
2651 if (!eatComma()) break;
2653 return PI_CONT_LINE
;
2656 static int piDEFB (void) { return piDEFBW(0); }
2657 static int piDEFW (void) { return piDEFBW(1); }
2658 static int piDEFR (void) { return piDEFBW(2); }
2661 static int piDEFS (void) {
2664 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2665 int32_t res
= getExprArg(&defined
, &fixuptype
);
2667 if (pass
> 0 && !defined
) fatal("undefined operand");
2668 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
2669 if (*currLine
&& currLine
[0] == ',') {
2671 bt
= getExprArg(&defined
, NULL
);
2672 if (pass
> 0 && !defined
) fatal("undefined operand");
2674 if (bt
< -128 || bt
> 255) fatal("invalid operand value: %d", res
);
2675 if (bt
< 0) bt
+= 256;
2676 if (fixuptype
!= UR_FIXUP_NONE
) {
2677 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
2679 for (f
= 0; f
< res
; ++f
) {
2680 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
2684 pc
+= res
; disp
+= res
;
2686 if (!eatComma()) break;
2688 return PI_CONT_LINE
;
2692 /* bit 0: put '\0' */
2693 /* bit 1: set bit 7 of last byte */
2694 /* bit 2: put length */
2695 static int piDEFSTR (int type
) {
2699 char *res
= getStrArg(&len
);
2702 if (len
> 255) fatal("string too long");
2705 for (f
= 0; f
< len
; ++f
) {
2706 uint8_t b
= (uint8_t)res
[f
];
2708 if ((type
&0x02) && f
== len
-1) b
|= 0x80;
2711 if (type
&0x01) emitByte(0);
2714 int32_t v
= getExprArg(&defined
, NULL
);
2716 if (pass
> 0 && !defined
) fatal("undefined expression");
2717 if (!defined
) v
= 0; else v
+= defIncr
;
2718 if (v
< -128 || v
> 255) fatal("invalid operand value: %d", v
);
2719 if (v
< 0) v
+= 256;
2722 if (!eatComma()) break;
2724 return PI_CONT_LINE
;
2728 static int piDEFM (void) { return piDEFSTR(0x00); }
2729 static int piDEFZ (void) { return piDEFSTR(0x01); }
2730 static int piDEFX (void) { return piDEFSTR(0x02); }
2731 static int piDEFC (void) { return piDEFSTR(0x04); }
2734 ///////////////////////////////////////////////////////////////////////////////
2737 /* INCBIN "name"[,maxlen] */
2738 static int piINCBIN (void) {
2744 char *args
= currLine
;
2746 if (!currLine
[0]) fatal("INCBIN without file name");
2750 } else if (currLine
[0] == '<') {
2757 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
2758 if (!fn
[0]) fatal("INCBIN: empty file name");
2759 memmove(currLine
, args
, strlen(args
)+1);
2761 if (currLine
[0] == ',') {
2764 maxlen
= getOneExprArg(&defined
, NULL
);
2765 if (!defined
) fatal("INCBIN: undefined maxlen");
2766 if (maxlen
< 1) return 1; // nothing to do
2769 char *fname
= createIncludeName(fn
, system
, NULL
);
2770 fl
= fopen(fname
, "rb");
2771 if (!fl
) fatal("INCBIN: file not found: '%s'", fname
);
2772 while (maxlen
-- > 0) {
2773 int res
= fread(&bt
, 1, 1, fl
);
2775 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: '%s'", fname
); }
2780 return PI_SKIP_LINE
;
2784 ///////////////////////////////////////////////////////////////////////////////
2787 /* INCLUDE "name" */
2788 static int piINCLUDE (void) {
2791 char *args
= currLine
;
2793 if (!currLine
[0]) fatal("INCLUDE without file name");
2797 } else if (currLine
[0] == '<') {
2804 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
2805 if (!fn
[0]) fatal("INCLUDE: empty file name");
2807 if (asmTextInclude(fn
, system
) != 0) fatal("INCLUDE: some shit happens!");
2808 return PI_SKIP_LINE
;
2812 ///////////////////////////////////////////////////////////////////////////////
2813 // MODULE, ENDMODULE
2815 static int piENDMODULE (void) {
2816 if (!curModule
) fatal("ENDMODULE without MODULE");
2818 char *mn
= getOneLabelArg();
2819 if (strcmp(mn
, curModule
->name
)) fatal("invalid module name in ENDMODULE: %s", mn
);
2822 return PI_SKIP_LINE
;
2826 static int piMODULE (void) {
2829 SourceLine
*ol
= curSrcLine
;
2832 if (curModule
) fatal("no nested modules allowed");
2833 mn
= getOneLabelArg();
2834 if (!urasm_is_valid_name(mn
)) fatal("invalid module name: %s", mn
);
2835 mi
= moduleFind(mn
);
2836 //printf("[%s] %p\n", mn, mi);
2839 if (strcmp(mi
->fname
, curSrcLine
->fname
)) fatal("duplicate module definition; previous was in %s", mi
->fname
);
2841 nextSrcLine(); /* skip "MODULE" line */
2842 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
2844 fatal("no ENDMODULE");
2846 if (inum
== 0) fatal("no nested modules allowed");
2848 //skipInstruction(); //k8:wtf?!
2849 return piENDMODULE();
2852 mi
= moduleAdd(mn
, curSrcLine
->fname
);
2856 return PI_SKIP_LINE
;
2861 static int piMODEL (void) {
2862 char *mn
= getOneIdArgLo();
2863 if (strSkipSpaces(currLine
)[0]) fatal("only one model name expected");
2864 if (strcmp(mn
, "z80") == 0) urasm_allow_zxnext
= 0;
2865 else if (strcmp(mn
, "z80a") == 0) urasm_allow_zxnext
= 0;
2866 else if (strcmp(mn
, "z80n") == 0) urasm_allow_zxnext
= 1;
2867 else if (strcmp(mn
, "z80next") == 0) urasm_allow_zxnext
= 1;
2868 else if (strcmp(mn
, "zxnext") == 0) urasm_allow_zxnext
= 1;
2869 else fatal("invalid model name: %s", mn
);
2870 return PI_SKIP_LINE
;
2874 ///////////////////////////////////////////////////////////////////////////////
2877 static int piEDUP (void) {
2878 fatal("EDUP without DUP");
2880 return PI_SKIP_LINE
;
2884 static int piDUP (void) {
2885 int defined
= 1, dupCnt
= 1, inum
;
2886 SourceLine
*stline
, *eline
= NULL
;
2887 int32_t cnt
= getOneExprArg(&defined
, NULL
);
2889 if (!defined
) fatal("DUP: counter must be defined");
2890 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
2891 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
2892 // now find corresponding EDUP
2893 // note that we should skip nested DUPs
2894 nextSrcLine(); // skip ourself
2895 stline
= curSrcLine
;
2896 while (curSrcLine
) {
2897 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "EDUP", NULL
))) break;
2898 // ok, we found something; what is it?
2902 nextSrcLine(); // skip DUP
2905 if (--dupCnt
== 0) {
2910 nextSrcLine(); // skip EDUP
2913 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
2914 // now repeat that lines
2916 setCurSrcLine(stline
);
2917 while (curSrcLine
!= eline
) processCurrentLine();
2919 return PI_SKIP_LINE
;
2923 ///////////////////////////////////////////////////////////////////////////////
2926 static int ifCount
= 0;
2930 // -1: error (should not happen)
2931 // 0: successfully complete
2932 // 1: stopped *AT* "ELSE"
2933 // 2: stopped *AT* "ELSEIF"
2934 static int ifSkipToEndIfOrElse (int wholeBody
) {
2935 int inum
, wasElse
= 0;
2938 while (curSrcLine
) {
2939 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL
))) break;
2943 nextSrcLine(); // skip IF
2944 ifSkipToEndIfOrElse(1); // and recurse
2945 nextSrcLine(); // skip ENDIF
2948 if (wasElse
) fatal("duplicate ELSE");
2949 if (!wholeBody
) return 1;
2951 nextSrcLine(); // skip ELSE
2952 break; // and continue
2954 return 0; // and exit
2957 if (wasElse
) fatal("ELSEIF in ELSE");
2958 if (!wholeBody
) return 2;
2959 nextSrcLine(); // skip ELSEIF
2960 break; // and continue
2962 // skip it as a whole
2963 nextSrcLine(); // skip MACRO
2966 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) { setCurSrcLine(oline
); fatal("invalid MACRO"); }
2967 if (inum
== 1) break;
2968 fatal("invalid nested MACRO");
2970 nextSrcLine(); // skip ENDM
2973 fatal("unexpected ENDM");
2976 fatal("IF without ENDIF");
2981 static int piENDIF (void) {
2982 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2983 if (--ifCount
< 0) fatal("ENDIF without IF");
2985 return PI_SKIP_LINE
;
2989 static int piELSE (void) {
2990 if (--ifCount
< 0) fatal("ELSE without IF");
2991 nextSrcLine(); // skip ELSE
2992 ifSkipToEndIfOrElse(1);
2993 return PI_SKIP_LINE
;
2996 static int piELSEIF (void) {
2997 if (--ifCount
< 0) fatal("ELSEIF without IF");
2998 nextSrcLine(); // skip ELSEIF
2999 ifSkipToEndIfOrElse(1);
3000 return PI_SKIP_LINE
;
3004 static int piELSEIFX (void) {
3005 if (--ifCount
< 0) fatal("ELSEIFX without IF");
3006 nextSrcLine(); // skip ELSEIFX
3007 ifSkipToEndIfOrElse(1);
3008 return PI_SKIP_LINE
;
3012 static int piIFAll (int isIfX
) {
3014 int ooo
= lblOptMakeU2
;
3015 lblOptMakeU2
= (isIfX
? 1 : 0);
3016 int32_t cond
= getOneExprArg(&defined
, NULL
);
3020 if (!isIfX
) fatal("IF: condition must be defined");
3021 cond
= 0; // for IFX: 0 if there is any undefined label
3024 // ok, do it until ELSE/ELSEIF/ENDIF
3026 return PI_SKIP_LINE
;
3032 nextSrcLine(); // skip last instruction
3033 // skip until ELSE/ELSEIF/ENDIF
3034 r
= ifSkipToEndIfOrElse(0);
3035 /*!fprintf(stderr, "ELSEIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
3036 if (r
== 0) break; // ENDIF
3037 if (r
== 1) { ++ifCount
; break; } // ELSE
3038 // ELSEIF, do condition
3039 args
= strIsCommand("ELSEIF", currLine
);
3043 if ((args
= strIsCommand("ELSEIFX", currLine
)) == NULL
) fatal("internal error in conditionals"); // the thing that should not be
3046 memmove(currLine
, args
, strlen(args
)+1);
3047 /*!fprintf(stderr, "ELSEIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
3048 cond
= getOneExprArg(&defined
, NULL
);
3049 /*!fprintf(stderr, "ELSEIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0), cond);*/
3051 if (!isIfX
) fatal("ELSEIF: condition must be defined");
3052 cond
= 0; // for IFX: 0 if there is any undefined label
3054 if (cond
) { ++ifCount
; break; } // condition is true
3056 return PI_SKIP_LINE
;
3060 static int piIF (void) { return piIFAll(0); }
3061 static int piIFX (void) { return piIFAll(1); }
3064 ///////////////////////////////////////////////////////////////////////////////
3066 ///////////////////////////////////////////////////////////////////////////////
3068 what i did with MACRO is the brain-damaged cheating all the way.
3070 first, i will collect the MACRO body and remember it (removing it
3071 from the main source code, so second pass will not see it).
3073 second, when the macro is used, i will:
3074 * insert the whole macro body in place (label resolver will
3075 fix "..lbl" labels for us)
3076 * let the asm play with it
3078 this is not the best scheme, but it is fairly simple and it works.
3081 // will be called when parser encounters term starting with '=' or '*'
3082 // first term char will not be skipped
3083 // must return pointer to the first char after expression end
3084 static const char *getValueCB (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3088 if (curmacro
== NULL
) fatal("'=' outside of macro");
3089 if (*expr
++ != '=') fatal("'=' expected!");
3091 expr
= strSkipSpaces(expr
);
3093 if (isAlphaDigit(*expr
) || *expr
== '_') {
3094 if (p
-name
> 250) fatal("id too long");
3102 expr
= strSkipSpaces(expr
);
3103 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
3104 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
3107 int l
= (int)strlen(curmacro
->argvals
[f
]);
3109 urasm_exprval_init(&v
);
3110 expr
= urasm_expr_ex(&v
, expr
, addr
, &donteval
, defined
, error
);
3111 if (*error
) return expr
;
3112 if (expr
== NULL
|| *expr
!= ']' || v
.str
!= NULL
) { *error
= UR_EXPRERR_FUNC
; return expr
; }
3114 if (v
.val
< 0) v
.val
+= l
;
3115 if (v
.val
< 0 || v
.val
>= l
) {
3118 res
->val
= (unsigned char)(curmacro
->argvals
[f
][v
.val
]);
3122 urasm_expr_ex(res
, curmacro
->argvals
[f
], addr
, &donteval
, defined
, error
);
3128 fatal("unknown macro variable: '%s'", name
);
3132 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
3133 // opr starts with '=' (invariant)
3134 static int expandCB (char *opr
, int oprlen
) {
3135 char name
[257], *p
= name
, *op
= opr
;
3137 if (curmacro
== NULL
) fatal("'=' outside of macro");
3138 if (*op
++ != '=') fatal("'=' expected!"); // just in case
3139 //fprintf(stderr, "expand: [%s]\n", opr);
3141 if (!isAlpha(op
[0])) return 0; // nothing to do
3143 // copy argument name
3145 if (isAlphaDigit(*op
) || *op
== '_') {
3146 if (p
-name
> 250) fatal("id too long");
3154 // expand argument? we only need to expand `=arg[n]`
3155 op
= strSkipSpaces(op
);
3156 if (op
[0] != '[') return 0;
3158 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
3159 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
3160 if (*op
!= '[') abort(); // assertion, just in case
3161 // replace argument with character, or with literal value
3162 // used for `=regpair[0]` or `=regpair[]`
3165 const int l
= (int)strlen(curmacro
->argvals
[f
]);
3166 const int lleft
= (int)strlen(op
);
3167 char *tmp
= malloc(l
+lleft
+8);
3168 snprintf(tmp
, l
+lleft
+8, "%s%s", curmacro
->argvals
[f
], op
);
3169 if ((int)strlen(tmp
) > oprlen
) {
3178 int error
= 0, defined
= 1, donteval
= 0;
3180 urasm_exprval_init(&v
);
3181 op
= (char *)urasm_expr_ex(&v
, op
, pc
, &donteval
, &defined
, &error
);
3182 if (error
) return -1;
3183 // result should be a number
3184 if (op
== NULL
|| *op
!= ']' || v
.str
!= NULL
) return -1;
3186 // it is guaranteed to have more than one char in opr
3187 // so we can simply put char from argument value, and remove other expression chars
3188 const int l
= (int)strlen(curmacro
->argvals
[f
]);
3189 if (v
.val
< 0) v
.val
+= l
; // negative: indexing from the end
3190 if (v
.val
< 0 || v
.val
>= l
) fatal("index %d is out of bounds for macro argument '%s'", v
.val
, curmacro
->mac
->argnames
[f
]);
3192 opr
[0] = curmacro
->argvals
[f
][v
.val
];
3193 // remove other chars
3194 memmove(opr
+1, op
, strlen(op
)+1);
3200 fatal("unknown macro variable: '%s'", name
);
3204 // main macro expander
3205 static void processMacro (MacroDef
*mc
) {
3206 SourceLine
*oldcurline
= curSrcLine
;
3209 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
3210 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
3212 // parse macro arguments
3214 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) cm
.argvals
[f
] = NULL
;
3218 // do we have more arguments?
3219 if (!currLine
[0]) break;
3220 // check for argument name (this is purely cosmetic thing)
3221 // use like this: "mcall :argname=[value]" (value may be omited for default)
3222 if (currLine
[0] == ':' && isAlpha(currLine
[1])) {
3224 while (isAlphaDigit(currLine
[pos
]) || currLine
[pos
] == '_') ++pos
;
3226 const char svch
= currLine
[pos
];
3228 if (strcasecmp(currLine
+1, mc
->argnames
[currArg
]) != 0) {
3229 // out-of-order, find proper argument and rewind to it
3231 for (int c
= 0; c
< mc
->argc
; ++c
) {
3232 if (strcasecmp(currLine
+1, mc
->argnames
[c
]) == 0) {
3237 if (currArg
< 0) fatal("macro '%s' has no argument named '%s'", mc
->name
, currLine
+1);
3239 currLine
[pos
] = svch
;
3240 // remove argument name
3241 memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
3244 if (currLine
[0] != '=') fatal("expected '=' after argument name");
3246 memmove(currLine
, currLine
+1, strlen(currLine
));
3250 if (currArg
>= mc
->argc
) fatal("too many arguments to macro '%s'", mc
->name
);
3251 // check for default value
3252 if (currLine
[0] == ',') {
3253 if (mc
->argdefaults
[currArg
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[currArg
]);
3254 cm
.argvals
[currArg
] = strdup(mc
->argdefaults
[currArg
]);
3255 memmove(currLine
, currLine
+1, strlen(currLine
));
3257 // skip argument (so we will know its length, and will be able to copy it)
3258 char *e
= skipMacroArg(currLine
);
3259 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
3260 const char ech
= *e
;
3262 if (ech
== ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc
->name
);
3263 if (ech
!= ',') fatal("invalid invocation of macro '%s'", mc
->name
);
3266 cm
.argvals
[currArg
] = strdup(currLine
);
3267 // strip trailing spaces
3268 strTrimRight(cm
.argvals
[currArg
]);
3271 memmove(currLine
, e
+1, strlen(e
));
3278 // check for line end
3280 if (currLine
[0]) fatal("invalid macro invocation");
3282 // setup default argument values
3283 for (int f
= 0; f
< mc
->argc
; ++f
) {
3284 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
3285 if (cm
.argvals
[f
]) continue;
3286 if (mc
->argdefaults
[f
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[f
]);
3287 cm
.argvals
[f
] = strdup(mc
->argdefaults
[f
]);
3288 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
3291 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
3293 // insert macro code into the source
3294 setCurSrcLine(mc
->lines
);
3297 while (curSrcLine
!= NULL
) {
3298 //fprintf(stderr, "*[%s] (%d)\n", curSrcLine->line, curSrcLine->lineNo);
3299 processCurrentLine();
3302 setCurSrcLine(oldcurline
);
3308 static int piMACRO (void) {
3311 char *argdefaults
[MAX_MACRO_ARGS
];
3312 char *argnames
[MAX_MACRO_ARGS
];
3313 SourceLine
*stline
, *eline
= NULL
;
3316 name
= strdup(getLabelArg(0));
3317 //fprintf(stderr, "[%s]\n", name);
3318 if (findMacro(name
) != NULL
) fatal("duplicate MACRO definition: '%s'", name
);
3319 if (currLine
[0] == ',') memmove(currLine
, currLine
+1, strlen(currLine
));
3322 while (currLine
[0]) {
3323 if (argc
>= MAX_MACRO_ARGS
) fatal("too many arguments in MACRO");
3324 if (!isAlpha(currLine
[0])) fatal("invalid MACRO definition");
3325 argnames
[argc
] = strdup(getLabelArg(0));
3326 if (currLine
[0] == '=') {
3328 char *e
= strchr(currLine
, ','), tch
;
3330 if (e
== NULL
) e
= currLine
+strlen(currLine
);
3333 argdefaults
[argc
] = strdup(currLine
+1);
3335 memmove(currLine
, e
, strlen(e
)+1);
3337 argdefaults
[argc
] = NULL
;
3340 if (currLine
[0] == ',') {
3341 memmove(currLine
, currLine
+1, strlen(currLine
));
3344 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
3348 // now find corresponding ENDM
3349 // note that we should skip nested DUPs
3350 stline
= curSrcLine
;
3351 nextSrcLine(); // skip ourself
3352 while (curSrcLine
) {
3355 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) break;
3356 // ok, we found something; what is it?
3359 fatal("no nested MACROs yet");
3365 nextSrcLine(); // skip ENDM
3369 if (!eline
) { setCurSrcLine(stline
); fatal("no ENDM"); }
3371 if ((mc
= calloc(1, sizeof(*mc
))) == NULL
) fatal("out of memory");
3374 for (int f
= 0; f
< argc
; ++f
) { mc
->argdefaults
[f
] = argdefaults
[f
]; mc
->argnames
[f
] = argnames
[f
]; }
3377 mc
->lines
= stline
->next
;
3378 stline
->next
= curSrcLine
;
3379 stline
->line
[0] = 0;
3383 setCurSrcLine(stline
);
3384 return PI_SKIP_LINE
;
3388 static int piENDM (void) {
3389 fatal("ENDM without MACRO");
3390 return PI_SKIP_LINE
;
3394 ///////////////////////////////////////////////////////////////////////////////
3396 static int optWriteType
= 't';
3397 static int optWTChanged
= 0;
3400 static void piTapParseLoaderName (void) {
3403 if (!isStrArg()) fatal("loader name expected");
3404 char *fn
= getStrArg(&len
);
3405 if (len
> 10) fatal("loader name too long");
3406 memset(tapeLoaderName
, ' ', 10);
3407 for (int f
= 0; f
< len
; ++f
) tapeLoaderName
[f
] = fn
[f
];
3412 static int piMATHMODE (void) {
3413 char *name
= getOneLabelArg();
3414 if (strcasecmp(name
, "OLD") == 0) urasm_use_old_priorities
= 1;
3415 else if (strcasecmp(name
, "NEW") == 0) urasm_use_old_priorities
= 0;
3416 else fatal("invalid math mode; NEW or OLD expected");
3417 return PI_SKIP_LINE
;
3421 static int piDEFFMT (void) {
3426 name
= getStrArg(&len
);
3428 name
= getLabelArg(1);
3430 if (optWTChanged
) return 1;
3432 optRunDMB
= optRunTape
= optRunSCL
= 0;
3435 if (!strcasecmp(name
, "SNA") || !strcasecmp(name
, "RUNSNA") || !strcasecmp(name
, "SNARUN")) {
3437 if (currLine
[0]) fatal("too many expressions");
3438 return PI_SKIP_LINE
;
3440 if (!strcasecmp(name
, "TAP") || !strcasecmp(name
, "TAPE")) {
3442 piTapParseLoaderName();
3443 return PI_SKIP_LINE
;
3445 if (!strcasecmp(name
, "RUNTAP") || !strcasecmp(name
, "RUNTAPE") || !strcasecmp(name
, "TAPERUN")) {
3448 piTapParseLoaderName();
3449 return PI_SKIP_LINE
;
3451 if (!strcasecmp(name
, "BIN") || !strcasecmp(name
, "RAW")) {
3453 if (currLine
[0]) fatal("too many expressions");
3454 return PI_SKIP_LINE
;
3456 if (!strcasecmp(name
, "DMB") || !strcasecmp(name
, "RUNDMB") || !strcasecmp(name
, "DMBRUN")) {
3457 optRunDMB
= (name
[3] != 0);
3459 if (currLine
[0]) fatal("too many expressions");
3460 return PI_SKIP_LINE
;
3462 if (!strcasecmp(name
, "NONE") || !strcasecmp(name
, "NOTHING")) {
3464 if (currLine
[0]) fatal("too many expressions");
3465 return PI_SKIP_LINE
;
3467 if (!strcasecmp(name
, "SCL") || !strcasecmp(name
, "RUNSCL") || !strcasecmp(name
, "SCLRUN")) {
3469 optRunSCL
= (name
[3] != 0 ? -1 : 0); /* no boot */
3470 piTapParseLoaderName();
3471 return PI_SKIP_LINE
;
3473 if (!strcasecmp(name
, "SCLBOOT") || !strcasecmp(name
, "BOOTSCL")) {
3475 optRunSCL
= 1; /* with boot */
3476 piTapParseLoaderName();
3477 return PI_SKIP_LINE
;
3479 fatal("invalid default output type: %s", name
);
3483 ///////////////////////////////////////////////////////////////////////////////
3486 static void processCurrentLine (void) {
3487 if (!curSrcLine
) return; // do nothing
3489 if (asmMode
== AMODE_FWORD
) {
3490 processForthWordLine();
3491 nextSrcLine(); // skip it
3496 char *str
, *ee
, name
[66];
3501 removeSpacesAndColons();
3502 // skip spaces and ':'
3503 if (!currLine
[0]) { nextSrcLine(); break; }
3504 // try to find and process command
3505 str
= currLine
; //while (*str && isSpace(*str)) ++str; // skip spaces
3507 for (ee
= str
; *ee
&& !isSpace(*ee
) && *ee
!= '"' && *ee
!= '\'' && *ee
!= ',' && *ee
!= ':'; ++ee
) {}
3508 // get command, if any
3509 if (ee
!= str
&& ee
-str
<= 64) {
3511 memset(name
, 0, sizeof(name
));
3512 memmove(name
, str
, ee
-str
);
3513 /* known command? */
3514 op
= urFindOp(name
);
3517 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
3518 memmove(currLine
, str
, strlen(str
)+1);
3520 nextSrcLine(); // skip it
3526 if ((mc
= findMacro(name
)) != NULL
) {
3528 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
3529 memmove(currLine
, str
, strlen(str
)+1);
3531 /* only one macro per line! */
3536 len
= urasm_opasm(currLine
, pc
, disp
, &errpos
);
3537 if (len
< 0) fatalUrLib(len
);
3540 if (len
>= 0 && errpos
) {
3541 memmove(currLine
, errpos
+1, strlen(errpos
));
3543 nextSrcLine(); // skip it
3550 ///////////////////////////////////////////////////////////////////////////////
3551 // forth instructions
3553 static uint16_t forthLatest
= 0;
3554 static const char *forthCurrWord
= NULL
;
3557 typedef struct ForthWordT
{
3558 char *name
; // uppercased
3562 struct ForthWordT
*next
;
3565 static ForthWord
*forthWordList
= NULL
;
3566 static ForthWord
*forthWordListTail
= NULL
;
3568 static ForthWord
*findForthWord (const char *name
) {
3569 if (!name
) name
= "";
3570 for (ForthWord
*w
= forthWordList
; w
; w
= w
->next
) {
3571 if (strcasecmp(name
, w
->name
) == 0) return w
;
3577 static ForthWord
*addForthWord (const char *name
) {
3578 if (!name
) name
= "";
3579 ForthWord
*nw
= findForthWord(name
);
3581 if (nw
) fatal("duplicate forth word: '%s'", name
);
3582 nw
= malloc(sizeof(ForthWord
));
3583 nw
->name
= strdup(name
);
3584 for (char *s
= nw
->name
; *s
; ++s
) {
3585 if (s
[0] >= 'a' && s
[0] <= 'z') s
[0] = s
[0]-'a'+'A';
3591 if (forthWordListTail
) forthWordListTail
->next
= nw
; else forthWordList
= nw
;
3592 forthWordListTail
= nw
;
3594 if (!nw
) fatal("internal compiler error");
3609 ;; bit 5: SMUDGE flag (=1: word definition isn't finished)
3610 ;; bit 6: IMMEDIATE flag (=1: true)
3612 ;; the last byte of the name always has bit 7 set
3614 static ForthWord
*forthWordHead (const char *name
, int imm
) {
3615 ForthWord
*nw
= addForthWord(name
);
3616 if (!name
) name
= "";
3617 const uint16_t lt
= disp
;
3618 //fprintf(stderr, "#%04X: fw=<%s>\n", lt, name);
3622 if (nw
->nfa
!= disp
) fatal("forth word `%s`: orig NFA=#%04X; pass1 NFA=#%04X\n", name
, nw
->nfa
, disp
);
3623 //fprintf(stderr, "#%04X: fw=<%s>\n", lt, name);
3625 size_t nlen
= strlen(name
);
3626 if (nlen
== 0) nlen
= 1;
3627 if (nlen
> 31) fatal("forth word name too long: '%s'", name
);
3628 // word length and flags (nfa)
3629 emitByte((nlen
&0xff)|0x80u
|(imm
? 0x40u
: 0x00u
));
3631 uint8_t b
= (uint8_t)(name
[0]&0xffu
);
3632 if (b
>= 'a' && b
<= 'z') b
= b
-'a'+'A';
3633 if (nlen
== 1) b
|= 0x80u
;
3639 emitWord(forthLatest
);
3642 //fprintf(stderr, "#%04X: fw=<%s>\n", nw->cfa, nw->name);
3647 /* returns static buffer */
3649 static char *forthPeekWord (void) {
3650 static char buf[256];
3652 while (isSpace(currLine[pos])) ++pos;
3654 while (currLine[pos] && !isSpace(currLine[pos])) {
3655 if (bp >= sizeof(buf)-2) fatal("forth word too long");
3656 buf[bp++] = currLine[pos++];
3659 return (buf[0] ? buf : NULL);
3664 /* returns static buffer */
3665 static char *forthGetWord (void) {
3666 static char buf
[256];
3668 while (isSpace(currLine
[pos
])) ++pos
;
3670 while (currLine
[pos
] && !isSpace(currLine
[pos
])) {
3671 if (bp
>= sizeof(buf
)-2) fatal("forth word too long");
3672 buf
[bp
++] = currLine
[pos
++];
3675 while (isSpace(currLine
[pos
])) ++pos
;
3676 if (pos
> 0) memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
3677 return (buf
[0] ? buf
: NULL
);
3681 static void forthEmitLabel (const char *name
) {
3682 UrLabelInfo
*lbl
= urFindLabel(name
);
3684 if (pass
!= 0) fatal("using undefined label '%s'", name
);
3685 lbl
= urAddLabel(name
);
3686 //fprintf(stderr, "****** <%s>\n", lbl->name);
3687 lbl
->type
= (lblOptMakeU2
? -42 : -1);
3689 lbl
->refLine
= curSrcLine
->lineNo
;
3690 lbl
->refFile
= strdup(curSrcLine
->fname
);
3692 emitWord(lbl
->value
);
3696 static void forthDoConstVar (const char *lbl
) {
3697 const char *wname
= forthGetWord();
3698 if (!wname
) fatal("forth word name expected");
3699 /*ForthWord *nw =*/ forthWordHead(wname
, 0);
3701 forthEmitLabel(lbl
);
3703 int defined
= 1, addr
= 0;
3704 int32_t res
= getOneExprArg(&defined
, &addr
);
3705 if (strcmp(lbl
, "_do2var") != 0) {
3706 if (res
< 0) res
= 65536+res
;
3707 emitWord(res
&0xffffU
);
3710 emitWord(res
&0xffffU
);
3711 emitWord((res
>>16)&0xffffU
);
3713 //fprintf(stderr, "FORTH CONST/VAR: '%s' = #%04X\n", nw->name, res);
3718 static int piForthConst (void) {
3719 forthDoConstVar("_doconst");
3720 return PI_SKIP_LINE
;
3724 static int piForthVar (void) {
3725 forthDoConstVar("_dovar");
3726 return PI_SKIP_LINE
;
3730 static int piForthDVar (void) {
3731 forthDoConstVar("_do2var");
3732 return PI_SKIP_LINE
;
3736 static int piForthUser (void) {
3737 forthDoConstVar("_douser");
3738 return PI_SKIP_LINE
;
3742 static int piForthCodeWord (void) {
3743 if (asmMode
!= AMODE_NORMAL
) fatal("invalid forth define");
3744 asmMode
= AMODE_FCODE
;
3745 const char *wname
= forthGetWord();
3746 if (!wname
) fatal("forth word name expected");
3747 ForthWord
*nw
= forthWordHead(wname
, 0);
3749 emitWord((disp
+2)&0xffffU
);
3750 wname
= forthGetWord();
3752 if (strcasecmp(wname
, "FBRANCH") == 0) nw
->isbranch
= 1; else fatal("end of line expected");
3755 //fprintf(stderr, "FORTH CODE WORD: '%s'\n", nw->name);
3756 forthCurrWord
= nw
->name
;
3757 return PI_SKIP_LINE
;
3761 static int piForthCodeWordEnd (void) {
3762 if (asmMode
!= AMODE_FCODE
|| !forthCurrWord
) fatal("invalid forth define");
3763 asmMode
= AMODE_NORMAL
;
3764 const char *wname
= forthGetWord();
3765 if (!wname
) fatal("forth word name expected");
3766 if (!forthWordListTail
) fatal("end of unknown forth code word");
3767 if (strcasecmp(wname
, forthCurrWord
) != 0) fatal("end of forth code word '%s', but expected '%s'", wname
, forthWordListTail
->name
);
3769 forthCurrWord
= NULL
;
3770 return PI_SKIP_LINE
;
3774 static int piForthWord (void) {
3775 if (asmMode
!= AMODE_NORMAL
) fatal("invalid forth define");
3776 asmMode
= AMODE_FWORD
;
3777 const char *wname
= forthGetWord();
3778 if (!wname
) fatal("forth word name expected");
3779 if (strcmp(wname
, "~") == 0) wname
= "";
3780 char *fwn
= strdup(wname
);
3781 wname
= forthGetWord();
3783 if (wname
&& strcasecmp(wname
, "IMM") == 0) imm
= 1;
3784 ForthWord
*nw
= forthWordHead(fwn
, imm
);
3787 forthEmitLabel("_doforth");
3788 wname
= forthGetWord();
3790 if (strcasecmp(wname
, "FBRANCH") == 0) nw
->isbranch
= 1; else fatal("end of line expected");
3793 //fprintf(stderr, "FORTH WORD: '%s'\n", nw->name);
3794 forthCurrWord
= nw
->name
;
3795 return PI_SKIP_LINE
;
3799 static int piForthWordDoes (void) {
3800 if (asmMode
!= AMODE_NORMAL
) fatal("invalid forth define");
3801 // get forth word name
3802 const char *wname
= forthGetWord();
3803 if (!wname
) fatal("forth word name expected");
3804 if (strcmp(wname
, "~") == 0) wname
= "";
3805 char *fwn
= strdup(wname
);
3806 // get does label name
3807 wname
= forthGetWord();
3808 if (!wname
) fatal("does label expected");
3809 char *dlb
= strdup(wname
);
3811 wname
= forthGetWord();
3813 if (wname
&& strcasecmp(wname
, "IMM") == 0) imm
= 1;
3815 /*ForthWord *nw =*/ forthWordHead(fwn
, imm
);
3818 forthEmitLabel("_dodoes");
3820 forthEmitLabel(dlb
);
3823 //fprintf(stderr, "FORTH DOES WORD: '%s'\n", nw->name);
3824 return PI_SKIP_LINE
;
3828 static int piForthWordEnd (void) {
3829 if (asmMode
!= AMODE_FWORD
|| !forthCurrWord
) fatal("invalid forth define");
3830 asmMode
= AMODE_NORMAL
;
3831 const char *wname
= forthGetWord();
3832 if (!wname
) fatal("forth word name expected");
3833 if (!forthWordListTail
) fatal("end of unknown forth code word");
3834 if (strcasecmp(wname
, forthCurrWord
) != 0) fatal("end of forth code word '%s', but expected '%s'", wname
, forthWordListTail
->name
);
3836 forthCurrWord
= NULL
;
3837 return PI_SKIP_LINE
;
3841 static int isStartsWithForthCmd (void) {
3843 currLine
[0] == '$' &&
3844 toUpper(currLine
[1]) == 'F' &&
3845 toUpper(currLine
[2]) == 'O' &&
3846 toUpper(currLine
[3]) == 'R' &&
3847 toUpper(currLine
[4]) == 'T' &&
3848 toUpper(currLine
[5]) == 'H';
3854 static int forthIsBranchWord (const char *wname) {
3855 if (!wname || !wname[0]) return 0;
3856 if (strcasestr(wname, "BRANCH")) return 1;
3857 if (strcasestr(wname, "LOOP)")) return 1;
3863 static void processForthWordLine (void) {
3864 // check for "$FORTH..."
3865 if (isStartsWithForthCmd()) {
3866 const char *w
= forthGetWord();
3867 if (!w
) fatal("wtf?!");
3868 if (asmMode
!= AMODE_FWORD
) fatal("wtf1?!");
3869 if (strcasecmp(w
, "$FORTH_END_WORD") != 0) fatal("invalid forth instruction");
3871 if (!w
) fatal("forth word name expected");
3872 if (strcmp(w
, "~") == 0) w
= "";
3873 asmMode
= AMODE_NORMAL
;
3874 if (!forthCurrWord
) fatal("end of unknown forth code word");
3875 if (strcasecmp(w
, forthCurrWord
) != 0) fatal("end of forth code word '%s', but expected '%s'", w
, forthWordListTail
->name
);
3877 forthCurrWord
= NULL
;
3880 // check for a label
3881 if (isAlpha(currLine
[0]) || currLine
[0] == '_') {
3883 while (currLine
[lend
] && currLine
[lend
] != ':') {
3884 //fprintf(stderr, "lend=%u (%c)\n", lend, currLine[lend]);
3885 if (isSpace(currLine
[lend
])) fatal("invalid label");
3888 if (lend
> 64) fatal("label too long");
3889 //fprintf(stderr, "lend=%u (%c)\n", lend, currLine[lend]);
3890 if (currLine
[lend
] != ':') fatal("label expected");
3892 memcpy(nn
, currLine
, lend
);
3895 memmove(currLine
, currLine
+lend
, strlen(currLine
+lend
)+1);
3896 UrLabelInfo
*lbl
= urAddLabel(nn
);
3897 if (!lbl
->refFile
) {
3898 lbl
->refLine
= curSrcLine
->lineNo
;
3899 lbl
->refFile
= strdup(curSrcLine
->fname
);
3901 if (pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
3906 lbl
->fixuptype
= UR_FIXUP_NONE
;
3907 //fprintf(stderr, "flabel: %s (#%04X)\n", lbl->name, lbl->value);
3910 //fprintf(stderr, "<: %s :>\n", currLine);
3913 const char *wname
= forthGetWord();
3915 //fprintf(stderr, " %s: #%04X: <%s>\n", forthCurrWord, disp, wname);
3918 if (strcmp(wname
, "(.\")") == 0 || strcmp(wname
, "(\")") == 0) {
3922 ForthWord
*fw
= findForthWord(wname
);
3923 if (!fw
) fatal("forth word `%s` not found", wname
);
3927 char qch
= currLine
[0];
3928 if (qch
!= '~' && qch
!= '"' && qch
!= '\'') fatal("invalid string quotation");
3929 memmove(currLine
, currLine
+1, strlen(currLine
+1)+1);
3931 while (currLine
[pos
] && currLine
[pos
] != qch
) {
3932 if (currLine
[pos
] != '\\') { ++pos
; continue; }
3934 if (currLine
[pos
+1] == qch
) {
3937 switch (currLine
[pos
+1]) {
3938 case 'r': ch
= 13; break;
3939 case 'n': ch
= 256; break;
3940 case 't': ch
= 9; break;
3941 case 'b': ch
= 8; break;
3942 case 'v': ch
= 10; break;
3943 case '\\': ch
= '\\'; break;
3945 //fprintf(stderr, "<%s>\n", currLine);
3946 if (!isHexDigit(currLine
[pos
+2]) || !isHexDigit(currLine
[pos
+3])) fatal("invalid hex escape");
3947 ch
= digitInBase(currLine
[pos
+2], 16)*16+digitInBase(currLine
[pos
+3], 16);
3948 currLine
[pos
++] = ch
;
3949 memmove(currLine
+pos
, currLine
+pos
+3, strlen(currLine
+pos
+3)+1);
3950 ch
= 666; // special
3954 if (ch
< 0) fatal("invalid escape");
3955 if (ch
== 666) continue;
3958 currLine
[pos
++] = 13;
3959 currLine
[pos
++] = 10;
3961 currLine
[pos
++] = ch
;
3962 memmove(currLine
+pos
, currLine
+pos
+1, strlen(currLine
+pos
+1)+1);
3965 if (pos
> 255) fatal("string too long");
3966 if (currLine
[pos
] != qch
) fatal("unterminated string");
3967 emitByte(pos
&0xffU
);
3968 for (size_t f
= 0; f
< pos
; ++f
) emitByte((uint8_t)(currLine
[f
]&0xffU
));
3970 memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
3974 // check for numeric literal
3975 if (strcasecmp(wname
, "LIT") == 0) {
3979 ForthWord
*fw
= findForthWord(wname
);
3980 if (!fw
) fatal("forth word `%s` not found", wname
);
3984 wname
= forthGetWord();
3985 if (!wname
) fatal("unexpected end of line");
3986 //fprintf(stderr, " %s: #%04X: <%s>\n", forthCurrWord, disp, wname);
3991 UrLabelInfo
*lbl
= urFindLabel(wname
);
3993 if (!lbl
->known
) fatal("unknown label '%s'", lbl
->name
);
3994 emitWord(lbl
->value
&0xffffU
);
4000 if (wname
[0] == '#' && isHexDigit(wname
[1])) {
4004 long int v
= strtol(wname
, &end
, base
);
4005 if (end
[0] == 0 && end
!= wname
) {
4006 if (v
< -32768 || v
> 65535) fatal("forth numeric literal out of range: %d\n", (int)v
);
4007 if (v
< 0) v
= 65536+v
;
4008 emitWord(v
&0xffffU
);
4011 fatal("number expected, got `%s`", wname
);
4016 // check for `COMPILE`
4017 if (strcasecmp(wname
, "COMPILE") == 0) {
4021 ForthWord
*fw
= findForthWord(wname
);
4022 if (!fw
) fatal("forth word `%s` not found", wname
);
4026 wname
= forthGetWord();
4027 if (!wname
) fatal("unexpected end of line");
4031 ForthWord
*fw
= findForthWord(wname
);
4032 if (!fw
) fatal("forth word `%s` not found", wname
);
4042 if (strcmp(wname
, "~") == 0) wname
= "";
4043 ForthWord
*fw
= findForthWord(wname
);
4046 UrLabelInfo
*lbl
= urFindLabel(wname
);
4048 fatal("label '%s' without LIT", lbl
->name
);
4049 emitWord(lbl
->value
&0xffffU
);
4054 if (wname
[0] == '#' && isHexDigit(wname
[1])) {
4058 long int v
= strtol(wname
, &end
, base
);
4059 if (end
[0] == 0 && end
!= wname
) {
4060 fatal("number without LIT! (%d)", (int)v
);
4063 fatal("unknown forth word '%s'", wname
);
4067 wname
= forthGetWord();
4068 if (!wname
) fatal("label expected");
4069 UrLabelInfo
*lbl
= urFindLabel(wname
);
4070 if (!lbl
) fatal("unknown label in forth code: '%s'", wname
);
4071 if (!lbl
->known
) fatal("undefined label '%s'", lbl
->name
);
4072 if (lbl
->value
< 0 || lbl
->value
> 65535) fatal("invalid jump label in forth code: '%s' (%d)", wname
, lbl
->value
);
4073 int v
= lbl
->value
-(int)disp
;
4074 if (v
< -32767 || v
> 32767) fatal("forth jump too far");
4075 if (v
< 0) v
= 65536+v
;
4076 emitWord(v
&0xffffU
);
4083 ///////////////////////////////////////////////////////////////////////////////
4084 // setup instructions
4086 static void registerInstructions (void) {
4087 urAddOp("DISPLAY", piDISPLAY
);
4088 urAddOp("DISPLAY0", piDISPLAY0
);
4089 urAddOp("DISPLAYA", piDISPLAYA
);
4090 urAddOp("DISPHEX", piDISPHEX
);
4091 urAddOp("DISPHEX0", piDISPHEX0
);
4092 urAddOp("DISPHEXA", piDISPHEXA
);
4094 urAddOp("DEFFMT", piDEFFMT
);
4095 urAddOp("$MODEL", piMODEL
);
4097 urAddOp("MACRO", piMACRO
);
4098 urAddOp("ENDM", piENDM
);
4100 urAddOp("ORG", piORG
);
4101 urAddOp("DISP", piDISP
);
4102 urAddOp("ENDDISP", piENDDISP
);
4103 urAddOp("PHASE", piDISP
);
4104 urAddOp("DEPHASE", piENDDISP
);
4105 urAddOp("UNPHASE", piENDDISP
);
4106 urAddOp("ALIGN", piALIGN
);
4107 urAddOp("DISPALIGN", piDISPALIGN
);
4108 urAddOp("PHASEALIGN", piDISPALIGN
);
4109 urAddOp("ENT", piENT
);
4110 urAddOp("CLR", piCLR
);
4111 urAddOp("RESERVE", piRESERVE
);
4113 urAddOp("INCLUDE", piINCLUDE
);
4114 urAddOp("INCBIN", piINCBIN
);
4116 urAddOp("MODULE", piMODULE
);
4117 urAddOp("ENDMODULE", piENDMODULE
);
4119 urAddOp("DUP", piDUP
);
4120 urAddOp("EDUP", piEDUP
);
4122 urAddOp("IF", piIF
);
4123 urAddOp("IFX", piIFX
);
4124 urAddOp("ELSE", piELSE
);
4125 urAddOp("ELSEIF", piELSEIF
);
4126 urAddOp("ELSEIFX", piELSEIFX
);
4127 urAddOp("ENDIF", piENDIF
);
4129 urAddOp("DEFINCR", piDEFINCR
);
4130 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
4131 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
4132 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
4133 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
4134 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
4135 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
4136 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
4137 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
4139 urAddOp("$ERROR", piERROR
);
4140 urAddOp("$WARNING", piWARNING
);
4142 urAddOp("$PRINTF", piPRINTF
);
4143 urAddOp("$PRINTF0", piPRINTF0
);
4144 urAddOp("$PRINTFA", piPRINTFA
);
4146 urAddOp("$MATHMODE", piMATHMODE
);
4148 urAddOp("$FORTH_CONST", piForthConst
);
4149 urAddOp("$FORTH_VAR", piForthVar
);
4150 urAddOp("$FORTH_DVAR", piForthDVar
);
4151 urAddOp("$FORTH_USER", piForthUser
);
4153 urAddOp("$FORTH_CODE_WORD", piForthCodeWord
);
4154 urAddOp("$FORTH_END_CODE_WORD", piForthCodeWordEnd
);
4156 urAddOp("$FORTH_WORD", piForthWord
);
4157 urAddOp("$FORTH_END_WORD", piForthWordEnd
);
4159 urAddOp("$FORTH_DOES", piForthWordDoes
);
4163 ///////////////////////////////////////////////////////////////////////////////
4164 // !0: invalid label
4166 static inline void fnSkipSpaces (const char *expr) {
4167 while (*expr && isSpace(*expr)) ++expr;
4173 static inline char fnNextChar (const char *expr
) {
4174 while (*expr
&& isSpace(*expr
)) ++expr
;
4179 #define FN_SKIP_BLANKS do { \
4180 while (*expr && isSpace(*expr)) ++expr; \
4183 #define FN_CHECK_END do { \
4184 while (*expr && isSpace(*expr)) ++expr; \
4185 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
4190 #define FN_CHECK_COMMA do { \
4191 while (*expr && isSpace(*expr)) ++expr; \
4192 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
4197 static const char *readLabelName (char *buf
, const char *expr
) {
4200 while (*expr
&& isSpace(*expr
)) ++expr
;
4204 if (pos
>= 128) return NULL
;
4205 if (ch
== ')') { --expr
; break; }
4207 if (isAlphaDigit(ch
) || ch
== '$' || ch
== '.' || ch
== '_' || ch
== '@') {
4213 if (pos
< 1) return NULL
;
4215 if (!urasm_is_valid_name(buf
)) return NULL
;
4220 static const char *fnDefKn (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
, int qtype
) {
4223 if ((expr
= readLabelName(lbl
, expr
)) == NULL
) { *error
= UR_EXPRERR_LABEL
; return NULL
; }
4225 if (!donteval
) res
->val
= (isLabelDefinedOrKnown(lbl
, addr
, qtype
) != 0);
4229 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
); }
4230 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
); }
4233 static const char *fnAligned256 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4234 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4236 if (!donteval
) res
->val
= (res
->val
%256 ? 0 : 1);
4241 static const char *fnSameSeg (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4242 urasm_exprval_t v0
, v1
;
4244 urasm_exprval_init(&v0
);
4245 urasm_exprval_init(&v1
);
4246 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
4247 if (*error
) return expr
;
4249 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
4250 if (*error
) return expr
;
4252 if (!donteval
) res
->val
= (v0
.val
/256 == v1
.val
/256);
4257 static const char *fnAlign (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4258 urasm_exprval_t v0
, v1
;
4259 urasm_exprval_init(&v0
);
4260 urasm_exprval_init(&v1
);
4261 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
4262 if (*error
) return expr
;
4264 if (fnNextChar(expr
) == ',') {
4266 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
4267 if (*error
) return expr
;
4273 if (v1
.val
< 1) { *error
= UR_EXPRERR_FUNC
; return NULL
; }
4274 res
->val
= (v0
.val
+v1
.val
-1)/v1
.val
*v1
.val
;
4280 static const char *fnLow (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4281 const char *ee
= expr
;
4282 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4285 if (res
->fixuptype
== UR_FIXUP_HIBYTE
) {
4286 *error
= UR_EXPRERR_FUNC
; return ee
;
4288 res
->fixuptype
= UR_FIXUP_LOBYTE
;
4295 static const char *fnHigh (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4296 const char *ee
= expr
;
4297 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4300 if (res
->fixuptype
== UR_FIXUP_LOBYTE
) {
4301 *error
= UR_EXPRERR_FUNC
; return ee
;
4303 res
->fixuptype
= UR_FIXUP_HIBYTE
;
4304 res
->val
= (res
->val
>>8)&0xff;
4310 static const char *fnAbs (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4311 const char *ee
= expr
;
4312 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4315 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
4316 *error
= UR_EXPRERR_FUNC
; return ee
;
4318 res
->val
= abs(res
->val
);
4324 static const char *fnBSwap (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4325 const char *ee
= expr
;
4326 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4329 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
4330 *error
= UR_EXPRERR_FUNC
; return ee
;
4332 res
->val
= ((res
->val
>>8)&0xff)|((res
->val
&0xff)<<8);
4338 static const char *fnScrAddr8xn (int nmul
, urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4339 int32_t scrbase
= 0x4000;
4341 const char *ee
= expr
;
4342 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
4343 // first argument is `x`
4344 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4346 // second argument is `y`
4348 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4350 // optional third arg is screen base
4352 if (expr
[0] == ',') {
4354 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4355 scrbase
= res
->val
&0xffff;
4359 //urasm_exprval_clear(res);
4360 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
4361 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
4362 if (y
< 0 || y
> 191) fatal("invalid y coordinate: %d", y
/nmul
);
4368 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
4374 static const char *fnScrAddr8x1 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4375 return fnScrAddr8xn(1, res
, expr
, addr
, donteval
, defined
, error
);
4378 static const char *fnScrAddr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4379 return fnScrAddr8xn(8, res
, expr
, addr
, donteval
, defined
, error
);
4383 static const char *fnScrAttr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4384 int32_t scrbase
= 0x4000;
4386 const char *ee
= expr
;
4387 // first argument is `x`
4388 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4390 // second argument is `y`
4392 urasm_exprval_clear(res
);
4393 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4395 // optional third arg is screen base
4397 if (expr
[0] == ',') {
4399 urasm_exprval_clear(res
);
4400 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4401 scrbase
= res
->val
&0xffff;
4403 urasm_exprval_clear(res
);
4406 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
4407 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
4408 if (y
< 0 || y
> 23) fatal("invalid y coordinate: %d", y
);
4409 res
->val
= scrbase
+6144+y
*32+x
;
4415 static const char *fnMArgToStr (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4416 const char *ee
= expr
;
4417 // argument is macro argument name
4418 expr
= strSkipSpacesConst(expr
);
4419 if (expr
[0] != '=') { *error
= UR_EXPRERR_FUNC
; return ee
; }
4421 if (!isAlpha(expr
[0]) && expr
[0] != '_') { *error
= UR_EXPRERR_FUNC
; return ee
; }
4422 const char *nend
= expr
+1;
4423 while (isAlphaDigit(*nend
) || *nend
== '_') ++nend
;
4424 if (nend
-expr
> 127) { *error
= UR_EXPRERR_FUNC
; return ee
; }
4426 memset(name
, 0, sizeof(name
));
4427 memcpy(name
, expr
, nend
-expr
);
4432 expr
= strSkipSpacesConst(expr
);
4433 if (expr
[0] == '[') {
4434 expr
= strSkipSpacesConst(expr
+1);
4435 if (expr
[0] != ']') {
4438 if (expr
[0] == '+') ++expr
;
4439 else if (expr
[0] == '-') { doneg
= 1; ++expr
; }
4441 while (isDigit(expr
[0])) {
4442 index
= index
*10+(expr
[0]-'0');
4443 if (index
>= 0x1fffffff) fatal("`margtostr` index too high");
4446 expr
= strSkipSpacesConst(expr
);
4447 if (doneg
) index
= -index
;
4449 if (expr
[0] != ']') fatal("`margtostr` invalid index syntax");
4454 if (curmacro
== NULL
) fatal("`margtostr` outside of macro");
4456 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
4457 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
4458 // found argument, convert it to string
4460 res
->str
= realloc(res
->str
, strlen(curmacro
->argvals
[f
])+1);
4461 strcpy(res
->str
, curmacro
->argvals
[f
]);
4463 const size_t slen
= strlen(curmacro
->argvals
[f
]);
4466 if (index
> slen
) fatal("index out of string");
4467 index
= (int)slen
-index
;
4469 if (index
>= slen
) fatal("index out of string");
4470 res
->str
= realloc(res
->str
, 2);
4471 res
->str
[0] = curmacro
->argvals
[f
][index
];
4475 for (char *s
= res
->str
; *s
; ++s
) *s
= toLower(*s
);
4476 //fprintf(stderr, "<%s>\n", res->str);
4479 res
->val
= ((unsigned char)res
->str
[0]);
4480 res
->val
|= ((unsigned char)res
->str
[1])<<8;
4482 res
->val
= (unsigned char)res
->str
[0];
4490 fatal("unknown macro argument '%s'", name
);
4496 static const char *fnStrLen (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4497 expr
= strSkipSpacesConst(expr
);
4500 case '"': case '\'': /* string literal? */
4502 char *a
= (char *)expr
+1;
4503 (void)parseStr(&a
, expr
[0], &stlen
);
4504 expr
= (const char *)a
;
4507 default: /* expression */
4510 urasm_exprval_init(&r
);
4511 expr
= urasm_expr_ex(&r
, expr
, disp
, &donteval
, defined
, error
);
4512 if (*error
) fatalUrLib(*error
);
4513 if (!r
.str
) fatal("string expected for `strlen()`");
4514 stlen
= (int)strlen(r
.str
);
4515 urasm_exprval_clear(&r
);
4521 urasm_exprval_setint(res
, stlen
);
4527 static void registerFunctions (void) {
4528 urasm_expr_register_func("defined", fnDefined
);
4529 urasm_expr_register_func("known", fnKnown
);
4530 urasm_expr_register_func("aligned256", fnAligned256
);
4531 urasm_expr_register_func("align", fnAlign
);
4532 urasm_expr_register_func("sameseg", fnSameSeg
);
4533 urasm_expr_register_func("low", fnLow
);
4534 urasm_expr_register_func("high", fnHigh
);
4535 urasm_expr_register_func("abs", fnAbs
);
4536 urasm_expr_register_func("bswap", fnBSwap
);
4538 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8
);
4539 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1
);
4540 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8
);
4542 urasm_expr_register_func("marg2str", fnMArgToStr
);
4543 urasm_expr_register_func("strlen", fnStrLen
);
4547 ///////////////////////////////////////////////////////////////////////////////
4548 // preparing another pass
4550 static void initPass (void) {
4551 curSrcLine
= asmText
;
4564 lastSeenGlobalLabel
= strdup(" [MAIN] ");
4567 urasm_use_old_priorities
= 0;
4569 forthCurrWord
= NULL
;
4573 static int posstPass (void) {
4574 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
4575 lastSeenGlobalLabel
= NULL
;
4576 if (checkLabels()) return -1;
4577 if (ifCount
!= 0) fatal("unbalanced IFs");
4578 if (forthCurrWord
) fatal("unfinished forth word `%s`", forthCurrWord
);
4583 ///////////////////////////////////////////////////////////////////////////////
4584 static int labelCmp (const void *aa
, const void *bb
) {
4585 if (aa
== bb
) return 0;
4586 const UrLabelInfo
*a
= *(const UrLabelInfo
**)aa
;
4587 const UrLabelInfo
*b
= *(const UrLabelInfo
**)bb
;
4589 a
->value
< b
->value
? -1 :
4590 a
->value
> b
->value
? 1 :
4595 static void writeLabelsFile (const char *fname
) {
4596 if (!fname
|| !fname
[0]) return;
4599 for (const UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) ++lcount
;
4600 UrLabelInfo
**larr
= NULL
;
4602 // create labels array
4603 larr
= (UrLabelInfo
**)malloc(sizeof(UrLabelInfo
*)*lcount
);
4605 for (UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
, ++lcount
) larr
[lcount
] = ll
;
4607 qsort(larr
, lcount
, sizeof(UrLabelInfo
*), &labelCmp
);
4610 FILE *fo
= fopen(fname
, "w");
4612 for (int f
= 0; f
< lcount
; ++f
) {
4613 UrLabelInfo
*ll
= larr
[f
];
4614 if (!ll
->name
|| (!isAlpha(ll
->name
[0]) && ll
->name
[0] != '@')) continue;
4615 if (ll
->value
< 0) {
4616 fprintf(fo
, "%d %s\n", ll
->value
, ll
->name
);
4618 fprintf(fo
, "#%04X %s\n", (unsigned)ll
->value
, ll
->name
);
4623 // write forth words
4624 if (forthWordList
) {
4626 for (ForthWord
*w
= forthWordList
; w
; w
= w
->next
) {
4627 fprintf(fo
, "#%04X %s\n", w
->cfa
, w
->name
);
4634 ///////////////////////////////////////////////////////////////////////////////
4637 static struct option longOpts
[] = {
4638 {"org", required_argument
, NULL
, 600},
4639 {"define", required_argument
, NULL
, 601},
4640 {"defzero", required_argument
, NULL
, 602},
4641 {"outdir", required_argument
, NULL
, 660},
4642 {"reffile", optional_argument
, NULL
, 669},
4644 {"sna", 0, NULL
, 's'},
4645 {"sna128", 0, NULL
, 'S'},
4646 {"tap", 0, NULL
, 't'},
4647 {"autotap", 0, NULL
, 'T'},
4648 {"raw", 0, NULL
, 'r'},
4649 {"autodmb", 0, NULL
, 'B'},
4650 {"dmb", 0, NULL
, 'b'},
4651 {"none", 0, NULL
, 'n'},
4652 {"help", 0, NULL
, 'h'},
4653 {"hob", 0, NULL
, 'H'},
4654 {"scl", 0, NULL
, 'l'},
4655 {"autoscl", 0, NULL
, 'L'},
4656 {"fixups", optional_argument
, NULL
, 'F'},
4662 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
4665 static void usage (const char *pname
) {
4667 "usage: %s [options] infile\n"
4668 "default infiles:", pname
);
4669 for (int f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
4672 " -s --sna write 48K .SNA file with autostart\n"
4673 " -S --sna128 write 148K .SNA file with autostart\n"
4674 " -t --tap write .tap file\n"
4675 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
4676 " -r --raw write raw file(s)\n"
4677 " -b --dmb write DMB file\n"
4678 " -B --autodmb write DMB file with autostart\n"
4679 " -H --hob write HoBeta code file(s)\n"
4680 " -l --scl write SCL TR-DOS archive\n"
4681 " -L --autoscl write autostarting SCL TR-DOS archive\n"
4682 " -n --none write nothing\n"
4683 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
4684 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
4685 " text: .txt file\n"
4686 " asm: address lists with counters\n"
4687 " asmdiff: 2nd and other addresses are relative, with counters\n"
4688 " asmz: address lists, with 0 end markers\n"
4689 " -h --help this help\n"
4691 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
4692 " --org xxx set ORG\n"
4693 " --define val perform 'val EQU 1'\n"
4694 " --defzero val perform 'val EQU 0'\n"
4695 " --outdir dir output dir for resulting files (default: current)\n"
4700 ///////////////////////////////////////////////////////////////////////////////
4703 int main (int argc
, char *argv
[]) {
4705 const char *pname
= argv
[0];
4706 char *inFile
= NULL
;
4707 char **defines
= NULL
, **values
= NULL
;
4713 urasm_getbyte
= getByte
;
4714 urasm_putbyte
= putByte
;
4715 urasm_label_by_name
= findLabelCB
;
4716 urasm_getval
= getValueCB
;
4717 urasm_expand
= expandCB
;
4718 urasm_fixup_operand
= fixupOperandCB
;
4720 //strcpy(tapeLoaderName, "cargador ");
4721 tapeLoaderName
[0] = 0;
4723 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
4724 while ((c
= getopt_long(argc
, argv
, "sStTrBbnhHFLl", longOpts
, NULL
)) >= 0) {
4727 case 'S': /*optRunSNA = 1;*/ optSNA48
= 0; optWriteType
= 's'; optWTChanged
= 1; break;
4728 case 's': /*optRunSNA = 0;*/ optSNA48
= 1; optWriteType
= 's'; optWTChanged
= 1; break;
4729 case 'T': optRunTape
= 1; optWriteType
= 't'; optWTChanged
= 1; break;
4730 case 't': optRunTape
= 0; optWriteType
= 't'; optWTChanged
= 1; break;
4731 case 'L': optRunSCL
= 1; optWriteType
= 'S'; optWTChanged
= 1; break; /* with boot */
4732 case 'l': optRunSCL
= -1; optWriteType
= 'S'; optWTChanged
= 1; break; /* no boot */
4733 case 'r': case 'n': optWriteType
= c
; optWTChanged
= 1; break;
4734 case 'b': optRunTape
= 0; optRunDMB
= 0; optWriteType
= 'd'; optWTChanged
= 1; break;
4735 case 'B': optRunTape
= 0; optRunDMB
= 1; optWriteType
= 'd'; optWTChanged
= 1; break;
4736 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
4737 case 'H': optWriteType
= 'H'; optWTChanged
= 1; break;
4740 if (optarg
!= NULL
) {
4741 if (strcmp(optarg
, "asm") == 0 || strcmp(optarg
, "zas") == 0) {
4743 } else if (strcmp(optarg
, "asmdiff") == 0 || strcmp(optarg
, "zasdiff") == 0) {
4745 } else if (strcmp(optarg
, "asmz") == 0 || strcmp(optarg
, "zasz") == 0) {
4747 } else if (strcmp(optarg
, "text") == 0) {
4750 fprintf(stderr
, "FATAL: invalid fixup type: '%s'\n", optarg
);
4757 //fprintf(stderr, "ORG: %d\n", c);
4758 if (c
< 0 || c
> 65535) {
4759 fprintf(stderr
, "FATAL: invalid ORG: %d\n", c
);
4762 start_pc
= start_disp
= start_ent
= c
;
4765 //fprintf(stderr, "define: [%s]\n", optarg);
4766 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
4767 values
= realloc(values
, sizeof(char *)*(defcount
+1));
4768 defines
[defcount
] = strdup(optarg
);
4769 values
[defcount
] = strdup("1");
4772 case 602: // defzero
4773 //fprintf(stderr, "defzero: [%s]\n", optarg);
4774 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
4775 values
= realloc(values
, sizeof(char *)*(defcount
+1));
4776 defines
[defcount
] = strdup(optarg
);
4777 values
[defcount
] = strdup("0");
4781 if (optOutputDir
!= NULL
) free(optOutputDir
);
4782 optOutputDir
= strdup(optarg
);
4784 case 669: // reffile
4785 if (refFileName
) free(refFileName
);
4786 refFileName
= (optarg
? strdup(optarg
) : NULL
);
4792 if (optind
>= argc
) {
4793 // try to find default input file
4794 for (int f
= 0; defInFiles
[f
]; ++f
) {
4795 if (!access(defInFiles
[f
], R_OK
)) {
4796 inFile
= strdup(defInFiles
[f
]);
4801 inFile
= strdup(argv
[optind
]);
4804 if (!inFile
|| !inFile
[0]) {
4806 fprintf(stderr
, "ERROR: no input file!\n");
4810 if (optOutputDir
== NULL
) optOutputDir
= strdup(".");
4812 registerInstructions();
4813 registerFunctions();
4815 res
= asmTextLoad(inFile
, 0);
4817 for (int f
= 0; f
< defcount
; ++f
) {
4818 if (labelDoEQU(defines
[f
], values
[f
]) != 0) {
4819 fprintf(stderr
, "FATAL: can't define label: '%s'\n", defines
[f
]);
4824 for (pass
= 0; pass
<= 1; ++pass
) {
4826 printf("pass %d\n", pass
);
4827 setCurSrcLine(asmText
);
4828 if (setjmp(errJP
)) { res
= 1; break; }
4829 while (curSrcLine
) processCurrentLine();
4830 if (posstPass()) { res
= 1; break; }
4835 char *oc
= strdup(inFile
);
4836 char *pd
= strrchr(oc
, '.');
4837 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
4838 switch (optWriteType
) {
4839 case 's': saveSna(oc
, optSNA48
); break;
4840 case 't': saveTap(oc
); break;
4841 case 'r': saveRaw(oc
); break;
4842 case 'd': saveDMB(oc
); break;
4843 case 'H': saveHob(oc
); break;
4844 case 'S': saveSCL(oc
); break;
4847 if (optWriteFixups
) writeFixups();
4849 /* build ref file name */
4850 if (!refFileName
|| !refFileName
[0]) {
4851 if (refFileName
) free(refFileName
);
4852 refFileName
= malloc(strlen(inFile
)+128);
4853 strcpy(refFileName
, inFile
);
4854 char *ext
= strrchr(refFileName
, '.');
4856 strcat(refFileName
, ".ref");
4860 for (char *ts
= refFileName
; *ts
; ++ts
) {
4861 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
4864 char *slash
= strrchr(refFileName
, '/');
4866 if (!slash
|| slash
< ext
) {
4867 strcpy(ext
, ".ref");
4869 strcat(refFileName
, ".ref");
4873 writeLabelsFile(refFileName
);
4874 printf("refs written to '%s'\n", refFileName
);
4879 fprintf(stderr
, "ERROR: loading error!\n");
4883 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
4891 if (inFile
) free(inFile
);
4892 if (sysIncludeDir
) free(sysIncludeDir
);
4893 for (int f
= defcount
-1; f
>= 0; --f
) {
4897 if (defines
!= NULL
) { free(values
); free(defines
); }
4898 if (optOutputDir
!= NULL
) free(optOutputDir
);
4899 return (res
? 1 : 0);