implement [b], [w], .not, <, >, <=, >=, ==, !=

This commit is contained in:
Bernd Boeckmann
2023-04-18 13:09:24 +02:00
parent 4c1d9f44e5
commit f56f7ccaa3
7 changed files with 169 additions and 42 deletions

View File

@@ -1,6 +1,5 @@
MIT License
Copyright (c) 2022 Bernd Böckmann
Copyright (c) 2022-2023 Bernd Boeckmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

118
asm6502.c
View File

@@ -1,6 +1,6 @@
/* ASM6502
*
* Copyright (c) 2022-2023 Bernd Böckmann
* Copyright (c) 2022-2023 Bernd Boeckmann
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -329,19 +329,19 @@ static char sym_kind_to_char(u8 kind)
return '-';
}
#if 0
static char sym_type_to_char(u8 typ)
{
if ((typ & 0x3f) == 0) return 'U';
if ((typ & 0x3f) == 0) return '?';
switch (typ & 0x3f) {
case TYPE_BYTE:
return 'B';
return 'b';
case TYPE_WORD:
return 'W';
return 'w';
}
return '?';
}
#if 0
static void dump_symbols(void)
{
symbol *sym = symbols;
@@ -468,6 +468,14 @@ static void skip_to_eol(char **p)
while (**p != 0x0a && **p != 0x0d) (*p)++;
}
static int starts_with(char *text, char *s)
{
while (*s) {
if (toupper(*text++) != toupper(*s++)) return 0;
}
return 1;
}
static value number(char **p)
{
value num= {0,0};
@@ -636,8 +644,12 @@ static value product(char **p)
skip_white(p);
op = **p;
while((op == '*') || (op == '/') || (op == AND_LETTER)) {
while((op == '*') || (op == '/') || (op == AND_LETTER)
|| (**p == '<' && *(*p+1) == '<')
|| (**p == '>' && *(*p+1) == '>')) {
(*p)++;
if (**p == '<' || **p == '>') (*p)++;
n2 = primary(p);
switch (op) {
@@ -647,6 +659,10 @@ static value product(char **p)
res.v = (u16)(res.v / n2.v); break;
case AND_LETTER:
res.v = (u16)(res.v & n2.v); break;
case '<':
res.v = (u16)(res.v << n2.v); break;
case '>':
res.v = (u16)(res.v >> n2.v); break;
}
INFERE_TYPE(res, n2);
@@ -706,6 +722,41 @@ static value term(char **p)
return res;
}
static value comparison(char **p)
{
value res, n2;
char op, op2;
res = term(p);
skip_white(p);
while ((**p == '=' && *(*p+1) == '=') ||
(**p == '!' && *(*p+1) == '=') ||
(**p == '<' && *(*p+1) == '=') ||
(**p == '>' && *(*p+1) == '=') ||
(**p == '<') || (**p == '>')) {
op = **p;
op2 = *(*p + 1);
*p += 1;
if (**p == '=') *p += 1;
n2 = term(p);
switch (op) {
case '=': res.v = res.v == n2.v; break;
case '!': res.v = res.v != n2.v; break;
case '<': res.v = (op2 == '=') ? res.v <= n2.v : res.v < n2.v; break;
case '>': res.v = (op2 == '=') ? res.v >= n2.v : res.v > n2.v; break;
}
INFERE_DEFINED(res, n2);
if (DEFINED(res) && res.v) res.v = 1;
SET_TYPE(res, TYPE_BYTE);
}
return res;
}
static value expr(char **p)
{
value v;
@@ -713,22 +764,39 @@ static value expr(char **p)
skip_white(p);
if (**p == '>') {
(*p)++;
v = term(p);
v = comparison(p);
SET_TYPE(v, TYPE_BYTE);
v.v = v.v >> 8;
}
else if (**p == '<') {
(*p)++;
v = term(p);
v = comparison(p);
SET_TYPE(v, TYPE_BYTE);
v.v = v.v & 0xff;
}
else if (**p == '!') {
(*p)++;
v = term(p);
else if (starts_with(*p, "[b]")) {
/* lossless byte conversion */
*p += 3;
v = comparison(p);
if (DEFINED(v) && v.v > 0xff)
error(ERR_BYTERNG);
SET_TYPE(v, TYPE_BYTE);
}
else if (starts_with(*p, "[w]")) {
/* lossless word conversion */
*p += 3;
v = comparison(p);
SET_TYPE(v, TYPE_WORD);
}
else v = term(p);
else if (starts_with(*p, ".not")) {
*p += 4;
v = comparison(p);
if (DEFINED(v)) {
v.v = (v.v) ? 0 : 1;
}
SET_TYPE(v, TYPE_BYTE);
}
else v = comparison(p);
return v;
}
@@ -1561,7 +1629,7 @@ static void list_statement(char *statement_start, unsigned short pc_start,
}
fprintf(list_file, "%6d", line);
if (skipped)
fprintf(list_file, "! ");
fprintf(list_file, "- ");
else
fprintf(list_file, ": ");
fwrite(statement_start, 1, (int)(p - statement_start), list_file);
@@ -1606,25 +1674,27 @@ static void list_symbols(void)
sym_p = sym_array;
if (i == 1) {
fprintf(list_file, "\n\n<<< SYMBOLS BY NAME >>>\n\n");
fprintf(list_file, "\n\n-- SYMBOLS BY NAME -------------------\n\n");
qsort(sym_array, symbol_count, sizeof(symbol *), sym_cmp_name);
}
else {
fprintf(list_file, "\n\n<<< SYMBOLS BY VALUE >>>\n\n");
fprintf(list_file, "\n\n-- SYMBOLS BY VALUE ------------------\n\n");
qsort(sym_array, symbol_count, sizeof(symbol *), sym_cmp_val);
}
fprintf(list_file, " HEX DEC NAME\n");
fprintf(list_file, " HEX DEC NAME\n");
for (; *sym_p; sym_p++) {
sym = *sym_p;
if (TYPE(sym->value) == TYPE_BYTE)
fprintf(list_file, "%c %02X %5u %-32s\n",
sym_kind_to_char(sym->kind), sym->value.v, sym->value.v,
sym->name);
else
fprintf(list_file, "%c %04X %5u %-32s\n",
sym_kind_to_char(sym->kind), sym->value.v, sym->value.v,
sym->name);
fprintf(list_file, "%c%c ", sym_kind_to_char(sym->kind),
sym_type_to_char(sym->value.t));
if (DEFINED(sym->value)) {
if (TYPE(sym->value) == TYPE_BYTE)
fprintf(list_file, " %02X %5u",sym->value.v, sym->value.v);
else
fprintf(list_file, "%04X %5u", sym->value.v, sym->value.v);
}
else fprintf(list_file, " ? ?");
fprintf(list_file, " %-32s\n", sym->name);
}
}

View File

@@ -1,6 +1,6 @@
/* ASM6502
*
* Copyright (c) 2022 Bernd Böckmann
* Copyright (c) 2022 Bernd Boeckmann
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal

View File

@@ -4,10 +4,19 @@
\title ASM6502 Assembler Manual
\copyright Published by Bernd Boeckmann under BSD-3 license.
\copyright Copyright 2022-2023 by Bernd Boeckmann
\versionid \date
\C{} Where to get
Latest source and binaries may be found at:
\b https://github.com/boeckmann/asm6502
\b https://codeberg.org/boeckmann/asm6502
\C{intro}Introduction
ASM6502 is a small two-pass assembler for the MOS Technology 6502 microprocessor used in many home computers of the 8-bit era. It consists of under 2K lines of C code and can be built with compilers conformant to the C89 standard.
@@ -18,8 +27,7 @@ The assembler outputs plain binary files.
The following listing contains a small sample program. It is the classic hello world for the Commodore C64. To assemble it, invoke the assembler with the source, output, and listing files as arguments:
\c{asm6502 helloc64.a65 helloc64.prg helloc64.lst}
\cw{asm6502 helloc64.a65 helloc64.prg helloc64.lst}
\c 1: ; C64 Hello World
\c 2:
@@ -60,7 +68,7 @@ All instructions a processor understands is given a name, called \e{mnemonic}. T
Beside the instruction itself, additional information may be required to process it. The additional information is provided in the source by one or more \e{arguments} following the mnemonic. In machine code this additional information is encoded in binary form following the opcode.
\c{ADC #42} is an example of an instruction, where \c{ADC} is the mnemonic identifying the instruction, and \c{#42} is the argument. This particular instruction, understood by the MOS6502 processor, adds the value 42 to the value stored in processor register A. It then writes the result back to A.
\c{ADC #42} is an example of an instruction, where \cw{ADC} is the mnemonic identifying the instruction, and \cw{#42} is the argument. This particular instruction, understood by the MOS6502 processor, adds the value 42 to the value stored in processor register A. It then writes the result back to A.
The set of instructions a CPU understands is called \e{instruction set}. There are many different kinds of CPUs and instruction sets. As a result, there is not something like \q{the assembler}, but there are many of them, all adapted to one or more specific instruction sets. ASM6502 is an assembler that is adapted to the instruction set of the MOS6502 processor family.
@@ -91,7 +99,7 @@ assembler.
\H{input-files}Input Files
Input files should have \c{.A65} as extension to distinguish it from files written for other assemblers. Include-files should have \c{.I65} as extension. The files should be encoded in the ASCII or UTF-8 character sets.
Input files should have \cw{.a65} as extension to distinguish it from files written for other assemblers. Include-files should have \cw{.i65} as extension. The files should be encoded in the ASCII or UTF-8 character sets.
\H{data-types}Data Types
@@ -116,8 +124,8 @@ Forward-referenced means that a symbol can be used in expressions before it is d
Labels may not be redefined. If a variable is assigned a value multiple times, it must always be the same value. Otherwise, it is an illegal redefinition.
Symbols may be defined locally by prepending them with \c{@}. They are associated with the previous non-local label defined. They may be referenced within expressions locally by \c{@name} or with their qualified name
\c{label@name} outside their local scope. Example:
Symbols may be defined locally by prepending them with \cw{@}. They are associated with the previous non-local label defined. They may be referenced within expressions locally by \cw{@name} or with their qualified name
\cw{label@name} outside their local scope. Example:
\c jmp hello@l ; fully qualified label reference
\c hello:
@@ -159,7 +167,7 @@ Examples:
In the last example the unary + is only needed if used as an instruction argument to distinguish from 6502 indirect addressing mode.
The special symbol \c{@} evaluates to the current value of the program counter. It may not be confused with a local label, like \c{@abc}.
The special symbol \cw{@} evaluates to the current value of the program counter. It may not be confused with a local label, like \cw{@abc}.
\H{line-format}Line Format
@@ -201,7 +209,7 @@ Example:
\S{}.ECHO directive
Prints the arguments to standard output while running the second pass. The arguments may either be strings or numeric expressions, separated by comma. Numeric expressions may be prefixed by the format specifier \cw{[$]} to output the number in hexadecimal format.
Prints the arguments to standard output. Processed on second pass. The arguments may either be strings or numeric expressions, separated by comma. Numeric expressions may be prefixed by the format specifier \cw{[$]} to output the number in hexadecimal format.
Example:
@@ -231,9 +239,11 @@ Example:
\c .ECHO "I am assembled for the PET"
\c .ENDIF
In listing files, the unprocessed lines are indicated by a minus after the line number instead of a colon.
\S{}.INCLUDE directive
Substitutes the directive with the contents of a file given by the argument. As a convention the extension of include-files should be \c{.i65}.
Substitutes the directive with the contents of a file given by the argument. As a convention the extension of include-files should be \cw{.i65}.
Example:
@@ -241,11 +251,11 @@ Example:
\S{}.LIST and .NOLIST
If a listing file is given via command line, listing generation is initially enabled. If the user wants some parts of the code to be excluded from the listing, the region can be surrounded by \c{.NOLIST} and \c{.LIST} statements.
If a listing file is given via command line, listing generation is initially enabled. If the user wants some parts of the code to be excluded from the listing, the region can be surrounded by \cw{.NOLIST} and \cw{.LIST} statements.
If listing generation is disabled when an \c{.INCLUDE} statement is processed, \c{.LIST} inside the included file has no effect.
If listing generation is disabled when an \cw{.INCLUDE} statement is processed, \cw{.LIST} inside the included file has no effect.
The listing generation flag is restored when the processing of an included file finished. If a \c{.NOLIST} statement is contained in an include file and the listing is activated for the parent file, listing generation is resumed after processing the include file from the line after the \c{.INCLUDE} line.
The listing generation flag is restored when the processing of an included file finished. If a \cw{.NOLIST} statement is contained in an include file and the listing is activated for the parent file, listing generation is resumed after processing the include file from the line after the \cw{.INCLUDE} line.
\S{}.ORG directive
@@ -341,7 +351,7 @@ Indirect indirect by X addresses the byte referenced by the contents of the word
\A{instruction-ref}Instruction Reference
In the following instruction list, \c{#$42} is a representative for a byte-sized immediate value. This value may be substituted by any other byte-sized value. \c{$15} is a representative for a zero-page memory address, and \c{$4711} is a representative for a word-sized memory address.
In the following instruction list, \cw{#$42} is a representative for a byte-sized immediate value. This value may be substituted by any other byte-sized value. \cw{$15} is a representative for a zero-page memory address, and \cw{$4711} is a representative for a word-sized memory address.
The first hexadecimal value on a line is the instruction opcode followed by at most two bytes of additional data defined by the instruction argument. The syntax of the different addressing modes is described in one of the previous chapters.

2
itbl.c
View File

@@ -1,6 +1,6 @@
/* ASM6502
*
* Copyright (c) 2022 Bernd Böckmann
* Copyright (c) 2022 Bernd Boeckmann
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal

6
tests/testconv.a65 Normal file
View File

@@ -0,0 +1,6 @@
lda IDX + 5 ; 3-byte instruction
lda [b] IDX + 5 ; 2-byte instruction
IDX = $10
X = [w]1 ; defines word-sized variable

42
tests/testop.a65 Normal file
View File

@@ -0,0 +1,42 @@
X = $ffff >> 3 ; logical shift right
.if X != $1FFF
.echo "assembler error!"
.else
.if X == $1FFF
.echo "X = ", X
.endif
.endif
.if 1 >= 2
.echo "assembler error!"
.endif
.if 2 <= 1
.echo "assembler error!"
.endif
.if 2 < 1
.echo "assembler error!"
.endif
.if 1 > 2
.echo "assembler error!"
.endif
.echo "Comparison with undefined yields ", 1 < ?
Y = $4711
.echo "low byte of ", [$]Y, " is ", [$]<Y, ", high byte is ", [$]>Y
.echo (0 < (<$4711)) + 41, " should be 42"
.if .not 0
.echo "not zero"
.endif
B1 = 42 == 42
B2 = [b]?
B3 = [w]?
B4 = ?
B5 = 42 == ?