First commit after fork.

This commit is contained in:
waltje
2023-04-25 14:36:25 -04:00
parent c8ec473cb0
commit 32dc773bce
32 changed files with 7258 additions and 110 deletions

4
.gitignore vendored
View File

@@ -1,5 +1,9 @@
# Local stuff
.gitignore
t.*
test*.*
hello.*
*.cmd
# Prerequisites
*.d

View File

@@ -1,39 +1,22 @@
+ implement full getopt interface
+ full source restructuring, broken up into modules
+ allow 'X' for .byte
+ fix .org to auto-fill
+ add a .end directive
+ implement .fill
+ fix the symbol table stuff, added 'where' to them
+ add generating hex files for output
+ add << and >> operators in expressions
+ fix the ^ and / operators in expressions
+ fix the ! (NOT) operator, added the ~ alias
+ support '.', '*' and '$' for "current", not just @
+ added "EQU" in addition to "="
+ allow 'X' for .byte
+ add '.', '*' and '$' for "current pc", not just @
+ fix .org to auto-fill
+ implement ".end" directive
+ implement ".page [plen[,pwidth]" and ".title [str]" directives
+ implement paged listing file
+ implement .binary (and .blob) for binary blob imports
+ add ".page [plen[,pwidth]" and ".title [str]" directives
+ add paged listing file
+ add .cpu directive and underlying multi-target support
- implement .nol option
- implement .sym option
- allow [] for expressions, not just ()
+ implement multiple source files on command line
+ add generating .s19 et al (Motorola) files for output
- Add C02/RC02/WC02/816, 68xx, 80xx et al support.
The basic infrastructure for multi-CPU support is in now in place; we can
start adding more processors and their (sub-)models in the backend files.
- Filling, ORG and hex files.
We currently allow for filling spaces and auto-filling the .org directive
for use with straight-binary output files. This is, however, not necessary
if we are using hex (IntelHEX) or srec (Motorola SRecord) output files, as
those formats "know" that data does not have to be linear. For this to be
implemented properly, we will have to re-work the "generating output" code
to do it as it goes (in pass 2), and not only at the end of that pass. This
allows such "jumping" to be handled by those file formats.
+ move the processor-specific errors to the backend.
+ implement multiple source files on command line
+ Reading raw source data.
We currently perform binary-mode reads on the input source files and all
included files. Although this works fine on Linux (and, assumedly, on all
@@ -51,3 +34,62 @@
That said, reading text files in binary mode is not a good practice to
begin with, so most likely this problem will be fixed by changing the input
scanner to process all input files in ASCII (text) mode instead. DONE.
+ implement .if/ifdef/ifndef .. .else .. .endif directives
+ implement .def() inside expression handler for use with .if
+ implement ".fill" directive
+ implement ".align" directive
- implement ".nol option" directive
+ implement ".sym option" directive
+ implement ".radix" directive for default numbering
+ implement ".asciz" directive
+ allow argument to .title to have no quotes
+ implement predefined symbols ("P65C02" etc) for processors
+ allow register A for accu-based (implied) operations
+ re-implement expression comparisons with upstream code
+ re-added the NOT operators, must have deleted those earlier..
+ reworked expression handler to (also) recognize keywords such as
NOT, AND, ASR and such, instead of just the ! and & etc chars.
+ added .func() functions as part of the expression handler
+ move .def( special-case from expr.c to new func.c.
+ implement .sum() function
+ allow labels to start with .
These are sometimes found in assemblers, either the same way we use
@ now, but sometimes also as 'extenders' for the previous global labels.
foo: ldx #$00
.1 lda .msg, x
beq .2
jsr $ffd2
jmp .1
.2 rts
.msg: .asciz "Hello!"
The above looks the same as when using but '@' local labels, but,
not completely. With "dot labels", they (internally) get *added* to
the previous global label, so the above would be in the symbol table
as 'foo.1', 'foo.2' and 'foo.msg', respectively.
- implement loginal AND (&&) and OR (||) operators
- parentheses (precedence levels within an expression) ?
- We now support byte, word and doubleword values. However, are we consistent
in checking these types within the symbols, labels and expressions?
- Filling, ORG and hex files.
We currently allow for filling spaces and auto-filling the .org directive
for use with straight-binary output files. This is, however, not necessary
if we are using hex (IntelHEX) or srec (Motorola SRecord) output files, as
those formats "know" that data does not have to be linear. For this to be
implemented properly, we will have to re-work the "generating output" code
to do it as it goes (in pass 2), and not only at the end of that pass. This
allows such "jumping" to be handled by those file formats.
- fix the sub-relative include path
If one includes a "../foo/bar/bla.inc" file, and then that file also does
an include of, say, "xxx.inc", then that file is considered "local" to its
parent. So, we should check for that.
- add include search paths using system path and/or -I option?
- Add C02/RC02/WC02/816, 68xx, 80xx et al support.
The basic infrastructure for multi-CPU support is now in place; we can
start adding more processors and their (sub-)models in the backend files.

53
LICENSE
View File

@@ -1,33 +1,32 @@
The BSD 3-Clause Open Software License.
VASM VARCem Multi-Target Assembler.
A simple table-driven assembler for several 8-bit target
devices, like the 6502, 6800, 80x, Z80 et al series. The
code originated from Bernd B”ckmann's "asm6502" project.
This file is part of the VARCem Project.
Redistribution and use in source and binary forms, with
or without modification, are permitted provided that the
following conditions are met:
Authors: Fred N. van Kempen, <waltje@varcem.com>
Bernd B”ckmann, <https://codeberg.org/boeckmann/asm6502>
1. Redistributions of source code must retain the entire
above notice, this list of conditions and the following
disclaimer.
Copyright 2023 Fred N. van Kempen.
Copyright 2022,2023 Bernd B”ckmann.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
MIT License Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following
condition:
3. Neither the name of the copyright holder nor the names
of its contributors may be used to endorse or promote
products derived from this software without specific
prior written permission.
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,6 +1,6 @@
Welcome to **VARCem**, the Virtual Archaeological Computer EMulator.
Vasm is a multi-target, table-driven cross assembler, part of the
**Vasm** is a multi-target, table-driven cross assembler, part of the
VARCem8 emulator project. It implements a general assembler block
with the usual (and, some less usual) features, and then uses a
back-end specific for a given microprocessor to generate code for
@@ -8,13 +8,23 @@ that processor.
HISTORY
-------
Vasm has its roots in the [asm6502](https://github.com/boeckmann/asm6502) project.
Vasm has its roots in the [asm6502](https://github.com/boeckmann/asm6502)
project. From there on, the code was split up into modules, features
have been added and existing features have been improved. All this
did not fit within the design goals of the original project, resulting
in a fork so both projects can benefit.
LICENSE
-------
Vasm is released under the MIT license, the same license as the
original project (see HISTORY above.)
VARCem as a whole is released under the BSD 3-Clause Open Software
License, which is compatible with the GPL (GNU Public License)
version 2 or later that is used by many of the modules imported
from other emulator projects. Parts project are released under the
MIT license.
The author of the original project (see HISTORY above) has authorized
the VARCem team to re-license its fork of their project to the
BSD 3-Clause Open Source License.
Author credits and specific copyrights can be found in each of
the source files.

752
src/expr.c Normal file
View File

@@ -0,0 +1,752 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Handle all expressions.
*
* Version: @(#)expr.c 1.0.4 2023/04/23
*
* Authors: Fred N. van Kempen, <waltje@varcem.com>
* Bernd B”ckmann, <https://codeberg.org/boeckmann/asm6502>
*
* Copyright 2023 Fred N. van Kempen.
* Copyright 2022,2023 Bernd B”ckmann.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "global.h"
#ifndef isxdigit
# define isxdigit(x) (isdigit((x)) || (((x) >= 'a') && ((x) <= 'f')) || \
(((x) >= 'A') && ((x) <= 'F')))
#endif
static int
starts_with(char *text, char *s)
{
while (! IS_END(*s)) {
if (toupper(*text++) != toupper(*s++))
return 0;
}
return 1;
}
static int
digit(const char *p)
{
if (*p <= '9')
return (int)(*p - '0');
if (*p <= 'F')
return (int)(*p + 10 - 'A');
return (int)(*p + 10 - 'a');
}
/* Read a number in some radix. */
static value_t
number(char **p)
{
value_t num = { 0 };
uint8_t type = 0;
char *pt = *p;
if (**p == '&') {
(*p)++;
do_dec:
if (! isdigit(**p))
error(ERR_NUM, NULL);
do {
num.v = num.v * 10 + digit((*p)++);
} while (isdigit(**p));
type = NUM_TYPE(num.v);
} else if (**p == '\\') {
(*p)++;
do_oct:
if (! (**p >= 0 && **p <= '7'))
error(ERR_NUM, NULL);
do {
num.v = num.v * 8 + digit((*p)++);
} while (**p >= '0' && **p <= '7');
type = NUM_TYPE(num.v);
} else if (**p == '$') {
(*p)++;
do_hex:
if (! isxdigit(**p))
error(ERR_NUM, NULL);
do {
num.v = (num.v << 4) + digit((*p)++);
} while (isxdigit(**p));
type = ((*p - pt) > 7) ? TYPE_DWORD : ((*p - pt) > 3) ? TYPE_WORD : NUM_TYPE(num.v);
} else if (**p == '%') {
(*p)++;
do_bin:
if ((**p != '0') && (**p != '1'))
error(ERR_NUM, NULL);
do {
num.v = (num.v << 1) + digit((*p)++);
} while ((**p == '0') || (**p == '1'));
type = ((*p - pt) > 17) ? TYPE_DWORD : ((*p - pt) > 9) ? TYPE_WORD : NUM_TYPE(num.v);
} else {
/* No explicit radix specifier present, go with the default. */
switch (radix) {
case 2:
goto do_bin;
case 8:
goto do_oct;
case 10:
goto do_dec;
case 16:
goto do_hex;
}
/*NOTREACHED*/
}
SET_TYPE(num, type);
SET_DEFINED(num);
return num;
}
static value_t
primary(char **p, int label)
{
char id[ID_LEN], id2[ID_LEN], *pt;
value_t res = { 0 };
symbol_t *sym;
skip_white(p);
#ifdef _DEBUG
if (opt_d)
printf("> PRIMARY(%s)\n", dumpline(*p));
#endif
pt = *p;
if (**p == '(') {
/* Should we keep a nesting count? */
(*p)++;
res = expr(p);
skip_white(p);
if (**p != ')')
error(ERR_UNBALANCED, NULL);
(*p)++;
} else if (**p == DOT_CHAR) {
/* Check for functions. */
(*p)++;
pt = *p;
if (isalpha(**p)) {
ident_upcase(p, id);
if (**p == '(') {
/* We have an alnum AND a (, must be function. */
(*p)++;
/* Execute function. */
res = function(id, p);
if (**p != ')')
error(ERR_OPER, NULL);
(*p)++;
} else {
/* We have alnum but no (, must be a dot label */
*p = pt;
goto dot_label;
}
} else {
dot_label:
if (isalnum(**p) || IS_IDENT(**p)) {
/* Dot labels require preceeding global label. */
if (current_label == NULL)
error(ERR_NO_GLOBAL, NULL);
/* OK, create full global label. */
(*p)--;
nident(p, id2);
if ((strlen(current_label->name) + strlen(id2)) >= ID_LEN)
error(ERR_IDLEN, NULL);
strcpy(id, current_label->name);
strcat(id, id2);
sym = sym_lookup(id, NULL);
if (sym != NULL) {
res = sym->value;
} else {
res.v = 0;
res.t = 0;
}
} else {
/* Current program counter. */
res.v = pc;
res.t = TYPE_WORD | VALUE_DEFINED;
}
}
} else if (**p == '@') {
/* Current program counter or local label. */
(*p)++;
if (isalnum(**p)) {
/* Most likely a local label. */
if (current_label == NULL)
error(ERR_NO_GLOBAL, NULL);
nident(p, id);
sym = sym_lookup(id, &current_label->locals);
if (sym != NULL) {
res = sym->value;
} else {
res.v = 0;
res.t = 0;
}
} else {
/* Current program counter. */
program_counter:
res.v = pc;
res.t = TYPE_WORD | VALUE_DEFINED;
}
} else if (**p == '*') {
/* Current program counter. */
(*p)++;
goto program_counter;
} else if ((**p == '$') && (! isxdigit(*(pt+1)))) {
(*p)++;
goto program_counter;
} else if (**p == '\'') {
/* Single character. */
(*p)++;
if (is_end(**p) || (**p < 0x20))
error(ERR_CHR, NULL);
res.v = **p;
res.t = TYPE_BYTE | VALUE_DEFINED;
(*p)++;
if (**p != '\'')
error(ERR_CHR, NULL);
(*p)++;
} else if (isalpha(**p)) {
/* Symbol reference. */
nident(p, id);
sym = sym_lookup(id, NULL);
if (sym == NULL) {
/* Forward reference, remember it. */
sym = sym_aquire(id, NULL);
if (DEFINED(sym->value))
error(ERR_REDEF, id);
/* Set the correct type. */
if (label) {
sym->kind = KIND_LBL;
sym->value.t = TYPE_WORD;
} else {
sym->kind = KIND_VAR;
sym->value.t = TYPE_BYTE;
}
sym->value.v = 0;
}
res = sym->value;
} else {
/* Must be just a number, but do mind the radix! */
res = number(p);
}
return res;
}
/*
* For compatibility with other assemblers,
* we add word-based operators for:
*
* % MOD
* & AND
* << ASL
* >> ASR
*
* for completeness.
*/
static value_t
product(char **p)
{
value_t n2, res;
char op, op2;
#ifdef _DEBUG
if (opt_d)
printf("PRODUCT(%s)\n", dumpline(*p));
#endif
res = primary(p, 1);
skip_white(p);
op = **p; op2 = *((*p)+1);
if (starts_with(*p, "MOD ")) { op = '%'; *p += 4; }
else if (starts_with(*p, "AND ")) { op = '&'; *p += 4; }
else if (starts_with(*p, "ASL ")) { op = '<'; op2 = '<'; *p += 4; }
else if (starts_with(*p, "ASR ")) { op = '>'; op2 = '>'; *p += 4; };
while ((op == '*') || (op == '&') || (op == '/') || (op == '%') ||
(op == '<' && op2 == '<') || (op == '>' && op2 == '>') ||
(op == '|' && op2 == '|') || (op == '&' && op2 == '&')) {
/* Skip operator. */
(*p)++;
/* For double-character operators, skip the second character. */
if ((op == '<' || op == '>' || op == '|') ||
(op == '&' && op2 == '&'))
(*p)++;
n2 = primary(p, 1);
switch (op) {
case '*': // multiply
res.v = (uint16_t)(res.v * n2.v);
break;
case '/': // divide
if (n2.v == 0)
error(ERR_ZERO, NULL);
res.v = (uint16_t)(res.v / n2.v);
break;
case '%': // modulo
if (n2.v == 0)
error(ERR_ZERO, NULL);
res.v = (uint16_t)(res.v % n2.v);
break;
case '&': // AND
if (op2 == op) // logical AND
res.v = !!res.v && !!n2.v;
else // bitwise AND
res.v = (uint16_t)(res.v & n2.v);
break;
case '|': // OR
if (op2 == op) // logical OR
res.v = !!res.v || !!n2.v;
else // bitwise OR
res.v = (uint16_t)(res.v | n2.v);
break;
case '<': // bitwise shift LEFT
res.v = res.v << n2.v;
break;
case '>': // bitwise shift RIGHT
res.v = res.v >> n2.v;
break;
}
INFER_TYPE(res, n2);
INFER_DEFINED(res, n2);
skip_white(p);
op = **p;
op2 = *((*p)+1);
}
return res;
}
/*
* For compatibility with other assemblers,
* we add word-based operators for:
*
* | OR
* ^ XOR (and/or EOR)
*
* for completeness.
*/
static value_t
term(char **p)
{
value_t n2, res;
char op;
skip_white(p);
#ifdef _DEBUG
if (opt_d)
printf("TERM(%s)\n", dumpline(*p));
#endif
if (**p == '-') { // indicate negative value
/* Unary minus. */
(*p)++;
res = product(p);
res.v = -res.v;
} else {
/* Unary plus. */
if (**p == '+') // skip, default is positive
(*p)++;
res = product(p);
}
skip_white(p);
op = **p;
if (starts_with(*p, "OR ")) { op = '|'; *p += 3; }
else if (starts_with(*p, "XOR ")) { op = '^'; *p += 4; }
else if (starts_with(*p, "EOR ")) { op = '^'; *p += 4; };
while ((op == '+') || (op == '-') || (op == '|') || (op == '^')) {
/* Skip operator. */
(*p)++;
n2 = product(p);
switch (op) {
case '+': // add
res.v = res.v + n2.v;
break;
case '-': // subtract
res.v = res.v - n2.v;
break;
case '|': // bitwise OR
res.v = res.v | n2.v;
break;
case '^': // bitwise XOR
res.v = res.v ^ n2.v;
break;
}
INFER_TYPE(res, n2);
INFER_DEFINED(res, n2);
skip_white(p);
op = **p;
}
return res;
}
static value_t
compare(char **p)
{
value_t res, n2;
char op, op2;
#ifdef _DEBUG
if (opt_d)
printf("COMPARE(%s)\n", dumpline(*p));
#endif
res = term(p);
skip_white(p);
op = **p; op2 = *(*p + 1);
while ((op == '<') || (op == '>') ||
(op == '=' && op2 == '=') || (op == '!' && op2 == '=') ||
(op == '<' && op2 == '=') || (op == '>' && op2 == '=')) {
/* Skip operator. */
(*p)++;
/* For double-character operators, skip the second character. */
if ((op == '=') || (op == '!') ||
(op == '<' && op2 == '=') || (op == '>' && op2 == '='))
(*p)++;
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;
}
INFER_DEFINED(res, n2);
if (DEFINED(res) && res.v)
res.v = 1;
SET_TYPE(res, TYPE_BYTE);
skip_white(p);
op = **p;
op2 = *((*p)+1);
}
return res;
}
/*
* For compatibility with other assemblers,
* we add word-based operators for:
*
* ! NOT
* ~ NOT
*
* for completeness.
*/
value_t
expr(char **p)
{
value_t v;
char op;
skip_white(p);
#ifdef _DEBUG
if (opt_d)
printf("EXPR(%s)\n", dumpline(*p));
#endif
op = **p;
if (starts_with(*p, "NOT ")) { op = '~'; *p += 4; };
if (op == '>') {
/* High-byte (MSB) operator. */
(*p)++;
v = compare(p);
v.v = v.v >> 8;
SET_TYPE(v, TYPE_BYTE);
} else if (op == '<') {
/* Low-byte (LSB) operator. */
(*p)++;
v = compare(p);
v.v = v.v & 0xff;
SET_TYPE(v, TYPE_BYTE);
} else if ((op == '!') || (op == '~')) {
/* NOT operators. */
(*p)++;
v = term(p);
v.v = ~v.v;
SET_TYPE(v, TYPE_WORD);
} else if (starts_with(*p, "[b]")) {
/* Lossless conversion to byte. */
*p += 3;
v = compare(p);
if (DEFINED(v) && v.v > 0xff)
error(ERR_RNG_BYTE, NULL);
SET_TYPE(v, TYPE_BYTE);
} else if (starts_with(*p, "[d]")) {
/* Lossless conversion to doubleword. */
*p += 3;
v = compare(p);
SET_TYPE(v, TYPE_DWORD);
} else if (starts_with(*p, "[w]")) {
/* Lossless conversion to word. */
*p += 3;
v = compare(p);
SET_TYPE(v, TYPE_WORD);
} else {
/* Iterate. */
v = compare(p);
}
return v;
}
/* Take a value and try to convert it to a byte value. */
value_t
to_byte(value_t v)
{
if (DEFINED(v) && (v.v > 0xff))
error(ERR_RNG_BYTE, NULL);
SET_TYPE(v, TYPE_BYTE);
return v;
}
/* Take a value and try to convert it to a word value. */
value_t
to_word(value_t v)
{
if (DEFINED(v) && (v.v > 0xffff))
error(ERR_RNG_WORD, NULL);
SET_TYPE(v, TYPE_WORD);
return v;
}
char
value_type(uint8_t type)
{
switch (type & TYPE_MASK) {
case TYPE_BYTE:
return 'B';
case TYPE_WORD:
return 'W';
case TYPE_DWORD:
return 'D';
}
return '?';
}
/* Determine the desired format for printing a value. */
int
value_format(char **p)
{
char *pt = *p;
int fmt = 0;
skip_white(p);
if (**p != FMT_B_CHAR) {
*p = pt;
return 0;
}
skip_curr_and_white(p);
switch (**p) {
case FMT_BIN_CHAR:
case FMT_DEC_CHAR:
case FMT_HEX_CHAR:
case FMT_HEX1_CHAR:
case FMT_HEX2_CHAR:
fmt = (int)**p;
(*p)++;
break;
case FMT_E_CHAR:
fmt = FMT_DEC_CHAR;
break;
default:
*p = pt;
return -ERR_FMT;
}
if (**p != FMT_E_CHAR) {
*p = pt;
return -ERR_FMT;
}
(*p)++;
return fmt;
}
char *
value_print(value_t v)
{
static char buff[9];
switch (TYPE(v)) {
case TYPE_BYTE:
sprintf(buff, "%02X", v.v & 0xff);
break;
case TYPE_WORD:
sprintf(buff, "%04X", v.v & 0xffff);
break;
case TYPE_DWORD:
sprintf(buff, "%08X", v.v);
break;
}
return buff;
}
/* Print a value in a specific format. */
char *
value_print_format(value_t v, int fmt)
{
static char buff[33];
char *bufp = buff;
int len = (v.t & TYPE_DWORD) ? 32 : (v.t & TYPE_WORD) ? 16 : 8;
int i;
switch (fmt) {
case FMT_BIN_CHAR:
for (i = 0; i < len; i++)
*bufp++ = (v.v & (1 << ((len - i) - 1))) ? '1':'0';
*bufp = '\0';
break;
case FMT_DEC_CHAR:
sprintf(bufp, "%u", (unsigned)v.v);
break;
case FMT_HEX_CHAR:
sprintf(bufp, "$%0*X", len / 4, (unsigned)v.v);
break;
case FMT_HEX1_CHAR:
sprintf(bufp, "0x%0*x", len / 4, (unsigned)v.v);
break;
case FMT_HEX2_CHAR:
sprintf(bufp, "0x%0*X", len / 4, (unsigned)v.v);
break;
}
return buff;
}

2
src/f.sh Normal file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
vasm -p6502 -s -l foo.lst -o foo.hex '-DLOAD=$400' foo.asm

149
src/func.c Normal file
View File

@@ -0,0 +1,149 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Handle all functions.
*
* Version: @(#)func.c 1.0.1 2023/04/22
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2023 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "global.h"
typedef struct pseudo {
const char *name;
value_t (*func)(char **);
} func_t;
/* Implement the ".def(symbol)" function. */
static value_t
do_def(char **p)
{
char id[ID_LEN];
value_t res = { 0 };
symbol_t *sym;
ident(p, id);
sym = sym_lookup(id, NULL);
if (sym == NULL) {
/* Forward reference, remember it. */
sym = sym_aquire(id, NULL);
if (DEFINED(sym->value))
error(ERR_REDEF, id);
/* Set the correct type. */
sym->kind = KIND_VAR;
sym->value.t = TYPE_BYTE;
sym->value.v = 0;
}
res = sym->value;
if (IS_END(**p))
error(ERR_EOL, NULL);
return res;
}
/* Implement the ".sum(startaddr,numbytes)" function. */
static value_t
do_sum(char **p)
{
value_t res = { 0 };
value_t v1, v2;
v1 = expr(p);
skip_white(p);
if (IS_END(**p))
error(ERR_EOL, NULL);
if (**p != ',')
error(ERR_OPER, NULL);
(*p)++;
v2 = expr(p);
/*
* Calculate the (regular) checksum of the code bytes
* between v1 (inclusive) and v2 (exclusive), using a
* normal addition.
*/
if (code != NULL) {
v2.v += v1.v;
while (v1.v < v2.v)
res.v += code[v1.v++ - code_base];
}
SET_DEFINED(res);
return res;
}
static const func_t functions[] = {
{ "DEF", do_def },
{ "DEFINED", do_def },
{ "SUM", do_sum },
{ NULL }
};
value_t
function(const char *name, char **p)
{
const func_t *ptr;
value_t res = { 0 };
/* Skip any whitespace. */
skip_white(p);
for (ptr = functions; ptr->name != NULL; ptr++) {
if (! strcmp(ptr->name, name)) {
res = ptr->func(p);
break;
}
}
return res;
}

279
src/global.h Normal file
View File

@@ -0,0 +1,279 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Definitions for the entire application.
*
* Version: @(#)global.h 1.0.6 2023/04/23
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2023 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef GLOBAL_H
# define GLOBAL_H
#define COMMENT_CHAR ';' // starts a comment (until EOL)
#define DOT_CHAR '.' // starts a directive or local label
#define EOF_CHAR 0x1a
#define EQUAL_CHAR '=' // indicates variable assignment
#define COLON_CHAR ':' // marks a label
#define ALPHA_CHAR '@' // marks a local label
#define FMT_B_CHAR '[' // starts a format specifier
#define FMT_E_CHAR ']' // ends a format specifier
#define FMT_BIN_CHAR '%' // specifies binary format
#define FMT_DEC_CHAR '#' // specifies decimal format
#define FMT_HEX_CHAR '$' // specifies hex format ($ style)
#define FMT_HEX1_CHAR 'x' // specifies hex format (C style)
#define FMT_HEX2_CHAR 'X' // specifies hex format (C style)
#define IS_EOL(c) (((c) == EOF_CHAR) || ((c) == '\n') || ((c) == '\r'))
#define IS_END(c) ((!(c)) || IS_EOL((c)))
#define IS_SPACE(c) (((c) == '\t') || ((c) == ' '))
#define IS_IDENT(c) (((c) == DOT_CHAR) || ((c) == '_'))
#define MAX_FILENAMES 256 + 1 // maximum include files
#define MAX_IFLEVEL 16 // maximum depth of IF levels
#define RADIX_DEFAULT 10 // default radix is decimal
typedef enum errors_e {
ERR_USER = 1, // user-specified error
ERR_ZERO, // "division by zero"
ERR_NOCPU, // "processor type not set"
ERR_CPU, // "unknown processor type"
ERR_MEM, // "out of memory"
ERR_NODIRECTIVE, // "unknown directive"
ERR_INSTR, // "unknown instruction"
ERR_NUM, // "value expected"
ERR_FMT, // "invalid format specifier"
ERR_EXPR, // "error in expression"
ERR_OPER, // "incomplete operator"
ERR_UNBALANCED, // "unbalanced parentheses"
ERR_ID, // "identifier expected"
ERR_IDLEN, // "identifier length exceeded"
ERR_STMT, // "illegal statement"
ERR_EOL, // "end of line expected"
ERR_REDEF, // "illegal redefinition"
ERR_IF, // "IF nesting too deep"
ERR_ELSE, // "ELSE without IF"
ERR_ENDIF, // "ENDIF without IF"
ERR_LBLREDEF, // "symbol already defined as label"
ERR_CLBR, // "missing closing brace"
ERR_UNDEF, // "undefined value"
ERR_ILLTYPE, // "illegal type"
ERR_STREND, // "string not terminated"
ERR_CHREND, // "character constant not terminated"
ERR_RNG, // "value out of range"
ERR_RNG_BYTE, // "byte value out of range
ERR_RNG_WORD, // "word value out of range
ERR_LOCAL_REDEF, // "illegal redefinition of local label"
ERR_NO_GLOBAL, // "local label definition .. global label"
ERR_CHR, // "malformed character constant"
ERR_STRLEN, // "string too long"
ERR_STR, // "string expected"
ERR_OPEN, // "can not open file"
ERR_MAXINC, // "maximum number of include files reached"
ERR_NO_FMT, // "file format not enabled"
ERR_MAXERR // last generic error
} errors_t;
#define ID_LEN 32 // max #characters in identifiers
#define STR_LEN 128 // max #characters in string literals
#define MAXINT(a,b) (((b) >= (a)) ? (b) : (a))
/* The generic "value" type for variables and expressions. */
typedef struct value {
uint32_t v;
uint8_t t;
#define TYPE_NONE 0x00
#define TYPE_BYTE 0x01
#define TYPE_WORD 0x02
#define TYPE_DWORD 0x04
#define TYPE_MASK 0x0f
} value_t;
/* For the value-specific directives. */
#define VALUE_DEFINED 0x80
#define DEFINED(x) (((x).t & VALUE_DEFINED) != 0)
#define UNDEFINED(x) (((x).t & VALUE_DEFINED) == 0)
#define SET_DEFINED(v) ((v).t = ((v).t | VALUE_DEFINED))
#define SET_UNDEFINED(v) ((v).t = (v).t & TYPE_MASK);
#define INFER_DEFINED(a,b) \
if (UNDEFINED(a) || UNDEFINED(b)) { SET_UNDEFINED(a); } \
else { SET_DEFINED(a); }
/* For the type-specific directives. */
#define TYPE(v) ((v).t & TYPE_MASK)
#define SET_TYPE(v, u) ((v).t = ((v).t & VALUE_DEFINED) | (u))
#define NUM_TYPE(x) (((x) < 0x100) ? TYPE_BYTE : ((x) < 0x10000) ? TYPE_WORD : TYPE_DWORD)
#define INFER_TYPE(a,b) (((a).v >= 0x100) || ((b).v >= 0x100)) \
? SET_TYPE((a), TYPE_WORD) \
: SET_TYPE((a), MAXINT(TYPE(a),(TYPE(b))))
/* Data type for storing symbols (labels and variables.) */
typedef struct sym_ {
char name[ID_LEN];
value_t value;
uint8_t kind; // is it a label or a variable?
#define KIND_LBL 1
#define KIND_VAR 2
uint8_t pass; // defined in which pass?
short filenr; // in which file was it defined?
int linenr; // on what line in that file?
struct sym_ *next;
struct sym_ *locals; // local subdefinitions
} symbol_t;
/* Symbol-specific directives. */
#define IS_LBL(x) (((x)->kind & KIND_LBL) != 0)
#define IS_VAR(x) (((x)->kind & KIND_VAR) != 0)
struct pseudo;
/* Global variables. */
extern int opt_d,
opt_C,
opt_F,
opt_P,
opt_q,
opt_v;
extern char myname[],
version[];
extern uint16_t pc;
extern int line,
newline;
extern symbol_t *current_label;
extern int8_t org_done;
extern int8_t radix;
extern int iflevel,
ifstate,
newifstate,
ifstack[];
extern char *filenames[];
extern int filelines[];
extern int filenames_idx,
filenames_len;
extern char *text;
extern int text_len;
extern uint8_t *code;
extern uint32_t code_base;
extern uint16_t oc,
sa;
extern int list_plength,
list_pwidth;
#if defined(_WIN32) && defined(_MSC_VER)
# define strcasecmp _stricmp
#endif
/* Functions. */
#ifdef _DEBUG
extern char *dumpline(const char *p);
#endif
extern int pass(char **, int);
extern void error(int, const char *);
extern int is_end(char);
extern void skip_eol(char **);
extern void skip_white(char **);
extern void skip_white_and_comment(char **);
extern void skip_curr_and_white(char **);
extern void ident(char **, char *);
extern void upcase(char **, char *);
extern void ident_upcase(char **, char *);
extern void nident(char **, char *);
extern void nident_upcase(char **, char *);
extern symbol_t *sym_table(void);
extern char sym_type(const symbol_t *);
extern symbol_t *sym_lookup(const char *, symbol_t **);
extern void sym_free(symbol_t **);
extern symbol_t *sym_aquire(const char *, symbol_t **);
extern symbol_t *define_label(const char *, uint16_t, symbol_t *);
extern void define_variable(const char *, value_t);
extern value_t expr(char **);
extern value_t to_byte(value_t);
extern value_t to_word(value_t);
extern char value_type(uint8_t);
extern int value_format(char **);
extern char *value_print(value_t);
extern char *value_print_format(value_t, int);
extern value_t function(const char *, char **);
extern size_t file_size(const char *);
extern int file_read_buf(const char *, char *);
extern int file_read(const char *, char **, size_t *);
extern void file_add(const char *, int, const char *, size_t);
extern void emit_addr(uint32_t);
extern int emit_str(const char *, int, int);
extern int emit_byte(uint8_t, int);
extern int emit_word(uint16_t, int);
extern int emit_dword(uint32_t, int);
extern int save_code(const char *, const uint8_t *, int);
extern void list_set_head(const char *);
extern void list_set_syms(int);
extern int list_init(const char *);
extern void list_close(void);
extern void list_line(const char *);
extern void list_page(const char *);
extern void list_save(uint16_t, uint16_t);
extern void list_symbols(void);
extern const struct pseudo *is_pseudo(const char *);
extern char *pseudo(const struct pseudo *, char **, int);
extern int set_cpu(const char *, int);
#endif /*GLOBAL_H*/

212
src/input.c Normal file
View File

@@ -0,0 +1,212 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Handle reading source input. Care must be taken (especially
* on Windows systems, where we seem to have some issues with
* the "fread" function on text files) to properly read data
* from them when opened as a text file.
*
* Version: @(#)input.c 1.0.3 2023/04/11
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2023 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "global.h"
/*
* The text variable holds all the assembler source.
*
* The main assembler file and all files included are joined by an EOF (0x1A)
* character and stored as a whole in the text variable. Binary zero
* marks the end of the assembler text.
*/
char *text = NULL; // holds the assembler source
int text_len; // total length of the source
/*
* The filenames variable stores the filenames of all included files.
* When parsing the source the filenames_idx variable is incremented
* when en EOF character is encountered, and the line counter variable is
* set to filelines[filenames_idx], current filename is set to
* filenames[filenames_idx].
*/
char *filenames[MAX_FILENAMES];
int filelines[MAX_FILENAMES];
int filenames_idx;
int filenames_len;
/* Determine the "cooked" size (in characters) of a text file. */
size_t
file_size(const char *fn)
{
size_t size;
FILE *fp;
int c;
/* Open the file in text mode. */
if ((fp = fopen(fn, "r")) == NULL)
error(ERR_OPEN, fn);
/* Text mode, so read file, counting characters. */
size = 0;
while (!feof(fp) && !ferror(fp)) {
if ((c = fgetc(fp)) == EOF)
break;
if (c == '\r')
continue;
else
size++;
}
/* File can be closed now. */
(void)fclose(fp);
return size;
}
int
file_read_buf(const char *fn, char *bufp)
{
char *ptr;
FILE *fp;
int c;
/* Open the file in text mode. */
if ((fp = fopen(fn, "r")) == NULL)
return 0;
/* Now read the file's contents into the buffer. */
ptr = bufp;
while (!feof(fp) && !ferror(fp)) {
if ((c = fgetc(fp)) == EOF)
break;
if (c == '\r')
continue;
*ptr++ = c;
}
*ptr = '\0';
/* File can be closed now. */
(void)fclose(fp);
/* Return the buffer size. */
return (int)(ptr - bufp);
}
int
file_read(const char *fn, char **pp, size_t *sizep)
{
char *ptr;
size_t size;
FILE *fp;
int c;
/* Open the file in text mode. */
if ((fp = fopen(fn, "r")) == NULL)
return 0;
/* Text mode, so read file, counting characters. */
size = 0;
while (!feof(fp) && !ferror(fp)) {
if ((c = fgetc(fp)) == EOF)
break;
if (c == '\r')
continue;
else
size++;
}
fseek(fp, 0, SEEK_SET);
/* Allocate a buffer for the contents. */
if (*pp == NULL) {
/* No buffer yet, just allocate one. */
*pp = malloc(size + 1); // +1 for NUL at end
ptr = *pp;
} else {
/* We already have a buffer, so add to it. */
*pp = realloc(*pp, *sizep + 1 + size + 1); // +1 for EOF inbetween
ptr = *pp + *sizep;
*ptr++ = EOF_CHAR; // insert EOF
}
ptr[size] = '\0'; // terminate buffer
*sizep += size;
if (*pp == NULL) {
(void)fclose(fp);
return 0;
}
/* Now read the file's contents into the buffer. */
while (!feof(fp) && !ferror(fp)) {
if ((c = fgetc(fp)) == EOF)
break;
if (c == '\r')
continue;
*ptr++ = c;
}
*ptr = '\0';
/* File can be closed now. */
(void)fclose(fp);
return 1;
}
void
file_add(const char *name, int linenr, const char *str, size_t size)
{
int c = filenames_idx;
filenames[c] = strdup(name);
filelines[c] = 1;
// filetexts[c] = str;
// filesizes[c] = size;
filenames_idx++;
filenames_len++;
}

335
src/list.c Normal file
View File

@@ -0,0 +1,335 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Handle the listfile output.
*
* Version: @(#)list.c 1.0.3 2023/04/17
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2023 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "global.h"
#define LIST_PLENGTH 66 // number of lines per page
#define LIST_PWIDTH 80 // number of characters per line
#define LIST_CHAR_FF "\014" // FormFeed character
#define LIST_CHAR_SI "\017" // SI (condensed printing mode)
#define LIST_CHAR_DC2 "\022" // DC2 (end condensed printing mode)
#define LIST_NBYTES 4 // this many code bytes per line
int list_plength = LIST_PLENGTH, // lines per page
list_pwidth = LIST_PWIDTH; // characters per line
static int list_lnr,
list_pnr,
list_pln;
static uint16_t list_oc,
list_pc;
static char *list_head;
static FILE *list_file = NULL;
static int list_syms = 0;
void
list_set_head(const char *head)
{
if (list_head != NULL)
free(list_head);
list_head = NULL;
/* .. and set the new one. */
if (head != NULL)
list_head = strdup(head);
}
void
list_set_syms(int syms)
{
list_syms = syms;
}
/*
* Start a new page in the listing file.
*
* We have the dimensions set in PLENGTH (66) and PWIDTH (80),
* which can be changed with the .page directive. Do remember
* that these numbers INCLUDE the page margings, so effectively
* we have less than those- most printers require about 0.25"
* of margings at the minimum, so a U.S. Letter page of 8.5x11"
* will result in 80 (at 10cpi) characters and 63 (at 6lpi) lines
* for the page. We also use 3 lines per page for the header, so
* with those default settings, we get to "keep" 60 lines.
*/
void
list_page(const char *head)
{
char buff[1024], page[256], date[64];
char *ptr = buff;
struct tm *tm;
time_t now;
int i, skip;
if (list_file == NULL)
return;
if (head == NULL)
head = list_head;
/* Initialize printer to condensed mode if width > 80. */
if (opt_P && list_pnr == 0 && list_pwidth > 80) {
fprintf(list_file, LIST_CHAR_SI);
}
/* Bump the page number. */
list_pnr++;
/* Get a (localized) date and time string. */
(void)time(&now);
tm = localtime(&now);
strftime(date, sizeof(date), "%c", tm);
sprintf(page, "%s Page %i", date, list_pnr);
skip = list_pwidth - (strlen(myname) + 1 + strlen(version) + strlen(page));
if (list_pnr > 1) {
/* Insert a form-feed for all but first page. */
sprintf(ptr, "%s", LIST_CHAR_FF);
ptr += strlen(ptr);
}
sprintf(ptr, "%s %s", myname, version);
ptr += strlen(ptr);
while (skip--)
*ptr++ = ' ';
strcpy(ptr, page);
fprintf(list_file, "%s\n", buff);
sprintf(page, "File: %s", filenames[filenames_idx]);
i = (head != NULL) ? strlen(head) : 0;
skip = list_pwidth - (i + strlen(page));
ptr = buff;
if (head != NULL) {
sprintf(ptr, "%s", head);
ptr += strlen(ptr);
}
while (skip--)
*ptr++ = ' ';
strcpy(ptr, page);
fprintf(list_file, "%s\n\n", buff);
/* OK, good for another page.. */
list_pln = list_plength - (3 + 3); // margin + header
}
/*
* Generate one line of listing info.
*
* Keep in mind that many source files contain TAB characters,
* and for those to look "nice" in our listings, we must make
* sure the actual listing starts at a multiple-of-8 position
* plus one. For now, we are using position 33.
*/
void
list_line(const char *p)
{
int count = 0;
if (list_file == NULL)
return;
if (list_pln == 0) {
/* Reset the page. */
list_page(list_head);
}
/* Output listing line number. */
fprintf(list_file, "%05i ", list_lnr++);
/* Output program counter, but only if we emitted code. */
if (list_oc < oc) {
if (opt_C)
fprintf(list_file, "%04X ", list_oc);
fprintf(list_file, "%04X ", list_pc);
} else {
if (opt_C)
fputs(" ", list_file);
fputs(" ", list_file);
}
/* Output any code bytes. */
while (list_oc < oc && count < LIST_NBYTES) {
fprintf(list_file, "%02X ", code[list_oc++]);
list_pc++;
count++;
}
/* Fill the remaining space. */
while (count < LIST_NBYTES) {
fputs(" ", list_file);
count++;
}
fprintf(list_file, "%6i%c ", line, ifstate ? ':' : '-');
if (p != NULL) {
while (*p && *p != '\n' && *p != EOF_CHAR)
fputc(*p++, list_file);
}
fputs("\n", list_file);
list_pln--;
if (list_oc < oc)
list_line(NULL);
}
void
list_symbols(void)
{
symbol_t *sym = sym_table();
symbol_t *loc;
FILE *fp;
/* Has this been enabled? */
if (! list_syms)
return;
/* If we are not using a listing file, dump to stdout. */
if (list_file != NULL) {
list_page("** SYMBOL TABLE **"); // new page
fp = list_file; // use listing file
} else
fp = stdout; // use stdout
if (sym != NULL) {
if (list_file == NULL) // only when on stdout
fprintf(fp, "Symbol table:\n");
for (; sym; sym = sym->next) {
if ((list_file != NULL) && (list_pln == 0))
list_page("** SYMBOL TABLE **");
fprintf(fp, "%-32s %c ", sym->name, sym_type(sym));
if (DEFINED(sym->value)) {
fprintf(fp, "%5s ", value_print(sym->value));
if (IS_VAR(sym))
fprintf(fp, "%c", value_type(sym->value.t));
else
fprintf(fp, " ");
fprintf(fp, " ");
if (sym->filenr != -1)
fprintf(fp, "%s:%i",
filenames[sym->filenr], sym->linenr);
else
fprintf(fp, "-command line-");
} else
fprintf(fp, "%5s", "??");
fprintf(fp, "\n");
if (list_file != NULL)
list_pln--;
if ((list_syms == 2) && IS_LBL(sym)) {
for (loc = sym->locals; loc; loc = loc->next) {
if ((list_file != NULL) && (--list_pln == 0))
list_page("** SYMBOL TABLE **");
fprintf(fp, " %c%-29s %c ",
ALPHA_CHAR, loc->name, sym_type(sym));
fprintf(fp, "%5s %s:%i\n",
value_print(loc->value),
filenames[loc->filenr], loc->linenr);
}
}
}
} else
fprintf(fp, "No symbols defined.\n");
}
void
list_save(uint16_t pc, uint16_t oc)
{
if (list_file != NULL) {
list_oc = oc;
list_pc = pc;
}
}
void
list_close(void)
{
if (list_file != NULL) {
/* Reset printer to normal mode if needed. */
if (opt_P && list_pwidth > 80 && list_pnr > 0) {
fprintf(list_file, LIST_CHAR_DC2);
}
(void)fclose(list_file);
list_file = NULL;
}
}
int
list_init(const char *fn)
{
list_file = fopen(fn, "w");
if (list_file == NULL)
return 0;
list_lnr = 1;
list_pnr = list_pln = 0;
list_oc = list_pc = 0;
list_head = NULL;
return 1;
}

314
src/main.c Normal file
View File

@@ -0,0 +1,314 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* A simple but reasonably useful assembler for the 6502.
*
* Usage: vasm [-dCFqsvPV] [-p processor] [-l fn] [-o fn] [-Dsym[=val]] file ...
*
* Version: @(#)main.c 1.0.7 2023/04/23
*
* Authors: Fred N. van Kempen, <waltje@varcem.com>
* Bernd B”ckmann, <https://codeberg.org/boeckmann/asm6502>
*
* Copyright 2023 Fred N. van Kempen.
* Copyright 2022,2023 Bernd B”ckmann.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#ifndef _MSC_VER
# include <getopt.h>
#endif
#include "global.h"
#include "version.h"
int opt_d, // set DEBUG env variable to enable debug
opt_C, // is true, also list the list offset
opt_F, // if true, perform autofill with .org
opt_P, // enable Printer mode
opt_q, // be very quiet
opt_v; // more verbose
char myname[64], // my name
version[128]; // my full version string
#ifdef _MSC_VER
extern int getopt(int ac, char *av[], const char *),
optind, opterr;
extern char *optarg;
#endif
/* Define a symbol from the command line. */
static void
do_define(char *str)
{
char id[ID_LEN];
char **p = &str;
value_t v = { 0 };
ident(p, id);
if (**p == '=') {
/* We have an equal sign. */
(*p)++;
if (! IS_END(**p)) {
/* Try to decode a value. */
v = expr(p);
} else {
/* No value given, whoops. */
goto nodata;
}
} else {
nodata:
v.v = 1;
SET_DEFINED(v);
SET_TYPE(v, TYPE_BYTE);
}
define_variable(id, v);
}
static void
usage(const char *prog)
{
printf("Usage: %s [-dCFPqsvV] [-p processor] [-l fn] [-o fn] [-Dsym[=val]] file ...\n", prog);
exit(1);
/*NOTREACHED*/
}
static void
banner(void)
{
printf("%s %s\nCopyright 2023 Fred N. van Kempen, <waltje@varcem.com>\n",
APP_TITLE, version);
#ifdef _WIN32
printf("Copyright 2022,2023 Bernd B”ckmann, <bernd@varcem.com>\n\n");
#else
printf("Copyright 2022,2023 Bernd B\303\266ckmann, <bernd@varcem.com>\n\n");
#endif
}
int
main(int argc, char *argv[])
{
char *out_name, *list_name;
char *ttext;
int c, opt_s;
size_t size;
int errors = 0;
/* Set option defaults. */
#ifdef _DEBUG
opt_d = (getenv("DEBUG") != NULL);
#endif
opt_C = 0;
opt_F = 1;
opt_P = opt_s = 0;
opt_q = opt_v = 0;
out_name = list_name = NULL;
filenames_idx = -1; // this indicates "command line"
radix = RADIX_DEFAULT;
/* Create a version string. */
sprintf(myname, "%s", APP_NAME);
sprintf(version, "version %s (%s, %s)",
APP_VERSION,
#ifdef _WIN32
"Windows",
#else
"Linux",
#endif
STR(ARCH));
opterr = 0;
while ((c = getopt(argc, argv, "dCD:Fl:o:Pp:qsvV")) != EOF) switch(c) {
case 'C': // toggle list-offset display (disabled)
opt_C ^= 1;
break;
case 'D': // define symbol
do_define(optarg);
break;
case 'd': // debug mode (disabled)
opt_d ^= 1;
break;
case 'F': // auto-fill for .org (enabled)
opt_F ^= 1;
break;
case 'l': // set listing file name (none)
list_name = optarg;
break;
case 'o': // set output file name (none)
out_name = optarg;
break;
case 'P': // enable Printer mode
opt_P ^= 1;
break;
case 'p': // processor name
if (! set_cpu(optarg, 1)) {
fprintf(stderr, "Unknown processor '%s'.\n", optarg);
return 1;
}
break;
case 'q': // be very quiet during operation (disabled)
opt_q ^= 1;
break;
case 's': // show (or dump) the symbol table
opt_s ^= 1;
list_set_syms(opt_s << 1); // FULL or OFF
break;
case 'v': // level of verbosity (lowest)
opt_v++;
break;
case 'V': // just show version and exit
banner();
exit(EXIT_SUCCESS);
/*NOTREACHED*/
default:
usage(argv[0]);
/*NOTREACHED*/
}
/* Say hello. */
if (! opt_q)
banner();
/* We need at least one filename argument. */
if (optind == argc)
usage(argv[0]);
//FIXME: if we do not have an output file, auto-generate filename
// from the (first) input file?
if (out_name == NULL) {
fprintf(stderr, "No output file specified.\n");
errors = 1;
goto ret0;
}
/* Create a listing file if requested. */
if ((list_name != NULL) && !list_init(list_name)) {
fprintf(stderr, "Listing file '%s' could not be created!\n", list_name);
errors = 1;
goto ret0;
}
/* Read all input files into our buffer. */
text = NULL;
size = 0;
filenames_idx = 0;
while (optind < argc) {
if (! file_read(argv[optind], &text, &size)) {
fprintf(stderr, "Error loading file %s\n", argv[optind]);
errors = 1;
goto ret0;
}
file_add(argv[optind++], 1, text, size);
}
text_len = size;
/* Perform Pass 1. */
ttext = text;
errors = pass(&ttext, 1);
if (errors)
goto ret1;
/* Create the output buffer. */
ttext = text;
code = malloc(oc);
/* Perform Pass 2. */
errors = pass(&ttext, 2);
if (errors)
goto ret2;
if (! opt_q)
printf("Generated %i bytes of output.\n", oc);
if (! save_code(out_name, code, oc)) {
fprintf(stderr, "error writing output file %s\n", out_name);
errors = 1;
goto ret2;
}
/* Dump the symbols, if enabled. */
list_symbols();
ret2:
list_close();
if (code != NULL)
free(code);
ret1:
// if (text != NULL)
// free(text);
sym_free(NULL);
ret0:
if (errors) {
if (list_name != NULL)
(void)remove(list_name);
if (out_name != NULL)
(void)remove(out_name);
return EXIT_FAILURE;
}
return 0;
}

244
src/output.c Normal file
View File

@@ -0,0 +1,244 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Handle the writing of output data.
*
* **NOTE** This is far from 'correct' right now. We perform output in
* a single block right after assembly, but for non-binary
* output formats, this is wrong. The right way to do this is
* to "start" the output file as needed, and then, as we move
* on in the assembly, output data as needed. This also allows
* for the proper use of the ".org" directive, as this then
* merely changes the 'load address' of the following bytes.
*
* Version: @(#)output.c 1.0.3 2023/04/22
*
* Authors: Fred N. van Kempen, <waltje@varcem.com>
* Bernd B”ckmann, <https://codeberg.org/boeckmann/asm6502>
*
* Copyright 2023 Fred N. van Kempen.
* Copyright 2022,2023 Bernd B”ckmann.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef USE_IHEX
# include <ihex.h>
#endif
#ifdef USE_SREC
# include <srec.h>
#endif
#include "global.h"
uint8_t *code = NULL; // holds the emitted code
uint32_t code_base = 0; // our load (base) address
static FILE *out_file; // output file
/* Set our load (base) address. */
void
emit_addr(uint32_t addr)
{
code_base = addr;
}
/* Copy a string (of characters) to the output. */
int
emit_str(const char *p, int len, int pass)
{
uint16_t i;
if (pass == 2) {
for (i = 0; i < len; i++)
code[oc + i] = (uint8_t)*p++;
}
oc += len;
return len;
}
/* Copy a single byte to the output. */
int
emit_byte(uint8_t b, int pass)
{
if (pass == 2)
code[oc] = b;
oc++;
return 1;
}
/* Copy a single word to the output. */
int
emit_word(uint16_t w, int pass)
{
if (pass == 2) {
/* We use little-endian format here. */
code[oc] = w & 0xff;
code[oc + 1] = w >> 8;
}
oc += 2;
return 2;
}
/* Copy a single double-word to the output. */
int
emit_dword(uint32_t w, int pass)
{
if (pass == 2) {
/* We use little-endian format here. */
code[oc] = w & 0xff;
code[oc + 1] = (w >> 8) & 0xff;
code[oc + 2] = (w >> 16) & 0xff;
code[oc + 3] = (w >> 24) & 0xff;
}
oc += 2;
return 4;
}
#ifdef USE_IHEX
void
ihex_flush_buffer(ihex_t *ihex, char *buffer, char *eptr)
{
*eptr = '\0';
(void)fputs(buffer, out_file);
}
#endif
//FIXME: we should use out_name, and update that if we change it here!
int
save_code(const char *fn, const uint8_t *data, int len)
{
char name[1024], *p;
#ifdef USE_IHEX
ihex_t ihex;
ihex_count_t count;
uint8_t *ptr;
#endif
int fmt, i;
/* Copy the filename and determine the desired format. */
strncpy(name, fn, sizeof(name) - 1);
p = strrchr(name, '/');
#ifdef _WIN32
if (p == NULL)
p = strrchr(name, '\\');
#endif
if (p == NULL)
p = name;
p = strrchr(p, '.');
if (p != NULL)
p++;
fmt = 1;
if (p == NULL) {
/* No filename extension - use .bin as a default! */
strcat(name, ".bin");
} else if (! strcasecmp(p, "hex")) {
fmt = 2;
} else if (!strcasecmp(p, "srec") || !strcasecmp(p, "s19")) {
fmt = 3;
}
i = 0;
switch (fmt) {
case 1: // raw binary
out_file = fopen(fn, "wb");
if (out_file == NULL)
break;
i = fwrite(data, len, 1, out_file);
(void)fclose(out_file);
break;
case 2: // intel hex
#ifdef USE_IHEX
out_file = fopen(fn, "w");
if (out_file == NULL)
break;
ihex_init(&ihex);
// ihex_write_at_address(&ihex, pc);
ihex_set_output_line_length(&ihex,
IHEX_DEFAULT_OUTPUT_LINE_LENGTH);
ptr = code;
while (oc > 0) {
count = (oc > 1024) ? 1024 : oc;
ihex_write_bytes(&ihex, ptr, count);
ptr += count;
oc -= count;
}
if (sa != 0)
ihex_write_start_address(&ihex, sa);
ihex_end_write(&ihex);
(void)fclose(out_file);
i = 1;
#else
error(ERR_NO_FMT, p);
#endif
break;
case 3: // motorola SRecord
i = 0;
error(ERR_NO_FMT, p);
}
out_file = NULL;
/* If this did not work, let them know. */
if ((i == 0) && (pc != 0))
return 0;
return 1;
}

505
src/parse.c Normal file
View File

@@ -0,0 +1,505 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Parse the source input, process it, and generate output.
*
* Version: @(#)parse.c 1.0.6 2023/04/23
*
* Authors: Fred N. van Kempen, <waltje@varcem.com>
* Bernd B”ckmann, <https://codeberg.org/boeckmann/asm6502>
*
* Copyright 2023 Fred N. van Kempen.
* Copyright 2022,2023 Bernd B”ckmann.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#if __STDC_VERSION__ >= 201112L
# include <stdnoreturn.h>
#else
# define noreturn /*NORETURN*/
#endif
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#include "global.h"
#include "target.h"
int line, // currently processed line number
newline; // next line to be processed
symbol_t *current_label = NULL; // search scope for local labels
int8_t org_done; // has a .org been performed?
int8_t radix; // current numerical radix
int iflevel, // current level of conditionals
ifstate, newifstate, // current conditional state
ifstack[MAX_IFLEVEL];
/* program counter and output counter may not be in sync */
/* this happens if an .org directive is used, which modifies the */
/* program counter but not the output counter. */
uint16_t pc = 0; // addr of currently assembled instr
uint16_t oc = 0; // counter of emitted output bytes
uint16_t sa = 0; // start addr for generated code (.end)
static int errors;
static jmp_buf error_jmp;
static char error_hint[128];
static const char *err_msg[] = {
"no error",
"fatal",
"division by zero detected",
"processor type not set",
"unknown processor type",
"out of memory",
"unknown directive",
"unknown instruction",
"value expected",
"invalid format specifier",
"error in expression",
"incomplete operator",
"unbalanced parentheses",
"identifier expected",
"identifier length exceeded",
"illegal statement",
"end of line expected",
"illegal redefinition",
"IF nesting too deep",
"ELSE without IF",
"ENDIF without IF",
"symbol already defined as label",
"missing closing brace",
"undefined value",
"illegal type",
"string not terminated",
"character constant not terminated",
"value out of range",
"byte value out of range",
"word value out of range",
"illegal redefinition of local label",
"local label definition requires previous global label",
"malformed character constant",
"string too long",
"string expected",
"can not open file",
"maximum number of include files reached",
"file format not enabled"
};
#ifdef _DEBUG
char *
dumpline(const char *p)
{
static char temp[1024];
char *s = temp;
while (*p && *p != '\n')
*s++ = *p++;
*s = '\0';
return(temp);
}
#endif
/* Display error message and abort action. */
noreturn void
error(int err, const char *msg)
{
errors++;
if (msg != NULL)
strncpy(error_hint, msg, sizeof(error_hint) - 1);
else
memset(error_hint, 0x00, sizeof(error_hint));
longjmp(error_jmp, err);
/*NOTREACHED*/
}
void
skip_eol(char **p)
{
if (**p == EOF_CHAR)
(*p)++;
if (**p == 0x0d)
(*p)++;
if (**p == 0x0a)
(*p)++;
}
int
is_end(char p)
{
return IS_END(p);
}
void
skip_white(char **p)
{
while (IS_SPACE(**p))
(*p)++;
}
void
skip_white_and_comment(char **p)
{
while (IS_SPACE(**p))
(*p)++;
if (**p == COMMENT_CHAR) {
(*p)++;
while (! IS_END(**p))
(*p)++;
}
}
void
skip_curr_and_white(char **p)
{
(*p)++;
while (IS_SPACE(**p))
(*p)++;
}
static void
_ident(char **p, char *id, int numeric)
{
int i = 0;
/* Identifiers may have letters, digits, and some specials. */
if ((!numeric && !isalpha(**p) && !IS_IDENT(**p)) ||
(!isalnum(**p) && !IS_IDENT(**p))) error(ERR_ID, NULL);
do {
*id++ = *(*p)++;
if (++i >= ID_LEN)
error(ERR_IDLEN, NULL);
} while (isalnum(**p) || IS_IDENT(**p));
*id = '\0';
}
/* Read identifier which may not start with a digit. */
void
ident(char **p, char *id)
{
_ident(p, id, 0);
}
/* Read identifier which may start with a digit. */
void
nident(char **p, char *id)
{
_ident(p, id, 1);
}
/* Read string and convert to uppercase. */
void
upcase(char **p, char *str)
{
int i = 0;
do {
*str++ = (char)toupper(*(*p)++);
if (++i >= ID_LEN)
error(ERR_IDLEN, NULL);
} while (isalnum(**p));
*str = '\0';
}
/* Read identifier and convert to uppercase. */
void
ident_upcase(char **p, char *id)
{
int i = 0;
if (!isalpha(**p) && !IS_IDENT(**p))
error(ERR_ID, NULL);
do {
*id++ = (char)toupper(*(*p)++);
if (++i >= ID_LEN)
error(ERR_IDLEN, NULL);
} while (isalnum(**p) || IS_IDENT(**p));
*id = '\0';
}
/* Read identifier and convert to uppercase. */
void
nident_upcase(char **p, char *id)
{
int i = 0;
do {
*id++ = (char)toupper(*(*p)++);
if (++i >= ID_LEN)
error(ERR_IDLEN, NULL);
} while (isalnum(**p) || IS_IDENT(**p));
*id = '\0';
}
/* Processes one statement or assembler instruction. */
static char *
statement(char **p, int pass)
{
char id[ID_LEN], id2[ID_LEN];
const struct pseudo *ptr;
char *newp = NULL;
value_t v;
char *pt;
skip_white_and_comment(p);
if (IS_END(**p))
return NULL;
/* First check for variable or label definition. */
pt = *p;
if (isalpha(**p)) {
ident(p, id);
skip_white(p);
if ((**p == EQUAL_CHAR) || !strncmp(*p, "EQU", 3)) {
/* Variable definition. */
if (**p == EQUAL_CHAR)
(*p)++;
else
(*p) += 3;
v = expr(p);
if (ifstate)
define_variable(id, v);
return NULL;
} else if ((**p == COLON_CHAR) || !trg_instr_ok(id)) {
/*
* Labels either have a colon at the end, OR are anything
* but an valid instruction. To force a label with the same
* name as an instruction, add a colon to it :)
*/
if (**p == COLON_CHAR)
(*p)++;
if (ifstate)
current_label = define_label(id, pc, NULL);
skip_white_and_comment(p);
if (IS_END(**p))
return NULL;
} else
*p = pt;
}
/* Local label definition? */
else if (**p == ALPHA_CHAR) {
(*p)++;
nident(p, id);
if (ifstate) {
if (current_label == NULL)
error(ERR_NO_GLOBAL, NULL);
define_label(id, pc, current_label);
}
skip_white(p);
if (**p == COLON_CHAR)
(*p)++;
skip_white_and_comment(p);
if (IS_END(**p))
return NULL;
}
/* Check for directive or instruction. */
again:
if (**p == DOT_CHAR) {
/* Local label or directive. */
pt = *p;
(*p)++;
nident_upcase(p, id);
if ((ptr = is_pseudo(id)) != NULL) {
/* All good, we're a directive. */
newp = pseudo(ptr, p, pass);
return newp;
}
if (ifstate) {
if (current_label == NULL)
error(ERR_NO_GLOBAL, NULL);
/* Restore our pointer and get the label ID. */
*p = pt;
nident(p, id2);
/* We don't care about the colon. */
if (**p == COLON_CHAR)
(*p)++;
if ((strlen(current_label->name) + strlen(id2)) >= ID_LEN)
error(ERR_IDLEN, id2);
strcpy(id, current_label->name);
strcat(id, id2);
define_label(id, pc, NULL);
}
skip_white_and_comment(p);
if (IS_END(**p))
return NULL;
/* Restart scanning for statements on this line. */
goto again;
}
skip_white_and_comment(p);
if (IS_END(**p))
return NULL;
/* We only parse the rest if we are active. */
if (ifstate) {
if (isalpha(**p)) {
/* Execute instruction and update program counter. */
pc += trg_instr(p, pass);
} else
error(ERR_STMT, NULL);
} else {
/* Just skip. */
while (! IS_END(**p))
(*p)++;
}
return NULL;
}
int
pass(char **p, int pass)
{
const char *msg;
char *list;
char *newp;
int err;
errors = 0;
line = 1;
current_label = NULL;
org_done = 0;
radix = RADIX_DEFAULT;
pc = oc = 0;
filenames_idx = 0;
iflevel = 0;
ifstate = 1;
memset(ifstack, 0x00, sizeof(ifstack));
list_set_head(NULL);
if ((err = setjmp(error_jmp)) == 0) {
while (**p) {
/* Save current line position for the listing. */
list = *p;
newline = line + 1;
newifstate = ifstate;
newp = statement(p, pass);
skip_white_and_comment(p);
/*
* Every statement ends with a newline.
* If it is not found here it is an error condition.
*/
if (! IS_END(**p))
error(ERR_EOL, NULL);
skip_eol(p);
if (pass == 2)
list_line(list);
line = newline;
ifstate = newifstate;
if (**p == EOF_CHAR) {
(*p)++;
filenames_idx++;
line = filelines[filenames_idx];
}
if (newp != NULL)
text = newp;
list_save(pc, oc);
}
} else {
if (err < ERR_MAXERR)
msg = err_msg[err];
else
msg = trg_error(err);
printf("%s:%i: error: %s",
filenames[filenames_idx], line, msg);
if (error_hint[0] != '\0')
printf(" (%s)", error_hint);
printf("\n");
}
return errors;
}

1034
src/pseudo.c Normal file

File diff suppressed because it is too large Load Diff

256
src/symbol.c Normal file
View File

@@ -0,0 +1,256 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Handle symbols.
*
* Version: @(#)symbol.c 1.0.3 2023/04/22
*
* Authors: Fred N. van Kempen, <waltje@varcem.com>
* Bernd B”ckmann, <https://codeberg.org/boeckmann/asm6502>
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "global.h"
static symbol_t *symbols = NULL; // global symbol table
/* Create a new symbol, initialize to defaults. */
static symbol_t *
sym_new(const char *name)
{
symbol_t *sym;
sym = malloc(sizeof(symbol_t));
if (sym == NULL)
error(ERR_MEM, name);
memset(sym, 0x00, sizeof(symbol_t));
strcpy(sym->name, name);
return sym;
}
/* Return the first entry of the symbol table. */
symbol_t *
sym_table(void)
{
return symbols;
}
/* Delete all entries from a symbol table. */
void
sym_free(symbol_t **table)
{
symbol_t *sym, *next;
if (table == NULL)
table = &symbols;
sym = *table;
while (sym != NULL) {
if (sym->locals != NULL)
sym_free(&sym->locals);
next = sym->next;
free(sym);
sym = next;
}
*table = NULL;
}
/* Look up a symbol in one of the tables. */
symbol_t *
sym_lookup(const char *name, symbol_t **table)
{
symbol_t *ptr;
if (table == NULL)
table = &symbols;
for (ptr = *table; ptr != NULL; ptr = ptr->next) {
if (! strcmp(name, ptr->name))
return ptr;
}
return NULL;
}
char
sym_type(const symbol_t *sym)
{
switch (sym->kind) {
case KIND_LBL:
return 'L';
case KIND_VAR:
return 'V';
}
return '-';
}
symbol_t *
sym_aquire(const char *name, symbol_t **table)
{
symbol_t *ptr, *sym;
if (table == NULL)
table = &symbols;
sym = sym_lookup(name, table);
if (sym == NULL) {
sym = sym_new(name);
/* Insert symbol in alphabetical order. */
if ((*table == NULL) || (strcasecmp((*table)->name, name) > 0)) {
sym->next = *table;
*table = sym;
} else {
for (ptr = *table; ptr->next != NULL; ptr = ptr->next) {
if (strcasecmp(ptr->next->name, name) > 0)
break;
}
sym->next = ptr->next;
ptr->next = sym;
}
}
return sym;
}
symbol_t *
define_label(const char *id, uint16_t v, symbol_t *parent)
{
char nid[ID_LEN];
symbol_t *sym;
if (parent != NULL) {
if (*id == DOT_CHAR) {
/*
* This is a "dot label", meaning, its name
* has to be tacked on to the preceeding
* global label's name.
*/
if ((strlen(parent->name) + strlen(id)) >= ID_LEN)
error(ERR_IDLEN, id);
strcpy(nid, parent->name);
strcat(nid, id);
id = nid;
}
sym = sym_aquire(id, &parent->locals);
} else
sym = sym_aquire(id, NULL);
if (IS_VAR(sym) || (DEFINED(sym->value) && (sym->value.v != v)))
error(parent ? ERR_LOCAL_REDEF : ERR_REDEF, id);
sym->kind = KIND_LBL;
sym->filenr = filenames_idx;
sym->linenr = line;
sym->value.v = v;
sym->value.t = ((TYPE(sym->value) == TYPE_WORD)
? TYPE_WORD : NUM_TYPE(v)) | VALUE_DEFINED;
return sym;
}
void
define_variable(const char *id, value_t v)
{
symbol_t *sym;
sym = sym_aquire(id, NULL);
if (DEFINED(sym->value) && (sym->value.v != v.v))
error(ERR_REDEF, id);
sym->kind = KIND_VAR;
sym->filenr = filenames_idx;
sym->linenr = line;
/* if the type is already set do not change it */
sym->value.v = v.v;
if (TYPE(sym->value)) {
#if 0
if (NUM_TYPE(v.v) > TYPE(sym->value)) error(ERR_REDEF, id);
#endif
if (DEFINED(v))
SET_DEFINED(sym->value);
} else
sym->value.t = v.t;
/* If previously defined as label make it word sized. */
if (IS_LBL(sym))
SET_TYPE(sym->value, TYPE_WORD);
}
const char *
sym_print(const symbol_t *sym)
{
static char buff[32];
switch (TYPE(sym->value)) {
case TYPE_BYTE:
sprintf(buff, "%02X", sym->value.v & 0xff);
break;
case TYPE_WORD:
sprintf(buff, "%04X", sym->value.v & 0xffff);
break;
case TYPE_DWORD:
sprintf(buff, "%08X", sym->value.v);
break;
}
return buff;
}

180
src/target.c Normal file
View File

@@ -0,0 +1,180 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Handle selection of a target device.
*
* Version: @(#)target.c 1.0.1 2023/04/20
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2023 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "global.h"
#include "target.h"
extern const target_t t_6502_old,
t_6502_nmos,
t_csg6510,
t_csg8500,
t_65c02;
static const target_t *targets[] = {
&t_6502_old, // original MOS6502 (NMOS)
&t_6502_nmos, // standard MOS6502 (NMOS)
&t_csg6510, // CSG 6510 (NMOS)
&t_csg8500, // CSG 8500 (NMOS)
&t_65c02, // standard MOS65C02 (CMOS)
NULL
};
static const target_t *target = NULL;
/*
* Select a specific CPU or model.
*
* Called once to allow the back-end to set up the proper tables
* and such, which may depend on the exact processor model used.
*/
int
set_cpu(const char *p, int pass)
{
const target_t **t;
if (opt_v && (pass == 1))
printf("Setting processor to '%s'\n", p);
target = NULL;
for (t = targets; *t != NULL; t++) {
if (! strcasecmp((*t)->name, p)) {
target = *t;
/* Create a predefined "CPU" symbol. */
trg_symbol(target->name);
return 1;
}
}
return 0;
}
/* Create a target-specific symbol. */
void
trg_symbol(const char *name)
{
char id[ID_LEN+1];
value_t v = { 0 };
int i = 0;
id[i++] = 'P';
do {
id[i++] = (char)toupper(*name++);
if (i >= ID_LEN)
error(ERR_IDLEN, NULL);
} while (*name != '\0');
id[i] = '\0';
v.v = 1;
SET_DEFINED(v);
SET_TYPE(v, TYPE_BYTE);
define_variable(id, v);
}
/* Get a target-specific error message. */
const char *
trg_error(int err)
{
if (target == NULL)
error(ERR_NOCPU, NULL);
return target->error(err);
}
/*
* Process one instruction.
*
* This gets called by the main code, once per pass. Its task is
* to process an entire instruction line, or, at least, the part
* of the line where the assembler thinks the "code" is. So, in
* most cases, we get called with "p" pointing to the very start
* of the code and we can begin parsing right away.
*
* If we find an error, we simply call the error() function, and
* that will properly terminate all processing for us.
*
* During pass 2, we will generate any code bytes, which we emit
* by calling the emit() function.
*/
int
trg_instr(char **p, int pass)
{
if (target == NULL)
error(ERR_NOCPU, NULL);
return target->instr(target, p, pass);
}
/*
* Check to see if the identifier matches an existing mnemonic.
*
* The assembler will not allow the use of these "reserved words"
* as label names.
*/
int
trg_instr_ok(const char *p)
{
if (target == NULL)
error(ERR_NOCPU, NULL);
return target->instr_ok(target, p);
}

73
src/target.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Definitions for the target backends.
*
* Version: @(#)target.h 1.0.1 2023/04/20
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2023 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TARGET_H
# define TARGET_H
typedef struct target {
const char *name;
uint32_t flags;
const void *priv;
int priv2;
const char *(*error)(int);
int (*instr)(const struct target *, char **, int);
int (*instr_ok)(const struct target *, const char *);
} target_t;
extern int trg_set_cpu(const char *);
extern void trg_symbol(const char *);
extern const char *trg_error(int);
extern int trg_instr(char **, int);
extern int trg_instr_ok(const char *);
#endif /*TARGET_H*/

758
src/targets/mos6502.c Normal file
View File

@@ -0,0 +1,758 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Implement the MOS6502 series of device targets.
*
* We keep two copies of the opcode table - one for the original
* NMOS version, which was also used by a number of chips done
* by CSG (Commodore) for their systems, and one for the CMOS
* version produced later. The CMOS version also has variants
* from Rockwell and WDC, with even more changes.
*
* Version: @(#)mos6502.c 1.0.3 2023/04/18
*
* Authors: Fred N. van Kempen, <waltje@varcem.com>
* Bernd B”ckmann, <https://codeberg.org/boeckmann/asm6502>
*
* Copyright 2023 Fred N. van Kempen.
* Copyright 2022,2023 Bernd B”ckmann.
*
* MIT License Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* condition:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "../global.h"
#include "../target.h"
#define AM_NUM 15 // number of addressing modes
# define AM_ACC 0 // A
# define AM_IMP 1 // (none)
# define AM_IMM 2 // #$12
# define AM_REL 3 // LABEL
# define AM_ZP 4 // $12
# define AM_ZPI 5 // ($12) (C02)
# define AM_ZPR 6 // ($12),LABEL (WDC C02)
# define AM_ZPX 7 // $12,X
# define AM_ZPY 8 // $12,Y
# define AM_ABS 9 // $1234
# define AM_ABX 10 // $1234,X
# define AM_ABY 11 // $1234,Y
# define AM_IND 12 // ($1234)
# define AM_INX 13 // ($1234,X)
# define AM_INY 14 // ($1234),Y
#define AM_INV -1
#define INV 0xff // FIXME: wont work with C02 etc!
#define AM_VALID(op, am) (op->opc[am] != INV)
typedef enum {
ERR_AM = ERR_MAXERR, // "invalid addressing mode"
ERR_REG, // "invalid register"
ERR_ILLAM, // "malformed addressing mode"
ERR_INX, // "malformed indirect X addressing"
ERR_INY, // "malformed indirect Y addressing"
ERR_OPUNDEFT, // "undefined operand size"
ERR_RELRNG, // "relative jump target out of range"
ERR_MAXTRG // last local error message
} trg_errors_t;
#define CPU_NMOS_0 0x00 // first NMOS processor, ROR bug
#define CPU_NMOS_1 0x01 // regular NMOS core
#define CPU_CMOS 0x02 // standard CMOS core
#define CPU_CMOS_1 0x04 // Rockwell CMOS
#define CPU_CMOS_2 0x08 // WDC CMOS
typedef struct opcode {
char mn[4];
uint8_t flags;
uint8_t opc[AM_NUM];
} opcode_t;
static const opcode_t opc_nmos[] = {
{ "ADC", CPU_NMOS_0,
{INV ,INV ,0x69,INV ,0x65,INV ,INV ,0x75,INV ,0x6d,0x7d,0x79,INV ,0x61,0x71}},
{ "AND", CPU_NMOS_0,
{INV ,INV ,0x29,INV ,0x25,INV ,INV ,0x35,INV ,0x2d,0x3d,0x39,INV ,0x21,0x31}},
{ "ASL", CPU_NMOS_0,
{0x0a,INV ,INV ,INV ,0x06,INV ,INV ,0x16,INV ,0x0e,0x1e,INV ,INV ,INV ,INV }},
{ "BCC", CPU_NMOS_0,
{INV ,INV ,INV ,0x90,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BCS", CPU_NMOS_0,
{INV ,INV ,INV ,0xb0,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BEQ", CPU_NMOS_0,
{INV ,INV ,INV ,0xf0,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BIT", CPU_NMOS_0,
{INV ,INV ,INV ,INV ,0x24,INV ,INV ,INV ,INV ,0x2c,INV ,INV ,INV ,INV ,INV }},
{ "BMI", CPU_NMOS_0,
{INV ,INV ,INV ,0x30,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BNE", CPU_NMOS_0,
{INV ,INV ,INV ,0xd0,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BPL", CPU_NMOS_0,
{INV ,INV ,INV ,0x10,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BRK", CPU_NMOS_0,
{INV ,0x00,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BVC", CPU_NMOS_0,
{INV ,INV ,INV ,0x50,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BVS", CPU_NMOS_0,
{INV ,INV ,INV ,0x70,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CLC", CPU_NMOS_0,
{INV ,0x18,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CLD", CPU_NMOS_0,
{INV ,0xd8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CLI", CPU_NMOS_0,
{INV ,0x58,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CLV", CPU_NMOS_0,
{INV ,0xb8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CMP", CPU_NMOS_0,
{INV ,INV ,0xc9,INV ,0xc5,INV ,INV ,0xd5,INV ,0xcd,0xdd,0xd9,INV ,0xc1,0xd1}},
{ "CPX", CPU_NMOS_0,
{INV ,INV ,0xe0,INV ,0xe4,INV ,INV ,INV ,INV ,0xec,INV ,INV ,INV ,INV ,INV }},
{ "CPY", CPU_NMOS_0,
{INV ,INV ,0xc0,INV ,0xc4,INV ,INV ,INV ,INV ,0xcc,INV ,INV ,INV ,INV ,INV }},
{ "DEC", CPU_NMOS_0,
{INV ,INV ,INV ,INV ,0xc6,INV ,INV ,0xd6,INV ,0xce,0xde,INV ,INV ,INV ,INV }},
{ "DEX", CPU_NMOS_0,
{INV ,0xca,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "DEY", CPU_NMOS_0,
{INV ,0x88,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "EOR", CPU_NMOS_0,
{INV ,INV ,0x49,INV ,0x45,INV ,INV ,0x55,INV ,0x4d,0x5d,0x59,INV ,0x41,0x51}},
{ "INC", CPU_NMOS_0,
{INV ,INV ,INV ,INV ,0xe6,INV ,INV ,0xf6,INV ,0xee,0xfe,INV ,INV ,INV ,INV }},
{ "INX", CPU_NMOS_0,
{INV ,0xe8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "INY", CPU_NMOS_0,
{INV ,0xc8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{"JMP", CPU_NMOS_0,
{INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,0x4c,INV ,INV ,0x6c,INV ,INV }},
{ "JSR", CPU_NMOS_0,
{INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,0x20,INV ,INV ,INV ,INV ,INV }},
{ "LDA", CPU_NMOS_0,
{INV ,INV ,0xa9,INV ,0xa5,INV ,INV ,0xb5,INV ,0xad,0xbd,0xb9,INV ,0xa1,0xb1}},
{ "LDX", CPU_NMOS_0,
{INV ,INV ,0xa2,INV ,0xa6,INV ,INV ,INV ,0xb6,0xae,INV ,0xbe,INV ,INV ,INV }},
{ "LDY", CPU_NMOS_0,
{INV ,INV ,0xa0,INV ,0xa4,INV ,INV ,0xb4,INV ,0xac,0xbc,INV ,INV ,INV ,INV }},
{ "LSR", CPU_NMOS_0,
{0x4a,INV ,INV ,INV ,0x46,INV ,INV ,0x56,INV ,0x4e,0x5e,INV ,INV ,INV ,INV }},
{ "NOP", CPU_NMOS_0,
{INV ,0xea,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "ORA", CPU_NMOS_0,
{INV ,INV ,0x09,INV ,0x05,INV ,INV ,0x15,INV ,0x0d,0x1d,0x19,INV ,0x01,0x11}},
{ "PHA", CPU_NMOS_0,
{INV ,0x48,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PHP", CPU_NMOS_0,
{INV ,0x08,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PLA", CPU_NMOS_0,
{INV ,0x68,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PLP", CPU_NMOS_0,
{INV ,0x28,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "ROL", CPU_NMOS_0,
{0x2a,INV ,INV ,INV ,0x26,INV ,INV ,0x36,INV ,0x2e,0x3e,INV ,INV ,INV ,INV }},
{ "ROR", CPU_NMOS_1,
{0x6a,INV ,INV ,INV ,0x66,INV ,INV ,0x76,INV ,0x6e,0x7e,INV ,INV ,INV ,INV }},
{ "RTI", CPU_NMOS_0,
{INV ,0x40,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "RTS", CPU_NMOS_0,
{INV ,0x60,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "SBC", CPU_NMOS_0,
{INV ,INV ,0xe9,INV ,0xe5,INV ,INV ,0xf5,INV ,0xed,0xfd,0xf9,INV ,0xe1,0xf1}},
{ "SEC", CPU_NMOS_0,
{INV ,0x38,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "SED", CPU_NMOS_0,
{INV ,0xf8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "SEI", CPU_NMOS_0,
{INV ,0x78,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "STA", CPU_NMOS_0,
{INV ,INV ,INV ,INV ,0x85,INV ,INV ,0x95,INV ,0x8d,0x9d,0x99,INV ,0x81,0x91}},
{ "STX", CPU_NMOS_0,
{INV ,INV ,INV ,INV ,0x86,INV ,INV ,INV ,0x96,0x8e,INV ,INV ,INV ,INV ,INV }},
{ "STY", CPU_NMOS_0,
{INV ,INV ,INV ,INV ,0x84,INV ,INV ,0x94,INV ,0x8c,INV ,INV ,INV ,INV ,INV }},
{ "TAX", CPU_NMOS_0,
{INV ,0xaa,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TAY", CPU_NMOS_0,
{INV ,0xa8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TSX", CPU_NMOS_0,
{INV ,0xba,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TXA", CPU_NMOS_0,
{INV ,0x8a,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TXS", CPU_NMOS_0,
{INV ,0x9a,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TYA", CPU_NMOS_0,
{INV ,0x98,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }}
};
static const opcode_t opc_cmos[] = {
{ "ADC", CPU_CMOS,
{INV ,INV ,0x69,INV ,0x65,0x72,INV ,0x75,INV ,0x6d,0x7d,0x79,INV ,0x61,0x71}},
{ "AND", CPU_CMOS,
{INV ,INV ,0x29,INV ,0x25,0x32,INV ,0x35,INV ,0x2d,0x3d,0x39,INV ,0x21,0x31}},
{ "ASL", CPU_CMOS,
{0x0a,INV ,INV ,INV ,0x06,INV ,INV ,0x16,INV ,0x0e,0x1e,INV ,INV ,INV ,INV }},
{ "BBR", CPU_CMOS,
{INV ,INV ,INV ,INV ,INV ,INV ,0x0f,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BBS", CPU_CMOS,
{INV ,INV ,INV ,INV ,INV ,INV ,0x8f,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BCC", CPU_CMOS,
{INV ,INV ,INV ,0x90,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BCS", CPU_CMOS,
{INV ,INV ,INV ,0xb0,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BEQ", CPU_CMOS,
{INV ,INV ,INV ,0xf0,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BIT", CPU_CMOS,
{INV ,INV ,INV ,INV ,0x24,INV ,INV ,INV ,INV ,0x2c,INV ,INV ,INV ,INV ,INV }},
{ "BMI", CPU_CMOS,
{INV ,INV ,INV ,0x30,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BNE", CPU_CMOS,
{INV ,INV ,INV ,0xd0,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BPL", CPU_CMOS,
{INV ,INV ,INV ,0x10,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BRA", CPU_CMOS,
{INV ,INV ,INV ,0x80,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BRK", CPU_CMOS,
{INV ,0x00,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BVC", CPU_CMOS,
{INV ,INV ,INV ,0x50,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "BVS", CPU_CMOS,
{INV ,INV ,INV ,0x70,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CLC", CPU_CMOS,
{INV ,0x18,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CLD", CPU_CMOS,
{INV ,0xd8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CLI", CPU_CMOS,
{INV ,0x58,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CLV", CPU_CMOS,
{INV ,0xb8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "CMP", CPU_CMOS,
{INV ,INV ,0xc9,INV ,0xc5,0xd2,INV ,0xd5,INV ,0xcd,0xdd,0xd9,INV ,0xc1,0xd1}},
{ "CPX", CPU_CMOS,
{INV ,INV ,0xe0,INV ,0xe4,INV ,INV ,INV ,INV ,0xec,INV ,INV ,INV ,INV ,INV }},
{ "CPY", CPU_CMOS,
{INV ,INV ,0xc0,INV ,0xc4,INV ,INV ,INV ,INV ,0xcc,INV ,INV ,INV ,INV ,INV }},
{ "DEC", CPU_CMOS,
{0x3a,INV ,INV ,INV ,0xc6,INV ,INV ,0xd6,INV ,0xce,0xde,INV ,INV ,INV ,INV }},
{ "DEX", CPU_CMOS,
{INV ,0xca,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "DEY", CPU_CMOS,
{INV ,0x88,INV ,INV ,INV ,0x52,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "EOR", CPU_CMOS,
{INV ,INV ,0x49,INV ,0x45,INV ,INV ,0x55,INV ,0x4d,0x5d,0x59,INV ,0x41,0x51}},
{ "INC", CPU_CMOS,
{0x1a,INV ,INV ,INV ,0xe6,INV ,INV ,0xf6,INV ,0xee,0xfe,INV ,INV ,INV ,INV }},
{ "INX", CPU_CMOS,
{INV ,0xe8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "INY", CPU_CMOS,
{INV ,0xc8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{"JMP", CPU_CMOS,
{INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,0x4c,0x7c,INV ,0x6c,INV ,INV }},
{ "JSR", CPU_CMOS,
{INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,0x20,INV ,INV ,INV ,INV ,INV }},
{ "LDA", CPU_CMOS,
{INV ,INV ,0xa9,INV ,0xa5,0xb2,INV ,0xb5,INV ,0xad,0xbd,0xb9,INV ,0xa1,0xb1}},
{ "LDX", CPU_CMOS,
{INV ,INV ,0xa2,INV ,0xa6,INV ,INV ,INV ,0xb6,0xae,INV ,0xbe,INV ,INV ,INV }},
{ "LDY", CPU_CMOS,
{INV ,INV ,0xa0,INV ,0xa4,INV ,INV ,0xb4,INV ,0xac,0xbc,INV ,INV ,INV ,INV }},
{ "LSR", CPU_CMOS,
{0x4a,INV ,INV ,INV ,0x46,INV ,INV ,0x56,INV ,0x4e,0x5e,INV ,INV ,INV ,INV }},
{ "NOP", CPU_CMOS,
{INV ,0xea,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "ORA", CPU_CMOS,
{INV ,INV ,0x09,INV ,0x05,0x12,INV ,0x15,INV ,0x0d,0x1d,0x19,INV ,0x01,0x11}},
{ "PHA", CPU_CMOS,
{INV ,0x48,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PHP", CPU_CMOS,
{INV ,0x08,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PHX", CPU_CMOS,
{INV ,0xda,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PHY", CPU_CMOS,
{INV ,0x5a,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PLA", CPU_CMOS,
{INV ,0x68,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PLP", CPU_CMOS,
{INV ,0x28,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PLX", CPU_CMOS,
{INV ,0xfa,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "PLY", CPU_CMOS,
{INV ,0x7a,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "RMB", CPU_CMOS,
{INV ,INV ,INV ,INV ,INV ,INV ,0x07,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "ROL", CPU_CMOS,
{0x2a,INV ,INV ,INV ,0x26,INV ,INV ,0x36,INV ,0x2e,0x3e,INV ,INV ,INV ,INV }},
{ "ROR", CPU_NMOS_1,
{0x6a,INV ,INV ,INV ,0x66,INV ,INV ,0x76,INV ,0x6e,0x7e,INV ,INV ,INV ,INV }},
{ "RTI", CPU_CMOS,
{INV ,0x40,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "RTS", CPU_CMOS,
{INV ,0x60,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "SBC", CPU_CMOS,
{INV ,INV ,0xe9,INV ,0xe5,0xf2,INV ,0xf5,INV ,0xed,0xfd,0xf9,INV ,0xe1,0xf1}},
{ "SEC", CPU_CMOS,
{INV ,0x38,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "SED", CPU_CMOS,
{INV ,0xf8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "SEI", CPU_CMOS,
{INV ,0x78,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "SMB", CPU_CMOS,
{INV ,INV ,INV ,INV ,INV ,INV ,0x87,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "STA", CPU_CMOS,
{INV ,INV ,INV ,INV ,0x85,0x92,INV ,0x95,INV ,0x8d,0x9d,0x99,INV ,0x81,0x91}},
{ "STP", CPU_CMOS|CPU_CMOS_2,
{INV ,0xdb,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "STX", CPU_CMOS,
{INV ,INV ,INV ,INV ,0x86,INV ,INV ,INV ,0x96,0x8e,INV ,INV ,INV ,INV ,INV }},
{ "STY", CPU_CMOS,
{INV ,INV ,INV ,INV ,0x84,INV ,INV ,0x94,INV ,0x8c,INV ,INV ,INV ,INV ,INV }},
{ "STZ", CPU_CMOS,
{INV ,INV ,INV ,INV ,0x64,INV ,INV ,0x74,INV ,0x9c,0x9e,INV ,INV ,INV ,INV }},
{ "TAX", CPU_CMOS,
{INV ,0xaa,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TAY", CPU_CMOS,
{INV ,0xa8,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TRB", CPU_CMOS,
{INV ,INV ,INV ,INV ,0x14,INV ,INV ,INV ,INV ,0x1c,INV ,INV ,INV ,INV ,INV }},
{ "TSB", CPU_CMOS,
{INV ,INV ,INV ,INV ,0x04,INV ,INV ,INV ,INV ,0x0c,INV ,INV ,INV ,INV ,INV }},
{ "TSX", CPU_CMOS,
{INV ,0xba,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TXA", CPU_CMOS,
{INV ,0x8a,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TXS", CPU_CMOS,
{INV ,0x9a,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "TYA", CPU_CMOS,
{INV ,0x98,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }},
{ "WAI", CPU_CMOS|CPU_CMOS_2,
{INV ,0xcb,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV ,INV }}
};
static const uint16_t am_size[AM_NUM] = { 1,1,2,2,2,2,2,2,2,3,3,3,3,2,2 };
static const char *err_msg[] = {
"invalid addressing mode",
"invalid register",
"malformed indirect X addressing",
"malformed indirect Y addressing",
"malformed addressing mode",
"undefined operand size",
"relative jump target out of range"
};
/* Handle the Implied and Accumulator modes. */
static int
op_imp_acc(char **p, int pass, const opcode_t *instr)
{
char id[ID_LEN];
int am = AM_IMP;
char *str = *p;
/* We only handle Implied and Accumulator. */
if ((instr->opc[AM_ACC] == INV) &&
(instr->opc[AM_IMP] == INV)) return AM_INV;
/* See if we have a register name. */
if (! IS_END(**p)) {
/* Looks like it, get it. */
upcase(p, id);
/* We only allow the A register. */
if (strcmp(id, "A")) {
*p = str;
#if 0
/* Not an error, we are TRYING these to AMs.. */
error(ERR_REG, id);
#else
return AM_INV;
#endif
}
/* OK, all good. */
}
if (instr->opc[AM_ACC] != INV)
am = AM_ACC;
else if (instr->opc[AM_IMP] != INV)
am = AM_IMP;
else
error(ERR_AM, NULL);
emit_byte(instr->opc[am], pass);
return am;
}
/* Handle the Immediate mode. */
static int
op_imm(char **p, int pass, const opcode_t *instr)
{
int am = AM_IMM;
value_t v;
(*p)++;
if (instr->opc[am] == INV) error(ERR_AM, NULL);
v = expr(p);
if (pass == 2)
if (UNDEFINED(v)) error(ERR_UNDEF, NULL);
emit_byte(instr->opc[am], pass);
emit_byte((uint8_t)to_byte(v).v, pass);
return am;
}
/* Handle the Relative mode. */
static int
op_rel(int pass, const opcode_t *instr, value_t v)
{
int am = AM_REL;
uint16_t pct = pc + 2u;
uint16_t off;
/* relative branch offsets are in 2-complement */
/* have to calculate it by hand avoiding implementation defined behaviour */
/* using unsigned int because int may not be in 2-complement */
if (pass == 2) {
if (UNDEFINED(v)) error(ERR_UNDEF, NULL);
if ((v.v >= pct) && ((uint16_t)(v.v - pct) > 0x7f))
error(ERR_RELRNG, NULL);
else if ((pct > v.v) && ((uint16_t)(pct - v.v) > 0x80))
error(ERR_RELRNG, NULL);
}
if (v.v >= pct)
off = v.v - pct;
else
off = (uint16_t)((~0) - (pct - v.v - 1));
emit_byte(instr->opc[am], pass);
emit_byte(off & 0xff, pass);
return am;
}
/* Handle the Indirect mode. */
static int
op_ind(char **p, int pass, const opcode_t *instr)
{
char id[ID_LEN];
int am = AM_INV;
value_t v;
(*p)++;
v = expr(p);
skip_white(p);
/* indirect X addressing mode? */
if (**p == ',') {
skip_curr_and_white(p);
ident_upcase(p, id);
if (strcmp(id, "X"))
error(ERR_INX, NULL);
am = AM_INX;
skip_white(p);
if (**p != ')')
error(ERR_CLBR, NULL);
skip_curr_and_white(p);
} else {
if (**p != ')')
error(ERR_CLBR, NULL);
skip_curr_and_white(p);
/* indirect Y addressing mode? */
if (**p == ',') {
skip_curr_and_white(p);
ident_upcase(p, id);
if (strcmp(id, "Y"))
error(ERR_INY, NULL);
am = AM_INY;
} else
am = AM_IND;
}
if ((instr->opc[am]) == INV)
error(ERR_AM, NULL);
if (pass == 2) {
if (UNDEFINED(v))
error(ERR_UNDEF, NULL);
if (((am == AM_INX) || am == (AM_INY)) && (TYPE(v) != TYPE_BYTE))
error(ERR_ILLTYPE, NULL);
}
emit_byte(instr->opc[am], pass);
if (am == AM_IND)
emit_word(v.v, pass);
else
emit_byte((uint8_t)to_byte(v).v, pass);
return am;
}
/* Handle the absolute X and Y, zeropage X and Y modes. */
static int
op_abxy_zpxy(char **p, int pass, const opcode_t *instr, value_t v)
{
char id[ID_LEN];
int am = AM_INV;
ident_upcase(p, id);
/* Test for absolute and zeropage X addressing. */
if (! strcmp(id, "X")) {
if ((TYPE(v) == TYPE_BYTE) && AM_VALID(instr, AM_ZPX))
am = AM_ZPX;
else if (AM_VALID(instr, AM_ABX))
am = AM_ABX;
else
error(ERR_AM, NULL);
}
/* Test for absolute and zeropage Y addressing. */
else if (! strcmp(id, "Y")) {
if ((TYPE(v) == TYPE_BYTE) && AM_VALID(instr, AM_ZPY))
am = AM_ZPY;
else if (AM_VALID(instr, AM_ABY))
am = AM_ABY;
else
error(ERR_AM, NULL);
}
else
error(ERR_AM, NULL);
if (pass == 2)
if (UNDEFINED(v)) error(ERR_UNDEF, NULL);
emit_byte(instr->opc[am], pass);
if ((am == AM_ZPX) || (am == AM_ZPY))
emit_byte((uint8_t)to_byte(v).v, pass);
else
emit_word(v.v, pass);
return am;
}
/* Handle the Absolute and Zeropage modes. */
static int
op_abs_zp(int pass, const opcode_t *instr, value_t v)
{
int am = AM_INV;
if ((TYPE(v) == TYPE_BYTE) && AM_VALID(instr, AM_ZP)) {
am = AM_ZP;
if (pass == 2) {
if (UNDEFINED(v)) error(ERR_UNDEF, NULL);
}
emit_byte(instr->opc[am], pass);
emit_byte((uint8_t)to_byte(v).v, pass);
} else if (AM_VALID(instr, AM_ABS)) {
am = AM_ABS;
if (pass == 2) {
if (UNDEFINED(v)) error(ERR_UNDEF, NULL);
}
emit_byte(instr->opc[am], pass);
emit_word(v.v, pass);
} else
error(ERR_AM, NULL);
return am;
}
/* Look up a mnemonic in the table. */
static const opcode_t *
get_mnemonic(const opcode_t *table, int size, const char *p)
{
int i;
for (i = 0; i < size; i++)
if (! strcmp(p, table[i].mn))
return &table[i];
return NULL;
}
/*
* Process one instruction.
*
* This gets called by the main code, once per pass. Its task is
* to process an entire instruction line, or, at least, the part
* of the line where the assembler thinks the "code" is. So, in
* most cases, we get called with "p" pointing to the very start
* of the code and we can begin parsing right away.
*
* If we find an error, we simply call the error() function, and
* that will properly terminate all processing for us.
*
* During pass 2, we will generate any code bytes, which we emit
* by calling the emit() function.
*/
static int
t_instr(const target_t *trg, char **p, int pass)
{
char id[ID_LEN];
const opcode_t *op;
int am = AM_INV;
value_t v;
/* Convert the mnemonic to uppercase. */
ident_upcase(p, id);
/* First get instruction for given mnemonic. */
op = get_mnemonic((const opcode_t *)trg->priv, trg->priv2, id);
if (op == NULL)
error(ERR_INSTR, NULL);
/* Found, so get addressing mode. */
skip_white_and_comment(p);
/* Try Accumulator or implied first. */
if ((am = op_imp_acc(p, pass, op)) != AM_INV) {
/* Good, we handled that. */
} else if (**p == '#') {
/* Immediate mode. */
am = op_imm(p, pass, op);
} else if (**p == '(') {
/* Indirect addressing modes. */
am = op_ind(p, pass, op);
} else {
/* Relative and absolute addressing modes. */
v = expr(p);
skip_white(p);
/* Relative instruction mode if instruction supports it. */
if (op->opc[AM_REL] != INV) {
am = op_rel(pass, op, v);
} else if (**p == ',') {
/* .. else we try the possible absolute addressing modes. */
skip_curr_and_white(p);
am = op_abxy_zpxy(p, pass, op, v);
} else {
/* Must be absolute or zeropage addressing. */
am = op_abs_zp(pass, op, v);
}
}
/* Bail out if that could not be done. */
if (am == AM_INV)
error(ERR_AM, id);
/* Return the amount of code generated. */
return am_size[am];
}
/*
* Check to see if the identifier matches an existing mnemonic.
*
* The assembler will not allow the use of these "reserved words"
* as label names.
*/
static int
t_instr_ok(const target_t *trg, const char *p)
{
char id[ID_LEN], *ptr;
const opcode_t *op;
/* Convert the mnemonic to uppercase. */
ptr = id;
while (*p != '\0')
*ptr++ = (char)toupper(*p++);
*ptr = '\0';
/* First get instruction for given mnemonic. */
op = get_mnemonic((const opcode_t *)trg->priv, trg->priv2, id);
if (op != NULL)
return 1;
return 0;
}
/*
* Return a local error message.
*/
static const char *
t_error(int err)
{
if (err < ERR_MAXTRG)
return err_msg[err - ERR_MAXERR];
return "??";
}
const target_t t_6502_old = {
"6502_old", CPU_CMOS,
opc_nmos, (sizeof(opc_nmos) / sizeof(opcode_t)),
t_error, t_instr, t_instr_ok
};
const target_t t_6502_nmos = {
"6502", CPU_NMOS_1,
opc_nmos, (sizeof(opc_nmos) / sizeof(opcode_t)),
t_error, t_instr, t_instr_ok
};
const target_t t_csg6510 = {
"6510", CPU_NMOS_1,
opc_nmos, (sizeof(opc_nmos) / sizeof(opcode_t)),
t_error, t_instr, t_instr_ok
};
const target_t t_csg8500 = {
"8500", CPU_NMOS_1,
opc_nmos, (sizeof(opc_nmos) / sizeof(opcode_t)),
t_error, t_instr, t_instr_ok
};
const target_t t_65c02 = {
"65c02", CPU_CMOS,
opc_cmos, (sizeof(opc_cmos) / sizeof(opcode_t)),
t_error, t_instr, t_instr_ok
};

280
src/unix/Makefile.GCC Normal file
View File

@@ -0,0 +1,280 @@
#
# VASM VARCem Multi-Target Assembler.
# A simple table-driven assembler for several 8-bit target
# devices, like the 6502, 6800, 80x, Z80 et al series. The
# code originated from Bernd B”ckmann's "asm6502" project.
#
# This file is part of the VARCem Project.
#
# Makefile for UNIX-like systems using the GCC environment.
#
# Version: @(#)Makefile.GCC 1.1.0 2023/04/22
#
# Author: Fred N. van Kempen, <waltje@varcem.com>
#
# Copyright 2023 Fred N. van Kempen.
#
# Redistribution and use in source and binary forms, with
# or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the entire
# above notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the
# following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names
# of its contributors may be used to endorse or promote
# products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
ifndef ARCH
ARCH := x86
endif
ifndef DEBUG
DEBUG := n
endif
# Where is the VARCem tree?
VARCEM := ../../../..
# Where is our Ihex support library?
IHEX := $(VARCEM)/contrib/Ihex
INCS += -I$(IHEX)/include
LDIR += -L$(IHEX)/lib/$(ARCH)/linux
LDLIBS += -lihex #_s
DEFS := -DUSE_IHEX -DALLOW_UNDEFINED_IF
#########################################################################
# Nothing should need changing from here on.. #
#########################################################################
# Import the various platform/architecture names.
ifdef MAKEFILE_LIST
#TOPDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
TOPDIR := /project/VARCem/src/build
TOPFILE := $(abspath $(firstword $(MAKEFILE_LIST)))
else
TOPDIR := ./build
TOPFILE := Makefile.GCC
endif
include $(TOPDIR)/OSDetect.mk
include $(TOPDIR)/archs.mk
# Definitions for this enivonment.
DEVENV := gcc
DEPS = -MMD -MF $*.d
DEPFILE := .depends-$(DEVENV)
# Compilation for X86.
ifeq ($(ARCH), x86)
TARGET := 32
PREFIX :=
RCTARGET := pe-i386
AFLAGS := -msse2 -mfpmath=sse
ifeq ($(OPTIM), y)
DFLAGS := -march=native
else
DFLAGS := -march=i686
endif
endif
# Compilation for X64.
ifeq ($(ARCH), x64)
TARGET := 64
PREFIX :=
RCTARGET := pe-x86-64
ifeq ($(OPTIM), y)
DFLAGS := -march=native
endif
endif
# Compilation for ARM.
ifeq ($(ARCH), armv7)
error Target ARM7 not supported yet !
endif
# Compilation for ARM64.
ifeq ($(ARCH), aarch64)
error Target ARM64 not supported yet !
endif
# The various toolchain programs.
CC := gcc -m$(TARGET)
CXX := g++ -m$(TARGET)
CPP := cpp -P
LINK := gcc -m$(TARGET)
AR := ar
RANLIB := ranlib
STRIP := strip
ifndef CAT
CAT := cat
endif
# Set up the correct toolchain flags.
OPTS := -DARCH=$(ARCH) \
-D_CRT_NON_CONFORMING_SWPRINTFS
AOPTS := r
LOPTS :=
# General build options.
ifeq ($(DEBUG), y)
DFLAGS += -ggdb -D_DEBUG
AOPTIM :=
ifndef COPTIM
COPTIM := -Og
endif
else
ifeq ($(OPTIM), y)
AOPTIM := -mtune=native
ifndef COPTIM
COPTIM := -O3
endif
else
ifndef COPTIM
COPTIM := -O3
endif
endif
endif
OPTS +=
ifeq ($(PROFILER), y)
LOPTS += -Xlinker -Map=$(PROG).map
endif
# Final fixups.
INCS += -Iunix
# Final versions of the toolchain flags.
CFLAGS = $(WX_FLAGS) $(OPTS) $(DFLAGS) $(COPTIM) $(AOPTIM) \
$(AFLAGS) $(INCS) $(DEFS) -mstackrealign \
-fomit-frame-pointer -fno-strict-aliasing \
-Wall -Wundef #-Wmissing-declarations
LDFLAGS := $(LOPTS) $(DFLAGS) $(LDIR)
ARFLAGS := $(AOPTS)
# Build module rules.
.SUFFIXES: .c .cpp .o
ifeq ($(AUTODEP), y)
%.o: %.c
@echo Compiling $<
@$(CC) $(CFLAGS) $(DEPS) -c $<
else
%.o: %.c
@echo Compiling $<
@$(CC) $(CFLAGS) -c $<
%.d: %.c $(wildcard $*.d)
@echo Compiling $<
@$(CC) $(CFLAGS) $(DEPS) -E $< >NUL
endif
#########################################################################
# Nothing should need changing from here on.. #
#########################################################################
VPATH := unix . targets
#LIBS :=
#LOBJ :=
PROG := vasm
SYSOBJ :=
OBJ := $(SYSOBJ) \
main.o symbol.o expr.o func.o input.o output.o list.o \
parse.o pseudo.o \
target.o \
mos6502.o
all: $(LIBS) $(PROG)
libfoo.dll.a libfoo.so: $(LOBJ)
@echo Linking shared library $@ ..
@$(LINK) $(LDFLAGS) -shared -o $@ \
$(LOBJ)
$(if $(filter $(DEBUG),y),,@$(STRIP) --strip-unneeded $@)
libfoo.a: $(LOBJ)
@echo Creating static library $@ ..
@$(AR) $(ARFLAGS) $@ $(LOBJ)
@$(RANLIB) $@
vasm: $(OBJ)
@echo Linking $@ ..
@$(LINK) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(LDLIBS)
$(if $(filter $(DEBUG),y),,@$(STRIP) $@)
test: tests/hello.asm
@echo Assembling hello.asm ..
@asm6502 -v -o hello $<
clean:
@echo Cleaning objects..
@-rm -f *.o
@-rm -f *.d
@echo Cleaning resources..
clobber: clean
@echo Removing executables..
@-rm -f $(PROG)
@echo Cleaning libraries..
@-rm -f *.so
@-rm -f *.a
# @-rm -f $(DEPFILE)
depclean:
@-del $(DEPFILE)
@echo Creating dependencies..
@echo # Run "make depends" to re-create this file. >$(DEPFILE)
depends: DEPOBJ=$(OBJ:%.o=%.d)
depends: depclean $(DEPOBJ)
@$(CAT) $(DEPOBJ) >>$(DEPFILE)
@-del $(DEPOBJ)
$(DEPFILE):
# Module dependencies.
ifeq ($(AUTODEP), y)
-include *.d
else
ifneq ($(wildcard $(DEPFILE)), )
include $(wildcard $(DEPFILE))
endif
endif
# End of Makefile.GCC.

79
src/version.h Normal file
View File

@@ -0,0 +1,79 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Define application version and build info.
*
* Version: @(#)version.h 1.0.4 2023/04/22
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2023 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef VERSION_H
# define VERSION_H
/* Application name. */
#define APP_NAME "VARCem VASM"
#define APP_TITLE "VARCem Multi-Target Assembler"
/* Version info. */
#define APP_VER_MAJOR 1
#define APP_VER_MINOR 0
#define APP_VER_REV 12
#define APP_VER_PATCH 0
/* Standard C preprocessor macros. */
#define STR_STRING(x) #x
#define STR(x) STR_STRING(x)
#define STR_RC(a,e) a ## , ## e
/* These are used in the application. */
#define APP_VER_NUM APP_VER_MAJOR.APP_VER_MINOR.APP_VER_REV
#if defined(APP_VER_PATCH) && APP_VER_PATCH > 0
# define APP_VER_NUM_4 APP_VER_MAJOR.APP_VER_MINOR.APP_VER_REV.APP_VER_PATCH
#else
# define APP_VER_NUM_4 APP_VER_MAJOR.APP_VER_MINOR.APP_VER_REV.0
#endif
#define APP_VERSION STR(APP_VER_NUM)
#define APP_VERSION_4 STR(APP_VER_NUM_4)
#endif /*VERSION_H*/

303
src/win/Makefile.MSVC Normal file
View File

@@ -0,0 +1,303 @@
#
# VARCem Virtual ARchaeological Computer EMulator.
# An emulator of (mostly) x86-based PC systems and devices,
# using the ISA,EISA,VLB,MCA and PCI system buses, roughly
# spanning the era between 1981 and 1995.
#
# This file is part of the VARCem Project.
#
# Makefile for Windows using Visual Studio 2019.
#
# Version: @(#)Makefile.MSVC 1.1.0 2023/04/24
#
# Author: Fred N. van Kempen, <waltje@varcem.com>
#
# Copyright 2023 Fred N. van Kempen.
#
# Redistribution and use in source and binary forms, with
# or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the entire
# above notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the
# following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names
# of its contributors may be used to endorse or promote
# products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
ifndef ARCH
ARCH := x86
endif
ifndef DEBUG
DEBUG := n
endif
# Where is the VARCem tree?
VARCEM := ../../../..
# Where is our Ihex support library?
IHEX := $(VARCEM)/contrib/Ihex
INCS += -I$(IHEX)/include -DUSE_IHEX=3
LDIR += -LIBPATH:$(IHEX)/lib/$(ARCH)
LDLIBS += ihex_s.lib
DEFS := -DALLOW_UNDEFINED_IF
#########################################################################
# Nothing should need changing from here on.. #
#########################################################################
# Definitions for this enivonment.
DEVENV := msvc
DEPS = -MMD -MF $*.d
DEPFILE := .depends-$(DEVENV)
# Compilation for Windows X86.
ifeq ($(ARCH), x86)
MACH := /MACHINE:$(ARCH)
# Allow for WinXP-SP3 builds in 32-bit mode.
LOPTS_OS := ,5.01
ifeq ($(OPTIM), y)
AFLAGS := /arch:AVX2
else
AFLAGS := /arch:SSE2
endif
endif
# Compilation for Windows X64.
ifeq ($(ARCH), x64)
MACH := /MACHINE:$(ARCH)
LOPTS_OS :=
endif
# Compilation for Windows ARM.
ifeq ($(ARCH), arm)
MACH := /MACHINE:arm
LOPTS_OS :=
# Static-link the VC runtime to avoid DLL problems on Windows RT.
VCRUNTIME := MT
endif
# Compilation for Windows ARM64.
ifeq ($(ARCH), arm64)
MACH := /MACHINE:arm64
LOPTS_OS :=
# fatal error LNK1322: cannot avoid potential ARM hazard (Cortex-A53
# MPCore processor bug #843419) in section #3; please consider using
# compiler option /Gy if it was not used.
COPTS += /Gy
endif
# The various toolchain programs.
CC := cl.exe /nologo
CXX := cl.exe /nologo
CPP := cl.exe /nologo /EP
LINK := link.exe /NOLOGO
WINDRES := rc.exe /NOLOGO
AR := lib.exe /NOLOGO $(MACH)
ifndef CAT
CAT := cat
endif
# Set up the correct toolchain flags.
OPTS := /DARCH=$(ARCH) \
/D_CRT_SECURE_NO_WARNINGS \
/D_CRT_NONSTDC_NO_DEPRECATE \
/D_USE_MATH_DEFINES \
/D_WINSOCK_DEPRECATED_NO_WARNINGS
COPTS += /W3
CXXOPTS += /EHsc
LOPTS := $(MACH)
ifndef VCRUNTIME
VCRUNTIME := MD
endif
ARFLAGS :=
LFLAGS :=
RCOPTS := /r /n /dRC_INVOKED /d_MSC_VER /fo
# General build options.
ifeq ($(DEBUG), y)
OPTS += -D_DEBUG
VCRUNTIME := $(VCRUNTIME)d
DOPTS = /Gs /Zi
RCOPTS := -D_DEBUG $(RCOPTS)
LOPTS += /DEBUG
ifndef COPTIM
COPTIM := /Od /Ob3
endif
else
LOPTS += /DEBUG:NONE #/LTCG
DOPTS = #/GL
ifndef COPTIM
COPTIM := /Ox /Ob3
endif
endif
COPTS += /$(VCRUNTIME)
ifeq ($(PROFILER), y)
LOPTS += /MAP
endif
MSVC_API_SHARED := -DSHARED
MSVC_API_STATIC := -DSTATIC
ifndef MSVC_NO_ISO_WIDE_SPECIFIERS
OPTS += /D_CRT_STDIO_ISO_WIDE_SPECIFIERS
endif
# Final versions of the toolchain flags.
CFLAGS = $(OPTS) $(DFLAGS) $(COPTIM) $(AOPTIM) \
$(AFLAGS) $(INCS) $(DEFS)
LFLAGS := $(LOPTS) $(DFLAGS) $(LDIR)
ARFLAGS := $(AOPTS)
RCFLAGS = $(RCOPTS)
# Build module rules.
.SUFFIXES: .c .cpp .obj .rc .res
ifeq ($(AUTODEP), y)
%.obj: %.c
@$(CC) $(CFLAGS) -Fo$@ $(MSVC_API_SHARED) -c $<
%.dbj: %.c
@$(CC) $(CFLAGS) -Fo$@ $(MSVC_API_STATIC) -c $<
%.obj: %.cpp
@$(CXX) $(CXXFLAGS) -Fo$@ $(MSVC_API_SHARED) -c $<
%.dbj: %.cpp
@$(CXX) $(CXXFLAGS) -Fo$@ $(MSVC_API_STATIC) -c $<
else
%.obj: %.c
@$(CC) $(CFLAGS) -Fo$@ $(MSVC_API_SHARED) -c $<
%.dbj: %.c
@$(CC) $(CFLAGS) -Fo$@ $(MSVC_API_STATIC) -c $<
%.obj: %.cpp
@$(CXX) $(CXXFLAGS) -Fo$@ $(MSVC_API_SHARED) -c $<
%.dbj: %.cpp
@$(CXX) $(CXXFLAGS) -Fo$@ $(MSVC_API_STATIC) -c $<
%.d: %.c $(wildcard $*.d)
@$(CXX) $(CXXFLAGS) -Fo$@ $(MSVC_API_SHARED) -c $<
endif
# For systems that use these, compile a resource file.
.rc.res: $<
@echo Processing $<
@$(WINDRES) $(RCFLAGS) $@ $<
#########################################################################
# Nothing should need changing from here on.. #
#########################################################################
# Final fixups.
INCS += -Iwin
VPATH := win . targets
#LIBS :=
#LOBJ :=
PROG := vasm.exe
SYSOBJ := vasm.res getopt.obj
OBJ := $(SYSOBJ) \
main.obj symbol.obj expr.obj func.obj input.obj output.obj \
list.obj parse.obj pseudo.obj \
target.obj \
mos6502.obj
LDLIBS += #advapi32.lib shell32.lib user32.lib kernel32.lib winmm.lib
all: $(LIBS) $(PROG)
libfoo.lib libfoo.dll: $(LOBJ)
@echo Linking shared library $@ ..
@$(LINK) $(LFLAGS) $(LOPTS_W) /DLL /OUT:$@ \
/IMPLIB:$@.lib $(LOBJ) $(LDLIBS)
libfoo_s.lib: $(LOBJ)
@echo Creating static library $@ ..
@$(AR) $(ARFLAGS) /OUT:$@ $(LOBJ)
vasm.exe: $(SYSOBJ) $(OBJ)
@echo Linking $@ ..
@$(LINK) $(LFLAGS) /SUBSYSTEM:CONSOLE$(LOPTS_OS) \
-OUT:$@ $(OBJ) $(LIBS) $(LDLIBS)
clean:
@echo Cleaning objects..
@-del *.obj 2>NUL
@-del *.d 2>NUL
@echo Cleaning resources..
@-del *.res 2>NUL
clobber: clean
@echo Removing executables..
@-del *.exe 2>NUL
@echo Cleaning libraries..
# @-del *.dll 2>NUL
@-del *.lib 2>NUL
@-del *.manifest 2>NUL
# @-del $(DEPFILE) 2>NUL
depclean:
@-del $(DEPFILE)
@echo Creating dependencies..
@echo # Run "make depends" to re-create this file. >$(DEPFILE)
depends: DEPOBJ=$(OBJ:%.obj=%.d)
depends: depclean $(DEPOBJ)
@$(CAT) $(DEPOBJ) >>$(DEPFILE)
@-del $(DEPOBJ)
$(DEPFILE):
# Module dependencies.
ifeq ($(AUTODEP), y)
-include *.d
else
ifneq ($(wildcard $(DEPFILE)), )
include $(wildcard $(DEPFILE))
endif
endif
# End of Makefile.MSVC.

288
src/win/Makefile.MinGW Normal file
View File

@@ -0,0 +1,288 @@
#
# VASM VARCem Multi-Target Assembler.
# A simple table-driven assembler for several 8-bit target
# devices, like the 6502, 6800, 80x, Z80 et al series. The
# code originated from Bernd B”ckmann's "asm6502" project.
#
# This file is part of the VARCem Project.
#
# Makefile for Windows systems using the MinGW-w64 environment.
#
# Version: @(#)Makefile.MinGW 1.1.0 2023/04/24
#
# Author: Fred N. van Kempen, <waltje@varcem.com>
#
# Copyright 2023 Fred N. van Kempen.
#
# Redistribution and use in source and binary forms, with
# or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the entire
# above notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the
# following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names
# of its contributors may be used to endorse or promote
# products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
ifndef ARCH
ARCH := x86
endif
ifndef DEBUG
DEBUG := n
endif
# Where is the VARCem tree?
VARCEM := ../../../..
# Where is our Ihex support library?
IHEX := $(VARCEM)/contrib/Ihex
INCS += -I$(IHEX)/include -DUSE_HEX=2
LDIR += -L$(IHEX)/lib/$(ARCH)
DEFS := -DALLOW_UNDEFINED_IF
#########################################################################
# Nothing should need changing from here on.. #
#########################################################################
# Definitions for this enivonment.
DEVENV := mingw
DEPS = -MMD -MF $*.d
DEPFILE := .depends-$(DEVENV)
# Compilation for X86.
ifeq ($(ARCH), x86)
TARGET := 32
PREFIX :=
RCTARGET := pe-i386
AFLAGS := -msse2 -mfpmath=sse
ifeq ($(OPTIM), y)
DFLAGS := -march=native
else
DFLAGS := -march=i686
endif
endif
# Compilation for X64.
ifeq ($(ARCH), x64)
TARGET := 64
PREFIX :=
RCTARGET := pe-x86-64
ifeq ($(OPTIM), y)
DFLAGS := -march=native
endif
endif
# Compilation for ARM.
ifeq ($(ARCH), armv7)
error Target ARM7 not supported for Windows !
endif
# Compilation for ARM64.
ifeq ($(ARCH), aarch64)
error Target ARM64 not supported for Windows !
endif
# The various toolchain programs.
CC := gcc -m$(TARGET)
CXX := g++ -m$(TARGET)
CPP := cpp -P
LINK := gcc -m$(TARGET)
WINDRES := windres
AR := ar
RANLIB := ranlib
STRIP := strip
ifndef CAT
CAT := cat
endif
# Set up the correct toolchain flags.
OPTS := -DARCH=$(ARCH) \
-D_CRT_NON_CONFORMING_SWPRINTFS \
-D__USE_MINGW_ANSI_STDIO_X
ifndef VCRUNTIME
# MinGW does not provide MD/MT yet.
VCRUNTIME := MD
endif
RCOPTS := --input-format=rc -O coff
ifdef RCTARGET
RCOPTS += --target=$(RCTARGET)
endif
RCOPTS += -D RC_INVOKED -o
AOPTS := r
LOPTS := -mconsole
# General build options.
ifeq ($(DEBUG), y)
VCRUNTIME := $(VCRUNTIME)d
DFLAGS += -ggdb -D_DEBUG
RCOPTS := -D_DEBUG $(RCOPTS)
AOPTIM :=
ifndef COPTIM
COPTIM := -Og
endif
else
ifeq ($(OPTIM), y)
AOPTIM := -mtune=native
ifndef COPTIM
COPTIM := -O3
endif
else
ifndef COPTIM
COPTIM := -O3
endif
endif
endif
OPTS += #/$(VCRUNTIME)
ifeq ($(PROFILER), y)
LOPTS += -Xlinker -Map=$(PROG).map
endif
# Final versions of the toolchain flags.
CFLAGS = $(OPTS) $(DFLAGS) $(COPTIM) $(AOPTIM) \
$(AFLAGS) $(INCS) $(DEFS) \
-mstackrealign \
-fomit-frame-pointer -fno-strict-aliasing \
-Wall -Wundef #-Wmissing-declarations
LDFLAGS := $(LOPTS) $(DFLAGS) $(LDIR)
ARFLAGS := $(AOPTS)
RCFLAGS = $(RCOPTS)
# Build module rules.
.SUFFIXES: .c .cpp .o .rc .res
ifeq ($(AUTODEP), y)
%.o: %.c
@echo Compiling $<
@$(CC) $(CFLAGS) $(DEPS) -c $<
else
%.o: %.c
@echo Compiling $<
@$(CC) $(CFLAGS) -c $<
%.d: %.c $(wildcard $*.d)
@echo Compiling $<
@$(CC) $(CFLAGS) $(DEPS) -E $< >NUL
endif
# For systems that use these, compile a resource file.
.rc.res: $<
@echo Processing $<
@$(WINDRES) $(RCFLAGS) $@ $<
#########################################################################
# Nothing should need changing from here on.. #
#########################################################################
# Final fixups.
INCS += -Iwin
VPATH := win . targets
#LIBS :=
#LOBJ :=
PROG := vasm.exe
SYSOBJ := vasm.res getopt.o
OBJ := $(SYSOBJ) \
main.o symbol.o expr.o func.o input.o output.o \
list.o parse.o pseudo.o \
target.o \
mos6502.o
all: $(LIBS) $(PROG)
libfoo.dll.a libfoo.dll: $(LOBJ)
@echo Linking shared library $@ ..
@$(LINK) $(LDFLAGS) -shared -o $@ \
-Wl,--subsystem=console \
-Wl,--out-implib,$@.a $(LOBJ)
$(if $(filter $(DEBUG),y),,@$(STRIP) --strip-unneeded $@)
libfoo.a: $(LOBJ)
@echo Creating static library $@ ..
@$(AR) $(ARFLAGS) $@ $(LOBJ)
@$(RANLIB) $@
vasm.exe: $(SYSOBJ) $(OBJ)
@echo Linking $@ ..
@$(LINK) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(LDLIBS)
$(if $(filter $(DEBUG),y),,@$(STRIP) $@)
clean:
@echo Cleaning objects..
@-del *.o 2>NUL
@-del *.d 2>NUL
@echo Cleaning resources..
@-del *.res 2>NUL
clobber: clean
@echo Removing executables..
@-del *.exe 2>NUL
@echo Cleaning libraries..
@-del *.dll 2>NUL
@-del *.a 2>NUL
@-del *.manifest 2>NUL
# @-del $(DEPFILE) 2>NUL
depclean:
@-del $(DEPFILE)
@echo Creating dependencies..
@echo # Run "make depends" to re-create this file. >$(DEPFILE)
depends: DEPOBJ=$(OBJ:%.o=%.d)
depends: depclean $(DEPOBJ)
@$(CAT) $(DEPOBJ) >>$(DEPFILE)
@-del $(DEPOBJ)
$(DEPFILE):
# Module dependencies.
ifeq ($(AUTODEP), y)
-include *.d
else
ifneq ($(wildcard $(DEPFILE)), )
include $(wildcard $(DEPFILE))
endif
endif
# End of Makefile.MinGW.

110
src/win/getopt.c Normal file
View File

@@ -0,0 +1,110 @@
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Portable GETOPT(3) function.
*
* This is a simple version of the UNIX-standard GetOpt(3)
* library routine. We don't support any of the GNU LibC
* enhancements such as long-name options, double-dash
* options and such. We keep it simple and small here.
*
* Version: @(#)getopt.c 1.0.1 2002/08/13
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 1998-2018 MicroWalt Corporation
* Copyright 2018-2023 The VARCem Team
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include "getopt.h"
char *optarg;
int optind = 0;
int opterr = 1;
int optopt;
static char *scan = (char *)NULL;
int
getopt(int argc, char **argv, const char *optstring)
{
char c;
char *place;
optarg = (char *)NULL;
if (scan == (char *)NULL || *scan == '\0') {
if (optind == 0) optind++;
if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
return(EOF);
if (strcmp(argv[optind], "--") == 0) {
optind++;
return(EOF);
}
scan = argv[optind] + 1;
optind++;
}
optopt = c = *scan++;
place = strchr(optstring, c);
if (place == (char *)NULL || c == ':') {
if (opterr) fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
return('?');
}
place++;
if (*place == ':') {
if (*scan != '\0') {
optarg = scan;
scan = (char *)NULL;
} else if (optind < argc) {
optarg = argv[optind];
optind++;
} else {
if (opterr)
fprintf(stderr,
"%s: -%c argument missing\n", argv[0], c);
return('?');
}
}
return(c);
}

68
src/win/getopt.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Portable GETOPT(3) function.
*
* Version: @(#)getopt.h 1.0.1 2002/08/13
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 1998-2018 MicroWalt Corporation
* Copyright 2018-2023 The VARCem Team
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef GETOPT_H
# define GETOPT_H
extern int optind,
opterr;
extern char *optarg;
#ifdef __cplusplus
extern "C" {
#endif
extern int getopt(int ac, char *av[], const char *);
#ifdef __cplusplus
}
#endif
#endif /*GETOPT_H*/

BIN
src/win/varcem.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

98
src/win/vasm.rc Normal file
View File

@@ -0,0 +1,98 @@
/*
* VASM VARCem Multi-Target Assembler.
* A simple table-driven assembler for several 8-bit target
* devices, like the 6502, 6800, 80x, Z80 et al series. The
* code originated from Bernd B”ckmann's "asm6502" project.
*
* This file is part of the VARCem Project.
*
* Application resource script for Windows.
*
* Version: @(#)vasm.rc 1.0.2 2023/04/24
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2023 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <windows.h>
#include "../version.h"
100 ICON "win/varcem.ico"
VS_VERSION_INFO VERSIONINFO
#ifdef APP_VER_PATCH
FILEVERSION APP_VER_MAJOR,APP_VER_MINOR,APP_VER_REV,APP_VER_PATCH
PRODUCTVERSION APP_VER_MAJOR,APP_VER_MINOR,APP_VER_REV,APP_VER_PATCH
#else
FILEVERSION APP_VER_MAJOR,APP_VER_MINOR,APP_VER_REV,0
PRODUCTVERSION APP_VER_MAJOR,APP_VER_MINOR,APP_VER_REV,0
#endif
FILEFLAGSMASK 0x3fL
#ifndef RELEASE_BUILD
# ifdef _DEBUG
FILEFLAGS VS_FF_SPECIALBUILD | VS_FF_DEBUG
# else
FILEFLAGS VS_FF_SPECIALBUILD
# endif
#else
# ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
# else
FILEFLAGS 0x0L
# endif
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "0409fde9"
BEGIN
VALUE "CompanyName", "The VARCem Group LLC"
VALUE "LegalCopyright", "See LICENSE for legal information."
VALUE "ProductName", "VARCem Support Tools"
VALUE "ProductVersion", APP_VERSION
VALUE "InternalName", APP_NAME
VALUE "FileDescription", APP_NAME
VALUE "FileVersion", APP_VERSION
VALUE "OriginalFilename", "vasm.exe"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 65001
END
END

View File

@@ -1,4 +1,12 @@
; C64 Hello World
.ifdef C64
.include "tests/c64_prg.asm"
.else
.cpu 6502
.org $0400
.endif
; assemble to .PRG file: vasm -o hello.prg c64.asm c64_hello.asm
CHROUT = $FFD2 ; kernal function address

View File

@@ -1,52 +0,0 @@
VARCem VASM version 1.0.10 (Windows, x86) 04/11/23 00:34:32 Page 1
File: h1.asm
1: ; Preamble to create a C64 .PRG file.
2:
3: SYS = $9E ; basic SYS token number
4:
5: LOAD = $0801 ; load address
6:
0000 0000 01 08 7: .word LOAD ; .PRG header: load address
8: .org LOAD
9:
10: basic: ; BASIC code: 10 SYS 2062
0002 0801 0C 08 0A 11: .word @end, 10 ; ptr to next basic line and line number 10
0006 0805 9E 20 32 ... 12: .byte SYS, " 2062", 0 ; SYS token and address string of subroutine
000D 080C 00 00 13: @end .word 0 ; null ptr to indicate end of basic text
14:
15: ; End of preamble code.
1: ; C64 Hello World
2: ; assemble to .PRG file: vasm -o hello.prg c64.asm c64_hello.asm
3:
4: CHROUT = $FFD2 ; kernal function address
5: CR = 13 ; carrige return character
6: LF = %1010 ; line feed character
7:
8: main: ; this is at address 2062 ($080E)
000F 080E A2 00 9: ldx #0
0011 0810 BD 1C 08 10: @l lda msg, x
0014 0813 20 D2 FF 11: jsr CHROUT
0017 0816 E8 12: inx
0018 0817 E0 0F 13: cpx #len
001A 0819 D0 F5 14: bne @l
001C 081B 60 15: rts
16:
001D 081C 48 45 4C ... 17: msg .byte "HELLO, WORLD!", CR, LF
18: len = @ - msg
19:
20: ; End of file.
VARCem VASM version 1.0.10 (Windows, x86) 04/11/23 00:34:32 Page 2
** SYMBOL TABLE ** File: h2.asm
basic L 0801 W h1.asm:10
@end 080C h1.asm:10
CHROUT V FFD2 W h2.asm:4
CR V 000D B h2.asm:5
len V 000F W h2.asm:18
LF V 000A B h2.asm:6
LOAD V 0801 W h1.asm:5
main L 080E W h2.asm:8
@l 0810 h2.asm:8
msg L 081C W h2.asm:17
SYS V 009E B h1.asm:3

Binary file not shown.

View File

@@ -4,8 +4,11 @@ SYS = $9E ; basic SYS token number
LOAD = $0801 ; load address
.cpu 6510 ; set processor for C64
.word LOAD ; .PRG header: load address
.org LOAD
.org LOAD ; we start at this address
basic: ; BASIC code: 10 SYS 2062
.word @end, 10 ; ptr to next basic line and line number 10

760
tests/kow-test-65c02.asm Normal file
View File

@@ -0,0 +1,760 @@
;
; Michal Kowalski
;
; All documented 650X, 651X, 65F11, 65F12, 65C00/21, 65C29,
; 65C02, 65C102, and 65C112 instructions with proper AS6500 syntax.
;
.title 6500 Assembler Test
; .cpu r65f11
; .cpu r65c02
; .cpu r65c00
extext = $0000 ; zero, but force it to a word
extdir = 0
dir = $33
ext = $1122
offset = $44
.org $1000
adc #$12 ; 69 12
;---
adc $1234 ; 6D 34 12
adc ext ; 6D 22 11
adc extext ; 6Du00v00
;---
adc $12 ; 65 12
adc dir ; 65 33
adc extext ; 65 00
;---
adc $12,x ; 75 12
adc dir,x ; 75 33
adc offset,x ; 75 44
adc extdir,x ; 75 00
;---
adc $1234,x ; 7D 34 12
adc ext,x ; 7D 22 11
adc extext,x ; 7Du00v00
;---
adc $1234,y ; 79 34 12
adc dir,y ; 79 33 00
adc extdir,y ; 79u00v00
adc ext,y ; 79 22 11
adc extext,y ; 79u00v00
;---
adc ($12,x) ; 61 12
adc (dir,x) ; 61 33
adc (offset,x) ; 61 44
adc (extdir,x) ; 61 00
adc (extext,x) ; 61 00
;---
adc ($12),y ; 71 12
adc (dir),y ; 71 33
adc (offset),y ; 71 44
adc (extdir),y ; 71 00
adc (extext),y ; 71 00
;---
.ifdef P65C02
adc ($12) ; 72 12
adc (dir) ; 72 33
adc (offset) ; 72 44
adc (extdir) ; 72 00
adc (extext) ; 72 00
.endif
and #$12 ; 29 12
;---
and $1234 ; 2D 34 12
and ext ; 2D 22 11
and extext ; 2Du00v00
;---
and $12 ; 25 12
and dir ; 25 33
and extext ; 25 00
;---
and $12,x ; 35 12
and dir,x ; 35 33
and offset,x ; 35 44
and extdir,x ; 35 00
;---
and $1234,x ; 3D 34 12
and ext,x ; 3D 22 11
and extext,x ; 3Du00v00
;---
and $1234,y ; 39 34 12
and dir,y ; 39 33 00
and extdir,y ; 39u00v00
and ext,y ; 39 22 11
and extext,y ; 39u00v00
;---
and ($12,x) ; 21 12
and (dir,x) ; 21 33
and (offset,x) ; 21 44
and (extdir,x) ; 21 00
and (extext,x) ; 21 00
;---
and ($12),y ; 31 12
and (dir),y ; 31 33
and (offset),y ; 31 44
and (extdir),y ; 31 00
and (extext),y ; 31 00
;---
.ifdef P65C02
and ($12) ; 32 12
and (dir) ; 32 33
and (offset) ; 32 44
and (extdir) ; 32 00
and (extext) ; 32 00
.endif
asl a ; 0A
asl ; 0A
;---
asl $1234 ; 0E 34 12
asl ext ; 0E 22 11
asl extext ; 0Eu00v00
;---
asl $12 ; 06 12
asl dir ; 06 33
asl extext ; 06 00
;---
asl $12,x ; 16 12
asl dir,x ; 16 33
asl offset,x ; 16 44
asl extdir,x ; 16 00
;---
asl $1234,x ; 1E 34 12
asl ext,x ; 1E 22 11
asl extext,x ; 1Eu00v00
;.if r65f11+r65c00+r65c02
.ifdef P65C02
bbr 0,$12,* ; 0F 12 FD
bbr 1,$12,* ; 1F 12 FD
bbr 2,$12,* ; 2F 12 FD
bbr 3,$12,* ; 3F 12 FD
bbr 4,$12,* ; 4F 12 FD
bbr 5,$12,* ; 5F 12 FD
bbr 6,$12,* ; 6F 12 FD
bbr 7,$12,* ; 7F 12 FD
bbs 0,$12,* ; 8F 12 FD
bbs 1,$12,* ; 9F 12 FD
bbs 2,$12,* ; AF 12 FD
bbs 3,$12,* ; BF 12 FD
bbs 4,$12,* ; CF 12 FD
bbs 5,$12,* ; DF 12 FD
bbs 6,$12,* ; EF 12 FD
bbs 7,$12,* ; FF 12 FD
@1: bbr #0,$12,@3 ; 0F 12 03
@2: bbr #0,dir,@2 ; 0F 33 FD
@3: bbr #0,extext,@1 ; 0F 00 F7
@4: bbs #0,$12,@6 ; 8F 12 03
@5: bbs #0,dir,@5 ; 8F 33 FD
@6: bbs #0,extext,@4 ; 8F 00 F7
.endif
bcc @ ; 90 FE
bcs @ ; B0 FE
beq @ ; F0 FE
;---
.ifdef P65C02
bit #$12 ; 89 12
.endif
bit $1234 ; 2C 34 12
bit ext ; 2C 22 11
bit extext ; 2Cu00v00
;---
bit $12 ; 24 12
bit dir ; 24 33
bit extext ; 24 00
;---
.ifdef P65C02
bit $12,x ; 34 12
bit dir,x ; 34 33
bit offset,x ; 34 44
bit extdir,x ; 34 00
;---
bit $1234,x ; 3C 34 12
bit ext,x ; 3C 22 11
bit extext,x ; 3Cu00v00
.endif
bmi @ ; 30 FE
bne @ ; D0 FE
bpl @ ; 10 FE
;.if r65c00+r65c02
.ifdef P65C02
bra @ ; 80 FE
.endif
brk ; 00
bvc @ ; 50 FE
bvs @ ; 70 FE
clc ; 18
cld ; D8
cli ; 58
clv ; B8
cmp #$12 ; C9 12
;---
cmp $1234 ; CD 34 12
cmp ext ; CD 22 11
cmp extext ; CDu00v00
;---
cmp $12 ; C5 12
cmp dir ; C5 33
cmp extext ; C5 00
;---
cmp $12,x ; D5 12
cmp dir,x ; D5 33
cmp offset,x ; D5 44
cmp extdir,x ; D5 00
;---
cmp $1234,x ; DD 34 12
cmp ext,x ; DD 22 11
cmp extext,x ; DDu00v00
;---
cmp $1234,y ; D9 34 12
cmp dir,y ; D9 33 00
cmp extdir,y ; D9u00v00
cmp ext,y ; D9 22 11
cmp extext,y ; D9u00v00
;---
cmp ($12,x) ; C1 12
cmp (dir,x) ; C1 33
cmp (offset,x) ; C1 44
cmp (extdir,x) ; C1 00
cmp (extext,x) ; C1 00
;---
cmp ($12),y ; D1 12
cmp (dir),y ; D1 33
cmp (offset),y ; D1 44
cmp (extdir),y ; D1 00
cmp (extext),y ; D1 00
;---
.ifdef P65C02
cmp ($12) ; D2 12
cmp (dir) ; D2 33
cmp (offset) ; D2 44
cmp (extdir) ; D2 00
cmp (extext) ; D2 00
.endif
;---
.ifdef P65C02
cpx #$12 ; E0 12
.endif
cpx $12 ; E4 12
cpx dir ; E4 33
cpx extdir ; E4 00
;---
cpx $1234 ; EC 34 12
cpx ext ; EC 22 11
cpx extext ; ECu00v00
.ifdef P65C02
cpy #$12 ; C0 12
.endif
;---
cpy $12 ; C4 12
cpy dir ; C4 33
cpy extdir ; C4 00
;---
cpy $1234 ; CC 34 12
cpy ext ; CC 22 11
cpy extext ; CCu00v00
.ifdef P65C02
; dec a ; 3A
dea ; 3A
.endif
;---
dec $1234 ; CE 34 12
dec ext ; CE 22 11
dec extext ; CEu00v00
;---
dec $12 ; C6 12
dec dir ; C6 33
dec extext ; C6 00
;---
dec $12,x ; D6 12
dec dir,x ; D6 33
dec offset,x ; D6 44
dec extdir,x ; D6 00
;---
dec $1234,x ; DE 34 12
dec ext,x ; DE 22 11
dec extext,x ; DEu00v00
dex ; CA
dey ; 88
eor #$12 ; 49 12
;---
eor $1234 ; 4D 34 12
eor ext ; 4D 22 11
eor extext ; 4Du00v00
;---
eor $12 ; 45 12
eor dir ; 45 33
eor extext ; 45 00
;---
eor $12,x ; 55 12
eor dir,x ; 55 33
eor offset,x ; 55 44
eor extdir,x ; 55 00
;---
eor $1234,x ; 5D 34 12
eor ext,x ; 5D 22 11
eor extext,x ; 5Du00v00
;---
eor $1234,y ; 59 34 12
eor dir,y ; 59 33 00
eor extdir,y ; 59u00v00
eor ext,y ; 59 22 11
eor extext,y ; 59u00v00
;---
eor ($12,x) ; 41 12
eor (dir,x) ; 41 33
eor (offset,x) ; 41 44
eor (extdir,x) ; 41 00
eor (extext,x) ; 41 00
;---
eor ($12),y ; 51 12
eor (dir),y ; 51 33
eor (offset),y ; 51 44
eor (extdir),y ; 51 00
eor (extext),y ; 51 00
;---
.ifdef P65C02
eor ($12) ; 52 12
eor (dir) ; 52 33
eor (offset) ; 52 44
eor (extdir) ; 52 00
eor (extext) ; 52 00
.endif
;---
.ifdef P65C02
inc a ; 1A
ina ; 1A
.endif
;---
inc $1234 ; EE 34 12
inc ext ; EE 22 11
inc extext ; EEu00v00
;---
inc $12 ; E6 12
inc dir ; E6 33
inc extext ; E6 00
;---
inc $12,x ; F6 12
inc dir,x ; F6 33
inc offset,x ; F6 44
inc extdir,x ; F6 00
;---
inc $1234,x ; FE 34 12
inc ext,x ; FE 22 11
inc extext,x ; FEu00v00
inx ; E8
iny ; C8
jmp $12 ; 4C 12 00
jmp dir ; 4C 33 00
jmp extext ; 4Cu00v00
;---
jmp $1234 ; 4C 34 12
jmp ext ; 4C 22 11
jmp extext ; 4Cu00v00
;---
jmp ($1234) ; 6C 34 12
jmp (ext) ; 6C 22 11
jmp (extext) ; 6Cu00v00
;---
.ifdef P65C02
jmp ($1234,x) ; 7C 34 12
jmp (ext,x) ; 7C 22 11
; jmp (extext,x) ; 7Cu00v00
.endif
jsr dir ; 20 33 00
jsr extdir ; 20u00v00
;---
jsr $1234 ; 20 34 12
jsr ext ; 20 22 11
jsr extext ; 20u00v00
lda #$12 ; A9 12
;---
lda $1234 ; AD 34 12
lda ext ; AD 22 11
lda extext ; ADu00v00
;---
lda $12 ; A5 12
lda dir ; A5 33
lda extext ; A5 00
;---
lda $12,x ; B5 12
lda dir,x ; B5 33
lda offset,x ; B5 44
lda extdir,x ; B5 00
;---
lda $1234,x ; BD 34 12
lda ext,x ; BD 22 11
lda extext,x ; BDu00v00
;---
lda $1234,y ; B9 34 12
lda dir,y ; B9 33 00
lda extdir,y ; B9u00v00
lda ext,y ; B9 22 11
lda extext,y ; B9u00v00
;---
lda ($12,x) ; A1 12
lda (dir,x) ; A1 33
lda (offset,x) ; A1 44
lda (extdir,x) ; A1 00
lda (extext,x) ; A1 00
;---
lda ($12),y ; B1 12
lda (dir),y ; B1 33
lda (offset),y ; B1 44
lda (extdir),y ; B1 00
lda (extext),y ; B1 00
;---
.ifdef P65C02
lda ($12) ; B2 12
lda (dir) ; B2 33
lda (offset) ; B2 44
lda (extdir) ; B2 00
lda (extext) ; B2 00
.endif
ldx #$12 ; A2 12
;---
ldx $12 ; A6 12
ldx dir ; A6 33
ldx extdir ; A6 00
;---
ldx $12,y ; B6 12
ldx dir,y ; B6 33
ldx extdir,y ; B6 00
;---
ldx $1234 ; AE 34 12
ldx ext ; AE 22 11
ldx extext ; AEu00v00
;---
ldx $1234,y ; BE 34 12
ldx ext,y ; BE 22 11
ldx extext,y ; BEu00v00
ldy #$12 ; A0 12
;---
ldy $12 ; A4 12
ldy dir ; A4 33
ldy extdir ; A4 00
;---
ldy $12,x ; B4 12
ldy dir,x ; B4 33
ldy extdir,x ; B4 00
;---
ldy $1234 ; AC 34 12
ldy ext ; AC 22 11
ldy extext ; ACu00v00
;---
ldy $1234,x ; BC 34 12
ldy ext,x ; BC 22 11
ldy extext,x ; BCu00v00
lsr a ; 4A
lsr ; 4A
;---
lsr $1234 ; 4E 34 12
lsr ext ; 4E 22 11
lsr extext ; 4Eu00v00
;---
lsr $12 ; 46 12
lsr dir ; 46 33
lsr extext ; 46 00
;---
lsr $12,x ; 56 12
lsr dir,x ; 56 33
lsr offset,x ; 56 44
lsr extdir,x ; 56 00
;---
lsr $1234,x ; 5E 34 12
lsr ext,x ; 5E 22 11
lsr extext,x ; 5Eu00v00
.ifdef PR65C00
mul ; 02
.endif
nop ; EA
ora #$12 ; 09 12
;---
ora $1234 ; 0D 34 12
ora ext ; 0D 22 11
ora extext ; 0Du00v00
;---
ora $12 ; 05 12
ora dir ; 05 33
ora extext ; 05 00
;---
ora $12,x ; 15 12
ora dir,x ; 15 33
ora offset,x ; 15 44
ora extdir,x ; 15 00
;---
ora $1234,x ; 1D 34 12
ora ext,x ; 1D 22 11
ora extext,x ; 1Du00v00
;---
ora $1234,y ; 19 34 12
ora dir,y ; 19 33 00
ora extdir,y ; 19u00v00
ora ext,y ; 19 22 11
ora extext,y ; 19u00v00
;---
ora ($12,x) ; 01 12
ora (dir,x) ; 01 33
ora (offset,x) ; 01 44
ora (extdir,x) ; 01 00
ora (extext,x) ; 01 00
;---
ora ($12),y ; 11 12
ora (dir),y ; 11 33
ora (offset),y ; 11 44
ora (extdir),y ; 11 00
ora (extext),y ; 11 00
;---
.ifdef P65C02
ora ($12) ; 12 12
ora (dir) ; 12 33
ora (offset) ; 12 44
ora (extdir) ; 12 00
ora (extext) ; 12 00
.endif
pha ; 48
php ; 08
;.if r65c00+r65c02
.ifdef P65C02
phx ; DA
phy ; 5A
.endif
pla ; 68
plp ; 28
;.if r65c00+r65c02
.ifdef P65C02
plx ; FA
ply ; 7A
.endif
;.if r65f11+r65c00+r65c02
.ifdef P65C02
rmb 0,$12 ; 07 12
rmb 1,$12 ; 17 12
rmb 2,$12 ; 27 12
rmb 3,$12 ; 37 12
rmb 4,$12 ; 47 12
rmb 5,$12 ; 57 12
rmb 6,$12 ; 67 12
rmb 7,$12 ; 77 12
rmb 0,dir ; 07 33
rmb 0,extdir ; 07 00
.endif
.ifdef P65C02
rol a ; 2A
rol ; 2A
.endif
;---
rol $1234 ; 2E 34 12
rol ext ; 2E 22 11
rol extext ; 2Eu00v00
;---
rol $12 ; 26 12
rol dir ; 26 33
rol extext ; 26 00
;---
rol $12,x ; 36 12
rol dir,x ; 36 33
rol offset,x ; 36 44
rol extdir,x ; 36 00
;---
rol $1234,x ; 3E 34 12
rol ext,x ; 3E 22 11
rol extext,x ; 3Eu00v00
ror a ; 6A
ror ; 6A
;---
ror $1234 ; 6E 34 12
ror ext ; 6E 22 11
ror extext ; 6Eu00v00
;---
ror $12 ; 66 12
ror dir ; 66 33
ror extext ; 66 00
;---
ror $12,x ; 76 12
ror dir,x ; 76 33
ror offset,x ; 76 44
ror extdir,x ; 76 00
;---
ror $1234,x ; 7E 34 12
ror ext,x ; 7E 22 11
ror extext,x ; 7Eu00v00
rti ; 40
rts ; 60
sbc #$12 ; E9 12
;---
sbc $1234 ; ED 34 12
sbc ext ; ED 22 11
sbc extext ; EDu00v00
;---
sbc $12 ; E5 12
sbc dir ; E5 33
sbc extext ; E5 00
;---
sbc $12,x ; F5 12
sbc dir,x ; F5 33
sbc offset,x ; F5 44
sbc extdir,x ; F5 00
;---
sbc $1234,x ; FD 34 12
sbc ext,x ; FD 22 11
sbc extext,x ; FDu00v00
;---
sbc $1234,y ; F9 34 12
sbc dir,y ; F9 33 00
sbc extdir,y ; F9u00v00
sbc ext,y ; F9 22 11
sbc extext,y ; F9u00v00
;---
sbc ($12,x) ; E1 12
sbc (dir,x) ; E1 33
sbc (offset,x) ; E1 44
sbc (extdir,x) ; E1 00
sbc (extext,x) ; E1 00
;---
sbc ($12),y ; F1 12
sbc (dir),y ; F1 33
sbc (offset),y ; F1 44
sbc (extdir),y ; F1 00
sbc (extext),y ; F1 00
;---
.ifdef P65C02
sbc ($12) ; F2 12
sbc (dir) ; F2 33
sbc (offset) ; F2 44
sbc (extdir) ; F2 00
sbc (extext) ; F2 00
.endif
sec ; 38
sed ; F8
sei ; 78
;.if r65f11+r65c00+r65c02
.ifdef P65C02
smb 0,$12 ; 87 12
smb 1,$12 ; 97 12
smb 2,$12 ; A7 12
smb 3,$12 ; B7 12
smb 4,$12 ; C7 12
smb 5,$12 ; D7 12
smb 6,$12 ; E7 12
smb 7,$12 ; F7 12
smb 0,dir ; 87 33
smb 0,extdir ; 87 00
.endif
;sta #$12 ; 89 12
;---
sta $1234 ; 8D 34 12
sta ext ; 8D 22 11
sta extext ; 8Du00v00
;---
sta $12 ; 85 12
sta dir ; 85 33
sta extext ; 85 00
;---
sta $12,x ; 95 12
sta dir,x ; 95 33
sta offset,x ; 95 44
sta extdir,x ; 95 00
;---
sta $1234,x ; 9D 34 12
sta ext,x ; 9D 22 11
sta extext,x ; 9Du00v00
;---
sta $1234,y ; 99 34 12
sta dir,y ; 99 33 00
sta extdir,y ; 99u00v00
sta ext,y ; 99 22 11
sta extext,y ; 99u00v00
;---
sta ($12,x) ; 81 12
sta (dir,x) ; 81 33
sta (offset,x) ; 81 44
sta (extdir,x) ; 81 00
sta (extext,x) ; 81 00
;---
sta ($12),y ; 91 12
sta (dir),y ; 91 33
sta (offset),y ; 91 44
sta (extdir),y ; 91 00
sta (extext),y ; 91 00
;---
.ifdef P65C02
sta ($12) ; 92 12
sta (dir) ; 92 33
sta (offset) ; 92 44
sta (extdir) ; 92 00
sta (extext) ; 92 00
.endif
stx $12 ; 86 12
stx dir ; 86 33
stx extdir ; 86 00
;---
stx $1234 ; 8E 34 12
stx ext ; 8E 22 11
stx extext ; 8Eu00v00
;---
stx $12,y ; 96 12
stx dir,y ; 96 33
stx extdir,y ; 96 00
sty $12 ; 84 12
sty dir ; 84 33
sty extdir ; 84 00
;---
sty $1234 ; 8C 34 12
sty ext ; 8C 22 11
sty extext ; 8Cu00v00
;---
sty $12,x ; 94 12
sty dir,x ; 94 33
sty extdir,x ; 94 00
.ifdef P65C02
stz $12 ; 64 12
stz dir ; 64 33
stz extdir ; 64 00
;---
stz $1234 ; 9C 34 12
stz ext ; 9C 22 11
stz extext ; 9Cu00v00
;---
stz $12,x ; 74 12
stz dir,x ; 74 33
stz extdir,x ; 74 00
;---
stz $1234,x ; 9E 34 12
stz ext,x ; 9E 22 11
stz extext,x ; 9Eu00v00
.endif
tax ; AA
tay ; A8
;---
.ifdef P65C02
trb $1234 ; 1C 34 12
trb ext ; 1C 22 11
trb extext ; 1Cu00v00
;---
trb $12 ; 14 12
trb dir ; 14 33
trb extdir ; 14 00
tsb $1234 ; 0C 34 12
tsb ext ; 0C 22 11
tsb extext ; 0Cu00v00
;---
tsb $12 ; 04 12
tsb dir ; 04 33
tsb extdir ; 04 00
;---
.endif
tsx ; BA
txa ; 8A
txs ; 9A
tya ; 98
.ifdef PW65C02
;---
stp ; DB
wai ; CB
.endif
.end

55
tests/kow-test-asm.asm Normal file
View File

@@ -0,0 +1,55 @@
;KOWALSKI ASSEMBLER LOGICAL, BITWISE & ARITHMETIC OPERATORS
;
;
; number radices...
;
; % binary, e.g., %01011010
; $ hex, e.g., $5a
; none decimal
;
; .opt proc65c02,caseinsensitive
test0001 =%00001111
test0002 =test0001 << 4 ;logical shift left 4 bits
test0003 =test0002 >> 4 ;logical shift right 4 bits
test0004 =test0001 & test0002 ;bitwise AND
test0005 =test0001 | test0002 ;bitwise OR
test0006 =test0001 && test0002 ;logical AND
test0007 =test0001 || test0002 ;logical OR
test0008 =!0 ;bitwise NOT
test0009 =$5a ^ test0005 ;bitwise XOR
test0010 =4 == 3 ;equality test
test0011 =4 == 4 ;equality test
test0012 =4 != 3 ;inequality test
test0013 =4 != 4 ;inequality test
test0014 =4 > 3 ;greater-than test
test0015 =4 < 3 ;lesser-than test
test0016 =~1 ;2s complement
;
;
; arithmetic operators...
;
; + addition
; - subtraction
; * multiplication
; / division
; % modulo
;
sum = 5 + 6 ;evaluates to 11
diff = sum - 6 ;evaluates to 5
prod = 5 * 6 ;evaluates to 30
quot =prod / diff ;evaluates to 6
mod =prod % sum ;evaluates to 8
;
;
; example using square brackets to alter evaluation precedence...
;
test0017 =5 + 6 * 2 ;strictly left-to-right: evaluates to 17
test0018 =(5 + 6) * 2 ;sum of 5 & 6 computed 1st: evaluates to 22
;
nop
.end