2 // coded by Ketmar // Vamprile Avalon
15 #include <sys/types.h>
19 #include "liburasm/liburasm.h"
29 #define MAYBE_UNUSED __attribute__((unused))
31 ///////////////////////////////////////////////////////////////////////////////
34 static char *sysIncludeDir
= NULL
;
36 #define MAX_LINE_SIZE 16384
37 static char curLine
[MAX_LINE_SIZE
]; /* current processing line; can be freely modified */
40 ///////////////////////////////////////////////////////////////////////////////
43 static void initInclideDir (void) {
44 const char *id
= getenv("URASM_INCLUDE_DIR");
46 sysIncludeDir
= strdup(id
);
49 memset(myDir
, 0, sizeof(myDir
));
50 if (readlink("/proc/self/exe", myDir
, sizeof(myDir
)-1) < 0) strcpy(myDir
, ".");
52 char *p
= (char *)strrchr(myDir
, '/');
53 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
55 strcat(myDir
, "/libs");
56 sysIncludeDir
= strdup(myDir
);
58 while (sysIncludeDir
[0] && sysIncludeDir
[strlen(sysIncludeDir
)-1] == '/') sysIncludeDir
[strlen(sysIncludeDir
)-1] = '\0';
59 if (!sysIncludeDir
[0]) strcpy(sysIncludeDir
, ".");
63 ///////////////////////////////////////////////////////////////////////////////
66 /* trim trailing spaces and comments */
67 static void normalizeStr (char *s
) {
71 for (p
= s
; *p
; ++p
) {
76 case 'x': case 'X': // hex code
78 for (int f
= 0; f
< 2; ++f
, ++p
) if (!isxdigit(*p
)) break;
79 --p
; // last digit will be skiped by 'for'
81 case '0': // octal code
82 for (int f
= 0; f
< 4; ++f
, ++p
) if (!isdigit(*p
) || *p
> '7') break;
83 --p
; // last digit will be skiped by 'for'
85 case '1' ... '9': // decimal code
86 for (int f
= 0; f
< 3; ++f
, ++p
) if (!isdigit(*p
)) break;
87 --p
; // last digit will be skiped by 'for'
89 default: ; // other escapes, do nothing
92 if (*p
== inQ
) inQ
= 0;
98 case '"': case '\'': // string catched
101 case ';': // end of line, comment follows
102 *p
-- = '\0'; // strip it and quit
104 case ':': // reduce colons
105 while (p
> s
&& isspace(p
[-1])) --p
;
107 //fprintf(stderr, ":[%s]\n", p);
108 if (p
[1] && (p
[1] == ':' || isspace(p
[1]))) {
111 for (e
= p
+2; *e
&& (*e
== ':' || isspace(*e
)); ++e
) ;
113 while ((*t
++ = *e
++)) ;
114 //fprintf(stderr, "::[%s]\n", p);
117 default: ; // do nothing
121 for (p
= s
+strlen(s
)-1; p
>= s
&& (isspace(*p
) || *p
== ':'); --p
) ;
126 /* returns NULL or pointer to args */
127 /* skips spaces after command if any */
128 static char *strIsCommand (const char *command
, char *str
) {
129 for (int cnt
= 1; cnt
> 0; --cnt
) {
130 while (*str
&& isspace(*str
)) ++str
; // skip spaces
131 for (; *command
&& *str
; ++command
, ++str
) {
132 if (toupper(*command
) != toupper(*str
)) return NULL
; // alas
134 if (*command
) return NULL
; // alas
135 if (*str
&& isalnum(*str
)) return NULL
; // alas
136 while (*str
&& isspace(*str
)) ++str
; // skip spaces
137 if (*str
&& *str
== ':') break; // try again if we have a colon
144 /* don't free() result */
145 /* skips trailing spaces */
146 static char *parseStr (char **str
, char endQ
, int *lenp
) {
147 static char buf
[MAX_LINE_SIZE
];
148 int len
= 0, n
, f
, base
;
151 int xDigit (char ch
, int base
) {
152 if (ch
< '0') return -1;
154 if (ch
>= '0'+base
) return -1;
158 if (ch
<= '9') return ch
-'0';
159 if (ch
< 'A' || ch
> 'A'+base
-10) return -1;
161 return (ch
< base
? ch
: -1);
164 memset(buf
, 0, sizeof(buf
));
170 case 'a': buf
[len
++] = '\a'; break;
171 case 'b': buf
[len
++] = '\b'; break;
172 case 'e': buf
[len
++] = '\x1b'; break;
173 case 'f': buf
[len
++] = '\f'; break;
174 case 'n': buf
[len
++] = '\n'; break;
175 case 'r': buf
[len
++] = '\r'; break;
176 case 't': buf
[len
++] = '\t'; break;
177 case 'v': buf
[len
++] = '\v'; break;
178 case 'z': buf
[len
++] = '\0'; break;
179 case 'x': case 'X': // hex
182 donum
: for (n
= 0; f
> 0; --f
) {
183 char ch
= xDigit(*a
++, base
);
185 if (ch
< 0) { --a
; break; }
190 --a
; // return to the last digit, 'for' will skip it
195 case '1' ... '9': // decimal
198 default: buf
[len
++] = a
[0]; break; // others
201 if (*a
== endQ
) { ++a
; break; }
205 while (*a
&& isspace(*a
)) ++a
; // skip trailing spaces
208 if (lenp
) *lenp
= len
;
213 ///////////////////////////////////////////////////////////////////////////////
214 // source file stack, reader, etc
216 typedef struct SourceLine
{
217 struct SourceLine
*next
;
223 static SourceLine
*asmText
= NULL
;
224 static SourceLine
*asmTextLast
= NULL
;
225 static SourceLine
*curSrcLine
= NULL
;
228 typedef struct MacroDef
{
229 struct MacroDef
*next
;
233 char *argdef
[32]; // default values
234 char *argnames
[32]; // argument names
239 char *argvals
[32]; // argument values
242 static MacroDef
*maclist
= NULL
;
243 static CurMacroDef
*curmacro
= NULL
; // !NULL if we are not inserting macro
244 static int curmacronum
;
247 static void asmTextClear (void) {
249 SourceLine
*l
= asmText
;
251 asmText
= asmText
->next
;
256 asmTextLast
= curSrcLine
= NULL
;
260 static void normIncName (char *dest
, const char *fn
, int system
) {
263 if (system
) sprintf(dest
, "%s/%s", sysIncludeDir
, fn
); else sprintf(dest
, "%s", fn
);
264 if (stat(dest
, &st
)) return;
265 if (S_ISDIR(st
.st_mode
)) strcat(dest
, "/zzmain.zas");
269 static int asmTextLoad (const char *fname
) {
274 static int includeCount
= 0;
276 if (!(fl
= fopen(fname
, "r"))) {
277 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
280 printf("loading: %s\n", fname
);
282 while (fgets(curLine
, sizeof(curLine
)-1, fl
)) {
284 curLine
[sizeof(curLine
)-1] = '\0';
285 normalizeStr(curLine
);
286 //fprintf(stderr, "*[%s]\n", curLine);
287 if (!curLine
[0]) continue; // don't store empty lines
288 // find specials, if any
289 if (isspace(curLine
[0])) {
290 if ((args
= strIsCommand("INCLUDE", curLine
)) != NULL
) {
297 fprintf(stderr
, "ERROR: file %s, line %d: invalid INCLUDE!\n", fname
, lineNo
);
300 if (args
[0] == '"' || args
[0] == '\'') qCh
= *args
++;
301 else if (args
[0] == '<') { qCh
= '>'; ++args
; system
= 1; }
303 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
306 fprintf(stderr
, "ERROR: file %s, line %d: can't INCLUDE nothing!\n", fname
, lineNo
);
311 fprintf(stderr
, "ERROR: file %s, line %d: extra args after INCLUDE filename!\n", fname
, lineNo
);
314 if (includeCount
> 256) {
316 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", fname
, lineNo
);
319 normIncName(curLine
, fn
, system
);
320 //if (system) sprintf(curLine, "%s/%s", sysIncludeDir, fn); else sprintf(curLine, "%s", fn);
321 if ((fn
= strdup(curLine
)) == NULL
) abort();
323 system
= asmTextLoad(fn
);
326 if (system
) { fclose(fl
); return system
; }
331 if ((s
= calloc(1, sizeof(SourceLine
))) == NULL
) abort();
333 if ((s
->line
= strdup(curLine
)) == NULL
) abort();
334 if ((s
->fname
= strdup(fname
)) == NULL
) abort();
335 if (asmTextLast
) asmTextLast
->next
= s
; else asmText
= s
;
343 static inline void loadCurSrcLine (void) { if (curSrcLine
) strcpy(curLine
, (curSrcLine
!= NULL
? curSrcLine
->line
: "")); }
344 static inline SourceLine
*setCurSrcLine (SourceLine
*l
) { curSrcLine
= l
; loadCurSrcLine(); return l
; }
345 static inline SourceLine
*nextSrcLine (void) { return (curSrcLine
!= NULL
? setCurSrcLine(curSrcLine
->next
) : NULL
); }
348 ///////////////////////////////////////////////////////////////////////////////
351 static void processCurrentLine (void); // only one, will skip to next one
354 ///////////////////////////////////////////////////////////////////////////////
355 // error raisers, etc
357 static jmp_buf errJP
;
360 static void errorWriteFile (void) {
362 fprintf(stderr
, "at file %s, line %d\n%s\n*", curSrcLine
->fname
, curSrcLine
->lineNo
, curSrcLine
->line
);
364 fprintf(stderr
, "somewhere in time: ");
368 static void errorMsgV (const char *fmt
, va_list ap
) {
370 vfprintf(stderr
, fmt
, ap
);
377 static void __attribute__((format(printf
, 1, 2))) warningMsg (const char *fmt
, ...) {
379 fprintf(stderr
, "WARNING ");
385 static void __attribute__((format(printf
, 1, 2))) errorMsg (const char *fmt
, ...) {
387 fprintf(stderr
, "FATAL ");
393 static void __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2))) fatal (const char *fmt
, ...) {
401 static void __attribute__((noreturn
)) fatalUrLib (int errcode
) {
402 errorMsg("%s", urErrorMessage(errcode
));
407 //////////////////////////////////////////////////////////////////////////////
408 // operator management
410 typedef int (*UrAsmOpFn
) (void);
412 typedef struct UrAsmOp
{
415 struct UrAsmOp
*next
;
418 static UrAsmOp
*oplist
= NULL
;
421 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
422 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
424 res
->name
= strdup(name
);
432 static UrAsmOp
*urFindOp (const char *name
) {
434 for (res
= oplist
; res
; res
= res
->next
) if (!strcasecmp(name
, res
->name
)) break;
439 static void urClearOps (void) {
443 oplist
= oplist
->next
;
450 ///////////////////////////////////////////////////////////////////////////////
453 typedef struct UrLabelInfo
{
456 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
457 int known
; /* !0: label value already known */
458 int refLine
; /* first referenced line */
459 int fixuptype
; /* UR_FIXUP_XXX */
461 struct UrLabelInfo
*next
;
464 static UrLabelInfo
*labels
= NULL
;
467 static void urClearLabels (void) {
470 while ((c
= labels
) != NULL
) {
472 if (c
->name
) free(c
->name
);
473 if (c
->refFile
) free(c
->refFile
);
479 static UrLabelInfo
*urFindLabel (const char *name
) {
480 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) if (strcmp(name
, c
->name
) == 0) return c
;
485 static UrLabelInfo
*urAddLabel (const char *name
) {
486 UrLabelInfo
*c
= urFindLabel(name
);
491 for (p
= NULL
, c
= labels
; c
; p
= c
, c
= c
->next
) ;
492 c
= calloc(1, sizeof(UrLabelInfo
));
494 c
->name
= strdup(name
);
496 c
->fixuptype
= UR_FIXUP_NONE
;
497 if (p
) p
->next
= c
; else labels
= c
;
504 ///////////////////////////////////////////////////////////////////////////////
505 // module list management
507 typedef struct ModuleInfo
{
509 char *fname
; // opened in this file
510 int seen
; // !0: module already seen, skip other definitions from the same file
511 struct ModuleInfo
*next
;
514 static ModuleInfo
*modules
= NULL
;
515 static ModuleInfo
*curModule
= NULL
;
518 static void modulesClear (void) {
521 ModuleInfo
*c
= modules
;
523 modules
= modules
->next
;
531 static void modulesResetSeen (void) {
532 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
536 static ModuleInfo
*moduleFind (const char *name
) {
537 if (!name
|| !name
[0]) return NULL
;
538 for (ModuleInfo
*c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) return c
;
543 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
546 if (!name
|| !fname
|| !name
[0] || !fname
[0]) abort();
547 if ((c
= calloc(1, sizeof(ModuleInfo
))) == NULL
) abort();
548 if ((c
->name
= strdup(name
)) == NULL
) abort();
549 if ((c
->fname
= strdup(fname
)) == NULL
) abort();
551 return (modules
= c
);
555 ///////////////////////////////////////////////////////////////////////////////
557 typedef struct FixupItem
{
558 struct FixupItem
*next
;
564 static FixupItem
*fixlisthead
= NULL
, *fixlisttail
= NULL
;
567 static void clearFixups (void) {
570 while ((c
= fixlisthead
) != NULL
) {
571 fixlisthead
= c
->next
;
577 static void addFixup (uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
578 FixupItem
*fx
= calloc(1, sizeof(FixupItem
));
580 fx
->opdestaddr
= opdestaddr
;
582 fx
->fixuptype
= fixuptype
;
585 if (fixlisttail
!= NULL
) fixlisttail
->next
= fx
; else fixlisthead
= fx
;
591 ///////////////////////////////////////////////////////////////////////////////
592 // destination memory management
594 static uint8_t memory
[65536];
595 static char memused
[65536];
596 static char memresv
[65536];
597 static uint16_t pc
= 0; /* current position to write */
598 static uint16_t disp
= 0; /* current 'virtual PC' */
599 static uint16_t ent
= 0; /* starting address */
600 static uint16_t clrAddr
= 24999; /* address for CLEAR for cargador */
601 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
602 static int inTapeBlock
= 0;
603 static uint8_t tapeXorB
= 0;
606 static inline uint8_t getByte (uint16_t addr
) {
612 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
613 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
618 static inline void putByte (uint16_t addr
, uint8_t b
) {
619 if (inTapeBlock
) tapeXorB
^= b
;
625 static inline MAYBE_UNUSED
void putWord (uint16_t addr
, uint16_t w
) {
626 putByte(addr
, w
&0xFFU
);
627 putByte(addr
+1, (w
>>8)&0xFFU
);
631 static inline void emitByte (uint8_t b
) {
638 static inline void emitWord (uint16_t w
) {
640 emitByte((w
>>8)&0xFFU
);
644 static inline void emitRWord (uint16_t w
) {
645 emitByte((w
>>8)&0xFFU
);
650 static void prepareMemory (void) {
651 memset(memory
, 0, sizeof(memory
));
652 memset(memused
, 0, sizeof(memused
));
653 memset(memresv
, 0, sizeof(memresv
));
657 ///////////////////////////////////////////////////////////////////////////////
658 // label getter and utilities
660 static char *lastSeenGlobalLabel
= NULL
; /* global */
663 static char *fixLocalLabel (const char *name
) {
664 static char newname
[MAX_LINE_SIZE
*2+1024];
666 memset(newname
, 0, sizeof(newname
));
667 if (!name
|| !name
[0]) {
669 } else if (!lastSeenGlobalLabel
|| name
[0] != '.') {
670 strcpy(newname
, name
);
672 if (name
[0] == '.' && name
[1] == '.') {
673 // this is macro label
674 if (curmacro
== NULL
) fatal("macro label outside of macro: '%s'", name
);
675 sprintf(newname
, "{#MAC%d:%s:%s}", curmacronum
, curmacro
->mac
->name
, name
);
677 // this is local label, let's rename it
678 sprintf(newname
, "{%s%s}", lastSeenGlobalLabel
, name
);
685 static char *fixGlobalLabel (const char *name
) {
686 static char newname
[MAX_LINE_SIZE
*2+1024];
688 memset(newname
, 0, sizeof(newname
));
689 if (!name
|| !name
[0]) {
691 } else if (!curModule
|| name
[0] == '.' || name
[0] == '{' || name
[0] == '@' || strchr(name
, '.')) {
692 if (name
[0] == '@' && name
[1]) ++name
;
693 strcpy(newname
, name
);
695 // this is global unqualified label and we have a module; let's rename it
696 sprintf(newname
, "%s.%s", curModule
->name
, name
);
698 //printf("%s --> %s\n", name, newname);
703 static int lblOptMakeU2
= 0;
705 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
, int *fixuptype
) {
709 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
710 lbl
= urFindLabel(nn
);
712 // try non-module label
713 lbl
= urFindLabel(ln
);
717 errorMsg("using undefined label %s", ln
);
722 lbl
= urAddLabel(nn
);
723 lbl
->type
= (lblOptMakeU2
? -42 : -1);
725 lbl
->refLine
= curSrcLine
->lineNo
;
726 lbl
->refFile
= strdup(curSrcLine
->fname
);
727 //printf("new label: [%s]\n", lbl->name);
729 //printf("label reference: [%s]\n", lbl->name);
733 *defined
= lbl
->known
!=0;
734 *fixuptype
= lbl
->fixuptype
;
749 static int isLabelDefinedOrKnown (const char *name
, uint16_t addr
, int qtype
) {
753 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
754 lbl
= urFindLabel(nn
);
756 // try non-module label
757 lbl
= urFindLabel(ln
);
760 case UR_QTYPE_DEFINED
: return lbl
? lbl
->known
!=0 : 0;
761 case UR_QTYPE_KNOWN
: return lbl
!=NULL
;
768 static void fixupOperandCB (const URAOperand
*op
, uint16_t opdestaddr
, uint16_t opaddr
, int fixuptype
, int size
) {
770 //static const char *n[4] = {"none", "word", "low", "high"};
771 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
772 addFixup(opdestaddr
, opaddr
, fixuptype
, size
);
777 static int checkLabels (void) {
780 for (UrLabelInfo
*c
= labels
; c
; c
= c
->next
) {
782 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
785 if (c
->type
== 0) c
->known
= -1;
787 //if (wasError) longjmp(errJP, 667);
792 ///////////////////////////////////////////////////////////////////////////////
795 static inline char *strSkipSpaces (const char *s
) {
796 while (*s
&& isspace(*s
)) ++s
;
801 static inline char *strSkipSpacesColons (char *s
) {
802 while (*s
&& (isspace(*s
) || *s
== ':')) ++s
;
807 static inline void skipSpaces (void) {
808 char *e
= strSkipSpaces(curLine
);
810 if (e
!= curLine
) memmove(curLine
, e
, strlen(e
)+1);
814 static char *skipArg (char *str
) {
816 str
= strSkipSpaces(str
);
817 if (!str
[0] || str
[0] == ',' || str
[0] == ':') return str
;
818 if (str
[0] == '"' || str
[0] == '\'') {
821 while (*str
&& *str
!= qch
) {
822 if (*str
++ == '\\') {
825 for (int f
= 0; f
< 2; ++f
, ++str
) if (!isxdigit(*str
)) break;
828 for (int f
= 0; f
< 4; ++f
, ++str
) if (*str
< '0' || *str
> '7') break;
831 for (int f
= 0; f
< 3; ++f
, ++str
) if (!isdigit(*str
)) break;
843 static int32_t getExprArg (int *defined
, int *addr
) {
845 char *a
= strSkipSpaces(curLine
);
849 if (!a
[0]) fatal("expression expected");
850 ee
= urExpression(&res
, a
, disp
, defined
, addr
, &error
);
851 if (error
) fatalUrLib(error
);
853 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
854 memmove(curLine
, ee
, strlen(ee
)+1);
862 static int32_t getOneExprArg (int *defined
, int *addr
) {
863 int32_t res
= getExprArg(defined
, addr
);
865 if (curLine
[0] && curLine
[0] != ':') fatal("too many expressions");
870 static inline int isStrArg (void) { return (curLine
[0] == '"' || curLine
[0] == '\''); }
873 static char *getStrArg (int *lenp
) {
875 char *a
= strSkipSpaces(curLine
);
878 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
879 res
= parseStr(&a
, qCh
, lenp
);
881 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
882 memmove(curLine
, a
, strlen(a
)+1);
890 static MAYBE_UNUSED
char *getOneStrArg (int *lenp
) {
891 char *res
= getStrArg(lenp
);
893 if (curLine
[0] && curLine
[0] != ':') fatal("too many expressions");
898 static char *getLabelArg (int checkdelim
) {
899 static char res
[MAX_LINE_SIZE
+128], *p
;
900 char *a
= strSkipSpaces(curLine
);
902 memset(res
, 0, sizeof(res
));
903 if (!a
[0]) fatal("label expected");
904 for (p
= res
; *a
&& *a
!= ',' && *a
!= ':' && *a
!= '=' && !isspace(*a
); ++a
, ++p
) *p
= *a
;
905 for (; p
> res
&& isspace(p
[-1]); --p
) ;
907 if (p
-res
> 120) fatal("label name too long: %s", res
);
908 while (*a
&& isspace(*a
)) ++a
;
910 if (checkdelim
&& a
[0] != ',' && a
[0] != ':') fatal("bad label expression");
911 memmove(curLine
, a
, strlen(a
)+1);
919 static char *getOneLabelArg (void) {
920 char *res
= getLabelArg(1);
922 if (curLine
[0] && curLine
[0] != ':') fatal("too many expressions");
927 /* res == 0: end of expression */
928 static int skipComma (void) {
929 char *a
= strSkipSpaces(curLine
);
931 if (!a
[0]) { curLine
[0] = '\0'; return 0; }
933 while (a
[0] && (a
[0] == ':' || isspace(a
[0]))) ++a
;
934 if (!a
[0]) { curLine
[0] = '\0'; return 0; }
935 memmove(curLine
, a
, strlen(a
)+1);
938 if (a
[0] != ',') fatal("invalid expression: ',' expected");
939 for (++a
; *a
&& isspace(*a
); ++a
) ;
940 if (!a
[0]) { curLine
[0] = '\0'; return 0; }
941 memmove(curLine
, a
, strlen(a
)+1);
947 static void skipInstruction (void) {
950 for (int cnt
= 1; cnt
> 0; --cnt
) {
951 while (*str
&& isspace(*str
)) ++str
; // skip spaces
952 while (*str
&& !isspace(*str
)) ++str
; // skip non-spaces
953 while (*str
&& isspace(*str
)) ++str
; // skip spaces
954 if (!str
[0] || *str
!= ':') break;
955 // try again if we have a colon
957 memmove(curLine
, str
, strlen(str
)+1);
961 ///////////////////////////////////////////////////////////////////////////////
964 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
965 char *ep
= strSkipSpacesColons(curLine
);
967 memmove(curLine
, ep
, strlen(ep
)+1);
971 static void checkExprEnd (void) {
972 char *ep
= strSkipSpaces(curLine
);
974 memmove(curLine
, ep
, strlen(ep
)+1);
975 if (curLine
[0] && curLine
[0] != ':') fatal("end of expression expected");
979 /* remove label from curLine */
980 static void removeLabel (void) {
983 if (ep
[0] && !isspace(ep
[0])) for (ep
= curLine
; *ep
&& !isspace(*ep
) && *ep
!= ':'; ++ep
) ; // skip text
984 // skip spaces and colons
985 ep
= strSkipSpacesColons(ep
);
986 memmove(curLine
, ep
, strlen(ep
)+1);
990 static void processLabel (void) {
995 int noLocAff
= 0, doEQU
= 0;
997 memset(n2
, 0, sizeof(n2
));
998 if (!curLine
[0] || isspace(curLine
[0]) || curLine
[0] == ':') { removeLabel(); return; } // removeLabel() removes any spaces, etc
1000 for (ep
= curLine
; *ep
&& !isspace(*ep
) && *ep
!= ':'; ++ep
) ;
1001 if (ep
-curLine
> 120) fatal("label too long");
1003 memset(n2
, 0, sizeof(n2
));
1004 memmove(n2
, curLine
, ep
-curLine
);
1005 if (!urIsValidLabelName(n2
)) return; // this is not a valid label, get out of here
1006 // check if this can be instruction
1007 ep
= strSkipSpaces(ep
);
1008 if (*ep
!= ':' && urFindOp(n2
)) return; // this must be and instruction, process it
1009 // ok, we got a good label
1011 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
1012 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
1013 lbl
= urAddLabel(nn
);
1014 if (!lbl
->refFile
) {
1015 lbl
->refLine
= curSrcLine
->lineNo
;
1016 lbl
->refFile
= strdup(curSrcLine
->fname
);
1018 //printf("new: [%s]\n", lbl->name);
1020 if (curLine
[0] == '=') {
1022 argstart
= curLine
+1;
1025 argstart
= strIsCommand("EQU", curLine
);
1027 if (!argstart
|| doEQU
) {
1028 if (pass
== 0 && lbl
->type
!= -1) fatal("duplicate label %s", lbl
->name
);
1032 memmove(curLine
, argstart
, strlen(argstart
)+1);
1034 if (lbl
->type
!= -1 && lbl
->type
!= 0) fatal("duplicate label %s", lbl
->name
);
1037 int defined
= 1, addr
= UR_FIXUP_NONE
;
1038 int32_t res
= getOneExprArg(&defined
, &addr
);
1041 if (addr
!= UR_FIXUP_NONE
) lbl
->fixuptype
= addr
;
1046 if (pass
!= 0) fatal("can't calculate label %s", lbl
->name
);
1052 if (lbl
->name
[0] != '{' && !noLocAff
) {
1053 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
1054 lastSeenGlobalLabel
= strdup(lbl
->name
);
1059 lbl
->fixuptype
= UR_FIXUP_WORD
;
1063 ///////////////////////////////////////////////////////////////////////////////
1064 // instruction finder (in source)
1066 /* array ends with NULL */
1067 /* returns line or NULL */
1068 /* iidx will be set to found instruction number */
1069 static __attribute__((sentinel
)) SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
1070 if (iidx
) *iidx
= -1;
1071 for (SourceLine
*cur
= curSrcLine
; cur
; cur
= cur
->next
) {
1074 //if (!isspace(cur->line[0])) continue; // fuck labeled strings
1076 for (int f
= 0; ;++f
) {
1077 const char *name
= va_arg(ap
, const char *);
1080 if (strIsCommand(name
, cur
->line
)) {
1082 if (iidx
) *iidx
= f
;
1092 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
1093 return findNextInstructionFromList(NULL
, name
, NULL
);
1097 ///////////////////////////////////////////////////////////////////////////////
1100 static int optWriteFixups
= 0;
1101 static int optRunTape
= 1;
1102 static int optRunDMB
= 1;
1103 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1104 static int optSNA48
= 1;
1107 ///////////////////////////////////////////////////////////////////////////////
1108 static void writeFixups (void) {
1109 FILE *fo
= fopen("zfixuptable.txt", "w");
1111 if (fo
== NULL
) fatal("can't write fixup file");
1112 fprintf(fo
, "; addr dadr sz\n");
1113 for (const FixupItem
*fx
= fixlisthead
; fx
!= NULL
; fx
= fx
->next
) {
1114 static const char type
[4] = "NWLH";
1115 fprintf(fo
, "%c #%04x #%04x %d\n", type
[fx
->fixuptype
], fx
->opaddr
, fx
->opdestaddr
, fx
->size
);
1121 ///////////////////////////////////////////////////////////////////////////////
1122 /* return 'found' flag */
1123 static int findChunkFrom (int addr
, int *start
, int *len
) {
1124 if (addr
< 0) addr
= 0;
1125 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) ;
1126 if (addr
> 65535) return 0;
1128 for (; addr
<= 65535 && memused
[addr
]; ++addr
) ;
1129 *len
= addr
-(*start
);
1134 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
1135 for (; buflen
>= 2; buflen
-= 2) {
1137 uint8_t byte
= *buf
++;
1141 for (cnt
= byte
; cnt
>= 0; --cnt
, ++addr
, --buflen
, ++buf
) {
1142 if (!memused
[addr
]) putByte(addr
, *buf
);
1143 if (addr
== 0xffff) return;
1147 for (; cnt
> 0; --cnt
, ++addr
) {
1148 if (!memused
[addr
]) putByte(addr
, byte
);
1149 if (addr
== 0xffff) return;
1156 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
1157 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1158 if (bxor
) *bxor
= (*bxor
)^b
;
1163 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
1164 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
1165 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
1170 ///////////////////////////////////////////////////////////////////////////////
1173 static int saveSna (const char *fname
, int as48
) {
1174 char *fn
= malloc(strlen(fname
)+16);
1179 sprintf(fn
, "%s.sna", fname
);
1180 fo
= fopen(fn
, "wb");
1182 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
1183 printf("out: %s.sna\n", fname
);
1184 memmove(regs
, ursna48
, 27);
1187 /* push new address */
1188 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1190 putByte(sp
, ent
&0xFFU
);
1191 putByte(sp
+1, (ent
>>8)&0xFFU
);
1192 regs
[23] = sp
&0xFFU
;
1193 regs
[24] = (sp
>>8)&0xFFU
;
1195 fwrite(regs
, 27, 1, fo
);
1196 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
1197 fwrite(memory
+16384, 49152, 1, fo
);
1200 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1202 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1203 //fprintf(stderr, "%d\n", memused[sp]);
1204 if (memused
[sp
] || memused
[(sp
+1)&0xffff]) {
1205 fprintf(stderr
, "FATAL: can't save snapshot!\n");
1209 if (fwrite(ursna48
, 27, 1, fo
) != 1) goto error
;
1210 rleUnpack(16384, (const uint8_t *)ursna48
+27, sizeof(ursna48
)-27);
1211 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1213 sprintf(abuf
, "%05d", clrAddr
);
1214 memcpy(memory
+23762, abuf
, 5);
1215 sprintf(abuf
, "%05d", ent
);
1216 memcpy(memory
+23773, abuf
, 5);
1218 if (fwrite(memory
+16384, 49152, 1, fo
) != 1) goto error
;
1221 int addr
= memory
[sp
]+256*memory
[(sp
+1)&0xffff];
1223 if (fWriteWord(fo
, addr
, NULL
) != 0) goto error
; // PC
1224 if (fWriteByte(fo
, 0x10, NULL
) != 0) goto error
; // 7FFD
1225 if (fWriteByte(fo
, 0, NULL
) != 0) goto error
; // TR-DOS inactive
1227 memset(memory
, 0, 16384);
1228 for (int f
= 1; f
< 8; ++f
) {
1229 if (f
!= 2 && f
!= 5) {
1230 if (fwrite(memory
, 16384, 1, fo
) != 1) goto error
;
1244 ///////////////////////////////////////////////////////////////////////////////
1247 static void saveRaw (const char *basename
) {
1248 char *fname
= malloc(strlen(basename
)+16);
1252 while (findChunkFrom(start
, &start
, &len
)) {
1253 sprintf(fname
, "%s_%04X.%s", basename
, start
, optTapExt
?"tap":"bin");
1254 fo
= fopen(fname
, "wb");
1256 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1258 printf("out: %s\n", fname
);
1259 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1260 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1273 ///////////////////////////////////////////////////////////////////////////////
1276 typedef struct __attribute__((packed
)) __attribute__((gcc_struct
)) {
1281 uint8_t zero
; // always zero
1282 uint8_t secLen
; // length in sectors
1287 static uint16_t calcHobSum (const void *hdr
) {
1288 const uint8_t *buf
= (const uint8_t *)hdr
;
1291 for (unsigned int f
= 0; f
< 15; ++f
) res
+= ((uint16_t)buf
[f
])*257+f
;
1296 static void saveHob (const char *basename
) {
1297 char *fname
= malloc(strlen(basename
)+16);
1302 while (findChunkFrom(start
, &start
, &len
)) {
1303 sprintf(fname
, "%s_%04X.%s", basename
, start
, "$C");
1304 fo
= fopen(fname
, "wb");
1306 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1309 printf("out: %s\n", fname
);
1310 memset(&hdr
, 0, sizeof(hdr
));
1311 memset(&hdr
.name
, 32, 8);
1312 sprintf(hdr
.name
, "%c%04X", basename
[0], start
);
1313 hdr
.name
[strlen(hdr
.name
)] = 32;
1317 hdr
.secLen
= (len
+255)/256;
1318 hdr
.checksum
= calcHobSum(&hdr
);
1319 if (fwrite(&hdr
, sizeof(hdr
), 1, fo
) != 1) {
1320 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1325 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1326 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1333 if (fwrite(&hdr
.zero
, 1, 1, fo
) != 1) {
1334 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1340 //fprintf(stderr, ":%d\n", len%256);
1350 ///////////////////////////////////////////////////////////////////////////////
1353 static int saveDMB (const char *fname
) {
1354 char *fn
= malloc(strlen(fname
)+16);
1359 sprintf(fn
, "%s.dmb", fname
);
1360 fo
= fopen(fn
, "wb");
1362 if (!fo
) { fprintf(stderr
, "ERROR: can't write DMB file: %s.dmb\n", fname
); return -1; }
1364 while (findChunkFrom(start
, &start
, &len
)) { ++pcnt
; start
+= len
; }
1366 printf("out: %s.dmb\n", fname
);
1367 if (fwrite("DMB1", 4, 1, fo
) != 1) goto error
;
1368 if (fWriteWord(fo
, (optRunDMB
? ent
: 0), NULL
) != 0) goto error
;
1369 if (fWriteWord(fo
, clrAddr
, NULL
) != 0) goto error
;
1370 if (fWriteWord(fo
, pcnt
, NULL
) != 0) goto error
;
1373 while (findChunkFrom(start
, &start
, &len
)) {
1374 if (fWriteWord(fo
, len
, NULL
) != 0) goto error
;
1375 if (fWriteWord(fo
, start
, NULL
) != 0) goto error
;
1376 if (fwrite(memory
+start
, len
, 1, fo
) != 1) goto error
;
1384 fprintf(stderr
, "ERROR: error writing file %s.dmb!\n", fname
);
1389 ///////////////////////////////////////////////////////////////////////////////
1392 static char tapeLoaderName
[16];
1395 static void saveTapCargador (FILE *fo
) {
1397 static uint8_t cargador
[16384]; // should be enough for everyone
1398 int start
= 0, len
, pos
, f
;
1402 void putStr (const char *s
) {
1403 for (; *s
; ++s
) cargador
[pos
++] = *s
;
1406 void putNum (int num
) {
1408 sprintf(buf
, "%d", num
);
1415 cargador
[0] = 0; cargador
[1] = 10;
1416 // size (will be fixed later)
1417 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\""); // CLEAR VAL "xxx"
1419 while (findChunkFrom(start
, &start
, &len
)) {
1421 putStr(":\xef\"\"\xaf");
1425 // :RANDOMIZE USR VAL "xxx"
1426 putStr(":\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
1428 cargador
[2] = (pos
-4)&0xff;
1429 cargador
[3] = ((pos
-4)>>8)&0xff;
1431 fWriteWord(fo
, 19, NULL
); // length of header
1433 fWriteByte(fo
, 0, &bxor
); // header block
1434 fWriteByte(fo
, 0, &bxor
); // 'basic' flag
1435 if (tapeLoaderName
[0]) {
1436 for (int f
= 0; f
< 10; ++f
) fWriteByte(fo
, tapeLoaderName
[f
], &bxor
);
1438 fWriteByte(fo
, 'c', &bxor
);
1439 fWriteByte(fo
, 'a', &bxor
);
1440 fWriteByte(fo
, 'r', &bxor
);
1441 fWriteByte(fo
, 'g', &bxor
);
1442 fWriteByte(fo
, 'a', &bxor
);
1443 fWriteByte(fo
, 'd', &bxor
);
1444 fWriteByte(fo
, 'o', &bxor
);
1445 fWriteByte(fo
, 'r', &bxor
);
1446 fWriteByte(fo
, ' ', &bxor
);
1447 fWriteByte(fo
, ' ', &bxor
);
1449 fWriteWord(fo
, pos
, &bxor
); // length
1450 fWriteWord(fo
, 10, &bxor
); // start
1451 fWriteWord(fo
, pos
, &bxor
); // length2
1452 fWriteByte(fo
, bxor
, NULL
);
1454 fWriteWord(fo
, pos
+2, NULL
); // plus type and checkbyte
1456 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
1457 for (f
= 0; f
< pos
; ++f
) fWriteByte(fo
, cargador
[f
], &bxor
);
1458 fWriteByte(fo
, bxor
, NULL
);
1462 static void saveTap (const char *basename
) {
1463 char *fname
= malloc(strlen(basename
)+16);
1465 int start
= 0, len
, f
;
1469 sprintf(fname
, "%s.tap", basename
);
1470 fo
= fopen(fname
, "wb");
1472 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
1473 printf("out: %s.tap\n", basename
);
1474 if (optRunTape
) saveTapCargador(fo
);
1475 while (findChunkFrom(start
, &start
, &len
)) {
1477 if (tapeLoaderName
[0]) {
1478 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1479 memcpy(blkname
, tapeLoaderName
, 10);
1481 sprintf(blkname
, "c%04X:%04X", start
, len
);
1483 //printf(" block: %s\n", blkname);
1484 fWriteWord(fo
, 19, NULL
); // length of header
1486 fWriteByte(fo
, 0, &bxor
); // header block
1487 fWriteByte(fo
, 3, &bxor
); // 'code' flag
1488 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
1489 fWriteWord(fo
, len
, &bxor
);
1490 fWriteWord(fo
, start
, &bxor
);
1491 fWriteWord(fo
, 32768, &bxor
);
1492 fWriteByte(fo
, bxor
, NULL
);
1494 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
1496 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
1497 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
1498 fWriteByte(fo
, bxor
, NULL
);
1505 ///////////////////////////////////////////////////////////////////////////////
1506 // pseudoinstructions
1508 // note that processCurrentLine() will NOT skip to the next line before
1509 // calling pseudoinstruction handler!
1510 // note that processCurrentLine() will skip current line after calling
1511 // pseudoinstruction handler!
1513 static int wasOrg
= 0;
1514 static int wasClr
= 0;
1517 ///////////////////////////////////////////////////////////////////////////////
1520 static int piDISPLAYX (int passNo
, int asHex
) {
1524 char *res
= getStrArg(&len
);
1526 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
1529 int32_t v
= getExprArg(&defined
, NULL
);
1531 if (passNo
< 0 || pass
== passNo
) {
1532 if (asHex
) printf("%04X", (unsigned int)v
);
1533 else printf("%d", v
);
1536 if (!skipComma()) break;
1542 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
1543 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
1544 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
1545 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
1546 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
1547 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
1550 ///////////////////////////////////////////////////////////////////////////////
1553 static int piORG (void) {
1555 int32_t res
= getOneExprArg(&defined
, NULL
);
1557 if (!defined
) fatal("sorry, ORG operand value must be known here");
1558 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
1559 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
1564 if (!wasClr
&& res
> 0) clrAddr
= res
-1;
1570 static int piDISP (void) {
1572 int32_t res
= getOneExprArg(&defined
, NULL
);
1574 if (!defined
) fatal("sorry, DISP operand value must be known here");
1575 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
1576 //printf("DISP=%d\n", res);
1582 static int piENDDISP (void) {
1583 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
1590 static int piENT (void) {
1592 int32_t res
= getOneExprArg(&defined
, NULL
);
1594 //if (!defined) fatal("sorry, ENT operand value must be known here");
1595 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
1601 static int piCLR (void) {
1603 int32_t res
= getOneExprArg(&defined
, NULL
);
1605 //if (!defined) fatal("sorry, CLR operand value must be known here");
1606 if (res
< 0 || res
> 65535) fatal("invalid CLR operand value: %d", res
);
1613 static int piRESERVE (void) {
1615 int defined = 1, start;
1616 int32_t res = getExprArg(&defined, NULL);
1617 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1618 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1620 if (!skipComma()) fatal("RESERVE needs 2 args");
1621 res = getOneExprArg(&defined, NULL);
1622 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1623 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1624 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
1626 fatal("RESERVE: not yet!");
1631 static int piALIGN (void) {
1635 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
1636 res
= getOneExprArg(&defined
, NULL
);
1637 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
1638 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
1639 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
1640 if (res
> 0 && pc
%res
!= 0) {
1650 static int piDISPALIGN (void) {
1652 int32_t res
= getOneExprArg(&defined
, NULL
);
1654 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
1655 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
1656 if (res
> 0 && disp
%res
!= 0) {
1665 ///////////////////////////////////////////////////////////////////////////////
1668 static int defIncr
= 0;
1671 static int piDEFINCR (void) {
1673 int32_t cnt
= getOneExprArg(&defined
, NULL
);
1675 if (!defined
) fatal("DEFINCR: increment must be defined");
1681 static int piDEFBW (int isWord
) {
1683 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
1687 char *res
= getStrArg(&len
);
1689 for (f
= 0; f
< len
; ++f
) {
1690 int32_t b
= (uint8_t)res
[f
];
1692 if (b
< -128 || b
> 255) fatal("invalid operand value: %d", b
);
1693 if (b
< 0) b
+= 256;
1697 int32_t res
= getExprArg(&defined
, &fixuptype
);
1699 if (pass
> 0 && !defined
) fatal("undefined operand");
1702 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
1703 if (res
< 0) res
+= 65536;
1704 if (fixuptype
!= UR_FIXUP_NONE
) urAsmFixupOperandFn(NULL
, pc
, disp
, fixuptype
, 2);
1705 if (isWord
== 1) emitWord(res
); else emitRWord(res
);
1707 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
1708 if (fixuptype
!= UR_FIXUP_NONE
) {
1709 if (fixuptype
== UR_FIXUP_WORD
) fatal("invalid fixup for operand");
1710 urAsmFixupOperandFn(NULL
, pc
, disp
, fixuptype
, 1);
1712 if (res
< 0) res
+= 256;
1716 if (!skipComma()) break;
1721 static int piDEFB (void) { return piDEFBW(0); }
1722 static int piDEFW (void) { return piDEFBW(1); }
1723 static int piDEFR (void) { return piDEFBW(2); }
1726 static int piDEFS (void) {
1729 int defined
= 0, fixuptype
= UR_FIXUP_NONE
;
1730 int32_t res
= getExprArg(&defined
, &fixuptype
);
1732 if (pass
> 0 && !defined
) fatal("undefined operand");
1733 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
1734 if (*curLine
&& curLine
[0] == ',') {
1736 bt
= getExprArg(&defined
, NULL
);
1737 if (pass
> 0 && !defined
) fatal("undefined operand");
1739 if (bt
< -128 || bt
> 255) fatal("invalid operand value: %d", res
);
1740 if (bt
< 0) bt
+= 256;
1741 if (fixuptype
!= UR_FIXUP_NONE
) {
1742 if (fixuptype
== UR_FIXUP_WORD
) fatal("invalid fixup for operand");
1744 for (f
= 0; f
< res
; ++f
) {
1745 if (fixuptype
!= UR_FIXUP_NONE
) urAsmFixupOperandFn(NULL
, pc
, disp
, fixuptype
, 1);
1749 pc
+= res
; disp
+= res
;
1751 if (!skipComma()) break;
1757 /* bit 0: put '\0' */
1758 /* bit 1: set bit 7 of last byte */
1759 /* bit 2: put length */
1760 static int piDEFSTR (int type
) {
1764 char *res
= getStrArg(&len
);
1767 if (len
> 255) fatal("string too long");
1770 for (f
= 0; f
< len
; ++f
) {
1771 uint8_t b
= (uint8_t)res
[f
];
1773 if ((type
&0x02) && f
== len
-1) b
|= 0x80;
1776 if (type
&0x01) emitByte(0);
1779 int32_t v
= getExprArg(&defined
, NULL
);
1781 if (pass
> 0 && !defined
) fatal("undefined expression");
1782 if (!defined
) v
= 0; else v
+= defIncr
;
1783 if (v
< -128 || v
> 255) fatal("invalid operand value: %d", v
);
1784 if (v
< 0) v
+= 256;
1787 if (!skipComma()) break;
1793 static int piDEFM (void) { return piDEFSTR(0x00); }
1794 static int piDEFZ (void) { return piDEFSTR(0x01); }
1795 static int piDEFX (void) { return piDEFSTR(0x02); }
1796 static int piDEFC (void) { return piDEFSTR(0x04); }
1799 ///////////////////////////////////////////////////////////////////////////////
1802 /* INCBIN "name"[,maxlen] */
1803 static int piINCBIN (void) {
1809 char *args
= curLine
;
1811 if (!curLine
[0]) fatal("INCBIN without file name");
1815 } else if (curLine
[0] == '<') {
1822 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
1823 if (!fn
[0]) fatal("INCBIN: empty file name");
1824 memmove(curLine
, args
, strlen(args
)+1);
1826 if (curLine
[0] == ',') {
1830 maxlen
= getOneExprArg(&defined
, NULL
);
1831 if (!defined
) fatal("INCBIN: undefined maxlen");
1832 if (maxlen
< 1) return 1; // nothing to do
1836 sprintf(curLine
, "%s/%s", sysIncludeDir
, fn
);
1838 sprintf(curLine
, "%s", fn
);
1841 fl
= fopen(curLine
, "rb");
1842 if (!fl
) fatal("INCBIN: file not found: %s", curLine
);
1843 while (maxlen
-- > 0) {
1844 int res
= fread(&bt
, 1, 1, fl
);
1847 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: %s", curLine
); }
1855 ///////////////////////////////////////////////////////////////////////////////
1856 // MODULE, ENDMODULE
1858 static int piENDMODULE (void) {
1859 if (!curModule
) fatal("ENDMODULE without MODULE");
1861 char *mn
= getOneLabelArg();
1863 if (strcmp(mn
, curModule
->name
)) fatal("invalid module name in ENDMODULE: %s", mn
);
1870 static int piMODULE (void) {
1873 SourceLine
*ol
= curSrcLine
;
1876 if (curModule
) fatal("no nested modules allowed");
1877 mn
= getOneLabelArg();
1878 if (!urIsValidLabelName(mn
)) fatal("invalid module name: %s", mn
);
1879 mi
= moduleFind(mn
);
1880 //printf("[%s] %p\n", mn, mi);
1883 if (strcmp(mi
->fname
, curSrcLine
->fname
)) fatal("duplicate module definition; previous was in %s", mi
->fname
);
1885 nextSrcLine(); // skip ourself
1886 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
1888 fatal("no ENDMODULE");
1890 if (inum
== 0) fatal("no nested modules allowed");
1897 mi
= moduleAdd(mn
, curSrcLine
->fname
);
1905 ///////////////////////////////////////////////////////////////////////////////
1908 static int piEDUP (void) {
1909 fatal("EDUP without DUP");
1915 static int piDUP (void) {
1916 int defined
= 1, dupCnt
= 1, inum
;
1917 SourceLine
*stline
, *eline
= NULL
;
1918 int32_t cnt
= getOneExprArg(&defined
, NULL
);
1920 if (!defined
) fatal("DUP: counter must be defined");
1921 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
1922 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
1923 // now find corresponding EDUP
1924 // note that we should skip nested DUPs
1925 nextSrcLine(); // skip ourself
1926 stline
= curSrcLine
;
1927 while (curSrcLine
) {
1928 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "EDUP", NULL
))) break;
1929 // ok, we found something; what is it?
1933 nextSrcLine(); // skip DUP
1936 if (--dupCnt
== 0) {
1941 nextSrcLine(); // skip EDUP
1944 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
1945 // now repeat that lines
1947 setCurSrcLine(stline
);
1948 while (curSrcLine
!= eline
) processCurrentLine();
1954 ///////////////////////////////////////////////////////////////////////////////
1957 static int ifCount
= 0;
1960 static int ifSkipToEndIfOrElse (int wholeBody
) {
1961 int inum
, wasElse
= 0;
1964 while (curSrcLine
) {
1965 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL
))) break;
1969 nextSrcLine(); // skip IF
1970 ifSkipToEndIfOrElse(1); // and recurse
1971 nextSrcLine(); // skip ENDIF
1974 if (wasElse
) fatal("duplicate ELSE");
1975 if (!wholeBody
) return 1;
1977 nextSrcLine(); // skip ELSE
1978 break; // and continue
1980 return 0; // and exit
1983 if (wasElse
) fatal("ELSEIF in ELSE");
1984 if (!wholeBody
) return 2;
1985 nextSrcLine(); // skip ELSEIF
1986 break; // and continue
1988 // skip it as a whole
1989 nextSrcLine(); // skip MACRO
1992 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) { setCurSrcLine(oline
); fatal("invalid MACRO"); }
1993 if (inum
== 1) break;
1994 fatal("invalid nested MACRO");
1996 nextSrcLine(); // skip ENDM
1999 fatal("unexpected ENDM");
2002 fatal("IF without ENDIF");
2007 static int piENDIF (void) {
2008 if (--ifCount
< 0) fatal("ENDIF without IF");
2014 static int piELSE (void) {
2015 if (--ifCount
< 0) fatal("ELSE without IF");
2016 nextSrcLine(); // skip ELSE
2017 ifSkipToEndIfOrElse(1);
2022 static int piELSEIF (void) {
2023 if (--ifCount
< 0) fatal("ELSEIF without IF");
2024 nextSrcLine(); // skip ELSEIF
2025 ifSkipToEndIfOrElse(1);
2030 static int piIFAll (int isIfX
) {
2032 int ooo
= lblOptMakeU2
;
2033 lblOptMakeU2
= isIfX
? 1 : 0;
2034 int32_t cond
= getOneExprArg(&defined
, NULL
);
2038 if (!isIfX
) fatal("IF: condition must be defined");
2039 cond
= 0; // for IFX: 0 is there is any undefined label
2042 // ok, do it until ELSE/ELSEIF/ENDIF
2050 nextSrcLine(); // skip last instruction
2051 // skip until ELSE/ELSEIF/ENDIF
2052 r
= ifSkipToEndIfOrElse(0);
2053 if (r
== 0) break; // ENDIF
2054 if (r
== 1) { ++ifCount
; break; } // ELSE
2055 // ELSEIF, do condition
2056 if ((args
= strIsCommand("ELSEIF", curLine
)) == NULL
) fatal("internal error in conditionals"); // the thing that should not be
2057 memmove(curLine
, args
, strlen(args
)+1);
2058 cond
= getOneExprArg(&defined
, NULL
);
2059 if (!defined
) fatal("ELSEIF: condition must be defined");
2060 if (cond
) { ++ifCount
; break; } // condition is true
2066 static int piIF (void) { return piIFAll(0); }
2067 static int piIFX (void) { return piIFAll(1); }
2070 ///////////////////////////////////////////////////////////////////////////////
2073 * what i did with MACRO is the brain-damaged cheating all the way.
2074 * first, i will collect the MACRO body and remember it.
2075 * second, when the macro is used, i will:
2076 * * create unique labels for all supplied macro args, each with
2077 * number (so IFARG/IFNARG can check the existance)
2078 * * insert the whole macro body in place, fixing argument refs
2079 * * let the asm play with it
2080 * another tricky part is 'local labels': i have to change all
2081 * '..lbl' references -- generate new label name for it and
2082 * replace all refs. and be careful to not touch the strings.
2083 * this is not the best scheme, but it is fairly simple and it works.
2085 // will be called when parser encounters term starting with '=' or '*'
2086 // first term char will not be skipped
2087 // must return pointer to the first char after expression end
2088 static const char *getValueCB (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2089 char name
[257], *p
= name
;
2091 if (curmacro
== NULL
) fatal("'=' outside of macro");
2092 if (*expr
++ != '=') fatal("'=' expected!");
2093 //fprintf(stderr, "GV: [%s]\n", expr);
2095 expr
= strSkipSpaces(expr
);
2097 if (isalnum(*expr
) || *expr
== '_') {
2098 if (p
-name
> 250) fatal("id too long");
2105 expr
= strSkipSpaces(expr
);
2106 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
2107 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
2110 int l
= strlen(curmacro
->argvals
[f
]);
2113 urInitExprValue(&v
);
2114 expr
= urExpressionEx(&v
, expr
, addr
, &donteval
, defined
, error
);
2115 if (*error
) return expr
;
2116 if (expr
== NULL
|| *expr
!= ']' || v
.str
!= NULL
) { *error
= UR_EXPRERR_FUNC
; return expr
; }
2118 if (v
.val
< 0) v
.val
+= l
;
2119 if (v
.val
< 0 || v
.val
>= l
) {
2122 res
->val
= (unsigned char)(curmacro
->argvals
[f
][v
.val
]);
2126 urExpressionEx(res
, curmacro
->argvals
[f
], addr
, &donteval
, defined
, error
);
2127 //fprintf(stderr, "GV: [%s] [%s] [%d]\n", name, curmacro->argvals[f], res->val);
2132 fatal("unknown macro variable: '%s'", name
);
2136 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
2137 static int expandCB (char *opr
, int oprlen
) {
2138 char name
[257], *p
= name
, *op
= opr
;
2140 if (curmacro
== NULL
) fatal("'=' outside of macro");
2141 if (*op
++ != '=') fatal("'=' expected!");
2142 //fprintf(stderr, "expand: [%s]\n", opr);
2144 op
= strSkipSpaces(op
);
2146 if (isalnum(*op
) || *op
== '_') {
2147 if (p
-name
> 250) fatal("id too long");
2154 op
= strSkipSpaces(op
);
2155 for (int f
= 0; f
< curmacro
->mac
->argc
; ++f
) {
2156 if (strcasecmp(name
, curmacro
->mac
->argnames
[f
]) == 0) {
2159 int l
= strlen(curmacro
->argvals
[f
]), error
= 0, defined
= 1, donteval
= 0;
2162 urInitExprValue(&v
);
2163 op
= (char *)urExpressionEx(&v
, op
, pc
, &donteval
, &defined
, &error
);
2164 if (error
) return -1;
2165 if (op
== NULL
|| *op
!= ']' || v
.str
!= NULL
) return -1;
2166 op
= strSkipSpaces(op
+1);
2168 if (v
.val
< 0) v
.val
+= l
;
2169 if (v
.val
< 0 || v
.val
>= l
) {
2172 opr
[0] = curmacro
->argvals
[f
][v
.val
];
2178 strncpy(opr
, curmacro
->argvals
[f
], oprlen
);
2180 return (*op
? -1 : 0);
2183 fatal("unknown macro variable: '%s'", name
);
2187 static void processMacro (MacroDef
*mc
) {
2188 SourceLine
*oldcurline
= curSrcLine
;
2191 //fprintf(stderr, "processMacro: [%s]\n", mc->name);
2192 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
2194 //parse macro arguments
2197 for (int f
= 0; f
< mc
->argc
; ++f
) {
2203 for (int c
= f
; c
< mc
->argc
; ++c
) {
2204 if (mc
->argdef
[c
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[c
]);
2205 cm
.argvals
[c
] = strdup(mc
->argdef
[c
]);
2210 if (curLine
[0] == ',') {
2211 if (mc
->argdef
[f
] == NULL
) fatal("macro '%s' has no default value for argument '%s'", mc
->name
, mc
->argnames
[f
]);
2212 cm
.argvals
[f
] = strdup(mc
->argdef
[f
]);
2213 memmove(curLine
, curLine
+1, strlen(curLine
));
2217 e
= skipArg(curLine
);
2218 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
2219 if ((ech
= *e
) != 0) {
2220 if (f
== mc
->argc
-1 || ech
!= ',') fatal("invalid macro invocation");
2223 cm
.argvals
[f
] = strdup(curLine
);
2226 memmove(curLine
, e
+1, strlen(e
));
2233 if (curLine
[0]) fatal("invalid macro invocation");
2235 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
2237 setCurSrcLine(mc
->lines
);
2241 while (curSrcLine
!= NULL
) {
2242 //fprintf(stderr, "*[%s]\n", curSrcLine->line);
2243 processCurrentLine();
2246 setCurSrcLine(oldcurline
);
2254 static MacroDef
*findMacro (const char *name
) {
2255 for (MacroDef
*mc
= maclist
; mc
!= NULL
; mc
= mc
->next
) if (strcasecmp(name
, mc
->name
) == 0) return mc
;
2260 static int piMACRO (void) {
2265 SourceLine
*stline
, *eline
= NULL
;
2268 name
= strdup(getLabelArg(0));
2269 //fprintf(stderr, "[%s]\n", name);
2270 if (findMacro(name
) != NULL
) fatal("duplicate MACRO definition: '%s'", name
);
2271 if (curLine
[0] == ',') memmove(curLine
, curLine
+1, strlen(curLine
));
2274 while (curLine
[0]) {
2275 if (argc
>= 31) fatal("too many arguments in MACRO");
2276 if (!isalpha(curLine
[0])) fatal("invalid MACRO definition");
2278 argnames
[argc
] = strdup(getLabelArg(0));
2279 if (curLine
[0] == '=') {
2281 char *e
= strchr(curLine
, ','), tch
;
2283 if (e
== NULL
) e
= curLine
+strlen(curLine
);
2286 argdef
[argc
] = strdup(curLine
+1);
2288 memmove(curLine
, e
, strlen(e
)+1);
2290 argdef
[argc
] = NULL
;
2293 if (curLine
[0] == ',') {
2294 memmove(curLine
, curLine
+1, strlen(curLine
));
2297 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdef[argc]);
2301 // now find corresponding ENDM
2302 // note that we should skip nested DUPs
2303 stline
= curSrcLine
;
2304 nextSrcLine(); // skip ourself
2305 while (curSrcLine
) {
2308 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) break;
2309 // ok, we found something; what is it?
2312 fatal("no nested MACROs yet");
2318 nextSrcLine(); // skip ENDM
2322 if (!eline
) { setCurSrcLine(stline
); fatal("no ENDM"); }
2324 if ((mc
= calloc(1, sizeof(*mc
))) == NULL
) fatal("out of memory");
2327 for (int f
= 0; f
< argc
; ++f
) { mc
->argdef
[f
] = argdef
[f
]; mc
->argnames
[f
] = argnames
[f
]; }
2330 if ((tline = calloc(1, sizeof(*tline))) == NULL) fatal("out of memory");
2331 tline->next = stline->next;
2332 tline->line = strdup(stline->line);
2333 tline->fname = strdup(stline->fname);
2334 tline->lineNo = stline->lineNo;
2337 mc
->lines
= stline
->next
;
2338 stline
->next
= curSrcLine
;
2339 stline
->line
[0] = 0;
2343 //fatal("sorry, no MACRO yet");
2348 static int piENDM (void) {
2349 fatal("ENDM without MACRO");
2354 ///////////////////////////////////////////////////////////////////////////////
2356 static int optWriteType
= 't';
2357 static int optWTChanged
= 0;
2360 static void piTapParseLoaderName (void) {
2365 if (!isStrArg()) fatal("loader name expected");
2366 fn
= getStrArg(&len
);
2367 if (len
> 10) fatal("loader name too long");
2368 memset(tapeLoaderName
, ' ', 10);
2369 for (int f
= 0; f
< len
; ++f
) tapeLoaderName
[f
] = fn
[f
];
2374 static int piDEFFMT (void) {
2380 name
= getStrArg(&len
);
2382 name
= getLabelArg(1);
2384 if (optWTChanged
) return 1;
2385 if (!strcasecmp(name
, "SNA") || !strcasecmp(name
, "RUNSNA")) {
2387 //optRunSNA = (toupper(name[0]) == 'R');
2389 if (curLine
[0]) fatal("too many expressions");
2392 if (!strcasecmp(name
, "TAP") || !strcasecmp(name
, "TAPE")) {
2396 piTapParseLoaderName();
2399 if (!strcasecmp(name
, "RUNTAP") || !strcasecmp(name
, "RUNTAPE")) {
2403 piTapParseLoaderName();
2406 if (!strcasecmp(name
, "BIN") || !strcasecmp(name
, "RAW")) {
2410 if (curLine
[0]) fatal("too many expressions");
2413 if (!strcasecmp(name
, "DMB") || !strcasecmp(name
, "RUNDMB")) {
2416 optRunDMB
= (toupper(name
[0]) == 'R');
2418 if (curLine
[0]) fatal("too many expressions");
2421 if (!strcasecmp(name
, "NONE") || !strcasecmp(name
, "NOTHING")) {
2425 if (curLine
[0]) fatal("too many expressions");
2428 fatal("invalid default output type: %s", name
);
2432 ///////////////////////////////////////////////////////////////////////////////
2435 static void processCurrentLine (void) {
2436 if (!curSrcLine
) return; // do nothing
2440 char *str
, *ee
, name
[66];
2445 removeSpacesAndColons();
2446 // skip spaces and ':'
2447 if (!curLine
[0]) { nextSrcLine(); break; }
2448 // try to find and process command
2449 str
= curLine
; //while (*str && isspace(*str)) ++str; // skip spaces
2451 for (ee
= str
; *ee
&& !isspace(*ee
) && *ee
!= '"' && *ee
!= '\''; ++ee
) ;
2452 // get command, if any
2453 if (ee
!= str
&& ee
-str
<= 64) {
2456 memset(name
, 0, sizeof(name
));
2457 memmove(name
, str
, ee
-str
);
2459 op
= urFindOp(name
);
2462 str
= ee
; while (*str
&& isspace(*str
)) ++str
; // skip spaces
2463 memmove(curLine
, str
, strlen(str
)+1);
2465 nextSrcLine(); // skip it
2470 if ((mc
= findMacro(name
)) != NULL
) {
2472 str
= ee
; while (*str
&& isspace(*str
)) ++str
; // skip spaces
2473 memmove(curLine
, str
, strlen(str
)+1);
2476 //FIXME: allow ':' here?
2481 len
= urAssembleOne(curLine
, pc
, disp
, &errpos
);
2482 if (len
< 0) fatalUrLib(len
);
2483 pc
+= len
; disp
+= len
;
2484 if (len
>= 0 && errpos
) {
2485 memmove(curLine
, errpos
+1, strlen(errpos
));
2487 nextSrcLine(); // skip it
2494 ///////////////////////////////////////////////////////////////////////////////
2495 // setup instructions
2497 static void registerInstructions (void) {
2498 urAddOp("DISPLAY", piDISPLAY
);
2499 urAddOp("DISPLAY0", piDISPLAY0
);
2500 urAddOp("DISPLAYA", piDISPLAYA
);
2501 urAddOp("DISPHEX", piDISPHEX
);
2502 urAddOp("DISPHEX0", piDISPHEX0
);
2503 urAddOp("DISPHEXA", piDISPHEXA
);
2505 urAddOp("DEFFMT", piDEFFMT
);
2507 urAddOp("MACRO", piMACRO
);
2508 urAddOp("ENDM", piENDM
);
2510 urAddOp("ORG", piORG
);
2511 urAddOp("DISP", piDISP
);
2512 urAddOp("ENDDISP", piENDDISP
);
2513 urAddOp("PHASE", piDISP
);
2514 urAddOp("DEPHASE", piENDDISP
);
2515 urAddOp("UNPHASE", piENDDISP
);
2516 urAddOp("ALIGN", piALIGN
);
2517 urAddOp("DISPALIGN", piDISPALIGN
);
2518 urAddOp("PHASEALIGN", piDISPALIGN
);
2519 urAddOp("ENT", piENT
);
2520 urAddOp("CLR", piCLR
);
2521 urAddOp("RESERVE", piRESERVE
);
2523 urAddOp("INCBIN", piINCBIN
);
2525 urAddOp("MODULE", piMODULE
);
2526 urAddOp("ENDMODULE", piENDMODULE
);
2528 urAddOp("DUP", piDUP
);
2529 urAddOp("EDUP", piEDUP
);
2531 urAddOp("IF", piIF
);
2532 urAddOp("IFX", piIFX
);
2533 urAddOp("ELSE", piELSE
);
2534 urAddOp("ELSEIF", piELSEIF
);
2535 urAddOp("ELSEIFX", piELSEIF
);
2536 urAddOp("ENDIF", piENDIF
);
2538 urAddOp("DEFINCR", piDEFINCR
);
2539 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
2540 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
2541 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
2542 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
2543 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
2544 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
2545 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
2546 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
2550 ///////////////////////////////////////////////////////////////////////////////
2551 // !0: invalid label
2553 static inline void fnSkipSpaces (const char *expr) {
2554 while (*expr && isspace(*expr)) ++expr;
2560 static inline char fnNextChar (const char *expr
) {
2561 while (*expr
&& isspace(*expr
)) ++expr
;
2566 #define FN_CHECK_END do { \
2567 while (*expr && isspace(*expr)) ++expr; \
2568 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
2573 #define FN_CHECK_COMMA do { \
2574 while (*expr && isspace(*expr)) ++expr; \
2575 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
2580 static const char *readLabelName (char *buf
, const char *expr
) {
2583 while (*expr
&& isspace(*expr
)) ++expr
;
2587 if (pos
>= 128) return NULL
;
2589 if (isalnum(ch
) || ch
== '$' || ch
== '.' || ch
== '_' || ch
== '@') {
2595 if (pos
< 1) return NULL
;
2597 if (!urIsValidLabelName(buf
)) return NULL
;
2602 static const char *fnDefKn (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
, int qtype
) {
2605 if ((expr
= readLabelName(lbl
, expr
)) == NULL
) { *error
= UR_EXPRERR_LABEL
; return NULL
; }
2607 if (!donteval
) res
->val
= (isLabelDefinedOrKnown(lbl
, addr
, qtype
) != 0);
2611 static const char *fnDefined (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) { return fnDefKn(res
, expr
, addr
, donteval
, defined
, error
, UR_QTYPE_DEFINED
); }
2612 static const char *fnKnown (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) { return fnDefKn(res
, expr
, addr
, donteval
, defined
, error
, UR_QTYPE_KNOWN
); }
2615 static const char *fnAligned256 (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2616 expr
= urExpressionEx(res
, expr
, addr
, &donteval
, defined
, error
);
2618 if (!donteval
) res
->val
= (res
->val
%256 ? 0 : 1);
2623 static const char *fnSameSeg (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2626 urInitExprValue(&v0
);
2627 urInitExprValue(&v1
);
2628 expr
= urExpressionEx(&v0
, expr
, addr
, &donteval
, defined
, error
);
2629 if (*error
) return expr
;
2631 expr
= urExpressionEx(&v1
, expr
, addr
, &donteval
, defined
, error
);
2632 if (*error
) return expr
;
2634 if (!donteval
) res
->val
= (v0
.val
/256 == v1
.val
/256);
2639 static const char *fnAlign (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2642 urInitExprValue(&v0
);
2643 urInitExprValue(&v1
);
2644 expr
= urExpressionEx(&v0
, expr
, addr
, &donteval
, defined
, error
);
2645 if (*error
) return expr
;
2646 if (fnNextChar(expr
) == ',') {
2648 expr
= urExpressionEx(&v1
, expr
, addr
, &donteval
, defined
, error
);
2649 if (*error
) return expr
;
2655 if (v1
.val
< 1) { *error
= UR_EXPRERR_FUNC
; return NULL
; }
2656 res
->val
= (v0
.val
+v1
.val
-1)/v1
.val
*v1
.val
;
2662 static const char *fnLow (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2663 const char *ee
= expr
;
2665 expr
= urExpressionEx(res
, expr
, addr
, &donteval
, defined
, error
);
2668 if (res
->fixuptype
== UR_FIXUP_HIBYTE
) {
2669 *error
= UR_EXPRERR_FUNC
; return ee
;
2671 res
->fixuptype
= UR_FIXUP_LOBYTE
;
2678 static const char *fnHigh (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2679 const char *ee
= expr
;
2681 expr
= urExpressionEx(res
, expr
, addr
, &donteval
, defined
, error
);
2684 if (res
->fixuptype
== UR_FIXUP_LOBYTE
) {
2685 *error
= UR_EXPRERR_FUNC
; return ee
;
2687 res
->fixuptype
= UR_FIXUP_HIBYTE
;
2688 res
->val
= (res
->val
>>8)&0xff;
2694 static void registerFunctions (void) {
2695 urExpressionRegisterFunction("defined", fnDefined
);
2696 urExpressionRegisterFunction("known", fnKnown
);
2697 urExpressionRegisterFunction("aligned256", fnAligned256
);
2698 urExpressionRegisterFunction("align", fnAlign
);
2699 urExpressionRegisterFunction("sameseg", fnSameSeg
);
2700 urExpressionRegisterFunction("low", fnLow
);
2701 urExpressionRegisterFunction("high", fnHigh
);
2705 ///////////////////////////////////////////////////////////////////////////////
2706 // preparing another pass
2708 static void initPass (void) {
2709 curSrcLine
= asmText
;
2711 pc
= disp
= ent
= 0x100; // viva CP/M!
2720 lastSeenGlobalLabel
= strdup(" [MAIN] ");
2726 static int posstPass (void) {
2727 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
); lastSeenGlobalLabel
= NULL
;
2728 if (checkLabels()) return -1;
2729 if (ifCount
!= 0) fatal("unbalanced IFs");
2734 ///////////////////////////////////////////////////////////////////////////////
2737 static struct option longOpts
[] = {
2738 {"sna", 0, NULL
, 's'},
2739 {"sna128", 0, NULL
, 'S'},
2740 {"tap", 0, NULL
, 't'},
2741 {"autotap", 0, NULL
, 'T'},
2742 /*{"rawtap", 0, NULL, 'T'},*/
2743 {"raw", 0, NULL
, 'r'},
2744 {"autodmb", 0, NULL
, 'B'},
2745 {"dmb", 0, NULL
, 'b'},
2746 {"none", 0, NULL
, 'n'},
2747 {"help", 0, NULL
, 'h'},
2748 {"hob", 0, NULL
, 'H'},
2749 {"fixups", 0, NULL
, 'H'},
2754 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
2757 static void usage (const char *pname
) {
2759 "usage: %s [options] infile\n"
2760 "default infiles:", pname
);
2761 for (int f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
2764 " -s --sna write 48K .SNA file with autostart\n"
2765 " -S --sna128 write 148K .SNA file with autostart\n"
2766 " -t --tap write .tap file\n"
2767 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
2768 " -r --raw write raw file(s)\n"
2769 " -b --dmb write DMB file\n"
2770 " -B --autodmb write DMB file with autostart\n"
2771 " -H --hob write HoBeta code file(s)\n"
2772 " -n --none write nothing\n"
2773 " -F --fixups write fixup file 'zfixuptable.txt'\n"
2774 " -h --help this help\n");
2778 ///////////////////////////////////////////////////////////////////////////////
2781 int main (int argc
, char *argv
[]) {
2783 const char *pname
= argv
[0];
2784 char *inFile
= NULL
;
2787 urGetByteFn
= getByte
;
2788 urPutByteFn
= putByte
;
2789 urFindLabelByNameFn
= findLabelCB
;
2790 urGetValueFn
= getValueCB
;
2791 urExpandFn
= expandCB
;
2792 //urIsLabelDefinedOrKnownFn = isLabelDefinedOrKnown;
2793 urAsmFixupOperandFn
= fixupOperandCB
;
2795 //strcpy(tapeLoaderName, "cargador ");
2796 tapeLoaderName
[0] = 0;
2798 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
2799 while ((c
= getopt_long(argc
, argv
, "sStTbBrnhHF", longOpts
, NULL
)) >= 0) {
2801 case 'S': /*optRunSNA = 1;*/ optSNA48
= 0; optWriteType
= 's'; optWTChanged
= 1; break;
2802 case 's': /*optRunSNA = 0;*/ optSNA48
= 1; optWriteType
= 's'; optWTChanged
= 1; break;
2803 case 'T': optRunTape
= 1; optWriteType
= 't'; optWTChanged
= 1; break;
2804 case 't': optRunTape
= 0; optWriteType
= 't'; optWTChanged
= 1; break;
2805 case 'b': optRunTape
= 0; optRunDMB
= 0; optWriteType
= 'd'; optWTChanged
= 1; break;
2806 case 'B': optRunTape
= 0; optRunDMB
= 1; optWriteType
= 'd'; optWTChanged
= 1; break;
2807 case 'H': optWriteType
= 'H'; optWTChanged
= 1; break;
2808 case 'r': case 'n': optWriteType
= c
; optWTChanged
= 1; break;
2809 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
2810 case 'F': optWriteFixups
= 1; break;
2814 if (optind
>= argc
) {
2815 // try to find default input file
2817 for (f
= 0; defInFiles
[f
]; ++f
) {
2818 if (!access(defInFiles
[f
], R_OK
)) {
2819 inFile
= strdup(defInFiles
[f
]);
2824 inFile
= strdup(argv
[optind
]);
2826 if (!inFile
|| !inFile
[0]) {
2828 fprintf(stderr
, "ERROR: no input file!\n");
2832 registerInstructions();
2833 registerFunctions();
2835 res
= asmTextLoad(inFile
);
2838 printf("dumping...\n");
2839 FILE *fo = fopen("z000.out", "w");
2842 for (c = asmText; c; c = c->next) fprintf(fo, "%s ; %s: %d\n", c->line, c->fname, c->lineNo);
2846 for (pass
= 0; pass
<= 1; ++pass
) {
2848 printf("pass %d\n", pass
);
2849 setCurSrcLine(asmText
);
2850 if (setjmp(errJP
)) { res
= 1; break; }
2851 while (curSrcLine
) processCurrentLine();
2852 if (posstPass()) { res
= 1; break; }
2856 char *oc
= strdup(inFile
);
2857 char *pd
= strrchr(oc
, '.');
2858 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
2859 switch (optWriteType
) {
2860 case 's': saveSna(oc
, optSNA48
); break;
2861 case 't': saveTap(oc
); break;
2862 case 'r': saveRaw(oc
); break;
2863 case 'd': saveDMB(oc
); break;
2864 case 'H': saveHob(oc
); break;
2867 if (optWriteFixups
) writeFixups();
2870 fprintf(stderr
, "ERROR: loading error!\n");
2873 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
2880 if (inFile
) free(inFile
);
2881 if (sysIncludeDir
) free(sysIncludeDir
);