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);
69 ////////////////////////////////////////////////////////////////////////////////
70 static char *strprintfVA (const char *fmt
, va_list vaorig
) {
75 if (buf
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
81 olen
= vsnprintf(buf
, len
, fmt
, va
);
83 if (olen
>= 0 && olen
< len
) return buf
;
84 if (olen
< 0) olen
= len
*2-1;
85 nb
= realloc(buf
, olen
+1);
86 if (nb
== NULL
) { fprintf(stderr
, "\nFATAL: out of memory!\n"); abort(); }
93 static __attribute((format(printf
,1,2))) char *strprintf (const char *fmt
, ...) {
98 buf
= strprintfVA(fmt
, va
);
104 ///////////////////////////////////////////////////////////////////////////////
109 AMODE_FCODE
= 1, // forth assembler code word
110 AMODE_FWORD
= 2, // forth threaded code word
113 static int asmMode
= 0;
115 static char *sysIncludeDir
= NULL
;
116 static char *refFileName
= NULL
;
117 static char *lastIncludePath
= NULL
;
118 static char *lastSysIncludePath
= NULL
;
120 #define MAX_LINE_SIZE 16384
121 static char currLine
[MAX_LINE_SIZE
]; /* current processing line; can be freely modified */
124 ///////////////////////////////////////////////////////////////////////////////
127 static void initInclideDir (void) {
128 const char *id
= getenv("URASM_INCLUDE_DIR");
130 sysIncludeDir
= strdup(id
);
133 memset(myDir
, 0, sizeof(myDir
));
135 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) {
138 char *p
= (char *)strrchr(myDir
, '/');
139 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
142 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
143 char *p
= strrchr(myDir
, '\\');
144 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
146 strcat(myDir
, "/libs");
147 sysIncludeDir
= strdup(myDir
);
149 while (sysIncludeDir
[0] && sysIncludeDir
[strlen(sysIncludeDir
)-1] == '/') sysIncludeDir
[strlen(sysIncludeDir
)-1] = '\0';
150 if (!sysIncludeDir
[0]) strcpy(sysIncludeDir
, ".");
154 ///////////////////////////////////////////////////////////////////////////////
157 /* trim trailing spaces and comments; normalize colons */
158 static void normalizeForthStr (char *s
) {
160 /* now skip all shit */
162 const char ch
= *p
++;
164 if (ch
== ';' && p
[0] == ';') { p
[-1] = 0; break; }
166 /* done; trim trailing spaces and colons */
167 size_t slen
= strlen(s
);
168 while (slen
> 0 && isSpace(s
[slen
-1])) --slen
;
173 /* trim trailing spaces and comments; normalize colons */
174 static void normalizeStr (char *s
) {
175 if (asmMode
== AMODE_FWORD
) return normalizeForthStr(s
);
176 if (strcasestr(s
, "$FORTH")) return normalizeForthStr(s
);
178 /* now skip all shit */
180 const char ch
= *p
++;
181 /* check for "af'" */
182 if (ch
== '\'' && p
-s
>= 2 && toLower(p
[-2] == 'a') && toLower(p
[-1] == 'f')) continue;
184 if (ch
== ';') { p
[-1] = 0; break; }
186 if (ch
== '"' || ch
== '\'') {
189 const char c1
= *p
++;
190 if (c1
== qch
) break;
191 if (c1
== '\\' && *p
) ++p
;
195 /* reduce and normalise colons */
197 --p
; /* back to colon */
198 /* remove spaces before colon */
200 while (t
!= s
&& isSpace(t
[-1])) --t
;
201 if (t
!= p
) memmove(t
, p
, strlen(p
)+1);
203 if (p
[0] != ':') abort(); // assert
204 ++p
; /* skip colon */
205 /* remove following spaces and colons */
207 while (*t
== ':' || isSpace(*t
)) ++t
;
208 if (t
!= p
) memmove(p
, t
, strlen(t
)+1);
212 /* done; trim trailing spaces and colons */
213 size_t slen
= strlen(s
);
214 while (slen
> 0 && (isSpace(s
[slen
-1]) || s
[slen
-1] == ':')) --slen
;
219 /* check if string starts with the given command (case-insensitive) */
220 /* returns NULL or pointer to args */
221 /* skips spaces after command if any */
222 static char *strIsCommand (const char *command
, char *str
) {
223 for (int cnt
= 1; cnt
> 0; --cnt
) {
224 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
225 for (; *command
&& *str
; ++command
, ++str
) {
226 if (toUpper(*command
) != toUpper(*str
)) return NULL
; // alas
228 if (*command
) return NULL
; // alas
229 if (*str
&& isAlphaDigit(*str
)) return NULL
; // alas
230 while (*str
&& isSpace(*str
)) ++str
; // skip spaces
231 if (*str
&& *str
== ':') break; // try again if we have a colon
238 /* parse string literal */
239 /* don't free() result */
240 /* skips trailing spaces */
241 static char *parseStr (char **str
, char endQ
, int *lenp
) {
242 static char buf
[MAX_LINE_SIZE
];
243 int len
= 0, n
, f
, base
;
245 memset(buf
, 0, sizeof(buf
));
251 case 'a': buf
[len
++] = '\a'; break;
252 case 'b': buf
[len
++] = '\b'; break;
253 case 'e': buf
[len
++] = '\x1b'; break;
254 case 'f': buf
[len
++] = '\f'; break;
255 case 'n': buf
[len
++] = '\n'; break;
256 case 'r': buf
[len
++] = '\r'; break;
257 case 't': buf
[len
++] = '\t'; break;
258 case 'v': buf
[len
++] = '\v'; break;
259 case 'z': buf
[len
++] = '\0'; break;
260 case 'x': case 'X': // hex
263 donum
: for (n
= 0; f
> 0; --f
) {
264 char ch
= digitInBase(*a
++, base
);
266 if (ch
< 0) { --a
; break; }
271 --a
; // return to the last digit, 'for' will skip it
276 case '1' ... '9': // decimal
279 default: buf
[len
++] = a
[0]; break; // others
282 if (*a
== endQ
) { ++a
; break; }
286 while (*a
&& isSpace(*a
)) ++a
; // skip trailing spaces
289 if (lenp
) *lenp
= len
;
294 ///////////////////////////////////////////////////////////////////////////////
295 // source file stack, reader, etc
297 typedef struct SourceLine
{
298 struct SourceLine
*next
;
305 static SourceLine
*asmText
= NULL
;
306 static SourceLine
*asmTextLast
= NULL
;
307 static SourceLine
*curSrcLine
= NULL
;
309 #define MAX_MACRO_ARGS (32)
311 typedef struct MacroDef
{
312 struct MacroDef
*next
;
316 char *argdefaults
[MAX_MACRO_ARGS
]; // default values
317 char *argnames
[MAX_MACRO_ARGS
]; // argument names
322 char *argvals
[MAX_MACRO_ARGS
]; // argument values
325 static MacroDef
*maclist
= NULL
;
326 static CurMacroDef
*curmacro
= NULL
; // !NULL if we are not inserting macro
327 static int curmacronum
= 0;
330 static MacroDef
*findMacro (const char *name
) {
331 for (MacroDef
*mc
= maclist
; mc
!= NULL
; mc
= mc
->next
) if (strcasecmp(name
, mc
->name
) == 0) return mc
;
336 static void asmTextClear (void) {
338 SourceLine
*l
= asmText
;
340 asmText
= asmText
->next
;
345 asmTextLast
= curSrcLine
= NULL
;
349 static int asmTextLoad (const char *fname
, int system
) {
354 if (!(fl
= fopen(fname
, "r"))) {
355 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
358 printf("loading: %s\n", fname
);
360 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
362 currLine
[sizeof(currLine
)-1] = '\0';
363 //!normalizeStr(currLine);
364 //fprintf(stderr, "*[%s]\n", curLine);
365 if (!currLine
[0]) continue; // don't store empty lines
367 if ((s
= calloc(1, sizeof(SourceLine
))) == NULL
) abort();
370 if ((s
->line
= strdup(currLine
)) == NULL
) abort();
371 if ((s
->fname
= strdup(fname
)) == NULL
) abort();
372 if (asmTextLast
) asmTextLast
->next
= s
; else asmText
= s
;
380 static char *extractFileDir (const char *s
) {
381 if (!s
|| !s
[0]) return strdup("");
385 for (const char *ts
= s
; *ts
; ++ts
) {
386 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
388 if (slash
== s
&& (s
[0] == '/' || s
[0] == '\\') && (s
[1] == '/' || s
[1] == '\\')) slash
= NULL
;
390 slash
= strrchr(s
, '/');
392 if (!slash
) return strdup("");
393 ptrdiff_t len
= (ptrdiff_t)(slash
-s
)+1;
394 char *res
= malloc(len
+1);
398 while (len
> 0 && (res
[len
-1] == '\\' || res
[len
-1] == '/')) --len
;
400 while (len
> 0 && res
[len
-1] == '/') --len
;
402 if (len
== 0) { free(res
); return strdup(""); }
408 static void loadCurSrcLine (void) {
410 strcpy(currLine
, curSrcLine
->line
);
411 /* macros will not change include dirs */
413 char **incpp
= (!curSrcLine
->system
? &lastIncludePath
: &lastSysIncludePath
);
414 if (*incpp
) free(*incpp
);
415 *incpp
= extractFileDir(curSrcLine
->fname
);
417 normalizeStr(currLine
);
423 static inline SourceLine
*setCurSrcLine (SourceLine
*l
) { curSrcLine
= l
; loadCurSrcLine(); return l
; }
425 static inline SourceLine
*nextSrcLine (void) { return (curSrcLine
!= NULL
? setCurSrcLine(curSrcLine
->next
) : NULL
); }
428 static inline int strHasPathDelim (const char *s
) {
429 if (!s
|| !s
[0]) return 0;
431 return (strchr(s
, '/') || strchr(s
, '\\') ? 1 : 0);
433 return (strchr(s
, '/') ? 1 : 0);
438 /* returns malloced string */
439 static char *createIncludeName (const char *fname
, int assystem
, const char *defaultmain
) {
440 if (!fname
|| !fname
[0]) return NULL
;
442 if (fname
[0] != '/') {
445 incdir
= lastIncludePath
;
447 incdir
= lastSysIncludePath
;
448 if (!incdir
|| !incdir
[0]) incdir
= sysIncludeDir
;
450 res
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
452 res
= strprintf("%s", fname
);
455 if (defaultmain
&& defaultmain
[0]) {
456 if (stat(res
, &st
) == 0) {
457 if (S_ISDIR(st
.st_mode
)) {
458 char *rs
= strprintf("%s/%s", res
, defaultmain
);
464 /* check if there is disk file */
465 if (strHasPathDelim(fname
) && stat(res
, &st
) != 0) {
466 /* no file, try "root include" */
467 const char *incdir
= (!assystem
? NULL
: sysIncludeDir
);
468 char *rs
= strprintf("%s/%s", (incdir
&& incdir
[0] ? incdir
: "."), fname
);
471 /* check for dir again */
472 if (defaultmain
&& defaultmain
[0]) {
473 if (stat(res
, &st
) == 0) {
474 if (S_ISDIR(st
.st_mode
)) {
475 char *rs
= strprintf("%s/%s", res
, defaultmain
);
482 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
487 static int includeCount
= 0;
490 // include file instead of the current line
491 static int asmTextInclude (const char *fname
, int system
) {
495 SourceLine
*first
= NULL
, *last
= NULL
, *s
= NULL
;
497 if (includeCount
> 256) {
498 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", curSrcLine
->fname
, curSrcLine
->lineNo
);
502 fn
= createIncludeName(fname
, system
, "zzmain.zas");
504 printf("loading: %s\n", fn
);
505 if ((fl
= fopen(fn
, "r")) == NULL
) {
506 fprintf(stderr
, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", curSrcLine
->fname
, curSrcLine
->lineNo
, currLine
);
511 while (fgets(currLine
, sizeof(currLine
)-1, fl
)) {
513 currLine
[sizeof(currLine
)-1] = '\0';
514 const size_t slen
= strlen(currLine
);
515 if (slen
== 0 || (currLine
[slen
-1] != '\n' && currLine
[slen
-1] != '\r')) {
516 fprintf(stderr
, "ERROR: file %s, line %d: line too long\n", fn
, lineNo
);
520 //!normalizeStr(currLine);
521 if (!currLine
[0]) continue; // don't store empty lines
523 if ((s
= calloc(1, sizeof(SourceLine
))) == NULL
) abort();
526 if ((s
->line
= strdup(currLine
)) == NULL
) abort();
527 if ((s
->fname
= strdup(fn
)) == NULL
) abort();
528 if (last
!= NULL
) last
->next
= s
; else first
= s
;
534 curSrcLine
->line
[0] = 0;
536 last
->next
= curSrcLine
->next
;
537 curSrcLine
->next
= first
;
543 ///////////////////////////////////////////////////////////////////////////////
546 static void processCurrentLine (void); // only one, will skip to next one
547 static void processForthWordLine (void); // only one, will skip to next one
550 ///////////////////////////////////////////////////////////////////////////////
551 // error raisers, etc
553 static jmp_buf errJP
;
556 static void errorWriteFile (FILE *fo
) {
558 fprintf(fo
, "at file %s, line %d\n%s\n*", curSrcLine
->fname
, curSrcLine
->lineNo
, curSrcLine
->line
);
560 fprintf(fo
, "somewhere in time: ");
564 static void errorMsgV (const char *fmt
, va_list ap
) {
565 errorWriteFile(stderr
);
566 vfprintf(stderr
, fmt
, ap
);
573 static void __attribute__((format(printf
, 1, 2))) warningMsg (const char *fmt
, ...) {
575 fprintf(stderr
, "WARNING ");
581 static void __attribute__((format(printf
, 1, 2))) errorMsg (const char *fmt
, ...) {
583 fprintf(stderr
, "FATAL ");
589 static void __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2))) fatal (const char *fmt
, ...) {
597 static void __attribute__((noreturn
)) fatalUrLib (int errcode
) {
598 errorMsg("%s", urasm_errormsg(errcode
));
603 //////////////////////////////////////////////////////////////////////////////
604 // operator management
606 // return !0 to skip current line
607 typedef int (*UrAsmOpFn
) (void);
614 typedef struct UrAsmOp
{
617 struct UrAsmOp
*next
;
620 static UrAsmOp
*oplist
= NULL
;
623 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
624 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
626 res
->name
= strdup(name
);
634 static UrAsmOp
*urFindOp (const char *name
) {
636 for (res
= oplist
; res
; res
= res
->next
) if (!strcasecmp(name
, res
->name
)) break;
641 static void urClearOps (void) {
645 oplist
= oplist
->next
;
652 ///////////////////////////////////////////////////////////////////////////////
655 typedef struct UrLabelInfo
{
658 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
659 int known
; /* !0: label value already known */
660 int refLine
; /* first referenced line */
661 int fixuptype
; /* UR_FIXUP_XXX */
663 struct UrLabelInfo
*next
;
666 static UrLabelInfo
*labels
= NULL
;
667 static UrLabelInfo
*labelsTail
= NULL
;
670 static void urClearLabels (void) {
672 while ((c
= labels
) != NULL
) {
674 if (c
->name
) free(c
->name
);
675 if (c
->refFile
) free(c
->refFile
);
682 static UrLabelInfo
*urFindLabel (const char *name
) {
683 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) if (strcmp(name
, c
->name
) == 0) return c
;
688 static UrLabelInfo
*urAddLabel (const char *name
) {
689 UrLabelInfo
*c
= urFindLabel(name
);
691 c
= calloc(1, sizeof(UrLabelInfo
));
693 c
->name
= strdup(name
);
695 c
->fixuptype
= UR_FIXUP_NONE
;
697 if (labelsTail
) labelsTail
->next
= c
; else labels
= c
;
704 ///////////////////////////////////////////////////////////////////////////////
705 // module list management
707 typedef struct ModuleInfo
{
709 char *fname
; // opened in this file
710 int seen
; // !0: module already seen, skip other definitions from the same file
711 struct ModuleInfo
*next
;
714 static ModuleInfo
*modules
= NULL
;
715 static ModuleInfo
*curModule
= NULL
;
718 static void modulesClear (void) {
721 ModuleInfo
*c
= modules
;
723 modules
= modules
->next
;
731 static void modulesResetSeen (void) {
732 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
736 static ModuleInfo
*moduleFind (const char *name
) {
737 if (!name
|| !name
[0]) return NULL
;
738 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) return c
;
743 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
746 if (!name
|| !fname
|| !name
[0] || !fname
[0]) abort();
747 if ((c
= calloc(1, sizeof(ModuleInfo
))) == NULL
) abort();
748 if ((c
->name
= strdup(name
)) == NULL
) abort();
749 if ((c
->fname
= strdup(fname
)) == NULL
) abort();
751 return (modules
= c
);
755 ///////////////////////////////////////////////////////////////////////////////
757 typedef struct FixupItem
{
758 struct FixupItem
*next
;
764 static FixupItem
*fixlisthead
= NULL
, *fixlisttail
= NULL
;
767 static void clearFixups (void) {
770 while ((c
= fixlisthead
) != NULL
) {
771 fixlisthead
= c
->next
;
777 static void addFixup (uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
778 FixupItem
*fx
= calloc(1, sizeof(FixupItem
));
780 fx
->opdestaddr
= opdestaddr
;
782 fx
->fixuptype
= fixuptype
;
785 if (fixlisttail
!= NULL
) fixlisttail
->next
= fx
; else fixlisthead
= fx
;
791 ///////////////////////////////////////////////////////////////////////////////
792 // destination memory management
794 static uint8_t memory
[65536];
795 static char memused
[65536];
796 static char memresv
[65536];
797 static uint16_t start_pc
= 0x100; // viva CP/M!
798 static uint16_t start_disp
= 0x100; // viva CP/M!
799 static uint16_t start_ent
= 0x100; // viva CP/M!
800 static uint16_t pc
= 0; /* current position to write */
801 static uint16_t disp
= 0; /* current 'virtual PC' */
802 static uint16_t ent
= 0; /* starting address */
803 static uint16_t clrAddr
= 24999; /* address for CLEAR for cargador */
804 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
805 static int inTapeBlock
= 0;
806 static uint8_t tapeXorB
= 0;
809 static inline uint8_t getByte (uint16_t addr
) {
815 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
816 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
821 static inline void putByte (uint16_t addr
, uint8_t b
) {
822 if (inTapeBlock
) tapeXorB
^= b
;
828 static inline MAYBE_UNUSED
void putWord (uint16_t addr
, uint16_t w
) {
829 putByte(addr
, w
&0xFFU
);
830 putByte(addr
+1, (w
>>8)&0xFFU
);
834 static inline void emitByte (uint8_t b
) {
841 static inline void emitWord (uint16_t w
) {
843 emitByte((w
>>8)&0xFFU
);
847 static inline void emitRWord (uint16_t w
) {
848 emitByte((w
>>8)&0xFFU
);
853 static void prepareMemory (void) {
854 memset(memory
, 0, sizeof(memory
));
855 memset(memused
, 0, sizeof(memused
));
856 memset(memresv
, 0, sizeof(memresv
));
860 ///////////////////////////////////////////////////////////////////////////////
861 // label getter and utilities
863 static char *lastSeenGlobalLabel
= NULL
; /* global */
866 static char *fixLocalLabel (const char *name
) {
867 static char newname
[MAX_LINE_SIZE
*2+1024];
869 memset(newname
, 0, sizeof(newname
));
870 if (!name
|| !name
[0]) {
872 } else if (!lastSeenGlobalLabel
|| name
[0] != '.') {
873 strcpy(newname
, name
);
875 if (name
[0] == '.' && name
[1] == '.') {
876 // this is macro label
877 if (curmacro
== NULL
) fatal("macro label outside of macro: '%s'", name
);
878 sprintf(newname
, "{#MAC%d:%s:%s}", curmacronum
, curmacro
->mac
->name
, name
);
880 // this is local label, let's rename it
881 sprintf(newname
, "{%s%s}", lastSeenGlobalLabel
, name
);
883 /*!fprintf(stderr, "LABEL <%s> renamed to <%s> (%d)\n", name, newname, (curSrcLine ? curSrcLine->lineNo : 0));*/
889 static char *fixGlobalLabel (const char *name
) {
890 static char newname
[MAX_LINE_SIZE
*2+1024];
892 memset(newname
, 0, sizeof(newname
));
893 if (!name
|| !name
[0]) {
895 } else if (!curModule
|| name
[0] == '.' || name
[0] == '{' || name
[0] == '@' || strchr(name
, '.')) {
896 if (name
[0] == '@' && name
[1]) ++name
;
897 strcpy(newname
, name
);
899 // this is global unqualified label and we have a module; let's rename it
900 sprintf(newname
, "%s.%s", curModule
->name
, name
);
902 //printf("%s --> %s\n", name, newname);
907 static int lblOptMakeU2
= 0;
909 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
, int *fixuptype
) {
913 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
914 lbl
= urFindLabel(nn
);
916 // try non-module label
917 lbl
= urFindLabel(ln
);
921 errorMsg("using undefined label %s", ln
);
926 lbl
= urAddLabel(nn
);
927 lbl
->type
= (lblOptMakeU2
? -42 : -1);
929 lbl
->refLine
= curSrcLine
->lineNo
;
930 lbl
->refFile
= strdup(curSrcLine
->fname
);
931 //fprintf(stderr, "new label: [%s]\n", lbl->name);
932 //fprintf(stderr, "new label: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
934 //fprintf(stderr, "label reference: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
938 *defined
= lbl
->known
!=0;
939 *fixuptype
= lbl
->fixuptype
;
954 static int isLabelDefinedOrKnown (const char *name
, uint16_t addr
, int qtype
) {
958 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
959 lbl
= urFindLabel(nn
);
961 // try non-module label
962 lbl
= urFindLabel(ln
);
965 case UR_QTYPE_DEFINED
: return lbl
? lbl
->known
!=0 : 0;
966 case UR_QTYPE_KNOWN
: return lbl
!=NULL
;
973 static void fixupOperandCB (const urasm_operand_t
*op
, uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
975 //static const char *n[4] = {"none", "word", "low", "high"};
976 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
977 addFixup(opdestaddr
, opaddr
, fixuptype
, size
);
982 static int checkLabels (void) {
984 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
985 //fprintf(stderr, ":LBL: <%s> (type=%d)\n", c->name, c->type);
987 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
990 if (c
->type
== 0) c
->known
= -1;
992 //if (wasError) longjmp(errJP, 667);
997 ///////////////////////////////////////////////////////////////////////////////
998 // expression utils (aka string parsing)
1001 /* skip leading spaces */
1002 /* returns string with spaces skipped */
1003 static inline char *strSkipSpaces (const char *s
) {
1004 while (*s
&& isSpace(*s
)) ++s
;
1009 /* skip leading spaces */
1010 /* returns string with spaces skipped */
1011 static inline const char *strSkipSpacesConst (const char *s
) {
1012 while (*s
&& isSpace(*s
)) ++s
;
1017 /* remove trailing spaces from string */
1018 static void strTrimRight (char *s
) {
1019 if (!s
|| !s
[0]) return;
1020 size_t len
= strlen(s
);
1021 while (len
> 0 && isSpace(s
[len
-1])) --len
;
1026 /* skip leading spaces and colons */
1027 /* returns string with spaces skipped */
1028 static inline char *strSkipSpacesColons (char *s
) {
1029 while (*s
&& (isSpace(*s
) || *s
== ':')) ++s
;
1034 /* remove leading spaces from the current line */
1035 static inline void removeSpaces (void) {
1036 char *e
= strSkipSpaces(currLine
);
1037 if (e
!= currLine
) memmove(currLine
, e
, strlen(e
)+1);
1041 /* skip leading spaces, and argument (up to, but not including comma or colon) */
1042 /* correctly skip strings */
1043 /* returns string after skipped argument (with trailing spaces skipped) */
1044 static char *skipMacroArg (char *str
) {
1046 char *strstart
= str
;
1048 str
= strSkipSpaces(str
);
1049 if (!str
[0]) return str
;
1050 if (parens
== 0 && (str
[0] == ',' || str
[0] == ':')) return str
;
1051 /* check for "af'" */
1052 if (str
[0] == '\'' && str
-strstart
>= 2 && toLower(str
[-2] == 'a') && toLower(str
[-1] == 'f')) {
1056 if (str
[0] == '(') { ++parens
; continue; }
1057 if (str
[0] == ')') { if (--parens
< 0) parens
= 0; continue; }
1058 /* check for string */
1059 if (str
[0] == '"' || str
[0] == '\'') {
1060 const char qch
= *str
++;
1062 const char ch
= *str
++;
1063 if (ch
== qch
) break;
1064 if (ch
== '\\' && *str
) ++str
;
1073 /* evaluate next numeric expression in input string */
1074 /* returns expression value */
1075 static int32_t getExprArg (int *defined
, int *addr
) {
1077 char *a
= strSkipSpaces(currLine
);
1079 if (!a
[0]) fatal("expression expected");
1080 const char *ee
= urasm_expr(&res
, a
, disp
, defined
, addr
, &error
);
1081 if (error
) fatalUrLib(error
);
1083 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
1084 memmove(currLine
, ee
, strlen(ee
)+1);
1092 /* evaluate next string expression in input string */
1093 /* returns expression value */
1094 static char *getStrExprArg (void) {
1096 int donteval
= 0, defined
= 0;
1097 static char resbuf
[256];
1098 char *a
= strSkipSpaces(currLine
);
1099 if (!a
[0]) fatal("expression expected");
1100 urasm_exprval_t res
;
1101 urasm_exprval_init(&res
);
1102 const char *ee
= urasm_expr_ex(&res
, a
, disp
, &donteval
, &defined
, &error
);
1103 if (error
) fatalUrLib(error
);
1105 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
1106 memmove(currLine
, ee
, strlen(ee
)+1);
1111 snprintf(resbuf
, sizeof(resbuf
), "%s", res
.str
);
1113 snprintf(resbuf
, sizeof(resbuf
), "%d", res
.val
);
1115 urasm_exprval_clear(&res
);
1120 /* evaluate next numeric expression in input string */
1121 /* there shoild be no other expressions in the string */
1122 /* returns expression value */
1123 static int32_t getOneExprArg (int *defined
, int *addr
) {
1124 int32_t res
= getExprArg(defined
, addr
);
1125 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
1130 /* is next expression a string literal? */
1131 static inline int isStrArg (void) {
1132 const char *s
= strSkipSpaces(currLine
);
1133 return (s
[0] == '"' || s
[0] == '\'');
1137 /* check of we reached end of operator */
1138 static __attribute__((unused
)) inline int isOperatorEnd (void) {
1139 const char *s
= strSkipSpaces(currLine
);
1140 return (s
[0] == 0 || s
[0] == ':');
1144 /* check of we reached end of operator */
1145 static __attribute__((unused
)) inline int isLineEnd (void) {
1146 const char *s
= strSkipSpaces(currLine
);
1151 /* parse string argument from input string */
1152 /* returns parsed string */
1153 static char *getStrArg (int *lenp
) {
1155 char *a
= strSkipSpaces(currLine
);
1157 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
1158 res
= parseStr(&a
, qCh
, lenp
);
1160 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
1161 memmove(currLine
, a
, strlen(a
)+1);
1169 /* get identifier (and lowercase it) */
1170 static char *getOneIdArgLo (void) {
1171 static char res
[MAX_LINE_SIZE
+128];
1173 char *a
= strSkipSpaces(currLine
);
1174 memset(res
, 0, sizeof(res
));
1175 if (!a
[0]) fatal("identifier expected");
1176 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
1177 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
1179 if (p
-res
> 120) fatal("identifier too long: %s", res
);
1180 while (*a
&& isSpace(*a
)) ++a
;
1182 memmove(currLine
, a
, strlen(a
)+1);
1183 if (currLine
[0] == ';') currLine
[0] = 0;
1184 if (currLine
[0]) fatal("extra arguments");
1188 for (char *t
= res
; *t
; ++t
) *t
= toLower(*t
);
1193 /* get label argument */
1194 static char *getLabelArg (int checkdelim
) {
1195 static char res
[MAX_LINE_SIZE
+128];
1197 char *a
= strSkipSpaces(currLine
);
1198 memset(res
, 0, sizeof(res
));
1199 if (!a
[0]) fatal("label expected");
1200 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isSpace(*a
); ++a
, ++p
) *p
= *a
;
1201 for (; p
> res
&& isSpace(p
[-1]); --p
) {}
1203 if (p
-res
> 120) fatal("label name too long: %s", res
);
1204 while (*a
&& isSpace(*a
)) ++a
;
1206 if (checkdelim
&& a
[0] != ',' && a
[0] != ':') fatal("bad label expression");
1207 memmove(currLine
, a
, strlen(a
)+1);
1215 /* get label argument, and ensure that it is the last one */
1216 static char *getOneLabelArg (void) {
1217 char *res
= getLabelArg(1);
1218 if (currLine
[0] && currLine
[0] != ':') fatal("too many expressions");
1223 /* returns ',' or 0 */
1224 static char eatComma (void) {
1225 char *a
= strSkipSpaces(currLine
);
1226 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
1227 if (a
[0] == ':') return 0;
1228 if (a
[0] != ',') fatal("invalid expression: ',' expected");
1229 for (++a
; *a
&& isSpace(*a
); ++a
) {}
1230 if (!a
[0]) { currLine
[0] = '\0'; return 0; }
1231 memmove(currLine
, a
, strlen(a
)+1);
1236 ///////////////////////////////////////////////////////////////////////////////
1239 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
1240 char *ep
= strSkipSpacesColons(currLine
);
1241 memmove(currLine
, ep
, strlen(ep
)+1);
1245 static void checkExprEnd (void) {
1246 char *ep
= strSkipSpaces(currLine
);
1247 memmove(currLine
, ep
, strlen(ep
)+1);
1248 if (currLine
[0] && currLine
[0] != ':') fatal("end of expression expected");
1252 static void checkOperatorEnd (void) {
1253 char *ep
= strSkipSpaces(currLine
);
1254 memmove(currLine
, ep
, strlen(ep
)+1);
1255 if (currLine
[0]) fatal("end of operator expected");
1259 /* remove label from curLine */
1260 static void removeLabel (void) {
1261 char *ep
= currLine
;
1262 if (ep
[0] && !isSpace(ep
[0])) for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {} // skip text
1263 // skip spaces and colons
1264 ep
= strSkipSpacesColons(ep
);
1265 memmove(currLine
, ep
, strlen(ep
)+1);
1269 static int labelDoEQU (const char *lblname
, const char *value
) {
1270 static char n2
[256];
1273 if (value
== NULL
|| lblname
== NULL
|| !lblname
[0] || strlen(lblname
) > 255 || strlen(value
) >= MAX_LINE_SIZE
) return -1;
1274 memset(n2
, 0, sizeof(n2
));
1275 strcpy(n2
, lblname
);
1276 if (!urasm_is_valid_name(n2
)) return -1; // this is not a valid label, get out of here
1277 // check if this can be an instruction
1278 lbl
= urAddLabel(lblname
);
1279 if (!lbl
->refFile
) {
1281 lbl
->refFile
= strdup("artificially-defined-label");
1283 strcpy(currLine
, value
);
1285 int defined
= 1, addr
= UR_FIXUP_NONE
;
1286 int32_t res
= getOneExprArg(&defined
, &addr
);
1287 lbl
->type
= 1; // equ label
1288 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1293 return -1; //fatal("can't calculate label %s", lbl->name);
1300 static void processLabel (void) {
1303 static char n2
[256];
1305 int noLocAff
= 0, doEQU
= 0;
1306 /*!fprintf(stderr, "LINE <%s> (%d)\n", curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
1307 memset(n2
, 0, sizeof(n2
));
1308 if (!currLine
[0] || isSpace(currLine
[0]) || currLine
[0] == ':') {
1309 // this may be " id = smth" or " id equ smth"
1312 while (isSpace(*ep
)) ++ep
;
1313 if (ep
[0] == ':' || !ep
[0] || (!isAlpha(ep
[0]) && ep
[0] != '_' && ep
[0] != '.' && ep
[0] != '@')) {
1314 removeLabel(); // removeLabel() removes any spaces, etc
1317 // this looks like a label; check for '=' or 'equ'
1318 while (isAlphaDigit(ep
[0]) || ep
[0] == '_' || ep
[0] == '.' || ep
[0] == '@') ++ep
;
1320 // skip trailing spaces
1321 while (isSpace(*ep
)) ++ep
;
1325 } else if (isSpace(*nn
)) {
1327 argstart
= strIsCommand("EQU", ep
);
1332 removeLabel(); // removeLabel() removes any spaces, etc
1335 // remove leading spaces from name
1339 while (isSpace(*ep
)) ++ep
;
1340 if (ep
>= nn
) fatal("internal compiler error");
1341 if (nn
-ep
> 120) fatal("label too long");
1342 memset(n2
, 0, sizeof(n2
));
1343 memmove(n2
, ep
, nn
-ep
);
1344 if (urFindOp(n2
) || !urasm_is_valid_name(n2
)) {
1345 //fatal("invalid label name");
1346 removeLabel(); // removeLabel() removes any spaces, etc
1349 // remove label name
1350 memmove(currLine
, argstart
, strlen(argstart
)+1);
1352 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
1353 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
1354 lbl
= urAddLabel(nn
);
1355 if (!lbl
->refFile
) {
1356 lbl
->refLine
= curSrcLine
->lineNo
;
1357 lbl
->refFile
= strdup(curSrcLine
->fname
);
1359 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
1360 if (doEQU
&& pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
1361 int defined
= 1, addr
= UR_FIXUP_NONE
;
1362 int32_t res
= getOneExprArg(&defined
, &addr
);
1364 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1369 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
1375 for (ep
= currLine
; *ep
&& !isSpace(*ep
) && *ep
!= ':'; ++ep
) {}
1376 if (ep
-currLine
> 120) fatal("label too long");
1378 memset(n2
, 0, sizeof(n2
));
1379 memmove(n2
, currLine
, ep
-currLine
);
1381 ep
= strSkipSpaces(ep
);
1382 if (*ep
!= ':') return; // this must be an instruction, process it
1384 if (!urasm_is_valid_name(n2
)) return; // this is not a valid label, get out of here
1386 if (findMacro(n2
)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
1387 // check if this can be instruction
1388 //ep = strSkipSpaces(ep);
1389 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
1390 // ok, we got a good label
1392 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
1393 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (curSrcLine ? curSrcLine->lineNo : 0));*/
1394 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
1395 /*!fprintf(stderr, " RENAMED(ln) <%s>\n", ln);*/
1396 /*!fprintf(stderr, " RENAMED(nn) <%s>\n", nn);*/
1397 lbl
= urAddLabel(nn
);
1398 if (!lbl
->refFile
) {
1399 lbl
->refLine
= curSrcLine
->lineNo
;
1400 lbl
->refFile
= strdup(curSrcLine
->fname
);
1402 //printf("new: [%s]\n", lbl->name);
1404 if (currLine
[0] == '=') {
1406 argstart
= currLine
+1;
1409 argstart
= strIsCommand("EQU", currLine
);
1411 if (!argstart
|| doEQU
) {
1412 if (pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
1414 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
1417 memmove(currLine
, argstart
, strlen(argstart
)+1);
1419 if (lbl
->type
!= -1 && lbl
->type
!= 0) fatal("duplicate label '%s'", lbl
->name
);
1421 int defined
= 1, addr
= UR_FIXUP_NONE
;
1422 int32_t res
= getOneExprArg(&defined
, &addr
);
1424 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1429 if (pass
!= 0) fatal("can't calculate label '%s'", lbl
->name
);
1434 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d); disp=#%04X; pc=#%04X\n", lbl->name, doEQU, disp, pc);
1436 if (lbl
->name
[0] != '{' && !noLocAff
) {
1437 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
1438 lastSeenGlobalLabel
= strdup(lbl
->name
);
1443 lbl
->fixuptype
= UR_FIXUP_WORD
;
1447 ///////////////////////////////////////////////////////////////////////////////
1448 // instruction finder (in source)
1450 /* array ends with NULL */
1451 /* returns line or NULL */
1452 /* iidx will be set to found instruction number */
1453 static __attribute__((sentinel
)) SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
1454 if (iidx
) *iidx
= -1;
1455 for (SourceLine
*cur
= curSrcLine
; cur
; cur
= cur
->next
) {
1457 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
1459 for (int f
= 0; ;++f
) {
1460 const char *name
= va_arg(ap
, const char *);
1463 if (strIsCommand(name
, cur
->line
)) {
1465 if (iidx
) *iidx
= f
;
1475 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
1476 return findNextInstructionFromList(NULL
, name
, NULL
);
1480 ///////////////////////////////////////////////////////////////////////////////
1483 static int optWriteFixups
= 0;
1484 static int optFixupType
= 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
1485 static int optRunTape
= 1;
1486 static int optRunDMB
= 1;
1487 static int optRunSCL
= -1; /* -1: save cargador, but no boot; 1: boot and cargador */
1488 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1489 static int optSNA48
= 1;
1490 static char *optOutputDir
= NULL
;
1493 ///////////////////////////////////////////////////////////////////////////////
1494 static void writeFixups (void) {
1496 void writeFixupList (FILE *fo
, int cnt
, const char *lbl
, int (*chk
)(const FixupItem
*)) {
1499 fprintf(fo
, "%s:\n", lbl
);
1500 if (optFixupType
== 1) fprintf(fo
, " dw %d ; count\n", cnt
);
1501 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1503 fprintf(fo
, " dw #%04X\n", fx
->opaddr
-prevaddr
);
1504 if (optFixupType
== 2) {
1505 prevaddr
= fx
->opaddr
;
1509 if (optFixupType
== 2 || optFixupType
== 3) fprintf(fo
, " dw 0 ; end marker\n");
1513 if (optFixupType
== 0) {
1514 /* simple text file */
1515 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.txt");
1516 printf("writing fixups to '%s'...\n", fname
);
1517 FILE *fo
= fopen(fname
, "w");
1519 if (fo
== NULL
) fatal("can't write fixup file");
1520 fprintf(fo
, "; addr dadr sz\n");
1521 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1522 static const char type
[4] = "NWLH";
1523 fprintf(fo
, "%c #%04x #%04x %d\n", type
[fx
->fixuptype
], fx
->opaddr
, fx
->opdestaddr
, fx
->size
);
1527 /* various asm formats */
1528 char *fname
= strprintf("%s/%s", optOutputDir
, "zfixuptable.zas");
1529 printf("writing fixups to '%s'...\n", fname
);
1530 FILE *fo
= fopen(fname
, "w");
1531 int cntw
= 0, cntwl
= 0, cntwh
= 0, cntbl
= 0, cntbh
= 0;
1533 if (fo
== NULL
) fatal("can't write fixup file");
1534 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1535 if (fx
->fixuptype
== UR_FIXUP_WORD
) { ++cntw
; continue; }
1536 switch (fx
->fixuptype
) {
1537 case UR_FIXUP_LOBYTE
: if (fx
->size
== 2) ++cntwl
; else ++cntbl
; break;
1538 case UR_FIXUP_HIBYTE
: if (fx
->size
== 2) ++cntwh
; else ++cntbh
; break;
1541 writeFixupList(fo
, cntw
, "fixup_table_w", lambda(int, (const FixupItem
*fx
) {
1542 return (fx
->fixuptype
== UR_FIXUP_WORD
);
1544 writeFixupList(fo
, cntwl
, "fixup_table_wl", lambda(int, (const FixupItem
*fx
) {
1545 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 2);
1547 writeFixupList(fo
, cntwh
, "fixup_table_wh", lambda(int, (const FixupItem
*fx
) {
1548 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 2);
1550 writeFixupList(fo
, cntbl
, "fixup_table_bl", lambda(int, (const FixupItem
*fx
) {
1551 return (fx
->fixuptype
== UR_FIXUP_LOBYTE
&& fx
->size
== 1);
1553 writeFixupList(fo
, cntbh
, "fixup_table_bh", lambda(int, (const FixupItem
*fx
) {
1554 return (fx
->fixuptype
== UR_FIXUP_HIBYTE
&& fx
->size
== 1);
1561 ///////////////////////////////////////////////////////////////////////////////
1562 /* return 'found' flag */
1563 static int findChunkFrom (int addr
, int *start
, int *len
) {
1564 if (addr
< 0) addr
= 0;
1565 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) {}
1566 if (addr
> 65535) return 0;
1568 for (; addr
<= 65535 && memused
[addr
]; ++addr
) {}
1569 *len
= addr
-(*start
);
1574 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
1575 for (; buflen
>= 2; buflen
-= 2) {
1577 uint8_t byte
= *buf
++;
1581 for (cnt
= byte
; cnt
>= 0; --cnt
, ++addr
, --buflen
, ++buf
) {
1582 if (!memused
[addr
]) putByte(addr
, *buf
);
1583 if (addr
== 0xffff) return;
1587 for (; cnt
> 0; --cnt
, ++addr
) {
1588 if (!memused
[addr
]) putByte(addr
, byte
);
1589 if (addr
== 0xffff) return;
1596 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
1597 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1598 if (bxor
) *bxor
= (*bxor
)^b
;
1603 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
1604 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
1605 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
1610 ///////////////////////////////////////////////////////////////////////////////
1613 static int saveSna (const char *fname
, int as48
) {
1614 char *fn
;// = malloc(strlen(fname)+16);
1619 fn
= strprintf("%s/%s.sna", optOutputDir
, fname
);
1620 fo
= fopen(fn
, "wb");
1622 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
1623 printf("out: %s.sna\n", fname
);
1624 memmove(regs
, ursna48
, 27);
1627 /* push new address */
1628 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1630 putByte(sp
, ent
&0xFFU
);
1631 putByte(sp
+1, (ent
>>8)&0xFFU
);
1632 regs
[23] = sp
&0xFFU
;
1633 regs
[24] = (sp
>>8)&0xFFU
;
1635 fwrite(regs
, 27, 1, fo
);
1636 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
1637 fwrite(memory
+16384, 49152, 1, fo
);
1640 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1642 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1643 //fprintf(stderr, "%d\n", memused[sp]);
1644 if (memused
[sp
] || memused
[(sp
+1)&0xffff]) {
1645 fprintf(stderr
, "FATAL: can't save snapshot!\n");
1649 if (fwrite(ursna48
, 27, 1, fo
) != 1) goto error
;
1650 rleUnpack(16384, (const uint8_t *)ursna48
+27, sizeof(ursna48
)-27);
1651 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1653 sprintf(abuf
, "%05d", clrAddr
);
1654 memcpy(memory
+23762, abuf
, 5);
1655 sprintf(abuf
, "%05d", ent
);
1656 memcpy(memory
+23773, abuf
, 5);
1658 if (fwrite(memory
+16384, 49152, 1, fo
) != 1) goto error
;
1661 int addr
= memory
[sp
]+256*memory
[(sp
+1)&0xffff];
1663 if (fWriteWord(fo
, addr
, NULL
) != 0) goto error
; // PC
1664 if (fWriteByte(fo
, 0x10, NULL
) != 0) goto error
; // 7FFD
1665 if (fWriteByte(fo
, 0, NULL
) != 0) goto error
; // TR-DOS inactive
1667 memset(memory
, 0, 16384);
1668 for (int f
= 1; f
< 8; ++f
) {
1669 if (f
!= 2 && f
!= 5) {
1670 if (fwrite(memory
, 16384, 1, fo
) != 1) goto error
;
1684 ///////////////////////////////////////////////////////////////////////////////
1687 static void saveRaw (const char *basename
) {
1688 char *fname
= NULL
;// = malloc(strlen(basename)+16);
1692 while (findChunkFrom(start
, &start
, &len
)) {
1693 if (fname
!= NULL
) free(fname
);
1694 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, (optTapExt
? "tap" : "bin"));
1695 fo
= fopen(fname
, "wb");
1697 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1699 printf("out: %s\n", fname
);
1700 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1701 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1710 if (fname
!= NULL
) free(fname
);
1714 ///////////////////////////////////////////////////////////////////////////////
1717 typedef struct __attribute__((packed
)) __attribute__((gcc_struct
)) {
1722 uint8_t zero
; // always zero
1723 uint8_t secLen
; // length in sectors
1728 static uint16_t calcHobSum (const void *hdr
) {
1729 const uint8_t *buf
= (const uint8_t *)hdr
;
1732 for (unsigned int f
= 0; f
< 15; ++f
) res
+= ((uint16_t)buf
[f
])*257+f
;
1737 static void saveHob (const char *basename
) {
1738 char *fname
= NULL
;//malloc(strlen(basename)+16);
1743 while (findChunkFrom(start
, &start
, &len
)) {
1744 if (fname
!= NULL
) free(fname
);
1745 fname
= strprintf("%s/%s_%04X.%s", optOutputDir
, basename
, start
, "$C");
1746 fo
= fopen(fname
, "wb");
1748 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1751 char tmpbuf
[sizeof(hdr
.name
)*2];
1752 printf("out: %s\n", fname
);
1753 memset(&hdr
, 0, sizeof(hdr
));
1754 memset(&hdr
.name
, 32, 8);
1755 snprintf(tmpbuf
, sizeof(tmpbuf
), "%c%04X", basename
[0], start
);
1756 while (strlen(tmpbuf
) < sizeof(hdr
.name
)) strcat(tmpbuf
, " "); /* sorry! */
1757 memcpy(hdr
.name
, tmpbuf
, sizeof(hdr
.name
));
1761 hdr
.secLen
= (len
+255)/256;
1762 hdr
.checksum
= calcHobSum(&hdr
);
1763 if (fwrite(&hdr
, sizeof(hdr
), 1, fo
) != 1) {
1764 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1769 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1770 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1777 if (fwrite(&hdr
.zero
, 1, 1, fo
) != 1) {
1778 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1784 //fprintf(stderr, ":%d\n", len%256);
1790 if (fname
!= NULL
) free(fname
);
1794 // ////////////////////////////////////////////////////////////////////////// //
1797 uint8_t dir
[14*256];
1799 uint32_t currfstartpos
;
1806 static void sclInit (SCLFile
*scl
) {
1807 memset(scl
, 0, sizeof(*scl
));
1808 scl
->datasize
= 2*80*16*256; /* maximum disk size */
1809 scl
->data
= malloc(scl
->datasize
);
1810 memset(scl
->data
, 0, scl
->datasize
);
1811 scl
->currfstartpos
= 0xffffffffu
;
1815 static void sclClear (SCLFile
*scl
) {
1817 if (scl
->data
) free(scl
->data
);
1818 memset(scl
, 0, sizeof(*scl
));
1819 scl
->currfstartpos
= 0xffffffffu
;
1823 static void sclStartFile (SCLFile
*scl
, const char *name
, char type
, uint16_t v0
, uint16_t v1
) {
1824 if (scl
->fcount
== 255) {
1825 fprintf(stderr
, "FATAL: too many files in SCL!\n");
1828 if (scl
->currfstartpos
!= 0xffffffffu
) {
1829 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
1833 while (nlen
< 8 && name
&& name
[nlen
]) scl
->dir
[scl
->dirpos
++] = name
[nlen
++];
1834 while (nlen
< 8) { scl
->dir
[scl
->dirpos
++] = ' '; ++nlen
; }
1835 scl
->dir
[scl
->dirpos
++] = type
;
1836 scl
->dir
[scl
->dirpos
++] = v0
&0xff;
1837 scl
->dir
[scl
->dirpos
++] = (v0
>>8)&0xff;
1838 scl
->dir
[scl
->dirpos
++] = v1
&0xff;
1839 scl
->dir
[scl
->dirpos
++] = (v1
>>8)&0xff;
1840 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
1841 scl
->currfstartpos
= scl
->datapos
;
1845 static void sclEndFile (SCLFile
*scl
) {
1846 if (scl
->currfstartpos
== 0xffffffffu
) {
1847 fprintf(stderr
, "FATAL: last SCL file already finished!\n");
1850 /* align to sector */
1851 while (scl
->datapos
%256) scl
->data
[scl
->datapos
++] = 0;
1852 const uint32_t fsz
= scl
->datapos
-scl
->currfstartpos
;
1853 if (fsz
> 255*256) {
1854 fprintf(stderr
, "FATAL: SCL file too big!\n");
1857 if (fsz
&255) abort();
1858 scl
->dir
[scl
->dirpos
++] = fsz
/256; /* size in sectors */
1859 if (scl
->dirpos
%14) abort();
1861 scl
->currfstartpos
= 0xffffffffu
;
1865 static void sclWriteData (SCLFile
*scl
, const void *buf
, size_t len
) {
1866 if (scl
->currfstartpos
== 0xffffffffu
) {
1867 fprintf(stderr
, "FATAL: no open SCL file!\n");
1871 if (len
> 255*256) {
1872 fprintf(stderr
, "FATAL: SCL file too big!\n");
1875 memcpy(scl
->data
+scl
->datapos
, buf
, len
);
1876 scl
->datapos
+= len
;
1880 static void sclWriteByte (SCLFile
*scl
, uint8_t b
) {
1881 sclWriteData(scl
, &b
, 1);
1885 static void sclWriteWord (SCLFile
*scl
, uint16_t w
) {
1887 sclWriteData(scl
, &b
, 1);
1889 sclWriteData(scl
, &b
, 1);
1893 static int sclFileWrite (FILE *fo
, const void *buf
, size_t count
, uint32_t *checksum
) {
1894 if (!count
) return 0;
1895 const uint8_t *p
= (const uint8_t *)buf
;
1896 if (checksum
) for (size_t n
= count
; n
--; ++p
) *checksum
+= (uint32_t)(*p
);
1897 if (fwrite(buf
, count
, 1, fo
) != 1) return -1;
1903 static int sclSaveToFile (FILE *fo
, SCLFile
*scl
) {
1904 if (scl
->currfstartpos
!= 0xffffffffu
) {
1905 fprintf(stderr
, "FATAL: last SCL file is not finished!\n");
1908 const char *sign
= "SINCLAIR";
1909 uint32_t checksum
= 0;
1911 if (sclFileWrite(fo
, sign
, 8, &checksum
) != 0) return -1;
1912 if (sclFileWrite(fo
, &scl
->fcount
, 1, &checksum
) != 0) return -1;
1914 if (sclFileWrite(fo
, scl
->dir
, scl
->dirpos
, &checksum
) != 0) return -1;
1916 if (sclFileWrite(fo
, scl
->data
, scl
->datapos
, &checksum
) != 0) return -1;
1918 for (unsigned f
= 0; f
< 4; ++f
) {
1919 const uint8_t b
= (checksum
>>(f
*8))&0xff;
1920 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1927 // ////////////////////////////////////////////////////////////////////////// //
1928 static void saveSCLCargador (SCLFile
*scl
) {
1929 static uint8_t cargador
[16384]; // should be enough for everyone
1932 int start
= 0, len
, pos
;
1934 void putStr (const char *s
) {
1935 for (; *s
; ++s
) cargador
[pos
++] = *s
;
1938 void putNum (int num
) {
1940 sprintf(buf
, "%d", num
);
1948 cargador
[pos
++] = (linenum
>>8)&0xff;
1949 cargador
[pos
++] = linenum
&0xff;
1950 // size (will be fixed later)
1951 cargador
[pos
++] = 0;
1952 cargador
[pos
++] = 0;
1956 if (linestart
>= 0) {
1957 const int size
= pos
-linestart
-4;
1958 cargador
[linestart
+2] = size
&0xff;
1959 cargador
[linestart
+3] = (size
>>8)&0xff;
1969 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\"");
1972 while (findChunkFrom(start
, &start
, &len
)) {
1973 // :RANDOMIZE USR VAL "15619":REM:LOAD "
1974 if (cont
) { putStr(":"); cont
= 0; }
1975 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
1976 // generate chunk name
1977 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
1986 if (cont
) { putStr(":"); cont
= 0; }
1987 // RANDOMIZE USR VAL "xxx"
1988 putStr("\xf9\xc0\xb0\""); putNum(ent
); putStr("\"");
1993 //putWord(1); // autostart line
1996 sclStartFile(scl
, "CARGADOR", 'B', (unsigned)pos
, (unsigned)pos
);
1997 sclWriteData(scl
, cargador
, (size_t)pos
);
1998 sclWriteByte(scl
, 0x80);
1999 sclWriteByte(scl
, 0xaa);
2000 sclWriteWord(scl
, 1);
2005 static void saveSCL (const char *basename
) {
2009 char *fname
= strprintf("%s/%s.scl", optOutputDir
, basename
);
2012 while (findChunkFrom(start
, &start
, &len
)) {
2016 if (fcount
&& optRunSCL
) fcount
+= (optRunSCL
> 0 ? 2 : 1); // +loader and boot
2018 fprintf(stderr
, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname
, fcount
);
2021 // create output file
2022 FILE *fo
= fopen(fname
, "wb");
2024 fprintf(stderr
, "ERROR: can't write file '%s'!\n", fname
);
2027 // initialise SCL writer
2030 if (fcount
&& optRunSCL
) {
2031 // create simple boot
2032 if (optRunSCL
> 0) {
2033 const uint8_t dasboot
[] = {
2034 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"
2036 sclStartFile(&scl
, "boot", 'B', sizeof(dasboot
)+4, sizeof(dasboot
)+4);
2037 sclWriteWord(&scl
, 1); // line number
2038 sclWriteWord(&scl
, (uint16_t)sizeof(dasboot
)); // line size
2039 sclWriteData(&scl
, dasboot
, sizeof(dasboot
)); // line data
2041 sclWriteByte(&scl
, 0x80);
2042 sclWriteByte(&scl
, 0xaa);
2044 sclWriteWord(&scl
, 0);
2047 saveSCLCargador(&scl
);
2052 while (findChunkFrom(start
, &start
, &len
)) {
2053 snprintf(cname
, sizeof(cname
), "c%04X", (unsigned)start
);
2054 sclStartFile(&scl
, cname
, 'C', (unsigned)start
, (unsigned)len
);
2055 sclWriteData(&scl
, memory
+(unsigned)start
, (unsigned)len
);
2059 if (sclSaveToFile(fo
, &scl
) < 0) {
2060 fprintf(stderr
, "ERROR: error writing file '%s'!\n", fname
);
2067 printf("out: %s\n", fname
);
2068 if (fname
!= NULL
) free(fname
);
2072 ///////////////////////////////////////////////////////////////////////////////
2075 static int saveDMB (const char *fname
) {
2076 char *fn
;// = malloc(strlen(fname)+16);
2081 fn
= strprintf("%s/%s.dmb", optOutputDir
, fname
);
2082 fo
= fopen(fn
, "wb");
2084 if (!fo
) { fprintf(stderr
, "ERROR: can't write DMB file: %s.dmb\n", fname
); return -1; }
2086 while (findChunkFrom(start
, &start
, &len
)) { ++pcnt
; start
+= len
; }
2088 printf("out: %s.dmb\n", fname
);
2089 if (fwrite("DMB1", 4, 1, fo
) != 1) goto error
;
2090 if (fWriteWord(fo
, (optRunDMB
? ent
: 0), NULL
) != 0) goto error
;
2091 if (fWriteWord(fo
, clrAddr
, NULL
) != 0) goto error
;
2092 if (fWriteWord(fo
, pcnt
, NULL
) != 0) goto error
;
2095 while (findChunkFrom(start
, &start
, &len
)) {
2096 if (fWriteWord(fo
, len
, NULL
) != 0) goto error
;
2097 if (fWriteWord(fo
, start
, NULL
) != 0) goto error
;
2098 if (fwrite(memory
+start
, len
, 1, fo
) != 1) goto error
;
2106 fprintf(stderr
, "ERROR: error writing file %s.dmb!\n", fname
);
2111 ///////////////////////////////////////////////////////////////////////////////
2114 static char tapeLoaderName
[16];
2117 static void saveTapCargador (FILE *fo
) {
2119 static uint8_t cargador
[16384]; // should be enough for everyone
2120 int start
= 0, len
, pos
, f
;
2124 void putStr (const char *s
) {
2125 for (; *s
; ++s
) cargador
[pos
++] = *s
;
2128 void putNum (int num
) {
2130 sprintf(buf
, "%d", num
);
2137 cargador
[0] = 0; cargador
[1] = 10;
2138 // size (will be fixed later)
2139 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\""); // CLEAR VAL "xxx"
2141 while (findChunkFrom(start
, &start
, &len
)) {
2143 putStr(":\xef\"\"\xaf");
2147 // :RANDOMIZE USR VAL "xxx"
2148 putStr(":\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
2150 cargador
[2] = (pos
-4)&0xff;
2151 cargador
[3] = ((pos
-4)>>8)&0xff;
2153 fWriteWord(fo
, 19, NULL
); // length of header
2155 fWriteByte(fo
, 0, &bxor
); // header block
2156 fWriteByte(fo
, 0, &bxor
); // 'basic' flag
2157 if (tapeLoaderName
[0]) {
2158 for (int f
= 0; f
< 10; ++f
) fWriteByte(fo
, tapeLoaderName
[f
], &bxor
);
2160 fWriteByte(fo
, 'c', &bxor
);
2161 fWriteByte(fo
, 'a', &bxor
);
2162 fWriteByte(fo
, 'r', &bxor
);
2163 fWriteByte(fo
, 'g', &bxor
);
2164 fWriteByte(fo
, 'a', &bxor
);
2165 fWriteByte(fo
, 'd', &bxor
);
2166 fWriteByte(fo
, 'o', &bxor
);
2167 fWriteByte(fo
, 'r', &bxor
);
2168 fWriteByte(fo
, ' ', &bxor
);
2169 fWriteByte(fo
, ' ', &bxor
);
2171 fWriteWord(fo
, pos
, &bxor
); // length
2172 fWriteWord(fo
, 10, &bxor
); // start
2173 fWriteWord(fo
, pos
, &bxor
); // length2
2174 fWriteByte(fo
, bxor
, NULL
);
2176 fWriteWord(fo
, pos
+2, NULL
); // plus type and checkbyte
2178 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
2179 for (f
= 0; f
< pos
; ++f
) fWriteByte(fo
, cargador
[f
], &bxor
);
2180 fWriteByte(fo
, bxor
, NULL
);
2184 static void saveTap (const char *basename
) {
2185 char *fname
;// = malloc(strlen(basename)+16);
2187 int start
= 0, len
, f
;
2191 fname
= strprintf("%s/%s.tap", optOutputDir
, basename
);
2192 fo
= fopen(fname
, "wb");
2194 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
2195 printf("out: %s.tap\n", basename
);
2196 if (optRunTape
) saveTapCargador(fo
);
2197 while (findChunkFrom(start
, &start
, &len
)) {
2199 if (tapeLoaderName
[0]) {
2200 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2201 memcpy(blkname
, tapeLoaderName
, 10);
2203 sprintf(blkname
, "c%04X:%04X", start
, len
);
2205 //printf(" block: %s\n", blkname);
2206 fWriteWord(fo
, 19, NULL
); // length of header
2208 fWriteByte(fo
, 0, &bxor
); // header block
2209 fWriteByte(fo
, 3, &bxor
); // 'code' flag
2210 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
2211 fWriteWord(fo
, len
, &bxor
);
2212 fWriteWord(fo
, start
, &bxor
);
2213 fWriteWord(fo
, 32768, &bxor
);
2214 fWriteByte(fo
, bxor
, NULL
);
2216 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
2218 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
2219 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
2220 fWriteByte(fo
, bxor
, NULL
);
2227 ///////////////////////////////////////////////////////////////////////////////
2228 // pseudoinstructions
2230 // note that processCurrentLine() will NOT skip to the next line before
2231 // calling pseudoinstruction handler!
2232 // note that processCurrentLine() will skip current line after calling
2233 // pseudoinstruction handler!
2235 static int wasOrg
= 0;
2236 static int wasClr
= 0;
2239 // ////////////////////////////////////////////////////////////////////////// //
2240 // print message using printf-like syntax
2241 // doesn't print new line
2242 static void processPrintf (FILE *fo
, const char *fmt
) {
2243 if (!fmt
|| !fmt
[0]) return;
2244 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
2245 char *tempstr
= NULL
;
2246 size_t tempsize
= 0;
2247 char *fmtcopy
= malloc(strlen(fmt
)+1);
2248 strcpy(fmtcopy
, fmt
);
2249 char *currfmt
= fmtcopy
;
2257 char *prcs
= strchr(currfmt
, '%');
2258 if (!prcs
|| !prcs
[1]) {
2259 /* no more formatting; print the tail and exit the loop */
2260 fprintf(fo
, "%s", currfmt
);
2265 if (prcs
[1] == '%') {
2269 /* print up to `prcs` */
2270 if (prcs
> currfmt
) {
2271 size_t partlen
= (ptrdiff_t)(prcs
-currfmt
);
2272 if (partlen
+1 > tempsize
) {
2273 tempsize
= ((partlen
+8)|0xff)+1;
2274 tempstr
= realloc(tempstr
, tempsize
);
2276 memcpy(tempstr
, currfmt
, partlen
);
2277 tempstr
[partlen
] = 0;
2278 fprintf(fo
, "%s", tempstr
);
2280 currfmt
= ++prcs
; /* skip percent */
2281 if (!docheck
) continue;
2287 if (*currfmt
== '+' || *currfmt
== '-') sign
= *currfmt
++;
2288 if (sign
!= '-' && *currfmt
== '0') zerofill
= 1;
2289 while (isDigit(*currfmt
)) { width
= width
*10+((*currfmt
)-'0'); ++currfmt
; }
2290 if (width
> 256) width
= 256;
2292 if (!ftype
) break; /* oops */
2293 if (!eatComma()) fatal("out of arguments for string format");
2295 case 's': /* string */
2297 switch (strSkipSpaces(currLine
)[0]) {
2298 case '"': case '\'': /* string literal? */
2299 strarg
= getStrArg(&stlen
);
2301 default: /* expression */
2302 strarg
= getStrExprArg();
2303 stlen
= (int)strlen(strarg
);
2307 if (sign
!= '-' && stlen
< width
) {
2308 int padlen
= width
-stlen
;
2309 memset(tempbuf
, ' ', padlen
);
2310 tempbuf
[padlen
+1] = 0;
2311 fprintf(fo
, "%s", tempbuf
);
2313 fprintf(fo
, "%s", strarg
);
2315 if (sign
== '-' && stlen
< width
) {
2316 int padlen
= width
-stlen
;
2317 memset(tempbuf
, ' ', padlen
);
2318 tempbuf
[padlen
+1] = 0;
2319 fprintf(fo
, "%s", tempbuf
);
2322 case 'd': /* decimal */
2324 exprval
= getExprArg(&defined
, NULL
);
2325 if (width
&& zerofill
) {
2326 fprintf(fo
, "%0*d", width
, exprval
);
2328 fprintf(fo
, "%*d", (sign
!= '-' ? width
: -width
), exprval
);
2330 fprintf(fo
, "%d", exprval
);
2333 case 'c': /* char */
2335 exprval
= getExprArg(&defined
, NULL
);
2336 if (exprval
<= 0 || exprval
== '?' || exprval
> 255) exprval
= '?';
2338 fprintf(fo
, "%*c", (sign
!= '-' ? width
: -width
), exprval
);
2340 fprintf(fo
, "%c", exprval
);
2343 case 'u': /* unsigned */
2345 exprval
= getExprArg(&defined
, NULL
);
2346 if (width
&& zerofill
) {
2347 fprintf(fo
, "%0*u", width
, (unsigned)(exprval
&0xffff));
2349 fprintf(fo
, "%*u", (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
2351 fprintf(fo
, "%u", (unsigned)(exprval
&0xffff));
2354 case 'x': case 'X': /* hex */
2356 exprval
= getExprArg(&defined
, NULL
);
2357 if (width
&& zerofill
) {
2358 fprintf(fo
, (ftype
== 'x' ? "%0*x" : "%0*X"), width
, (unsigned)(exprval
&0xffff));
2360 fprintf(fo
, (ftype
== 'x' ? "%*x" : "%*X"), (sign
!= '-' ? width
: -width
), (unsigned)(exprval
&0xffff));
2362 fprintf(fo
, (ftype
== 'x' ? "%x" : "%X"), (unsigned)(exprval
&0xffff));
2366 if (ftype
<= 0 || ftype
== 127) ftype
= '?';
2367 fatal("invalid format specifier: '%c'", ftype
);
2371 if (tempstr
) free(tempstr
);
2376 ///////////////////////////////////////////////////////////////////////////////
2379 static int piERROR (void) {
2381 char *res
= getStrArg(&len
);
2382 fprintf(stdout
, "*** USER ERROR: ");
2383 processPrintf(stdout
, res
);
2384 fputc('\n', stdout
);
2385 fatal("user error abort");
2386 return PI_SKIP_LINE
;
2390 static int piWARNING (void) {
2392 char *res
= getStrArg(&len
);
2393 fprintf(stdout
, "*** USER WARNING ");
2395 fprintf(stdout
, "at file %s, line %d: ", curSrcLine
->fname
, curSrcLine
->lineNo
);
2397 fprintf(stdout
, "somewhere in time: ");
2399 processPrintf(stdout
, res
);
2400 fputc('\n', stdout
);
2401 return PI_SKIP_LINE
;
2405 ///////////////////////////////////////////////////////////////////////////////
2408 static int piPrintfCommon (int passNo
) {
2409 if (passNo
< 0 || pass
== passNo
) {
2411 char *res
= getStrArg(&len
);
2412 processPrintf(stdout
, res
);
2413 fputc('\n', stdout
);
2415 return PI_SKIP_LINE
;
2419 static int piDISPLAYX (int passNo
, int asHex
) {
2423 char *res
= getStrArg(&len
);
2425 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
2428 int32_t v
= getExprArg(&defined
, NULL
);
2430 if (passNo
< 0 || pass
== passNo
) {
2431 if (asHex
) printf("%04X", (unsigned int)v
);
2432 else printf("%d", v
);
2435 if (!eatComma()) break;
2437 return PI_SKIP_LINE
;
2441 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
2442 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
2443 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
2444 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
2445 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
2446 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
2448 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
2449 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
2450 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
2453 ///////////////////////////////////////////////////////////////////////////////
2456 static int piORG (void) {
2458 int32_t res
= getOneExprArg(&defined
, NULL
);
2460 if (!defined
) fatal("sorry, ORG operand value must be known here");
2461 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
2462 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
2467 if (!wasClr
&& res
> 0) clrAddr
= res
-1;
2469 return PI_CONT_LINE
;
2473 static int piDISP (void) {
2475 int32_t res
= getOneExprArg(&defined
, NULL
);
2477 if (!defined
) fatal("sorry, DISP operand value must be known here");
2478 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
2479 //printf("DISP=%d\n", res);
2481 return PI_CONT_LINE
;
2485 static int piENDDISP (void) {
2486 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
2489 return PI_CONT_LINE
;
2493 static int piENT (void) {
2495 int32_t res
= getOneExprArg(&defined
, NULL
);
2497 //if (!defined) fatal("sorry, ENT operand value must be known here");
2498 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
2500 return PI_CONT_LINE
;
2504 static int piCLR (void) {
2506 int32_t res
= getOneExprArg(&defined
, NULL
);
2508 //if (!defined) fatal("sorry, CLR operand value must be known here");
2509 if (res
< 0 || res
> 65535) fatal("invalid CLR operand value: %d", res
);
2512 return PI_CONT_LINE
;
2516 static int piRESERVE (void) {
2518 int defined = 1, start;
2519 int32_t res = getExprArg(&defined, NULL);
2520 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2521 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2523 if (!eatComma()) fatal("RESERVE needs 2 args");
2524 res = getOneExprArg(&defined, NULL);
2525 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2526 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2527 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
2529 fatal("RESERVE: not yet!");
2530 return PI_CONT_LINE
;
2534 static int piALIGN (void) {
2538 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
2539 res
= getOneExprArg(&defined
, NULL
);
2540 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
2541 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
2542 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
2543 if (res
> 0 && pc
%res
!= 0) {
2549 return PI_CONT_LINE
;
2553 static int piDISPALIGN (void) {
2555 int32_t res
= getOneExprArg(&defined
, NULL
);
2557 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
2558 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
2559 if (res
> 0 && disp
%res
!= 0) {
2564 return PI_CONT_LINE
;
2568 ///////////////////////////////////////////////////////////////////////////////
2571 static int defIncr
= 0;
2574 static int piDEFINCR (void) {
2576 int32_t cnt
= getOneExprArg(&defined
, NULL
);
2578 if (!defined
) fatal("DEFINCR: increment must be defined");
2580 return PI_CONT_LINE
;
2584 static int piDEFBW (int isWord
) {
2586 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2590 char *res
= getStrArg(&len
);
2592 for (f
= 0; f
< len
; ++f
) {
2593 int32_t b
= (uint8_t)res
[f
];
2595 if (b
< -128 || b
> 255) fatal("invalid operand value: %d", b
);
2596 if (b
< 0) b
+= 256;
2600 int32_t res
= getExprArg(&defined
, &fixuptype
);
2602 if (pass
> 0 && !defined
) fatal("undefined operand");
2605 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
2606 if (res
< 0) res
+= 65536;
2608 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 2);
2612 switch (fixuptype
) {
2613 case UR_FIXUP_WORD
: /* swapped bytes */
2614 urasm_fixup_operand(NULL
, pc
, disp
, UR_FIXUP_HIBYTE
, 1);
2615 urasm_fixup_operand(NULL
, pc
, disp
+1, UR_FIXUP_LOBYTE
, 1);
2617 case UR_FIXUP_LOBYTE
:
2618 case UR_FIXUP_HIBYTE
:
2619 warningMsg("non-word fixup for reversed word; wtf?!");
2626 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
2627 if (fixuptype
!= UR_FIXUP_NONE
) {
2628 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
2629 urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
2631 if (res
< 0) res
+= 256;
2635 if (!eatComma()) break;
2637 return PI_CONT_LINE
;
2640 static int piDEFB (void) { return piDEFBW(0); }
2641 static int piDEFW (void) { return piDEFBW(1); }
2642 static int piDEFR (void) { return piDEFBW(2); }
2645 static int piDEFS (void) {
2648 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
2649 int32_t res
= getExprArg(&defined
, &fixuptype
);
2651 if (pass
> 0 && !defined
) fatal("undefined operand");
2652 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
2653 if (*currLine
&& currLine
[0] == ',') {
2655 bt
= getExprArg(&defined
, NULL
);
2656 if (pass
> 0 && !defined
) fatal("undefined operand");
2658 if (bt
< -128 || bt
> 255) fatal("invalid operand value: %d", res
);
2659 if (bt
< 0) bt
+= 256;
2660 if (fixuptype
!= UR_FIXUP_NONE
) {
2661 if (fixuptype
== UR_FIXUP_WORD
) warningMsg("invalid fixup for operand");
2663 for (f
= 0; f
< res
; ++f
) {
2664 if (fixuptype
!= UR_FIXUP_NONE
) urasm_fixup_operand(NULL
, pc
, disp
, fixuptype
, 1);
2668 pc
+= res
; disp
+= res
;
2670 if (!eatComma()) break;
2672 return PI_CONT_LINE
;
2676 /* bit 0: put '\0' */
2677 /* bit 1: set bit 7 of last byte */
2678 /* bit 2: put length */
2679 static int piDEFSTR (int type
) {
2683 char *res
= getStrArg(&len
);
2686 if (len
> 255) fatal("string too long");
2689 for (f
= 0; f
< len
; ++f
) {
2690 uint8_t b
= (uint8_t)res
[f
];
2692 if ((type
&0x02) && f
== len
-1) b
|= 0x80;
2695 if (type
&0x01) emitByte(0);
2698 int32_t v
= getExprArg(&defined
, NULL
);
2700 if (pass
> 0 && !defined
) fatal("undefined expression");
2701 if (!defined
) v
= 0; else v
+= defIncr
;
2702 if (v
< -128 || v
> 255) fatal("invalid operand value: %d", v
);
2703 if (v
< 0) v
+= 256;
2706 if (!eatComma()) break;
2708 return PI_CONT_LINE
;
2712 static int piDEFM (void) { return piDEFSTR(0x00); }
2713 static int piDEFZ (void) { return piDEFSTR(0x01); }
2714 static int piDEFX (void) { return piDEFSTR(0x02); }
2715 static int piDEFC (void) { return piDEFSTR(0x04); }
2718 ///////////////////////////////////////////////////////////////////////////////
2721 /* INCBIN "name"[,maxlen] */
2722 static int piINCBIN (void) {
2728 char *args
= currLine
;
2730 if (!currLine
[0]) fatal("INCBIN without file name");
2734 } else if (currLine
[0] == '<') {
2741 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
2742 if (!fn
[0]) fatal("INCBIN: empty file name");
2743 memmove(currLine
, args
, strlen(args
)+1);
2745 if (currLine
[0] == ',') {
2748 maxlen
= getOneExprArg(&defined
, NULL
);
2749 if (!defined
) fatal("INCBIN: undefined maxlen");
2750 if (maxlen
< 1) return 1; // nothing to do
2753 char *fname
= createIncludeName(fn
, system
, NULL
);
2754 fl
= fopen(fname
, "rb");
2755 if (!fl
) fatal("INCBIN: file not found: '%s'", fname
);
2756 while (maxlen
-- > 0) {
2757 int res
= fread(&bt
, 1, 1, fl
);
2759 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: '%s'", fname
); }
2764 return PI_SKIP_LINE
;
2768 ///////////////////////////////////////////////////////////////////////////////
2771 /* INCLUDE "name" */
2772 static int piINCLUDE (void) {
2775 char *args
= currLine
;
2777 if (!currLine
[0]) fatal("INCLUDE without file name");
2781 } else if (currLine
[0] == '<') {
2788 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
2789 if (!fn
[0]) fatal("INCLUDE: empty file name");
2791 if (asmTextInclude(fn
, system
) != 0) fatal("INCLUDE: some shit happens!");
2792 return PI_SKIP_LINE
;
2796 ///////////////////////////////////////////////////////////////////////////////
2797 // MODULE, ENDMODULE
2799 static int piENDMODULE (void) {
2800 if (!curModule
) fatal("ENDMODULE without MODULE");
2802 char *mn
= getOneLabelArg();
2803 if (strcmp(mn
, curModule
->name
)) fatal("invalid module name in ENDMODULE: %s", mn
);
2806 return PI_SKIP_LINE
;
2810 static int piMODULE (void) {
2813 SourceLine
*ol
= curSrcLine
;
2816 if (curModule
) fatal("no nested modules allowed");
2817 mn
= getOneLabelArg();
2818 if (!urasm_is_valid_name(mn
)) fatal("invalid module name: %s", mn
);
2819 mi
= moduleFind(mn
);
2820 //printf("[%s] %p\n", mn, mi);
2823 if (strcmp(mi
->fname
, curSrcLine
->fname
)) fatal("duplicate module definition; previous was in %s", mi
->fname
);
2825 nextSrcLine(); /* skip "MODULE" line */
2826 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
2828 fatal("no ENDMODULE");
2830 if (inum
== 0) fatal("no nested modules allowed");
2832 //skipInstruction(); //k8:wtf?!
2833 return piENDMODULE();
2836 mi
= moduleAdd(mn
, curSrcLine
->fname
);
2840 return PI_SKIP_LINE
;
2845 static int piMODEL (void) {
2846 char *mn
= getOneIdArgLo();
2847 if (strSkipSpaces(currLine
)[0]) fatal("only one model name expected");
2848 if (strcmp(mn
, "z80") == 0) urasm_allow_zxnext
= 0;
2849 else if (strcmp(mn
, "z80a") == 0) urasm_allow_zxnext
= 0;
2850 else if (strcmp(mn
, "z80n") == 0) urasm_allow_zxnext
= 1;
2851 else if (strcmp(mn
, "z80next") == 0) urasm_allow_zxnext
= 1;
2852 else if (strcmp(mn
, "zxnext") == 0) urasm_allow_zxnext
= 1;
2853 else fatal("invalid model name: %s", mn
);
2854 return PI_SKIP_LINE
;
2858 ///////////////////////////////////////////////////////////////////////////////
2861 static int piEDUP (void) {
2862 fatal("EDUP without DUP");
2864 return PI_SKIP_LINE
;
2868 static int piDUP (void) {
2869 int defined
= 1, dupCnt
= 1, inum
;
2870 SourceLine
*stline
, *eline
= NULL
;
2871 int32_t cnt
= getOneExprArg(&defined
, NULL
);
2873 if (!defined
) fatal("DUP: counter must be defined");
2874 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
2875 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
2876 // now find corresponding EDUP
2877 // note that we should skip nested DUPs
2878 nextSrcLine(); // skip ourself
2879 stline
= curSrcLine
;
2880 while (curSrcLine
) {
2881 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "EDUP", NULL
))) break;
2882 // ok, we found something; what is it?
2886 nextSrcLine(); // skip DUP
2889 if (--dupCnt
== 0) {
2894 nextSrcLine(); // skip EDUP
2897 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
2898 // now repeat that lines
2900 setCurSrcLine(stline
);
2901 while (curSrcLine
!= eline
) processCurrentLine();
2903 return PI_SKIP_LINE
;
2907 ///////////////////////////////////////////////////////////////////////////////
2910 static int ifCount
= 0;
2914 // -1: error (should not happen)
2915 // 0: successfully complete
2916 // 1: stopped *AT* "ELSE"
2917 // 2: stopped *AT* "ELSEIF"
2918 static int ifSkipToEndIfOrElse (int wholeBody
) {
2919 int inum
, wasElse
= 0;
2922 while (curSrcLine
) {
2923 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL
))) break;
2927 nextSrcLine(); // skip IF
2928 ifSkipToEndIfOrElse(1); // and recurse
2929 nextSrcLine(); // skip ENDIF
2932 if (wasElse
) fatal("duplicate ELSE");
2933 if (!wholeBody
) return 1;
2935 nextSrcLine(); // skip ELSE
2936 break; // and continue
2938 return 0; // and exit
2941 if (wasElse
) fatal("ELSEIF in ELSE");
2942 if (!wholeBody
) return 2;
2943 nextSrcLine(); // skip ELSEIF
2944 break; // and continue
2946 // skip it as a whole
2947 nextSrcLine(); // skip MACRO
2950 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) { setCurSrcLine(oline
); fatal("invalid MACRO"); }
2951 if (inum
== 1) break;
2952 fatal("invalid nested MACRO");
2954 nextSrcLine(); // skip ENDM
2957 fatal("unexpected ENDM");
2960 fatal("IF without ENDIF");
2965 static int piENDIF (void) {
2966 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2967 if (--ifCount
< 0) fatal("ENDIF without IF");
2969 return PI_SKIP_LINE
;
2973 static int piELSE (void) {
2974 if (--ifCount
< 0) fatal("ELSE without IF");
2975 nextSrcLine(); // skip ELSE
2976 ifSkipToEndIfOrElse(1);
2977 return PI_SKIP_LINE
;
2980 static int piELSEIF (void) {
2981 if (--ifCount
< 0) fatal("ELSEIF without IF");
2982 nextSrcLine(); // skip ELSEIF
2983 ifSkipToEndIfOrElse(1);
2984 return PI_SKIP_LINE
;
2988 static int piELSEIFX (void) {
2989 if (--ifCount
< 0) fatal("ELSEIFX without IF");
2990 nextSrcLine(); // skip ELSEIFX
2991 ifSkipToEndIfOrElse(1);
2992 return PI_SKIP_LINE
;
2996 static int piIFAll (int isIfX
) {
2998 int ooo
= lblOptMakeU2
;
2999 lblOptMakeU2
= (isIfX
? 1 : 0);
3000 int32_t cond
= getOneExprArg(&defined
, NULL
);
3004 if (!isIfX
) fatal("IF: condition must be defined");
3005 cond
= 0; // for IFX: 0 if there is any undefined label
3008 // ok, do it until ELSE/ELSEIF/ENDIF
3010 return PI_SKIP_LINE
;
3016 nextSrcLine(); // skip last instruction
3017 // skip until ELSE/ELSEIF/ENDIF
3018 r
= ifSkipToEndIfOrElse(0);
3019 /*!fprintf(stderr, "ELSEIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
3020 if (r
== 0) break; // ENDIF
3021 if (r
== 1) { ++ifCount
; break; } // ELSE
3022 // ELSEIF, do condition
3023 args
= strIsCommand("ELSEIF", currLine
);
3027 if ((args
= strIsCommand("ELSEIFX", currLine
)) == NULL
) fatal("internal error in conditionals"); // the thing that should not be
3030 memmove(currLine
, args
, strlen(args
)+1);
3031 /*!fprintf(stderr, "ELSEIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
3032 cond
= getOneExprArg(&defined
, NULL
);
3033 /*!fprintf(stderr, "ELSEIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0), cond);*/
3035 if (!isIfX
) fatal("ELSEIF: condition must be defined");
3036 cond
= 0; // for IFX: 0 if there is any undefined label
3038 if (cond
) { ++ifCount
; break; } // condition is true
3040 return PI_SKIP_LINE
;
3044 static int piIF (void) { return piIFAll(0); }
3045 static int piIFX (void) { return piIFAll(1); }
3048 ///////////////////////////////////////////////////////////////////////////////
3050 ///////////////////////////////////////////////////////////////////////////////
3052 what i did with MACRO is the brain-damaged cheating all the way.
3054 first, i will collect the MACRO body and remember it (removing it
3055 from the main source code, so second pass will not see it).
3057 second, when the macro is used, i will:
3058 * insert the whole macro body in place (label resolver will
3059 fix "..lbl" labels for us)
3060 * let the asm play with it
3062 this is not the best scheme, but it is fairly simple and it works.
3065 // will be called when parser encounters term starting with '=' or '*'
3066 // first term char will not be skipped
3067 // must return pointer to the first char after expression end
3068 static const char *getValueCB (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
3072 if (curmacro
== NULL
) fatal("'=' outside of macro");
3073 if (*expr
++ != '=') fatal("'=' expected!");
3075 expr
= strSkipSpaces(expr
);
3077 if (isAlphaDigit(*expr
) || *expr
== '_') {
3078 if (p
-name
> 250) fatal("id too long");
3086 expr
= strSkipSpaces(expr
);
3087 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
3088 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
3091 int l
= (int)strlen(curmacro
->argvals
[f
]);
3093 urasm_exprval_init(&v
);
3094 expr
= urasm_expr_ex(&v
, expr
, addr
, &donteval
, defined
, error
);
3095 if (*error
) return expr
;
3096 if (expr
== NULL
|| *expr
!= ']' || v
.str
!= NULL
) { *error
= UR_EXPRERR_FUNC
; return expr
; }
3098 if (v
.val
< 0) v
.val
+= l
;
3099 if (v
.val
< 0 || v
.val
>= l
) {
3102 res
->val
= (unsigned char)(curmacro
->argvals
[f
][v
.val
]);
3106 urasm_expr_ex(res
, curmacro
->argvals
[f
], addr
, &donteval
, defined
, error
);
3112 fatal("unknown macro variable: '%s'", name
);
3116 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
3117 // opr starts with '=' (invariant)
3118 static int expandCB (char *opr
, int oprlen
) {
3119 char name
[257], *p
= name
, *op
= opr
;
3121 if (curmacro
== NULL
) fatal("'=' outside of macro");
3122 if (*op
++ != '=') fatal("'=' expected!"); // just in case
3123 //fprintf(stderr, "expand: [%s]\n", opr);
3125 if (!isAlpha(op
[0])) return 0; // nothing to do
3127 // copy argument name
3129 if (isAlphaDigit(*op
) || *op
== '_') {
3130 if (p
-name
> 250) fatal("id too long");
3138 // expand argument? we only need to expand `=arg[n]`
3139 op
= strSkipSpaces(op
);
3140 if (op
[0] != '[') return 0;
3142 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
3143 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
3144 if (*op
!= '[') abort(); // assertion, just in case
3145 // replace argument with character, or with literal value
3146 // used for `=regpair[0]` or `=regpair[]`
3149 const int l
= (int)strlen(curmacro
->argvals
[f
]);
3150 const int lleft
= (int)strlen(op
);
3151 char *tmp
= malloc(l
+lleft
+8);
3152 snprintf(tmp
, l
+lleft
+8, "%s%s", curmacro
->argvals
[f
], op
);
3153 if ((int)strlen(tmp
) > oprlen
) {
3162 int error
= 0, defined
= 1, donteval
= 0;
3164 urasm_exprval_init(&v
);
3165 op
= (char *)urasm_expr_ex(&v
, op
, pc
, &donteval
, &defined
, &error
);
3166 if (error
) return -1;
3167 // result should be a number
3168 if (op
== NULL
|| *op
!= ']' || v
.str
!= NULL
) return -1;
3170 // it is guaranteed to have more than one char in opr
3171 // so we can simply put char from argument value, and remove other expression chars
3172 const int l
= (int)strlen(curmacro
->argvals
[f
]);
3173 if (v
.val
< 0) v
.val
+= l
; // negative: indexing from the end
3174 if (v
.val
< 0 || v
.val
>= l
) fatal("index %d is out of bounds for macro argument '%s'", v
.val
, curmacro
->mac
->argnames
[f
]);
3176 opr
[0] = curmacro
->argvals
[f
][v
.val
];
3177 // remove other chars
3178 memmove(opr
+1, op
, strlen(op
)+1);
3184 fatal("unknown macro variable: '%s'", name
);
3188 // main macro expander
3189 static void processMacro (MacroDef
*mc
) {
3190 SourceLine
*oldcurline
= curSrcLine
;
3193 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
3194 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
3196 // parse macro arguments
3198 for (unsigned f
= 0; f
< MAX_MACRO_ARGS
; ++f
) cm
.argvals
[f
] = NULL
;
3202 // do we have more arguments?
3203 if (!currLine
[0]) break;
3204 // check for argument name (this is purely cosmetic thing)
3205 // use like this: "mcall :argname=[value]" (value may be omited for default)
3206 if (currLine
[0] == ':' && isAlpha(currLine
[1])) {
3208 while (isAlphaDigit(currLine
[pos
]) || currLine
[pos
] == '_') ++pos
;
3210 const char svch
= currLine
[pos
];
3212 if (strcasecmp(currLine
+1, mc
->argnames
[currArg
]) != 0) {
3213 // out-of-order, find proper argument and rewind to it
3215 for (int c
= 0; c
< mc
->argc
; ++c
) {
3216 if (strcasecmp(currLine
+1, mc
->argnames
[c
]) == 0) {
3221 if (currArg
< 0) fatal("macro '%s' has no argument named '%s'", mc
->name
, currLine
+1);
3223 currLine
[pos
] = svch
;
3224 // remove argument name
3225 memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
3228 if (currLine
[0] != '=') fatal("expected '=' after argument name");
3230 memmove(currLine
, currLine
+1, strlen(currLine
));
3234 if (currArg
>= mc
->argc
) fatal("too many arguments to macro '%s'", mc
->name
);
3235 // check for default value
3236 if (currLine
[0] == ',') {
3237 if (mc
->argdefaults
[currArg
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[currArg
]);
3238 cm
.argvals
[currArg
] = strdup(mc
->argdefaults
[currArg
]);
3239 memmove(currLine
, currLine
+1, strlen(currLine
));
3241 // skip argument (so we will know its length, and will be able to copy it)
3242 char *e
= skipMacroArg(currLine
);
3243 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
3244 const char ech
= *e
;
3246 if (ech
== ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc
->name
);
3247 if (ech
!= ',') fatal("invalid invocation of macro '%s'", mc
->name
);
3250 cm
.argvals
[currArg
] = strdup(currLine
);
3251 // strip trailing spaces
3252 strTrimRight(cm
.argvals
[currArg
]);
3255 memmove(currLine
, e
+1, strlen(e
));
3262 // check for line end
3264 if (currLine
[0]) fatal("invalid macro invocation");
3266 // setup default argument values
3267 for (int f
= 0; f
< mc
->argc
; ++f
) {
3268 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
3269 if (cm
.argvals
[f
]) continue;
3270 if (mc
->argdefaults
[f
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[f
]);
3271 cm
.argvals
[f
] = strdup(mc
->argdefaults
[f
]);
3272 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
3275 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
3277 // insert macro code into the source
3278 setCurSrcLine(mc
->lines
);
3281 while (curSrcLine
!= NULL
) {
3282 //fprintf(stderr, "*[%s] (%d)\n", curSrcLine->line, curSrcLine->lineNo);
3283 processCurrentLine();
3286 setCurSrcLine(oldcurline
);
3292 static int piMACRO (void) {
3295 char *argdefaults
[MAX_MACRO_ARGS
];
3296 char *argnames
[MAX_MACRO_ARGS
];
3297 SourceLine
*stline
, *eline
= NULL
;
3300 name
= strdup(getLabelArg(0));
3301 //fprintf(stderr, "[%s]\n", name);
3302 if (findMacro(name
) != NULL
) fatal("duplicate MACRO definition: '%s'", name
);
3303 if (currLine
[0] == ',') memmove(currLine
, currLine
+1, strlen(currLine
));
3306 while (currLine
[0]) {
3307 if (argc
>= MAX_MACRO_ARGS
) fatal("too many arguments in MACRO");
3308 if (!isAlpha(currLine
[0])) fatal("invalid MACRO definition");
3309 argnames
[argc
] = strdup(getLabelArg(0));
3310 if (currLine
[0] == '=') {
3312 char *e
= strchr(currLine
, ','), tch
;
3314 if (e
== NULL
) e
= currLine
+strlen(currLine
);
3317 argdefaults
[argc
] = strdup(currLine
+1);
3319 memmove(currLine
, e
, strlen(e
)+1);
3321 argdefaults
[argc
] = NULL
;
3324 if (currLine
[0] == ',') {
3325 memmove(currLine
, currLine
+1, strlen(currLine
));
3328 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
3332 // now find corresponding ENDM
3333 // note that we should skip nested DUPs
3334 stline
= curSrcLine
;
3335 nextSrcLine(); // skip ourself
3336 while (curSrcLine
) {
3339 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) break;
3340 // ok, we found something; what is it?
3343 fatal("no nested MACROs yet");
3349 nextSrcLine(); // skip ENDM
3353 if (!eline
) { setCurSrcLine(stline
); fatal("no ENDM"); }
3355 if ((mc
= calloc(1, sizeof(*mc
))) == NULL
) fatal("out of memory");
3358 for (int f
= 0; f
< argc
; ++f
) { mc
->argdefaults
[f
] = argdefaults
[f
]; mc
->argnames
[f
] = argnames
[f
]; }
3361 mc
->lines
= stline
->next
;
3362 stline
->next
= curSrcLine
;
3363 stline
->line
[0] = 0;
3367 setCurSrcLine(stline
);
3368 return PI_SKIP_LINE
;
3372 static int piENDM (void) {
3373 fatal("ENDM without MACRO");
3374 return PI_SKIP_LINE
;
3378 ///////////////////////////////////////////////////////////////////////////////
3380 static int optWriteType
= 't';
3381 static int optWTChanged
= 0;
3384 static void piTapParseLoaderName (void) {
3387 if (!isStrArg()) fatal("loader name expected");
3388 char *fn
= getStrArg(&len
);
3389 if (len
> 10) fatal("loader name too long");
3390 memset(tapeLoaderName
, ' ', 10);
3391 for (int f
= 0; f
< len
; ++f
) tapeLoaderName
[f
] = fn
[f
];
3396 static int piMATHMODE (void) {
3397 char *name
= getOneLabelArg();
3398 if (strcasecmp(name
, "OLD") == 0) urasm_use_old_priorities
= 1;
3399 else if (strcasecmp(name
, "NEW") == 0) urasm_use_old_priorities
= 0;
3400 else fatal("invalid math mode; NEW or OLD expected");
3401 return PI_SKIP_LINE
;
3405 static int piDEFFMT (void) {
3410 name
= getStrArg(&len
);
3412 name
= getLabelArg(1);
3414 if (optWTChanged
) return 1;
3416 optRunDMB
= optRunTape
= optRunSCL
= 0;
3419 if (!strcasecmp(name
, "SNA") || !strcasecmp(name
, "RUNSNA") || !strcasecmp(name
, "SNARUN")) {
3421 if (currLine
[0]) fatal("too many expressions");
3422 return PI_SKIP_LINE
;
3424 if (!strcasecmp(name
, "TAP") || !strcasecmp(name
, "TAPE")) {
3426 piTapParseLoaderName();
3427 return PI_SKIP_LINE
;
3429 if (!strcasecmp(name
, "RUNTAP") || !strcasecmp(name
, "RUNTAPE") || !strcasecmp(name
, "TAPERUN")) {
3432 piTapParseLoaderName();
3433 return PI_SKIP_LINE
;
3435 if (!strcasecmp(name
, "BIN") || !strcasecmp(name
, "RAW")) {
3437 if (currLine
[0]) fatal("too many expressions");
3438 return PI_SKIP_LINE
;
3440 if (!strcasecmp(name
, "DMB") || !strcasecmp(name
, "RUNDMB") || !strcasecmp(name
, "DMBRUN")) {
3441 optRunDMB
= (name
[3] != 0);
3443 if (currLine
[0]) fatal("too many expressions");
3444 return PI_SKIP_LINE
;
3446 if (!strcasecmp(name
, "NONE") || !strcasecmp(name
, "NOTHING")) {
3448 if (currLine
[0]) fatal("too many expressions");
3449 return PI_SKIP_LINE
;
3451 if (!strcasecmp(name
, "SCL") || !strcasecmp(name
, "RUNSCL") || !strcasecmp(name
, "SCLRUN")) {
3453 optRunSCL
= (name
[3] != 0 ? -1 : 0); /* no boot */
3454 piTapParseLoaderName();
3455 return PI_SKIP_LINE
;
3457 if (!strcasecmp(name
, "SCLBOOT") || !strcasecmp(name
, "BOOTSCL")) {
3459 optRunSCL
= 1; /* with boot */
3460 piTapParseLoaderName();
3461 return PI_SKIP_LINE
;
3463 fatal("invalid default output type: %s", name
);
3467 ///////////////////////////////////////////////////////////////////////////////
3470 static void processCurrentLine (void) {
3471 if (!curSrcLine
) return; // do nothing
3473 if (asmMode
== AMODE_FWORD
) {
3474 processForthWordLine();
3475 nextSrcLine(); // skip it
3480 char *str
, *ee
, name
[66];
3485 removeSpacesAndColons();
3486 // skip spaces and ':'
3487 if (!currLine
[0]) { nextSrcLine(); break; }
3488 // try to find and process command
3489 str
= currLine
; //while (*str && isSpace(*str)) ++str; // skip spaces
3491 for (ee
= str
; *ee
&& !isSpace(*ee
) && *ee
!= '"' && *ee
!= '\'' && *ee
!= ',' && *ee
!= ':'; ++ee
) {}
3492 // get command, if any
3493 if (ee
!= str
&& ee
-str
<= 64) {
3495 memset(name
, 0, sizeof(name
));
3496 memmove(name
, str
, ee
-str
);
3497 /* known command? */
3498 op
= urFindOp(name
);
3501 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
3502 memmove(currLine
, str
, strlen(str
)+1);
3504 nextSrcLine(); // skip it
3510 if ((mc
= findMacro(name
)) != NULL
) {
3512 str
= ee
; while (*str
&& isSpace(*str
)) ++str
; // skip spaces
3513 memmove(currLine
, str
, strlen(str
)+1);
3515 /* only one macro per line! */
3520 len
= urasm_opasm(currLine
, pc
, disp
, &errpos
);
3521 if (len
< 0) fatalUrLib(len
);
3524 if (len
>= 0 && errpos
) {
3525 memmove(currLine
, errpos
+1, strlen(errpos
));
3527 nextSrcLine(); // skip it
3534 ///////////////////////////////////////////////////////////////////////////////
3535 // forth instructions
3537 static uint16_t forthLatest
= 0;
3538 static const char *forthCurrWord
= NULL
;
3541 typedef struct ForthWordT
{
3542 char *name
; // uppercased
3546 struct ForthWordT
*next
;
3549 static ForthWord
*forthWordList
= NULL
;
3550 static ForthWord
*forthWordListTail
= NULL
;
3552 static ForthWord
*findForthWord (const char *name
) {
3553 if (!name
) name
= "";
3554 for (ForthWord
*w
= forthWordList
; w
; w
= w
->next
) {
3555 if (strcasecmp(name
, w
->name
) == 0) return w
;
3561 static ForthWord
*addForthWord (const char *name
) {
3562 if (!name
) name
= "";
3563 ForthWord
*nw
= findForthWord(name
);
3565 if (nw
) fatal("duplicate forth word: '%s'", name
);
3566 nw
= malloc(sizeof(ForthWord
));
3567 nw
->name
= strdup(name
);
3568 for (char *s
= nw
->name
; *s
; ++s
) {
3569 if (s
[0] >= 'a' && s
[0] <= 'z') s
[0] = s
[0]-'a'+'A';
3575 if (forthWordListTail
) forthWordListTail
->next
= nw
; else forthWordList
= nw
;
3576 forthWordListTail
= nw
;
3578 if (!nw
) fatal("internal compiler error");
3593 ;; bit 5: SMUDGE flag (=1: word definition isn't finished)
3594 ;; bit 6: IMMEDIATE flag (=1: true)
3596 ;; the last byte of the name always has bit 7 set
3598 static ForthWord
*forthWordHead (const char *name
, int imm
) {
3599 ForthWord
*nw
= addForthWord(name
);
3600 if (!name
) name
= "";
3601 const uint16_t lt
= disp
;
3602 //fprintf(stderr, "#%04X: fw=<%s>\n", lt, name);
3606 if (nw
->nfa
!= disp
) fatal("forth word `%s`: orig NFA=#%04X; pass1 NFA=#%04X\n", name
, nw
->nfa
, disp
);
3607 //fprintf(stderr, "#%04X: fw=<%s>\n", lt, name);
3609 size_t nlen
= strlen(name
);
3610 if (nlen
== 0) nlen
= 1;
3611 if (nlen
> 31) fatal("forth word name too long: '%s'", name
);
3612 // word length and flags (nfa)
3613 emitByte((nlen
&0xff)|0x80u
|(imm
? 0x40u
: 0x00u
));
3615 uint8_t b
= (uint8_t)(name
[0]&0xffu
);
3616 if (b
>= 'a' && b
<= 'z') b
= b
-'a'+'A';
3617 if (nlen
== 1) b
|= 0x80u
;
3623 emitWord(forthLatest
);
3626 //fprintf(stderr, "#%04X: fw=<%s>\n", nw->cfa, nw->name);
3631 /* returns static buffer */
3633 static char *forthPeekWord (void) {
3634 static char buf[256];
3636 while (isSpace(currLine[pos])) ++pos;
3638 while (currLine[pos] && !isSpace(currLine[pos])) {
3639 if (bp >= sizeof(buf)-2) fatal("forth word too long");
3640 buf[bp++] = currLine[pos++];
3643 return (buf[0] ? buf : NULL);
3648 /* returns static buffer */
3649 static char *forthGetWord (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 while (isSpace(currLine
[pos
])) ++pos
;
3660 if (pos
> 0) memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
3661 return (buf
[0] ? buf
: NULL
);
3665 static void forthEmitLabel (const char *name
) {
3666 UrLabelInfo
*lbl
= urFindLabel(name
);
3668 if (pass
!= 0) fatal("using undefined label '%s'", name
);
3669 lbl
= urAddLabel(name
);
3670 //fprintf(stderr, "****** <%s>\n", lbl->name);
3671 lbl
->type
= (lblOptMakeU2
? -42 : -1);
3673 lbl
->refLine
= curSrcLine
->lineNo
;
3674 lbl
->refFile
= strdup(curSrcLine
->fname
);
3676 emitWord(lbl
->value
);
3680 static void forthDoConstVar (const char *lbl
) {
3681 const char *wname
= forthGetWord();
3682 if (!wname
) fatal("forth word name expected");
3683 /*ForthWord *nw =*/ forthWordHead(wname
, 0);
3685 forthEmitLabel(lbl
);
3687 int defined
= 1, addr
= 0;
3688 int32_t res
= getOneExprArg(&defined
, &addr
);
3689 if (strcmp(lbl
, "_do2var") != 0) {
3690 if (res
< 0) res
= 65536+res
;
3691 emitWord(res
&0xffffU
);
3694 emitWord(res
&0xffffU
);
3695 emitWord((res
>>16)&0xffffU
);
3697 //fprintf(stderr, "FORTH CONST/VAR: '%s' = #%04X\n", nw->name, res);
3702 static int piForthConst (void) {
3703 forthDoConstVar("_doconst");
3704 return PI_SKIP_LINE
;
3708 static int piForthVar (void) {
3709 forthDoConstVar("_dovar");
3710 return PI_SKIP_LINE
;
3714 static int piForthDVar (void) {
3715 forthDoConstVar("_do2var");
3716 return PI_SKIP_LINE
;
3720 static int piForthUser (void) {
3721 forthDoConstVar("_douser");
3722 return PI_SKIP_LINE
;
3726 static int piForthCodeWord (void) {
3727 if (asmMode
!= AMODE_NORMAL
) fatal("invalid forth define");
3728 asmMode
= AMODE_FCODE
;
3729 const char *wname
= forthGetWord();
3730 if (!wname
) fatal("forth word name expected");
3731 ForthWord
*nw
= forthWordHead(wname
, 0);
3733 emitWord((disp
+2)&0xffffU
);
3734 wname
= forthGetWord();
3736 if (strcasecmp(wname
, "FBRANCH") == 0) nw
->isbranch
= 1; else fatal("end of line expected");
3739 //fprintf(stderr, "FORTH CODE WORD: '%s'\n", nw->name);
3740 forthCurrWord
= nw
->name
;
3741 return PI_SKIP_LINE
;
3745 static int piForthCodeWordEnd (void) {
3746 if (asmMode
!= AMODE_FCODE
|| !forthCurrWord
) fatal("invalid forth define");
3747 asmMode
= AMODE_NORMAL
;
3748 const char *wname
= forthGetWord();
3749 if (!wname
) fatal("forth word name expected");
3750 if (!forthWordListTail
) fatal("end of unknown forth code word");
3751 if (strcasecmp(wname
, forthCurrWord
) != 0) fatal("end of forth code word '%s', but expected '%s'", wname
, forthWordListTail
->name
);
3753 forthCurrWord
= NULL
;
3754 return PI_SKIP_LINE
;
3758 static int piForthWord (void) {
3759 if (asmMode
!= AMODE_NORMAL
) fatal("invalid forth define");
3760 asmMode
= AMODE_FWORD
;
3761 const char *wname
= forthGetWord();
3762 if (!wname
) fatal("forth word name expected");
3763 if (strcmp(wname
, "~") == 0) wname
= "";
3764 char *fwn
= strdup(wname
);
3765 wname
= forthGetWord();
3767 if (wname
&& strcasecmp(wname
, "IMM") == 0) imm
= 1;
3768 ForthWord
*nw
= forthWordHead(fwn
, imm
);
3771 forthEmitLabel("_doforth");
3772 wname
= forthGetWord();
3774 if (strcasecmp(wname
, "FBRANCH") == 0) nw
->isbranch
= 1; else fatal("end of line expected");
3777 //fprintf(stderr, "FORTH WORD: '%s'\n", nw->name);
3778 forthCurrWord
= nw
->name
;
3779 return PI_SKIP_LINE
;
3783 static int piForthWordDoes (void) {
3784 if (asmMode
!= AMODE_NORMAL
) fatal("invalid forth define");
3785 // get forth word name
3786 const char *wname
= forthGetWord();
3787 if (!wname
) fatal("forth word name expected");
3788 if (strcmp(wname
, "~") == 0) wname
= "";
3789 char *fwn
= strdup(wname
);
3790 // get does label name
3791 wname
= forthGetWord();
3792 if (!wname
) fatal("does label expected");
3793 char *dlb
= strdup(wname
);
3795 wname
= forthGetWord();
3797 if (wname
&& strcasecmp(wname
, "IMM") == 0) imm
= 1;
3799 /*ForthWord *nw =*/ forthWordHead(fwn
, imm
);
3802 forthEmitLabel("_dodoes");
3804 forthEmitLabel(dlb
);
3807 //fprintf(stderr, "FORTH DOES WORD: '%s'\n", nw->name);
3808 return PI_SKIP_LINE
;
3812 static int piForthWordEnd (void) {
3813 if (asmMode
!= AMODE_FWORD
|| !forthCurrWord
) fatal("invalid forth define");
3814 asmMode
= AMODE_NORMAL
;
3815 const char *wname
= forthGetWord();
3816 if (!wname
) fatal("forth word name expected");
3817 if (!forthWordListTail
) fatal("end of unknown forth code word");
3818 if (strcasecmp(wname
, forthCurrWord
) != 0) fatal("end of forth code word '%s', but expected '%s'", wname
, forthWordListTail
->name
);
3820 forthCurrWord
= NULL
;
3821 return PI_SKIP_LINE
;
3825 static int isStartsWithForthCmd (void) {
3827 currLine
[0] == '$' &&
3828 toUpper(currLine
[1]) == 'F' &&
3829 toUpper(currLine
[2]) == 'O' &&
3830 toUpper(currLine
[3]) == 'R' &&
3831 toUpper(currLine
[4]) == 'T' &&
3832 toUpper(currLine
[5]) == 'H';
3838 static int forthIsBranchWord (const char *wname) {
3839 if (!wname || !wname[0]) return 0;
3840 if (strcasestr(wname, "BRANCH")) return 1;
3841 if (strcasestr(wname, "LOOP)")) return 1;
3847 static void processForthWordLine (void) {
3848 // check for "$FORTH..."
3849 if (isStartsWithForthCmd()) {
3850 const char *w
= forthGetWord();
3851 if (!w
) fatal("wtf?!");
3852 if (asmMode
!= AMODE_FWORD
) fatal("wtf1?!");
3853 if (strcasecmp(w
, "$FORTH_END_WORD") != 0) fatal("invalid forth instruction");
3855 if (!w
) fatal("forth word name expected");
3856 if (strcmp(w
, "~") == 0) w
= "";
3857 asmMode
= AMODE_NORMAL
;
3858 if (!forthCurrWord
) fatal("end of unknown forth code word");
3859 if (strcasecmp(w
, forthCurrWord
) != 0) fatal("end of forth code word '%s', but expected '%s'", w
, forthWordListTail
->name
);
3861 forthCurrWord
= NULL
;
3864 // check for a label
3865 if (isAlpha(currLine
[0]) || currLine
[0] == '_') {
3867 while (currLine
[lend
] && currLine
[lend
] != ':') {
3868 //fprintf(stderr, "lend=%u (%c)\n", lend, currLine[lend]);
3869 if (isSpace(currLine
[lend
])) fatal("invalid label");
3872 if (lend
> 64) fatal("label too long");
3873 //fprintf(stderr, "lend=%u (%c)\n", lend, currLine[lend]);
3874 if (currLine
[lend
] != ':') fatal("label expected");
3876 memcpy(nn
, currLine
, lend
);
3879 memmove(currLine
, currLine
+lend
, strlen(currLine
+lend
)+1);
3880 UrLabelInfo
*lbl
= urAddLabel(nn
);
3881 if (!lbl
->refFile
) {
3882 lbl
->refLine
= curSrcLine
->lineNo
;
3883 lbl
->refFile
= strdup(curSrcLine
->fname
);
3885 if (pass
== 0 && lbl
->type
!= -1) fatal("duplicate label '%s'", lbl
->name
);
3890 lbl
->fixuptype
= UR_FIXUP_NONE
;
3891 //fprintf(stderr, "flabel: %s (#%04X)\n", lbl->name, lbl->value);
3894 //fprintf(stderr, "<: %s :>\n", currLine);
3897 const char *wname
= forthGetWord();
3899 //fprintf(stderr, " %s: #%04X: <%s>\n", forthCurrWord, disp, wname);
3902 if (strcmp(wname
, "(.\")") == 0 || strcmp(wname
, "(\")") == 0) {
3906 ForthWord
*fw
= findForthWord(wname
);
3907 if (!fw
) fatal("forth word `%s` not found", wname
);
3911 char qch
= currLine
[0];
3912 if (qch
!= '~' && qch
!= '"' && qch
!= '\'') fatal("invalid string quotation");
3913 memmove(currLine
, currLine
+1, strlen(currLine
+1)+1);
3915 while (currLine
[pos
] && currLine
[pos
] != qch
) {
3916 if (currLine
[pos
] != '\\') { ++pos
; continue; }
3918 if (currLine
[pos
+1] == qch
) {
3921 switch (currLine
[pos
+1]) {
3922 case 'r': ch
= 13; break;
3923 case 'n': ch
= 256; break;
3924 case 't': ch
= 9; break;
3925 case 'b': ch
= 8; break;
3926 case 'v': ch
= 10; break;
3927 case '\\': ch
= '\\'; break;
3929 //fprintf(stderr, "<%s>\n", currLine);
3930 if (!isHexDigit(currLine
[pos
+2]) || !isHexDigit(currLine
[pos
+3])) fatal("invalid hex escape");
3931 ch
= digitInBase(currLine
[pos
+2], 16)*16+digitInBase(currLine
[pos
+3], 16);
3932 currLine
[pos
++] = ch
;
3933 memmove(currLine
+pos
, currLine
+pos
+3, strlen(currLine
+pos
+3)+1);
3934 ch
= 666; // special
3938 if (ch
< 0) fatal("invalid escape");
3939 if (ch
== 666) continue;
3942 currLine
[pos
++] = 13;
3943 currLine
[pos
++] = 10;
3945 currLine
[pos
++] = ch
;
3946 memmove(currLine
+pos
, currLine
+pos
+1, strlen(currLine
+pos
+1)+1);
3949 if (pos
> 255) fatal("string too long");
3950 if (currLine
[pos
] != qch
) fatal("unterminated string");
3951 emitByte(pos
&0xffU
);
3952 for (size_t f
= 0; f
< pos
; ++f
) emitByte((uint8_t)(currLine
[f
]&0xffU
));
3954 memmove(currLine
, currLine
+pos
, strlen(currLine
+pos
)+1);
3958 // check for numeric literal
3959 if (strcasecmp(wname
, "LIT") == 0) {
3963 ForthWord
*fw
= findForthWord(wname
);
3964 if (!fw
) fatal("forth word `%s` not found", wname
);
3968 wname
= forthGetWord();
3969 if (!wname
) fatal("unexpected end of line");
3970 //fprintf(stderr, " %s: #%04X: <%s>\n", forthCurrWord, disp, wname);
3975 UrLabelInfo
*lbl
= urFindLabel(wname
);
3977 if (!lbl
->known
) fatal("unknown label '%s'", lbl
->name
);
3978 emitWord(lbl
->value
&0xffffU
);
3984 if (wname
[0] == '#' && isHexDigit(wname
[1])) {
3988 long int v
= strtol(wname
, &end
, base
);
3989 if (end
[0] == 0 && end
!= wname
) {
3990 if (v
< -32768 || v
> 65535) fatal("forth numeric literal out of range: %d\n", (int)v
);
3991 if (v
< 0) v
= 65536+v
;
3992 emitWord(v
&0xffffU
);
3995 fatal("number expected, got `%s`", wname
);
4000 // check for `COMPILE`
4001 if (strcasecmp(wname
, "COMPILE") == 0) {
4005 ForthWord
*fw
= findForthWord(wname
);
4006 if (!fw
) fatal("forth word `%s` not found", wname
);
4010 wname
= forthGetWord();
4011 if (!wname
) fatal("unexpected end of line");
4015 ForthWord
*fw
= findForthWord(wname
);
4016 if (!fw
) fatal("forth word `%s` not found", wname
);
4026 if (strcmp(wname
, "~") == 0) wname
= "";
4027 ForthWord
*fw
= findForthWord(wname
);
4030 UrLabelInfo
*lbl
= urFindLabel(wname
);
4032 fatal("label '%s' without LIT", lbl
->name
);
4033 emitWord(lbl
->value
&0xffffU
);
4038 if (wname
[0] == '#' && isHexDigit(wname
[1])) {
4042 long int v
= strtol(wname
, &end
, base
);
4043 if (end
[0] == 0 && end
!= wname
) {
4044 fatal("number without LIT! (%d)", (int)v
);
4047 fatal("unknown forth word '%s'", wname
);
4051 wname
= forthGetWord();
4052 if (!wname
) fatal("label expected");
4053 UrLabelInfo
*lbl
= urFindLabel(wname
);
4054 if (!lbl
) fatal("unknown label in forth code: '%s'", wname
);
4055 if (!lbl
->known
) fatal("undefined label '%s'", lbl
->name
);
4056 if (lbl
->value
< 0 || lbl
->value
> 65535) fatal("invalid jump label in forth code: '%s' (%d)", wname
, lbl
->value
);
4057 int v
= lbl
->value
-(int)disp
;
4058 if (v
< -32767 || v
> 32767) fatal("forth jump too far");
4059 if (v
< 0) v
= 65536+v
;
4060 emitWord(v
&0xffffU
);
4067 ///////////////////////////////////////////////////////////////////////////////
4068 // setup instructions
4070 static void registerInstructions (void) {
4071 urAddOp("DISPLAY", piDISPLAY
);
4072 urAddOp("DISPLAY0", piDISPLAY0
);
4073 urAddOp("DISPLAYA", piDISPLAYA
);
4074 urAddOp("DISPHEX", piDISPHEX
);
4075 urAddOp("DISPHEX0", piDISPHEX0
);
4076 urAddOp("DISPHEXA", piDISPHEXA
);
4078 urAddOp("DEFFMT", piDEFFMT
);
4079 urAddOp("$MODEL", piMODEL
);
4081 urAddOp("MACRO", piMACRO
);
4082 urAddOp("ENDM", piENDM
);
4084 urAddOp("ORG", piORG
);
4085 urAddOp("DISP", piDISP
);
4086 urAddOp("ENDDISP", piENDDISP
);
4087 urAddOp("PHASE", piDISP
);
4088 urAddOp("DEPHASE", piENDDISP
);
4089 urAddOp("UNPHASE", piENDDISP
);
4090 urAddOp("ALIGN", piALIGN
);
4091 urAddOp("DISPALIGN", piDISPALIGN
);
4092 urAddOp("PHASEALIGN", piDISPALIGN
);
4093 urAddOp("ENT", piENT
);
4094 urAddOp("CLR", piCLR
);
4095 urAddOp("RESERVE", piRESERVE
);
4097 urAddOp("INCLUDE", piINCLUDE
);
4098 urAddOp("INCBIN", piINCBIN
);
4100 urAddOp("MODULE", piMODULE
);
4101 urAddOp("ENDMODULE", piENDMODULE
);
4103 urAddOp("DUP", piDUP
);
4104 urAddOp("EDUP", piEDUP
);
4106 urAddOp("IF", piIF
);
4107 urAddOp("IFX", piIFX
);
4108 urAddOp("ELSE", piELSE
);
4109 urAddOp("ELSEIF", piELSEIF
);
4110 urAddOp("ELSEIFX", piELSEIFX
);
4111 urAddOp("ENDIF", piENDIF
);
4113 urAddOp("DEFINCR", piDEFINCR
);
4114 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
4115 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
4116 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
4117 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
4118 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
4119 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
4120 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
4121 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
4123 urAddOp("$ERROR", piERROR
);
4124 urAddOp("$WARNING", piWARNING
);
4126 urAddOp("$PRINTF", piPRINTF
);
4127 urAddOp("$PRINTF0", piPRINTF0
);
4128 urAddOp("$PRINTFA", piPRINTFA
);
4130 urAddOp("$MATHMODE", piMATHMODE
);
4132 urAddOp("$FORTH_CONST", piForthConst
);
4133 urAddOp("$FORTH_VAR", piForthVar
);
4134 urAddOp("$FORTH_DVAR", piForthDVar
);
4135 urAddOp("$FORTH_USER", piForthUser
);
4137 urAddOp("$FORTH_CODE_WORD", piForthCodeWord
);
4138 urAddOp("$FORTH_END_CODE_WORD", piForthCodeWordEnd
);
4140 urAddOp("$FORTH_WORD", piForthWord
);
4141 urAddOp("$FORTH_END_WORD", piForthWordEnd
);
4143 urAddOp("$FORTH_DOES", piForthWordDoes
);
4147 ///////////////////////////////////////////////////////////////////////////////
4148 // !0: invalid label
4150 static inline void fnSkipSpaces (const char *expr) {
4151 while (*expr && isSpace(*expr)) ++expr;
4157 static inline char fnNextChar (const char *expr
) {
4158 while (*expr
&& isSpace(*expr
)) ++expr
;
4163 #define FN_SKIP_BLANKS do { \
4164 while (*expr && isSpace(*expr)) ++expr; \
4167 #define FN_CHECK_END do { \
4168 while (*expr && isSpace(*expr)) ++expr; \
4169 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
4174 #define FN_CHECK_COMMA do { \
4175 while (*expr && isSpace(*expr)) ++expr; \
4176 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
4181 static const char *readLabelName (char *buf
, const char *expr
) {
4184 while (*expr
&& isSpace(*expr
)) ++expr
;
4188 if (pos
>= 128) return NULL
;
4189 if (ch
== ')') { --expr
; break; }
4191 if (isAlphaDigit(ch
) || ch
== '$' || ch
== '.' || ch
== '_' || ch
== '@') {
4197 if (pos
< 1) return NULL
;
4199 if (!urasm_is_valid_name(buf
)) return NULL
;
4204 static const char *fnDefKn (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
, int qtype
) {
4207 if ((expr
= readLabelName(lbl
, expr
)) == NULL
) { *error
= UR_EXPRERR_LABEL
; return NULL
; }
4209 if (!donteval
) res
->val
= (isLabelDefinedOrKnown(lbl
, addr
, qtype
) != 0);
4213 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
); }
4214 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
); }
4217 static const char *fnAligned256 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4218 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4220 if (!donteval
) res
->val
= (res
->val
%256 ? 0 : 1);
4225 static const char *fnSameSeg (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4226 urasm_exprval_t v0
, v1
;
4228 urasm_exprval_init(&v0
);
4229 urasm_exprval_init(&v1
);
4230 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
4231 if (*error
) return expr
;
4233 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
4234 if (*error
) return expr
;
4236 if (!donteval
) res
->val
= (v0
.val
/256 == v1
.val
/256);
4241 static const char *fnAlign (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4242 urasm_exprval_t v0
, v1
;
4243 urasm_exprval_init(&v0
);
4244 urasm_exprval_init(&v1
);
4245 expr
= urasm_expr_ex(&v0
, expr
, addr
, &donteval
, defined
, error
);
4246 if (*error
) return expr
;
4248 if (fnNextChar(expr
) == ',') {
4250 expr
= urasm_expr_ex(&v1
, expr
, addr
, &donteval
, defined
, error
);
4251 if (*error
) return expr
;
4257 if (v1
.val
< 1) { *error
= UR_EXPRERR_FUNC
; return NULL
; }
4258 res
->val
= (v0
.val
+v1
.val
-1)/v1
.val
*v1
.val
;
4264 static const char *fnLow (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4265 const char *ee
= expr
;
4266 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4269 if (res
->fixuptype
== UR_FIXUP_HIBYTE
) {
4270 *error
= UR_EXPRERR_FUNC
; return ee
;
4272 res
->fixuptype
= UR_FIXUP_LOBYTE
;
4279 static const char *fnHigh (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4280 const char *ee
= expr
;
4281 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4284 if (res
->fixuptype
== UR_FIXUP_LOBYTE
) {
4285 *error
= UR_EXPRERR_FUNC
; return ee
;
4287 res
->fixuptype
= UR_FIXUP_HIBYTE
;
4288 res
->val
= (res
->val
>>8)&0xff;
4294 static const char *fnAbs (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4295 const char *ee
= expr
;
4296 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4299 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
4300 *error
= UR_EXPRERR_FUNC
; return ee
;
4302 res
->val
= abs(res
->val
);
4308 static const char *fnBSwap (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4309 const char *ee
= expr
;
4310 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4313 if (res
->fixuptype
!= UR_FIXUP_NONE
) {
4314 *error
= UR_EXPRERR_FUNC
; return ee
;
4316 res
->val
= ((res
->val
>>8)&0xff)|((res
->val
&0xff)<<8);
4322 static const char *fnScrAddr8xn (int nmul
, urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4323 int32_t scrbase
= 0x4000;
4325 const char *ee
= expr
;
4326 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
4327 // first argument is `x`
4328 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4330 // second argument is `y`
4332 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4334 // optional third arg is screen base
4336 if (expr
[0] == ',') {
4338 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4339 scrbase
= res
->val
&0xffff;
4343 //urasm_exprval_clear(res);
4344 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
4345 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
4346 if (y
< 0 || y
> 191) fatal("invalid y coordinate: %d", y
/nmul
);
4352 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
4358 static const char *fnScrAddr8x1 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4359 return fnScrAddr8xn(1, res
, expr
, addr
, donteval
, defined
, error
);
4362 static const char *fnScrAddr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4363 return fnScrAddr8xn(8, res
, expr
, addr
, donteval
, defined
, error
);
4367 static const char *fnScrAttr8x8 (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4368 int32_t scrbase
= 0x4000;
4370 const char *ee
= expr
;
4371 // first argument is `x`
4372 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4374 // second argument is `y`
4376 urasm_exprval_clear(res
);
4377 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4379 // optional third arg is screen base
4381 if (expr
[0] == ',') {
4383 urasm_exprval_clear(res
);
4384 expr
= urasm_expr_ex(res
, expr
, addr
, &donteval
, defined
, error
);
4385 scrbase
= res
->val
&0xffff;
4387 urasm_exprval_clear(res
);
4390 if (res
->fixuptype
!= UR_FIXUP_NONE
) { *error
= UR_EXPRERR_FUNC
; return ee
; }
4391 if (x
< 0 || x
> 31) fatal("invalid x coordinate: %d", x
);
4392 if (y
< 0 || y
> 23) fatal("invalid y coordinate: %d", y
);
4393 res
->val
= scrbase
+6144+y
*32+x
;
4399 static const char *fnMArgToStr (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4400 const char *ee
= expr
;
4401 // argument is macro argument name
4402 expr
= strSkipSpacesConst(expr
);
4403 if (expr
[0] != '=') { *error
= UR_EXPRERR_FUNC
; return ee
; }
4405 if (!isAlpha(expr
[0]) && expr
[0] != '_') { *error
= UR_EXPRERR_FUNC
; return ee
; }
4406 const char *nend
= expr
+1;
4407 while (isAlphaDigit(*nend
) || *nend
== '_') ++nend
;
4408 if (nend
-expr
> 127) { *error
= UR_EXPRERR_FUNC
; return ee
; }
4410 memset(name
, 0, sizeof(name
));
4411 memcpy(name
, expr
, nend
-expr
);
4416 expr
= strSkipSpacesConst(expr
);
4417 if (expr
[0] == '[') {
4418 expr
= strSkipSpacesConst(expr
+1);
4419 if (expr
[0] != ']') {
4422 if (expr
[0] == '+') ++expr
;
4423 else if (expr
[0] == '-') { doneg
= 1; ++expr
; }
4425 while (isDigit(expr
[0])) {
4426 index
= index
*10+(expr
[0]-'0');
4427 if (index
>= 0x1fffffff) fatal("`margtostr` index too high");
4430 expr
= strSkipSpacesConst(expr
);
4431 if (doneg
) index
= -index
;
4433 if (expr
[0] != ']') fatal("`margtostr` invalid index syntax");
4438 if (curmacro
== NULL
) fatal("`margtostr` outside of macro");
4440 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
4441 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
4442 // found argument, convert it to string
4444 res
->str
= realloc(res
->str
, strlen(curmacro
->argvals
[f
])+1);
4445 strcpy(res
->str
, curmacro
->argvals
[f
]);
4447 const size_t slen
= strlen(curmacro
->argvals
[f
]);
4450 if (index
> slen
) fatal("index out of string");
4451 index
= (int)slen
-index
;
4453 if (index
>= slen
) fatal("index out of string");
4454 res
->str
= realloc(res
->str
, 2);
4455 res
->str
[0] = curmacro
->argvals
[f
][index
];
4459 for (char *s
= res
->str
; *s
; ++s
) *s
= toLower(*s
);
4460 //fprintf(stderr, "<%s>\n", res->str);
4463 res
->val
= ((unsigned char)res
->str
[0]);
4464 res
->val
|= ((unsigned char)res
->str
[1])<<8;
4466 res
->val
= (unsigned char)res
->str
[0];
4474 fatal("unknown macro argument '%s'", name
);
4480 static const char *fnStrLen (urasm_exprval_t
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
4481 expr
= strSkipSpacesConst(expr
);
4484 case '"': case '\'': /* string literal? */
4486 char *a
= (char *)expr
+1;
4487 (void)parseStr(&a
, expr
[0], &stlen
);
4488 expr
= (const char *)a
;
4491 default: /* expression */
4494 urasm_exprval_init(&r
);
4495 expr
= urasm_expr_ex(&r
, expr
, disp
, &donteval
, defined
, error
);
4496 if (*error
) fatalUrLib(*error
);
4497 if (!r
.str
) fatal("string expected for `strlen()`");
4498 stlen
= (int)strlen(r
.str
);
4499 urasm_exprval_clear(&r
);
4505 urasm_exprval_setint(res
, stlen
);
4511 static void registerFunctions (void) {
4512 urasm_expr_register_func("defined", fnDefined
);
4513 urasm_expr_register_func("known", fnKnown
);
4514 urasm_expr_register_func("aligned256", fnAligned256
);
4515 urasm_expr_register_func("align", fnAlign
);
4516 urasm_expr_register_func("sameseg", fnSameSeg
);
4517 urasm_expr_register_func("low", fnLow
);
4518 urasm_expr_register_func("high", fnHigh
);
4519 urasm_expr_register_func("abs", fnAbs
);
4520 urasm_expr_register_func("bswap", fnBSwap
);
4522 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8
);
4523 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1
);
4524 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8
);
4526 urasm_expr_register_func("marg2str", fnMArgToStr
);
4527 urasm_expr_register_func("strlen", fnStrLen
);
4531 ///////////////////////////////////////////////////////////////////////////////
4532 // preparing another pass
4534 static void initPass (void) {
4535 curSrcLine
= asmText
;
4548 lastSeenGlobalLabel
= strdup(" [MAIN] ");
4551 urasm_use_old_priorities
= 0;
4553 forthCurrWord
= NULL
;
4557 static int posstPass (void) {
4558 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
4559 lastSeenGlobalLabel
= NULL
;
4560 if (checkLabels()) return -1;
4561 if (ifCount
!= 0) fatal("unbalanced IFs");
4562 if (forthCurrWord
) fatal("unfinished forth word `%s`", forthCurrWord
);
4567 ///////////////////////////////////////////////////////////////////////////////
4568 static int labelCmp (const void *aa
, const void *bb
) {
4569 if (aa
== bb
) return 0;
4570 const UrLabelInfo
*a
= *(const UrLabelInfo
**)aa
;
4571 const UrLabelInfo
*b
= *(const UrLabelInfo
**)bb
;
4573 a
->value
< b
->value
? -1 :
4574 a
->value
> b
->value
? 1 :
4579 static void writeLabelsFile (const char *fname
) {
4580 if (!fname
|| !fname
[0]) return;
4583 for (const UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
) ++lcount
;
4584 UrLabelInfo
**larr
= NULL
;
4586 // create labels array
4587 larr
= (UrLabelInfo
**)malloc(sizeof(UrLabelInfo
*)*lcount
);
4589 for (UrLabelInfo
*ll
= labels
; ll
; ll
= ll
->next
, ++lcount
) larr
[lcount
] = ll
;
4591 qsort(larr
, lcount
, sizeof(UrLabelInfo
*), &labelCmp
);
4594 FILE *fo
= fopen(fname
, "w");
4596 for (int f
= 0; f
< lcount
; ++f
) {
4597 UrLabelInfo
*ll
= larr
[f
];
4598 if (!ll
->name
|| (!isAlpha(ll
->name
[0]) && ll
->name
[0] != '@')) continue;
4599 if (ll
->value
< 0) {
4600 fprintf(fo
, "%d %s\n", ll
->value
, ll
->name
);
4602 fprintf(fo
, "#%04X %s\n", (unsigned)ll
->value
, ll
->name
);
4607 // write forth words
4608 if (forthWordList
) {
4610 for (ForthWord
*w
= forthWordList
; w
; w
= w
->next
) {
4611 fprintf(fo
, "#%04X %s\n", w
->cfa
, w
->name
);
4618 ///////////////////////////////////////////////////////////////////////////////
4621 static struct option longOpts
[] = {
4622 {"org", required_argument
, NULL
, 600},
4623 {"define", required_argument
, NULL
, 601},
4624 {"defzero", required_argument
, NULL
, 602},
4625 {"outdir", required_argument
, NULL
, 660},
4626 {"reffile", optional_argument
, NULL
, 669},
4628 {"sna", 0, NULL
, 's'},
4629 {"sna128", 0, NULL
, 'S'},
4630 {"tap", 0, NULL
, 't'},
4631 {"autotap", 0, NULL
, 'T'},
4632 {"raw", 0, NULL
, 'r'},
4633 {"autodmb", 0, NULL
, 'B'},
4634 {"dmb", 0, NULL
, 'b'},
4635 {"none", 0, NULL
, 'n'},
4636 {"help", 0, NULL
, 'h'},
4637 {"hob", 0, NULL
, 'H'},
4638 {"scl", 0, NULL
, 'l'},
4639 {"autoscl", 0, NULL
, 'L'},
4640 {"fixups", optional_argument
, NULL
, 'F'},
4646 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
4649 static void usage (const char *pname
) {
4651 "usage: %s [options] infile\n"
4652 "default infiles:", pname
);
4653 for (int f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
4656 " -s --sna write 48K .SNA file with autostart\n"
4657 " -S --sna128 write 148K .SNA file with autostart\n"
4658 " -t --tap write .tap file\n"
4659 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
4660 " -r --raw write raw file(s)\n"
4661 " -b --dmb write DMB file\n"
4662 " -B --autodmb write DMB file with autostart\n"
4663 " -H --hob write HoBeta code file(s)\n"
4664 " -l --scl write SCL TR-DOS archive\n"
4665 " -L --autoscl write autostarting SCL TR-DOS archive\n"
4666 " -n --none write nothing\n"
4667 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
4668 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
4669 " text: .txt file\n"
4670 " asm: address lists with counters\n"
4671 " asmdiff: 2nd and other addresses are relative, with counters\n"
4672 " asmz: address lists, with 0 end markers\n"
4673 " -h --help this help\n"
4675 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
4676 " --org xxx set ORG\n"
4677 " --define val perform 'val EQU 1'\n"
4678 " --defzero val perform 'val EQU 0'\n"
4679 " --outdir dir output dir for resulting files (default: current)\n"
4684 ///////////////////////////////////////////////////////////////////////////////
4687 int main (int argc
, char *argv
[]) {
4689 const char *pname
= argv
[0];
4690 char *inFile
= NULL
;
4691 char **defines
= NULL
, **values
= NULL
;
4697 urasm_getbyte
= getByte
;
4698 urasm_putbyte
= putByte
;
4699 urasm_label_by_name
= findLabelCB
;
4700 urasm_getval
= getValueCB
;
4701 urasm_expand
= expandCB
;
4702 urasm_fixup_operand
= fixupOperandCB
;
4704 //strcpy(tapeLoaderName, "cargador ");
4705 tapeLoaderName
[0] = 0;
4707 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
4708 while ((c
= getopt_long(argc
, argv
, "sStTrBbnhHFLl", longOpts
, NULL
)) >= 0) {
4711 case 'S': /*optRunSNA = 1;*/ optSNA48
= 0; optWriteType
= 's'; optWTChanged
= 1; break;
4712 case 's': /*optRunSNA = 0;*/ optSNA48
= 1; optWriteType
= 's'; optWTChanged
= 1; break;
4713 case 'T': optRunTape
= 1; optWriteType
= 't'; optWTChanged
= 1; break;
4714 case 't': optRunTape
= 0; optWriteType
= 't'; optWTChanged
= 1; break;
4715 case 'L': optRunSCL
= 1; optWriteType
= 'S'; optWTChanged
= 1; break; /* with boot */
4716 case 'l': optRunSCL
= -1; optWriteType
= 'S'; optWTChanged
= 1; break; /* no boot */
4717 case 'r': case 'n': optWriteType
= c
; optWTChanged
= 1; break;
4718 case 'b': optRunTape
= 0; optRunDMB
= 0; optWriteType
= 'd'; optWTChanged
= 1; break;
4719 case 'B': optRunTape
= 0; optRunDMB
= 1; optWriteType
= 'd'; optWTChanged
= 1; break;
4720 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
4721 case 'H': optWriteType
= 'H'; optWTChanged
= 1; break;
4724 if (optarg
!= NULL
) {
4725 if (strcmp(optarg
, "asm") == 0 || strcmp(optarg
, "zas") == 0) {
4727 } else if (strcmp(optarg
, "asmdiff") == 0 || strcmp(optarg
, "zasdiff") == 0) {
4729 } else if (strcmp(optarg
, "asmz") == 0 || strcmp(optarg
, "zasz") == 0) {
4731 } else if (strcmp(optarg
, "text") == 0) {
4734 fprintf(stderr
, "FATAL: invalid fixup type: '%s'\n", optarg
);
4741 //fprintf(stderr, "ORG: %d\n", c);
4742 if (c
< 0 || c
> 65535) {
4743 fprintf(stderr
, "FATAL: invalid ORG: %d\n", c
);
4746 start_pc
= start_disp
= start_ent
= c
;
4749 //fprintf(stderr, "define: [%s]\n", optarg);
4750 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
4751 values
= realloc(values
, sizeof(char *)*(defcount
+1));
4752 defines
[defcount
] = strdup(optarg
);
4753 values
[defcount
] = strdup("1");
4756 case 602: // defzero
4757 //fprintf(stderr, "defzero: [%s]\n", optarg);
4758 defines
= realloc(defines
, sizeof(char *)*(defcount
+1));
4759 values
= realloc(values
, sizeof(char *)*(defcount
+1));
4760 defines
[defcount
] = strdup(optarg
);
4761 values
[defcount
] = strdup("0");
4765 if (optOutputDir
!= NULL
) free(optOutputDir
);
4766 optOutputDir
= strdup(optarg
);
4768 case 669: // reffile
4769 if (refFileName
) free(refFileName
);
4770 refFileName
= (optarg
? strdup(optarg
) : NULL
);
4776 if (optind
>= argc
) {
4777 // try to find default input file
4778 for (int f
= 0; defInFiles
[f
]; ++f
) {
4779 if (!access(defInFiles
[f
], R_OK
)) {
4780 inFile
= strdup(defInFiles
[f
]);
4785 inFile
= strdup(argv
[optind
]);
4788 if (!inFile
|| !inFile
[0]) {
4790 fprintf(stderr
, "ERROR: no input file!\n");
4794 if (optOutputDir
== NULL
) optOutputDir
= strdup(".");
4796 registerInstructions();
4797 registerFunctions();
4799 res
= asmTextLoad(inFile
, 0);
4801 for (int f
= 0; f
< defcount
; ++f
) {
4802 if (labelDoEQU(defines
[f
], values
[f
]) != 0) {
4803 fprintf(stderr
, "FATAL: can't define label: '%s'\n", defines
[f
]);
4808 for (pass
= 0; pass
<= 1; ++pass
) {
4810 printf("pass %d\n", pass
);
4811 setCurSrcLine(asmText
);
4812 if (setjmp(errJP
)) { res
= 1; break; }
4813 while (curSrcLine
) processCurrentLine();
4814 if (posstPass()) { res
= 1; break; }
4819 char *oc
= strdup(inFile
);
4820 char *pd
= strrchr(oc
, '.');
4821 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
4822 switch (optWriteType
) {
4823 case 's': saveSna(oc
, optSNA48
); break;
4824 case 't': saveTap(oc
); break;
4825 case 'r': saveRaw(oc
); break;
4826 case 'd': saveDMB(oc
); break;
4827 case 'H': saveHob(oc
); break;
4828 case 'S': saveSCL(oc
); break;
4831 if (optWriteFixups
) writeFixups();
4833 /* build ref file name */
4834 if (!refFileName
|| !refFileName
[0]) {
4835 if (refFileName
) free(refFileName
);
4836 refFileName
= malloc(strlen(inFile
)+128);
4837 strcpy(refFileName
, inFile
);
4838 char *ext
= strrchr(refFileName
, '.');
4840 strcat(refFileName
, ".ref");
4844 for (char *ts
= refFileName
; *ts
; ++ts
) {
4845 if (*ts
== '/' || *ts
== '\\') slash
= ts
;
4848 char *slash
= strrchr(refFileName
, '/');
4850 if (!slash
|| slash
< ext
) {
4851 strcpy(ext
, ".ref");
4853 strcat(refFileName
, ".ref");
4857 writeLabelsFile(refFileName
);
4858 printf("refs written to '%s'\n", refFileName
);
4863 fprintf(stderr
, "ERROR: loading error!\n");
4867 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
4875 if (inFile
) free(inFile
);
4876 if (sysIncludeDir
) free(sysIncludeDir
);
4877 for (int f
= defcount
-1; f
>= 0; --f
) {
4881 if (defines
!= NULL
) { free(values
); free(defines
); }
4882 if (optOutputDir
!= NULL
) free(optOutputDir
);
4883 return (res
? 1 : 0);