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 0000000..97f7f17 Binary files /dev/null and b/src/win/varcem.ico differ 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 4525c05..0000000 Binary files a/tests/c64_hello.prg and /dev/null differ 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