From 32dc773bceb8b5e32a59d54c37cc80fd5295e2ce Mon Sep 17 00:00:00 2001 From: waltje Date: Tue, 25 Apr 2023 14:36:25 -0400 Subject: [PATCH] First commit after fork. --- .gitignore | 4 + CHANGELOG | 92 ++- LICENSE | 53 +- README.md | 20 +- src/expr.c | 752 +++++++++++++++++++ src/f.sh | 2 + src/func.c | 149 ++++ src/global.h | 279 +++++++ src/input.c | 212 ++++++ src/list.c | 335 +++++++++ src/main.c | 314 ++++++++ src/output.c | 244 ++++++ src/parse.c | 505 +++++++++++++ src/pseudo.c | 1034 ++++++++++++++++++++++++++ src/symbol.c | 256 +++++++ src/target.c | 180 +++++ src/target.h | 73 ++ src/targets/mos6502.c | 758 +++++++++++++++++++ src/unix/Makefile.GCC | 280 +++++++ src/version.h | 79 ++ src/win/Makefile.MSVC | 303 ++++++++ src/win/Makefile.MinGW | 288 +++++++ src/win/getopt.c | 110 +++ src/win/getopt.h | 68 ++ src/win/varcem.ico | Bin 0 -> 64638 bytes src/win/vasm.rc | 98 +++ tests/c64_hello.asm | 8 + tests/c64_hello.lst | 52 -- tests/c64_hello.prg | Bin 44 -> 0 bytes tests/{c64_basic.asm => c64_prg.asm} | 5 +- tests/kow-test-65c02.asm | 760 +++++++++++++++++++ tests/kow-test-asm.asm | 55 ++ 32 files changed, 7258 insertions(+), 110 deletions(-) create mode 100644 src/expr.c create mode 100644 src/f.sh create mode 100644 src/func.c create mode 100644 src/global.h create mode 100644 src/input.c create mode 100644 src/list.c create mode 100644 src/main.c create mode 100644 src/output.c create mode 100644 src/parse.c create mode 100644 src/pseudo.c create mode 100644 src/symbol.c create mode 100644 src/target.c create mode 100644 src/target.h create mode 100644 src/targets/mos6502.c create mode 100644 src/unix/Makefile.GCC create mode 100644 src/version.h create mode 100644 src/win/Makefile.MSVC create mode 100644 src/win/Makefile.MinGW create mode 100644 src/win/getopt.c create mode 100644 src/win/getopt.h create mode 100644 src/win/varcem.ico create mode 100644 src/win/vasm.rc delete mode 100644 tests/c64_hello.lst delete mode 100644 tests/c64_hello.prg rename tests/{c64_basic.asm => c64_prg.asm} (85%) create mode 100644 tests/kow-test-65c02.asm create mode 100644 tests/kow-test-asm.asm diff --git a/.gitignore b/.gitignore index e6914eb..af6f28c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ # Local stuff .gitignore +t.* +test*.* +hello.* +*.cmd # Prerequisites *.d diff --git a/CHANGELOG b/CHANGELOG index db95320..4256bd4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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. diff --git a/LICENSE b/LICENSE index 47232a9..974acc7 100644 --- a/LICENSE +++ b/LICENSE @@ -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, - Bernd B”ckmann, + 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. diff --git a/README.md b/README.md index f756ea8..91f53ed 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/src/expr.c b/src/expr.c new file mode 100644 index 0000000..36401ed --- /dev/null +++ b/src/expr.c @@ -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, + * Bernd B”ckmann, + * + * 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 +#include +#include +#include +#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, ¤t_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; +} diff --git a/src/f.sh b/src/f.sh new file mode 100644 index 0000000..cd4f233 --- /dev/null +++ b/src/f.sh @@ -0,0 +1,2 @@ +#!/bin/sh +vasm -p6502 -s -l foo.lst -o foo.hex '-DLOAD=$400' foo.asm diff --git a/src/func.c b/src/func.c new file mode 100644 index 0000000..87d5f54 --- /dev/null +++ b/src/func.c @@ -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, + * + * 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 +#include +#include +#include +#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; +} diff --git a/src/global.h b/src/global.h new file mode 100644 index 0000000..7d67491 --- /dev/null +++ b/src/global.h @@ -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, + * + * 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*/ diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..b1f3e96 --- /dev/null +++ b/src/input.c @@ -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, + * + * 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 +#include +#include +#include +#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++; +} diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..8ba5e8c --- /dev/null +++ b/src/list.c @@ -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, + * + * 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 +#include +#include +#include +#include +#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; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d8771d0 --- /dev/null +++ b/src/main.c @@ -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, + * Bernd B”ckmann, + * + * 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 +#include +#include +#include +#include +#include +#ifndef _MSC_VER +# include +#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, \n", + APP_TITLE, version); +#ifdef _WIN32 + printf("Copyright 2022,2023 Bernd B”ckmann, \n\n"); +#else + printf("Copyright 2022,2023 Bernd B\303\266ckmann, \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; +} diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..2c3ecc6 --- /dev/null +++ b/src/output.c @@ -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, + * Bernd B”ckmann, + * + * 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 +#include +#include +#include +#ifdef USE_IHEX +# include +#endif +#ifdef USE_SREC +# include +#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; +} diff --git a/src/parse.c b/src/parse.c new file mode 100644 index 0000000..eab06e5 --- /dev/null +++ b/src/parse.c @@ -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, + * Bernd B”ckmann, + * + * 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 +#include +#include +#if __STDC_VERSION__ >= 201112L +# include +#else +# define noreturn /*NORETURN*/ +#endif +#include +#include +#include +#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; +} diff --git a/src/pseudo.c b/src/pseudo.c new file mode 100644 index 0000000..079fea3 --- /dev/null +++ b/src/pseudo.c @@ -0,0 +1,1034 @@ +/* + * 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 directives and pseudo-ops. + * + * Version: @(#)pseudo.c 1.0.2 2023/04/22 + * + * Authors: Fred N. van Kempen, + * 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 +#include +#include +#include +#include "global.h" + + +typedef struct pseudo { + const char *name; + int always; + char *(*func)(char **, int); + void *lister; +} pseudo_t; + + +static int +string_lit(char **p, char *buf, int bufsize, int quot) +{ + const char *start = buf; + int needquot = quot; + + if (**p != '"') { + if (quot) + error(ERR_STR, NULL); + } else + (*p)++; + + while (! IS_END(**p)) { + if (**p == '"') + break; + + if ((buf-start) >= (bufsize-1)) + error(ERR_STRLEN, NULL); + + *(buf++) = **p; + (*p)++; + } + *buf = '\0'; + + if (IS_END(**p)) { + if (needquot) + error(ERR_STREND, NULL); + } else + (*p)++; + + return (int)(buf - start); +} + + +/* The ".align []" directive. */ +static char * +do_align(char **p, int pass) +{ + int count = 2; + value_t v; + + skip_white(p); + if (! IS_END(**p)) { + /* We were given an alignment factor. */ + v = expr(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + + count = v.v; + } + + if ((count != 1) && (count != 2) && (count != 4) && (count != 8)) + error(ERR_RNG, NULL); + + /* Now "fill out" the space with bytes. */ + count--; + while ((pc & count) != 0) { + emit_byte(0x00, pass); + + pc++; + }; + + return NULL; +} + + +/* The ".asciz "string"[,"string",...]" directive. */ +static char * +do_asciz(char **p, int pass) +{ + char buf[STR_LEN]; + int next, len; + + do { + next = 0; + + skip_white(p); + if (**p == '"') { + len = string_lit(p, buf, STR_LEN, 1); + emit_str(buf, (uint16_t)len, pass); + pc += (uint16_t)len; + emit_byte(0x00, pass); + pc++; + } else + error(ERR_STR, NULL); + + skip_white(p); + if (**p == ',') { + skip_curr_and_white(p); + next = 1; + } + } while (next); + + return NULL; +} + + +/* The ".blob [,[skip][,count]]" directive. */ +static char * +do_blob(char **p, int pass) +{ + char filename[STR_LEN]; + size_t count, skip; + value_t v; + FILE *fp; + int b; + + /* Read filename. */ + skip_white(p); + string_lit(p, filename, STR_LEN, 1); + skip_white_and_comment(p); + + /* Check for more arguments. */ + count = skip = 0; + if (**p == ',') { + skip_curr_and_white(p); + if (!IS_END(**p) && (**p != ',')) { + /* Optional "skip" count Blank means 0. */ + v = expr(p); + if (UNDEFINED(v)) + error(ERR_UNDEF, NULL); + skip = (size_t)v.v; + } + + skip_white(p); + if (**p == ',') { + /* If comma present, "count" must be present. */ + skip_curr_and_white(p); + v = expr(p); + if (UNDEFINED(v)) + error(ERR_UNDEF, NULL); + count = (size_t)v.v; + } + } + + /* Make sure we can open the file.. */ + fp = fopen(filename, "rb"); + if (fp == NULL) + error(ERR_OPEN, filename); + + /* + * Read data from file, and "insert" the bytes into + * source as if they had been .byte statements. + */ + while (!feof(fp) && !ferror(fp)) { + b = fgetc(fp); + if (b == EOF) + break; + + if (skip > 0) + skip--; + else { + emit_byte((uint8_t)b, pass); + pc++; + + if ((count > 0) && (--count == 0)) + break; + } + } + + (void)fclose(fp); + + return NULL; +} + + +/* The ".byte [,,...]" directive. */ +static char * +do_byte(char **p, int pass) +{ + char buf[STR_LEN]; + value_t v; + int next, len; + + do { + next = 0; + + skip_white(p); + if (**p == '"') { + len = string_lit(p, buf, STR_LEN, 1); + emit_str(buf, (uint16_t)len, pass); + pc += (uint16_t)len; + } else if (**p == '\'') { + (*p)++; + if (**p == '\'') + error(ERR_CHR, NULL); + + buf[0] = **p; + + (*p)++; + if (**p != '\'') + error(ERR_CHREND, NULL); + len = 1; + emit_str(buf, (uint16_t)len, pass); + + (*p)++; + pc++; + } else { + v = expr(p); + + if (pass == 2) { + if (UNDEFINED(v)) + error(ERR_UNDEF, NULL); + if ((TYPE(v) != TYPE_BYTE) && (v.v > 0xff)) + error(ERR_ILLTYPE, NULL); + } + emit_byte((uint8_t)to_byte(v).v, pass); + + pc++; + } + + skip_white(p); + if (**p == ',') { + skip_curr_and_white(p); + next = 1; + } + } while (next); + + return NULL; +} + + +/* The ".cpu " directive. */ +static char * +do_cpu(char **p, int pass) +{ + char name[ID_LEN]; + + skip_white_and_comment(p); + if (IS_END(**p)) + error(ERR_ID, NULL); + + nident_upcase(p, name); + + if (! set_cpu(name, pass)) + error(ERR_CPU, name); + + return NULL; +} + + +/* Define a symbol from the command line. */ +static char * +do_define(char **p, int pass) +{ + char id[ID_LEN]; + value_t v = { 0 }; + + skip_white(p); + + ident(p, id); + if (**p == EQUAL_CHAR) { + /* 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); + + return NULL; +} + + +/* The ".dword [,,...]" directive. */ +static char * +do_dword(char **p, int pass) +{ + value_t v; + int next; + + do { + next = 0; + + skip_white(p); + + v = expr(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + + emit_dword(v.v, pass); + pc += 4; + + skip_white(p); + if (**p == ',') { + skip_curr_and_white(p); + next = 1; + } + } while (next); + + return NULL; +} + + +/* The ".echo [[,,...]]" directive. */ +static char * +do_echo(char **p, int pass) +{ + char temp[STR_LEN]; + int fmt, next; + value_t v; + + if (pass == 1) { + while (! IS_END(**p)) + (*p)++; + return NULL; + } + + do { + next = 0; + + skip_white(p); + if (**p == '"') { + string_lit(p, temp, STR_LEN, 1); + printf("%s", temp); + } else { + fmt = value_format(p); + if (fmt == 0) + fmt = FMT_DEC_CHAR; + else if (fmt < 0) + error(-fmt, NULL); + + v = expr(p); + if (DEFINED(v)) + printf("%s", value_print_format(v, fmt)); + else + printf("??"); + } + + skip_white(p); + if (**p == ',') { + skip_curr_and_white(p); + next = 1; + } + } while (next); + + putchar('\n'); + + return NULL; +} + + +/* The ".else" directive. */ +static char * +do_else(char **p, int pass) +{ + skip_white_and_comment(p); + if (! IS_END(**p)) + error(ERR_EOL, NULL); + + if (iflevel > 0) { + newifstate = !ifstate; + ifstate = ifstack[iflevel - 1]; + } else + error(ERR_ELSE, NULL); + + return NULL; +} + + +/* The ".end []" directive. */ +static char * +do_end(char **p, int pass) +{ + value_t v; + + skip_white(p); + if (! IS_END(**p)) { + /* Get the value for the start address. */ + v = expr(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + + /* Set the start address. */ + sa = v.v; + } + + return NULL; +} + + +/* The ".endif" directive. */ +static char * +do_endif(char **p, int pass) +{ + skip_white_and_comment(p); + if (! IS_END(**p)) + error(ERR_EOL, NULL); + + if (iflevel > 0) { + newifstate = ifstack[--iflevel]; + ifstate = ifstack[iflevel]; + } else + error(ERR_ENDIF, NULL); + + return NULL; +} + + +/* The ".error " directive. */ +static char * +do_error(char **p, int pass) +{ + char buff[STR_LEN * 4]; + + skip_white(p); + string_lit(p, buff, STR_LEN * 4, 0); + + error(ERR_USER, buff); + /*NOTREACHED*/ + + return NULL; +} + + +/* The ".equ " directive. */ +static char * +do_equ(char **p, int pass) +{ + char id[ID_LEN]; + value_t v; + + ident(p, id); + + v = expr(p); + + define_variable(id, v); + + return NULL; +} + + +/* The ".fill [,]" directive. */ +static char * +do_fill(char **p, int pass) +{ + uint8_t filler = 0x00; + value_t v; + int count; + + /* Get the size of the fill block. */ + v = expr(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + count = v.v; + + skip_white(p); + if (**p == ',') { + skip_curr_and_white(p); + + /* Get the optional filler byte value (default $FF.) */ + v = expr(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + if (TYPE(v) != TYPE_BYTE) + error(ERR_ILLTYPE, NULL); + filler = (uint8_t)v.v; + } + + /* Now "fill out" the space with bytes. */ + while (count--) { + //FIXME: not needed for ihex/srec output! + emit_byte(filler, pass); + + pc++; + }; + + return NULL; +} + + +/* The ".if " directive. */ +static char * +do_if(char **p, int pass) +{ + value_t v; + + skip_white(p); + + /* Get the value of the expression. */ + v = expr(p); + + /* + * It may have been that one of the symbols + * referenced was not defined. We can make + * this an error, or simply "assume" the + * value to be 0. + */ + if ((pass == 2) && UNDEFINED(v)) { +#ifndef ALLOW_UNDEFINED_IF + error(ERR_UNDEF, NULL); +#endif + } + + if (iflevel < MAX_IFLEVEL) { + ifstack[iflevel++] = ifstate; + newifstate = !!v.v; + } else + error(ERR_IF, NULL); + + return NULL; +} + + +/* The ".ifdef " directive. */ +static char * +do_ifdef(char **p, int pass) +{ + char id[ID_LEN]; + symbol_t *sym; + + skip_white(p); + + nident(p, id); + sym = sym_lookup(id, NULL); + if (sym != NULL && sym->kind != KIND_VAR) + sym = NULL; + + if (iflevel < MAX_IFLEVEL) { + ifstack[iflevel++] = ifstate; + newifstate = ((sym != NULL) && DEFINED(sym->value)) ? 1 : 0; + } else + error(ERR_IF, NULL); + + return NULL; +} + + +/* The ".ifndef " directive. */ +static char * +do_ifndef(char **p, int pass) +{ + char id[ID_LEN]; + symbol_t *sym; + + skip_white(p); + + nident(p, id); + sym = sym_lookup(id, NULL); + if (sym != NULL && sym->kind != KIND_VAR) + sym = NULL; + +//printf(">> IFNDEF(%d) pass=%d state=%d\n", iflevel, pass, ifstate); + if (iflevel < MAX_IFLEVEL) { + ifstack[iflevel++] = ifstate; + newifstate = ((sym != NULL) && DEFINED(sym->value)) ? 0 : 1; + + /* + * HACK. + * + * We are testing for the presence of a defined variable, + * which may or may not exist. If our source code first + * tests for this variable, and then DOES create or delete + * in, things get messed up. + * + * Case in point: + * + * .ifndef FOOBAR + * FOOBAR = 1234 + * .endif + * + * In pass 1, FOOBAR does not exist yet, so we will happily + * set IFSTATE here, and execute the enclosed = line, which + * will show up in the listing. + * + * In pass 2, however, this changes: the same variable now + * DOES exist (as we just created it..), and so the .ifndef + * fails - the = line will not be executed. + * + * This is wrong. Although the variable still exists (it will + * be re-defined to the same value and type in pass 2, which + * is OK), it shows up as "not executed"... + * + * So, for the sake of just this, we save the "state" as we + * found it in pass 1 into the variable's definition so it + * can be checked against in pass 2 ... + */ +//printf(">>> pass=%d ifstate=%d\n", pass, newifstate); + if (pass == 1) { + /* Save state. */ + if (sym != NULL) + sym->pass = newifstate; + } else { + /* Pass 2, check state from pass 1. */ + if (sym != NULL) + newifstate = sym->pass; + } +//printf("NEWstate = %d\n", newifstate); + } else + error(ERR_IF, NULL); + + return NULL; +} + + +/* The ".include " directive. */ +static char * +do_include(char **p, int pass) +{ + char filename[STR_LEN]; + char *ntext = NULL; + size_t last_sz, last_off; + size_t pos, size; + int i; + + /* Do we have room for another include file? */ + if (filenames_len + 2 > MAX_FILENAMES) + error(ERR_MAXINC, NULL); + + /* Read filename. */ + skip_white(p); + string_lit(p, filename, STR_LEN, 1); + skip_white_and_comment(p); + if (! IS_END(**p)) + error(ERR_EOL, NULL); + skip_eol(p); + + ntext = *p; + + if (pass == 1) { + /* Point at the first character of the line following the directive. */ + last_off = (int)(*p - text); + last_sz = text_len - last_off; + + size = file_size(filename); + + /* Calculate new source length and aquire memory. */ + text_len = last_off + 1 + size + 1 + last_sz; + ntext = malloc(text_len + 1); // plus NUL at end + if (ntext == NULL) + error(ERR_MEM, NULL); + + /* Copy pre-include block into buffer and terminate it. */ + memcpy(ntext, text, last_off); + ntext[last_off++] = EOF_CHAR; + + /* Now read the new file into the buffer and terminate it. */ + pos = last_off; + if (file_read_buf(filename, ntext + last_off) == 0) + error(ERR_OPEN, filename); + last_off += size; + ntext[last_off++] = EOF_CHAR; + + /* Finally, move second block of original buffer and terminate it. */ + memcpy(ntext + last_off, *p, last_sz); + last_off += last_sz; + + ntext[last_off] = '\0'; + + /* Set source pointer to beginning of included file. */ + *p = ntext + pos; + } + + /* Break up current file and make spaces for two new files. */ + for (i = (filenames_len - 1); i > (filenames_idx + 1); i--) { + filenames[i] = filenames[i - 2]; + filelines[i] = filelines[i - 2]; + } + filenames_len += 2; + filenames[filenames_idx + 2] = filenames[filenames_idx]; + filelines[filenames_idx + 2] = line + 1; + + /* Add this included file in the middle. */ + filenames[filenames_idx + 1] = strdup(filename); + filelines[filenames_idx + 1] = 1; + + /* We are now "in" the included file. */ + filenames_idx++; + newline = filelines[filenames_idx]; + + return ntext; +} + + +/* The ".org
" directive. */ +static char * +do_org(char **p, int pass) +{ + value_t v; + + skip_white(p); + + /* Get the new value for the origin. */ + v = expr(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + + /* + * Optionally, "fill out" the space with $00 bytes. + * + * Note that we do NOT do this the first time the origin + * is changed (from being $0000) since that would not be + * sensible. We only do it if we change it on the fly. + */ + if (opt_F) { + if (org_done) { + while (pc < v.v) { + //FIXME: not needed for ihex/srec output! + emit_byte(0x00, pass); + + pc++; + } + } else { + /* Remember our load address. */ + emit_addr(v.v); + + org_done = 1; + } + } + + /* Now set the new origin. */ + pc = v.v; + + return NULL; +} + + +/* The ".page []" directive. */ +static char * +do_page(char **p, int pass) +{ + value_t v; + + skip_white_and_comment(p); + if (IS_END(**p)) { + /* This was just a .page to start a new page. */ + list_page(NULL); + return NULL; + } + + /* Get the number of lines per page. */ + if (**p != ',') { + v = expr(p); + skip_white(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + + /* Set the new number of lines per page. */ + list_plength = (int)v.v; + } + + if (**p == ',') { + /* Comma present, so also have line length. */ + skip_curr_and_white(p); + + /* Get the number of characters per line. */ + v = expr(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + + /* Set the new number of characters per line. */ + list_pwidth = (int)v.v; + } + + return NULL; +} + + +/* The ".radix [2|8|10|16]" directive. */ +static char * +do_radix(char **p, int pass) +{ + value_t v; + + skip_white_and_comment(p); + if (IS_END(**p)) { + /* Terminate current radix, reset to default. */ + radix = RADIX_DEFAULT; + if (opt_v && pass == 1) + printf("Resetting radix to %i\n", radix); + + return NULL; + } + + /* + * We may currently be in a non-decimal radix, and if we + * then get a line saying + * + * .radix 16 + * + * this could be an invalid number for that radix. So, + * we first reset it to the default before trying to + * read the new one. + */ + radix = RADIX_DEFAULT; + + skip_white(p); + v = expr(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + + if (v.v != 2 && v.v != 8 && v.v != 10 && v.v != 16) + error(ERR_RNG, NULL); + + radix = (int8_t)v.v; + if (opt_v && pass == 1) + printf("Setting radix to %i\n", radix); + + return NULL; +} + + +/* The ".syms [off|on|full]" directive. */ +static char * +do_syms(char **p, int pass) +{ + char id[ID_LEN]; + int syms = 0; + + skip_white_and_comment(p); + if (! IS_END(**p)) { + nident_upcase(p, id); + + if (! strcmp(id, "OFF")) + syms = 0; + else if (! strcmp(id, "ON")) + syms = 1; + else if (! strcmp(id, "FULL")) + syms = 2; + else + error(ERR_STR, NULL); + } + + list_set_syms(syms); + + return NULL; +} + + +/* The ".title " directive. */ +static char * +do_title(char **p, int pass) +{ + char buff[STR_LEN]; + + /* Read filename. */ + skip_white(p); + string_lit(p, buff, STR_LEN, 0); + + /* Free any current heading. */ + list_set_head(buff); + + return NULL; +} + + +/* The ".word [,,...]" directive. */ +static char * +do_word(char **p, int pass) +{ + value_t v; + int next; + + do { + next = 0; + skip_white(p); + + v = expr(p); + if ((pass == 2) && UNDEFINED(v)) + error(ERR_UNDEF, NULL); + emit_word(v.v, pass); + + pc += 2; + skip_white(p); + if (**p == ',') { + skip_curr_and_white(p); + next = 1; + } + } while (next); + + return NULL; +} + + +/* The ".warn " directive. */ +static char * +do_warn(char **p, int pass) +{ + char buff[STR_LEN * 4]; + char *str = *p; + + skip_white(p); + string_lit(p, buff, STR_LEN * 4, 0); + + if (pass == 2) + printf("*** WARNING: "); + + *p = str; + do_echo(p, pass); + + return NULL; +} + + +static const pseudo_t pseudos[] = { + { "IF", 1, do_if, NULL }, + { "IFDEF", 1, do_ifdef, NULL }, + { "IFNDEF", 1, do_ifndef, NULL }, + { "ELSE", 1, do_else, NULL }, + { "ENDIF", 1, do_endif, NULL }, + { "ALIGN", 0, do_align, NULL }, + { "ASCIIZ", 0, do_asciz, NULL }, + { "ASCIZ", 0, do_asciz, NULL }, + { "BINARY", 0, do_blob, NULL }, + { "BLOB", 0, do_blob, NULL }, + { "BYTE", 0, do_byte, NULL }, + { "CPU", 0, do_cpu, NULL }, + { "DATA", 0, do_byte, NULL }, + { "DB", 0, do_byte, NULL }, + { "DEFINE", 0, do_define, NULL }, + { "DWORD", 0, do_dword, NULL }, + { "DL", 0, do_dword, NULL }, + { "DW", 0, do_word, NULL }, + { "ECHO", 0, do_echo, NULL }, + { "END", 0, do_end, NULL }, + { "ERROR", 0, do_error, NULL }, + { "EQU", 0, do_equ, NULL }, + { "FILL", 0, do_fill, NULL }, + { "INCLUDE", 0, do_include, NULL }, + { "ORG", 0, do_org, NULL }, + { "PAGE", 0, do_page, NULL }, + { "RADIX", 0, do_radix, NULL }, + { "RADX", 0, do_radix, NULL }, + { "STRING", 0, do_byte, NULL }, + { "STR", 0, do_byte, NULL }, + { "TITLE", 0, do_title, NULL }, + { "SYMS", 0, do_syms, NULL }, + { "SYM", 0, do_syms, NULL }, + { "WARN", 0, do_warn, NULL }, + { "WORD", 0, do_word, NULL }, + { NULL } +}; + + +const pseudo_t * +is_pseudo(const char *name) +{ + const pseudo_t *ptr; + + for (ptr = pseudos; ptr->name != NULL; ptr++) + if (! strcmp(ptr->name, name)) + return ptr; + + return NULL; +} + + +char * +pseudo(const pseudo_t *ptr, char **p, int pass) +{ + char *newp = NULL; + + if (ptr == NULL) + error(ERR_NODIRECTIVE, NULL); + + if (ptr->always || ifstate) + newp = ptr->func(p, pass); + + /* Just skip. */ + while (! IS_END(**p)) + (*p)++; + + return newp; +} diff --git a/src/symbol.c b/src/symbol.c new file mode 100644 index 0000000..223220f --- /dev/null +++ b/src/symbol.c @@ -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, + * 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 +#include +#include +#include +#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; +} diff --git a/src/target.c b/src/target.c new file mode 100644 index 0000000..0d734d8 --- /dev/null +++ b/src/target.c @@ -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, + * + * 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 +#include +#include +#include +#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); +} diff --git a/src/target.h b/src/target.h new file mode 100644 index 0000000..7a7901c --- /dev/null +++ b/src/target.h @@ -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, + * + * 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*/ diff --git a/src/targets/mos6502.c b/src/targets/mos6502.c new file mode 100644 index 0000000..b21b9a1 --- /dev/null +++ b/src/targets/mos6502.c @@ -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, + * Bernd B”ckmann, + * + * 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 +#include +#include +#include +#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 +}; diff --git a/src/unix/Makefile.GCC b/src/unix/Makefile.GCC new file mode 100644 index 0000000..a082770 --- /dev/null +++ b/src/unix/Makefile.GCC @@ -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, +# +# 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. diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..4ef1215 --- /dev/null +++ b/src/version.h @@ -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, + * + * 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*/ diff --git a/src/win/Makefile.MSVC b/src/win/Makefile.MSVC new file mode 100644 index 0000000..1d548fe --- /dev/null +++ b/src/win/Makefile.MSVC @@ -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, +# +# 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. diff --git a/src/win/Makefile.MinGW b/src/win/Makefile.MinGW new file mode 100644 index 0000000..5058752 --- /dev/null +++ b/src/win/Makefile.MinGW @@ -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, +# +# 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. diff --git a/src/win/getopt.c b/src/win/getopt.c new file mode 100644 index 0000000..7e3264f --- /dev/null +++ b/src/win/getopt.c @@ -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, + * + * 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 +#include +#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); +} diff --git a/src/win/getopt.h b/src/win/getopt.h new file mode 100644 index 0000000..ae84aee --- /dev/null +++ b/src/win/getopt.h @@ -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, + * + * 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*/ diff --git a/src/win/varcem.ico b/src/win/varcem.ico new file mode 100644 index 0000000000000000000000000000000000000000..97f7f1710777ee465f2250cb355ba36e90533d50 GIT binary patch literal 64638 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

swRi^ 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

>9cT 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 literal 0 HcmV?d00001 diff --git a/src/win/vasm.rc b/src/win/vasm.rc new file mode 100644 index 0000000..49f1cee --- /dev/null +++ b/src/win/vasm.rc @@ -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, + * + * 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 +#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 diff --git a/tests/c64_hello.asm b/tests/c64_hello.asm index ab86a99..cd51893 100644 --- a/tests/c64_hello.asm +++ b/tests/c64_hello.asm @@ -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 diff --git a/tests/c64_hello.lst b/tests/c64_hello.lst deleted file mode 100644 index f76376d..0000000 --- a/tests/c64_hello.lst +++ /dev/null @@ -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 diff --git a/tests/c64_hello.prg b/tests/c64_hello.prg deleted file mode 100644 index 4525c05a576e4fe6c98fb1a02598876b6d11a337..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 zcmZSN;Njq6n5ST5U}nU?z_5s6uMCI6rT;G;@L%|v;Nj}y diff --git a/tests/c64_basic.asm b/tests/c64_prg.asm similarity index 85% rename from tests/c64_basic.asm rename to tests/c64_prg.asm index 952fbd6..79306d3 100644 --- a/tests/c64_basic.asm +++ b/tests/c64_prg.asm @@ -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 diff --git a/tests/kow-test-65c02.asm b/tests/kow-test-65c02.asm new file mode 100644 index 0000000..4200f4d --- /dev/null +++ b/tests/kow-test-65c02.asm @@ -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 diff --git a/tests/kow-test-asm.asm b/tests/kow-test-asm.asm new file mode 100644 index 0000000..f59741a --- /dev/null +++ b/tests/kow-test-asm.asm @@ -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