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 /*TODO: expression parser should understant escapes in strings!*/
67 /* trim trailing spaces and comments */
68 static void normalizeStr (char *s
) {
72 for (p
= s
; *p
; ++p
) {
77 case 'x': case 'X': // hex code
79 for (f
= 0; f
< 2; ++f
) if (!isxdigit(*p
)) break;
80 --p
; // last digit will be skiped by 'for'
82 case '0': // octal code
83 for (f
= 0; f
< 4; ++f
) if (!isdigit(*p
) || *p
> '7') break;
84 --p
; // last digit will be skiped by 'for'
86 case '1' ... '9': // decimal code
87 for (f
= 0; f
< 3; ++f
) if (!isdigit(*p
)) break;
88 --p
; // last digit will be skiped by 'for'
90 default: ; // other escapes, do nothing
93 if (*p
== inQ
) inQ
= 0;
99 case '"': case '\'': // string catched
102 case ';': // end of line, comment follows
103 *p
-- = '\0'; // strip it and quit
105 default: ; // do nothing
109 for (p
= s
+strlen(s
)-1; p
>= s
&& isspace(*p
); --p
) ;
114 /* returns NULL or pointer to args */
115 /* skips spaces after command if any */
116 static char *strIsCommand (const char *command
, char *str
) {
119 for (cnt
= 1; cnt
> 0; --cnt
) {
120 while (*str
&& isspace(*str
)) ++str
; // skip spaces
121 for (; *command
&& *str
; ++command
, ++str
) {
122 if (toupper(*command
) != toupper(*str
)) return NULL
; // alas
124 if (*command
) return NULL
; // alas
125 if (*str
&& isalnum(*str
)) return NULL
; // alas
126 while (*str
&& isspace(*str
)) ++str
; // skip spaces
127 if (*str
&& *str
== ':') break; // try again if we have a colon
134 /* don't free() result */
135 /* skips trailing spaces */
136 static char *parseStr (char **str
, char endQ
, int *lenp
) {
137 static char buf
[MAX_LINE_SIZE
];
138 int len
= 0, n
, f
, base
;
141 int xDigit (char ch
, int base
) {
142 if (ch
< '0') return -1;
144 if (ch
>= '0'+base
) return -1;
148 if (ch
<= '9') return ch
-'0';
149 if (ch
< 'A' || ch
> 'A'+base
-10) return -1;
151 return ch
<base
? ch
: -1;
154 memset(buf
, 0, sizeof(buf
));
160 case 'a': buf
[len
++] = '\a'; break;
161 case 'b': buf
[len
++] = '\b'; break;
162 case 'f': buf
[len
++] = '\f'; break;
163 case 'n': buf
[len
++] = '\n'; break;
164 case 'r': buf
[len
++] = '\r'; break;
165 case 't': buf
[len
++] = '\t'; break;
166 case 'v': buf
[len
++] = '\v'; break;
167 case 'z': buf
[len
++] = '\0'; break;
168 case 'x': case 'X': // hex
171 donum
: for (n
= 0; f
> 0; --f
) {
172 char ch
= xDigit(*a
++, base
);
173 if (ch
< 0) { --a
; break; }
178 --a
; // return to the last digit, 'for' will skip it
183 case '1' ... '9': // decimal
186 default: buf
[len
++] = a
[0]; break; // others
189 if (*a
== endQ
) { ++a
; break; }
193 while (*a
&& isspace(*a
)) ++a
; // skip trailing spaces
196 if (lenp
) *lenp
= len
;
201 ///////////////////////////////////////////////////////////////////////////////
202 // source file stack, reader, etc
204 typedef struct SourceLine SourceLine
;
212 static SourceLine
*asmText
= NULL
;
213 static SourceLine
*asmTextLast
= NULL
;
214 static SourceLine
*curSrcLine
= NULL
;
217 static void asmTextClear (void) {
219 SourceLine
*l
= asmText
;
221 asmText
= asmText
->next
;
226 asmTextLast
= curSrcLine
= NULL
;
230 static void normIncName (char *dest
, const char *fn
, int system
) {
233 if (system
) sprintf(dest
, "%s/%s", sysIncludeDir
, fn
); else sprintf(dest
, "%s", fn
);
234 if (stat(dest
, &st
)) return;
235 if (S_ISDIR(st
.st_mode
)) strcat(dest
, "/zzmain.zas");
239 static int asmTextLoad (const char *fname
) {
244 static int includeCount
= 0;
246 if (!(fl
= fopen(fname
, "r"))) {
247 fprintf(stderr
, "ERROR: can't open file: %s\n", fname
);
250 printf("loading: %s\n", fname
);
252 while (fgets(curLine
, sizeof(curLine
)-1, fl
)) {
254 curLine
[sizeof(curLine
)-1] = '\0';
255 normalizeStr(curLine
);
256 if (!curLine
[0]) continue; // don't store empty lines
257 // find specials, if any
258 if (isspace(curLine
[0])) {
259 if ((args
= strIsCommand("INCLUDE", curLine
)) != NULL
) {
266 fprintf(stderr
, "ERROR: file %s, line %d: invalid INCLUDE!\n", fname
, lineNo
);
269 if (args
[0] == '"' || args
[0] == '\'') qCh
= *args
++;
270 else if (args
[0] == '<') { qCh
= '>'; ++args
; system
= 1; }
271 char *fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
274 fprintf(stderr
, "ERROR: file %s, line %d: can't INCLUDE nothing!\n", fname
, lineNo
);
279 fprintf(stderr
, "ERROR: file %s, line %d: extra args after INCLUDE filename!\n", fname
, lineNo
);
282 if (includeCount
> 256) {
284 fprintf(stderr
, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", fname
, lineNo
);
287 normIncName(curLine
, fn
, system
);
288 //if (system) sprintf(curLine, "%s/%s", sysIncludeDir, fn); else sprintf(curLine, "%s", fn);
289 fn
= strdup(curLine
); if (!fn
) abort();
291 system
= asmTextLoad(fn
);
294 if (system
) { fclose(fl
); return system
; }
299 s
= calloc(1, sizeof(SourceLine
));
302 s
->line
= strdup(curLine
); if (!s
->line
) abort();
303 s
->fname
= strdup(fname
); if (!s
->fname
) abort();
304 if (asmTextLast
) asmTextLast
->next
= s
; else asmText
= s
;
312 static inline void loadCurSrcLine (void) { if (curSrcLine
) strcpy(curLine
, (curSrcLine
!= NULL
? curSrcLine
->line
: "")); }
313 static inline SourceLine
*setCurSrcLine (SourceLine
*l
) { curSrcLine
= l
; loadCurSrcLine(); return l
; }
314 static inline SourceLine
*nextSrcLine (void) { return (curSrcLine
!= NULL
? setCurSrcLine(curSrcLine
->next
) : NULL
); }
317 ///////////////////////////////////////////////////////////////////////////////
320 static void processCurrentLine (void); // only one, will skip to next one
323 ///////////////////////////////////////////////////////////////////////////////
324 // error raisers, etc
326 static jmp_buf errJP
;
329 static void errorWriteFile (void) {
331 fprintf(stderr
, "at file %s, line %d\n%s\n*", curSrcLine
->fname
, curSrcLine
->lineNo
, curSrcLine
->line
);
333 fprintf(stderr
, "somewhere in time: ");
337 static void errorMsgV (const char *fmt
, va_list ap
) {
339 vfprintf(stderr
, fmt
, ap
);
346 static void __attribute__((format(printf
, 1, 2))) warningMsg (const char *fmt
, ...) {
348 fprintf(stderr
, "WARNING ");
354 static void __attribute__((format(printf
, 1, 2))) errorMsg (const char *fmt
, ...) {
356 fprintf(stderr
, "FATAL ");
362 static void __attribute__((noreturn
)) __attribute__((format(printf
, 1, 2))) fatal (const char *fmt
, ...) {
370 static void __attribute__((noreturn
)) fatalUrLib (int errcode
) {
371 errorMsg("%s", urErrorMessage(errcode
));
376 //////////////////////////////////////////////////////////////////////////////
377 // operator management
379 typedef int (*UrAsmOpFn
) (void);
381 typedef struct UrAsmOp
{
384 struct UrAsmOp
*next
;
387 static UrAsmOp
*oplist
= NULL
;
390 static UrAsmOp
*urAddOp (const char *name
, UrAsmOpFn fn
) {
391 UrAsmOp
*res
= calloc(1, sizeof(UrAsmOp
));
393 res
->name
= strdup(name
);
401 static UrAsmOp
*urFindOp (const char *name
) {
403 for (res
= oplist
; res
; res
= res
->next
) if (!strcasecmp(name
, res
->name
)) break;
408 static void urClearOps (void) {
411 c
= oplist
; oplist
= oplist
->next
;
418 ///////////////////////////////////////////////////////////////////////////////
421 typedef struct UrLabelInfo UrLabelInfo
;
425 int type
; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
426 int known
; /* !0: label value already known */
427 int refLine
; /* first referenced line */
432 static UrLabelInfo
*labels
= NULL
;
434 static void urClearLabels (void) {
436 while ((c
= labels
)) {
437 labels
= labels
->next
;
438 if (c
->name
) free(c
->name
);
439 if (c
->refFile
) free(c
->refFile
);
445 static UrLabelInfo
*urFindLabel (const char *name
) {
447 for (c
= labels
; c
; c
= c
->next
) if (!strcmp(name
, c
->name
)) break;
452 static UrLabelInfo
*urAddLabel (const char *name
) {
453 UrLabelInfo
*c
= urFindLabel(name
);
456 for (p
= NULL
, c
= labels
; c
; p
= c
, c
= c
->next
) ;
457 c
= calloc(1, sizeof(UrLabelInfo
));
459 c
->name
= strdup(name
);
461 if (p
) p
->next
= c
; else labels
= c
;
468 ///////////////////////////////////////////////////////////////////////////////
469 // module list management
471 typedef struct ModuleInfo ModuleInfo
;
474 char *fname
; // opened in this file
475 int seen
; // !0: module already seen, skip other definitions from the same file
478 static ModuleInfo
*modules
= NULL
;
479 static ModuleInfo
*curModule
= NULL
;
482 static void modulesClear (void) {
485 ModuleInfo
*c
= modules
;
486 modules
= modules
->next
;
494 static void modulesResetSeen (void) {
496 for (c
= modules
; c
; c
= c
->next
) c
->seen
= 0;
500 static ModuleInfo
*moduleFind (const char *name
) {
502 if (!name
|| !name
[0]) return NULL
;
503 for (c
= modules
; c
; c
= c
->next
) if (!strcmp(c
->name
, name
)) break;
508 static ModuleInfo
*moduleAdd (const char *name
, const char *fname
) {
510 if (!name
|| !fname
|| !name
[0] || !fname
[0]) abort();
511 c
= calloc(1, sizeof(ModuleInfo
));
513 c
->name
= strdup(name
); if (!c
->name
) abort();
514 c
->fname
= strdup(fname
); if (!c
->fname
) abort();
521 ///////////////////////////////////////////////////////////////////////////////
522 // destination memory management
524 static uint8_t memory
[65536];
525 static char memused
[65536];
526 static char memresv
[65536];
527 static uint16_t pc
= 0; /* current position to write */
528 static uint16_t disp
= 0; /* current 'virtual PC' */
529 static uint16_t ent
= 0; /* starting address */
530 static uint16_t clrAddr
= 24999; /* address for CLEAR for cargador */
531 static int pass
= 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
532 static int inTapeBlock
= 0;
533 static uint8_t tapeXorB
= 0;
536 static inline uint8_t getByte (uint16_t addr
) {
541 static inline __attribute__((unused
)) uint16_t getWord (uint16_t addr
) {
542 return ((uint16_t)memory
[addr
])|(((uint16_t)memory
[addr
+1])<<8);
546 static inline void putByte (uint16_t addr
, uint8_t b
) {
547 if (inTapeBlock
) tapeXorB
^= b
;
553 static inline MAYBE_UNUSED
void putWord (uint16_t addr
, uint16_t w
) {
554 putByte(addr
, w
&0xFFU
);
555 putByte(addr
+1, (w
>>8)&0xFFU
);
559 static inline void emitByte (uint8_t b
) {
566 static inline void emitWord (uint16_t w
) {
568 emitByte((w
>>8)&0xFFU
);
572 static inline void emitRWord (uint16_t w
) {
573 emitByte((w
>>8)&0xFFU
);
578 static void prepareMemory (void) {
579 memset(memory
, 0, sizeof(memory
));
580 memset(memused
, 0, sizeof(memused
));
581 memset(memresv
, 0, sizeof(memresv
));
585 ///////////////////////////////////////////////////////////////////////////////
586 // label getter and utilities
588 static char *lastSeenGlobalLabel
= NULL
; /* global */
591 static char *fixLocalLabel (const char *name
) {
592 static char newname
[MAX_LINE_SIZE
*2+1024];
594 memset(newname
, 0, sizeof(newname
));
595 if (!name
|| !name
[0]) newname
[0] = '\0';
596 else if (!lastSeenGlobalLabel
|| name
[0] != '.') strcpy(newname
, name
);
598 // this is local label, let's rename it
599 sprintf(newname
, "{%s%s}", lastSeenGlobalLabel
, name
);
605 static char *fixGlobalLabel (const char *name
) {
606 static char newname
[MAX_LINE_SIZE
*2+1024];
608 memset(newname
, 0, sizeof(newname
));
609 if (!name
|| !name
[0]) newname
[0] = '\0';
610 else if (!curModule
|| name
[0] == '.' || name
[0] == '{' || name
[0] == '@' || strchr(name
, '.')) {
611 if (name
[0] == '@' && name
[1]) ++name
;
612 strcpy(newname
, name
);
614 // this is global unqualified label and we have a module; let's rename it
615 sprintf(newname
, "%s.%s", curModule
->name
, name
);
617 //printf("%s --> %s\n", name, newname);
622 static int lblOptMakeU2
= 0;
623 static int32_t findLabelCB (const char *name
, uint16_t addr
, int *defined
, int *found
) {
627 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
628 lbl
= urFindLabel(nn
);
630 // try non-module label
631 lbl
= urFindLabel(ln
);
635 errorMsg("using undefined label %s", ln
);
640 lbl
= urAddLabel(nn
);
641 lbl
->type
= lblOptMakeU2
? -42 : -1;
643 lbl
->refLine
= curSrcLine
->lineNo
;
644 lbl
->refFile
= strdup(curSrcLine
->fname
);
645 //printf("new label: [%s]\n", lbl->name);
647 //printf("label reference: [%s]\n", lbl->name);
651 *defined
= lbl
->known
!=0;
667 static int isLabelDefinedOrKnown (const char *name
, uint16_t addr
, int qtype
) {
671 nn
= fixGlobalLabel((ln
= fixLocalLabel(name
)));
672 lbl
= urFindLabel(nn
);
674 // try non-module label
675 lbl
= urFindLabel(ln
);
678 case UR_QTYPE_DEFINED
: return lbl
? lbl
->known
!=0 : 0;
679 case UR_QTYPE_KNOWN
: return lbl
!=NULL
;
686 static int checkLabels (void) {
690 for (c
= labels
; c
; c
= c
->next
) {
692 fprintf(stderr
, "ERROR at file %s, line %d: referencing undefined label: %s\n", c
->refFile
, c
->refLine
, c
->name
);
695 if (c
->type
== 0) c
->known
= -1;
697 //if (wasError) longjmp(errJP, 667);
702 ///////////////////////////////////////////////////////////////////////////////
705 static int32_t getExprArg (int *defined
) {
711 while (*a
&& isspace(*a
)) ++a
;
712 if (!a
[0]) fatal("expression expected");
713 ee
= urExpression(&res
, a
, disp
, defined
, &error
);
714 if (error
) fatalUrLib(error
);
716 if (ee
[0] != ',' && ee
[0] != ':') fatal("bad expression");
717 memmove(curLine
, ee
, strlen(ee
)+1);
725 static int32_t getOneExprArg (int *defined
) {
726 int32_t res
= getExprArg(defined
);
728 if (curLine
[0] && curLine
[0] != ':') fatal("too many expressions");
733 static int isStrArg (void) { return (curLine
[0] == '"' || curLine
[0] == '\''); }
736 static char *getStrArg (int *lenp
) {
740 while (*a
&& isspace(*a
)) ++a
;
742 if (qCh
!= '"' && qCh
!= '\'') fatal("string expected");
743 res
= parseStr(&a
, qCh
, lenp
);
745 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
746 memmove(curLine
, a
, strlen(a
)+1);
754 static MAYBE_UNUSED
char *getOneStrArg (int *lenp
) {
755 char *res
= getStrArg(lenp
);
757 if (curLine
[0] && curLine
[0] != ':') fatal("too many expressions");
762 static char *getLabelArg (void) {
763 static char res
[MAX_LINE_SIZE
+128], *p
;
766 memset(res
, 0, sizeof(res
));
767 while (*a
&& isspace(*a
)) ++a
;
768 if (!a
[0]) fatal("label expected");
769 for (p
= res
; *a
&& *a
!= ','; ++a
, ++p
) *p
= *a
;
770 for (; p
> res
&& isspace(p
[-1]); --p
) ;
772 if (p
-res
> 120) fatal("label name too long: %s", res
);
773 while (*a
&& isspace(*a
)) ++a
;
775 if (a
[0] != ',' && a
[0] != ':') fatal("bad string expression");
776 memmove(curLine
, a
, strlen(a
)+1);
784 static char *getOneLabelArg (void) {
785 char *res
= getLabelArg();
787 if (curLine
[0] && curLine
[0] != ':') fatal("too many expressions");
792 /* res == 0: end of expression */
793 static int skipComma (void) {
796 for (a
= curLine
; *a
&& isspace(*a
); ++a
) ;
797 if (!a
[0]) { curLine
[0] = '\0'; return 0; }
799 while (a
[0] && (a
[0] == ':' || isspace(a
[0]))) ++a
;
800 if (!a
[0]) { curLine
[0] = '\0'; return 0; }
801 memmove(curLine
, a
, strlen(a
)+1);
804 if (a
[0] != ',') fatal("invalid expression: ',' expected");
805 for (++a
; *a
&& isspace(*a
); ++a
) ;
806 if (!a
[0]) { curLine
[0] = '\0'; return 0; }
807 memmove(curLine
, a
, strlen(a
)+1);
812 static void skipInstruction (void) {
816 for (cnt
= 1; cnt
> 0; --cnt
) {
817 while (*str
&& isspace(*str
)) ++str
; // skip spaces
818 while (*str
&& !isspace(*str
)) ++str
; // skip non-spaces
819 while (*str
&& isspace(*str
)) ++str
; // skip spaces
820 if (!str
[0] || *str
!= ':') break;
821 // try again if we have a colon
823 memmove(curLine
, str
, strlen(str
)+1);
827 ///////////////////////////////////////////////////////////////////////////////
830 static MAYBE_UNUSED
void removeSpacesAndColons (void) {
833 while (*ep
&& (isspace(*ep
) || *ep
== ':')) ++ep
;
834 memmove(curLine
, ep
, strlen(ep
)+1);
838 static void checkExprEnd (void) {
841 while (*ep
&& isspace(*ep
)) ++ep
;
842 memmove(curLine
, ep
, strlen(ep
)+1);
843 if (curLine
[0] && curLine
[0] != ':') fatal("end of expression expected");
847 /* remove label from curLine */
848 static void removeLabel (void) {
851 if (ep
[0] && !isspace(ep
[0])) for (ep
= curLine
; *ep
&& !isspace(*ep
) && *ep
!= ':'; ++ep
) ; // skip text
852 // skip spaces and colons
853 while (*ep
&& (isspace(*ep
) || *ep
== ':')) ++ep
;
854 memmove(curLine
, ep
, strlen(ep
)+1);
858 static void processLabel (void) {
863 int noLocAff
= 0, doEQU
= 0;
865 memset(n2
, 0, sizeof(n2
));
866 if (!curLine
[0] || isspace(curLine
[0]) || curLine
[0] == ':') { removeLabel(); return; } // removeLabel() removes any spaces, etc
868 for (ep
= curLine
; *ep
&& !isspace(*ep
) && *ep
!= ':'; ++ep
) ;
869 if (ep
-curLine
> 120) fatal("label too long");
871 memset(n2
, 0, sizeof(n2
));
872 memmove(n2
, curLine
, ep
-curLine
);
873 if (!urIsValidLabelName(n2
)) return; // this is not a valid label, get out of here
874 // check if this can be instruction
875 while (*ep
&& isspace(*ep
)) ++ep
;
876 if (*ep
!= ':' && urFindOp(n2
)) return; // this must be and instruction, process it
877 // ok, we got a good label
879 if (n2
[0] == '@' && n2
[1]) { noLocAff
= 1; memmove(n2
, n2
+1, strlen(n2
)); }
880 nn
= fixGlobalLabel((ln
= fixLocalLabel(n2
)));
881 lbl
= urAddLabel(nn
);
883 lbl
->refLine
= curSrcLine
->lineNo
;
884 lbl
->refFile
= strdup(curSrcLine
->fname
);
886 //printf("new: [%s]\n", lbl->name);
888 if (curLine
[0] == '=') {
890 argstart
= curLine
+1;
893 argstart
= strIsCommand("EQU", curLine
);
895 if (!argstart
|| doEQU
) {
896 if (pass
== 0 && lbl
->type
!= -1) fatal("duplicate label %s", lbl
->name
);
900 memmove(curLine
, argstart
, strlen(argstart
)+1);
902 if (lbl
->type
!= -1 && lbl
->type
!= 0) fatal("duplicate label %s", lbl
->name
);
905 int32_t res
= getOneExprArg(&defined
);
911 if (pass
!= 0) fatal("can't calculate label %s", lbl
->name
);
917 if (lbl
->name
[0] != '{' && !noLocAff
) {
918 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
919 lastSeenGlobalLabel
= strdup(lbl
->name
);
927 ///////////////////////////////////////////////////////////////////////////////
928 // instruction finder (in source)
930 /* array ends with NULL */
931 /* returns line or NULL */
932 /* iidx will be set to found instruction number */
933 static __attribute__((sentinel
)) SourceLine
*findNextInstructionFromList (int *iidx
, ...) {
934 if (iidx
) *iidx
= -1;
935 for (SourceLine
*cur
= curSrcLine
; cur
; cur
= cur
->next
) {
938 //if (!isspace(cur->line[0])) continue; // fuck labeled strings
940 for (int f
= 0; ;++f
) {
941 const char *name
= va_arg(ap
, const char *);
944 if (strIsCommand(name
, cur
->line
)) {
956 static MAYBE_UNUSED SourceLine
*findNextInstruction (const char *name
) {
957 return findNextInstructionFromList(NULL
, name
, NULL
);
961 ///////////////////////////////////////////////////////////////////////////////
964 static int optRunTape
= 1;
965 static int optRunDMB
= 1;
966 static int optTapExt
= 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
967 static int optSNA48
= 1;
970 /* return 'found' flag */
971 static int findChunkFrom (int addr
, int *start
, int *len
) {
972 if (addr
< 0) addr
= 0;
973 for (; addr
<= 65535 && !memused
[addr
]; ++addr
) ;
974 if (addr
> 65535) return 0;
976 for (; addr
<= 65535 && memused
[addr
]; ++addr
) ;
977 *len
= addr
-(*start
);
982 static void rleUnpack (uint16_t addr
, const uint8_t *buf
, int buflen
) {
983 for (; buflen
>= 2; buflen
-= 2) {
985 uint8_t byte
= *buf
++;
987 //printf("%d %u %u\n", cnt+1, byte, addr);
990 for (cnt
= byte
; cnt
>= 0; --cnt
, ++addr
, --buflen
, ++buf
) {
991 if (!memused
[addr
]) putByte(addr
, *buf
);
992 if (addr
== 0xffff) return;
996 for (; cnt
> 0; --cnt
, ++addr
) {
997 if (!memused
[addr
]) putByte(addr
, byte
);
998 if (addr
== 0xffff) return;
1005 static int fWriteByte (FILE *fo
, uint8_t b
, uint8_t *bxor
) {
1006 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1007 if (bxor
) *bxor
= (*bxor
)^b
;
1012 static int fWriteWord (FILE *fo
, uint16_t w
, uint8_t *bxor
) {
1013 if (fWriteByte(fo
, w
&0xFFU
, bxor
)) return -1;
1014 if (fWriteByte(fo
, (w
>>8)&0xFFU
, bxor
)) return -1;
1019 ///////////////////////////////////////////////////////////////////////////////
1022 static int saveSna (const char *fname
, int as48
) {
1023 char *fn
= malloc(strlen(fname
)+16);
1028 sprintf(fn
, "%s.sna", fname
);
1029 fo
= fopen(fn
, "wb");
1031 if (!fo
) { fprintf(stderr
, "ERROR: can't write SNA file: %s.sna\n", fname
); return -1; }
1032 printf("out: %s.sna\n", fname
);
1033 memmove(regs
, ursna48
, 27);
1036 /* push new address */
1037 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1039 putByte(sp
, ent
&0xFFU
);
1040 putByte(sp
+1, (ent
>>8)&0xFFU
);
1041 regs
[23] = sp
&0xFFU
;
1042 regs
[24] = (sp
>>8)&0xFFU
;
1044 fwrite(regs
, 27, 1, fo
);
1045 rleUnpack(16384, ursna48
+27, sizeof(ursna48
)-27);
1046 fwrite(memory
+16384, 49152, 1, fo
);
1049 uint16_t sp
= (uint16_t)regs
[23]+(((uint16_t)regs
[24])<<8);
1051 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1052 //fprintf(stderr, "%d\n", memused[sp]);
1053 if (memused
[sp
] || memused
[(sp
+1)&0xffff]) {
1054 fprintf(stderr
, "FATAL: can't save snapshot!\n");
1058 if (fwrite(ursna48
, 27, 1, fo
) != 1) goto error
;
1059 rleUnpack(16384, (const uint8_t *)ursna48
+27, sizeof(ursna48
)-27);
1060 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1062 sprintf(abuf
, "%05d", clrAddr
);
1063 memcpy(memory
+23762, abuf
, 5);
1064 sprintf(abuf
, "%05d", ent
);
1065 memcpy(memory
+23773, abuf
, 5);
1067 if (fwrite(memory
+16384, 49152, 1, fo
) != 1) goto error
;
1070 int addr
= memory
[sp
]+256*memory
[(sp
+1)&0xffff];
1072 if (fWriteWord(fo
, addr
, NULL
) != 0) goto error
; // PC
1073 if (fWriteByte(fo
, 0x10, NULL
) != 0) goto error
; // 7FFD
1074 if (fWriteByte(fo
, 0, NULL
) != 0) goto error
; // TR-DOS inactive
1076 memset(memory
, 0, 16384);
1077 for (int f
= 1; f
< 8; ++f
) {
1078 if (f
!= 2 && f
!= 5) {
1079 if (fwrite(memory
, 16384, 1, fo
) != 1) goto error
;
1093 ///////////////////////////////////////////////////////////////////////////////
1096 static void saveRaw (const char *basename
) {
1097 char *fname
= malloc(strlen(basename
)+16);
1101 while (findChunkFrom(start
, &start
, &len
)) {
1102 sprintf(fname
, "%s_%04X.%s", basename
, start
, optTapExt
?"tap":"bin");
1103 fo
= fopen(fname
, "wb");
1105 fprintf(stderr
, "ERROR: can't write file %s!\n", fname
);
1107 printf("out: %s\n", fname
);
1108 if (fwrite(memory
+start
, len
, 1, fo
) != 1) {
1109 fprintf(stderr
, "ERROR: error writing file %s!\n", fname
);
1122 ///////////////////////////////////////////////////////////////////////////////
1125 static int fwriteW16 (FILE *fo
, uint16_t w
) {
1128 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1130 if (fwrite(&b
, 1, 1, fo
) != 1) return -1;
1135 static int saveDMB (const char *fname
) {
1136 char *fn
= malloc(strlen(fname
)+16);
1141 sprintf(fn
, "%s.dmb", fname
);
1142 fo
= fopen(fn
, "wb");
1144 if (!fo
) { fprintf(stderr
, "ERROR: can't write DMB file: %s.dmb\n", fname
); return -1; }
1146 while (findChunkFrom(start
, &start
, &len
)) { ++pcnt
; start
+= len
; }
1148 printf("out: %s.dmb\n", fname
);
1149 if (fwrite("DMB1", 4, 1, fo
) != 1) goto error
;
1150 if (fwriteW16(fo
, (optRunDMB
? ent
: 0)) != 0) goto error
;
1151 if (fwriteW16(fo
, clrAddr
) != 0) goto error
;
1152 if (fwriteW16(fo
, pcnt
) != 0) goto error
;
1155 while (findChunkFrom(start
, &start
, &len
)) {
1156 if (fwriteW16(fo
, len
) != 0) goto error
;
1157 if (fwriteW16(fo
, start
) != 0) goto error
;
1158 if (fwrite(memory
+start
, len
, 1, fo
) != 1) goto error
;
1166 fprintf(stderr
, "ERROR: error writing file %s.dmb!\n", fname
);
1171 ///////////////////////////////////////////////////////////////////////////////
1174 static char tapeLoaderName
[16];
1177 static void saveTapCargador (FILE *fo
) {
1179 static uint8_t cargador
[16384]; // should be enough for everyone
1180 int start
= 0, len
, pos
, f
;
1184 void putStr (const char *s
) {
1185 for (; *s
; ++s
) cargador
[pos
++] = *s
;
1188 void putNum (int num
) {
1190 sprintf(buf
, "%d", num
);
1197 cargador
[0] = 0; cargador
[1] = 10;
1198 // size (will be fixed later)
1199 putStr("\xfd\xb0\""); putNum(clrAddr
); putStr("\""); // CLEAR VAL "xxx"
1201 while (findChunkFrom(start
, &start
, &len
)) {
1203 putStr(":\xef\"\"\xaf");
1207 // :RANDOMIZE USR VAL "xxx"
1208 putStr(":\xf9\xc0\xb0\""); putNum(ent
); putStr("\"\r");
1210 cargador
[2] = (pos
-4)&0xff;
1211 cargador
[3] = ((pos
-4)>>8)&0xff;
1213 fWriteWord(fo
, 19, NULL
); // length of header
1215 fWriteByte(fo
, 0, &bxor
); // header block
1216 fWriteByte(fo
, 0, &bxor
); // 'basic' flag
1217 if (tapeLoaderName
[0]) {
1218 for (int f
= 0; f
< 10; ++f
) fWriteByte(fo
, tapeLoaderName
[f
], &bxor
);
1220 fWriteByte(fo
, 'c', &bxor
);
1221 fWriteByte(fo
, 'a', &bxor
);
1222 fWriteByte(fo
, 'r', &bxor
);
1223 fWriteByte(fo
, 'g', &bxor
);
1224 fWriteByte(fo
, 'a', &bxor
);
1225 fWriteByte(fo
, 'd', &bxor
);
1226 fWriteByte(fo
, 'o', &bxor
);
1227 fWriteByte(fo
, 'r', &bxor
);
1228 fWriteByte(fo
, ' ', &bxor
);
1229 fWriteByte(fo
, ' ', &bxor
);
1231 fWriteWord(fo
, pos
, &bxor
); // length
1232 fWriteWord(fo
, 10, &bxor
); // start
1233 fWriteWord(fo
, pos
, &bxor
); // length2
1234 fWriteByte(fo
, bxor
, NULL
);
1236 fWriteWord(fo
, pos
+2, NULL
); // plus type and checkbyte
1238 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
1239 for (f
= 0; f
< pos
; ++f
) fWriteByte(fo
, cargador
[f
], &bxor
);
1240 fWriteByte(fo
, bxor
, NULL
);
1244 static void saveTap (const char *basename
) {
1245 char *fname
= malloc(strlen(basename
)+16);
1247 int start
= 0, len
, f
;
1251 sprintf(fname
, "%s.tap", basename
);
1252 fo
= fopen(fname
, "wb");
1254 if (!fo
) { fprintf(stderr
, "ERROR: can't write file %s.tap!\n", basename
); return; }
1255 printf("out: %s.tap\n", basename
);
1256 if (optRunTape
) saveTapCargador(fo
);
1257 while (findChunkFrom(start
, &start
, &len
)) {
1259 if (tapeLoaderName
[0]) {
1260 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1261 memcpy(blkname
, tapeLoaderName
, 10);
1263 sprintf(blkname
, "c%04X:%04X", start
, len
);
1265 //printf(" block: %s\n", blkname);
1266 fWriteWord(fo
, 19, NULL
); // length of header
1268 fWriteByte(fo
, 0, &bxor
); // header block
1269 fWriteByte(fo
, 3, &bxor
); // 'code' flag
1270 for (f
= 0; f
< 10; ++f
) fWriteByte(fo
, blkname
[f
], &bxor
);
1271 fWriteWord(fo
, len
, &bxor
);
1272 fWriteWord(fo
, start
, &bxor
);
1273 fWriteWord(fo
, 32768, &bxor
);
1274 fWriteByte(fo
, bxor
, NULL
);
1276 fWriteWord(fo
, len
+2, NULL
); // plus type and checkbyte
1278 fWriteByte(fo
, 0xFFU
, &bxor
); // data block
1279 for (f
= 0; f
< len
; ++f
) fWriteByte(fo
, memory
[start
+f
], &bxor
);
1280 fWriteByte(fo
, bxor
, NULL
);
1287 ///////////////////////////////////////////////////////////////////////////////
1288 // pseudoinstructions
1290 // note that processCurrentLine() will NOT skip to the next line before
1291 // calling pseudoinstruction handler!
1292 // note that processCurrentLine() will skip current line after calling
1293 // pseudoinstruction handler!
1295 static int wasOrg
= 0;
1296 static int wasClr
= 0;
1299 ///////////////////////////////////////////////////////////////////////////////
1302 static int piDISPLAYX (int passNo
, int asHex
) {
1306 char *res
= getStrArg(&len
);
1308 if (passNo
< 0 || pass
== passNo
) printf("%s", res
);
1311 int32_t v
= getExprArg(&defined
);
1313 if (passNo
< 0 || pass
== passNo
) {
1314 if (asHex
) printf("%04X", (unsigned int)v
);
1315 else printf("%d", v
);
1318 if (!skipComma()) break;
1324 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
1325 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
1326 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
1327 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
1328 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
1329 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
1332 ///////////////////////////////////////////////////////////////////////////////
1335 static int piORG (void) {
1337 int32_t res
= getOneExprArg(&defined
);
1339 if (!defined
) fatal("sorry, ORG operand value must be known here");
1340 if (res
< 0 || res
> 65535) fatal("invalid ORG operand value: %d", res
);
1341 if (inTapeBlock
) fatal("sorry, no ORGs in TAPEDATA allowed");
1346 if (!wasClr
&& res
> 0) clrAddr
= res
-1;
1352 static int piDISP (void) {
1354 int32_t res
= getOneExprArg(&defined
);
1356 if (!defined
) fatal("sorry, DISP operand value must be known here");
1357 if (res
< 0 || res
> 65535) fatal("invalid DISP operand value: %d", res
);
1358 //printf("DISP=%d\n", res);
1364 static int piENDDISP (void) {
1365 if (inTapeBlock
) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
1372 static int piENT (void) {
1374 int32_t res
= getOneExprArg(&defined
);
1376 //if (!defined) fatal("sorry, ENT operand value must be known here");
1377 if (res
< 0 || res
> 65535) fatal("invalid ENT operand value: %d", res
);
1383 static int piCLR (void) {
1385 int32_t res
= getOneExprArg(&defined
);
1387 //if (!defined) fatal("sorry, CLR operand value must be known here");
1388 if (res
< 0 || res
> 65535) fatal("invalid CLR operand value: %d", res
);
1395 static int piRESERVE (void) {
1397 int defined = 1, start;
1398 int32_t res = getExprArg(&defined);
1399 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1400 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1402 if (!skipComma()) fatal("RESERVE needs 2 args");
1403 res = getOneExprArg(&defined);
1404 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1405 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1406 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
1408 fatal("RESERVE: not yet!");
1413 static int piALIGN (void) {
1417 if (inTapeBlock
) fatal("sorry, no ALIGNs in TAPEDATA allowed");
1418 res
= getOneExprArg(&defined
);
1419 if (!defined
) fatal("sorry, ALIGN operand value must be known here");
1420 if (res
< 0 || res
> 65535) fatal("invalid ALIGN operand value: %d", res
);
1421 if (pc
!= disp
) fatal("sorry, no ALIGNs in desynced blocks allowed");
1422 if (res
> 0 && pc
%res
!= 0) {
1432 static int piDISPALIGN (void) {
1434 int32_t res
= getOneExprArg(&defined
);
1436 if (!defined
) fatal("sorry, DISPALIGN operand value must be known here");
1437 if (res
< 0 || res
> 65535) fatal("invalid DISPALIGN operand value: %d", res
);
1438 if (res
> 0 && disp
%res
!= 0) {
1447 ///////////////////////////////////////////////////////////////////////////////
1450 static int defIncr
= 0;
1453 static int piDEFINCR (void) {
1455 int32_t cnt
= getOneExprArg(&defined
);
1457 if (!defined
) fatal("DEFINCR: increment must be defined");
1463 static int piDEFBW (int isWord
) {
1469 char *res
= getStrArg(&len
);
1471 for (f
= 0; f
< len
; ++f
) {
1472 int32_t b
= (uint8_t)res
[f
];
1474 if (b
< -128 || b
> 255) fatal("invalid operand value: %d", b
);
1475 if (b
< 0) b
+= 256;
1479 int32_t res
= getExprArg(&defined
);
1481 if (pass
> 0 && !defined
) fatal("undefined operand");
1484 if (res
< -32768 || res
> 65535) fatal("invalid operand value: %d", res
);
1485 if (res
< 0) res
+= 65536;
1486 if (isWord
== 1) emitWord(res
); else emitRWord(res
);
1488 if (res
< -128 || res
> 255) fatal("invalid operand value: %d", res
);
1489 if (res
< 0) res
+= 256;
1493 if (!skipComma()) break;
1498 static int piDEFB (void) { return piDEFBW(0); }
1499 static int piDEFW (void) { return piDEFBW(1); }
1500 static int piDEFR (void) { return piDEFBW(2); }
1503 static int piDEFS (void) {
1507 int32_t res
= getExprArg(&defined
);
1509 if (pass
> 0 && !defined
) fatal("undefined operand");
1510 if (res
< 1 || res
> 65535) fatal("invalid number of repetitions: %d", res
);
1511 if (*curLine
&& curLine
[0] == ',') {
1513 bt
= getExprArg(&defined
);
1514 if (pass
> 0 && !defined
) fatal("undefined operand");
1516 if (bt
< -128 || bt
> 255) fatal("invalid operand value: %d", res
);
1517 if (bt
< 0) bt
+= 256;
1518 for (f
= 0; f
< res
; ++f
) emitByte(bt
);
1520 pc
+= res
; disp
+= res
;
1522 if (!skipComma()) break;
1528 /* bit 0: put '\0' */
1529 /* bit 1: set bit 7 of last byte */
1530 /* bit 2: put length */
1531 static int piDEFSTR (int type
) {
1535 char *res
= getStrArg(&len
);
1538 if (len
> 255) fatal("string too long");
1541 for (f
= 0; f
< len
; ++f
) {
1542 uint8_t b
= (uint8_t)res
[f
];
1544 if ((type
&0x02) && f
== len
-1) b
|= 0x80;
1547 if (type
&0x01) emitByte(0);
1550 int32_t v
= getExprArg(&defined
);
1552 if (pass
> 0 && !defined
) fatal("undefined expression");
1553 if (!defined
) v
= 0; else v
+= defIncr
;
1554 if (v
< -128 || v
> 255) fatal("invalid operand value: %d", v
);
1555 if (v
< 0) v
+= 256;
1558 if (!skipComma()) break;
1564 static int piDEFM (void) { return piDEFSTR(0x00); }
1565 static int piDEFZ (void) { return piDEFSTR(0x01); }
1566 static int piDEFX (void) { return piDEFSTR(0x02); }
1567 static int piDEFC (void) { return piDEFSTR(0x04); }
1570 ///////////////////////////////////////////////////////////////////////////////
1573 /* INCBIN "name"[,maxlen] */
1574 static int piINCBIN (void) {
1580 char *args
= curLine
;
1582 if (!curLine
[0]) fatal("INCBIN without file name");
1586 } else if (curLine
[0] == '<') {
1593 fn
= parseStr(&args
, qCh
, NULL
); // i don't care about string length here
1594 if (!fn
[0]) fatal("INCBIN: empty file name");
1595 memmove(curLine
, args
, strlen(args
)+1);
1597 if (curLine
[0] == ',') {
1601 maxlen
= getOneExprArg(&defined
);
1602 if (!defined
) fatal("INCBIN: undefined maxlen");
1603 if (maxlen
< 1) return 1; // nothing to do
1607 sprintf(curLine
, "%s/%s", sysIncludeDir
, fn
);
1609 sprintf(curLine
, "%s", fn
);
1612 fl
= fopen(curLine
, "rb");
1613 if (!fl
) fatal("INCBIN: file not found: %s", curLine
);
1614 while (maxlen
-- > 0) {
1615 int res
= fread(&bt
, 1, 1, fl
);
1618 if (res
!= 1) { fclose(fl
); fatal("INCBIN: error reading file: %s", curLine
); }
1626 ///////////////////////////////////////////////////////////////////////////////
1627 // MODULE, ENDMODULE
1629 static int piENDMODULE (void) {
1630 if (!curModule
) fatal("ENDMODULE without MODULE");
1632 char *mn
= getOneLabelArg();
1634 if (strcmp(mn
, curModule
->name
)) fatal("invalid module name in ENDMODULE: %s", mn
);
1641 static int piMODULE (void) {
1644 SourceLine
*ol
= curSrcLine
;
1647 if (curModule
) fatal("no nested modules allowed");
1648 mn
= getOneLabelArg();
1649 if (!urIsValidLabelName(mn
)) fatal("invalid module name: %s", mn
);
1650 mi
= moduleFind(mn
);
1651 //printf("[%s] %p\n", mn, mi);
1654 if (strcmp(mi
->fname
, curSrcLine
->fname
)) fatal("duplicate module definition; previous was in %s", mi
->fname
);
1656 nextSrcLine(); // skip ourself
1657 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MODULE", "ENDMODULE", NULL
))) {
1659 fatal("no ENDMODULE");
1661 if (inum
== 0) fatal("no nested modules allowed");
1668 mi
= moduleAdd(mn
, curSrcLine
->fname
);
1676 ///////////////////////////////////////////////////////////////////////////////
1679 static int piEDUP (void) {
1680 fatal("EDUP without DUP");
1686 static int piDUP (void) {
1687 int defined
= 1, dupCnt
= 1, inum
;
1688 SourceLine
*stline
, *eline
= NULL
;
1689 int32_t cnt
= getOneExprArg(&defined
);
1691 if (!defined
) fatal("DUP: counter must be defined");
1692 if (cnt
> 65535) fatal("DUP: counter too big: %d", cnt
);
1693 if (cnt
< 0) warningMsg("DUP: counter too small: %d", cnt
);
1694 // now find corresponding EDUP
1695 // note that we should skip nested DUPs
1696 nextSrcLine(); // skip ourself
1697 stline
= curSrcLine
;
1698 while (curSrcLine
) {
1699 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "DUP", "EDUP", NULL
))) break;
1700 // ok, we found something; what is it?
1704 nextSrcLine(); // skip DUP
1707 if (--dupCnt
== 0) {
1712 nextSrcLine(); // skip EDUP
1715 if (!eline
) { setCurSrcLine(stline
); fatal("no EDUP"); }
1716 // now repeat that lines
1718 setCurSrcLine(stline
);
1719 while (curSrcLine
!= eline
) processCurrentLine();
1725 ///////////////////////////////////////////////////////////////////////////////
1728 static int ifCount
= 0;
1731 static int ifSkipToEndIfOrElse (int wholeBody
) {
1732 int inum
, wasElse
= 0;
1735 while (curSrcLine
) {
1736 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL
))) break;
1740 nextSrcLine(); // skip IF
1741 ifSkipToEndIfOrElse(1); // and recurse
1742 nextSrcLine(); // skip ENDIF
1745 if (wasElse
) fatal("duplicate ELSE");
1746 if (!wholeBody
) return 1;
1748 nextSrcLine(); // skip ELSE
1749 break; // and continue
1751 return 0; // and exit
1754 if (wasElse
) fatal("ELSEIF in ELSE");
1755 if (!wholeBody
) return 2;
1756 nextSrcLine(); // skip ELSEIF
1757 break; // and continue
1759 // skip it as a whole
1760 nextSrcLine(); // skip MACRO
1763 if (!setCurSrcLine(findNextInstructionFromList(&inum
, "MACRO", "ENDM", NULL
))) { setCurSrcLine(oline
); fatal("invalid MACRO"); }
1764 if (inum
== 1) break;
1765 fatal("invalid nested MACRO");
1767 nextSrcLine(); // skip ENDM
1770 fatal("unexpected ENDM");
1773 fatal("IF without ENDIF");
1778 static int piENDIF (void) {
1779 if (--ifCount
< 0) fatal("ENDIF without IF");
1785 static int piELSE (void) {
1786 if (--ifCount
< 0) fatal("ELSE without IF");
1787 nextSrcLine(); // skip ELSE
1788 ifSkipToEndIfOrElse(1);
1793 static int piELSEIF (void) {
1794 if (--ifCount
< 0) fatal("ELSEIF without IF");
1795 nextSrcLine(); // skip ELSEIF
1796 ifSkipToEndIfOrElse(1);
1801 static int piIFAll (int isIfX
) {
1803 int ooo
= lblOptMakeU2
;
1804 lblOptMakeU2
= isIfX
? 1 : 0;
1805 int32_t cond
= getOneExprArg(&defined
);
1809 if (!isIfX
) fatal("IF: condition must be defined");
1810 cond
= 0; // for IFX: 0 is there is any undefined label
1813 // ok, do it until ELSE/ELSEIF/ENDIF
1821 nextSrcLine(); // skip last instruction
1822 // skip until ELSE/ELSEIF/ENDIF
1823 r
= ifSkipToEndIfOrElse(0);
1824 if (r
== 0) break; // ENDIF
1825 if (r
== 1) { ++ifCount
; break; } // ELSE
1826 // ELSEIF, do condition
1827 if ((args
= strIsCommand("ELSEIF", curLine
)) == NULL
) fatal("internal error in conditionals"); // the thing that should not be
1828 memmove(curLine
, args
, strlen(args
)+1);
1829 cond
= getOneExprArg(&defined
);
1830 if (!defined
) fatal("ELSEIF: condition must be defined");
1831 if (cond
) { ++ifCount
; break; } // condition is true
1837 static int piIF (void) { return piIFAll(0); }
1838 static int piIFX (void) { return piIFAll(1); }
1841 ///////////////////////////////////////////////////////////////////////////////
1844 * what i did with MACRO is the brain-damaged cheating all the way.
1845 * first, i will collect the MACRO body and remember it.
1846 * second, when the macro is used, i will:
1847 * * create unique labels for all supplied macro args, each with
1848 * number (so IFARG/IFNARG can check the existance)
1849 * * insert the whole macro body in place, fixing argument refs
1850 * * let the asm play with it
1851 * another tricky part is 'local labels': i have to change all
1852 * '..lbl' references -- generate new label name for it and
1853 * replace all refs. and be careful to not touch the strings.
1854 * this is not the best scheme, but it is fairly simple and it wokrs.
1856 static int piMACRO (void) {
1857 fatal("sorry, no MACRO yet");
1862 static int piENDM (void) {
1863 fatal("ENDM withoud MACRO");
1868 ///////////////////////////////////////////////////////////////////////////////
1870 static int optWriteType
= 't';
1871 static int optWTChanged
= 0;
1874 static void piTapParseLoaderName (void) {
1879 if (!isStrArg()) fatal("loader name expected");
1880 fn
= getStrArg(&len
);
1881 if (len
> 10) fatal("loader name too long");
1882 memset(tapeLoaderName
, ' ', 10);
1883 for (int f
= 0; f
< len
; ++f
) tapeLoaderName
[f
] = fn
[f
];
1888 static int piDEFFMT (void) {
1894 name
= getStrArg(&len
);
1896 name
= getLabelArg();
1898 if (optWTChanged
) return 1;
1899 if (!strcasecmp(name
, "SNA") || !strcasecmp(name
, "RUNSNA")) {
1901 //optRunSNA = (toupper(name[0]) == 'R');
1903 if (curLine
[0]) fatal("too many expressions");
1906 if (!strcasecmp(name
, "TAP") || !strcasecmp(name
, "TAPE")) {
1910 piTapParseLoaderName();
1913 if (!strcasecmp(name
, "RUNTAP") || !strcasecmp(name
, "RUNTAPE")) {
1917 piTapParseLoaderName();
1920 if (!strcasecmp(name
, "BIN") || !strcasecmp(name
, "RAW")) {
1924 if (curLine
[0]) fatal("too many expressions");
1927 if (!strcasecmp(name
, "DMB") || !strcasecmp(name
, "RUNDMB")) {
1930 optRunDMB
= (toupper(name
[0]) == 'R');
1932 if (curLine
[0]) fatal("too many expressions");
1935 if (!strcasecmp(name
, "NONE") || !strcasecmp(name
, "NOTHING")) {
1939 if (curLine
[0]) fatal("too many expressions");
1942 fatal("invalid default output type: %s", name
);
1946 ///////////////////////////////////////////////////////////////////////////////
1949 static void processCurrentLine (void) {
1950 if (!curSrcLine
) return; // do nothing
1954 char *str
, *ee
, name
[66];
1959 removeSpacesAndColons();
1960 // skip spaces and ':'
1961 if (!curLine
[0]) { nextSrcLine(); break; }
1962 // try to find and process command
1963 str
= curLine
; //while (*str && isspace(*str)) ++str; // skip spaces
1965 for (ee
= str
; *ee
&& !isspace(*ee
) && *ee
!= '"' && *ee
!= '\''; ++ee
) ;
1966 // get command, if any
1967 if (ee
!= str
&& ee
-str
<= 64) {
1968 memset(name
, 0, sizeof(name
));
1969 memmove(name
, str
, ee
-str
);
1971 op
= urFindOp(name
);
1974 str
= ee
; while (*str
&& isspace(*str
)) ++str
; // skip spaces
1975 memmove(curLine
, str
, strlen(str
)+1);
1977 nextSrcLine(); // skip it
1983 len
= urAssembleOne(curLine
, pc
, disp
, &errpos
);
1984 if (len
< 0) fatalUrLib(len
);
1985 pc
+= len
; disp
+= len
;
1986 if (len
>= 0 && errpos
) {
1987 memmove(curLine
, errpos
+1, strlen(errpos
));
1989 nextSrcLine(); // skip it
1996 ///////////////////////////////////////////////////////////////////////////////
1997 // setup instructions
1999 static void registerInstructions (void) {
2000 urAddOp("DISPLAY", piDISPLAY
);
2001 urAddOp("DISPLAY0", piDISPLAY0
);
2002 urAddOp("DISPLAYA", piDISPLAYA
);
2003 urAddOp("DISPHEX", piDISPHEX
);
2004 urAddOp("DISPHEX0", piDISPHEX0
);
2005 urAddOp("DISPHEXA", piDISPHEXA
);
2007 urAddOp("DEFFMT", piDEFFMT
);
2009 urAddOp("MACRO", piMACRO
);
2010 urAddOp("ENDM", piENDM
);
2012 urAddOp("ORG", piORG
);
2013 urAddOp("DISP", piDISP
);
2014 urAddOp("ENDDISP", piENDDISP
);
2015 urAddOp("PHASE", piDISP
);
2016 urAddOp("DEPHASE", piENDDISP
);
2017 urAddOp("UNPHASE", piENDDISP
);
2018 urAddOp("ALIGN", piALIGN
);
2019 urAddOp("DISPALIGN", piDISPALIGN
);
2020 urAddOp("PHASEALIGN", piDISPALIGN
);
2021 urAddOp("ENT", piENT
);
2022 urAddOp("CLR", piCLR
);
2023 urAddOp("RESERVE", piRESERVE
);
2025 urAddOp("INCBIN", piINCBIN
);
2027 urAddOp("MODULE", piMODULE
);
2028 urAddOp("ENDMODULE", piENDMODULE
);
2030 urAddOp("DUP", piDUP
);
2031 urAddOp("EDUP", piEDUP
);
2033 urAddOp("IF", piIF
);
2034 urAddOp("IFX", piIFX
);
2035 urAddOp("ELSE", piELSE
);
2036 urAddOp("ELSEIF", piELSEIF
);
2037 urAddOp("ELSEIFX", piELSEIF
);
2038 urAddOp("ENDIF", piENDIF
);
2040 urAddOp("DEFINCR", piDEFINCR
);
2041 urAddOp("DEFB", piDEFB
); urAddOp("DB", piDEFB
);
2042 urAddOp("DEFW", piDEFW
); urAddOp("DW", piDEFW
);
2043 urAddOp("DEFR", piDEFR
); urAddOp("DR", piDEFR
);
2044 urAddOp("DEFS", piDEFS
); urAddOp("DS", piDEFS
);
2045 urAddOp("DEFM", piDEFM
); urAddOp("DM", piDEFM
);
2046 urAddOp("DEFZ", piDEFZ
); urAddOp("DZ", piDEFZ
);
2047 urAddOp("DEFX", piDEFX
); urAddOp("DX", piDEFX
);
2048 urAddOp("DEFC", piDEFC
); urAddOp("DC", piDEFC
);
2052 ///////////////////////////////////////////////////////////////////////////////
2053 // !0: invalid label
2055 static inline void fnSkipSpaces (const char *expr) {
2056 while (*expr && isspace(*expr)) ++expr;
2062 static inline char fnNextChar (const char *expr
) {
2063 while (*expr
&& isspace(*expr
)) ++expr
;
2068 #define FN_CHECK_END do { \
2069 while (*expr && isspace(*expr)) ++expr; \
2070 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
2075 #define FN_CHECK_COMMA do { \
2076 while (*expr && isspace(*expr)) ++expr; \
2077 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
2082 static const char *readLabelName (char *buf
, const char *expr
) {
2085 while (*expr
&& isspace(*expr
)) ++expr
;
2089 if (pos
>= 128) return NULL
;
2091 if (isalnum(ch
) || ch
== '$' || ch
== '.' || ch
== '_' || ch
== '@') {
2097 if (pos
< 1) return NULL
;
2099 if (!urIsValidLabelName(buf
)) return NULL
;
2104 static const char *fnDefKn (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
, int qtype
) {
2107 if ((expr
= readLabelName(lbl
, expr
)) == NULL
) { *error
= UR_EXPRERR_LABEL
; return NULL
; }
2109 if (!donteval
) res
->val
= (isLabelDefinedOrKnown(lbl
, addr
, qtype
) != 0);
2113 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
); }
2114 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
); }
2117 static const char *fnAligned256 (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2118 expr
= urExpressionEx(res
, expr
, addr
, &donteval
, defined
, error
);
2120 if (!donteval
) res
->val
= (res
->val
%256 ? 0 : 1);
2125 static const char *fnSameSeg (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2128 urInitExprValue(&v0
);
2129 urInitExprValue(&v1
);
2130 expr
= urExpressionEx(&v0
, expr
, addr
, &donteval
, defined
, error
);
2132 expr
= urExpressionEx(&v1
, expr
, addr
, &donteval
, defined
, error
);
2134 if (!donteval
) res
->val
= (v0
.val
/256 == v1
.val
/256);
2139 static const char *fnAlign (URExprValue
*res
, const char *expr
, uint16_t addr
, int donteval
, int *defined
, int *error
) {
2142 urInitExprValue(&v0
);
2143 urInitExprValue(&v1
);
2144 expr
= urExpressionEx(&v0
, expr
, addr
, &donteval
, defined
, error
);
2145 if (fnNextChar(expr
) == ',') {
2147 expr
= urExpressionEx(&v1
, expr
, addr
, &donteval
, defined
, error
);
2153 if (v1
.val
< 1) { *error
= UR_EXPRERR_FUNC
; return NULL
; }
2154 res
->val
= (v0
.val
+v1
.val
-1)/v1
.val
*v1
.val
;
2160 static void registerFunctions (void) {
2161 urExpressionRegisterFunction("defined", fnDefined
);
2162 urExpressionRegisterFunction("known", fnKnown
);
2163 urExpressionRegisterFunction("aligned256", fnAligned256
);
2164 urExpressionRegisterFunction("align", fnAlign
);
2165 urExpressionRegisterFunction("sameseg", fnSameSeg
);
2169 ///////////////////////////////////////////////////////////////////////////////
2170 // preparing another pass
2172 static void initPass (void) {
2173 curSrcLine
= asmText
;
2175 pc
= disp
= ent
= 0x100; // viva CP/M!
2183 lastSeenGlobalLabel
= strdup(" [MAIN] ");
2189 static int posstPass (void) {
2190 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
); lastSeenGlobalLabel
= NULL
;
2191 if (checkLabels()) return -1;
2192 if (ifCount
!= 0) fatal("unbalanced IFs");
2197 ///////////////////////////////////////////////////////////////////////////////
2200 static struct option longOpts
[] = {
2201 {"sna", 0, NULL
, 's'},
2202 {"sna128", 0, NULL
, 'S'},
2203 {"tap", 0, NULL
, 't'},
2204 {"autotap", 0, NULL
, 'T'},
2205 /*{"rawtap", 0, NULL, 'T'},*/
2206 {"raw", 0, NULL
, 'r'},
2207 {"autodmb", 0, NULL
, 'B'},
2208 {"dmb", 0, NULL
, 'b'},
2209 {"none", 0, NULL
, 'n'},
2210 {"help", 0, NULL
, 'h'},
2215 static const char *defInFiles
[] = {"main.zas", "main.a80", "main.asm", NULL
};
2218 static void usage (const char *pname
) {
2220 "usage: %s [options] infile\n"
2221 "default infiles:", pname
);
2222 for (int f
= 0; defInFiles
[f
]; ++f
) printf(" %s", defInFiles
[f
]);
2225 " -s --sna write 48K .SNA file with autostart\n"
2226 " -S --sna128 write 148K .SNA file with autostart\n"
2227 " -t --tap write .tap file\n"
2228 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
2229 " -r --raw write raw file(s)\n"
2230 " -b --dmb write DMB file\n"
2231 " -B --autodmb write DMB file with autostart\n"
2232 " -n --none write nothing\n"
2233 " -h --help this help\n");
2237 ///////////////////////////////////////////////////////////////////////////////
2240 int main (int argc
, char *argv
[]) {
2242 const char *pname
= argv
[0];
2243 char *inFile
= NULL
;
2246 urGetByteFn
= getByte
;
2247 urPutByteFn
= putByte
;
2248 urFindLabelByNameFn
= findLabelCB
;
2249 //urIsLabelDefinedOrKnownFn = isLabelDefinedOrKnown;
2251 //strcpy(tapeLoaderName, "cargador ");
2252 tapeLoaderName
[0] = 0;
2254 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI
, VERSION_MID
, VERSION_LO
, __DATE__
, __TIME__
);
2255 while ((c
= getopt_long(argc
, argv
, "sStTbBrnh", longOpts
, NULL
)) >= 0) {
2257 case 'S': /*optRunSNA = 1;*/ optSNA48
= 0; optWriteType
= 's'; optWTChanged
= 1; break;
2258 case 's': /*optRunSNA = 0;*/ optSNA48
= 1; optWriteType
= 's'; optWTChanged
= 1; break;
2259 case 'T': optRunTape
= 1; optWriteType
= 't'; optWTChanged
= 1; break;
2260 case 't': optRunTape
= 0; optWriteType
= 't'; optWTChanged
= 1; break;
2261 case 'b': optRunTape
= 0; optRunDMB
= 0; optWriteType
= 'd'; optWTChanged
= 1; break;
2262 case 'B': optRunTape
= 0; optRunDMB
= 1; optWriteType
= 'd'; optWTChanged
= 1; break;
2263 case 'r': case 'n': optWriteType
= c
; optWTChanged
= 1; break;
2264 case 'h': usage(pname
); res
= 0; goto earlyerrquit
;
2268 if (optind
>= argc
) {
2269 // try to find default input file
2271 for (f
= 0; defInFiles
[f
]; ++f
) {
2272 if (!access(defInFiles
[f
], R_OK
)) {
2273 inFile
= strdup(defInFiles
[f
]);
2278 inFile
= strdup(argv
[optind
]);
2280 if (!inFile
|| !inFile
[0]) {
2282 fprintf(stderr
, "ERROR: no input file!\n");
2286 registerInstructions();
2287 registerFunctions();
2289 res
= asmTextLoad(inFile
);
2292 printf("dumping...\n");
2293 FILE *fo = fopen("z000.out", "w");
2296 for (c = asmText; c; c = c->next) fprintf(fo, "%s ; %s: %d\n", c->line, c->fname, c->lineNo);
2300 for (pass
= 0; pass
<= 1; ++pass
) {
2302 printf("pass %d\n", pass
);
2303 setCurSrcLine(asmText
);
2304 if (setjmp(errJP
)) { res
= 1; break; }
2305 while (curSrcLine
) processCurrentLine();
2306 if (posstPass()) { res
= 1; break; }
2310 char *oc
= strdup(inFile
);
2311 char *pd
= strrchr(oc
, '.');
2312 if (pd
&& !strchr(oc
, '/')) *pd
= '\0';
2313 switch (optWriteType
) {
2314 case 's': saveSna(oc
, optSNA48
); break;
2315 case 't': saveTap(oc
); break;
2316 case 'r': saveRaw(oc
); break;
2317 case 'd': saveDMB(oc
); break;
2322 fprintf(stderr
, "ERROR: loading error!\n");
2325 if (lastSeenGlobalLabel
) free(lastSeenGlobalLabel
);
2331 if (inFile
) free(inFile
);
2332 if (sysIncludeDir
) free(sysIncludeDir
);