Release 0.94.8: Segment changes are now possible within pseudopc blocks.

Finally disabled obsolete pseudo opcodes "!cbm", "!subzone" and "!realpc".
!pseudopc now *must* be used with a block in {} braces.
Added support for illegal opcode LXA #$00 (opcode 0xba).


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@26 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
marcobaye 2014-03-10 00:17:10 +00:00
parent e9999a07a4
commit f1341c44fd
19 changed files with 381 additions and 325 deletions

View File

@ -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 {

View File

@ -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.

View File

@ -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.

View File

@ -4,8 +4,6 @@
...the ACME Crossassembler for Multiple Environments
Release 0.94.7
- free software -
(C) 1998-2014 Marco Baye

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

210
src/cpu.c
View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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);

View File

@ -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();
}