From ca604ad81cf8c76e120cba7b5f98ecb8f077db41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Henrik=20Sk=C3=A5rstedt?= Date: Tue, 13 Oct 2015 19:38:13 -0700 Subject: [PATCH] Export and import of object files First pass implementation of object files - all imported labels are currently global. --- README.md | 2 +- sublime/README.md | 2 +- sublime/x65.sublime-package | Bin 3657 -> 4028 bytes x65.cpp | 577 ++++++++++++++++++++++++++++++------ 4 files changed, 492 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index c39a396..be46271 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# Asm6502 6502 Macro Assembler in a single c++ file using the struse single file text parsing library. Supports most syntaxes. Every assembler seems to add or change its own quirks to the 6502 syntax. This implementation aims to support all of them at once as long as there is no contradiction. To keep up with this trend Asm6502 is adding the following features to the mix: * Full expression evaluation everywhere values are used: [Expressions](#expressions) * Basic relative sections and linking. * C style scoping within '{' and '}': [Scopes](#scopes) * Reassignment of labels. This means there is no error if you declare the same label twice, but on the other hand you can do things like label = label + 2. * [Local labels](#labels) can be defined in a number of ways, such as leading period (.label) or leading at-sign (@label) or terminating dollar sign (label$). * [Directives](#directives) support both with and without leading period. * Labels don't need to end with colon, but they can. * No indentation required for instructions, meaning that labels can't be mnemonics, macros or directives. * Conditional assembly with #if/#ifdef/#else etc. * As far as achievable, support the syntax of other 6502 assemblers (Merlin syntax now requires command line argument). In summary, if you are familiar with any 6502 assembler syntax you should feel at home with Asm6502. If you're familiar with C programming expressions you should be familiar with '{', '}' scoping and complex expressions. There are no hard limits on binary size so if the address exceeds $ffff it will just wrap around to $0000. I'm not sure about the best way to handle that or if it really is a problem. There is a sublime package for coding/building in Sublime Text 3 in the *sublime* subfolder. ## Prerequisite Asm6502.cpp requires struse.h which is a single file text parsing library that can be retrieved from https://github.com/Sakrac/struse. ### References * [6502 opcodes](http://www.6502.org/tutorials/6502opcodes.html) * [6502 opcode grid](http://www.llx.com/~nparker/a2/opcodes.html) * [Codebase64 CPU section](http://codebase64.org/doku.php?id=base:6502_6510_coding) ## Features * **Code** * **Comments** * **Labels** * **Directives** * **Macros** * **Expressions** ### Code Code is any valid mnemonic/opcode and addressing mode. At the moment only one opcode per line is assembled. ### Comments Comments are currently line based and both ';' and '//' are accepted as delimiters. ### Expressions Anywhere a number can be entered it can also be interpreted as a full expression, for example: ``` Get123: bytes Get1-*, Get2-*, Get3-* Get1: lda #1 rts Get2: lda #2 rts Get3: lda #3 rts ``` Would yield 3 bytes where the address of a label can be calculated by taking the address of the byte plus the value of the byte. ### Labels Labels come in two flavors: **Addresses** (PC based) or **Values** (Evaluated from an expression). An address label is simply placed somewhere in code and a value label is follwed by '**=**' and an expression. All labels are rewritable so it is fine to do things like NumInstance = NumInstance+1. Value assignments can be prefixed with '.const' or '.label' but is not required to be prefixed by anything. *Local labels* exist inbetween *global labels* and gets discarded whenever a new global label is added. The syntax for local labels are one of: prefix with period, at-sign, exclamation mark or suffix with $, as in: **.local** or **!local** or **@local** or **local$**. Both value labels and address labels can be local labels. ``` Function: ; global label ldx #32 .local_label ; local label dex bpl .local_label rts Next_Function: ; next global label, the local label above is now erased. rts ``` ### Directives Directives are assembler commands that control the code generation but that does not generate code by itself. Some assemblers prefix directives with a period (.org instead of org) so a leading period is accepted but not required for directives. * [**ORG**](#org) (same as **PC**): Set the current compiling address. * [**LOAD**](#load) Set the load address for binary formats that support it. * [**SECTION**](#section) Start a relative section * [**LINK**](#link) Link a relative section at this address * [**ALIGN**](#align) Align the address to a multiple by filling with 0s * [**MACRO**](#macro) Declare a macro * [**EVAL**](#eval) Log an expression during assembly. * [**BYTES**](#bytes) Insert comma separated bytes at this address (same as **BYTE** or **DC.B**) * [**WORDS**](#words) Insert comma separated 16 bit values at this address (same as **WORD** or **DC.W**) * [**TEXT**](#text) Insert text at this address * [**INCLUDE**](#include) Include another source file and assemble at this address * [**INCBIN**](#incbin) Include a binary file at this address * [**CONST**](#const) Assign a value to a label and make it constant (error if reassigned with other value) * [**LABEL**](#label) Decorative directive to assign an expression to a label * [**INCSYM**](#incsym) Include a symbol file with an optional set of wanted symbols. * [**POOL**](#pool) Add a label pool for temporary address labels * [**#IF / #ELSE / #IFDEF / #ELIF / #ENDIF**](#conditional) Conditional assembly * [**STRUCT**](#struct) Hierarchical data structures (dot separated sub structures) * [**REPT**](#rept) Repeat a scoped block of code a number of times. * [**INCDIR**](#incdir) Add a directory to look for binary and text include files in. * [**MERLIN**](#merlin) A variety of directives and label rules to support Merlin assembler sources **ORG** ``` org $2000 (or pc $2000) ``` Sets the current assembler address to this address **SECTION** ``` section Code Start: lda #Data sta $ff rts section BSS Data: byte 1,2,3,4 ``` Starts a relative section. Relative sections require a name and sections that share the same name will be linked sequentially. The labels will be evaluated at link time. **LINK** Link a set of relative sections (sharing the same name) at this address The following lines will place all sections named Code sequentially at location $1000, followed by all sections named BSS: ``` org $1000 link Code link BSS ``` Currently there are no object files so there is not a great use of the link directive yet. When object files exist this feature will make working in big projects easier. **LOAD** ``` load $2000 ``` For c64 .prg files this prefixes the binary file with this address. **ALIGN** ``` align $100 ``` Add bytes of 0 up to the next address divisible by the alignment **MACRO** See the 'Macro' section below **EVAL** Example: ``` eval Current PC: * ``` Might yield the following in stdout: ``` Eval (15): Current PC : "*" = $2010 ``` When eval is encountered on a line print out "EVAL (\) \: \ = \" to stdout. This can be useful to see the size of things or debugging expressions. **BYTES** Adds the comma separated values on the current line to the assembled output, for example ``` RandomBytes: bytes NumRandomBytes { bytes 13,1,7,19,32 NumRandomBytes = * - ! } ``` **byte** or **dc.b** are also recognized. **WORDS** Adds comma separated 16 bit values similar to how **BYTES** work. **word** or **dc.w** are also recognized. **TEXT** Copies the string in quotes on the same line. The plan is to do a petscii conversion step. Use the modifier 'petscii' or 'petscii_shifted' to convert alphabetic characters to range. Example: ``` text petscii_shifted "This might work" ``` **INCLUDE** Include another source file. This should also work with .sym files to import labels from another build. The plan is for Asm6502 to export .sym files as well. Example: ``` include "wizfx.s" ``` **INCBIN** Include binary data from a file, this inserts the binary data at the current address. Example: ``` incbin "wizfx.gfx" ``` **CONST** Prefix a label assignment with 'const' or '.const' to cause an error if the label gets reassigned. ``` const zpData = $fe ``` **LABEL** Decorative directive to assign an expression to a label, label assignments are followed by '=' and an expression. These two assignments do the same thing (with different values): ``` label zpDest = $fc zpDest = $fa ``` **INCSYM** Include a symbol file with an optional set of wanted symbols. Open a symbol file and extract a set of symbols, or all symbols if no set was specified. Local labels will be discarded if possible. ``` incsym Part1_Init, Part1_Update, Part1_Exit "part1.sym" ``` **POOL** Add a label pool for temporary address labels. This is similar to how stack frame variables are assigned in C. A label pool is a mini stack of addresses that can be assigned as temporary labels with a scope ('{' and '}'). This can be handy for large functions trying to minimize use of zero page addresses, the function can declare a range (or set of ranges) of available zero page addresses and labels can be assigned within a scope and be deleted on scope closure. The format of a label pool is: "pool start-end, start-end" and labels can then be allocated from that range by ' **STRUCT** Hierarchical data structures (dot separated sub structures) Structs helps define complex data types, there are two basic types to define struct members, and as long as a struct is declared it can be used as a member type of another struct. The result of a struct is that each member is an offset from the start of the data block in memory. Each substruct is referenced by separating the struct names with dots. Example: ``` struct MyStruct { byte count word pointer } struct TwoThings { MyStruct thing_one MyStruct thing_two } struct Mixed { word banana TwoThings things } Eval Mixed.things Eval Mixed.things.thing_two Eval Mixed.things.thing_two.pointer Eval Mixed.things.thing_one.count ``` results in the output: ``` EVAL(16): "Mixed.things" = $2 EVAL(27): "Mixed.things.thing_two" = $5 EVAL(28): "Mixed.things.thing_two.pointer" = $6 EVAL(29): "Mixed.things.thing_one.count" = $2 ``` **REPT** Repeat a scoped block of code a number of times. The syntax is rept \ { \ }. Example: ``` columns = 40 rows = 25 screen_col = $400 height_buf = $1000 rept columns { screen_addr = screen_col ldx height_buf dest = screen_addr remainder = 3 rept (rows+remainder)/4 { stx dest dest = dest + 4*40 } rept 3 { inx remainder = remainder-1 screen_addr = screen_addr + 40 dest = screen_addr rept (rows+remainder)/4 { stx dest dest = dest + 4*40 } } screen_col = screen_col+1 height_buf = height_buf+1 } ``` **INCDIR** Adds a folder to search for INCLUDE, INCBIN, etc. files in ###**MERLIN** A variety of directives and label rules to support Merlin assembler sources. Merlin syntax is supported in Asm6502 since there is historic relevance and readily available publicly release source. * [Pinball Construction Set source](https://github.com/billbudge/PCS_AppleII) (Bill Budge) * [Prince of Persia source](https://github.com/jmechner/Prince-of-Persia-Apple-II) (Jordan Mechner) To enable Merlin 8.16 syntax use the '-merlin' command line argument. Where it causes no harm, Merlin directives are supported for non-merlin mode. *LABELS* ]label means mutable address label, also does not seem to invalidate local labels. :label is perfectly valid, currently treating as a local variable labels can include '?' Merlin labels are not allowed to include '.' as period means logical or in merlin, which also means that enums and structs are not supported when assembling with merlin syntax. *Expressions* Merlin may not process expressions (probably left to right, parenthesis not allowed) the same as Asm6502 but given that it wouldn't be intuitive to read the code that way, there are probably very few cases where this would be an issue. **EJECT** An old assembler directive that does not affect the assembler but if printed would insert a page break at that point. **DS** Define section, followed by a number of bytes. If number is positive insert this amount of 0 bytes, if negative, reduce the current PC. **DUM**, **DEND** Dummy section, this will not write any opcodes or data to the binary output but all code and data will increment the PC addres up to the point of DEND. **PUT** A variation of **INCLUDE** that applies an oddball set of filename rules. These rules apply to **INCLUDE** as well just in case they make sense. **USR** In Merlin USR calls a function at a fixed address in memory, Asm6502 safely avoids this. If there is a requirement for a user defined macro you've got the source code to do it in. ## Command Line Options Typical command line: ``` Asm6502 [-DLabel] [-iIncDir] [-sym dest.sym] [-vice dest.vs] [-c64]/[-bin]/[-a2b] [-merlin] ``` **Usage** Asm6502 [options] filename.s code.prg * -i\: Add include path (multiple allowed) * -D\[=\]: Define a label with an optional value (otherwise 1, multiple allowed) * -bin: Raw binary * -c64: Include load address * -a2b: Apple II Dos 3.3 Binary Executable * -sym \: vice/kick asm symbol file * -vice \: export a vice symbol file * -merlin: Assembler syntax for Apple II Merlin 8.16 ## Expression syntax Expressions contain values, such as labels or raw numbers and operators including +, -, \*, /, & (and), | (or), ^ (eor), << (shift left), >> (shift right) similar to how expressions work in C. Parenthesis are supported for managing order of operations where C style precedence needs to be overrided. In addition there are some special characters supported: * \*: Current address (PC). This conflicts with the use of \* as multiply so multiply will be interpreted only after a value or right parenthesis * <: If less than is not follwed by another '<' in an expression this evaluates to the low byte of a value (and $ff) * >: If greater than is not followed by another '>' in an expression this evaluates to the high byte of a value (>>8) * !: Start of scope (use like an address label in expression) * %: First address after scope (use like an address label in expression) * $: Preceeds hexadecimal value * %: If immediately followed by '0' or '1' this is a binary value and not scope closure address Example: ``` lda #(((>SCREEN_MATRIX)&$3c)*4)+8 sta $d018 ``` ## Macros A macro can be defined by the using the directive macro and includes the line within the following scope: Example: ``` macro ShiftLeftA(Source) { rol Source rol A } ``` The macro will be instantiated anytime the macro name is encountered: ``` lda #0 ShiftLeftA($a0) ``` The parameter field is optional for both the macro declaration and instantiation, if there is a parameter in the declaration but not in the instantiation the parameter will be removed from the macro. If there are no parameters in the declaration the parenthesis can be omitted and will be slightly more efficient to assemble, as in: ``` .macro GetBit { asl bne % jsr GetByte } ``` Currently macros with parameters use search and replace without checking if the parameter is a whole word, the plan is to fix this. ## Scopes Scopes are lines inbetween '{' and '}' including macros. The purpose of scopes is to reduce the need for local labels and the scopes nest just like C code to support function level and loops and inner loop scoping. '!' is a label that is the first address of the scope and '%' the first address after the scope. This means you can write ``` { lda #0 ldx #8 { sta Label,x dex bpl ! } } ``` to construct a loop without adding a label. ##Examples Using scoping to avoid local labels ``` ; set zpTextPtr to a memory location with text ; return: y is the offset to the first space. ; (y==0 means either first is space or not found.) FindFirstSpace ldy #0 { lda (zpTextPtr),y cmp #$20 beq % ; found, exit iny bne ! ; not found, keep searching } rts ``` ### Development Status Currently the assembler is in an early revision and while features are tested individually it is fairly certain that untested combinations of features will indicate flaws and certain features are not in a complete state. **TODO** * Object file format so sections can be saved for later linking * Export full memory of fixed sections instead of a single section * Macro parameters should replace only whole words instead of any substring * Add 'import' directive as a catch-all include/incbin/etc. alternative * irp (indefinite repeat) **FIXED** * Added relative sections and relocatable references * Added Apple II Dos 3.3 Binary Executable output (-a2b) * Added more Merlin rules * Added directives from older assemblers * Added ENUM, sharing some functionality with STRUCT * Added INCDIR and command line options * [REPT](#rept) * fixed a flaw in expressions that ignored the next operator after raw hex values if no whitespace * expressions now handles high byte/low byte (\>, \<) as RPN tokens and not special cased. * structs * ifdef / if / elif / else / endif conditional code generation directives * Label Pools added * Bracket scoping closure ('}') cleans up local variables within that scope (better handling of local variables within macros). * Context stack cleanup * % in expressions is interpreted as binary value if immediately followed by 0 or 1 * Add a const directive for labels that shouldn't be allowed to change (currently ignoring const) * TEXT directive converts ascii to petscii (respect uppercase or lowercase petscii) (simplistic) Revisions: * 2015-10-10 Relative Sections and Link support, adding -merlin command line to clean up code * 2015-10-06 Added ENUM and MERLIN / LISA assembler directives (EJECT, DUM, DEND, DS, DB, DFB, DDB, IF, ENDIF, etc.) * 2015-10-05 Added INCDIR, some command line options (-D, -i, -vice) * 2015-10-04 Added [REPT](#rept) directive * 2015-10-04 Added [STRUCT](#struct) directive, sorted functions by grouping a bit more, bug fixes * 2015-10-02 Cleanup hid an error (#else without #if), exit with nonzero if error was encountered * 2015-10-02 General cleanup, wrapping [conditional assembly](#conditional) in functions * 2015-10-01 Added [Label Pools](#pool) and conditional assembly * 2015-09-29 Moved Asm6502 out of Struse Samples. * 2015-09-28 First commit \ No newline at end of file +# x65 6502 Macro Assembler in a single c++ file using the struse single file text parsing library. Supports most syntaxes. x65 was recently named Asm6502 but was renamed because Asm6502 is too generic, x65 has no particular meaning. Every assembler seems to add or change its own quirks to the 6502 syntax. This implementation aims to support all of them at once as long as there is no contradiction. To keep up with this trend x65 is adding the following features to the mix: * Full expression evaluation everywhere values are used: [Expressions](#expressions) * Basic relative sections and linking. * C style scoping within '{' and '}': [Scopes](#scopes) * Reassignment of labels. This means there is no error if you declare the same label twice, but on the other hand you can do things like label = label + 2. * [Local labels](#labels) can be defined in a number of ways, such as leading period (.label) or leading at-sign (@label) or terminating dollar sign (label$). * [Directives](#directives) support both with and without leading period. * Labels don't need to end with colon, but they can. * No indentation required for instructions, meaning that labels can't be mnemonics, macros or directives. * Conditional assembly with #if/#ifdef/#else etc. * As far as achievable, support the syntax of other 6502 assemblers (Merlin syntax now requires command line argument). In summary, if you are familiar with any 6502 assembler syntax you should feel at home with x65. If you're familiar with C programming expressions you should be familiar with '{', '}' scoping and complex expressions. There are no hard limits on binary size so if the address exceeds $ffff it will just wrap around to $0000. I'm not sure about the best way to handle that or if it really is a problem. There is a sublime package for coding/building in Sublime Text 3 in the *sublime* subfolder. ## Prerequisite x65.cpp requires struse.h which is a single file text parsing library that can be retrieved from https://github.com/Sakrac/struse. ### References * [6502 opcodes](http://www.6502.org/tutorials/6502opcodes.html) * [6502 opcode grid](http://www.llx.com/~nparker/a2/opcodes.html) * [Codebase64 CPU section](http://codebase64.org/doku.php?id=base:6502_6510_coding) ## Features * **Code** * **Comments** * **Labels** * **Directives** * **Macros** * **Expressions** ### Code Code is any valid mnemonic/opcode and addressing mode. At the moment only one opcode per line is assembled. ### Comments Comments are currently line based and both ';' and '//' are accepted as delimiters. ### Expressions Anywhere a number can be entered it can also be interpreted as a full expression, for example: ``` Get123: bytes Get1-*, Get2-*, Get3-* Get1: lda #1 rts Get2: lda #2 rts Get3: lda #3 rts ``` Would yield 3 bytes where the address of a label can be calculated by taking the address of the byte plus the value of the byte. ### Labels Labels come in two flavors: **Addresses** (PC based) or **Values** (Evaluated from an expression). An address label is simply placed somewhere in code and a value label is follwed by '**=**' and an expression. All labels are rewritable so it is fine to do things like NumInstance = NumInstance+1. Value assignments can be prefixed with '.const' or '.label' but is not required to be prefixed by anything. *Local labels* exist inbetween *global labels* and gets discarded whenever a new global label is added. The syntax for local labels are one of: prefix with period, at-sign, exclamation mark or suffix with $, as in: **.local** or **!local** or **@local** or **local$**. Both value labels and address labels can be local labels. ``` Function: ; global label ldx #32 .local_label ; local label dex bpl .local_label rts Next_Function: ; next global label, the local label above is now erased. rts ``` ### Directives Directives are assembler commands that control the code generation but that does not generate code by itself. Some assemblers prefix directives with a period (.org instead of org) so a leading period is accepted but not required for directives. * [**ORG**](#org) (same as **PC**): Set the current compiling address. * [**LOAD**](#load) Set the load address for binary formats that support it. * [**SECTION**](#section) Start a relative section * [**LINK**](#link) Link a relative section at this address * [**ALIGN**](#align) Align the address to a multiple by filling with 0s * [**MACRO**](#macro) Declare a macro * [**EVAL**](#eval) Log an expression during assembly. * [**BYTES**](#bytes) Insert comma separated bytes at this address (same as **BYTE** or **DC.B**) * [**WORDS**](#words) Insert comma separated 16 bit values at this address (same as **WORD** or **DC.W**) * [**TEXT**](#text) Insert text at this address * [**INCLUDE**](#include) Include another source file and assemble at this address * [**INCBIN**](#incbin) Include a binary file at this address * [**CONST**](#const) Assign a value to a label and make it constant (error if reassigned with other value) * [**LABEL**](#label) Decorative directive to assign an expression to a label * [**INCSYM**](#incsym) Include a symbol file with an optional set of wanted symbols. * [**POOL**](#pool) Add a label pool for temporary address labels * [**#IF / #ELSE / #IFDEF / #ELIF / #ENDIF**](#conditional) Conditional assembly * [**STRUCT**](#struct) Hierarchical data structures (dot separated sub structures) * [**REPT**](#rept) Repeat a scoped block of code a number of times. * [**INCDIR**](#incdir) Add a directory to look for binary and text include files in. * [**MERLIN**](#merlin) A variety of directives and label rules to support Merlin assembler sources **ORG** ``` org $2000 (or pc $2000) ``` Sets the current assembler address to this address **SECTION** ``` section Code Start: lda #Data sta $ff rts section BSS Data: byte 1,2,3,4 ``` Starts a relative section. Relative sections require a name and sections that share the same name will be linked sequentially. The labels will be evaluated at link time. **LINK** Link a set of relative sections (sharing the same name) at this address The following lines will place all sections named Code sequentially at location $1000, followed by all sections named BSS: ``` org $1000 link Code link BSS ``` There is currently object file support (use -obj argument to generate), currently doing a lot of testing to make sure it works as expected. At this time all labels from object files are globally visible causing potential confusion if two files share the same name labels and XDEF is intended to mark labels as global to protect file scope labels. **LOAD** ``` load $2000 ``` For c64 .prg files this prefixes the binary file with this address. **ALIGN** ``` align $100 ``` Add bytes of 0 up to the next address divisible by the alignment **MACRO** See the 'Macro' section below **EVAL** Example: ``` eval Current PC: * ``` Might yield the following in stdout: ``` Eval (15): Current PC : "*" = $2010 ``` When eval is encountered on a line print out "EVAL (\) \: \ = \" to stdout. This can be useful to see the size of things or debugging expressions. **BYTES** Adds the comma separated values on the current line to the assembled output, for example ``` RandomBytes: bytes NumRandomBytes { bytes 13,1,7,19,32 NumRandomBytes = * - ! } ``` **byte** or **dc.b** are also recognized. **WORDS** Adds comma separated 16 bit values similar to how **BYTES** work. **word** or **dc.w** are also recognized. **TEXT** Copies the string in quotes on the same line. The plan is to do a petscii conversion step. Use the modifier 'petscii' or 'petscii_shifted' to convert alphabetic characters to range. Example: ``` text petscii_shifted "This might work" ``` **INCLUDE** Include another source file. This should also work with .sym files to import labels from another build. The plan is for x65 to export .sym files as well. Example: ``` include "wizfx.s" ``` **INCBIN** Include binary data from a file, this inserts the binary data at the current address. Example: ``` incbin "wizfx.gfx" ``` **CONST** Prefix a label assignment with 'const' or '.const' to cause an error if the label gets reassigned. ``` const zpData = $fe ``` **LABEL** Decorative directive to assign an expression to a label, label assignments are followed by '=' and an expression. These two assignments do the same thing (with different values): ``` label zpDest = $fc zpDest = $fa ``` **INCSYM** Include a symbol file with an optional set of wanted symbols. Open a symbol file and extract a set of symbols, or all symbols if no set was specified. Local labels will be discarded if possible. ``` incsym Part1_Init, Part1_Update, Part1_Exit "part1.sym" ``` **POOL** Add a label pool for temporary address labels. This is similar to how stack frame variables are assigned in C. A label pool is a mini stack of addresses that can be assigned as temporary labels with a scope ('{' and '}'). This can be handy for large functions trying to minimize use of zero page addresses, the function can declare a range (or set of ranges) of available zero page addresses and labels can be assigned within a scope and be deleted on scope closure. The format of a label pool is: "pool start-end, start-end" and labels can then be allocated from that range by ' **STRUCT** Hierarchical data structures (dot separated sub structures) Structs helps define complex data types, there are two basic types to define struct members, and as long as a struct is declared it can be used as a member type of another struct. The result of a struct is that each member is an offset from the start of the data block in memory. Each substruct is referenced by separating the struct names with dots. Example: ``` struct MyStruct { byte count word pointer } struct TwoThings { MyStruct thing_one MyStruct thing_two } struct Mixed { word banana TwoThings things } Eval Mixed.things Eval Mixed.things.thing_two Eval Mixed.things.thing_two.pointer Eval Mixed.things.thing_one.count ``` results in the output: ``` EVAL(16): "Mixed.things" = $2 EVAL(27): "Mixed.things.thing_two" = $5 EVAL(28): "Mixed.things.thing_two.pointer" = $6 EVAL(29): "Mixed.things.thing_one.count" = $2 ``` **REPT** Repeat a scoped block of code a number of times. The syntax is rept \ { \ }. Example: ``` columns = 40 rows = 25 screen_col = $400 height_buf = $1000 rept columns { screen_addr = screen_col ldx height_buf dest = screen_addr remainder = 3 rept (rows+remainder)/4 { stx dest dest = dest + 4*40 } rept 3 { inx remainder = remainder-1 screen_addr = screen_addr + 40 dest = screen_addr rept (rows+remainder)/4 { stx dest dest = dest + 4*40 } } screen_col = screen_col+1 height_buf = height_buf+1 } ``` **INCDIR** Adds a folder to search for INCLUDE, INCBIN, etc. files in ###**MERLIN** A variety of directives and label rules to support Merlin assembler sources. Merlin syntax is supported in x65 since there is historic relevance and readily available publicly release source. * [Pinball Construction Set source](https://github.com/billbudge/PCS_AppleII) (Bill Budge) * [Prince of Persia source](https://github.com/jmechner/Prince-of-Persia-Apple-II) (Jordan Mechner) To enable Merlin 8.16 syntax use the '-merlin' command line argument. Where it causes no harm, Merlin directives are supported for non-merlin mode. *LABELS* ]label means mutable address label, also does not seem to invalidate local labels. :label is perfectly valid, currently treating as a local variable labels can include '?' Merlin labels are not allowed to include '.' as period means logical or in merlin, which also means that enums and structs are not supported when assembling with merlin syntax. *Expressions* Merlin may not process expressions (probably left to right, parenthesis not allowed) the same as x65 but given that it wouldn't be intuitive to read the code that way, there are probably very few cases where this would be an issue. **EJECT** An old assembler directive that does not affect the assembler but if printed would insert a page break at that point. **DS** Define section, followed by a number of bytes. If number is positive insert this amount of 0 bytes, if negative, reduce the current PC. **DUM**, **DEND** Dummy section, this will not write any opcodes or data to the binary output but all code and data will increment the PC addres up to the point of DEND. **PUT** A variation of **INCLUDE** that applies an oddball set of filename rules. These rules apply to **INCLUDE** as well just in case they make sense. **USR** In Merlin USR calls a function at a fixed address in memory, x65 safely avoids this. If there is a requirement for a user defined macro you've got the source code to do it in. ## Command Line Options Typical command line: ``` x65 [-DLabel] [-iIncDir] [-sym dest.sym] [-vice dest.vs] [-c64]/[-bin]/[-a2b] [-merlin] ``` **Usage** x65 [options] filename.s code.prg * -i\: Add include path (multiple allowed) * -D\[=\]: Define a label with an optional value (otherwise 1, multiple allowed) * -bin: Raw binary * -c64: Include load address * -a2b: Apple II Dos 3.3 Binary Executable * -sym \: vice/kick asm symbol file * -vice \: export a vice symbol file * -merlin: Assembler syntax for Apple II Merlin 8.16 ## Expression syntax Expressions contain values, such as labels or raw numbers and operators including +, -, \*, /, & (and), | (or), ^ (eor), << (shift left), >> (shift right) similar to how expressions work in C. Parenthesis are supported for managing order of operations where C style precedence needs to be overrided. In addition there are some special characters supported: * \*: Current address (PC). This conflicts with the use of \* as multiply so multiply will be interpreted only after a value or right parenthesis * <: If less than is not follwed by another '<' in an expression this evaluates to the low byte of a value (and $ff) * >: If greater than is not followed by another '>' in an expression this evaluates to the high byte of a value (>>8) * !: Start of scope (use like an address label in expression) * %: First address after scope (use like an address label in expression) * $: Preceeds hexadecimal value * %: If immediately followed by '0' or '1' this is a binary value and not scope closure address Example: ``` lda #(((>SCREEN_MATRIX)&$3c)*4)+8 sta $d018 ``` ## Macros A macro can be defined by the using the directive macro and includes the line within the following scope: Example: ``` macro ShiftLeftA(Source) { rol Source rol A } ``` The macro will be instantiated anytime the macro name is encountered: ``` lda #0 ShiftLeftA($a0) ``` The parameter field is optional for both the macro declaration and instantiation, if there is a parameter in the declaration but not in the instantiation the parameter will be removed from the macro. If there are no parameters in the declaration the parenthesis can be omitted and will be slightly more efficient to assemble, as in: ``` .macro GetBit { asl bne % jsr GetByte } ``` Currently macros with parameters use search and replace without checking if the parameter is a whole word, the plan is to fix this. ## Scopes Scopes are lines inbetween '{' and '}' including macros. The purpose of scopes is to reduce the need for local labels and the scopes nest just like C code to support function level and loops and inner loop scoping. '!' is a label that is the first address of the scope and '%' the first address after the scope. This means you can write ``` { lda #0 ldx #8 { sta Label,x dex bpl ! } } ``` to construct a loop without adding a label. ##Examples Using scoping to avoid local labels ``` ; set zpTextPtr to a memory location with text ; return: y is the offset to the first space. ; (y==0 means either first is space or not found.) FindFirstSpace ldy #0 { lda (zpTextPtr),y cmp #$20 beq % ; found, exit iny bne ! ; not found, keep searching } rts ``` ### Development Status Currently the assembler is in an early revision and while features are tested individually it is fairly certain that untested combinations of features will indicate flaws and certain features are not in a complete state. **TODO** * Split object file non-xdef labels to keep from all labels being global (xdef) * Export full memory of fixed sections instead of a single section * Macro parameters should replace only whole words instead of any substring * Add 'import' directive as a catch-all include/incbin/etc. alternative * irp (indefinite repeat) **FIXED** * Object file format so sections can be saved for later linking * Added relative sections and relocatable references * Added Apple II Dos 3.3 Binary Executable output (-a2b) * Added more Merlin rules * Added directives from older assemblers * Added ENUM, sharing some functionality with STRUCT * Added INCDIR and command line options * [REPT](#rept) * fixed a flaw in expressions that ignored the next operator after raw hex values if no whitespace * expressions now handles high byte/low byte (\>, \<) as RPN tokens and not special cased. * structs * ifdef / if / elif / else / endif conditional code generation directives * Label Pools added * Bracket scoping closure ('}') cleans up local variables within that scope (better handling of local variables within macros). * Context stack cleanup * % in expressions is interpreted as binary value if immediately followed by 0 or 1 * Add a const directive for labels that shouldn't be allowed to change (currently ignoring const) * TEXT directive converts ascii to petscii (respect uppercase or lowercase petscii) (simplistic) Revisions: * 2015-10-10 Relative Sections and Link support, adding -merlin command line to clean up code * 2015-10-06 Added ENUM and MERLIN / LISA assembler directives (EJECT, DUM, DEND, DS, DB, DFB, DDB, IF, ENDIF, etc.) * 2015-10-05 Added INCDIR, some command line options (-D, -i, -vice) * 2015-10-04 Added [REPT](#rept) directive * 2015-10-04 Added [STRUCT](#struct) directive, sorted functions by grouping a bit more, bug fixes * 2015-10-02 Cleanup hid an error (#else without #if), exit with nonzero if error was encountered * 2015-10-02 General cleanup, wrapping [conditional assembly](#conditional) in functions * 2015-10-01 Added [Label Pools](#pool) and conditional assembly * 2015-09-29 Moved Asm6502 out of Struse Samples. * 2015-09-28 First commit \ No newline at end of file diff --git a/sublime/README.md b/sublime/README.md index d68de15..0fa5dae 100644 --- a/sublime/README.md +++ b/sublime/README.md @@ -1 +1 @@ -# Asm6502 - Sublime Text Package [Sublime Text 3](http://www.sublimetext.com/3) is a great text editor that supports custom languages and build scripts, Asm6502.sublime-package is a single file collection of config files that enable Asm6502 language syntax, Asm6502 Build and an Asm6502 color theme that's got some c64 colors in it as well. Updates * 2015-10-06 Fixed some naming, added a bunch more directives as recognized keywords including a bunch for MERLIN / LISA assemblers. Copy Asm6502.sublime-package from this folder to: Windows: ``` %USERPROFILE%\AppData\Roaming\Sublime Text 3\Installed Packages\ ``` OSX: ``` ~/Library/Application Support/Sublime Text 3/Installed Packages/ ``` Compile a 64 or 32 bit release build of Asm6502 and put it into c:\c64\asm6502\asm6502.exe Download the Vice C64 Emulator into c:\vice or c:\c64\vice Within Sublime Text set the Build System to Asm6502 with a 6502 .s or .asm source file loaded and press Ctrl+B to build, or Ctrl+6 to build and launch Vice with the built prg file. With sincere apologies to the author of the Sublime Text package for Kick Assembler without which I would have spent countless hours to figure this out, check out the kick assembler Sublime package here: http://goatpower.org/projects-releases/sublime-package-kick-assembler-c64/ Feel free to modify this package as desired. To open up the individual files add a '.zip' extension to Asm6502.sublime-package and unzip it and paste the files to ``` %USERPROFILE%\AppData\Roaming\Sublime Text 3\Packages\Asm6502 ``` \ No newline at end of file +# x65 - Sublime Text Package [Sublime Text 3](http://www.sublimetext.com/3) is a great text editor that supports custom languages and build scripts, x65.sublime-package is a single file collection of config files that enable Asm6502 language syntax, Asm6502 Build and an Asm6502 color theme that's got some c64 colors in it as well. Updates * 2015-10-06 Fixed some naming, added a bunch more directives as recognized keywords including a bunch for MERLIN / LISA assemblers. Copy x65.sublime-package from this folder to: Windows: ``` %USERPROFILE%\AppData\Roaming\Sublime Text 3\Installed Packages\ ``` OSX: ``` ~/Library/Application Support/Sublime Text 3/Installed Packages/ ``` Compile a 64 or 32 bit release build of Asm6502 and put it into c:\c64\asm6502\asm6502.exe Download the Vice C64 Emulator into c:\vice or c:\c64\vice Within Sublime Text set the Build System to x65 with a 6502 .s or .asm source file loaded and press Ctrl+B to build, or Ctrl+6 to build and launch Vice with the built prg file. With sincere apologies to the author of the Sublime Text package for Kick Assembler without which I would have spent countless hours to figure this out, check out the kick assembler Sublime package here: http://goatpower.org/projects-releases/sublime-package-kick-assembler-c64/ Feel free to modify this package as desired, or just unzip it if sublime doesn't recogize the package. To open up the individual files add a '.zip' extension to Asm6502.sublime-package and unzip it and paste the files to ``` %USERPROFILE%\AppData\Roaming\Sublime Text 3\Packages ``` \ No newline at end of file diff --git a/sublime/x65.sublime-package b/sublime/x65.sublime-package index 3a7845cc04385137c756d457115a45d052460f01..7e1b78cee7dd8072867ea4eab177848e2096a703 100644 GIT binary patch literal 4028 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 G9sLiMVx1iT literal 3657 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 dZ69a)504Ln&>oJ5007 MapSymbolArray; @@ -564,6 +569,7 @@ public: bool evaluated; // a value may not yet be evaluated bool pc_relative; // this is an inline label describing a point in the code bool constant; // the value of this label can not change + bool external; // this label is globally accessible } Label; // If an expression can't be evaluated immediately, this is required @@ -575,7 +581,7 @@ typedef struct { LET_BRANCH, // calculate a branch offset and store at this address LET_BYTE, // calculate a byte and store at this address }; - size_t target; // offset into output buffer + int target; // offset into output buffer int address; // current pc int scope; // scope pc int section; // which section to apply to. @@ -709,7 +715,7 @@ public: bool errorEncountered; // Convert source to binary - void Assemble(strref source, strref filename); + void Assemble(strref source, strref filename, bool obj_target); // Clean up memory allocations, reset assembler state void Cleanup(); @@ -721,6 +727,7 @@ public: void SetSection(strref name, int address); // fixed address section void SetSection(strref name); // relative address section StatusCode LinkSections(strref name); // link relative address sections with this name here + void LinkLabelsToAddress(int section_id, int section_address); void DummySection(int address); void DummySection(); void EndSection(); @@ -731,6 +738,10 @@ public: void AddWord(int w) { CurrSection().AddWord(w); } void AddBin(unsigned const char *p, int size) { CurrSection().AddBin(p, size); } + // Object file handling + StatusCode WriteObjectFile(strref filename); + StatusCode ReadObjectFile(strref filename); + // Macro management StatusCode AddMacro(strref macro, strref source_name, strref source_file, strref &left); StatusCode BuildMacro(Macro &m, strref arg_list); @@ -767,7 +778,7 @@ public: void FlushLabelPools(int scope_exit); // Late expression evaluation - void AddLateEval(int pc, int scope_pc, strref expression, + void AddLateEval(int target, int pc, int scope_pc, strref expression, strref source_file, LateEval::Type type); void AddLateEval(strref label, int pc, int scope_pc, strref expression, LateEval::Type type); @@ -884,6 +895,9 @@ char* Asm::LoadBinary(strref filename, size_t &size) { if (file.get_last()!='/' && file.get_last()!='\\') file.append('/'); file.append(filename); +#ifdef WIN32 + file.replace('/', '\\'); +#endif ++i; } size = 0; @@ -971,6 +985,26 @@ Section& Asm::ExportSection() { return CurrSection(); } +// Apply labels assigned to addresses in a relative section a fixed address +void Asm::LinkLabelsToAddress(int section_id, int section_address) +{ + Label *pLabels = labels.getValues(); + int numLabels = labels.count(); + for (int l = 0; l < numLabels; l++) { + if (pLabels->section == section_id) { + pLabels->value += section_address; + pLabels->section = -1; + if (pLabels->mapIndex>=0 && pLabels->mapIndex<(int)map.size()) { + struct MapSymbol &msym = map[pLabels->mapIndex]; + msym.value = pLabels->value; + msym.resolved = true; + } + CheckLateEval(pLabels->label_name); + } + ++pLabels; + } +} + StatusCode Asm::LinkSections(strref name) { if (CurrSection().IsRelativeSection()) return ERROR_LINKER_MUST_BE_IN_FIXED_ADDRESS_SECTION; @@ -1004,16 +1038,7 @@ StatusCode Asm::LinkSections(strref name) { s.merged_offset = (int)(section_out - CurrSection().output); // All labels in this section can now be assigned - Label *pLabels = labels.getValues(); - int numLabels = labels.count(); - for (int l = 0; l < numLabels; l++) { - if (pLabels->section == section_id) { - pLabels->value += section_address; - pLabels->section = -1; - CheckLateEval(pLabels->label_name); - } - ++pLabels; - } + LinkLabelsToAddress(section_id, section_address); // go through relocs in all sections to see if any targets this section // relocate section to address! @@ -1099,6 +1124,9 @@ void Section::AddReloc(int base, int offset, int section, Reloc::Type type) pRelocs = new relocList; if (pRelocs->size() == pRelocs->capacity()) pRelocs->reserve(pRelocs->size() + 32); + + printf("Add reloc base = $0x%04x offs=$0x%04x section=$0x%04x type = %d\n", base, offset, section, type); + pRelocs->push_back(Reloc(base, offset, section, type)); } @@ -1694,18 +1722,18 @@ StatusCode Asm::EvalExpression(strref expression, int pc, int scope_pc, int scop // if an expression could not be evaluated, add it along with // the action to perform if it can be evaluated later. -void Asm::AddLateEval(int pc, int scope_pc, strref expression, strref source_file, LateEval::Type type) +void Asm::AddLateEval(int target, int pc, int scope_pc, strref expression, strref source_file, LateEval::Type type) { LateEval le; le.address = pc; le.scope = scope_pc; - le.target = CurrSection().DataOffset(); + le.target = target; le.section = (int)(&CurrSection() - &allSections[0]); le.label.clear(); le.expression = expression; le.source_file = source_file; le.type = type; - + lateEval.push_back(le); } @@ -1761,7 +1789,7 @@ StatusCode Asm::CheckLateEval(strref added_label, int scope_end) i->type==LateEval::LET_BRANCH ? SectionId() : -1, value); if (ret == STATUS_OK || ret==STATUS_RELATIVE_SECTION) { // Check if target section merged with another section - size_t trg = i->target; + int trg = i->target; int sec = i->section; if (i->type != LateEval::LET_LABEL) { if (allSections[sec].IsMergedSection()) { @@ -1782,7 +1810,7 @@ StatusCode Asm::CheckLateEval(strref added_label, int scope_end) if (i->section<0) resolved = false; else { - allSections[i->section].AddReloc(lastEvalValue, i->address, lastEvalSection, + allSections[sec].AddReloc(lastEvalValue, trg, lastEvalSection, lastEvalPart==Reloc::HI_BYTE ? Reloc::HI_BYTE : Reloc::LO_BYTE); value = 0; } @@ -1794,7 +1822,7 @@ StatusCode Asm::CheckLateEval(strref added_label, int scope_end) if (i->section<0) resolved = false; else { - allSections[i->section].AddReloc(lastEvalValue, i->address, lastEvalSection, lastEvalPart); + allSections[sec].AddReloc(lastEvalValue, trg, lastEvalSection, lastEvalPart); value = 0; } } @@ -1839,7 +1867,7 @@ StatusCode Asm::CheckLateEval(strref added_label, int scope_end) -// Get a labelc record if it exists +// Get a label record if it exists Label *Asm::GetLabel(strref label) { unsigned int label_hash = label.fnv1a(); @@ -1860,10 +1888,10 @@ void Asm::LabelAdded(Label *pLabel, bool local) map.reserve(map.size() + 256); MapSymbol sym; sym.name = pLabel->label_name; - sym.section = pLabel->section; + sym.resolved = pLabel->section < 0; sym.value = pLabel->value; sym.local = local; - if (sym.section>=0) + if (!sym.resolved) pLabel->mapIndex = (int)map.size(); else pLabel->mapIndex = -1; @@ -2022,6 +2050,7 @@ StatusCode Asm::AssignPoolLabel(LabelPool &pool, strref label) pLabel->value = addr; pLabel->pc_relative = true; pLabel->constant = true; + pLabel->external = false; MarkLabelLocal(label, true); return error; @@ -2117,7 +2146,8 @@ StatusCode Asm::AssignLabel(strref label, strref line, bool make_constant) pLabel->mapIndex = -1; pLabel->pc_relative = false; pLabel->constant = make_constant; - + pLabel->external = false; + bool local = label[0]=='.' || label[0]=='@' || label[0]=='!' || label[0]==':' || label.get_last()=='$'; if (!pLabel->evaluated) AddLateEval(label, CurrSection().GetPC(), scope_address[scope_depth], line, LateEval::LET_LABEL); @@ -2148,6 +2178,7 @@ StatusCode Asm::AddressLabel(strref label) pLabel->value = CurrSection().GetPC(); pLabel->evaluated = true; pLabel->pc_relative = true; + pLabel->external = false; pLabel->constant = constLabel; bool local = label[0]=='.' || label[0]=='@' || label[0]=='!' || label[0]==':' || label.get_last()=='$'; LabelAdded(pLabel, local); @@ -2340,8 +2371,10 @@ DirectiveName aDirectiveNames[] { { "ORG", AD_ORG }, { "LOAD", AD_LOAD }, { "SECTION", AD_SECTION }, -// { "SEG", AD_SECTION }, // DASM version of SECTION + { "SEG", AD_SECTION }, // DASM version of SECTION { "LINK", AD_LINK }, + { "XDEF", AD_XDEF }, + { "INCOBJ", AD_INCOBJ }, { "ALIGN", AD_ALIGN }, { "MACRO", AD_MACRO }, { "EVAL", AD_EVAL }, @@ -2422,6 +2455,7 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc CurrSection().load_address = addr; CurrSection().address = addr; CurrSection().address_assigned = true; + LinkLabelsToAddress(SectionId(), addr); // in case any labels were defined prior to org & data } else SetSection(strref(), addr); break; @@ -2448,6 +2482,19 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc error = LinkSections(line); break; + case AD_INCOBJ: { + strref file = line.between('"', '"'); + if (!file) // MERLIN: No quotes around PUT filenames + file = line.split_range(filename_end_char_range); + size_t size = 0; + error = ReadObjectFile(file); + break; + } + + case AD_XDEF: + // this will store a string that when matched with a label will make that label external + break; + case AD_ALIGN: // align: align address to multiple of value, fill space with 0 if (line) { if (line[0]=='=' || keyword_equ.is_prefix_word(line)) @@ -2492,9 +2539,9 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc if (error>STATUS_NOT_READY) break; else if (error==STATUS_NOT_READY) - AddLateEval(CurrSection().GetPC(), scope_address[scope_depth], exp, source_file, LateEval::LET_BYTE); + AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], exp, source_file, LateEval::LET_BYTE); else if (error == STATUS_RELATIVE_SECTION) - CurrSection().AddReloc(lastEvalValue, CurrSection().GetPC(), lastEvalSection, + CurrSection().AddReloc(lastEvalValue, CurrSection().DataOffset(), lastEvalSection, lastEvalPart == Reloc::HI_BYTE ? Reloc::HI_BYTE : Reloc::LO_BYTE); AddByte(value); } @@ -2507,9 +2554,9 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc if (error>STATUS_NOT_READY) break; else if (error==STATUS_NOT_READY) - AddLateEval(CurrSection().GetPC(), scope_address[scope_depth], exp_w, source_file, LateEval::LET_ABS_REF); + AddLateEval(CurrSection().DataOffset(), CurrSection().DataOffset(), scope_address[scope_depth], exp_w, source_file, LateEval::LET_ABS_REF); else if (error == STATUS_RELATIVE_SECTION) { - CurrSection().AddReloc(lastEvalValue, CurrSection().GetPC(), lastEvalSection, lastEvalPart); + CurrSection().AddReloc(lastEvalValue, CurrSection().DataOffset(), lastEvalSection, lastEvalPart); value = 0; } } @@ -2535,13 +2582,13 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc if (error > STATUS_NOT_READY) break; else if (error == STATUS_NOT_READY) - AddLateEval(CurrSection().GetPC(), scope_address[scope_depth], exp_dc, source_file, words ? LateEval::LET_ABS_REF : LateEval::LET_BYTE); + AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], exp_dc, source_file, words ? LateEval::LET_ABS_REF : LateEval::LET_BYTE); else if (error == STATUS_RELATIVE_SECTION) { value = 0; if (words) - CurrSection().AddReloc(lastEvalValue, CurrSection().GetPC(), lastEvalSection, lastEvalPart); + CurrSection().AddReloc(lastEvalValue, CurrSection().DataOffset(), lastEvalSection, lastEvalPart); else - CurrSection().AddReloc(lastEvalValue, CurrSection().GetPC(), lastEvalSection, + CurrSection().AddReloc(lastEvalValue, CurrSection().DataOffset(), lastEvalSection, lastEvalPart == Reloc::HI_BYTE ? Reloc::HI_BYTE : Reloc::LO_BYTE); } } @@ -3082,7 +3129,7 @@ StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file) case CA_BRANCH: AddByte(opcode); if (evalLater) - AddLateEval(CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_BRANCH); + AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_BRANCH); else if (((int)value - (int)CurrSection().GetPC()-1) < -128 || ((int)value - (int)CurrSection().GetPC()-1) > 127) { error = ERROR_BRANCH_OUT_OF_RANGE; break; @@ -3092,18 +3139,20 @@ StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file) case CA_ONE_BYTE: AddByte(opcode); if (evalLater) - AddLateEval(CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_BYTE); + AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_BYTE); else if (error == STATUS_RELATIVE_SECTION) - CurrSection().AddReloc(target_section_offs, CurrSection().GetPC(), target_section, + CurrSection().AddReloc(target_section_offs, CurrSection().DataOffset(), target_section, target_section_type == Reloc::HI_BYTE ? Reloc::HI_BYTE : Reloc::LO_BYTE); AddByte(value); break; case CA_TWO_BYTES: AddByte(opcode); if (evalLater) - AddLateEval(CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_ABS_REF); + AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_ABS_REF); else if (error == STATUS_RELATIVE_SECTION) { - CurrSection().AddReloc(target_section_offs, CurrSection().GetPC(), + printf("Reloc two byte op: "); + + CurrSection().AddReloc(target_section_offs, CurrSection().DataOffset(), target_section, target_section_type); value = 0; } @@ -3309,7 +3358,7 @@ StatusCode Asm::BuildSegment(OP_ID *pInstr, int numInstructions) } // create an instruction table (mnemonic hash lookup + directives) -void Asm::Assemble(strref source, strref filename) +void Asm::Assemble(strref source, strref filename, bool obj_target) { OP_ID *pInstr = new OP_ID[256]; int numInstructions = BuildInstructionTable(pInstr, strref(aInstr, strl_t(sizeof(aInstr)-1)), 256); @@ -3334,31 +3383,383 @@ void Asm::Assemble(strref source, strref filename) fwrite(errorText.get(), errorText.get_len(), 1, stderr); } - for (std::vector::iterator i = lateEval.begin(); i!=lateEval.end(); ++i) { - strown<512> errorText; - int line = i->source_file.count_lines(i->expression); - errorText.sprintf("Error (%d): ", line+1); - errorText.append("Failed to evaluate label \""); - errorText.append(i->expression); - if (line>=0) { - errorText.append("\" : \""); - errorText.append(i->source_file.get_line(line).get_trimmed_ws()); + if (!obj_target) { + for (std::vector::iterator i = lateEval.begin(); i!=lateEval.end(); ++i) { + strown<512> errorText; + int line = i->source_file.count_lines(i->expression); + errorText.sprintf("Error (%d): ", line+1); + errorText.append("Failed to evaluate label \""); + errorText.append(i->expression); + if (line>=0) { + errorText.append("\" : \""); + errorText.append(i->source_file.get_line(line).get_trimmed_ws()); + } + errorText.append("\"\n"); + fwrite(errorText.get(), errorText.get_len(), 1, stderr); } - errorText.append("\"\n"); - fwrite(errorText.get(), errorText.get_len(), 1, stderr); } } } + +// +// +// OBJECT FILE HANDLING +// +// + +struct ObjFileHeader { + short id; // 'o6' + short sections; + short relocs; + short labels; + short late_evals; + short map_symbols; + unsigned int stringdata; + int bindata; +}; + +struct ObjFileStr { + int offs; // offset into string table +}; + +struct ObjFileSection { + enum SectionFlags { + OFS_DUMMY, + OFS_FIXED, + OFS_MERGED, + }; + struct ObjFileStr name; + int start_address; + int output_size; // assembled binary size + short relocs; + short flags; +}; + +struct ObjFileReloc { + int base_value; + int section_offset; + short target_section; + short value_type; // Reloc::Type +}; + +struct ObjFileLabel { + enum LabelFlags { + OFL_XDEF, // External + OFL_EVAL, // Evaluated (may still be relative) + OFL_ADDR, // Address or Assign + OFL_CNST // Constant + }; + struct ObjFileStr name; + int value; + int flags; // 1<<(LabelFlags) + short section; // -1 if resolved, file section # if section rel + short mapIndex; // -1 if resolved, index into map if relative +}; + +struct ObjFileLateEval { + struct ObjFileStr label; + struct ObjFileStr expression; + struct ObjFileStr source_file; + int address; // PC relative to section or fixed + short section; // section to target + short target; // offset into section memory + short scope; // PC start of scope + short type; // label, byte, branch, word (LateEval::Type) +}; + +struct ObjFileMapSymbol { + struct ObjFileStr name; // symbol name + int value; + bool local; // local labels are probably needed + bool resolved; // set if in a relative section, when resolved label eval should clear this.. +}; + +// Simple string pool, converts strref strings to zero terminated strings and returns the offset to the string in the pool. +static int _AddStrPool(strref str, pairArray *pLookup, char **strPool, unsigned int &strPoolSize, unsigned int &strPoolCap) +{ + if (!str) + return -1; // empty string + unsigned int hash = str.fnv1a(); + unsigned int index = FindLabelIndex(hash, pLookup->getKeys(), pLookup->count()); + if (indexcount() && str.same_str_case(*strPool + pLookup->getValue(index))) + return pLookup->getValue(index); + + if ((strPoolSize + str.get_len() + 1) > strPoolCap) { + strPoolCap += 4096; + char *strPoolGrow = (char*)malloc(strPoolCap); + if (*strPool) { + memcpy(strPoolGrow, *strPool, strPoolSize); + free(*strPool); + } + *strPool = strPoolGrow; + } + int ret = strPoolSize; + memcpy(*strPool + strPoolSize, str.get(), str.get_len()); + (*strPool + strPoolSize)[str.get_len()] = 0; + strPoolSize += str.get_len()+1; + pLookup->insert(index, hash); + pLookup->getValues()[index] = ret; + return ret; +} + +StatusCode Asm::WriteObjectFile(strref filename) +{ + if (FILE *f = fopen(strown<512>(filename).c_str(), "wb")) { + struct ObjFileHeader hdr = { 0 }; + hdr.id = 'o6'; + hdr.sections = (short)allSections.size(); + hdr.relocs = 0; + hdr.bindata = 0; + for (std::vector
::iterator s = allSections.begin(); s!=allSections.end(); ++s) { + if (s->pRelocs) + hdr.relocs += short(s->pRelocs->size()); + hdr.bindata += (int)s->size(); + } + hdr.labels = labels.count(); + hdr.late_evals = (short)lateEval.size(); + hdr.map_symbols = (short)map.size(); + hdr.stringdata = 0; + + char *stringPool = nullptr; + unsigned int stringPoolCap = 0; + pairArray stringArray; + stringArray.reserve(hdr.labels * 2 + hdr.sections + hdr.late_evals*2); + + struct ObjFileSection *aSects = hdr.sections ? (struct ObjFileSection*)calloc(hdr.sections, sizeof(struct ObjFileSection)) : nullptr; + struct ObjFileReloc *aRelocs = hdr.relocs ? (struct ObjFileReloc*)calloc(hdr.relocs, sizeof(struct ObjFileReloc)) : nullptr; + struct ObjFileLabel *aLabels = hdr.labels ? (struct ObjFileLabel*)calloc(hdr.labels, sizeof(struct ObjFileLabel)) : nullptr; + struct ObjFileLateEval *aLateEvals = hdr.late_evals ? (struct ObjFileLateEval*)calloc(hdr.late_evals, sizeof(struct ObjFileLateEval)) : nullptr; + struct ObjFileMapSymbol *aMapSyms = hdr.map_symbols ? (struct ObjFileMapSymbol*)calloc(hdr.map_symbols, sizeof(struct ObjFileMapSymbol)) : nullptr; + int sect = 0, reloc = 0, labs = 0, late = 0, map_sym = 0; + + // write out sections and relocs + for (std::vector
::iterator si = allSections.begin(); si!=allSections.end(); ++si) { + struct ObjFileSection &s = aSects[sect++]; + s.name.offs = _AddStrPool(si->name, &stringArray, &stringPool, hdr.stringdata, stringPoolCap); + s.output_size = (short)si->size(); + s.relocs = si->pRelocs ? (short)(si->pRelocs->size()) : 0; + s.start_address = si->start_address; + s.flags = + (si->IsDummySection() ? (1 << ObjFileSection::OFS_DUMMY) : 0) | + (si->IsMergedSection() ? (1 << ObjFileSection::OFS_MERGED) : 0) | + (si->address_assigned ? (1 << ObjFileSection::OFS_FIXED) : 0); + if (si->pRelocs && si->pRelocs->size()) { + for (relocList::iterator ri = si->pRelocs->begin(); ri!=si->pRelocs->end(); ++ri) { + struct ObjFileReloc &r = aRelocs[reloc++]; + r.base_value = ri->base_value; + r.section_offset = ri->section_offset; + r.target_section = ri->target_section; + r.value_type = ri->value_type; + } + } + } + + // write out labels + for (unsigned int li = 0; li::iterator lei = lateEval.begin(); lei != lateEval.end(); ++lei) { + struct ObjFileLateEval &le = aLateEvals[late++]; + le.label.offs = _AddStrPool(lei->label, &stringArray, &stringPool, hdr.stringdata, stringPoolCap); + le.expression.offs = _AddStrPool(lei->expression, &stringArray, &stringPool, hdr.stringdata, stringPoolCap); + le.source_file.offs = _AddStrPool(lei->source_file, &stringArray, &stringPool, hdr.stringdata, stringPoolCap); + le.section = lei->section; + le.target = (short)lei->target; + le.address = lei->address; + le.scope = lei->scope; + le.type = lei->type; + } + + // write out map symbols + for (MapSymbolArray::iterator mi = map.begin(); mi != map.end(); ++mi) { + struct ObjFileMapSymbol &ms = aMapSyms[map_sym++]; + ms.name.offs = _AddStrPool(mi->name, &stringArray, &stringPool, hdr.stringdata, stringPoolCap); + ms.value = mi->value; + ms.local = mi->local; + ms.resolved = mi->resolved; + } + + // write out the file + fwrite(&hdr, sizeof(hdr), 1, f); + fwrite(aSects, sizeof(aSects[0]), sect, f); + fwrite(aRelocs, sizeof(aRelocs[0]), reloc, f); + fwrite(aLabels, sizeof(aLabels[0]), labs, f); + fwrite(aLateEvals, sizeof(aLateEvals[0]), late, f); + fwrite(aMapSyms, sizeof(aMapSyms[0]), map_sym, f); + fwrite(stringPool, hdr.stringdata, 1, f); + for (std::vector
::iterator si = allSections.begin(); si!=allSections.end(); ++si) { + if (!si->IsDummySection() && !si->IsMergedSection() && si->size()!=0) + fwrite(si->output, si->size(), 1, f); + } + + // done with I/O + fclose(f); + + if (stringPool) + free(stringPool); + if (aMapSyms) + free(aMapSyms); + if (aLateEvals) + free(aLateEvals); + if (aLabels) + free(aLabels); + if (aRelocs) + free(aRelocs); + if (aSects) + free(aSects); + stringArray.clear(); + + } + return STATUS_OK; +} + +StatusCode Asm::ReadObjectFile(strref filename) +{ + size_t size; + if (char *data = LoadBinary(filename, size)) { + struct ObjFileHeader &hdr = *(struct ObjFileHeader*)data; + size_t sum = sizeof(hdr) + hdr.sections*sizeof(struct ObjFileSection) + + hdr.relocs * sizeof(struct ObjFileReloc) + hdr.labels * sizeof(struct ObjFileLabel) + + hdr.late_evals * sizeof(struct ObjFileLateEval) + + hdr.map_symbols * sizeof(struct ObjFileMapSymbol) + hdr.stringdata + hdr.bindata; + if (hdr.id == 'o6' && sum == size) { + struct ObjFileSection *aSect = (struct ObjFileSection*)(&hdr + 1); + struct ObjFileReloc *aReloc = (struct ObjFileReloc*)(aSect + hdr.sections); + struct ObjFileLabel *aLabels = (struct ObjFileLabel*)(aReloc + hdr.relocs); + struct ObjFileLateEval *aLateEval = (struct ObjFileLateEval*)(aLabels + hdr.labels); + struct ObjFileMapSymbol *aMapSyms = (struct ObjFileMapSymbol*)(aLateEval + hdr.late_evals); + const char *str_orig = (const char*)(aMapSyms + hdr.map_symbols); + const char *bin_data = str_orig + hdr.stringdata; + + char *str_pool = (char*)malloc(hdr.stringdata); + memcpy(str_pool, str_orig, hdr.stringdata); + loadedData.push_back(str_pool); + + int prevSection = SectionId(); + + short *aSctRmp = (short*)malloc(hdr.sections * sizeof(short)); + + // for now just append to existing assembler data + + // sections + int load_section = (int)allSections.size(); + int reloc_idx = 0; + for (int si = 0; si < hdr.sections; si++) { + Section *s = nullptr; + short f = aSect[si].flags; + aSctRmp[si] = (short)allSections.size(); + if (f & (1 << ObjFileSection::OFS_MERGED)) + continue; + if (f & (1 << ObjFileSection::OFS_DUMMY)) { + if (f&(1 << ObjFileSection::OFS_FIXED)) + DummySection(aSect[si].start_address); + else + DummySection(); + } else { + if (f&(1 << ObjFileSection::OFS_FIXED)) + SetSection(aSect[si].name.offs>=0 ? strref(str_pool + aSect[si].name.offs) : strref(), aSect[si].start_address); + else + SetSection(aSect[si].name.offs >= 0 ? strref(str_pool + aSect[si].name.offs) : strref()); + if (aSect[si].output_size) { + CurrSection().output = (unsigned char*)malloc(aSect[si].output_size+64); + memcpy(CurrSection().output, bin_data, aSect[si].output_size); + CurrSection().curr = CurrSection().output + aSect[si].output_size; + CurrSection().output_capacity = aSect[si].output_size; + + bin_data += aSect[si].output_size; + } + } + } + + for (int si = 0; si < hdr.sections; si++) { + for (int r = 0; r < aSect[si].relocs; r++) { + struct ObjFileReloc &rs = aReloc[reloc_idx++]; + allSections[aSctRmp[si]].AddReloc(rs.base_value, rs.section_offset, aSctRmp[rs.target_section], Reloc::Type(rs.value_type)); + } + } + + for (int li = 0; li < hdr.labels; li++) { + struct ObjFileLabel &l = aLabels[li]; + strref name = l.name.offs >= 0 ? strref(str_pool + l.name.offs) : strref(); + Label *lbl = GetLabel(name); + if (!lbl) { + lbl = AddLabel(name.fnv1a()); + short f = l.flags; + lbl->label_name = name; + lbl->pool_name.clear(); + lbl->value = l.value; + lbl->evaluated = !!(f&(1 << ObjFileLabel::OFL_EVAL)); + lbl->constant = !!(f&(1 << ObjFileLabel::OFL_CNST)); + lbl->pc_relative = !!(f&(1 << ObjFileLabel::OFL_ADDR)); + lbl->external = !!(f&(1 << ObjFileLabel::OFL_XDEF)); + lbl->section = l.section >= 0 ? aSctRmp[l.section] : l.section; + lbl->mapIndex = l.mapIndex + (int)map.size(); + } + } + + for (int li = 0; li < hdr.late_evals; ++li) { + struct ObjFileLateEval &le = aLateEval[li]; + strref name = le.label.offs >= 0 ? strref(str_pool + le.label.offs) : strref(); + Label *pLabel = GetLabel(name); + if (pLabel) { + if (pLabel->evaluated) { + AddLateEval(name, le.address, le.scope, strref(str_pool + le.expression.offs), (LateEval::Type)le.type); + lateEval[lateEval.size() - 1].section = le.section >= 0 ? aSctRmp[le.section] : le.section; + lateEval[lateEval.size() - 1].source_file = le.source_file.offs >= 0 ? strref(str_pool + le.source_file.offs) : strref(); + } + } else { + AddLateEval(le.target, le.address, le.scope, strref(str_pool + le.expression.offs), strref(str_pool + le.source_file.offs), (LateEval::Type)le.type); + } + } + + for (int mi = 0; mi < hdr.map_symbols; mi++) { + struct ObjFileMapSymbol &m = aMapSyms[mi]; + if (map.size() == map.capacity()) + map.reserve(map.size() + 256); + MapSymbol sym; + sym.name = m.name.offs>=0 ? strref(str_pool + m.name.offs) : strref(); + sym.resolved = m.resolved; + sym.value = m.value; + sym.local = m.local; + map.push_back(sym); + } + + free(aSctRmp); + + // restore previous section + current_section = &allSections[prevSection]; + } else + return ERROR_NOT_AN_X65_OBJECT_FILE; + + } + return STATUS_OK; +} + + int main(int argc, char **argv) { int return_value = 0; bool load_header = true; bool size_header = false; + bool info = false; Asm assembler; - const char *source_filename=nullptr, *binary_out_name=nullptr; + const char *source_filename = nullptr, *obj_out_file = nullptr; + const char *binary_out_name = nullptr; const char *sym_file=nullptr, *vs_file=nullptr; for (int a=1; a: Add include path\n * -D