diff --git a/docs/AllPOs.txt b/docs/AllPOs.txt index b0604fd..d46b8aa 100644 --- a/docs/AllPOs.txt +++ b/docs/AllPOs.txt @@ -73,7 +73,7 @@ Parameters: ANDVALUE: Any formula the value parser accepts, but it FILLVALUE: Any formula the value parser accepts. If it is omitted, a default value is used (currently 234, that's the 6502 CPU's NOP command). -Examples: ; eliminate the 6502's JMP()-Bug: +Examples: ; eliminate the 6502's JMP($xxff)-Bug: !align 1, 0 ; wait for even address Label !word Pointer @@ -113,14 +113,14 @@ Examples: !convtab raw Hint: If you don't want to fiddle with a hex editor to create a conversion table file, try using ACME: !to "asc2pet.ct", plain ; no load address - *=0 ; pc = table index + * = 0 ; pc = table index ; first create "as-is" table - !for i, 256 {!byte i-1} + !for i, 256 {!byte i - 1} ; now exchange upper and lower case characters - *=65 - !for i, 91-65 {!byte *+128} - *=97 - !for i, 123-97 {!byte *-32} + * = 65 + !for i, 91 - 65 {!byte * + 128} + * = 97 + !for i, 123 - 97 {!byte * - 32} The resulting file can be used as a conversion table to convert to PetSCII (which is useless, because ACME can do so anyway. But you get the idea). @@ -135,7 +135,7 @@ Parameters: STRING_VALUE: Can be either a string given in double but single characters involved in calculations will. Aliases: "!tx" Examples: !text "Loading...", Char_NewLine, "Filename:", 0 - !tx "Offset character is ", offset-1+'a', 0 + !tx "Offset character is ", offset - 1 + 'a', 0 Call: !pet STRING_VALUE [, STRING_VALUE]* @@ -147,7 +147,7 @@ Parameters: STRING_VALUE: Can be either a string given in double Please note that formula results won't be converted, but single characters involved in calculations will. Examples: !pet "Loading...", Char_NewLine, "Filename:", 0 - !pet "Offset character is ", offset-1+'a', 0 + !pet "Offset character is ", offset - 1 + 'a', 0 Call: !raw STRING_VALUE [, STRING_VALUE]* @@ -156,7 +156,7 @@ Purpose: Output the given string(s) without any conversion at Parameters: STRING_VALUE: Can be either a string given in double quotes or any formula the value parser accepts. Examples: !raw "Loading...", Char_NewLine, "Filename:", 0 - !raw "Offset character is ", offset-1+'a', 0 + !raw "Offset character is ", offset - 1 + 'a', 0 Call: !scr STRING_VALUE [, STRING_VALUE]* @@ -168,7 +168,7 @@ Parameters: STRING_VALUE: Can be either a string given in double Please note that formula results won't be converted, but single characters involved in calculations will. Examples: !scr "Loading...", Char_NewLine, "Filename:", 0 - !scr "Offset character is ", offset-1+'a', 0 + !scr "Offset character is ", offset - 1 + 'a', 0 Call: !scrxor XOR_VALUE, STRING_VALUE [, STRING_VALUE]* @@ -350,7 +350,7 @@ Examples: ; this was taken from <6502/std.a>: ; if the following code gets included several times, ; only assemble it at the first location: !ifndef my_label {my_label} ; only define if undefined - !if *=my_label { + !if * = my_label { ; imagine some code here... ; this block will only be assembled at the ; first location where it is included. all @@ -380,7 +380,7 @@ Parameters: LABEL: Any valid label name. The label's value will Examples: ; conversion table: integer to BCD int2BCD !for Outer, 10 { !for Inner, 10 { - !byte ((Outer-1) << 4) OR (Inner-1) + !byte ((Outer - 1) << 4) OR (Inner - 1) } } !fill 156, $ff ; values above 99 give 255 (invalid) @@ -388,7 +388,7 @@ Examples: ; conversion table: integer to BCD ; conversion table: BCD to integer BCD2int !for Outer, 10 { !for Inner, 10 { - !byte 10 * (Outer-1) + (Inner-1) + !byte 10 * (Outer - 1) + (Inner - 1) } !fill 6, $ff ; invalid BCD values give 255 } @@ -423,7 +423,7 @@ Examples: ; a loop with conditions at both start and end !set a = 0 ; init loop counter !do while loop_flag = TRUE { lda #a - sta label+a + sta label + a !set a = a + 1 } until a > 6 @@ -468,7 +468,7 @@ Purpose: Generate an error during assembly (therefore, no Parameters: STRING_VALUE: A string given in double quotes. Example: rts ; end of some function start !source "colors.a" - end !if end-start > 256 { + end !if end - start > 256 { !error "Color strings exceed 256 chars!" } @@ -542,7 +542,7 @@ Examples: ; far branch, as defined in <6502/std.a> ; define a pixel row of a C64 hardware sprite !macro SpriteLine .v { - !by .v>>16, (.v>>8)&255, .v&255 + !by .v >> 16, (.v >> 8) & 255, .v & 255 } @@ -607,9 +607,9 @@ Examples: inc label Since release 0.86, different macros are allowed to have the same name as long as their parameter lists differ in size (number of arguments) or type (call-by-value vs. call-by-reference). So - !macro process_bytes b1,b2 {...whatever...} - !macro process_bytes b1,b2,b3 {...whatever...} - !macro process_bytes b1,b2,~b3 {...whatever...} + !macro process_bytes b1, b2 {...whatever...} + !macro process_bytes b1, b2, b3 {...whatever...} + !macro process_bytes b1, b2, ~b3 {...whatever...} can *all* be used at the same time without any name clash. @@ -617,7 +617,7 @@ can *all* be used at the same time without any name clash. Section: Segment assembly ---------------------------------------------------------------------- -Call: *= EXPRESSION [,MODIFIER]* +Call: * = EXPRESSION [, MODIFIER]* Purpose: Set program counter to given value and start new segment. This opcode must be given at least once (or the command line option "--setpc" must be used). @@ -636,15 +636,15 @@ Parameters: EXPRESSION: Any formula the value parser accepts, but _other_ segments will never raise the warning "Segment reached another one, overwriting it". Examples: !to "TinyDemo", cbm ; define output file + format - *= $0801 ; start at C64 BASIC start + * = $0801 ; start at C64 BASIC start !src "basicmacros.a" ; include macro definitions +basic_header ; call program header macro !src "main.a" ; include main program - *= $1000 ; jump to new segment + * = $1000 ; jump to new segment !bin "music.b" ; load music to $1000 - *= $8000 ; jump to new segment + * = $8000 ; jump to new segment !bin "pic.b" ; load graphics to $8000 - *= $8010, overlay, invisible ; go back and patch + * = $8010, overlay, invisible ; go back and patch ; the graphics, suppressing warnings ; After assembly, ACME will save everything from $0801 ; up to the highest address written to. The resulting @@ -665,15 +665,15 @@ Parameters: EXPRESSION: Any formula the value parser accepts, but this opcode will be ignored in all later passes). Examples: !to "TinyDemo", cbm ; define output file + format !initmem $ea ; default memory content $ea. - *= $0801 ; start at C64 BASIC start + * = $0801 ; start at C64 BASIC start !src "basicmacros.a" ; include macro definitions +basic_header ; call program header macro !src "main.a" ; include main program - *= $1000 ; jump to new segment + * = $1000 ; jump to new segment !bin "music.b" ; load music to $1000 - *= $8000 ; jump to new segment + * = $8000 ; jump to new segment !bin "pic.b" ; load graphics to $8000 - *= $8010, overlay, invisible ; go back and patch + * = $8010, overlay, invisible ; go back and patch ; the graphics, suppressing warnings ; This is the same example as before, but now the big ; unused areas will contain the value $ea instead of @@ -687,7 +687,7 @@ Examples: !to "TinyDemo", cbm ; define output file + format Section: Offset assembly ---------------------------------------------------------------------- -Call: !pseudopc EXPRESSION [ { BLOCK } ] +Call: !pseudopc EXPRESSION { BLOCK } Purpose: Assemble code as if the program counter had the given value, effectively producing a program that has to be copied to a different address space before being run. @@ -701,9 +701,9 @@ Purpose: Assemble code as if the program counter had the given Parameters: EXPRESSION: Any formula the value parser accepts, but it must be solvable even in the first pass. BLOCK: A block of assembler statements. -Examples: ldx #.shifted_end-.shifted_start - - lda .shifted_start-1,x - sta .target-1,x +Examples: ldx #.shifted_end - .shifted_start + - lda .shifted_start - 1, x + sta .target - 1, x dex bne - jmp .target @@ -780,22 +780,20 @@ Purpose: Assume short (8 bits) index registers. Only needed ---------------------------------------------------------------------- -Section: Deprecated pseudo opcodes (they still work at the moment) +Section: Obsolete pseudo opcodes (they will throw errors if used) ---------------------------------------------------------------------- Call: !cbm Purpose: Use PetSCII as the text conversion table. Now superseded by the "!convtab" pseudo opcode. -Old usage: !cbm ; gives "use !ct pet instead" warning -Now use: !convtab pet ; does the same without warning +Old usage: !cbm ; gives "use !ct pet instead" error +Now use: !convtab pet ; does the same without error Call: !subzone [TITLE] { BLOCK } Purpose: Allows nesting of zones. Now superseded by "!zone" because that allows nesting as well. -Parameters: TITLE: May consist of letters and digits. Its only - purpose is to be displayed in error messages, so it'll - be omitted in most cases. +Parameters: TITLE: May consist of letters and digits. BLOCK: A block of assembler statements. Aliases: "!sz" Old usage: !subzone graphics { diff --git a/docs/Changes.txt b/docs/Changes.txt index 802e62e..ca7fb57 100644 --- a/docs/Changes.txt +++ b/docs/Changes.txt @@ -12,6 +12,23 @@ platform used. There should be another help file in this archive outlining the platform specific changes. +---------------------------------------------------------------------- +Section: New in release 0.94.8 +---------------------------------------------------------------------- + +Finally disabled pseudo opcode "!cbm". It now gives an error instead + of a warning. +Finally disabled pseudo opcode "!realpc". It now gives an error + instead of a warning. Therefore, "!pseudopc" now *must* be used + with a block in {} braces. +Finally disabled pseudo opcode "!subzone". It now gives an error + instead of a warning. +Added support for one more undocumented ("illegal") opcode: lxa. See + Illegals.txt for more info. +Removed "Offset assembly still active at end of segment" warning. New + segments can now be started even from within pseudopc sections. + + ---------------------------------------------------------------------- Section: New in release 0.94.7 ---------------------------------------------------------------------- @@ -88,7 +105,7 @@ New CLI switch: "-D" allows to set global labels via the command line. New CLI switch: "-Wno-label-indent" switches off warnings about indented implicit label definitions. New PO: "!ifndef" (finally a companion for "!ifdef"...) -When setting the program counter via "*=", modifiers ("overlay" and +When setting the program counter via "* =", modifiers ("overlay" and "invisible") allow to suppress warnings about segment overlap. Float values without leading digits are now accepted. @@ -328,7 +345,7 @@ New PO: "!convtab CONVERSION" (short "!ct") selects the default Improved PO: "!binary" now has "skip" parameter. Change: "!cbm" outputs a warning when used - use "!ct pet" instead. Change: "!end" no longer works - use "!eof" instead. -Change: "*=VALUE" is now segment change instead of offset assembly. +Change: "* = VALUE" is now segment change instead of offset assembly. Change: Argument order of MVN/MVP is now as is standard. The typecast system has been rewritten - now it works as intended. BIT without any parameters no longer works - use a macro instead. diff --git a/docs/Errors.txt b/docs/Errors.txt index 31f6172..1575a79 100644 --- a/docs/Errors.txt +++ b/docs/Errors.txt @@ -27,18 +27,6 @@ Error in CLI arguments: ... Section: Warnings during assembly ---------------------------------------------------------------------- -"!cbm" is deprecated; use "!ct pet" instead. - This is given when "!cbm" is encountered. It still works though. - -"!pseudopc/!realpc" is deprecated; use "!pseudopc {}" instead. - "!pseudopc" can now be used with a block, so it can be nested. - So "!realpc" is no longer needed. It still works though. - -"!subzone {}" is deprecated; use "!zone {}" instead. - "!zone" can now be used stand-alone (which just changes the - current zone) as well as with a block (which creates a subzone). - So "!subzone" is no longer needed. It still works though. - !warn: ... This is given when the pseudo opcode "!warn" is executed. The actual message varies according to the pseudo opcode's arguments. @@ -53,6 +41,13 @@ Assembling buggy JMP($xxff) instruction Note that this warning is only given for CPU types 6502 and 6510, because 65c02 and 65816 have been fixed in this respect. +Assembling unstable LXA #NONZERO instruction + This warning is only ever given for CPU type 6510. LXA is one of + the undocumented ("illegal") opcodes of this CPU (opcode 0xab), + and it only works reliably if its argument is zero. Therefore ACME + issues this warning if you are about to generate this instruction + with a non-zero argument. + Bug in ACME, code follows A situation has been encountered implying there is a bug in ACME. See the last section in this file. @@ -83,12 +78,6 @@ Memory already initialised. The "!initmem" command was given more than once (or in addition to the "--initmem" command line option). Only use it once. -Offset assembly still active at end of segment. - There's a "*=" command inside an offset assembly block. There has - been some discussion on the ACME mailing list over whether this - should raise a warning. Future releases of ACME might not warn - anymore. - Output file already chosen. The "!to" command was given more than once (or in addition to the "--outfile" command line option). Only use it once. @@ -97,16 +86,16 @@ Segment reached another one, overwriting it. The program counter has just reached the start of another segment. Because some people might want to assemble "onto" a binary file that was loaded before, this warning can be switched off using - modifier keywords when changing the program counter via "*=". + modifier keywords when changing the program counter via "* =". Future versions of ACME might throw an error instead of a warning in this case. Segment starts inside another one, overwriting it. - The given value in a "*=" command is located inside another + The given value in a "* =" command is located inside another segment. Because some people might want to assemble "onto" a binary file that was loaded before, this warning can be switched off using modifier keywords when changing the program counter via - "*=". + "* =". Future versions of ACME might throw an error instead of a warning in this case. @@ -142,6 +131,18 @@ Section: Errors during assembly the library, but the library location variable wasn't set. This can only be given on systems using the said variable. +"!cbm" is obsolete; use "!ct pet" instead. + This is given when the now obsolete "!cbm" pseudo opcode is + encountered. + +"!pseudopc/!realpc" is obsolete; use "!pseudopc {}" instead. + This is given when one of the now obsolete !pseudopc/!realpc + pseudo opcodes is encountered. + +"!subzone {}" is obsolete; use "!zone {}" instead. + This is given when the now obsolete "!subzone" pseudo opcode is + encountered. + !error: ... This is given when the pseudo opcode "!error" is executed. The actual message varies according to the pseudo opcode's arguments. @@ -268,7 +269,7 @@ Unknown processor. Unknown pseudo opcode. You have mistyped a "!" command. -Unknown "*=" segment modifier. +Unknown "* =" segment modifier. You used a modifier keyword ACME does not know. Value not yet defined. diff --git a/docs/Help.txt b/docs/Help.txt index d1bd1ec..8fcd5e3 100644 --- a/docs/Help.txt +++ b/docs/Help.txt @@ -4,8 +4,6 @@ ...the ACME Crossassembler for Multiple Environments - Release 0.94.7 - - free software - (C) 1998-2014 Marco Baye diff --git a/docs/Illegals.txt b/docs/Illegals.txt index 20081cd..ae6c3f5 100644 --- a/docs/Illegals.txt +++ b/docs/Illegals.txt @@ -22,7 +22,7 @@ Mnemo | 8 8,x 8,y 16 16,x 16,y (8,x) (8),y | performs: dcp | c7 d7 -- cf df db c3 d3 | dec + cmp isc | e7 f7 -- ef ff fb e3 f3 | inc + sbc -In release 0.89, further ones were added: +In release 0.89, more were added; and in 0.94.8, another one (lxa): | addressing mode | Mnemo | implied #8 8 8,x 16 16,x | performs: @@ -34,6 +34,7 @@ Mnemo | implied #8 8 8,x 16 16,x | performs: dop | 80* 80 04 14 -- -- | skips next byte top | 0c* -- -- -- 0c 1c | skips next two bytes jam | 02 -- -- -- -- -- | crash (wait for reset) + lxa | -- ab** -- -- -- -- | A/X = (A | ??) & arg Example: !cpu 6510 ; activate additional mnemonics... @@ -46,6 +47,11 @@ addressing, respectively. Using dop/top with x-indexed addressing might have its uses when timing is critical (crossing a page border adds a penalty cycle). +**) This opcode is said to first perform an ORA with an arbitrary(!) +value, then do an AND with the given argument, then do a TAX. +This means it is unstable and therefore useless - unless the given +argument is zero, in which case it reliably clears both A and X. + There is no guarantee that these opcodes actually work on a given 6502 (or 6510, or 8500, or 8502) CPU. But as far as I know, nobody ever found an unmodified C64/C128 where these illegals didn't work. That's @@ -93,7 +99,6 @@ Opcode| Description 9c | see notes below 9e | see notes below 9f | see notes below - ab | see notes below b2 | same as 02 and others jam CRASH bb | see notes below c2 | same as 82/e2 dop #8, but said to CRASH sometimes @@ -109,7 +114,7 @@ Opcode| Description fc | same as 1c and others top 16,x -Concerning opcodes 8b, 93, 9b, 9c, 9e, 9f, ab, bb: +Concerning opcodes 8b, 93, 9b, 9c, 9e, 9f, bb: These opcodes are said to be unstable. For more information about what they do, see these documents: @@ -126,5 +131,4 @@ reference documents above call them: 9c: shy, say, sya 9e: shx, xas, sxa 9f: sha, axa, ahx - ab: lxa, oal, atx bb: las, lar, lae diff --git a/docs/QuickRef.txt b/docs/QuickRef.txt index 98f291e..5b2ad63 100644 --- a/docs/QuickRef.txt +++ b/docs/QuickRef.txt @@ -19,7 +19,7 @@ Section: Example of what an ACME source code file looks like ;--- Example code fragment, start --- !to "tiny.o", cbm ; set output file and format - *= $c000 ; set program counter + * = $c000 ; set program counter basout = $ffd2 ; explicit global label def. ; a string output loop: @@ -28,7 +28,7 @@ Section: Example of what an ACME source code file looks like - jsr basout ; output character inx ; advance pointer -+ lda .string,x ; get character ++ lda .string, x ; get character bne - ; check whether last rts .string !pet "Dumb example", 13, 0 @@ -44,7 +44,7 @@ Here's the same fragment again, now with some additional info: ; This is a pseudo opcode to select the output filename and format. ; This can also be done using the command line options "-o" and "-f", ; respectively. - *= $c000 ; set program counter + * = $c000 ; set program counter ; This can also be done using the command line option "--setpc". basout = $ffd2 ; explicit global label def. ; Now "basout" is defined as a global label having the value $ffd2. @@ -99,7 +99,7 @@ macro/zone), but in addition to that, the "-" labels can only be used for backward references, while the "+" labels can only be used for forward references. In contrast to global and local labels, anonymous labels can not be -defined explicitly (as in LABEL=VALUE). +defined explicitly (as in LABEL = VALUE). Save the given example source code to a file called "tiny.a" and start acme by typing @@ -112,7 +112,7 @@ filename has been given. After assembly, the example program can be run on a C64 using - LOAD "tiny.o",8,1 + LOAD "tiny.o", 8, 1 SYS 49152 Note that ACME does not include any routines for transferring data to @@ -185,7 +185,7 @@ Available options are: opcode. Defaults to 6502. --setpc NUMBER set program counter - This can also be given in the source code using "*=NUMBER". + This can also be given in the source code using "* = NUMBER". --initmem NUMBER define 'empty' memory This can also be given using the "!initmem" pseudo opcode. diff --git a/docs/Upgrade.txt b/docs/Upgrade.txt index 33580b1..d1cf250 100644 --- a/docs/Upgrade.txt +++ b/docs/Upgrade.txt @@ -30,7 +30,7 @@ Offset assembly is now done using a new pseudo opcode called "!pseudopc". Have a look at "AllPOs.txt" for further information on its syntax and usage. The old way of just redefining the program counter by using more than -one "*= EXPRESSION" statements does something totally different now: +one "* = EXPRESSION" statements does something totally different now: Whenever the program counter is redefined, ACME will actually change its pointer into the output buffer, so you can write your code in distinct segments. These segments can be given in any order. After diff --git a/src/acme.c b/src/acme.c index 92c4798..cafc86b 100644 --- a/src/acme.c +++ b/src/acme.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,9 +15,9 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -#define RELEASE "0.94.7" // update before release (FIXME) +#define RELEASE "0.94.8" // update before release (FIXME) #define CODENAME "Zarquon" // update before release -#define CHANGE_DATE "5 Mar" // update before release +#define CHANGE_DATE "10 Mar" // update before release #define CHANGE_YEAR "2014" // update before release //#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" // FIXME #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME @@ -69,9 +69,10 @@ static const char name_dumpfile[] = "label dump filename"; // variables static const char **toplevel_sources; static int toplevel_src_count = 0; -static signed long start_addr = -1; // <0 is illegal +#define ILLEGAL_START_ADDRESS (-1) +static signed long start_address = ILLEGAL_START_ADDRESS; static signed long fill_value = MEMINIT_USE_DEFAULT; -static struct cpu_t *default_cpu = NULL; +static const struct cpu_type *default_cpu = NULL; const char *labeldump_filename = NULL; const char *output_filename = NULL; // maximum recursion depth for macro calls and "!source" @@ -185,8 +186,11 @@ static int perform_pass(void) int ii; // call modules' "pass init" functions + Output_passinit(); // disable output (until PC gets set) CPU_passinit(default_cpu); // set default cpu values (PC undefined) - Output_passinit(start_addr); // call after CPU_passinit(), to define PC + // if start address was given on command line, use it: + if (start_address != ILLEGAL_START_ADDRESS) + CPU_set_pc(start_address, 0); Encoding_passinit(); // set default encoding Section_passinit(); // set initial zone (untitled) // init variables @@ -317,8 +321,8 @@ static signed long string_to_number(const char *string) // set program counter static void set_starting_pc(void) { - start_addr = string_to_number(cliargs_safe_get_next("program counter")); - if ((start_addr > -1) && (start_addr < 65536)) + start_address = string_to_number(cliargs_safe_get_next("program counter")); + if ((start_address > -1) && (start_address < 65536)) return; fprintf(stderr, "%sProgram counter out of range (0-0xffff).\n", cliargs_error); exit(EXIT_FAILURE); diff --git a/src/alu.c b/src/alu.c index 28f3781..43e3bd0 100644 --- a/src/alu.c +++ b/src/alu.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // Arithmetic/logic unit @@ -624,7 +624,7 @@ static void expect_operand_or_monadic_operator(void) case '*': // Program counter GetByte(); // proceed with next char - PUSH_INTOPERAND(CPU_pc.intval, CPU_pc.flags | MVALUE_EXISTS); + PUSH_INTOPERAND(CPU_state.pc.intval, CPU_state.pc.flags | MVALUE_EXISTS); // Now GotByte = char after closing quote goto now_expect_dyadic; diff --git a/src/basics.c b/src/basics.c index 176fce3..ed91f88 100644 --- a/src/basics.c +++ b/src/basics.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // basic assembly stuff @@ -117,7 +117,7 @@ static enum eos_t PO_binary(void) // if verbose, produce some output if ((pass_count == 0) && (Process_verbosity > 1)) printf("Loaded %d (0x%04x) bytes from file offset %ld (0x%04lx).\n", - CPU_2add, CPU_2add, skip, skip); + CPU_state.add_to_pc, CPU_state.add_to_pc, skip, skip); return ENSURE_EOS; } diff --git a/src/cpu.c b/src/cpu.c index 341a38d..99c1a26 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // CPU stuff @@ -15,84 +15,74 @@ // constants -static struct cpu_t CPU_6502 = { +// FIXME - move to new cpu_type.c file +static struct cpu_type cpu_type_6502 = { keyword_is_6502mnemo, CPUFLAG_INDIRECTJMPBUGGY, // JMP ($xxFF) is buggy - 234, // !align fills with "NOP" - FALSE, // short accu - FALSE // short xy + 234 // !align fills with "NOP" }; -static struct cpu_t CPU_6510 = { +static struct cpu_type cpu_type_6510 = { keyword_is_6510mnemo, - CPUFLAG_INDIRECTJMPBUGGY, // JMP ($xxFF) is buggy - 234, // !align fills with "NOP" - FALSE, // short accu - FALSE // short xy + CPUFLAG_INDIRECTJMPBUGGY | // JMP ($xxFF) is buggy + CPUFLAG_AB_NEEDS_0_ARG, // LXA #$xx is unstable unless arg is $00 + 234 // !align fills with "NOP" }; -static struct cpu_t CPU_65c02= { +static struct cpu_type cpu_type_65c02 = { keyword_is_65c02mnemo, 0, // no flags - 234, // !align fills with "NOP" - FALSE, // short accu - FALSE // short xy + 234 // !align fills with "NOP" }; /* -static struct cpu_t CPU_Rockwell65c02 = { +static struct cpu_type cpu_type_Rockwell65c02 = { keyword_is_Rockwell65c02mnemo, 0, // no flags - 234, // !align fills with "NOP" - FALSE, // short accu - FALSE // short xy + 234 // !align fills with "NOP" }; -static struct cpu_t CPU_WDC65c02 = { +static struct cpu_type cpu_type_WDC65c02 = { keyword_is_WDC65c02mnemo, 0, // no flags - 234, // !align fills with "NOP" - FALSE, // short accu - FALSE // short xy + 234 // !align fills with "NOP" }; */ -static struct cpu_t CPU_65816 = { +static struct cpu_type cpu_type_65816 = { keyword_is_65816mnemo, CPUFLAG_SUPPORTSLONGREGS, // allows A and XY to be 16bits wide - 234, // !align fills with "NOP" - FALSE, // short accu - FALSE // short xy + 234 // !align fills with "NOP" }; #define s_rl (s_brl + 1) // Yes, I know I'm sick // variables -struct cpu_t *CPU_now; // struct of current CPU type (default 6502) -struct result_int_t CPU_pc; // (pseudo) program counter at start of statement -int CPU_2add; // increase PC by this after statement -static intval_t current_offset; // PseudoPC - MemIndex (FIXME - why is this needed?) -static int uses_pseudo_pc; // offset assembly active? FIXME - what is this for? + // predefined stuff +// FIXME - move to cpu_type.c file static struct node_t *CPU_tree = NULL; // tree to hold CPU types static struct node_t CPUs[] = { -// PREDEFNODE("z80", &CPU_Z80), - PREDEFNODE("6502", &CPU_6502), - PREDEFNODE("6510", &CPU_6510), - PREDEFNODE("65c02", &CPU_65c02), -// PREDEFNODE("Rockwell65c02", &CPU_Rockwell65c02), -// PREDEFNODE("WDC65c02", &CPU_WDC65c02), - PREDEFLAST(s_65816, &CPU_65816), +// PREDEFNODE("z80", &cpu_type_Z80), + PREDEFNODE("6502", &cpu_type_6502), + PREDEFNODE("6510", &cpu_type_6510), + PREDEFNODE("65c02", &cpu_type_65c02), +// PREDEFNODE("Rockwell65c02", &cpu_type_Rockwell65c02), +// PREDEFNODE("WDC65c02", &cpu_type_WDC65c02), + PREDEFLAST(s_65816, &cpu_type_65816), // ^^^^ this marks the last element }; +// FIXME - make static +struct cpu CPU_state; // current CPU state // insert byte until PC fits condition +// FIXME - move to basics.c static enum eos_t PO_align(void) { intval_t and, equal, fill, - test = CPU_pc.intval; + test = CPU_state.pc.intval; // make sure PC is defined. - if ((CPU_pc.flags & MVALUE_DEFINED) == 0) { + if ((CPU_state.pc.flags & MVALUE_DEFINED) == 0) { Throw_error(exception_pc_undefined); - CPU_pc.flags |= MVALUE_DEFINED; // do not complain again + CPU_state.pc.flags |= MVALUE_DEFINED; // do not complain again return SKIP_REMAINDER; } @@ -103,15 +93,17 @@ static enum eos_t PO_align(void) { if (Input_accept_comma()) fill = ALU_any_int(); else - fill = CPU_now->default_align_value; + fill = CPU_state.type->default_align_value; while ((test++ & and) != equal) Output_8b(fill); return ENSURE_EOS; } +// FIXME - move to cpu_type.c file // try to find CPU type held in DynaBuf. Returns whether succeeded. -int CPU_find_cpu_struct(struct cpu_t **target) +// FIXME - why not return ptr (or NULL to indicate failure)? +int CPU_find_cpu_struct(const struct cpu_type **target) { void *node_body; @@ -123,78 +115,105 @@ int CPU_find_cpu_struct(struct cpu_t **target) // select CPU ("!cpu" pseudo opcode) +// FIXME - move to basics.c static enum eos_t PO_cpu(void) { - struct cpu_t *cpu_buffer = CPU_now; // remember current cpu + const struct cpu_type *cpu_buffer = CPU_state.type; // remember current cpu if (Input_read_and_lower_keyword()) - if (!CPU_find_cpu_struct(&CPU_now)) + if (!CPU_find_cpu_struct(&CPU_state.type)) Throw_error("Unknown processor."); // if there's a block, parse that and then restore old value! if (Parse_optional_block()) - CPU_now = cpu_buffer; + CPU_state.type = cpu_buffer; return ENSURE_EOS; } -static const char Warning_old_offset_assembly[] = - "\"!pseudopc/!realpc\" is deprecated; use \"!pseudopc {}\" instead."; +static const char Error_old_offset_assembly[] = + "\"!pseudopc/!realpc\" is obsolete; use \"!pseudopc {}\" instead."; + + +// "!realpc" pseudo opcode (now obsolete) +// FIXME - move to basics.c +static enum eos_t PO_realpc(void) +{ + Throw_error(Error_old_offset_assembly); + return ENSURE_EOS; +} // start offset assembly +// FIXME - split into PO (move to basics.c) and backend +// TODO - add a label argument to assign the block size afterwards (for assemble-to-end-address) static enum eos_t PO_pseudopc(void) { -// future algo: remember outer memaddress and outer pseudopc - int outer_state = uses_pseudo_pc; intval_t new_pc, - outer_offset = current_offset; - int outer_flags = CPU_pc.flags; + new_offset; + int outer_flags = CPU_state.pc.flags; // set new - new_pc = ALU_defined_int(); // FIXME - allow for undefined pseudopc! - current_offset = (current_offset + new_pc - CPU_pc.intval) & 0xffff; - CPU_pc.intval = new_pc; - CPU_pc.flags |= MVALUE_DEFINED; // FIXME - remove! - uses_pseudo_pc = TRUE; + new_pc = ALU_defined_int(); // FIXME - allow for undefined! + new_offset = (new_pc - CPU_state.pc.intval) & 0xffff; + CPU_state.pc.intval = new_pc; + CPU_state.pc.flags |= MVALUE_DEFINED; // FIXME - remove when allowing undefined! // if there's a block, parse that and then restore old value! if (Parse_optional_block()) { // restore old - uses_pseudo_pc = outer_state; - CPU_pc.flags = outer_flags; - CPU_pc.intval = (outer_offset + CPU_pc.intval - current_offset) & 0xffff; - current_offset = outer_offset; -// future algo: new outer pseudopc = (old outer pseudopc + (current memaddress - outer memaddress)) & 0xffff + CPU_state.pc.intval = (CPU_state.pc.intval - new_offset) & 0xffff; + CPU_state.pc.flags = outer_flags; } else { - Throw_first_pass_warning(Warning_old_offset_assembly); + // not using a block is no longer allowed + Throw_error(Error_old_offset_assembly); } return ENSURE_EOS; } -// end offset assembly -static enum eos_t PO_realpc(void) +// set program counter to defined value (FIXME - allow for undefined!) +// if start address was given on command line, main loop will call this before each pass. +// in addition to that, it will be called on each "* = VALUE". +void CPU_set_pc(intval_t new_pc, int segment_flags) { - Throw_first_pass_warning(Warning_old_offset_assembly); - // deactivate offset assembly - CPU_pc.intval = (CPU_pc.intval - current_offset) & 0xffff; - current_offset = 0; - uses_pseudo_pc = FALSE; - return ENSURE_EOS; -} + intval_t new_offset; - -// return whether offset assembly is active (FIXME - remove this function) -int CPU_uses_pseudo_pc(void) -{ - return uses_pseudo_pc; + new_offset = (new_pc - CPU_state.pc.intval) & 0xffff; + CPU_state.pc.intval = new_pc; + CPU_state.pc.flags |= MVALUE_DEFINED; // FIXME - remove when allowing undefined! + // now tell output buffer to start a new segment + Output_start_segment(new_offset, segment_flags); } +/* +FIXME - TODO: +general stuff: PC and mem ptr might be marked as "undefined" via flags field. +However, their "value" fields are still updated, so we can calculate differences. + +on pass init: + if value given on command line, set PC and out ptr to that value + otherwise, set both to zero and mark as "undefined" +when ALU asks for "*": + return current PC (value and flags) +when encountering "!pseudopc VALUE { BLOCK }": + parse new value (NEW: might be undefined!) + remember difference between current and new value + set PC to new value + after BLOCK, use remembered difference to change PC back +when encountering "* = VALUE": + parse new value (NEW: might be undefined!) + calculate difference between current PC and new value + set PC to new value + tell outbuf to add difference to mem ptr (starting a new segment) - if new value is undefined, tell outbuf to disable output + +Problem: always check for "undefined"; there are some problematic combinations. +I need a way to return the size of a generated code block even if PC undefined. +*/ // if cpu type and value match, set register length variable to value. // if cpu type and value don't match, complain instead. static void check_and_set_reg_length(int *var, int make_long) { - if (((CPU_now->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) && make_long) + if (((CPU_state.type->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) && make_long) Throw_error("Chosen CPU does not support long registers."); else *var = make_long; @@ -218,32 +237,33 @@ static enum eos_t set_register_length(int *var, int make_long) // switch to long accu ("!al" pseudo opcode) static enum eos_t PO_al(void) { - return set_register_length(&CPU_now->a_is_long, TRUE); + return set_register_length(&CPU_state.a_is_long, TRUE); } // switch to short accu ("!as" pseudo opcode) static enum eos_t PO_as(void) { - return set_register_length(&CPU_now->a_is_long, FALSE); + return set_register_length(&CPU_state.a_is_long, FALSE); } // switch to long index registers ("!rl" pseudo opcode) static enum eos_t PO_rl(void) { - return set_register_length(&CPU_now->xy_are_long, TRUE); + return set_register_length(&CPU_state.xy_are_long, TRUE); } // switch to short index registers ("!rs" pseudo opcode) static enum eos_t PO_rs(void) { - return set_register_length(&CPU_now->xy_are_long, FALSE); + return set_register_length(&CPU_state.xy_are_long, FALSE); } // pseudo opcode table +// FIXME - move to basics.c static struct node_t pseudo_opcodes[] = { PREDEFNODE("align", PO_align), PREDEFNODE("cpu", PO_cpu), @@ -258,21 +278,20 @@ static struct node_t pseudo_opcodes[] = { // set default values for pass -void CPU_passinit(struct cpu_t *cpu_type) +void CPU_passinit(const struct cpu_type *cpu_type) { // handle cpu type (default is 6502) - CPU_now = cpu_type ? cpu_type : &CPU_6502; - CPU_pc.flags = 0; // not defined yet - CPU_pc.intval = 512; // actually, there should be no need to init - CPU_2add = 0; // increase PC by this at end of statement - CPU_65816.a_is_long = FALSE; // short accu - CPU_65816.xy_are_long = FALSE; // short index regs - uses_pseudo_pc = FALSE; // offset assembly is not active, - current_offset = 0; // so offset is 0 + CPU_state.type = cpu_type ? cpu_type : &cpu_type_6502; + CPU_state.pc.flags = 0; // not defined yet + CPU_state.pc.intval = 0; // same as output's write_idx on pass init + CPU_state.add_to_pc = 0; // increase PC by this at end of statement + CPU_state.a_is_long = FALSE; // short accu + CPU_state.xy_are_long = FALSE; // short index regs } // create cpu type tree (is done early) +// FIXME - move to cpu_type.c void CPUtype_init(void) { Tree_add_table(&CPU_tree, CPUs); @@ -280,15 +299,8 @@ void CPUtype_init(void) // register pseudo opcodes (done later) +// FIXME - move to basics.c void CPU_init(void) { Tree_add_table(&pseudo_opcode_tree, pseudo_opcodes); } - - -// set program counter to defined value -void CPU_set_pc(intval_t new_pc) -{ - CPU_pc.flags |= MVALUE_DEFINED; - CPU_pc.intval = new_pc; -} diff --git a/src/cpu.h b/src/cpu.h index 052c4fa..d90b905 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // CPU stuff @@ -10,38 +10,50 @@ #include "config.h" +// FIXME - create cpu_type.c file and move cpu type stuff to it // CPU type structure definition -struct cpu_t { +struct cpu_type { // This function is not allowed to change GlobalDynaBuf // because that's where the mnemonic is stored! int (*keyword_is_mnemonic)(int); int flags; char default_align_value; - int a_is_long; - int xy_are_long; }; -#define CPUFLAG_INDIRECTJMPBUGGY (1u << 0) -#define CPUFLAG_SUPPORTSLONGREGS (1u << 1) +#define CPUFLAG_INDIRECTJMPBUGGY (1u << 0) // warn if "jmp ($xxff)" is assembled +#define CPUFLAG_SUPPORTSLONGREGS (1u << 1) // allow "!al" and "!rl" pseudo opcodes +#define CPUFLAG_AB_NEEDS_0_ARG (1u << 2) // warn if "lxa #$xx" uses non-zero arg + +// current CPU state +// FIXME - move struct definition to .c file and change other .c files' accesses to fn calls +struct cpu { + const struct cpu_type *type; // current CPU type (default 6502) + struct result_int_t pc; // current program counter (pseudo value) + int add_to_pc; // add to PC after statement + int a_is_long; + int xy_are_long; +}; // variables -extern struct cpu_t *CPU_now; // struct of current CPU type (default 6502) -extern struct result_int_t CPU_pc; // current program counter (pseudo value) -extern int CPU_2add; // add to PC after statement +// FIXME - restrict visibility to cpu.c file +extern struct cpu CPU_state; // current CPU state +// FIXME - move to new cpu_type.h file // create cpu type tree (is done early) extern void CPUtype_init(void); + // register pseudo opcodes (done later) extern void CPU_init(void); // set default values for pass -extern void CPU_passinit(struct cpu_t *cpu_type); +extern void CPU_passinit(const struct cpu_type *cpu_type); // set program counter to defined value (FIXME - allow undefined!) -extern void CPU_set_pc(intval_t new_pc); +extern void CPU_set_pc(intval_t new_pc, int flags); + +// FIXME - move to new cpu_type.h file // try to find CPU type held in DynaBuf. Returns whether succeeded. -extern int CPU_find_cpu_struct(struct cpu_t **target); -// return whether offset assembly is active -extern int CPU_uses_pseudo_pc(void); +// FIXME - why not simply return struct ptr, or NULL in case of failure? +extern int CPU_find_cpu_struct(const struct cpu_type **target); #endif diff --git a/src/encoding.c b/src/encoding.c index 5280e4c..fdef6e3 100644 --- a/src/encoding.c +++ b/src/encoding.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // Character encoding stuff @@ -135,12 +135,10 @@ static enum eos_t PO_scrxor(void) return SKIP_REMAINDER; } -// Switch to CBM mode ("!cbm" pseudo opcode) +// "!cbm" pseudo opcode (now obsolete) static enum eos_t PO_cbm(void) { - Encoding_encode_char = encoder_pet; - // output deprecation warning - Throw_first_pass_warning("\"!cbm\" is deprecated; use \"!ct pet\" instead."); + Throw_error("\"!cbm\" is obsolete; use \"!ct pet\" instead."); return ENSURE_EOS; } diff --git a/src/global.c b/src/global.c index 837fcfc..3bdd0b2 100644 --- a/src/global.c +++ b/src/global.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // Global stuff - things that are needed by several modules @@ -127,7 +127,7 @@ static void parse_pc_def(void) // Now GotByte = "*" // re-definitions of program counter change segment if (GotByte == '=') { GetByte(); // proceed with next char - Output_start_segment(); + PO_setpc(); Input_ensure_EOS(); } else { Throw_error(exception_syntax); @@ -183,7 +183,7 @@ static int first_label_of_statement(int *statement_flags) static void parse_mnemo_or_global_label_def(int *statement_flags) { // It is only a label if it isn't a mnemonic - if ((CPU_now->keyword_is_mnemonic(Input_read_keyword()) == FALSE) + if ((CPU_state.type->keyword_is_mnemonic(Input_read_keyword()) == FALSE) && first_label_of_statement(statement_flags)) { // Now GotByte = illegal char // 04 Jun 2005 - this fix should help to @@ -300,8 +300,8 @@ void Parse_until_eob_or_eof(void) } } while (GotByte != CHAR_EOS); // until end-of-statement // adjust program counter - CPU_pc.intval = (CPU_pc.intval + CPU_2add) & 0xffff; - CPU_2add = 0; + CPU_state.pc.intval = (CPU_state.pc.intval + CPU_state.add_to_pc) & 0xffff; + CPU_state.add_to_pc = 0; // go on with next byte GetByte(); //NEXTANDSKIPSPACE(); } diff --git a/src/label.c b/src/label.c index eb3ff86..eeb94cb 100644 --- a/src/label.c +++ b/src/label.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // Label stuff @@ -213,8 +213,8 @@ void Label_implicit_definition(zone_t zone, int stat_flags, int force_bit, int c // implicit label definition (label) if ((stat_flags & SF_FOUND_BLANK) && warn_on_indented_labels) Throw_first_pass_warning("Implicit label definition not in leftmost column."); - result.flags = CPU_pc.flags & MVALUE_DEFINED; - result.val.intval = CPU_pc.intval; + result.flags = CPU_state.pc.flags & MVALUE_DEFINED; + result.val.intval = CPU_state.pc.intval; Label_set_value(label, &result, change); } diff --git a/src/mnemo.c b/src/mnemo.c index b10fd65..897227f 100644 --- a/src/mnemo.c +++ b/src/mnemo.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // Mnemonics stuff @@ -108,13 +108,13 @@ SCB accu_lindy8[] = { 0, 0, 0, 0, 0, 0, 0, // mnemotable), the assembler finds out the column to use here. The row // depends on the used addressing mode. A zero entry in these tables means // that the combination of mnemonic and addressing mode is illegal. -// | 6502 | 65c02 | 65816 | 6510 illegals | -enum { IDX_BIT,IDX_ASL,IDX_ROL,IDX_LSR,IDX_ROR,IDX_STY,IDX_STX,IDX_LDY,IDX_LDX,IDX_CPY,IDX_CPX,IDX_DEC,IDX_INC,IDXcTSB,IDXcTRB,IDXcBIT,IDXcDEC,IDXcINC,IDXcSTZ,IDX816COP,IDX816REP,IDX816SEP,IDX816PEA,IDX_ANC,IDX_ASR,IDX_ARR,IDX_SBX,IDX_DOP,IDX_TOP,IDX_JAM}; -SCS misc_abs[] = { 0x2c24, 0x0e06, 0x2e26, 0x4e46, 0x6e66, 0x8c84, 0x8e86, 0xaca4, 0xaea6, 0xccc4, 0xece4, 0xcec6, 0xeee6, 0x0c04, 0x1c14, 0x2c24, 0xcec6, 0xeee6, 0x9c64, 0x02, 0, 0, 0xf400, 0, 0, 0, 0, 0x04, 0x0c00, 0}; // $ff $ffff -SCS misc_xabs[] = { 0, 0x1e16, 0x3e36, 0x5e56, 0x7e76, 0x94, 0, 0xbcb4, 0, 0, 0, 0xded6, 0xfef6, 0, 0, 0x3c34, 0xded6, 0xfef6, 0x9e74, 0, 0, 0, 0, 0, 0, 0, 0, 0x14, 0x1c00, 0}; // $ff,x $ffff,x -SCS misc_yabs[] = { 0, 0, 0, 0, 0, 0, 0x96, 0, 0xbeb6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff,y $ffff,y -SCB misc_imm[] = { 0, 0, 0, 0, 0, 0, 0, 0xa0, 0xa2, 0xc0, 0xe0, 0, 0, 0, 0, 0x89, 0, 0, 0, 0, 0xc2, 0xe2, 0, 0x2b, 0x4b, 0x6b, 0xcb, 0x80, 0, 0}; // #$ff -SCB misc_impl[] = { 0, 0x0a, 0x2a, 0x4a, 0x6a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3a, 0x1a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x0c, 0x02}; // implied/accu +// | 6502 | 65c02 | 65816 | 6510 illegals | +enum { IDX_BIT,IDX_ASL,IDX_ROL,IDX_LSR,IDX_ROR,IDX_STY,IDX_STX,IDX_LDY,IDX_LDX,IDX_CPY,IDX_CPX,IDX_DEC,IDX_INC,IDXcTSB,IDXcTRB,IDXcBIT,IDXcDEC,IDXcINC,IDXcSTZ,IDX816COP,IDX816REP,IDX816SEP,IDX816PEA,IDX_ANC,IDX_ASR,IDX_ARR,IDX_SBX,IDX_DOP,IDX_TOP,IDX_JAM,IDX_LXA}; +SCS misc_abs[] = { 0x2c24, 0x0e06, 0x2e26, 0x4e46, 0x6e66, 0x8c84, 0x8e86, 0xaca4, 0xaea6, 0xccc4, 0xece4, 0xcec6, 0xeee6, 0x0c04, 0x1c14, 0x2c24, 0xcec6, 0xeee6, 0x9c64, 0x02, 0, 0, 0xf400, 0, 0, 0, 0, 0x04, 0x0c00, 0, 0}; // $ff $ffff +SCS misc_xabs[] = { 0, 0x1e16, 0x3e36, 0x5e56, 0x7e76, 0x94, 0, 0xbcb4, 0, 0, 0, 0xded6, 0xfef6, 0, 0, 0x3c34, 0xded6, 0xfef6, 0x9e74, 0, 0, 0, 0, 0, 0, 0, 0, 0x14, 0x1c00, 0, 0}; // $ff,x $ffff,x +SCS misc_yabs[] = { 0, 0, 0, 0, 0, 0, 0x96, 0, 0xbeb6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff,y $ffff,y +SCB misc_imm[] = { 0, 0, 0, 0, 0, 0, 0, 0xa0, 0xa2, 0xc0, 0xe0, 0, 0, 0, 0, 0x89, 0, 0, 0, 0, 0xc2, 0xe2, 0, 0x2b, 0x4b, 0x6b, 0xcb, 0x80, 0, 0, 0xab}; // #$ff +SCB misc_impl[] = { 0, 0x0a, 0x2a, 0x4a, 0x6a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3a, 0x1a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x0c, 0x02, 0}; // implied/accu // Code tables for group GROUP_ALLJUMPS: // These tables are needed for finding out the correct code when the mnemonic @@ -235,7 +235,8 @@ static struct node_t mnemos_6510[] = { PREDEFNODE("sbx", MERGE(GROUP_MISC, IDX_SBX)), // DEX + CMP (aka AXS aka SAX) PREDEFNODE("dop", MERGE(GROUP_MISC, IDX_DOP)), // skip next byte PREDEFNODE("top", MERGE(GROUP_MISC, IDX_TOP)), // skip next two bytes - PREDEFLAST("jam", MERGE(GROUP_MISC, IDX_JAM)), // jam/crash/kill/halt-and-catch-fire + PREDEFNODE("jam", MERGE(GROUP_MISC, IDX_JAM)), // jam/crash/kill/halt-and-catch-fire + PREDEFLAST("lxa", MERGE(GROUP_MISC, IDX_LXA)), // ORA #?? + AND #arg + TAX (aka OAL aka ATX) // ^^^^ this marks the last element }; @@ -587,11 +588,11 @@ static void group_only_relative8_addressing(int opcode) intval_t offset = 0; // dummy value, to not throw more errors than necessary ALU_int_result(&target); - if (CPU_pc.flags & target.flags & MVALUE_DEFINED) { + if (CPU_state.pc.flags & target.flags & MVALUE_DEFINED) { if ((target.intval | 0xffff) != 0xffff) { not_in_bank(target.intval); } else { - offset = (target.intval - (CPU_pc.intval + 2)) & 0xffff; // clip to 16 bit offset + offset = (target.intval - (CPU_state.pc.intval + 2)) & 0xffff; // clip to 16 bit offset // fix sign if (offset & 0x8000) offset -= 0x10000; @@ -620,11 +621,11 @@ static void group_only_relative16_addressing(int opcode) intval_t offset = 0; // dummy value, to not throw more errors than necessary ALU_int_result(&target); - if (CPU_pc.flags & target.flags & MVALUE_DEFINED) { + if (CPU_state.pc.flags & target.flags & MVALUE_DEFINED) { if ((target.intval | 0xffff) != 0xffff) { not_in_bank(target.intval); } else { - offset = (target.intval - (CPU_pc.intval + 3)) & 0xffff; + offset = (target.intval - (CPU_state.pc.intval + 3)) & 0xffff; // no further checks necessary, 16-bit branches can access whole bank } } @@ -667,14 +668,14 @@ static unsigned int imm_ops(int *force_bit, unsigned char opcode, int imm_flag) { // if the CPU does not allow 16bit immediate addressing (or if the // opcode does not allow it), return immediately. - if (((CPU_now->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) || (imm_flag == 0)) + if (((CPU_state.type->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) || (imm_flag == 0)) return opcode; // check force bits (if no force bits given, use relevant flag) if (*force_bit == 0) *force_bit = ((imm_flag & IMM_ACCU) ? - CPU_now->a_is_long : - CPU_now->xy_are_long) ? + CPU_state.a_is_long : + CPU_state.xy_are_long) ? MVALUE_FORCE16 : MVALUE_FORCE08; // return identical opcodes for 8bit and 16bit args! @@ -750,6 +751,12 @@ static void group_misc(int index, int imm_flag) // CAUTION - do not incorporate the line above into the line // below - "force_bit" might be undefined (depends on compiler). make_command(force_bit, &result, imm_opcodes); + // check whether to warn about 6510's unstable LXA + if ((imm_opcodes == 0xab) + && ((result.intval & 0xff) != 0x00) + && (result.flags & MVALUE_DEFINED) + && (CPU_state.type->flags & CPUFLAG_AB_NEEDS_0_ARG)) + Throw_warning("Assembling unstable LXA #NONZERO instruction"); break; case HAM_ABS: // $ff or $ffff make_command(force_bit, &result, misc_abs[index]); @@ -799,7 +806,7 @@ static void group_jump(int index) // check whether to warn about 6502's JMP() bug if (((result.intval & 0xff) == 0xff) && (result.flags & MVALUE_DEFINED) - && (CPU_now->flags & CPUFLAG_INDIRECTJMPBUGGY)) + && (CPU_state.type->flags & CPUFLAG_INDIRECTJMPBUGGY)) Throw_warning("Assembling buggy JMP($xxff) instruction"); break; case HAM_XIND: // ($ffff,x) diff --git a/src/output.c b/src/output.c index 9ef5631..a83cd3d 100644 --- a/src/output.c +++ b/src/output.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // Output stuff @@ -54,6 +54,7 @@ static struct output *out = &default_output; // variables +// FIXME - move file format stuff to some other .c file! // predefined stuff static struct node_t *file_format_tree = NULL; // tree to hold output formats // possible file formats @@ -72,6 +73,8 @@ static struct node_t file_formats[] = { }; // chosen file format static enum out_format_t output_format = OUTPUT_FORMAT_UNSPECIFIED; + + // predefined stuff static struct node_t *segment_modifier_tree = NULL; // tree to hold segment modifiers // segment modifiers @@ -97,11 +100,10 @@ static void find_segment_max(intval_t new_pc) out->segment.list_head.start = new_pc + 1; while (test_segment->start <= new_pc) test_segment = test_segment->next; - if (test_segment == &out->segment.list_head) { + if (test_segment == &out->segment.list_head) out->segment.max = OUTBUFFERSIZE - 1; - } else { + else out->segment.max = test_segment->start - 1; // last free address available - } } @@ -135,15 +137,7 @@ static void real_output(intval_t byte) out->highest_written = out->write_idx; // write byte and advance ptrs out->buffer[out->write_idx++] = byte & 0xff; - CPU_2add++; -} - - -// activate output and set output pointer -static void enable_output(intval_t index) -{ - Output_byte = real_output; - out->write_idx = index; + CPU_state.add_to_pc++; } @@ -151,18 +145,9 @@ static void enable_output(intval_t index) static void no_output(intval_t byte) { Throw_error(exception_pc_undefined); - // set ptr to not complain again. as we have thrown an error, assembly - // fails, so don't care about actual value. - enable_output(512); // 512 to not garble zero page and stack. ;) - Output_byte(byte); // try again - the line above has changed the fn ptr! -} - - -// deactivate output - any byte written will trigger error! -static void disable_output(void) -{ - Output_byte = no_output; - out->write_idx = 0; + // now change fn ptr to not complain again. + Output_byte = real_output; + Output_byte(byte); // try again } @@ -188,7 +173,7 @@ void Output_fake(int size) out->highest_written = out->write_idx + size - 1; // advance ptrs out->write_idx += size; - CPU_2add += size; + CPU_state.add_to_pc += size; } @@ -249,6 +234,7 @@ static void fill_completely(char value) // define default value for empty memory ("!initmem" pseudo opcode) +// FIXME - move to basics.c static enum eos_t PO_initmem(void) { intval_t content; @@ -281,6 +267,7 @@ static enum eos_t PO_initmem(void) // try to set output format held in DynaBuf. Returns whether succeeded. +// FIXME - move to basics.c? int Output_set_output_format(void) { void *node_body; @@ -294,6 +281,7 @@ int Output_set_output_format(void) // select output file and format ("!to" pseudo opcode) +// FIXME - move to basics.c static enum eos_t PO_to(void) { // bugfix: first read filename, *then* check for first pass. @@ -344,6 +332,7 @@ static enum eos_t PO_to(void) // pseudo ocpode table +// FIXME - move to basics.c static struct node_t pseudo_opcodes[] = { PREDEFNODE("initmem", PO_initmem), PREDEFLAST("to", PO_to), @@ -352,6 +341,7 @@ static struct node_t pseudo_opcodes[] = { // init file format tree (is done early, because it is needed for CLI argument parsing) +// FIXME - move to some other file void Outputfile_init(void) { Tree_add_table(&file_format_tree, file_formats); @@ -362,7 +352,6 @@ void Outputfile_init(void) void Output_init(signed long fill_value) { out->buffer = safe_malloc(OUTBUFFERSIZE); - out->write_idx = 0; if (fill_value == MEMINIT_USE_DEFAULT) { fill_value = FILLVALUE_INITIAL; out->initvalue_set = FALSE; @@ -447,28 +436,6 @@ static void link_segment(intval_t start, intval_t length) } -// show start and end of current segment -// called whenever a new segment begins, and at end of pass. -void Output_end_segment(void) -{ - intval_t amount; - - // if no segments were started, ignore the call at end-of-pass: - if (out->segment.start == NO_SEGMENT_START) - return; - - if (CPU_uses_pseudo_pc()) - Throw_first_pass_warning("Offset assembly still active at end of segment."); // FIXME - should be error! - if ((pass_count == 0) && !(out->segment.flags & SEGMENT_FLAG_INVISIBLE)) { - amount = out->write_idx - out->segment.start; - link_segment(out->segment.start, amount); - if (Process_verbosity > 1) - printf("Segment size is %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n", - amount, amount, out->segment.start, out->write_idx); - } -} - - // check whether given PC is inside segment. // only call in first pass, otherwise too many warnings might be thrown static void check_segment(intval_t new_pc) @@ -490,42 +457,93 @@ static void check_segment(intval_t new_pc) } -// clear segment list -void Output_passinit(signed long start_addr) +// clear segment list and disable output +void Output_passinit(void) { // struct segment *temp; //FIXME - why clear ring list in every pass? +// Because later pass shouldn't complain about overwriting the same segment from earlier pass! +// Currently this does not happen because segment checks are only done in first pass. FIXME! // delete segment list (and free blocks) // while ((temp = segment_list)) { // segment_list = segment_list->next; // free(temp); // } - // invalidate start and end (first byte actually output will fix them) + // invalidate start and end (first byte actually written will fix them) out->lowest_written = OUTBUFFERSIZE - 1; out->highest_written = 0; - - // if start address given, set program counter - if (start_addr >= 0) { - enable_output(start_addr); - CPU_set_pc(start_addr); - out->segment.start = start_addr; - } else { - disable_output(); - out->segment.start = NO_SEGMENT_START; - } - // other stuff + // deactivate output - any byte written will trigger error: + Output_byte = no_output; + out->write_idx = 0; // same as pc on pass init! + out->segment.start = NO_SEGMENT_START; // TODO - "no active segment" could be made a segment flag! out->segment.max = OUTBUFFERSIZE - 1; out->segment.flags = 0; } -// called when "*=EXPRESSION" is parsed -void Output_start_segment(void) +// show start and end of current segment +// called whenever a new segment begins, and at end of pass. +void Output_end_segment(void) +{ + intval_t amount; + + // in later passes, ignore completely + if (pass_count) + return; + + // if there is no segment, there is nothing to do + if (out->segment.start == NO_SEGMENT_START) + return; + + // ignore "invisible" segments + if (out->segment.flags & SEGMENT_FLAG_INVISIBLE) + return; + + // ignore empty segments + amount = out->write_idx - out->segment.start; + if (amount == 0) + return; + + // link to segment list + link_segment(out->segment.start, amount); + // announce + if (Process_verbosity > 1) + printf("Segment size is %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n", + amount, amount, out->segment.start, out->write_idx); +} + + +// change output pointer and enable output +void Output_start_segment(intval_t address_change, int segment_flags) +{ + // properly finalize previous segment (link to list, announce) + Output_end_segment(); + + // calculate start of new segment + out->write_idx = (out->write_idx + address_change) & 0xffff; + out->segment.start = out->write_idx; + out->segment.flags = segment_flags; + // allow writing to output buffer + Output_byte = real_output; + // in first pass, check for other segments and maybe issue warning + if (pass_count == 0) { + if (!(segment_flags & SEGMENT_FLAG_OVERLAY)) + check_segment(out->segment.start); + find_segment_max(out->segment.start); + } +} + + +// TODO - add "!skip AMOUNT" pseudo opcode as alternative to "* = * + AMOUNT" (needed for assemble-to-end-address) +// called when "* = EXPRESSION" is parsed +// setting program counter via "* = VALUE" +// FIXME - move to basics.c +void PO_setpc(void) { void *node_body; - int new_flags = 0; + int segment_flags = 0; intval_t new_addr = ALU_defined_int(); // check for modifiers @@ -536,27 +554,13 @@ void Output_start_segment(void) return; if (!Tree_easy_scan(segment_modifier_tree, &node_body, GlobalDynaBuf)) { - Throw_error("Unknown \"*=\" segment modifier."); + Throw_error("Unknown \"* =\" segment modifier."); return; } - new_flags |= (int) node_body; + segment_flags |= (int) node_body; } - - // if there was a segment before, end it - if (out->segment.start != NO_SEGMENT_START) { - // it's a redefinition, so: - // show status of previous segment - Output_end_segment(); - // in first pass, maybe issue warning - if (pass_count == 0) { - if (!(new_flags & SEGMENT_FLAG_OVERLAY)) - check_segment(new_addr); - find_segment_max(new_addr); - } - } - out->segment.start = new_addr; - out->segment.flags = new_flags; - enable_output(new_addr); - CPU_set_pc(new_addr); + CPU_set_pc(new_addr, segment_flags); } + + diff --git a/src/output.h b/src/output.h index 8ac9980..2864b0e 100644 --- a/src/output.h +++ b/src/output.h @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // Output stuff @@ -21,8 +21,8 @@ extern void Outputfile_init(void); // alloc and init mem buffer, register pseudo opcodes (done later) extern void Output_init(signed long fill_value); -// clear segment list -extern void Output_passinit(signed long start_addr); +// clear segment list and disable output +extern void Output_passinit(void); // call this if really calling Output_byte would be a waste of time extern void Output_fake(int size); // Send low byte of arg to output buffer and advance pointer @@ -39,8 +39,10 @@ extern void Output_32b(intval_t); extern int Output_set_output_format(void); // write smallest-possible part of memory buffer to file extern void Output_save_file(FILE *fd); -// Call when "*=EXPRESSION" is parsed -extern void Output_start_segment(void); +// Call when "* = EXPRESSION" is parsed +extern void PO_setpc(void); +// change output pointer and enable output +extern void Output_start_segment(intval_t address_change, int segment_flags); // Show start and end of current segment extern void Output_end_segment(void); diff --git a/src/section.c b/src/section.c index 8022fdf..19baafa 100644 --- a/src/section.c +++ b/src/section.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816 code. -// Copyright (C) 1998-2009 Marco Baye +// Copyright (C) 1998-2014 Marco Baye // Have a look at "acme.c" for further info // // Section stuff @@ -88,11 +88,10 @@ static enum eos_t PO_zone(void) return ENSURE_EOS; } -// Start subzone ("!subzone" or "!sz"). Has to be re-entrant. +// "!subzone" or "!sz" pseudo opcode (now obsolete) static enum eos_t PO_subzone(void) { - // output deprecation warning - Throw_first_pass_warning("\"!subzone {}\" is deprecated; use \"!zone {}\" instead."); + Throw_error("\"!subzone {}\" is obsolete; use \"!zone {}\" instead."); // call "!zone" instead return PO_zone(); }