diff --git a/Tools/Breakasm/ASM.cpp b/Tools/Breakasm/ASM.cpp index 741a9db..2313caf 100644 --- a/Tools/Breakasm/ASM.cpp +++ b/Tools/Breakasm/ASM.cpp @@ -19,7 +19,6 @@ static uint8_t *PRG; long org; // current emit offset -int linenum; long stop; long errors; @@ -27,8 +26,34 @@ static std::list labels; // name labels static std::list patchs; // patch history static std::list defines; // definitions +static std::list source_name_stack; // Stack for the name of the current source file (for INCLUDE) +static std::list linenum_stack; // Stack for the line number of the current source file (for INCLUDE) + // **************************************************************** +/// +/// The linenum stack is required to support INCLUDE. Where previously just the linenum variable was used, this call is now used. +/// +/// Line number where the assembler is currently located in the current source file +int get_linenum() +{ + return linenum_stack.back(); +} + +void nextline() +{ + linenum_stack.back()++; +} + +/// +/// Get the name of the current source file currently being worked with. +/// +/// +std::string get_source_name() +{ + return source_name_stack.back(); +} + // Labels management. // @@ -58,22 +83,24 @@ label_s *add_label (const char *name, long orig) label = new label_s; strcpy (label->name, temp_name); label->orig = orig; - label->line = linenum; + strcpy(label->source, get_source_name().c_str()); + label->line = get_linenum(); labels.push_back(label); } else { if ( label->orig == KEYWORD ) { - printf ("ERROR(%i): Reserved keyword used as label \'%s\'\n", linenum, temp_name); + printf ("ERROR(%s,%i): Reserved keyword used as label \'%s\'\n", get_source_name().c_str(), get_linenum(), temp_name); errors++; } else if (orig != UNDEF && label->orig != UNDEF ) { - printf ("ERROR(%i): label \'%s\' already defined in line %i\n", linenum, temp_name, label->line); + printf ("ERROR(%s,%i): label \'%s\' already defined in %s, line %i\n", get_source_name().c_str(), get_linenum(), temp_name, label->source, label->line); errors++; } else { if (orig != UNDEF) { label->orig = orig; - label->line = linenum; + strcpy(label->source, get_source_name().c_str()); + label->line = get_linenum(); } } } @@ -85,7 +112,7 @@ static void dump_labels (void) int i = 0; label_s * label; printf ("\nLABELS (%d):\n", (int)labels.size()); - for (auto it = labels.begin(); it != labels.end(); ++i) { + for (auto it = labels.begin(); it != labels.end(); ++it) { label = *it; if (label->orig == KEYWORD) continue; printf("%i: $%08X = \'%s\'\n", i + 1, label->orig, label->name); @@ -96,13 +123,14 @@ static void dump_labels (void) // Patch management. // -void add_patch (label_s *label, long orig, int branch, int line) +void add_patch (label_s *label, long orig, int branch) { patch_s * patch = new patch_s; patch->label = label; patch->orig = orig; patch->branch = branch; - patch->line = line; + strcpy(patch->source, get_source_name().c_str()); + patch->line = get_linenum(); patchs.push_back(patch); } @@ -115,7 +143,7 @@ static void do_patch (void) patch = *it; orig = patch->label->orig; if ( orig == UNDEF ) { - printf ("ERROR(%i): Undefined label \'%s\' (%08X)\n", patch->line, patch->label->name, orig ); + printf ("ERROR(%s,%i): Undefined label \'%s\' (%08X)\n", patch->source, patch->line, patch->label->name, orig ); errors++; } else { @@ -123,7 +151,7 @@ static void do_patch (void) org = patch->orig; rel = orig - org - 1; if (rel > 127 || rel < -128) { - printf("ERROR(%i): Branch relative offset to %s out of range\n", patch->line, patch->label->name); + printf("ERROR(%s,%i): Branch relative offset to %s out of range\n", patch->source, patch->line, patch->label->name); errors++; } else emit(rel & 0xff); @@ -143,7 +171,7 @@ static void dump_patches (void) printf ("\nPATCHS (%d):\n", (int)patchs.size()); for (auto it = patchs.begin(); it != patchs.end(); ++it) { patch = *it; - printf("LINE %i: $%08X = \'%s\'", patch->line, patch->orig, patch->label->name); + printf("%s, line %i: $%08X = \'%s\'", patch->source, patch->line, patch->orig, patch->label->name); if (patch->branch) printf(" (REL)\n"); else printf(" (ABS)\n"); } @@ -168,7 +196,7 @@ define_s *add_define (char *name, char *replace) define_s * def; if ( label = label_lookup (name) ) { - printf ("ERROR(%i): Already defined as label in line %i\n", linenum, label->line ); + printf ("ERROR(%s,%i): Already defined as label in %s, line %i\n", get_source_name().c_str(), get_linenum(), label->source, label->line ); errors++; return NULL; } @@ -201,7 +229,7 @@ static void dump_defines (void) // **************************************************************** // Evaluate expression -// Evaluation code can be replaced by more complicated version (with macro-operations etc.) +// TODO: Evaluation code will be replaced by more complicated version (with macro-operations etc.) int eval (char *text, eval_t *result) { @@ -479,6 +507,7 @@ static oplink optab[] = { { "STY", opLDSTY }, // Directives + { "INCLUDE", opINCLUDE }, { "DEFINE", opDEFINE }, { "BYTE", opBYTE }, { "WORD", opWORD }, @@ -505,6 +534,7 @@ static void cleanup (void) delete item; } + // Clear defines while (!defines.empty()) { define_s* item = defines.back(); defines.pop_back(); @@ -512,7 +542,7 @@ static void cleanup (void) } } -int assemble (char *text, uint8_t *prg) +int assemble (char *text, char* source_name, uint8_t *prg) { line l; oplink *opl; @@ -523,8 +553,10 @@ int assemble (char *text, uint8_t *prg) cleanup (); + source_name_stack.push_back(source_name); + // Add keywords - linenum = 0; + linenum_stack.push_back(0); add_label ("A", KEYWORD); add_label ("X", KEYWORD); add_label ("Y", KEYWORD); @@ -534,7 +566,7 @@ int assemble (char *text, uint8_t *prg) else add_label (opl->name, KEYWORD); opl++; } - linenum++; + nextline(); while (1) { if (*text == 0) break; @@ -552,7 +584,7 @@ int assemble (char *text, uint8_t *prg) opl = optab; while (1) { if ( opl->name == NULL ) { - printf ("ERROR(%i): Unknown command %s\n", linenum, l.cmd); + printf ("ERROR(%s,%i): Unknown command %s\n", get_source_name().c_str(), get_linenum(), l.cmd); break; } else if ( !_stricmp (opl->name, l.cmd) ) { @@ -564,7 +596,7 @@ int assemble (char *text, uint8_t *prg) } if (stop) break; } - linenum++; + nextline(); } // Patch jump/branch offsets. @@ -579,3 +611,52 @@ int assemble (char *text, uint8_t *prg) printf ( "%i error(s)\n", errors ); return errors; } + +/// +/// Assemble nested source file (called from the INCLUDE directive). +/// The name of the source file will be placed on the name stack, and the line counter will count from the beginning of the file. +/// +/// Source file text +/// Source file name +void assemble_include(char* text, char* source_name) +{ + line l; + oplink* opl; + + source_name_stack.push_back(source_name); + linenum_stack.push_back(1); + + while (1) { + if (*text == 0) break; + parse_line(&text, l); + + //printf ( "%s: \'%s\' \'%s\'\n", l.label, l.cmd, l.op ); + + // Add label + if (strlen(l.label) > 1) { + add_label(l.label, org); + } + + // Execute command + if (strlen(l.cmd) > 1) { + opl = optab; + while (1) { + if (opl->name == NULL) { + printf("ERROR(%s,%i): Unknown command %s\n", get_source_name().c_str(), get_linenum(), l.cmd); + break; + } + else if (!_stricmp(opl->name, l.cmd)) { + _strupr(l.cmd); + opl->handler(l.cmd, l.op); + break; + } + opl++; + } + if (stop) break; + } + nextline(); + } + + source_name_stack.pop_back(); + linenum_stack.pop_back(); +} diff --git a/Tools/Breakasm/ASM.h b/Tools/Breakasm/ASM.h index f55b75e..2679442 100644 --- a/Tools/Breakasm/ASM.h +++ b/Tools/Breakasm/ASM.h @@ -1,6 +1,6 @@ #pragma once -#define BREAKASM_VERSION "1.2" +#define BREAKASM_VERSION "1.3" #define UNDEF 0xbabadaba // undefined offset #define KEYWORD 0xd0d0d0d0 // keyword @@ -19,6 +19,7 @@ struct line { struct label_s { char name[128]; long orig; + char source[0x100]; int line; }; @@ -26,6 +27,7 @@ struct patch_s { label_s* label; long orig; int branch; // 1: relative branch, 0: absolute jmp + char source[0x100]; int line; }; @@ -55,7 +57,6 @@ struct param_t { }; extern long org; // current emit offset -extern int linenum; extern long stop; extern long errors; @@ -63,10 +64,14 @@ extern param_t* params; extern int param_num; label_s* add_label(const char* name, long orig); -void add_patch(label_s* label, long orig, int branch, int line); +void add_patch(label_s* label, long orig, int branch); define_s* add_define(char* name, char* replace); int eval(char* text, eval_t* result); void split_param(char* op); void emit(uint8_t b); -int assemble (char *text, uint8_t *prg); +int get_linenum(); +std::string get_source_name(); + +int assemble (char *text, char *source_name, uint8_t *prg); +void assemble_include(char* text, char* source_name); diff --git a/Tools/Breakasm/ASMOPS.cpp b/Tools/Breakasm/ASMOPS.cpp index c31342d..0a639bf 100644 --- a/Tools/Breakasm/ASMOPS.cpp +++ b/Tools/Breakasm/ASMOPS.cpp @@ -31,13 +31,13 @@ static void NotEnoughParameters (char *cmd) { - printf ( "ERROR(%i): %s not enough parameters\n", linenum, cmd); + printf ( "ERROR(%s,%i): %s not enough parameters\n", get_source_name().c_str(), get_linenum(), cmd); errors++; } static void WrongParameters (char *cmd, char *op) { - printf ( "ERROR(%i): %s wrong parameters: %s\n", linenum, cmd, op); + printf ( "ERROR(%s,%i): %s wrong parameters: %s\n", get_source_name().c_str(), get_linenum(), cmd, op); errors++; } @@ -110,7 +110,7 @@ void opLDST (char *cmd, char *ops) if ( !_stricmp (cmd, "LDA") ) emit (0xAD); if ( !_stricmp (cmd, "STA") ) emit (0x8D); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -133,14 +133,14 @@ void opLDST (char *cmd, char *ops) if ( !_stricmp ( cmd, "LDA" ) ) emit ( 0xBD ); if ( !_stricmp ( cmd, "STA" ) ) emit ( 0x9D ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else if (val[1].label->orig == KEYWORD && !_stricmp(val[1].label->name, "Y")) { if ( !_stricmp ( cmd, "LDA" ) ) emit ( 0xB9 ); if ( !_stricmp ( cmd, "STA" ) ) emit ( 0x99 ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -208,7 +208,7 @@ void opLDSTX (char *cmd, char *ops) if ( !_stricmp ( cmd, "LDX" ) ) emit ( 0xAE ); if ( !_stricmp ( cmd, "STX" ) ) emit ( 0x8E ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else if ( type[0] == EVAL_NUMBER ) { // # @@ -230,7 +230,7 @@ void opLDSTX (char *cmd, char *ops) if ( !_stricmp ( cmd, "LDX" ) ) { emit ( 0xBE ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -291,7 +291,7 @@ void opLDSTY (char *cmd, char *ops) if ( !_stricmp ( cmd, "LDY" ) ) emit ( 0xAC ); if ( !_stricmp ( cmd, "STY" ) ) emit ( 0x8C ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else if ( type[0] == EVAL_NUMBER ) { // # @@ -313,7 +313,7 @@ void opLDSTY (char *cmd, char *ops) if ( !_stricmp ( cmd, "LDY" ) ) { emit ( 0xBC ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -369,7 +369,7 @@ void opBRA (char *cmd, char *ops) if ( !_stricmp (cmd, "BNE") ) emit (0xD0); if ( !_stricmp (cmd, "BEQ") ) emit (0xF0); label = add_label (val.label->name, UNDEF); - add_patch (label, org, 1, linenum ); + add_patch (label, org, 1 ); emit (0); } else WrongParameters (cmd, ops); @@ -416,7 +416,7 @@ void opJMP (char *cmd, char *ops) if ( !_stricmp (cmd, "JSR") ) emit (0x20); } label = add_label (val.label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -478,7 +478,7 @@ void opALU1 (char *cmd, char *ops) if ( !_stricmp ( cmd, "CMP" ) ) emit ( 0xCD ); if ( !_stricmp ( cmd, "SBC" ) ) emit ( 0xED ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -509,7 +509,7 @@ void opALU1 (char *cmd, char *ops) if ( !_stricmp ( cmd, "CMP" ) ) emit ( 0xDD ); if ( !_stricmp ( cmd, "SBC" ) ) emit ( 0xFD ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else if (val[1].label->orig == KEYWORD && !_stricmp(val[1].label->name, "Y")) { @@ -520,7 +520,7 @@ void opALU1 (char *cmd, char *ops) if ( !_stricmp ( cmd, "CMP" ) ) emit ( 0xD9 ); if ( !_stricmp ( cmd, "SBC" ) ) emit ( 0xF9 ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -586,7 +586,13 @@ void opSHIFT (char *cmd, char *ops) split_param (ops); - if (param_num == 1) { + if (param_num == 0) { + if (!_stricmp(cmd, "ASL")) emit(0x0A); + if (!_stricmp(cmd, "ROL")) emit(0x2A); + if (!_stricmp(cmd, "LSR")) emit(0x4A); + if (!_stricmp(cmd, "ROR")) emit(0x6A); + } + else if (param_num == 1) { type[0] = eval ( params[0].string, &val[0] ); if ( type[0] == EVAL_ADDRESS ) { if ( val[0].address < 0x100 ) { // Zero page @@ -618,7 +624,7 @@ void opSHIFT (char *cmd, char *ops) if ( !_stricmp ( cmd, "LSR" ) ) emit ( 0x4E ); if ( !_stricmp ( cmd, "ROR" ) ) emit ( 0x6E ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } } @@ -636,7 +642,7 @@ void opSHIFT (char *cmd, char *ops) if ( !_stricmp ( cmd, "LSR" ) ) emit ( 0x5E ); if ( !_stricmp ( cmd, "ROR" ) ) emit ( 0x7E ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -694,7 +700,7 @@ void opBIT (char *cmd, char *ops) else if (type == EVAL_LABEL) { emit (0x2C); label = add_label (val.label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -730,7 +736,7 @@ void opINCDEC (char *cmd, char *ops) if ( !_stricmp ( cmd, "INC" ) ) emit ( 0xEE ); if ( !_stricmp ( cmd, "DEC" ) ) emit ( 0xCE ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -745,7 +751,7 @@ void opINCDEC (char *cmd, char *ops) if ( !_stricmp ( cmd, "INC" ) ) emit ( 0xFE ); if ( !_stricmp ( cmd, "DEC" ) ) emit ( 0xDE ); label = add_label (val[0].label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -807,7 +813,7 @@ void opCPXY (char *cmd, char *ops) if ( !_stricmp ( cmd, "CPX" ) ) emit ( 0xEC ); if ( !_stricmp ( cmd, "CPY" ) ) emit ( 0xCC ); label = add_label (val.label->name, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters (cmd, ops); @@ -815,6 +821,56 @@ void opCPXY (char *cmd, char *ops) else NotEnoughParameters (cmd); } +// INCLUDE +// ************************************************************** + +void opINCLUDE(char* cmd, char* ops) +{ + while (*ops <= ' ' && *ops) ops++; + + // Construct a file name enclosed in double or single quotes + + char source_name[0x100]{}, *ptr = source_name; + while (*ops) { + if (*ops == '\"' || *ops == '\'') { + ops++; + continue; + } + *ptr++ = *ops++; + } + *ptr++ = 0; + + FILE* f; + f = fopen(source_name, "rt"); + if (!f) { + printf("ERROR(%s,%i): Failed to load the nested source file: %s\n", get_source_name().c_str(), get_linenum(), source_name); + errors++; + return; + } + + // One more byte of memory is allocated to complete the text with the null character (END). + + fseek(f, 0, SEEK_END); + long size = ftell(f) + 1; + fseek(f, 0, SEEK_SET); + + char* text = new char[size]; + memset(text, 0, size); + + size_t readSize = fread(text, 1, size, f); + fclose(f); + if (readSize >= size) + { + delete[] text; + printf("ERROR(%s,%i): Error loading the source file.\n", get_source_name().c_str(), get_linenum()); + errors++; + return; + } + + assemble_include(text, source_name); + delete[] text; +} + // DEFINE // ************************************************************** @@ -839,7 +895,7 @@ void opBYTE (char *cmd, char *ops) for (i=0; iname, UNDEF); - add_patch (label, org, 0, linenum ); + add_patch (label, org, 0 ); emit (0); emit (0); } else WrongParameters(cmd, ops); diff --git a/Tools/Breakasm/ASMOPS.h b/Tools/Breakasm/ASMOPS.h index 712657b..5a349e3 100644 --- a/Tools/Breakasm/ASMOPS.h +++ b/Tools/Breakasm/ASMOPS.h @@ -39,6 +39,7 @@ void opBIT(char* cmd, char* ops); void opINCDEC(char* cmd, char* ops); void opCPXY(char* cmd, char* ops); +void opINCLUDE(char* cmd, char* ops); void opDEFINE(char* cmd, char* ops); void opBYTE(char* cmd, char* ops); void opWORD(char* cmd, char* ops); diff --git a/Tools/Breakasm/Readme.md b/Tools/Breakasm/Readme.md index 5c77df8..8902ba9 100644 --- a/Tools/Breakasm/Readme.md +++ b/Tools/Breakasm/Readme.md @@ -25,6 +25,7 @@ The label (`LABEL`) is optional. The command (`COMMAND`) contains 6502 instructi |Directive|Description| |---|---| |ORG|Set the current PRG assembly position.| +|INCLUDE|Process a nested source file| |DEFINE|Define a simple constant| |BYTE|Output a byte or string| |WORD|Output uint16_t in little-endian order. You can use both numbers as well as labels and addresses.| diff --git a/Tools/Breakasm/ReadmeRus.md b/Tools/Breakasm/ReadmeRus.md index decc5eb..8d4407b 100644 --- a/Tools/Breakasm/ReadmeRus.md +++ b/Tools/Breakasm/ReadmeRus.md @@ -25,6 +25,7 @@ PRG файл всегда 64 Кбайта (размером с адресное |Директива|Описание| |---|---| |ORG|Установить текущее положение ассемблирования в PRG.| +|INCLUDE|Обработать вложенный исходный файл| |DEFINE|Определить простую константу| |BYTE|Вывести байт или строку| |WORD|Вывести uint16_t в порядке little-endian. Можно использовать как простые числа, так и метки и адреса.| diff --git a/Tools/Breakasm/main.cpp b/Tools/Breakasm/main.cpp index 9785861..14dd23b 100644 --- a/Tools/Breakasm/main.cpp +++ b/Tools/Breakasm/main.cpp @@ -55,7 +55,7 @@ int main(int argc, char** argv) // Assemble - int err_count = assemble(text, prg); + int err_count = assemble(text, argv[1], prg); if (err_count != 0) { delete[] text; diff --git a/Tools/Breakasm/pch.h b/Tools/Breakasm/pch.h index a1a3372..12a4b81 100644 --- a/Tools/Breakasm/pch.h +++ b/Tools/Breakasm/pch.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "ASM.h" #include "ASMOPS.h"