mirror of
https://github.com/bobbimanners/Zapple-II.git
synced 2024-09-28 17:55:16 +00:00
499 lines
9.3 KiB
C
499 lines
9.3 KiB
C
/*
|
|
* Z80 - Assembler
|
|
* Copyright (C) 1987-1992 by Udo Munk
|
|
*
|
|
* History:
|
|
* 17-SEP-1987 Development under Digital Research CP/M 2.2
|
|
* 28-JUN-1988 Switched to Unix System V.3
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include "z80a.h"
|
|
#include "z80aglb.h"
|
|
|
|
static char *errmsg[] = { /* Fehlermeldungen fuer fatal() */
|
|
"out of memory: %s", /* 0 */
|
|
"usage: z80asm -ofile -f[b|m|h] -l[file] -s[n|a] -v -dsymbol ... file ...",
|
|
"Assembly halted", /* 2 */
|
|
"can't open file %s", /* 3 */
|
|
"internal error: %s" /* 4 */
|
|
};
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
int len;
|
|
|
|
init();
|
|
options(argc, argv);
|
|
printf("Z80 - Assembler Release %s, %s\n", REL, COPYR);
|
|
pass1();
|
|
pass2();
|
|
if (list_flag) {
|
|
switch (sym_flag) {
|
|
case 0: /* keine Symboltabelle */
|
|
break;
|
|
case 1: /* unsortierte Symboltabelle */
|
|
lst_sym();
|
|
break;
|
|
case 2: /* nach Namen sortierte Symboltabelle */
|
|
len = copy_sym();
|
|
n_sort_sym(len);
|
|
lst_sort_sym(len);
|
|
break;
|
|
case 3: /* nach Adressen sortierte Symboltabelle */
|
|
len = copy_sym();
|
|
a_sort_sym(len);
|
|
lst_sort_sym(len);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
fclose(lstfp);
|
|
}
|
|
return(errors);
|
|
}
|
|
|
|
/*
|
|
* Initialisierung
|
|
*/
|
|
init()
|
|
{
|
|
errfp = stdout;
|
|
}
|
|
|
|
/*
|
|
* Diese Funktion bearbeitet die beim Aufruf angegebenen Options.
|
|
* Die uebergebenen Dateinamen werden in die entsprechenden
|
|
* Strings uebernommen.
|
|
*/
|
|
options(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
register char *s, *t;
|
|
register int i;
|
|
char *malloc();
|
|
|
|
while (--argc > 0 && (*++argv)[0] == '-')
|
|
for (s = argv[0]+1; *s != '\0'; s++)
|
|
switch (*s) {
|
|
case 'o':
|
|
case 'O':
|
|
if (*++s == '\0') {
|
|
puts("name missing in option -o");
|
|
usage();
|
|
}
|
|
get_fn(objfn, s, OBJEXT);
|
|
s += (strlen(s) - 1);
|
|
break;
|
|
case 'l':
|
|
case 'L':
|
|
if (*(s + 1) != '\0') {
|
|
get_fn(lstfn, ++s, LSTEXT);
|
|
s += (strlen(s) - 1);
|
|
}
|
|
list_flag = 1;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
if (*(s + 1) == '\0')
|
|
sym_flag = 1;
|
|
else if ((*(s + 1) == 'n') || (*(s + 1) == 'N'))
|
|
sym_flag = 2;
|
|
else if ((*(s + 1) == 'a') || (*(s + 1) == 'A'))
|
|
sym_flag = 3;
|
|
else {
|
|
printf("unknown option -%s\n", s);
|
|
usage();
|
|
}
|
|
s += (strlen(s) - 1);
|
|
break;
|
|
case 'f':
|
|
case 'F':
|
|
if ((*(s + 1) == 'b') || (*(s + 1) == 'B'))
|
|
out_form = OUTBIN;
|
|
else if ((*(s + 1) == 'm') || (*(s + 1) == 'M'))
|
|
out_form = OUTMOS;
|
|
else if ((*(s + 1) == 'h') || (*(s + 1) == 'H'))
|
|
out_form = OUTHEX;
|
|
else {
|
|
printf("unknown option -%s\n", s);
|
|
usage();
|
|
}
|
|
s += (strlen(s) - 1);
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
if (*++s == '\0') {
|
|
puts("name missing in option -d");
|
|
usage();
|
|
}
|
|
t = tmp;
|
|
while (*s)
|
|
*t++ = islower(*s) ? toupper(*s++) : *s++;
|
|
s--;
|
|
*t = '\0';
|
|
if (put_sym(tmp, 0))
|
|
fatal(F_OUTMEM, "symbols");
|
|
break;
|
|
case 'v':
|
|
case 'V':
|
|
ver_flag = 1;
|
|
break;
|
|
default :
|
|
printf("unknown option %c\n", *s);
|
|
usage();
|
|
}
|
|
i = 0;
|
|
while ((argc--) && (i < MAXFN)) {
|
|
if ((infiles[i] = malloc(LENFN + 1)) == NULL)
|
|
fatal(F_OUTMEM, "filenames");
|
|
get_fn(infiles[i], *argv++, SRCEXT);
|
|
i++;
|
|
}
|
|
if (i == 0) {
|
|
printf("no input file given\n");
|
|
usage();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* An den Argumenten in der Befehlszeile ist was falsch,
|
|
* Gebrauchsanleitung ausgeben und abbrechen.
|
|
*/
|
|
usage()
|
|
{
|
|
fatal(F_USAGE, NULL);
|
|
}
|
|
|
|
/*
|
|
* Fehlermeldung ausgeben und abbrechen
|
|
*/
|
|
fatal(i, arg)
|
|
register int i;
|
|
register char *arg;
|
|
{
|
|
void exit();
|
|
|
|
printf(errmsg[i], arg);
|
|
putchar('\n');
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Pass 1:
|
|
* - Lauf ueber alle Quelldateien
|
|
*/
|
|
pass1()
|
|
{
|
|
register int fi;
|
|
|
|
pass = 1;
|
|
pc = 0;
|
|
fi = 0;
|
|
if (!ver_flag)
|
|
puts("Pass 1");
|
|
open_o_files(infiles[fi]);
|
|
while (infiles[fi] != NULL) {
|
|
if (!ver_flag)
|
|
printf(" Read %s\n", infiles[fi]);
|
|
p1_file(infiles[fi]);
|
|
fi++;
|
|
}
|
|
if (errors) {
|
|
fclose(objfp);
|
|
unlink(objfn);
|
|
printf("%d error(s)\n", errors);
|
|
fatal(F_HALT, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Pass 1:
|
|
* - Lauf ueber eine Quelldatei
|
|
*
|
|
* Input: Name der zu bearbeitenden Quelldatei
|
|
*/
|
|
p1_file(fn)
|
|
char *fn;
|
|
{
|
|
c_line = 0;
|
|
srcfn = fn;
|
|
if ((srcfp = fopen(fn, READA)) == NULL)
|
|
fatal(F_FOPEN, fn);
|
|
while (p1_line())
|
|
;
|
|
fclose(srcfp);
|
|
if (iflevel)
|
|
asmerr(E_MISEIF);
|
|
}
|
|
|
|
/*
|
|
* Pass 1:
|
|
* - Eine Zeile Quelle verarbeiten
|
|
*
|
|
* Output: 1 Zeile verarbeitet
|
|
* 0 EOF erreicht
|
|
*/
|
|
p1_line()
|
|
{
|
|
register char *p;
|
|
register int i;
|
|
register struct opc *op;
|
|
char *get_label(), *get_opcode(), *get_arg();
|
|
struct opc *search_op();
|
|
|
|
if ((p = fgets(line, MAXLINE, srcfp)) == NULL)
|
|
return(0);
|
|
c_line++;
|
|
p = get_label(label, p);
|
|
p = get_opcode(opcode, p);
|
|
p = get_arg(operand, p);
|
|
if (strcmp(opcode, ENDFILE) == 0)
|
|
return(0);
|
|
if (*opcode) {
|
|
if ((op = search_op(opcode)) != NULL) {
|
|
i = (*op->op_fun)(op->op_c1, op->op_c2);
|
|
if (gencode)
|
|
pc += i;
|
|
} else
|
|
asmerr(E_ILLOPC);
|
|
} else
|
|
if (*label)
|
|
put_label();
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* Pass 2:
|
|
* - Lauf ueber alle Quelldateien
|
|
*/
|
|
pass2()
|
|
{
|
|
register int fi;
|
|
|
|
pass = 2;
|
|
pc = 0;
|
|
fi = 0;
|
|
if (!ver_flag)
|
|
puts("Pass 2");
|
|
obj_header();
|
|
while (infiles[fi] != NULL) {
|
|
if (!ver_flag)
|
|
printf(" Read %s\n", infiles[fi]);
|
|
p2_file(infiles[fi]);
|
|
fi++;
|
|
}
|
|
obj_end();
|
|
fclose(objfp);
|
|
printf("%d error(s)\n", errors);
|
|
}
|
|
|
|
/*
|
|
* Pass 2:
|
|
* - Lauf ueber eine Quelldatei
|
|
*
|
|
* Input: Name der zu bearbeitenden Quelldatei
|
|
*/
|
|
p2_file(fn)
|
|
char *fn;
|
|
{
|
|
c_line = 0;
|
|
srcfn = fn;
|
|
if ((srcfp = fopen(fn, READA)) == NULL)
|
|
fatal(F_FOPEN, fn);
|
|
while (p2_line())
|
|
;
|
|
fclose(srcfp);
|
|
}
|
|
|
|
/*
|
|
* Pass 2:
|
|
* - Eine Zeile Quelle verarbeiten
|
|
*
|
|
* Output: 1 Zeile verarbeitet
|
|
* 0 EOF erreicht
|
|
*/
|
|
p2_line()
|
|
{
|
|
register char *p;
|
|
register int op_count;
|
|
register struct opc *op;
|
|
char *get_label(), *get_opcode(), *get_arg();
|
|
struct opc *search_op();
|
|
|
|
if ((p = fgets(line, MAXLINE, srcfp)) == NULL)
|
|
return(0);
|
|
c_line++;
|
|
s_line++;
|
|
p = get_label(label, p);
|
|
p = get_opcode(opcode, p);
|
|
p = get_arg(operand, p);
|
|
if (strcmp(opcode, ENDFILE) == 0) {
|
|
lst_line(pc, 0);
|
|
return(0);
|
|
}
|
|
if (*opcode) {
|
|
op = search_op(opcode);
|
|
op_count = (*op->op_fun)(op->op_c1, op->op_c2);
|
|
if (gencode) {
|
|
lst_line(pc, op_count);
|
|
obj_writeb(op_count);
|
|
pc += op_count;
|
|
} else {
|
|
sd_flag = 2;
|
|
lst_line(0, 0);
|
|
}
|
|
} else {
|
|
sd_flag = 2;
|
|
lst_line(0, 0);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* Oeffnen der Ausgabedateien: Objectdatei und bei Option
|
|
* -l der Listdatei. Der Dateiname der Quelldatei wird
|
|
* uebergeben. Die Dateinamen der Object- und Listdatei
|
|
* werden, wenn nicht hinter den Optionen -l und -o angegeben,
|
|
* aus dem Quelldateinamen erzeugt.
|
|
*/
|
|
open_o_files(source)
|
|
register char *source;
|
|
{
|
|
char *strcpy(), *strcat(), *strrchr();
|
|
register char *p;
|
|
|
|
if (*objfn == '\0')
|
|
strcpy(objfn, source);
|
|
if ((p = strrchr(objfn, '.')) != NULL)
|
|
strcpy(p, OBJEXT);
|
|
else
|
|
strcat(objfn, OBJEXT);
|
|
|
|
if (out_form == OUTHEX)
|
|
objfp = fopen(objfn, WRITEA);
|
|
else
|
|
objfp = fopen(objfn, WRITEB);
|
|
if (objfp == NULL)
|
|
fatal(F_FOPEN, objfn);
|
|
if (list_flag) {
|
|
if (*lstfn == '\0')
|
|
strcpy(lstfn, source);
|
|
if ((p = strrchr(lstfn, '.')) != NULL)
|
|
strcpy(p, LSTEXT);
|
|
else
|
|
strcat(lstfn, LSTEXT);
|
|
if ((lstfp = fopen(lstfn, WRITEA)) == NULL)
|
|
fatal(F_FOPEN, lstfn);
|
|
errfp = lstfp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Einen Dateinamen in "dest" aus "src" und "ext" zusammenbauen
|
|
*/
|
|
get_fn(dest, src, ext)
|
|
char *dest, *src, *ext;
|
|
{
|
|
char *strrchr(), *strcat();
|
|
register int i;
|
|
register char *sp, *dp;
|
|
|
|
i = 0;
|
|
sp = src;
|
|
dp = dest;
|
|
while ((i++ < LENFN) && (*sp != '\0'))
|
|
*dp++ = *sp++;
|
|
*dp = '\0';
|
|
if ((strrchr(dest,'.') == NULL) && (strlen(dest) <= (LENFN-strlen(ext))))
|
|
strcat(dest, ext);
|
|
}
|
|
|
|
/*
|
|
* Extrahieren der Labels, Konstanten und Variablen aus
|
|
* einer Zeile Quelltext mit Umwandlung in Grosschrift
|
|
* und Begrenzung der Laenge.
|
|
*/
|
|
char *get_label(s, l)
|
|
register char *s, *l;
|
|
{
|
|
register int i;
|
|
|
|
i = 0;
|
|
if (*l == LINCOM)
|
|
goto comment;
|
|
while (!isspace(*l) && *l != COMMENT && *l != LABSEP && i < SYMSIZE) {
|
|
*s++ = islower(*l) ? toupper(*l++) : *l++;
|
|
i++;
|
|
}
|
|
comment:
|
|
*s = '\0';
|
|
return(l);
|
|
}
|
|
|
|
/*
|
|
* Extrahieren des Op-Codes aus einer Zeile Quelltext ab der
|
|
* uebergebenen Position. Der String wird bei der Uebertragung
|
|
* in Grosschrift umgewandelt.
|
|
*/
|
|
char *get_opcode(s, l)
|
|
register char *s, *l;
|
|
{
|
|
if (*l == LINCOM)
|
|
goto comment;
|
|
while (!isspace(*l) && *l != COMMENT && *l != LABSEP)
|
|
l++;
|
|
if (*l == LABSEP)
|
|
l++;
|
|
while (*l == ' ' || *l == '\t')
|
|
l++;
|
|
while (!isspace(*l) && *l != COMMENT)
|
|
*s++ = islower(*l) ? toupper(*l++) : *l++;
|
|
comment:
|
|
*s = '\0';
|
|
return(l);
|
|
}
|
|
|
|
/*
|
|
* Extrahieren des Operanden aus einer Zeile Quelltext ab der
|
|
* uebergebenen Position. Der String wird bei der Uebertragung
|
|
* in Grosschrift umgewandelt und Blanks sowie Tabs werden
|
|
* ueberlesen. Strings, die in ' eingeschlossen sind, werden
|
|
* ohne Aenderung kopiert.
|
|
*/
|
|
char *get_arg(s, l)
|
|
register char *s, *l;
|
|
{
|
|
if (*l == LINCOM)
|
|
goto comment;
|
|
while (*l == ' ' || *l == '\t')
|
|
l++;
|
|
while (*l != '\n' && *l != COMMENT) {
|
|
if (isspace(*l)) {
|
|
l++;
|
|
continue;
|
|
}
|
|
if (*l != STRSEP) {
|
|
*s++ = islower(*l) ? toupper(*l) : *l;
|
|
l++;
|
|
continue;
|
|
}
|
|
*s++ = *l++;
|
|
if (*(s - 2) == 'F') /* EX AF,AF' !!!!! */
|
|
continue;
|
|
while (*l != STRSEP) {
|
|
if (*l == '\n' || *l == '\0' || *l == COMMENT)
|
|
goto comment;
|
|
*s++ = *l++;
|
|
}
|
|
*s++ = *l++;
|
|
}
|
|
comment:
|
|
*s = '\0';
|
|
return(l);
|
|
}
|