Compare commits

...

30 Commits
0.97 ... master

Author SHA1 Message Date
Uffe Jakobsen
a5bc7a48bd Added README.md 2020-09-17 21:39:37 +02:00
marcobaye
4901f44fdd updated syntax file for "joe" editor
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@295 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-08-26 23:25:53 +00:00
marcobaye
d59b45036d "Cannot open input file" error message now contains file name.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@294 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-08-24 16:31:36 +00:00
marcobaye
88cc8cd886 small fix to RISC OS makefile
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@293 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-08-22 22:31:33 +00:00
marcobaye
5b1fabc1f5 got rid of another init
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@292 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-08-20 18:00:20 +00:00
marcobaye
beb1e178cd got rid of most of the *_init() functions
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@291 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-08-16 17:39:13 +00:00
marcobaye
2be25080aa internal cleanup: keyword trees are now only built when needed.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@290 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-08-15 14:55:22 +00:00
marcobaye
aa8d766e6c made docs match online help
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@289 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-08-12 13:15:45 +00:00
marcobaye
b2b14cb176 minor cleanup, no change in functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@288 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-08-12 12:31:06 +00:00
marcobaye
b03b217979 disabled all indexed addressing for m65's "quad" mode, except for LDQ.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@287 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-29 23:30:33 +00:00
marcobaye
3db33bafb5 nmos6502 mode now also accepts ALR mnemonic (alias for ASR)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@286 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-28 23:08:07 +00:00
marcobaye
26168e6752 small change in tests, improved 65816 register length checking
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@285 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-28 13:13:26 +00:00
marcobaye
2acece9c60 added test script
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@284 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-28 12:57:26 +00:00
marcobaye
6dd15f7116 added BITQ and ASRQ to m65 cpu.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@283 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-27 23:37:36 +00:00
marcobaye
f87ddbb5e6 added last cpu test source and expected output
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@282 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-27 22:58:10 +00:00
marcobaye
32d59eafa3 still more test sources and expected outputs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@281 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-27 22:09:27 +00:00
marcobaye
a3d36ca156 more test sources and expected outputs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@280 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-27 21:25:39 +00:00
marcobaye
78390cb632 added more test sources
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@279 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-27 19:47:07 +00:00
marcobaye
465da8c139 several small changes:
fixed a bug where "number out of range" error led to bogus "label already defined" errors
 made "number out of range" errors more specific (8/16/24 bit range)
 re-phrased two error messages
 cleaned up docs concerning "command/opcode/mnemonic/instruction"
 added test program to trigger "out of range" errors
 internal cleanup concerning INW/DEW/JAM mnemonics


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@278 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-18 13:50:46 +00:00
marcobaye
62dd48ab9f fixed another typo in docs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@277 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-17 13:19:38 +00:00
marcobaye
b8f9bb9d36 minor changes only (FIXMEs and docs)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@276 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-17 11:47:05 +00:00
marcobaye
ca08a1d150 started work on !watch and !trace (commented out), minor cleanup, no changes
in functionality


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@275 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-12 18:46:06 +00:00
marcobaye
eb138ae785 added experimental support for 16MiB outbuf using "--test"
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@274 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-07-04 13:43:20 +00:00
marcobaye
70b9ee222d tweaked docs and removed some comments, no change in functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@273 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-30 09:24:30 +00:00
marcobaye
a7dd713d93 cleaned up list of cpu types and added opcode table(s) for m65 cpu
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@272 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-29 15:10:42 +00:00
marcobaye
a534d9c28a put hashbang info in change log
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@271 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-29 09:55:58 +00:00
marcobaye
94f36db2e5 added support for hashbang lines (if file starts with '#', line is ignored)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@270 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-29 09:53:03 +00:00
marcobaye
21cc635bc8 added library files making use of the new string features:
<cbm/msbstring.a> defines macro to store a string with msb set in last character,
<cbm/multicolor.a> defines macros to "paint" 4-color graphics via strings.


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@269 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-28 22:48:10 +00:00
marcobaye
ecca1552d0 added another test program
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@268 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-28 22:14:41 +00:00
marcobaye
d0c824c60a added library file for easy splitting of tables into low and high bytes
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@267 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-28 18:59:08 +00:00
74 changed files with 1895 additions and 722 deletions

View File

@ -1,4 +1,4 @@
;ACME 0.96.5
;ACME 0.97
!set split_cache = [] ; start every pass with an empty cache
!ifdef lib_6502_split_a !eof
lib_6502_split_a = 1

20
ACME_Lib/cbm/msbstring.a Normal file
View File

@ -0,0 +1,20 @@
;ACME 0.97
; macro to store a petscii string with msb set in last byte
!macro msbstring @s {
!ct pet {
@l = len(@s)
!if @l < 1 {
!error "String is empty!"
}
!for @i, 0, @l - 1 {
!if $80 & @s[@i] {
!error "String already contains character(s) with MSB set!"
}
}
!for @i, 0, @l - 2 {
!byte @s[@i]
}
!byte $80 | @s[-1]
}
}

94
ACME_Lib/cbm/multicolor.a Normal file
View File

@ -0,0 +1,94 @@
;ACME 0.97
!ifdef lib_cbm_multicolor_a !eof
lib_cbm_multicolor_a = 1
; this file contains macros to convert strings into bit patterns.
; the idea is to use four different characters to indicate the four
; different bit patterns of multicolor graphics, so mc sprites can be
; "drawn" in the source code even though ACME does not support any
; four-based number system. see the end of this file for an example.
; macro to set "digit" characters
; example:
; +mc_set " .o#"
!macro mc_set .s {
!if is_number(.s) or is_list(.s) {
!error "Argument to +mc_set must be a string."
} else if len(.s) != 4 {
!error "Argument to +mc_set must be four characters."
} else {
!set multicolor_alphabet = .s
}
}
; macro to convert string to number
!macro mc_value ~.result, .in, .len {
!ifndef multicolor_alphabet {
!error "Called +mc_value before calling +mc_set."
} else if is_number(.in) or is_list(.in) {
!error "Argument to +mc_value must be a string."
} else if len(.in) != .len {
!error "Argument to +mc_value must have ", .len, " characters."
} else {
!set .result = 0
!for .idx, 0, (.len / 2) - 1 {
!set .char = .in[2 * .idx] ; get first of pair
!if .char != .in[2 * .idx + 1] { ; compare to second
!error "Characters in argument to +mc_value must be given in pairs."
} else {
!if .char = multicolor_alphabet[0] {
!set .result = (.result << 2)
} else if .char = multicolor_alphabet[1] {
!set .result = (.result << 2) + 1
} else if .char = multicolor_alphabet[2] {
!set .result = (.result << 2) + 2
} else if .char = multicolor_alphabet[3] {
!set .result = (.result << 2) + 3
} else {
!error "Characters in argument to +mc_value must be from alphabet set via +mc_set."
}
}
}
}
}
; macro for a multicolor byte (for charsets)
!macro mc_8 .in {
+mc_value ~.result, .in, 8
!by .result
}
; macro for a multicolor sprite line
!macro mc_be24 .in {
+mc_value ~.result, .in, 24
!be24 .result
}
!eof
; Here's an example on how to use this:
!to "mc-sprites.prg", cbm
*=$e00
+mc_set " .o#" ; set four characters
; and now use those four characters to "paint" the sprite:
+mc_be24 " "
+mc_be24 ".. .."
+mc_be24 ".... ...."
+mc_be24 "...... ##....## ......"
+mc_be24 " .................... "
+mc_be24 " .................... "
+mc_be24 " ................ "
+mc_be24 " ######........###### "
+mc_be24 " ..oo####....####oo.. "
+mc_be24 "##..oo ######## oo..##"
+mc_be24 "....oo oo....oo oo...."
+mc_be24 "......oooo....oooo......"
+mc_be24 "........................"
+mc_be24 "......oooo....oooo......"
+mc_be24 "....oooooooooooooooo...."
+mc_be24 ".... oooooooooooo ...."
+mc_be24 ".. ####oooooooo#### .."
+mc_be24 ".. ######oooo###### .."
+mc_be24 " ###### #### "
+mc_be24 " ###### ## "
+mc_be24 " "

12
README.md Normal file
View File

@ -0,0 +1,12 @@
# ACME
Multi-platform cross-assembler for MOS 6502/65C02/6510/65816 CPUs
ACME is a free cross assembler released under the GNU GPL.
It can produce code for the following processors: 6502, 6510 (including illegal opcodes), 65c02 and 65816.
ACME supports the standard assembler stuff like global/local/anonymous labels, offset assembly, conditional assembly and looping assembly. It can include other source files as well as binaries while assembling.
Calculations can be done in integer or float mode.
Oh, and it is fast.
Imported from SourceForge SVN repository: https://sourceforge.net/projects/acme-crossass/
Release tags added - based on SVN commit messages

View File

@ -8,7 +8,7 @@ If you destroy your system, don't come whining to me.
--------------------
1) Copy the syntax file to the correct directory by typing:
cp acme.jsf /etc/joe/syntax/
cp acme.jsf /usr/share/joe/syntax/
2) Add the following lines to the "SECOND SECTION" of "/etc/joe/joerc":

View File

@ -4,6 +4,11 @@
# new in version 5: changed mnemo colors
# new in version 6: added !ifndef, !addr
# new in version 7: added !symbollist
# new in version 8: adjusted for ACME 0.97
# added backslash escaping,
# added "//" comments,
# added new mnemonics, keywords and pseudo opcodes,
# reduced colors for different instruction sets
# define colors
#
@ -18,16 +23,13 @@
=Call bold
=Comment green
=Constant cyan
=Escape bold cyan
=Keyword bold
=Pseudo bold
=Mnemo6502 bold yellow
=PCMnemo6502 bold red
=Mnemo6510 bg_red bold yellow
=PCMnemo6510 bg_red bold red
=Mnemo65c02 bg_cyan bold yellow
=PCMnemo65c02 bg_cyan bold red
=Mnemo65816 bg_blue bold yellow
=PCMnemo65816 bg_blue bold red
=MnemoExt bg_blue bold yellow
=PCMnemoExt bg_blue bold red
:reset Idle
* idle noeat
@ -36,6 +38,7 @@
:idle Idle
* idle
";" line_comment recolor=-1
"//" line_comment recolor=-1
":{\n" reset
"!.a-zA-Z_€-" checkstring recolor=-1 buffer
"+" anonf_or_macro recolor=-1
@ -101,10 +104,18 @@
:string Constant
* string
"\"" idle
"\\" string_escape recolor=-1
:string_escape Escape
* string
:char Constant
* char
"'" idle
"\\" char_escape recolor=-1
:char_escape Escape
* char
:ident Idle
* idle noeat
@ -117,10 +128,16 @@
"!by" pseudo
"!byte" pseudo
"!16" pseudo
"!le16" pseudo
"!be16" pseudo
"!wo" pseudo
"!word" pseudo
"!24" pseudo
"!le24" pseudo
"!be24" pseudo
"!32" pseudo
"!le32" pseudo
"!be32" pseudo
"!tx" pseudo
"!text" pseudo
"!raw" pseudo
@ -148,10 +165,17 @@
"!set" pseudo
"!macro" pseudo
"!if" pseudo
"!do" pseudo
"!for" pseudo
"!ifdef" pseudo
"!ifndef" pseudo
"else" keyword
"if" keyword
"ifdef" keyword
"ifndef" keyword
"!for" pseudo
"!while" pseudo
"!do" pseudo
"until" keyword
"while" keyword
"!al" pseudo
"!as" pseudo
"!rl" pseudo
@ -162,6 +186,10 @@
"!serious" pseudo
"!addr" pseudo
"!address" pseudo
"!h" pseudo
"!hex" pseudo
"!xor" pseudo
"!skip" pseudo
"ora" mnemo6502
"asl" mnemo6502
"and" mnemo6502
@ -205,9 +233,9 @@
"inx" mnemo6502
"nop" mnemo6502
"sed" mnemo6502
"jsr" mnemo6502
"brk" pcmnemo6502
"jmp" pcmnemo6502
"jsr" pcmnemo6502
"bpl" pcmnemo6502
"bmi" pcmnemo6502
"bvc" pcmnemo6502
@ -218,61 +246,149 @@
"beq" pcmnemo6502
"rti" pcmnemo6502
"rts" pcmnemo6502
"phy" mnemo65c02
"ply" mnemo65c02
"phx" mnemo65c02
"plx" mnemo65c02
"tsb" mnemo65c02
"trb" mnemo65c02
"stz" mnemo65c02
"bra" pcmnemo65c02
"wai" mnemo65816
"pei" mnemo65816
"per" mnemo65816
"mvp" mnemo65816
"mvn" mnemo65816
"rep" mnemo65816
"sep" mnemo65816
"pea" mnemo65816
"phd" mnemo65816
"tcs" mnemo65816
"pld" mnemo65816
"tsc" mnemo65816
"wdm" mnemo65816
"phk" mnemo65816
"tcd" mnemo65816
"tdc" mnemo65816
"phb" mnemo65816
"txy" mnemo65816
"plb" mnemo65816
"tyx" mnemo65816
"xba" mnemo65816
"xce" mnemo65816
"brl" pcmnemo65816
"cop" pcmnemo65816
"jml" pcmnemo65816
"jsl" pcmnemo65816
"rtl" pcmnemo65816
"stp" pcmnemo65816
"slo" mnemo6510
"rla" mnemo6510
"sre" mnemo6510
"rra" mnemo6510
"sax" mnemo6510
"lax" mnemo6510
"dcp" mnemo6510
"isc" mnemo6510
"anc" mnemo6510
"asr" mnemo6510
"arr" mnemo6510
"sbx" mnemo6510
"dop" mnemo6510
"top" mnemo6510
"lxa" mnemo6510
"jam" pcmnemo6510
"else" keyword
"until" keyword
"while" keyword
"phy" mnemoExt
"ply" mnemoExt
"phx" mnemoExt
"plx" mnemoExt
"tsb" mnemoExt
"trb" mnemoExt
"stz" mnemoExt
"bra" pcmnemoExt
"rmb0" mnemoExt
"bbr0" mnemoExt
"smb0" mnemoExt
"bbs0" mnemoExt
"rmb1" mnemoExt
"bbr1" mnemoExt
"smb1" mnemoExt
"bbs1" mnemoExt
"rmb2" mnemoExt
"bbr2" mnemoExt
"smb2" mnemoExt
"bbs2" mnemoExt
"rmb3" mnemoExt
"bbr3" mnemoExt
"smb3" mnemoExt
"bbs3" mnemoExt
"rmb4" mnemoExt
"bbr4" mnemoExt
"smb4" mnemoExt
"bbs4" mnemoExt
"rmb5" mnemoExt
"bbr5" mnemoExt
"smb5" mnemoExt
"bbs5" mnemoExt
"rmb6" mnemoExt
"bbr6" mnemoExt
"smb6" mnemoExt
"bbs6" mnemoExt
"rmb7" mnemoExt
"bbr7" mnemoExt
"smb7" mnemoExt
"bbs7" mnemoExt
"wai" mnemoExt
"pei" mnemoExt
"per" mnemoExt
"mvp" mnemoExt
"mvn" mnemoExt
"rep" mnemoExt
"sep" mnemoExt
"pea" mnemoExt
"phd" mnemoExt
"tcs" mnemoExt
"pld" mnemoExt
"tsc" mnemoExt
"wdm" mnemoExt
"phk" mnemoExt
"tcd" mnemoExt
"tdc" mnemoExt
"phb" mnemoExt
"txy" mnemoExt
"plb" mnemoExt
"tyx" mnemoExt
"xba" mnemoExt
"xce" mnemoExt
"brl" pcmnemoExt
"cop" mnemoExt
"jml" pcmnemoExt
"jsl" mnemoExt
"rtl" pcmnemoExt
"stp" pcmnemoExt
"slo" mnemoExt
"rla" mnemoExt
"sre" mnemoExt
"rra" mnemoExt
"sax" mnemoExt
"lax" mnemoExt
"dcp" mnemoExt
"isc" mnemoExt
"anc" mnemoExt
"ane" mnemoExt
"asr" mnemoExt
"arr" mnemoExt
"alr" mnemoExt
"sbx" mnemoExt
"sha" mnemoExt
"shx" mnemoExt
"shy" mnemoExt
"las" mnemoExt
"tas" mnemoExt
"dop" mnemoExt
"top" mnemoExt
"lxa" mnemoExt
"jam" pcmnemoExt
"map" mnemoExt
"eom" mnemoExt
"aug" mnemoExt
"sac" mnemoExt
"sir" mnemoExt
"orq" mnemoExt
"aslq" mnemoExt
"inq" mnemoExt
"bitq" mnemoExt
"andq" mnemoExt
"rolq" mnemoExt
"deq" mnemoExt
"asrq" mnemoExt
"eorq" mnemoExt
"lsrq" mnemoExt
"adcq" mnemoExt
"rorq" mnemoExt
"stq" mnemoExt
"ldq" mnemoExt
"cpq" mnemoExt
"sbcq" mnemoExt
"cle" mnemoExt
"see" mnemoExt
"tsy" mnemoExt
"inz" mnemoExt
"tys" mnemoExt
"dez" mnemoExt
"neg" mnemoExt
"taz" mnemoExt
"tab" mnemoExt
"bsr" mnemoExt
"tza" mnemoExt
"tba" mnemoExt
"ldz" mnemoExt
"cpz" mnemoExt
"dew" mnemoExt
"asw" mnemoExt
"phz" mnemoExt
"inw" mnemoExt
"row" mnemoExt
"phw" mnemoExt
"plz" mnemoExt
"lbpl" pcmnemoExt
"lbmi" pcmnemoExt
"lbvc" pcmnemoExt
"lbvs" pcmnemoExt
"lbra" pcmnemoExt
"lbcc" pcmnemoExt
"lbcs" pcmnemoExt
"lbne" pcmnemoExt
"lbeq" pcmnemoExt
"rtn" pcmnemoExt
done
"!a-zA-Z0-9" checkstring
# " \t" idle noeat
@ -283,17 +399,9 @@ done
* idle noeat
:pcmnemo6502 PCMnemo6502
* idle noeat
:mnemo65c02 Mnemo65c02
:mnemoExt MnemoExt
* idle noeat
:pcmnemo65c02 PCMnemo65c02
* idle noeat
:mnemo65816 Mnemo65816
* idle noeat
:pcmnemo65816 PCMnemo65816
* idle noeat
:mnemo6510 Mnemo6510
* idle noeat
:pcmnemo6510 PCMnemo6510
:pcmnemoExt PCMnemoExt
* idle noeat
:keyword Keyword
* idle noeat

View File

@ -1,27 +1,22 @@
;ACME 0.91 ; comments are green
;ACME 0.97 ; comments are green
!serious "This file is not meant to be assembled."
binary1=%00001000 ; label names are grey, constants are cyan
binary2=%....#...
octal=&0123456789 ; bad constants are bold red
decimal=63
hex1=0xcd
hex2=$ef
binary1 = %00001000 ; label names are grey, constants are cyan
binary2 = %....#...
octal = &0123456789 ; bad constants are bold red
decimal = 63
hex1 = 0xcd
hex2 = $ef
!sl "labeldump.l" ; strings are cyan
*=$1300
* = $1300
+dings ; macro calls are bold
else ; keyword: bold
!eof ; pseudo: bold
-- ; anonymous labels should be bold (white)
; 6502 mnemonics
nop ; normal ones are yellow
rts ; PC-changing ones are red
; illegals
dop ; most of them are yellow on red
jam ; this single one's red on red. Guess why.
; 65c02 extensions
stz ; normal ones are yellow on cyan
bra ; PC-changing ones (just "BRA") are red
; 65816 extensions
xce ; yellow on blue
cop ; PC-changing ones are red
; base 6502 mnemonics:
inx ; normal ones are yellow,
beq -- ; all that break sequential flow are red
rts
; all extended instruction sets:
stz ; normal ones are yellow on blue
bra ; flow-breaking ones are red

View File

@ -12,10 +12,10 @@ ACME.
----------------------------------------------------------------------
Section: Command aliases for "long" JMPs and JSRs
Section: Aliases for "long" JMPs and JSRs
----------------------------------------------------------------------
In addition to the commands JMP and JSR, the 65816 processor also
In addition to the mnemonics JMP and JSR, the 65816 processor also
knows JML and JSL, which are JMP and JSR using new (long) addressing
modes. ACME also accepts the new addressing modes when using the old
mnemonics JMP and JSR, but the old addressing modes cannot be used
@ -55,13 +55,13 @@ this at any time using the following pseudo opcodes:
!rs ; switch to short index registers
Please note that ACME, unlike some other assemblers, does *not* track
SEP/REP commands: I don't like that method - it fails when
SEP/REP instructions: I don't like that method - it fails when
encountering PLPs, for example. So if it doesn't work reliably in the
first place, why use it? :)
If you don't like that you always have to use a pseudo opcode
alongside SEP/REP commands, then have a look at the file <65816/std.a>
(in the library). There are some predefined macros that you can use.
alongside SEP/REP instructions, then have a look at the library file
<65816/std.a> which has some predefined macros you can use.
----------------------------------------------------------------------

View File

@ -72,6 +72,8 @@ Fix: Characters are now unsigned (had been architecture-dependent).
Improved error handling of "--cpu" and "--format" switches.
Added opcode table for NMOS6502 cpu to docs.
Added some test sources.
Added support for "hashbang" lines (if file starts with a '#'
character, the first line is ignored)
Fixed some minor bugs no-one ever seems to have encountered.
Rewritten "docs/Upgrade.txt".

View File

@ -112,12 +112,12 @@ Label name starts with a shift-space character.
warning is issued.
Memory already initialised.
The "!initmem" command was given more than once (or in addition to
the "--initmem" command line option). Only use it once.
The "!initmem" pseudo opcode was given more than once, or in
addition to the "--initmem" command line option. Only use it once.
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.
The "!to" pseudo opcode was given more than once, or in addition
to the "--outfile" command line option. Only use it once.
Segment reached another one, overwriting it.
The program counter has just reached the start of another segment.
@ -129,7 +129,7 @@ Segment reached another one, overwriting it.
ACME this might become the default.
Segment starts inside another one, overwriting it.
The given value in a "*=" command is located inside another
The given value in a "*=" assignment is located inside another
segment. Because some people might want to assemble "onto" a
binary file that was loaded before, this warning can be inhibited
using modifier keywords when changing the program counter via
@ -139,8 +139,8 @@ Segment starts inside another one, overwriting it.
ACME this might become the default.
Symbol list file name already chosen.
The "!sl" command was given more than once (or in addition to the
"--symbollist" command line option). Only use it once.
The "!sl" pseudo opcode was given more than once, or in addition
to the "--symbollist" command line option. Only use it once.
Used "!to" without file format indicator. Defaulting to "cbm".
Now that "!to" can be given a file format keyword (either "plain"
@ -148,19 +148,19 @@ Used "!to" without file format indicator. Defaulting to "cbm".
works though.
Using oversized addressing mode.
ACME just assembled a command using an addressing mode that was
larger than needed. This only happens if ACME could not work out
the argument's value in the first pass, therefore assuming a 16-
bit addressing mode. If, in a later pass, ACME finds out that the
argument is small enough to fit in 8 bits, then this warning is
shown. If you define all your zeropage symbols *before* they are
first used, this shouldn't happen. If you know that a specific
ACME just assembled an instruction using an addressing mode that
was larger than needed. This only happens if ACME could not work
out the argument's value in the first pass, therefore assuming a
16-bit addressing mode. If, in a later pass, ACME finds out that
the argument is small enough to fit in 8 bits, then this warning
is shown. If you define all your zeropage symbols *before* they
are first used, this shouldn't happen. If you know that a specific
argument fits in 8 bits, you can force ACME to use 8 bits
addressing by postfixing the command with "+1". Example:
addressing by postfixing the mnemonic with "+1". Example:
lda+1 label
ACME will then use an 8-bit addressing mode, regardless of whether
the label is known or not. If the label value happens to be too
large to fit in 8 bits, ACME will show an error of course (To
large to fit in 8 bits, ACME will show an error of course (to
always truncate a value to 8 bits, use the '<' operator).
More about the postfixing method can be found in "AddrModes.txt".
@ -233,6 +233,15 @@ Conversion table incomplete.
The conversion table file is too small. It needs to be exactly 256
bytes in size.
CPU does not support this addressing mode for this mnemonic.
The given mnemonic cannot be combined with the given addressing
mode on the CPU you have chosen.
CPU does not support this postfix for this mnemonic.
The given mnemonic cannot be combined with the addressing mode
indicated by the given postfix, at least not on the CPU you have
chosen.
Division by zero.
Guess what - you attempted to divide by zero.
@ -271,14 +280,6 @@ Hex digits are not given in pairs.
The two digits of a hex byte are separated by another character,
or there is an odd number of digits.
Illegal combination of command and addressing mode.
The given command cannot be used with the given addressing mode on
the CPU you have chosen.
Illegal combination of command and postfix.
The given command cannot be used with the addressing mode
indicated by the given postfix.
Illegal postfix.
You used a postfix other than "+1", "+2" or "+3".
@ -317,8 +318,9 @@ Negative value - cannot choose addressing mode.
No string given.
ACME expects a string but doesn't find it, or the string is empty.
Number does not fit in N bits.
Number out of range.
A value is too high or too low.
A value is too high or too low to be stored in 8/16/24 bits.
This can also mean the desired addressing mode is not available,
as in "sty $e000, x".
@ -349,14 +351,19 @@ Syntax error.
Target not in bank (0xTARGET).
You tried to branch to an address not in the 0x0000..0xffff range.
Relative addressing (branch commands or PER) cannot leave the
Relative addressing (branch instructions or PER) cannot leave the
current code bank of 64 KiB.
Target out of range (N; M too far).
Branch commands use relative addressing, which only has a limited
range. You exceeded it. N is the attempted offset, M is the
difference to the limit - so if you succeed in optimizing M bytes
away, the code would assemble.
Branch instructions use relative addressing, which only has a
limited range. You exceeded it. N is the attempted offset, M is
the difference to the limit - so if you succeed in optimizing M
bytes away, the code would assemble.
The chosen CPU uses opcode 0xXY as a prefix code, do not use this mnemonic!
The mnemonic is valid, but should not be used on this CPU. If you
know better, you can get around this error like this:
!cpu ANY_OTHER_CPU { PROBLEMATIC_MNEMONIC }
There's more than one character.
You used a text string containing more than one character in a
@ -384,7 +391,8 @@ Un-pseudopc operator '&' has no !pseudopc context.
around the definition.
Unknown encoding.
You used the "!convtab" command with a keyword ACME does not know.
You used the "!convtab" pseudo opcode with a keyword ACME does not
know.
Unknown function.
You used a mathematical function ACME does not know.
@ -393,17 +401,26 @@ Unknown operator.
You used an arithmetic/logical operator ACME does not know.
Unknown output format.
You used the "!to" command with a keyword ACME does not know.
You used the "!to" pseudo opcode with a format specifier ACME does
not know.
Unknown processor.
You used the "!cpu" command with a keyword ACME does not know.
You used the "!cpu" pseudo opcode with a cpu specifier ACME does
not know.
Unknown pseudo opcode.
You have mistyped a "!" command.
You have mistyped the keyword after "!".
Unknown "*=" segment modifier.
You used a modifier keyword ACME does not know.
Unsupported backslash sequence.
The character following the backslash was not one of the allowed
ones. Backslash escaping was added in release 0.97 of ACME.
If you want to assemble an old source code without first updating
it, you can use the "--dialect" CLI switch to make ACME mimic an
older version.
Unterminated index spec.
An index was started with '[' but did not end with ']'.
@ -429,8 +446,9 @@ Found end-of-file instead of '}'.
(because there was at least one block left open).
Loop count is negative.
You used the "!for" command with a negative loop count (getting
this error is only possible when using the now deprecated syntax).
You used the "!for" pseudo opcode with a negative loop count
(getting this error is only possible when using the now deprecated
syntax).
Macro already defined.
Macros can only be defined once. If you define a macro twice, ACME
@ -440,7 +458,7 @@ Macro already defined.
Missing '{'.
ACME didn't find the expected '{' character. Remember that '{'
characters must be given on the same line as the command they
characters must be given on the same line as the keyword they
belong to.
Out of memory.
@ -533,9 +551,15 @@ IllegalImmediateMode
The mnemonic tree contains invalid info about the size of immediate
arguments.
IllegalInputSource
Input is taken neither from a file nor from a RAM block.
IllegalNumberTypeX
A number was neither INT nor FLOAT nor UNDEFINED.
IllegalObjectType
A symbol is used that is neither number nor list nor string.
IllegalOperatorId
IllegalOperatorGroup
The expression parser found an operator that does not exist.
@ -547,6 +571,7 @@ NotEnoughArgs
There was not enough data for a dyadic operator to work on.
NullTypeObject
ObjectHasNullType
A symbol is used that does not have a type (number/list/string)
associated with it.
@ -558,6 +583,9 @@ OperatorStackNotEmpty
The expression parser has finished though there are still
operators left to process.
PartialEscapeSequence
Buffered data ended on a backslash, which shouldn't be possible.
SecondArgIsNotAnInt
A sanity check failed: An argument should have been converted to
integer but wasn't.

View File

@ -69,7 +69,7 @@ The files in the docs directory and what they contain:
IMPORTANT: If you upgrade from an earlier version of ACME, don't
forget to read the files "Changes.txt" and "Upgrade.txt". Adding new
features can not always be done in a 100% compatible way, so newer
version may behave slightly different. To solve this problem, the
versions may behave slightly differently. To solve this problem, the
"--dialect" CLI switch can be used.
If you want to start using ACME right away, read the file

View File

@ -40,7 +40,7 @@ opcodes (mnemonics in parentheses are used by other sources):
mnemonic | implied #8 8 8,x 16 16,x | performs:
----------------+---------------------------------+-----------------------
anc (ana, anb) | 0b* | A = A & arg, then C=N
asr (alr) | 4b | A = A & arg, then lsr
alr/asr | 4b | A = A & arg, then lsr
arr | 6b | A = A & arg, then ror
sbx (axs, sax) | cb | X = (A & X) - arg
dop (nop, skb) | 80** 80 04 14 | skips next byte

View File

@ -213,8 +213,8 @@ Available options are:
--vicelabels FILE set file name for label dump in VICE format
The resulting file uses a format suited for the VICE emulator.
--setpc NUMBER set program counter
This can also be given in the source code using "* = NUMBER".
--setpc VALUE set program counter
This can also be given in the source code using "* = VALUE".
--cpu CPU_TYPE set target processor
This can be changed in the source code using the "!cpu" pseudo
@ -222,7 +222,7 @@ Available options are:
Use this with a bogus cpu type to get a list of all supported
ones.
--initmem NUMBER define 'empty' memory
--initmem VALUE define 'empty' memory
This can also be given using the "!initmem" pseudo opcode.
Defaults to zero.
@ -374,9 +374,9 @@ poll_joy2 a global symbol
will give the same value four times. I think most
assemblers do it this way.
In older versions of ACME , 'x' and "x" were the same thing, namely
the character code of the letter x using the currently selected
encoding table.
In older versions of ACME, 'x' and "x" were the same thing, namely the
character code of the letter x using the currently selected encoding
table.
Since release 0.97, anything in single quotes gives the character code
(as before), while anything in double quotes is treated as a string
object. To be compatible to those older versions, ACME keeps accepting

View File

@ -163,8 +163,8 @@ The mnemonic BIT can no longer be assembled without any argument. If
you want to insert the opcode only to mask the next instruction, use
!src <6502/std.a>
to get the definitions for these two macros:
+bit8 ; output $24 to mask following 1-byte command
+bit16 ; output $2c to mask following 2-byte command
+bit8 ; output $24 to mask following 1-byte instruction
+bit16 ; output $2c to mask following 2-byte instruction
When using the 65816 cpu, ACME now uses the correct argument order for
the MVN and MVP mnemonics, which is:

View File

@ -7,11 +7,32 @@
--- cpu types ---
ACME supports the following cpu types:
ACME supports the following cpu types (shown here as a sort of family
tree):
6502 standard
|
|\_nmos6502 (=6510) + undocumented opcodes
|
|\_c64dtv2 + BRA/SAC/SIR and some (not all!) undocumented
|
\_65c02 + BRA/PHX/PHY/PLX/PLY/STZ/TRB/TSB/...
|
|\_65816 16 bit regs, 24 bit address space, ...
|
\_r65c02 + bit manipulation instructions
|
|\_w65c02 + STP/WAI
|
\_65ce02 + Z reg, long branches, ...
|
\_4502 + MAP/EOM
|
\_m65 + 32-bit pointers, 32-bit 'Q' register
*** 6502
!cpu 6502
This is the official instruction set of the original NMOS 6502 CPU
designed by MOS (later CSG).
@ -21,16 +42,34 @@ the mnemonic without any argument: "LSR" will work, "LSR A" won't.
*** 6510
!cpu nmos6502
This is the 6502 variant used in the C64 computer. It uses the same
instruction set as the 6502, but in addition to that, ACME supports
most of the undocumented opcodes as well.
This instruction set includes the undocumented ("illegal") opcodes of
the NMOS 6502.
See "docs/Illegals.txt" for more info.
*** 65c02
!cpu 6510
This is an alias for "nmos6502", because the 6510 cpu (as used in the
C64 computer) is a variant of this type.
!cpu c64dtv2
This is the cpu in version 2 of the C64DTV. It uses a superset of the
6502 instruction set. Features:
- new instructions:
BRA near_target branch always
SAC #$12 set accumulator mapping
SIR #$12 set index register mapping
- support for some (but not all!) of the undocumented opcodes.
!cpu 65c02
This is the CMOS re-design of the 6502. It seems to have also been
available from Rockwell, GTE/CMD and others. Features:
@ -61,7 +100,21 @@ There are 178 documented opcodes.
*** r65c02
!cpu 65816
This is a superset of 65c02, originally designed by WDC (it seems to
have been available from GTE/CMD as well). Features:
- register sizes can be changed to 16-bit
- 24-bit address space
- several new instructions (including block transfers)
- several new addressing modes for existing instructions
There are 256 documented opcodes, but one of them ("WDM") is reserved
for future expansion.
See "docs/65816.txt" for more info.
!cpu r65c02
This is a superset of 65c02, probably originally by Rockwell. It adds
bit manipulation instructions:
@ -77,7 +130,7 @@ There are 210 documented opcodes.
*** w65c02
!cpu w65c02
This is a superset of r65c02, originating at WDC. It adds two new
instructions:
@ -87,21 +140,7 @@ There are 212 documented opcodes.
*** 65816
This is a superset of 65c02, originally designed by WDC (it seems to
have been available from GTE/CMD as well). Features:
- register sizes can be changed to 16-bit
- 24-bit address space
- several new instructions (including block transfers)
- several new addressing modes for existing instructions
There are 256 documented opcodes, but one of them ("WDM") is reserved
for future expansion.
See "docs/65816.txt" for more info.
*** 65ce02
!cpu 65ce02
This is a superset of r65c02, originating at CSG. Features:
- Z register
@ -121,7 +160,7 @@ unconditional") instead. ACME accepts both mnemonics.
*** 4502
!cpu 4502
This is basically the same as 65ce02, but
- MAP replaces AUG
@ -131,65 +170,55 @@ There are 256 documented opcodes.
*** m65
!cpu m65
This is a superset of 4502 specified by the MEGA65 project. It uses
NOP and NEG:NEG as prefix bytes to extend the instruction set.
NEG:NEG and NOP as prefix bytes to extend the instruction set.
Features:
- new "long indirect z-indexed" addressing mode with four-byte-pointer
for existing instructions:
LDA/STA/ADC/SBC [$12], z ; contents of $12/$13/$14/$15
AND/ORA/EOR/CMP [$12], z ; plus z form the address
- 32-bit data operations indicated via 'Q' ("quad"):
- "quad mode" (32-bit data operations on virtual register 'Q')
- "long mode" (32-bit pointer addressing for existing mnemonics)
- "quad" and "long" modes can be combined
quad mode introduces several new mnemonics:
LDQ/STQ/CPQ like LDA/STA/CMP
ADCQ/SBCQ like ADC/SBC
ANDQ/EORQ/ORQ like AND/EOR/ORA
ASLQ/LSRQ/ROLQ/RORQ like ASL/LSR/ROL/ROR
INQ/DEQ like INC/DEC
The new mnemonics support all the addressing modes of the original
mnemonics, except there are no 32-bit immediate arguments.
CAUTION: The STQ mnemonic clobbers the N and Z flags!
BITQ like BIT
ASRQ like ASR
The new mnemonics support most of the addressing modes of the
original mnemonics with these exceptions:
- there is no immediate addressing
- indirect-Z-indexed addressing becomes indirect addressing
- all other indexed addressing modes can only really be used
with read-modify-write instructions or LDQ, because otherwise
a part of the 'Q' value would be used as the index.
CAUTION: The STQ instruction clobbers the N and Z flags!
There is no "real" Q register, instead A/X/Y/Z are combined to form
the Q register (A holds lsb, Z holds msb), except for read-modify-
write instructions, where the 32-bit operation is performed without
using A/X/Y/Z.
- The NOP mnemonic is disabled for this instruction set because its
opcode is re-used internally as a prefix byte.
CAUTION: The !align pseudo opcode still inserts NOPs.
*** c64dtv2
This is the cpu in version 2 of the C64DTV. It uses a superset of the
6502 instruction set. Features:
- new instructions:
BRA near_target branch always
SAC #$12 set accumulator mapping
SIR #$12 set index register mapping
- support for some of the undocumented opcodes.
Here's a family tree:
6502 (standard)
|
|\_6510 (+ undocumented opcodes of nmos6502)
|
|\_c64dtv2 (+ bra/sac/sir and some undocumented)
|
\_65c02 (+ bra/phx/phy/plx/ply/stz/trb/tsb, ...)
|
|\_65816 (16 bit regs, 24 bit address space, ...)
|
\_r65c02 (+ bit manipulation instructions)
|
|\_w65c02 (+ stp/wai)
|
\_65ce02 (+ Z reg, long branches, ...)
|
\_4502 (+ map/eom)
|
\_m65 (+ 32-bit pointers, 32-bit data)
To load a 32-bit immediate constant into the Q register, use the
+movq macro from the <m65/std.a> library file.
long mode brings a single new addressing mode for eight mnemonics:
LDA [$12], z contents of $12/$13/$14/$15
STA [$12], z plus z form the address
CMP [$12], z
ADC [$12], z
SBC [$12], z
AND [$12], z
EOR [$12], z
ORA [$12], z
quad and long modes combined result in another addressing mode for
eight of the new mnemonics:
LDQ [$12] contents of $12/$13/$14/$15
STQ [$12] form the address
CPQ [$12]
ADCQ [$12]
SBCQ [$12]
ANDQ [$12]
EORQ [$12]
ORQ [$12]
The NOP mnemonic is disabled for this instruction set because its
opcode is re-used internally as a prefix byte.
CAUTION: The !align pseudo opcode still inserts NOPs.

120
docs/cputypes/cpu m65.txt Normal file
View File

@ -0,0 +1,120 @@
m65 opcode table(s)
The m65 instruction set extends the 4502 instruction set using prefix bytes.
Therefore, the "normal" opcode table is the same as for the 4502 cpu (see that
file), so this file only contains information about the extensions.
"quad mode" allows 32-bit data operations using a virtual register called 'Q'.
The mnemonics aslq/lsrq/rolq/rorq/inq/deq have five addressing modes.
The mnemonic ldq has eight addressing modes in quad mode, and a ninth when
combined with long mode.
The mnemonics stq/cpq/adcq/sbcq/andq/eorq/orq have three addressing modes in
quad mode, and a fourth when combined with long mode.
The mnemonic bitq has two addressing modes.
The mnemonic asrq has three addressing modes.
This mode is entered after a NEG:NEG (42 42) prefix, the following opcode is
then taken from this table:
00 01 02 03
04 05 orq zp 06 aslq zp 07
08 09 0a aslq 0b
0c 0d orq abs16 0e aslq abs16 0f
10 11 12 orq (zp) 13
14 15 16 aslq zp, x 17
18 19 1a inq 1b
1c 1d 1e aslq abs16, x 1f
20 21 22 23
24 bitq zp 25 andq zp 26 rolq zp 27
28 29 2a rolq 2b
2c bitq abs16 2d andq abs16 2e rolq abs16 2f
30 31 32 andq (zp) 33
34 35 36 rolq zp, x 37
38 39 3a deq 3b
3c 3d 3e rolq abs16, x 3f
40 41 42 43 asrq
44 asrq zp 45 eorq zp 46 lsrq zp 47
48 49 4a lsrq 4b
4c 4d eorq abs16 4e lsrq abs16 4f
50 51 52 eorq (zp) 53
54 asrq zp, x 55 56 lsrq zp, x 57
58 59 5a 5b
5c 5d 5e lsrq abs16, x 5f
60 61 62 63
64 65 adcq zp 66 rorq zp 67
68 69 6a rorq 6b
6c 6d adcq abs16 6e rorq abs16 6f
70 71 72 adcq (zp) 73
74 75 76 rorq zp, x 77
78 79 7a 7b
7c 7d 7e rorq abs16, x 7f
80 81 82 83
84 85 stq zp 86 87
88 89 8a 8b
8c 8d stq abs16 8e 8f
90 91 92 stq (zp) 93
94 95 96 97
98 99 9a 9b
9c 9d 9e 9f
a0 a1 a2 a3
a4 a5 ldq zp a6 a7
a8 a9 aa ab
ac ad ldq abs16 ae af
b0 b1 ldq (zp), y b2 ldq (zp) b3
b4 b5 ldq zp, x b6 b7
b8 b9 ldq abs16, y ba bb
bc bd ldq abs16, x be bf
c0 c1 c2 c3
c4 c5 cpq zp c6 deq zp c7
c8 c9 ca cb
cc cd cpq abs16 ce deq abs16 cf
d0 d1 d2 cpq (zp) d3
d4 d5 d6 deq zp, x d7
d8 d9 da db
dc dd de deq abs16, x df
e0 e1 e2 ldq (zp, s), y e3
e4 e5 sbcq zp e6 inq zp e7
e8 e9 ea eb
ec ed sbcq abs16 ee inq abs16 ef
f0 f1 f2 sbcq (zp) f3
f4 f5 f6 inq zp, x f7
f8 f9 fa fb
fc fd fe inq abs16, x ff
zp: 8-bit zeropage address
abs16: 16-bit absolute address
"long mode" adds an addressing mode using 32-bit pointers for eight existing
mnemonics. This mode is entered after a NOP (ea) prefix, the following opcode
should then be one of these:
12 ora [zp], z 32 and [zp], z 52 eor [zp], z 72 adc [zp], z
92 sta [zp], z b2 lda [zp], z d2 cmp [zp], z f2 sbc [zp], z
"quad" and "long" modes can be combined to have 32-bit data access using a
32-bit pointer. This adds another addressing mode for eight of the new
mnemonics. This mode is entered after a NEG:NEG:NOP (42 42 ea) prefix, the
following opcode should then be one of these:
12 orq [zp] 32 andq [zp] 52 eorq [zp] 72 adcq [zp]
92 stq [zp] b2 ldq [zp] d2 cpq [zp] f2 sbcq [zp]
Because the addressing modes are changed a bit by the prefix codes, here are
some of the unsupported combinations just for comparison (these result in
"Illegal combination of command and addressing mode"):
lda (zp) ; 65c02 knew this, but 65ce02 added z index!
lda [zp] ; long mode also expects z index!
ldq #imm ; quad mode has no immediate addressing!
ldq (zp), z ; quad mode does not use z index!
ldq [zp], z ; quad and long modes combined do not use z index!

View File

@ -27,7 +27,7 @@ marked using '+' or '!' signs:
40 rti 41 eor (zp, x) 42! jam 43+ sre (zp, x)
44! nop zp 45 eor zp 46 lsr zp 47+ sre zp
48 pha 49 eor #imm8 4a lsr 4b+ asr #imm8
48 pha 49 eor #imm8 4a lsr 4b+ alr #imm8
4c jmp abs16 4d eor abs16 4e lsr abs16 4f+ sre abs16
50 bvc rel8 51 eor (zp), y 52! jam 53+ sre (zp), y
54! nop zp, x 55 eor zp, x 56 lsr zp, x 57+ sre zp, x

View File

@ -32,7 +32,7 @@ encoding.o: config.h alu.h acme.h dynabuf.h global.h output.h input.h tree.h enc
flow.o: config.h acme.h alu.h dynabuf.h global.h input.h mnemo.h symbol.h tree.h flow.h flow.c
global.o: config.h platform.h acme.h cpu.h encoding.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c
global.o: config.h platform.h acme.h cpu.h dynabuf.h encoding.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c
input.o: config.h alu.h dynabuf.h global.h section.h symbol.h tree.h input.h input.c

View File

@ -32,7 +32,7 @@ encoding.o: config.h alu.h acme.h dynabuf.h global.h output.h input.h tree.h enc
flow.o: config.h acme.h alu.h dynabuf.h global.h input.h mnemo.h symbol.h tree.h flow.h flow.c
global.o: config.h platform.h acme.h cpu.h encoding.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c
global.o: config.h platform.h acme.h cpu.h dynabuf.h encoding.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c
input.o: config.h alu.h dynabuf.h global.h section.h symbol.h tree.h input.h input.c

View File

@ -35,7 +35,7 @@ encoding.o: config.h alu.h acme.h dynabuf.h global.h output.h input.h tree.h enc
flow.o: config.h acme.h alu.h dynabuf.h global.h input.h mnemo.h symbol.h tree.h flow.h flow.c
global.o: config.h platform.h acme.h cpu.h encoding.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c
global.o: config.h platform.h acme.h cpu.h dynabuf.h encoding.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c
input.o: config.h alu.h dynabuf.h global.h section.h symbol.h tree.h input.h input.c

View File

@ -1,4 +1,5 @@
CFLAGS = -O3 -mthrowback -mlibscl -Wall -Wstrict-prototypes
LINKFLAGS = -mlibscl -Wall
LIBS = -lm
CC = gcc
RM = rm
@ -13,7 +14,7 @@ OBJS = acme.o alu.o cliargs.o cpu.o dynabuf.o encoding.o flow.o global.o input.
all: $(PROGS)
acme: $(OBJS)
$(CC) $(CFLAGS) -o !Unsqueezed $(OBJS) $(LIBS)
$(CC) $(LINKFLAGS) -o !Unsqueezed $(OBJS) $(LIBS)
Squeeze -f -v !Unsqueezed !ACME.!RunImage
acme.o: config.h platform.h acme.h alu.h cpu.h dynabuf.h encoding.h flow.h global.h input.h macro.h mnemo.h output.h pseudoopcodes.h section.h symbol.h version.h acme.h acme.c
@ -30,7 +31,7 @@ encoding.o: config.h alu.h acme.h dynabuf.h global.h output.h input.h tree.h enc
flow.o: config.h acme.h alu.h dynabuf.h global.h input.h mnemo.h symbol.h tree.h flow.h flow.c
global.o: config.h platform.h acme.h cpu.h encoding.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c
global.o: config.h platform.h acme.h cpu.h dynabuf.h encoding.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c
input.o: config.h alu.h dynabuf.h global.h section.h symbol.h tree.h input.h input.c

View File

@ -129,9 +129,9 @@ static void show_help_and_exit(void)
" -l, --" OPTION_SYMBOLLIST " FILE set symbol list file name\n"
" --" OPTION_LABELDUMP " (old name for --" OPTION_SYMBOLLIST ")\n"
" --" OPTION_VICELABELS " FILE set file name for label dump in VICE format\n"
" --" OPTION_SETPC " NUMBER set program counter\n"
" --" OPTION_SETPC " VALUE set program counter\n"
" --" OPTION_CPU " CPU set target processor\n"
" --" OPTION_INITMEM " NUMBER define 'empty' memory\n"
" --" OPTION_INITMEM " VALUE define 'empty' memory\n"
" --" OPTION_MAXERRORS " NUMBER set number of errors before exiting\n"
" --" OPTION_MAXDEPTH " NUMBER set recursion depth for macro calls and !src\n"
" --" OPTION_IGNORE_ZEROES " do not determine number size by leading zeroes\n"
@ -422,6 +422,7 @@ static void set_starting_pc(const char expression[])
start_address = string_to_number(expression);
if ((start_address > -1) && (start_address < 65536))
return;
fprintf(stderr, "%sProgram counter out of range (0-0xffff).\n", cliargs_error);
exit(EXIT_FAILURE);
}
@ -433,6 +434,7 @@ static void set_mem_contents(const char expression[])
fill_value = string_to_number(expression);
if ((fill_value >= -128) && (fill_value <= 255))
return;
fprintf(stderr, "%sInitmem value out of range (0-0xff).\n", cliargs_error);
exit(EXIT_FAILURE);
}
@ -637,24 +639,15 @@ int main(int argc, const char *argv[])
if (argc == 1)
show_help_and_exit();
cliargs_init(argc, argv);
DynaBuf_init(); // inits *global* dynamic buffer - important, so first
// Init platform-specific stuff.
// For example, this could read the library path from an
// environment variable, which in turn may need DynaBuf already.
// init platform-specific stuff.
// this may read the library path from an environment variable.
PLATFORM_INIT;
// prepare a buffer large enough to hold pointers to "-D" switch values
// cli_defines = safe_malloc(argc * sizeof(*cli_defines));
includepaths_init(); // must be done before cli arg handling
// handle command line arguments
cliargs_handle_options(short_option, long_option);
// generate list of files to process
cliargs_get_rest(&toplevel_src_count, &toplevel_sources, "No top level sources given");
// Init modules (most of them will just build keyword trees)
ALU_init();
Macro_init();
Mnemo_init();
Output_init(fill_value);
pseudoopcodes_init(); // setup keyword tree for pseudo opcodes
// init output buffer
Output_init(fill_value, config.test_new_features);
if (do_actual_work())
save_output_file();
return ACME_finalize(EXIT_SUCCESS); // dump labels, if wanted

View File

@ -35,8 +35,8 @@
// constants
#define ERRORMSG_DYNABUF_INITIALSIZE 256 // ad hoc
#define FUNCTION_DYNABUF_INITIALSIZE 8 // enough for "arctan"
#define ERRORMSG_INITIALSIZE 256 // ad hoc
#define FUNCTION_INITIALSIZE 8 // enough for "arctan"
#define HALF_INITIAL_STACK_SIZE 8
static const char exception_div_by_zero[] = "Division by zero.";
static const char exception_no_value[] = "No value given.";
@ -167,8 +167,8 @@ static struct op ops_isstring = {42, OPGROUP_MONADIC, OPID_ISSTRING, "is_string
// variables
static struct dynabuf *errormsg_dyna_buf; // dynamic buffer to build variable-length error messages
static struct dynabuf *function_dyna_buf; // dynamic buffer for fn names
static STRUCT_DYNABUF_REF(errormsg_dyna_buf, ERRORMSG_INITIALSIZE); // to build variable-length error messages
static STRUCT_DYNABUF_REF(function_dyna_buf, FUNCTION_INITIALSIZE); // for fn names
// operator stack, current size and stack pointer:
static struct op **op_stack = NULL;
static int opstack_size = HALF_INITIAL_STACK_SIZE;
@ -186,22 +186,22 @@ enum alu_state {
};
static enum alu_state alu_state; // deterministic finite automaton
// predefined stuff
static struct ronode *op_tree = NULL; // tree to hold operators
static struct ronode op_list[] = {
PREDEFNODE(s_asr, &ops_asr),
PREDEFNODE(s_lsr, &ops_lsr),
PREDEFNODE(s_asl, &ops_shift_left),
static struct ronode op_tree[] = {
PREDEF_START,
PREDEFNODE("asr", &ops_asr),
PREDEFNODE("lsr", &ops_lsr),
PREDEFNODE("asl", &ops_shift_left),
PREDEFNODE("lsl", &ops_shift_left),
PREDEFNODE("div", &ops_intdiv),
PREDEFNODE("mod", &ops_modulo),
PREDEFNODE(s_and, &ops_and),
PREDEFNODE("and", &ops_and),
PREDEFNODE("or", &ops_or),
PREDEFNODE(s_eor, &ops_eor), // FIXME - remove
PREDEFLAST("xor", &ops_xor),
PREDEFNODE("eor", &ops_eor), // FIXME - remove
PREDEF_END("xor", &ops_xor),
// ^^^^ this marks the last element
};
static struct ronode *function_tree = NULL; // tree to hold functions
static struct ronode function_list[] = {
static struct ronode function_tree[] = {
PREDEF_START,
PREDEFNODE("addr", &ops_addr),
PREDEFNODE("address", &ops_addr),
PREDEFNODE("int", &ops_int),
@ -215,7 +215,7 @@ static struct ronode function_list[] = {
PREDEFNODE("arctan", &ops_arctan),
PREDEFNODE("sin", &ops_sin),
PREDEFNODE("cos", &ops_cos),
PREDEFLAST("tan", &ops_tan),
PREDEF_END("tan", &ops_tan),
// ^^^^ this marks the last element
};
@ -248,6 +248,7 @@ do { \
static void enlarge_operator_stack(void)
{
opstack_size *= 2;
//printf("Doubling op stack size to %d.\n", opstack_size);
op_stack = realloc(op_stack, opstack_size * sizeof(*op_stack));
if (op_stack == NULL)
Throw_serious_error(exception_no_memory_left);
@ -258,24 +259,13 @@ static void enlarge_operator_stack(void)
static void enlarge_argument_stack(void)
{
argstack_size *= 2;
//printf("Doubling arg stack size to %d.\n", argstack_size);
arg_stack = realloc(arg_stack, argstack_size * sizeof(*arg_stack));
if (arg_stack == NULL)
Throw_serious_error(exception_no_memory_left);
}
// create dynamic buffer, operator/function trees and operator/argument stacks
void ALU_init(void)
{
errormsg_dyna_buf = DynaBuf_create(ERRORMSG_DYNABUF_INITIALSIZE);
function_dyna_buf = DynaBuf_create(FUNCTION_DYNABUF_INITIALSIZE);
Tree_add_table(&op_tree, op_list);
Tree_add_table(&function_tree, function_list);
enlarge_operator_stack();
enlarge_argument_stack();
}
// not-so-braindead algorithm for calculating "to the power of" function for
// integer arguments.
// my_pow(whatever, 0) returns 1.
@ -2378,6 +2368,12 @@ static int parse_expression(struct expression *expression)
{
struct object *result = &expression->result;
// make sure stacks are ready (if not yet initialised, do it now)
if (arg_stack == NULL)
enlarge_argument_stack();
if (op_stack == NULL)
enlarge_operator_stack();
// init
expression->is_empty = TRUE; // becomes FALSE when first valid char gets parsed
expression->open_parentheses = 0;
@ -2390,9 +2386,10 @@ static int parse_expression(struct expression *expression)
PUSH_OP(&ops_start_expression);
alu_state = STATE_EXPECT_ARG_OR_MONADIC_OP;
do {
// check stack sizes. enlarge if needed
// check arg stack size. enlarge if needed
if (arg_sp >= argstack_size)
enlarge_argument_stack();
// (op stack size is checked whenever pushing an operator)
switch (alu_state) {
case STATE_EXPECT_ARG_OR_MONADIC_OP:
if (expect_argument_or_monadic_operator(expression))
@ -2445,8 +2442,8 @@ static int parse_expression(struct expression *expression)
// make sure no additional (spurious) errors are reported:
Input_skip_remainder();
// FIXME - remove this when new function interface gets used:
// callers must decide for themselves what to do when expression parser returns error
// (currently LDA'' results in both "no string given" AND "illegal combination of command and addressing mode"!)
// callers must decide for themselves what to do when expression
// parser returns error (and may decide to call Input_skip_remainder)
return 1; // error
}
}

View File

@ -50,19 +50,16 @@ struct expression {
// labels that are undefined, we can't simply get the addressing mode
// from looking at the parameter's value. FIXME - rename to TAINTED :)
// create dynamic buffer, operator/function trees and operator/operand stacks
extern void ALU_init(void);
/*
// FIXME - replace all the functions below with a single one using a "flags" arg!
// its return value would then be "error"/"ok".
// input flags:
#define ACCEPT_UNDEFINED (1u << 0) // if not given, undefined throws serious error
#define ACCEPT_INT (1u << 1) needed when strings come along!
#define ACCEPT_INT (1u << 1)
#define ACCEPT_FLOAT (1u << 2) // if not given, floats are converted to integer
#define ACCEPT_OPENPARENTHESIS (1u << 3) // if not given, throws syntax error
//#define ACCEPT_STRING
#define ACCEPT_STRING (1u << 4) // if not given, convert 1-char strings to int?
#define ACCEPT_LIST (1u << 5)
// do I need ACCEPT_NONADDR and/or ACCEPT_ADDRESS?
*/

View File

@ -71,8 +71,8 @@ static struct cpu_type cpu_type_m65 = {
// variables
// predefined stuff
static struct ronode *cputype_tree = NULL;
static struct ronode cputype_list[] = {
static struct ronode cputype_tree[] = {
PREDEF_START,
#define KNOWN_TYPES "'6502', 'nmos6502', '6510', '65c02', 'r65c02', 'w65c02', '65816', '65ce02', '4502', 'm65', 'c64dtv2'" // shown in CLI error message for unknown types
// PREDEFNODE("z80", &cpu_type_Z80),
PREDEFNODE("6502", &cpu_type_6502),
@ -85,7 +85,7 @@ static struct ronode cputype_list[] = {
PREDEFNODE("65ce02", &cpu_type_65ce02),
PREDEFNODE("4502", &cpu_type_4502),
PREDEFNODE("m65", &cpu_type_m65),
PREDEFLAST("c64dtv2", &cpu_type_c64dtv2),
PREDEF_END("c64dtv2", &cpu_type_c64dtv2),
// ^^^^ this marks the last element
};
const char cputype_names[] = KNOWN_TYPES; // string to show if cputype_find() returns NULL
@ -95,9 +95,6 @@ const struct cpu_type *cputype_find(void)
{
void *node_body;
// make sure tree is initialised
if (cputype_tree == NULL)
Tree_add_table(&cputype_tree, cputype_list);
// perform lookup
if (!Tree_easy_scan(cputype_tree, &node_body, GlobalDynaBuf))
return NULL;

View File

@ -21,10 +21,12 @@
// initial size for global dynabuf
// (as it holds macros, loop bodies, etc., make it large to begin with)
#define GLOBALDYNABUF_INITIALSIZE 1024 // should be >0 (see above)
// TODO - get rid of this, or move to global.c
// Variables
struct dynabuf *GlobalDynaBuf; // global dynamic buffer
STRUCT_DYNABUF_REF(GlobalDynaBuf, GLOBALDYNABUF_INITIALSIZE); // global dynamic buffer
// TODO - get rid of this, or move to global.c
// Functions
@ -34,37 +36,42 @@ static void resize(struct dynabuf *db, size_t new_size)
{
char *new_buf;
//printf("Growing dynabuf to size %d.\n", new_size);
new_buf = realloc(db->buffer, new_size);
if (new_buf == NULL)
Throw_serious_error(exception_no_memory_left);
db->reserved = new_size;
db->buffer = new_buf;
}
// get buffer mem and fill in struct
static void initstruct(struct dynabuf *db, size_t initial_size)
{
//printf("dynabuf-init: %d.\n", initial_size);
if (initial_size < DYNABUF_MINIMUM_INITIALSIZE)
initial_size = DYNABUF_MINIMUM_INITIALSIZE;
db->size = 0;
db->reserved = initial_size;
db->buffer = malloc(initial_size);
if (db->buffer == NULL) {
// scream and die because there is not enough memory
fputs("Error: No memory for dynamic buffer.\n", stderr);
exit(EXIT_FAILURE);
}
}
// Exported functions
// Create and init a dynamic buffer and return pointer
struct dynabuf *DynaBuf_create(int initial_size)
// (ensure buffer is ready to use, then) clear dynamic buffer
void dynabuf_clear(struct dynabuf *db)
{
struct dynabuf *db;
if (initial_size < DYNABUF_MINIMUM_INITIALSIZE)
initial_size = DYNABUF_MINIMUM_INITIALSIZE;
if ((db = malloc(sizeof(*db)))) {
db->size = 0;
db->reserved = initial_size;
db->buffer = malloc(initial_size);
if (db->buffer)
return db; // if both pointers are != NULL, no error
}
// otherwise, complain
fputs("Error: No memory for dynamic buffer.\n", stderr);
exit(EXIT_FAILURE);
if (db->buffer == NULL)
initstruct(db, db->reserved); // get initial buffer
db->size = 0; // clear buffer
}
// Enlarge buffer
void DynaBuf_enlarge(struct dynabuf *db)
void dynabuf_enlarge(struct dynabuf *db)
{
resize(db, MAKE_LARGER_THAN(db->reserved));
}
@ -112,6 +119,11 @@ void DynaBuf_to_lower(struct dynabuf *target, struct dynabuf *source)
*write,
byte;
// if target has not been initialised yet, do it now
// (do not clear it unconditionally, because it may equal source!)
if (target->buffer == NULL)
initstruct(target, target->reserved); // get initial buffer
// make sure target can take it
if (source->size > target->reserved)
resize(target, source->size);
@ -131,9 +143,3 @@ void DynaBuf_to_lower(struct dynabuf *target, struct dynabuf *source)
// FIXME - use BYTE_ macro from global.h
*write = '\0'; // terminate
}
// Initialisation - allocate global dynamic buffer
void DynaBuf_init(void)
{
GlobalDynaBuf = DynaBuf_create(GLOBALDYNABUF_INITIALSIZE);
}

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code.
// Copyright (C) 1998-2016 Marco Baye
// Copyright (C) 1998-2020 Marco Baye
// Have a look at "acme.c" for further info
//
// Dynamic buffer stuff
@ -8,14 +8,14 @@
#include "config.h"
#include <stdlib.h> // for size_t
// macros
#define DYNABUF_CLEAR(db) do {db->size = 0;} while (0)
#define DYNABUF_APPEND(db, byte) \
do { \
if (db->size == db->reserved) \
DynaBuf_enlarge(db); \
dynabuf_enlarge(db); \
db->buffer[(db->size)++] = byte;\
} while (0)
// the next one is dangerous - the buffer location can change when a character
@ -27,21 +27,28 @@ do { \
// dynamic buffer structure
struct dynabuf {
char *buffer; // pointer to buffer
int size; // size of buffer's used portion
int reserved; // total size of buffer
size_t size; // size of buffer's used portion
size_t reserved; // total size of buffer
};
// new way of declaration/definition:
// the small struct above is static, only the buffer itself gets malloc'd (on
// first "clear").
#define STRUCT_DYNABUF_REF(name, size) struct dynabuf name[1] = {{NULL, 0, size}}
// the "[1]" makes sure the name refers to the address and not the struct
// itself, so existing code where the name referred to a pointer does not need
// to be changed.
// variables
extern struct dynabuf *GlobalDynaBuf; // global dynamic buffer
extern struct dynabuf GlobalDynaBuf[1]; // global dynamic buffer
// TODO - get rid of this, or move to global.c
// create global DynaBuf (call once on program startup)
extern void DynaBuf_init(void);
// create (private) DynaBuf
extern struct dynabuf *DynaBuf_create(int initial_size);
// (ensure buffer is ready to use, then) clear dynamic buffer
#define DYNABUF_CLEAR(db) dynabuf_clear(db) // TODO - remove old macro
extern void dynabuf_clear(struct dynabuf *db);
// call whenever buffer is too small
extern void DynaBuf_enlarge(struct dynabuf *db);
extern void dynabuf_enlarge(struct dynabuf *db);
// return malloc'd copy of buffer contents
extern char *DynaBuf_get_copy(struct dynabuf *db);
// copy string to buffer (without terminator)

View File

@ -83,12 +83,12 @@ const struct encoder encoder_file = {
// keywords for "!convtab" pseudo opcode
static struct ronode *encoder_tree = NULL; // tree to hold encoders
static struct ronode encoder_list[] = {
static struct ronode encoder_tree[] = {
PREDEF_START,
//no! PREDEFNODE("file", &encoder_file), "!ct file" is not needed; just use {} after initial loading of table!
PREDEFNODE(s_pet, &encoder_pet),
PREDEFNODE(s_raw, &encoder_raw),
PREDEFLAST(s_scr, &encoder_scr),
PREDEFNODE("pet", &encoder_pet),
PREDEFNODE("raw", &encoder_raw),
PREDEF_END("scr", &encoder_scr),
// ^^^^ this marks the last element
};
@ -120,9 +120,6 @@ const struct encoder *encoding_find(void)
{
void *node_body;
// make sure tree is initialised
if (encoder_tree == NULL)
Tree_add_table(&encoder_tree, encoder_list);
// perform lookup
if (!Tree_easy_scan(encoder_tree, &node_body, GlobalDynaBuf)) {
Throw_error("Unknown encoding.");

View File

@ -46,7 +46,7 @@ boolean check_ifdef_condition(void)
symbol = (struct symbol *) node->body;
symbol->has_been_read = TRUE; // we did not really read the symbol's value, but checking for its existence still counts as "used it"
if (symbol->object.type == NULL)
Bug_found("ObjectHasNullType", 0); // FIXME - add to docs!
Bug_found("ObjectHasNullType", 0);
return symbol->object.type->is_defined(&symbol->object);
}
@ -246,7 +246,6 @@ void flow_do_while(struct do_while *loop)
// parse a whole source code file
void flow_parse_and_close_file(FILE *fd, const char *filename)
{
//TODO - check for bogus/malformed BOM and ignore!
// be verbose
if (config.process_verbosity > 2)
printf("Parsing source file '%s'\n", filename);

View File

@ -29,31 +29,21 @@
// constants
const char s_and[] = "and";
const char s_asl[] = "asl";
const char s_asr[] = "asr";
const char s_bra[] = "bra";
const char s_brl[] = "brl";
const char s_eor[] = "eor";
const char s_error[] = "error";
const char s_lsr[] = "lsr";
const char s_scrxor[] = "scrxor";
char s_untitled[] = "<untitled>"; // FIXME - this is actually const
const char s_pet[] = "pet";
const char s_raw[] = "raw";
const char s_scr[] = "scr";
// Exception messages during assembly
const char exception_cannot_open_input_file[] = "Cannot open input file.";
const char exception_missing_string[] = "No string given.";
const char exception_negative_size[] = "Negative size argument.";
const char exception_no_left_brace[] = "Missing '{'.";
const char exception_no_memory_left[] = "Out of memory.";
const char exception_no_right_brace[] = "Found end-of-file instead of '}'.";
//const char exception_not_yet[] = "Sorry, feature not yet implemented.";
// TODO - show actual value in error message
const char exception_number_out_of_range[] = "Number out of range.";
const char exception_number_out_of_8b_range[] = "Number does not fit in 8 bits.";
static const char exception_number_out_of_16b_range[] = "Number does not fit in 16 bits.";
static const char exception_number_out_of_24b_range[] = "Number does not fit in 24 bits.";
const char exception_pc_undefined[] = "Program counter undefined.";
const char exception_symbol_defined[] = "Symbol already defined.";
const char exception_syntax[] = "Syntax error.";
@ -491,7 +481,7 @@ void output_object(struct object *object, struct iter_context *iter)
else if (object->u.number.ntype == NUMTYPE_FLOAT)
iter->fn(object->u.number.val.fpval);
else
Bug_found("IllegalNumberType7", object->u.number.ntype); // FIXME - add to docs!
Bug_found("IllegalNumberType0", object->u.number.ntype);
} else if (object->type == &type_list) {
// iterate over list
item = object->u.listhead->next;
@ -512,7 +502,7 @@ void output_object(struct object *object, struct iter_context *iter)
Throw_error("There's more than one character."); // see alu.c for the original of this error
}
} else {
Bug_found("IllegalObjectType9", 0); // FIXME - add to docs!
Bug_found("IllegalObjectType", 0);
}
}
@ -520,86 +510,79 @@ void output_object(struct object *object, struct iter_context *iter)
// output 8-bit value with range check
void output_8(intval_t value)
{
if ((value <= 0xff) && (value >= -0x80))
Output_byte(value);
else
Throw_error(exception_number_out_of_range);
if ((value < -0x80) || (value > 0xff))
Throw_error(exception_number_out_of_8b_range);
Output_byte(value);
}
// output 16-bit value with range check big-endian
void output_be16(intval_t value)
{
if ((value <= 0xffff) && (value >= -0x8000)) {
Output_byte(value >> 8);
Output_byte(value);
} else {
Throw_error(exception_number_out_of_range);
}
if ((value < -0x8000) || (value > 0xffff))
Throw_error(exception_number_out_of_16b_range);
Output_byte(value >> 8);
Output_byte(value);
}
// output 16-bit value with range check little-endian
void output_le16(intval_t value)
{
if ((value <= 0xffff) && (value >= -0x8000)) {
Output_byte(value);
Output_byte(value >> 8);
} else {
Throw_error(exception_number_out_of_range);
}
if ((value < -0x8000) || (value > 0xffff))
Throw_error(exception_number_out_of_16b_range);
Output_byte(value);
Output_byte(value >> 8);
}
// output 24-bit value with range check big-endian
void output_be24(intval_t value)
{
if ((value <= 0xffffff) && (value >= -0x800000)) {
Output_byte(value >> 16);
Output_byte(value >> 8);
Output_byte(value);
} else {
Throw_error(exception_number_out_of_range);
}
if ((value < -0x800000) || (value > 0xffffff))
Throw_error(exception_number_out_of_24b_range);
Output_byte(value >> 16);
Output_byte(value >> 8);
Output_byte(value);
}
// output 24-bit value with range check little-endian
void output_le24(intval_t value)
{
if ((value <= 0xffffff) && (value >= -0x800000)) {
Output_byte(value);
Output_byte(value >> 8);
Output_byte(value >> 16);
} else {
Throw_error(exception_number_out_of_range);
}
if ((value < -0x800000) || (value > 0xffffff))
Throw_error(exception_number_out_of_24b_range);
Output_byte(value);
Output_byte(value >> 8);
Output_byte(value >> 16);
}
// FIXME - the range checks below are commented out because 32-bit
// signed integers cannot exceed the range of 32-bit signed integers.
// But now that 64-bit machines are the norm, "intval_t" might be a
// 64-bit int. I need to address this problem one way or another.
// output 32-bit value (without range check) big-endian
void output_be32(intval_t value)
{
// if ((Value <= 0x7fffffff) && (Value >= -0x80000000)) {
// if ((value < -0x80000000) || (value > 0xffffffff))
// Throw_error(exception_number_out_of_32b_range);
Output_byte(value >> 24);
Output_byte(value >> 16);
Output_byte(value >> 8);
Output_byte(value);
// } else {
// Throw_error(exception_number_out_of_range);
// }
}
// output 32-bit value (without range check) little-endian
void output_le32(intval_t value)
{
// if ((Value <= 0x7fffffff) && (Value >= -0x80000000)) {
// if ((value < -0x80000000) || (value > 0xffffffff))
// Throw_error(exception_number_out_of_32b_range);
Output_byte(value);
Output_byte(value >> 8);
Output_byte(value >> 16);
Output_byte(value >> 24);
// } else {
// Throw_error(exception_number_out_of_range);
// }
}

View File

@ -21,21 +21,8 @@
#define SF_FOUND_BLANK (1u << 0) // statement had space or tab
#define SF_IMPLIED_LABEL (1u << 1) // statement had implied label def
extern const char s_and[];
extern const char s_asl[];
extern const char s_asr[];
extern const char s_bra[];
extern const char s_brl[];
extern const char s_eor[];
extern const char s_error[];
extern const char s_lsr[];
extern const char s_scrxor[];
extern char s_untitled[];
extern const char s_pet[];
extern const char s_raw[];
extern const char s_scr[];
// error messages during assembly
extern const char exception_cannot_open_input_file[];
extern const char exception_missing_string[];
extern const char exception_negative_size[];
extern const char exception_no_left_brace[];
@ -43,6 +30,7 @@ extern const char exception_no_memory_left[];
extern const char exception_no_right_brace[];
//extern const char exception_not_yet[];
extern const char exception_number_out_of_range[];
extern const char exception_number_out_of_8b_range[];
extern const char exception_pc_undefined[];
extern const char exception_symbol_defined[];
extern const char exception_syntax[];

View File

@ -48,7 +48,7 @@ void Input_new_file(const char *filename, FILE *fd)
Input_now->original_filename = filename;
Input_now->line_number = 1;
Input_now->source = INPUTSRC_FILE;
Input_now->state = INPUTSTATE_NORMAL;
Input_now->state = INPUTSTATE_SOF;
Input_now->src.fd = fd;
}
@ -76,14 +76,15 @@ static void report_srcchar(char new_char)
// show line number...
fprintf(report->fd, "%6d ", Input_now->line_number - 1);
// prepare outbytes' start address
if (report->bin_used)
if (report->bin_used) {
#if _BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L
snprintf(hex_address, HEXBUFSIZE, "%04x", report->bin_address);
#else
sprintf(hex_address, "%04x", report->bin_address);
#endif
else
} else {
hex_address[0] = '\0';
}
// prepare outbytes
hexdump[0] = '\0';
for (ii = 0; ii < report->bin_used; ++ii)
@ -117,6 +118,19 @@ static char get_processed_from_file(void)
for (;;) {
switch (Input_now->state) {
case INPUTSTATE_SOF:
// fetch first byte from the current source file
from_file = getc(Input_now->src.fd);
IF_WANTED_REPORT_SRCCHAR(from_file);
//TODO - check for bogus/malformed BOM and ignore?
// check for hashbang line and ignore
if (from_file == '#') {
// remember to skip remainder of line
Input_now->state = INPUTSTATE_COMMENT;
return CHAR_EOS; // end of statement
}
Input_now->state = INPUTSTATE_AGAIN;
break;
case INPUTSTATE_NORMAL:
// fetch a fresh byte from the current source file
from_file = getc(Input_now->src.fd);
@ -265,7 +279,7 @@ char GetByte(void)
GotByte = get_processed_from_file();
break;
default:
Bug_found("InvalidInputSrc", Input_now->source); // FIXME - add to docs
Bug_found("IllegalInputSrc", Input_now->source);
}
// // if start-of-line was read, increment line counter and repeat
// if (GotByte != CHAR_SOL)
@ -316,7 +330,7 @@ static char GetQuotedByte(void)
}
break;
default:
Bug_found("InvalidInputSrc", Input_now->source); // FIXME - add to docs!
Bug_found("IllegalInputSrc", Input_now->source);
}
// now check for end of statement
if (GotByte == CHAR_EOS)
@ -416,7 +430,7 @@ int Input_unescape_dynabuf(int read_index)
break;
// TODO - 'a' to BEL? others?
default:
Throw_error("Unsupported backslash sequence."); // TODO - add to docs (and add unexpected character to error message?)
Throw_error("Unsupported backslash sequence."); // TODO - add unexpected character to error message?
}
GLOBALDYNABUF_CURRENT[write_index++] = byte;
escaped = FALSE;
@ -429,7 +443,7 @@ int Input_unescape_dynabuf(int read_index)
}
}
if (escaped)
Bug_found("PartialEscapeSequence", 0); // FIXME - add to docs!
Bug_found("PartialEscapeSequence", 0);
GlobalDynaBuf->size = write_index;
return 0; // ok
}
@ -672,18 +686,9 @@ struct ipi {
*prev;
const char *path;
};
static struct ipi ipi_head; // head element
static struct dynabuf *pathbuf; // buffer to combine search path and file spec
static struct ipi ipi_head = {&ipi_head, &ipi_head, NULL}; // head element
static STRUCT_DYNABUF_REF(pathbuf, 256); // to combine search path and file spec
// init list
void includepaths_init(void)
{
// init ring list
ipi_head.next = &ipi_head;
ipi_head.prev = &ipi_head;
// init dynabuf
pathbuf = DynaBuf_create(256);
}
// add entry
void includepaths_add(const char *path)
{
@ -732,8 +737,15 @@ FILE *includepaths_open_ro(boolean uses_lib)
}
}
}
if (stream == NULL)
Throw_error(exception_cannot_open_input_file);
if (stream == NULL) {
// CAUTION, I'm re-using the path dynabuf to assemble the error message:
DYNABUF_CLEAR(pathbuf);
DynaBuf_add_string(pathbuf, "Cannot open input file \"");
DynaBuf_add_string(pathbuf, GLOBALDYNABUF_CURRENT);
DynaBuf_add_string(pathbuf, "\".");
DynaBuf_append(pathbuf, '\0');
Throw_error(pathbuf->buffer);
}
//fprintf(stderr, "File is [%s]\n", GLOBALDYNABUF_CURRENT);
return stream;
}

View File

@ -15,6 +15,7 @@
// values for input component "src.state"
enum inputstate {
INPUTSTATE_SOF, // start of file (check for hashbang)
INPUTSTATE_NORMAL, // everything's fine
INPUTSTATE_AGAIN, // re-process last byte
INPUTSTATE_SKIPBLANKS, // shrink multiple spaces
@ -125,8 +126,6 @@ extern bits Input_get_force_bit(void);
// include path stuff - should be moved to its own file:
// init list
extern void includepaths_init(void);
// add entry
extern void includepaths_add(const char *path);
// open file for reading (trying list entries as prefixes)

View File

@ -18,7 +18,7 @@
// Constants
#define MACRONAME_DYNABUF_INITIALSIZE 128
#define NAME_INITIALSIZE 128
#define ARG_SEPARATOR ' ' // separates macro title from arg types
#define ARGTYPE_VALUE 'v'
#define ARGTYPE_REF 'r'
@ -46,8 +46,8 @@ union macro_arg_t {
// Variables
static struct dynabuf *user_macro_name; // original macro title
static struct dynabuf *internal_name; // plus param type chars
static STRUCT_DYNABUF_REF(user_macro_name, NAME_INITIALSIZE); // original macro title
static STRUCT_DYNABUF_REF(internal_name, NAME_INITIALSIZE); // plus param type chars
static struct rwnode *macro_forest[256]; // trees (because of 8b hash)
// Dynamic argument table
static union macro_arg_t *arg_table = NULL;
@ -60,19 +60,12 @@ static int argtable_size = HALF_INITIAL_ARG_TABLE_SIZE;
static void enlarge_arg_table(void)
{
argtable_size *= 2;
//printf("Doubling arg table size to %d.\n", argtable_size);
arg_table = realloc(arg_table, argtable_size * sizeof(*arg_table));
if (arg_table == NULL)
Throw_serious_error(exception_no_memory_left);
}
// create dynamic buffers and arg table
void Macro_init(void)
{
user_macro_name = DynaBuf_create(MACRONAME_DYNABUF_INITIALSIZE);
internal_name = DynaBuf_create(MACRONAME_DYNABUF_INITIALSIZE);
enlarge_arg_table();
}
// Read macro scope and title. Title is read to GlobalDynaBuf and then copied
// over to internal_name DynaBuf, where ARG_SEPARATOR is added.
// In user_macro_name DynaBuf, the original name is reconstructed (even with
@ -241,6 +234,10 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name
int arg_count = 0;
int outer_err_count;
// make sure arg_table is ready (if not yet initialised, do it now)
if (arg_table == NULL)
enlarge_arg_table();
// Enter deeper nesting level
// Quit program if recursion too deep.
if (--macro_recursions_left < 0)

View File

@ -12,8 +12,6 @@
// Prototypes
// create dynamic buffers and arg table
extern void Macro_init(void); // create private dynabuf
// only call once (during first pass)
extern void Macro_parse_definition(void);
// Parse macro call ("+MACROTITLE"). Has to be re-entrant.

View File

@ -15,9 +15,8 @@
#include "typesystem.h"
// Constants
#define s_ror (s_error + 2) // Yes, I know I'm sick
#define MNEMO_DYNABUF_INITIALSIZE 8 // 4 + terminator should suffice
// constants
#define MNEMO_INITIALSIZE 8 // 4 + terminator should suffice
// These values are needed to recognize addressing modes:
// indexing:
@ -55,7 +54,7 @@
// only for m65:
#define LONG_INDIRECT_Z_INDEXED_ADDRESSING (AMB_LONGINDIRECT | AMB_INDEX(INDEX_Z))
// Constant values, used to mark the possible parameter lengths of commands.
// constant values, used to mark the possible parameter lengths of instructions.
// Not all of the eight possible combinations are actually used, however (because of the supported CPUs).
#define MAYBE______ 0
#define MAYBE_1____ NUMBER_FORCES_8
@ -72,10 +71,15 @@ enum mnemogroup {
GROUP_BITBRANCH, // bbr0..7 and bbs0..7 Byte value = opcode
GROUP_REL16_2, // 16bit relative to pc+2 Byte value = opcode
GROUP_REL16_3, // 16bit relative to pc+3 Byte value = opcode
GROUP_BOTHMOVES, // the "move" commands MVP and MVN Byte value = opcode
GROUP_ZPONLY, // rmb0..7 and smb0..7 Byte value = opcode FIXME - use for IDXeDEW,IDXeINW as well!
GROUP_BOTHMOVES, // the "move" instructions MVP and MVN Byte value = opcode
GROUP_ZPONLY, // rmb0..7, smb0..7, inw, dew Byte value = opcode
GROUP_PREFIX, // NOP on m65 (throws error) Byte value = opcode
};
// TODO: make sure groups like IMPLIEDONLY and ZPONLY output
// "Mnemonic does not support this addressing mode" instead of
// "Garbage data at end of statement".
// TODO: maybe add GROUP_IMMEDIATEONLY?
// (for RTN, REP, SEP, ANC, ALR, ARR, SBX, LXA, ANE, SAC, SIR)
// save some space
#define SCB static const unsigned char
@ -92,13 +96,13 @@ enum mnemogroup {
enum { IDX_ORA,IDXcORA,IDX16ORA,IDXeORA,IDXmORA,IDXmORQ,IDX_AND,IDXcAND,IDX16AND,IDXeAND,IDXmAND,IDXmANDQ,IDX_EOR,IDXcEOR,IDX16EOR,IDXeEOR,IDXmEOR,IDXmEORQ,IDX_ADC,IDXcADC,IDX16ADC,IDXeADC,IDXmADC,IDXmADCQ,IDX_STA,IDXcSTA,IDX16STA,IDXeSTA,IDXmSTA,IDXmSTQ,IDX_LDA,IDXcLDA,IDX16LDA,IDXeLDA,IDXmLDA,IDXmLDQ,IDX_CMP,IDXcCMP,IDX16CMP,IDXeCMP,IDXmCMP,IDXmCPQ,IDX_SBC,IDXcSBC,IDX16SBC,IDXeSBC,IDXmSBC,IDXmSBCQ,IDX16PEI,IDXuSLO,IDXuRLA,IDXuSRE,IDXuRRA,IDXuSAX,IDXuLAX,IDXuDCP,IDXuISC,IDXuSHA};
SCB accu_imm[] = { 0x09, 0x09, 0x09, 0x09, 0x09, 0, 0x29, 0x29, 0x29, 0x29, 0x29, 0, 0x49, 0x49, 0x49, 0x49, 0x49, 0, 0x69, 0x69, 0x69, 0x69, 0x69, 0, 0, 0, 0, 0, 0, 0, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // #$ff #$ffff
SCL accu_abs[] = { 0x0d05, 0x0d05,0x0f0d05, 0x0d05, 0x0d05, 0x0d05, 0x2d25, 0x2d25,0x2f2d25, 0x2d25, 0x2d25, 0x2d25, 0x4d45, 0x4d45,0x4f4d45, 0x4d45, 0x4d45, 0x4d45, 0x6d65, 0x6d65,0x6f6d65, 0x6d65, 0x6d65, 0x6d65, 0x8d85, 0x8d85,0x8f8d85, 0x8d85, 0x8d85, 0x8d85, 0xada5, 0xada5,0xafada5, 0xada5, 0xada5, 0xada5, 0xcdc5, 0xcdc5,0xcfcdc5, 0xcdc5, 0xcdc5, 0xcdc5, 0xede5, 0xede5,0xefede5, 0xede5, 0xede5, 0xede5, 0, 0x0f07, 0x2f27, 0x4f47, 0x6f67, 0x8f87, 0xafa7, 0xcfc7, 0xefe7, 0}; // $ff $ffff $ffffff
SCL accu_xabs[] = { 0x1d15, 0x1d15,0x1f1d15, 0x1d15, 0x1d15, 0x1d15, 0x3d35, 0x3d35,0x3f3d35, 0x3d35, 0x3d35, 0x3d35, 0x5d55, 0x5d55,0x5f5d55, 0x5d55, 0x5d55, 0x5d55, 0x7d75, 0x7d75,0x7f7d75, 0x7d75, 0x7d75, 0x7d75, 0x9d95, 0x9d95,0x9f9d95, 0x9d95, 0x9d95, 0x9d95, 0xbdb5, 0xbdb5,0xbfbdb5, 0xbdb5, 0xbdb5, 0xbdb5, 0xddd5, 0xddd5,0xdfddd5, 0xddd5, 0xddd5, 0xddd5, 0xfdf5, 0xfdf5,0xfffdf5, 0xfdf5, 0xfdf5, 0xfdf5, 0, 0x1f17, 0x3f37, 0x5f57, 0x7f77, 0, 0, 0xdfd7, 0xfff7, 0}; // $ff,x $ffff,x $ffffff,x
SCS accu_yabs[] = { 0x1900, 0x1900, 0x1900, 0x1900, 0x1900, 0x1900, 0x3900, 0x3900, 0x3900, 0x3900, 0x3900, 0x3900, 0x5900, 0x5900, 0x5900, 0x5900, 0x5900, 0x5900, 0x7900, 0x7900, 0x7900, 0x7900, 0x7900, 0x7900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0xb900, 0xb900, 0xb900, 0xb900, 0xb900, 0xb900, 0xd900, 0xd900, 0xd900, 0xd900, 0xd900, 0xd900, 0xf900, 0xf900, 0xf900, 0xf900, 0xf900, 0xf900, 0, 0x1b00, 0x3b00, 0x5b00, 0x7b00, 0x97, 0xbfb7, 0xdb00, 0xfb00, 0x9f00}; // $ff,y $ffff,y
SCB accu_xind8[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0, 0x03, 0x23, 0x43, 0x63, 0x83, 0xa3, 0xc3, 0xe3, 0}; // ($ff,x)
SCB accu_indy8[] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0, 0x13, 0x33, 0x53, 0x73, 0, 0xb3, 0xd3, 0xf3, 0x93}; // ($ff),y
SCL accu_xabs[] = { 0x1d15, 0x1d15,0x1f1d15, 0x1d15, 0x1d15, 0, 0x3d35, 0x3d35,0x3f3d35, 0x3d35, 0x3d35, 0, 0x5d55, 0x5d55,0x5f5d55, 0x5d55, 0x5d55, 0, 0x7d75, 0x7d75,0x7f7d75, 0x7d75, 0x7d75, 0, 0x9d95, 0x9d95,0x9f9d95, 0x9d95, 0x9d95, 0, 0xbdb5, 0xbdb5,0xbfbdb5, 0xbdb5, 0xbdb5, 0xbdb5, 0xddd5, 0xddd5,0xdfddd5, 0xddd5, 0xddd5, 0, 0xfdf5, 0xfdf5,0xfffdf5, 0xfdf5, 0xfdf5, 0, 0, 0x1f17, 0x3f37, 0x5f57, 0x7f77, 0, 0, 0xdfd7, 0xfff7, 0}; // $ff,x $ffff,x $ffffff,x
SCS accu_yabs[] = { 0x1900, 0x1900, 0x1900, 0x1900, 0x1900, 0, 0x3900, 0x3900, 0x3900, 0x3900, 0x3900, 0, 0x5900, 0x5900, 0x5900, 0x5900, 0x5900, 0, 0x7900, 0x7900, 0x7900, 0x7900, 0x7900, 0, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0, 0xb900, 0xb900, 0xb900, 0xb900, 0xb900, 0xb900, 0xd900, 0xd900, 0xd900, 0xd900, 0xd900, 0, 0xf900, 0xf900, 0xf900, 0xf900, 0xf900, 0, 0, 0x1b00, 0x3b00, 0x5b00, 0x7b00, 0x97, 0xbfb7, 0xdb00, 0xfb00, 0x9f00}; // $ff,y $ffff,y
SCB accu_xind8[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0, 0x21, 0x21, 0x21, 0x21, 0x21, 0, 0x41, 0x41, 0x41, 0x41, 0x41, 0, 0x61, 0x61, 0x61, 0x61, 0x61, 0, 0x81, 0x81, 0x81, 0x81, 0x81, 0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0, 0, 0x03, 0x23, 0x43, 0x63, 0x83, 0xa3, 0xc3, 0xe3, 0}; // ($ff,x)
SCB accu_indy8[] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0, 0x31, 0x31, 0x31, 0x31, 0x31, 0, 0x51, 0x51, 0x51, 0x51, 0x51, 0, 0x71, 0x71, 0x71, 0x71, 0x71, 0, 0x91, 0x91, 0x91, 0x91, 0x91, 0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0, 0, 0x13, 0x33, 0x53, 0x73, 0, 0xb3, 0xd3, 0xf3, 0x93}; // ($ff),y
SCB accu_ind8[] = { 0, 0x12, 0x12, 0, 0, 0x12, 0, 0x32, 0x32, 0, 0, 0x32, 0, 0x52, 0x52, 0, 0, 0x52, 0, 0x72, 0x72, 0, 0, 0x72, 0, 0x92, 0x92, 0, 0, 0x92, 0, 0xb2, 0xb2, 0, 0, 0xb2, 0, 0xd2, 0xd2, 0, 0, 0xd2, 0, 0xf2, 0xf2, 0, 0, 0xf2, 0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff)
SCB accu_sabs8[] = { 0, 0, 0x03, 0, 0, 0, 0, 0, 0x23, 0, 0, 0, 0, 0, 0x43, 0, 0, 0, 0, 0, 0x63, 0, 0, 0, 0, 0, 0x83, 0, 0, 0, 0, 0, 0xa3, 0, 0, 0, 0, 0, 0xc3, 0, 0, 0, 0, 0, 0xe3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff,s
SCB accu_sindy8[] = { 0, 0, 0x13, 0, 0, 0, 0, 0, 0x33, 0, 0, 0, 0, 0, 0x53, 0, 0, 0, 0, 0, 0x73, 0, 0, 0, 0, 0, 0x93, 0x82, 0x82, 0x82, 0, 0, 0xb3, 0xe2, 0xe2, 0xe2, 0, 0, 0xd3, 0, 0, 0, 0, 0, 0xf3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff,s),y
SCB accu_sindy8[] = { 0, 0, 0x13, 0, 0, 0, 0, 0, 0x33, 0, 0, 0, 0, 0, 0x53, 0, 0, 0, 0, 0, 0x73, 0, 0, 0, 0, 0, 0x93, 0x82, 0x82, 0, 0, 0, 0xb3, 0xe2, 0xe2, 0xe2, 0, 0, 0xd3, 0, 0, 0, 0, 0, 0xf3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff,s),y
SCB accu_lind8[] = { 0, 0, 0x07, 0, 0, 0x12, 0, 0, 0x27, 0, 0, 0x32, 0, 0, 0x47, 0, 0, 0x52, 0, 0, 0x67, 0, 0, 0x72, 0, 0, 0x87, 0, 0, 0x92, 0, 0, 0xa7, 0, 0, 0xb2, 0, 0, 0xc7, 0, 0, 0xd2, 0, 0, 0xe7, 0, 0, 0xf2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff]
SCB accu_lindy8[] = { 0, 0, 0x17, 0, 0, 0, 0, 0, 0x37, 0, 0, 0, 0, 0, 0x57, 0, 0, 0, 0, 0, 0x77, 0, 0, 0, 0, 0, 0x97, 0, 0, 0, 0, 0, 0xb7, 0, 0, 0, 0, 0, 0xd7, 0, 0, 0, 0, 0, 0xf7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff],y
SCB accu_indz8[] = { 0, 0, 0, 0x12, 0x12, 0, 0, 0, 0, 0x32, 0x32, 0, 0, 0, 0, 0x52, 0x52, 0, 0, 0, 0, 0x72, 0x72, 0, 0, 0, 0, 0x92, 0x92, 0, 0, 0, 0, 0xb2, 0xb2, 0, 0, 0, 0, 0xd2, 0xd2, 0, 0, 0, 0, 0xf2, 0xf2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff),z
@ -110,13 +114,13 @@ SCB accu_lindz8[] = { 0, 0, 0, 0, 0x12, 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 | 6502/65c02/65ce02 | 65c02 | 65ce02 | 65816 | NMOS 6502 undocumented opcodes | C64DTV2 |
enum { IDX_ASL,IDX_ROL,IDX_LSR,IDX_ROR,IDX_LDY,IDX_LDX,IDX_CPY,IDX_CPX,IDX_BIT,IDXcBIT,IDX_STX,IDXeSTX,IDX_STY,IDXeSTY,IDX_DEC,IDXcDEC,IDX_INC,IDXcINC,IDXcTSB,IDXcTRB,IDXcSTZ,IDXeASR,IDXeASW,IDXeCPZ,IDXeDEW,IDXeINW,IDXeLDZ,IDXePHW,IDXeROW,IDXeRTN,IDX16COP,IDX16REP,IDX16SEP,IDX16PEA,IDXuANC,IDXuASR,IDXuARR,IDXuSBX,IDXuNOP,IDXuDOP,IDXuTOP,IDXuJAM,IDXuLXA,IDXuANE,IDXuLAS,IDXuTAS,IDXuSHX,IDXuSHY,IDX_SAC,IDX_SIR};
SCB misc_impl[] = { 0x0a, 0x2a, 0x4a, 0x6a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3a, 0, 0x1a, 0, 0, 0, 0x43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xea, 0x80, 0x0c, 0x02, 0, 0, 0, 0, 0, 0, 0, 0}; // implied/accu
SCB misc_imm[] = { 0, 0, 0, 0, 0xa0, 0xa2, 0xc0, 0xe0, 0, 0x89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc2, 0, 0, 0xa3, 0xf4, 0, 0x62, 0, 0xc2, 0xe2, 0, 0x0b, 0x4b, 0x6b, 0xcb, 0x80, 0x80, 0, 0, 0xab, 0x8b, 0, 0, 0, 0, 0x32, 0x42}; // #$ff #$ffff
SCS misc_abs[] = { 0x0e06, 0x2e26, 0x4e46, 0x6e66, 0xaca4, 0xaea6, 0xccc4, 0xece4, 0x2c24, 0x2c24, 0x8e86, 0x8e86, 0x8c84, 0x8c84, 0xcec6, 0xcec6, 0xeee6, 0xeee6, 0x0c04, 0x1c14, 0x9c64, 0x44, 0xcb00, 0xdcd4, 0xc3, 0xe3, 0xab00, 0xfc00, 0xeb00, 0, 0x02, 0, 0, 0xf400, 0, 0, 0, 0, 0x0c04, 0x04, 0x0c00, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff $ffff
SCS misc_xabs[] = { 0x1e16, 0x3e36, 0x5e56, 0x7e76, 0xbcb4, 0, 0, 0, 0, 0x3c34, 0, 0, 0x94, 0x8b94, 0xded6, 0xded6, 0xfef6, 0xfef6, 0, 0, 0x9e74, 0x54, 0, 0, 0, 0, 0xbb00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1c14, 0x14, 0x1c00, 0, 0, 0, 0, 0, 0, 0x9c00, 0, 0}; // $ff,x $ffff,x
SCS misc_yabs[] = { 0, 0, 0, 0, 0, 0xbeb6, 0, 0, 0, 0, 0x96, 0x9b96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbb00, 0x9b00, 0x9e00, 0, 0, 0}; // $ff,y $ffff,y
// | 6502 | 6502/65c02/65ce02/m65 | 65c02 | 65ce02 | 65816 | NMOS 6502 undocumented opcodes | C64DTV2 |
enum { IDX_ASL,IDX_ROL,IDX_LSR,IDX_ROR,IDX_LDY,IDX_LDX,IDX_CPY,IDX_CPX,IDX_BIT,IDXcBIT,IDXmBITQ,IDX_STX,IDXeSTX,IDX_STY,IDXeSTY,IDX_DEC,IDXcDEC,IDX_INC,IDXcINC,IDXcTSB,IDXcTRB,IDXcSTZ,IDXeASR,IDXeASW,IDXeCPZ,IDXeLDZ,IDXePHW,IDXeROW,IDXeRTN,IDX16COP,IDX16REP,IDX16SEP,IDX16PEA,IDXuANC,IDXuALR,IDXuARR,IDXuSBX,IDXuNOP,IDXuDOP,IDXuTOP,IDXuLXA,IDXuANE,IDXuLAS,IDXuTAS,IDXuSHX,IDXuSHY,IDX_SAC,IDX_SIR};
SCB misc_impl[] = { 0x0a, 0x2a, 0x4a, 0x6a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3a, 0, 0x1a, 0, 0, 0, 0x43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xea, 0x80, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0}; // implied/accu
SCB misc_imm[] = { 0, 0, 0, 0, 0xa0, 0xa2, 0xc0, 0xe0, 0, 0x89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc2, 0xa3, 0xf4, 0, 0x62, /*2?*/0, 0xc2, 0xe2, 0, 0x0b, 0x4b, 0x6b, 0xcb, 0x80, 0x80, 0, 0xab, 0x8b, 0, 0, 0, 0, 0x32, 0x42}; // #$ff #$ffff
SCS misc_abs[] = { 0x0e06, 0x2e26, 0x4e46, 0x6e66, 0xaca4, 0xaea6, 0xccc4, 0xece4, 0x2c24, 0x2c24, 0x2c24, 0x8e86, 0x8e86, 0x8c84, 0x8c84, 0xcec6, 0xcec6, 0xeee6, 0xeee6, 0x0c04, 0x1c14, 0x9c64, 0x44, 0xcb00, 0xdcd4, 0xab00, 0xfc00, 0xeb00, 0, 0x02, 0, 0, 0xf400, 0, 0, 0, 0, 0x0c04, 0x04, 0x0c00, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff $ffff
SCS misc_xabs[] = { 0x1e16, 0x3e36, 0x5e56, 0x7e76, 0xbcb4, 0, 0, 0, 0, 0x3c34, 0, 0, 0, 0x94, 0x8b94, 0xded6, 0xded6, 0xfef6, 0xfef6, 0, 0, 0x9e74, 0x54, 0, 0, 0xbb00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1c14, 0x14, 0x1c00, 0, 0, 0, 0, 0, 0x9c00, 0, 0}; // $ff,x $ffff,x
SCS misc_yabs[] = { 0, 0, 0, 0, 0, 0xbeb6, 0, 0, 0, 0, 0, 0x96, 0x9b96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbb00, 0x9b00, 0x9e00, 0, 0, 0}; // $ff,y $ffff,y
// Code tables for group GROUP_ALLJUMPS:
// These tables are needed for finding out the correct code when the mnemonic
@ -137,28 +141,15 @@ SCS jump_lind[] = { 0, 0, 0xdc00, 0, 0, 0, 0xdc00,
#undef SCL
// error message strings
static const char exception_illegal_combination[] = "Illegal combination of command and addressing mode.";
static const char exception_illegal_combination[] = "CPU does not support this addressing mode for this mnemonic.";
static const char exception_oversized_addrmode[] = "Using oversized addressing mode.";
// Variables
static struct dynabuf *mnemo_dyna_buf; // dynamic buffer for mnemonics
// predefined stuff
static struct ronode *mnemo_6502_tree = NULL; // 6502 mnemonics
static struct ronode *mnemo_6502undoc1_tree = NULL; // 6502 undocumented ("illegal") opcodes supported by DTV2
static struct ronode *mnemo_6502undoc2_tree = NULL; // remaining 6502 undocumented ("illegal") opcodes (currently ANC only, maybe more will get moved)
static struct ronode *mnemo_c64dtv2_tree = NULL; // C64DTV2 extensions (BRA/SAC/SIR)
static struct ronode *mnemo_65c02_tree = NULL; // 65c02 extensions
static struct ronode *mnemo_bitmanips_tree = NULL; // Rockwell's bit manipulation extensions
static struct ronode *mnemo_stp_wai_tree = NULL; // WDC's "stp" and "wai" instructions
static struct ronode *mnemo_65816_tree = NULL; // WDC 65816 extensions
static struct ronode *mnemo_65ce02_tree = NULL; // CSG 65ce02/4502 extensions
static struct ronode *mnemo_aug_tree = NULL; // CSG 65ce02's "aug" instruction
static struct ronode *mnemo_map_eom_tree = NULL; // CSG 4502's "map" and "eom" instructions
static struct ronode *mnemo_m65_tree = NULL; // MEGA65 extensions
static STRUCT_DYNABUF_REF(mnemo_dyna_buf, MNEMO_INITIALSIZE); // for mnemonics
// Command's code, flags and group values are stored together in a single integer.
// mnemonic's code, flags and group values are stored together in a single integer.
// ("code" is either a table index or the opcode itself, depending on group value)
// To extract the code, use "& CODEMASK".
// To extract the flags, use "& FLAGSMASK".
@ -177,20 +168,22 @@ static struct ronode *mnemo_m65_tree = NULL; // MEGA65 extensions
#define MERGE(g, v) (((g) << 12) | (v))
#define GROUP(v) ((v) >> 12)
static struct ronode mnemos_6502[] = {
// 6502 mnemonics
static struct ronode mnemo_6502_tree[] = {
PREDEF_START,
PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX_ORA)),
PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDX_AND)),
PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDX_EOR)),
PREDEFNODE("and", MERGE(GROUP_ACCU, IDX_AND)),
PREDEFNODE("eor", MERGE(GROUP_ACCU, IDX_EOR)),
PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX_ADC)),
PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX_STA)),
PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX_LDA)),
PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDX_CMP)),
PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDX_SBC)),
PREDEFNODE("bit", MERGE(GROUP_MISC, IDX_BIT)),
PREDEFNODE(s_asl, MERGE(GROUP_MISC, IDX_ASL)),
PREDEFNODE("asl", MERGE(GROUP_MISC, IDX_ASL)),
PREDEFNODE("rol", MERGE(GROUP_MISC, IDX_ROL)),
PREDEFNODE(s_lsr, MERGE(GROUP_MISC, IDX_LSR)),
PREDEFNODE(s_ror, MERGE(GROUP_MISC, IDX_ROR)),
PREDEFNODE("lsr", MERGE(GROUP_MISC, IDX_LSR)),
PREDEFNODE("ror", MERGE(GROUP_MISC, IDX_ROR)),
PREDEFNODE("sty", MERGE(GROUP_MISC, IDX_STY)),
PREDEFNODE("stx", MERGE(GROUP_MISC, IDX_STX)),
PREDEFNODE("ldy", MERGE(GROUP_MISC, IDX_LDY)),
@ -209,81 +202,87 @@ static struct ronode mnemos_6502[] = {
PREDEFNODE("beq", MERGE(GROUP_RELATIVE8, 0xf0)),
PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDX_JMP)),
PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDX_JSR)),
PREDEFNODE("brk", MERGE(GROUP_IMPLIEDONLY, 0)),
PREDEFNODE("php", MERGE(GROUP_IMPLIEDONLY, 8)),
PREDEFNODE("clc", MERGE(GROUP_IMPLIEDONLY, 24)),
PREDEFNODE("plp", MERGE(GROUP_IMPLIEDONLY, 40)),
PREDEFNODE("sec", MERGE(GROUP_IMPLIEDONLY, 56)),
PREDEFNODE("rti", MERGE(GROUP_IMPLIEDONLY, 64)),
PREDEFNODE("pha", MERGE(GROUP_IMPLIEDONLY, 72)),
PREDEFNODE("cli", MERGE(GROUP_IMPLIEDONLY, 88)),
PREDEFNODE("rts", MERGE(GROUP_IMPLIEDONLY, 96)),
PREDEFNODE("pla", MERGE(GROUP_IMPLIEDONLY, 104)),
PREDEFNODE("sei", MERGE(GROUP_IMPLIEDONLY, 120)),
PREDEFNODE("dey", MERGE(GROUP_IMPLIEDONLY, 136)),
PREDEFNODE("txa", MERGE(GROUP_IMPLIEDONLY, 138)),
PREDEFNODE("tya", MERGE(GROUP_IMPLIEDONLY, 152)),
PREDEFNODE("txs", MERGE(GROUP_IMPLIEDONLY, 154)),
PREDEFNODE("tay", MERGE(GROUP_IMPLIEDONLY, 168)),
PREDEFNODE("tax", MERGE(GROUP_IMPLIEDONLY, 170)),
PREDEFNODE("clv", MERGE(GROUP_IMPLIEDONLY, 184)),
PREDEFNODE("tsx", MERGE(GROUP_IMPLIEDONLY, 186)),
PREDEFNODE("iny", MERGE(GROUP_IMPLIEDONLY, 200)),
PREDEFNODE("dex", MERGE(GROUP_IMPLIEDONLY, 202)),
PREDEFNODE("cld", MERGE(GROUP_IMPLIEDONLY, 216)),
PREDEFNODE("inx", MERGE(GROUP_IMPLIEDONLY, 232)),
PREDEFNODE("nop", MERGE(GROUP_IMPLIEDONLY, 234)),
PREDEFLAST("sed", MERGE(GROUP_IMPLIEDONLY, 248)),
PREDEFNODE("brk", MERGE(GROUP_IMPLIEDONLY, 0x00)),
PREDEFNODE("php", MERGE(GROUP_IMPLIEDONLY, 0x08)),
PREDEFNODE("clc", MERGE(GROUP_IMPLIEDONLY, 0x18)),
PREDEFNODE("plp", MERGE(GROUP_IMPLIEDONLY, 0x28)),
PREDEFNODE("sec", MERGE(GROUP_IMPLIEDONLY, 0x38)),
PREDEFNODE("rti", MERGE(GROUP_IMPLIEDONLY, 0x40)),
PREDEFNODE("pha", MERGE(GROUP_IMPLIEDONLY, 0x48)),
PREDEFNODE("cli", MERGE(GROUP_IMPLIEDONLY, 0x58)),
PREDEFNODE("rts", MERGE(GROUP_IMPLIEDONLY, 0x60)),
PREDEFNODE("pla", MERGE(GROUP_IMPLIEDONLY, 0x68)),
PREDEFNODE("sei", MERGE(GROUP_IMPLIEDONLY, 0x78)),
PREDEFNODE("dey", MERGE(GROUP_IMPLIEDONLY, 0x88)),
PREDEFNODE("txa", MERGE(GROUP_IMPLIEDONLY, 0x8a)),
PREDEFNODE("tya", MERGE(GROUP_IMPLIEDONLY, 0x98)),
PREDEFNODE("txs", MERGE(GROUP_IMPLIEDONLY, 0x9a)),
PREDEFNODE("tay", MERGE(GROUP_IMPLIEDONLY, 0xa8)),
PREDEFNODE("tax", MERGE(GROUP_IMPLIEDONLY, 0xaa)),
PREDEFNODE("clv", MERGE(GROUP_IMPLIEDONLY, 0xb8)),
PREDEFNODE("tsx", MERGE(GROUP_IMPLIEDONLY, 0xba)),
PREDEFNODE("iny", MERGE(GROUP_IMPLIEDONLY, 0xc8)),
PREDEFNODE("dex", MERGE(GROUP_IMPLIEDONLY, 0xca)),
PREDEFNODE("cld", MERGE(GROUP_IMPLIEDONLY, 0xd8)),
PREDEFNODE("inx", MERGE(GROUP_IMPLIEDONLY, 0xe8)),
PREDEFNODE("nop", MERGE(GROUP_IMPLIEDONLY, 0xea)),
PREDEF_END("sed", MERGE(GROUP_IMPLIEDONLY, 0xf8)),
// ^^^^ this marks the last element
};
// undocumented opcodes of the NMOS 6502 that are also supported by c64dtv2:
static struct ronode mnemos_6502undoc1[] = {
static struct ronode mnemo_6502undoc1_tree[] = {
PREDEF_START,
PREDEFNODE("slo", MERGE(GROUP_ACCU, IDXuSLO)), // ASL + ORA (aka ASO)
PREDEFNODE("rla", MERGE(GROUP_ACCU, IDXuRLA)), // ROL + AND
PREDEFNODE("rla", MERGE(GROUP_ACCU, IDXuRLA)), // ROL + AND (aka RLN)
PREDEFNODE("sre", MERGE(GROUP_ACCU, IDXuSRE)), // LSR + EOR (aka LSE)
PREDEFNODE("rra", MERGE(GROUP_ACCU, IDXuRRA)), // ROR + ADC
PREDEFNODE("sax", MERGE(GROUP_ACCU, IDXuSAX)), // STX + STA (aka AXS aka AAX)
PREDEFNODE("rra", MERGE(GROUP_ACCU, IDXuRRA)), // ROR + ADC (aka RRD)
PREDEFNODE("sax", MERGE(GROUP_ACCU, IDXuSAX)), // store A & X (aka AXS/AAX)
PREDEFNODE("lax", MERGE(GROUP_ACCU, IDXuLAX)), // LDX + LDA
PREDEFNODE("dcp", MERGE(GROUP_ACCU, IDXuDCP)), // DEC + CMP (aka DCM)
PREDEFNODE("isc", MERGE(GROUP_ACCU, IDXuISC)), // INC + SBC (aka ISB aka INS)
PREDEFNODE("las", MERGE(GROUP_MISC, IDXuLAS)), // A,X,S = {addr} & S (aka LAR aka LAE)
PREDEFNODE("tas", MERGE(GROUP_MISC, IDXuTAS)), // S = A & X {addr} = A&X& {H+1} (aka SHS aka XAS)
PREDEFNODE("sha", MERGE(GROUP_ACCU, IDXuSHA)), // {addr} = A & X & {H+1} (aka AXA aka AHX)
PREDEFNODE("shx", MERGE(GROUP_MISC, IDXuSHX)), // {addr} = X & {H+1} (aka XAS aka SXA)
PREDEFNODE("shy", MERGE(GROUP_MISC, IDXuSHY)), // {addr} = Y & {H+1} (aka SAY aka SYA)
PREDEFNODE(s_asr, MERGE(GROUP_MISC, IDXuASR)), // LSR + EOR (aka ALR)
PREDEFNODE("arr", MERGE(GROUP_MISC, IDXuARR)), // ROR + ADC
PREDEFNODE("sbx", MERGE(GROUP_MISC, IDXuSBX)), // DEX + CMP (aka AXS aka SAX)
PREDEFNODE("isc", MERGE(GROUP_ACCU, IDXuISC)), // INC + SBC (aka ISB/INS)
PREDEFNODE("las", MERGE(GROUP_MISC, IDXuLAS)), // A,X,S = {addr} & S (aka LAR/LAE)
PREDEFNODE("tas", MERGE(GROUP_MISC, IDXuTAS)), // S = A & X {addr} = A&X& {H+1} (aka SHS/XAS)
PREDEFNODE("sha", MERGE(GROUP_ACCU, IDXuSHA)), // {addr} = A & X & {H+1} (aka AXA/AHX)
PREDEFNODE("shx", MERGE(GROUP_MISC, IDXuSHX)), // {addr} = X & {H+1} (aka XAS/SXA)
PREDEFNODE("shy", MERGE(GROUP_MISC, IDXuSHY)), // {addr} = Y & {H+1} (aka SAY/SYA)
PREDEFNODE("alr", MERGE(GROUP_MISC, IDXuALR)), // A = A & arg, then LSR (aka ASR)
PREDEFNODE("asr", MERGE(GROUP_MISC, IDXuALR)), // A = A & arg, then LSR (aka ALR)
PREDEFNODE("arr", MERGE(GROUP_MISC, IDXuARR)), // A = A & arg, then ROR
PREDEFNODE("sbx", MERGE(GROUP_MISC, IDXuSBX)), // X = (A & X) - arg (aka AXS/SAX)
PREDEFNODE("nop", MERGE(GROUP_MISC, IDXuNOP)), // combines documented $ea and the undocumented dop/top below
PREDEFNODE("dop", MERGE(GROUP_MISC, IDXuDOP)), // "double nop" (skip next byte)
PREDEFNODE("top", MERGE(GROUP_MISC, IDXuTOP)), // "triple nop" (skip next word)
PREDEFNODE("jam", MERGE(GROUP_MISC, IDXuJAM)), // jam/crash/kill/halt-and-catch-fire
PREDEFNODE("ane", MERGE(GROUP_MISC, IDXuANE)), // A = (A | ??) & X & arg (aka XAA)
PREDEFLAST("lxa", MERGE(GROUP_MISC, IDXuLXA)), // A,X = (A | ??) & arg (aka OAL aka ATX)
PREDEFNODE("ane", MERGE(GROUP_MISC, IDXuANE)), // A = (A | ??) & X & arg (aka XAA/AXM)
PREDEFNODE("lxa", MERGE(GROUP_MISC, IDXuLXA)), // A,X = (A | ??) & arg (aka LAX/ATX/OAL)
PREDEF_END("jam", MERGE(GROUP_IMPLIEDONLY, 0x02)), // jam/crash/kill/halt-and-catch-fire
// ^^^^ this marks the last element
};
// undocumented opcodes of the NMOS 6502 that are _not_ supported by c64dtv2:
static struct ronode mnemos_6502undoc2[] = {
PREDEFLAST("anc", MERGE(GROUP_MISC, IDXuANC)), // ROL + AND, ASL + ORA (aka AAC)
// (currently ANC only, maybe more will get moved)
static struct ronode mnemo_6502undoc2_tree[] = {
PREDEF_START,
PREDEF_END("anc", MERGE(GROUP_MISC, IDXuANC)), // A = A & arg, then C=N (aka ANA, ANB)
// ^^^^ this marks the last element
};
// additional opcodes of c64dtv2:
static struct ronode mnemos_c64dtv2[] = {
PREDEFNODE(s_bra, MERGE(GROUP_RELATIVE8, 0x12)), // branch always
static struct ronode mnemo_c64dtv2_tree[] = {
PREDEF_START,
PREDEFNODE("bra", MERGE(GROUP_RELATIVE8, 0x12)), // branch always
PREDEFNODE("sac", MERGE(GROUP_MISC, IDX_SAC)), // set accumulator mapping
PREDEFLAST("sir", MERGE(GROUP_MISC, IDX_SIR)), // set index register mapping
PREDEF_END("sir", MERGE(GROUP_MISC, IDX_SIR)), // set index register mapping
// ^^^^ this marks the last element
};
// new stuff in CMOS re-design:
static struct ronode mnemos_65c02[] = {
static struct ronode mnemo_65c02_tree[] = {
PREDEF_START,
// more addressing modes for some mnemonics:
PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXcORA)),
PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDXcAND)),
PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDXcEOR)),
PREDEFNODE("and", MERGE(GROUP_ACCU, IDXcAND)),
PREDEFNODE("eor", MERGE(GROUP_ACCU, IDXcEOR)),
PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXcADC)),
PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXcSTA)),
PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXcLDA)),
@ -294,19 +293,20 @@ static struct ronode mnemos_65c02[] = {
PREDEFNODE("dec", MERGE(GROUP_MISC, IDXcDEC)),
PREDEFNODE("inc", MERGE(GROUP_MISC, IDXcINC)),
// and eight new mnemonics:
PREDEFNODE(s_bra, MERGE(GROUP_RELATIVE8, 0x80)),
PREDEFNODE("phy", MERGE(GROUP_IMPLIEDONLY, 90)),
PREDEFNODE("ply", MERGE(GROUP_IMPLIEDONLY, 122)),
PREDEFNODE("phx", MERGE(GROUP_IMPLIEDONLY, 218)),
PREDEFNODE("plx", MERGE(GROUP_IMPLIEDONLY, 250)),
PREDEFNODE("bra", MERGE(GROUP_RELATIVE8, 0x80)),
PREDEFNODE("phy", MERGE(GROUP_IMPLIEDONLY, 0x5a)),
PREDEFNODE("ply", MERGE(GROUP_IMPLIEDONLY, 0x7a)),
PREDEFNODE("phx", MERGE(GROUP_IMPLIEDONLY, 0xda)),
PREDEFNODE("plx", MERGE(GROUP_IMPLIEDONLY, 0xfa)),
PREDEFNODE("tsb", MERGE(GROUP_MISC, IDXcTSB)),
PREDEFNODE("trb", MERGE(GROUP_MISC, IDXcTRB)),
PREDEFLAST("stz", MERGE(GROUP_MISC, IDXcSTZ)),
PREDEF_END("stz", MERGE(GROUP_MISC, IDXcSTZ)),
// ^^^^ this marks the last element
};
// bit-manipulation extensions (by Rockwell?)
static struct ronode mnemos_bitmanips[] = {
static struct ronode mnemo_bitmanips_tree[] = {
PREDEF_START,
PREDEFNODE("rmb0", MERGE(GROUP_ZPONLY, 0x07)),
PREDEFNODE("rmb1", MERGE(GROUP_ZPONLY, 0x17)),
PREDEFNODE("rmb2", MERGE(GROUP_ZPONLY, 0x27)),
@ -338,19 +338,21 @@ static struct ronode mnemos_bitmanips[] = {
PREDEFNODE("bbs4", MERGE(GROUP_BITBRANCH, 0xcf)),
PREDEFNODE("bbs5", MERGE(GROUP_BITBRANCH, 0xdf)),
PREDEFNODE("bbs6", MERGE(GROUP_BITBRANCH, 0xef)),
PREDEFLAST("bbs7", MERGE(GROUP_BITBRANCH, 0xff)),
PREDEF_END("bbs7", MERGE(GROUP_BITBRANCH, 0xff)),
// ^^^^ this marks the last element
};
// "stp" and "wai" extensions by WDC:
static struct ronode mnemos_stp_wai[] = {
PREDEFNODE("stp", MERGE(GROUP_IMPLIEDONLY, 219)),
PREDEFLAST("wai", MERGE(GROUP_IMPLIEDONLY, 203)),
static struct ronode mnemo_stp_wai_tree[] = {
PREDEF_START,
PREDEFNODE("wai", MERGE(GROUP_IMPLIEDONLY, 0xcb)),
PREDEF_END("stp", MERGE(GROUP_IMPLIEDONLY, 0xdb)),
// ^^^^ this marks the last element
};
// most of the 65816 stuff
static struct ronode mnemos_65816[] = {
// the 65816 stuff
static struct ronode mnemo_65816_tree[] = {
PREDEF_START,
// CAUTION - these use 6502/65c02 indices, because the opcodes are the same - but I need flags for immediate mode!
PREDEFNODE("ldy", MERGE(GROUP_MISC, IDX_LDY | IM_INDEXREGS)),
PREDEFNODE("ldx", MERGE(GROUP_MISC, IDX_LDX | IM_INDEXREGS)),
@ -359,8 +361,8 @@ static struct ronode mnemos_65816[] = {
PREDEFNODE("bit", MERGE(GROUP_MISC, IDXcBIT | IM_ACCUMULATOR)),
// more addressing modes for some mnemonics:
PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX16ORA | IM_ACCUMULATOR)),
PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDX16AND | IM_ACCUMULATOR)),
PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDX16EOR | IM_ACCUMULATOR)),
PREDEFNODE("and", MERGE(GROUP_ACCU, IDX16AND | IM_ACCUMULATOR)),
PREDEFNODE("eor", MERGE(GROUP_ACCU, IDX16EOR | IM_ACCUMULATOR)),
PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX16ADC | IM_ACCUMULATOR)),
PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX16STA)),
PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX16LDA | IM_ACCUMULATOR)),
@ -374,36 +376,39 @@ static struct ronode mnemos_65816[] = {
PREDEFNODE("jsl", MERGE(GROUP_ALLJUMPS, IDX16JSL)),
PREDEFNODE("mvp", MERGE(GROUP_BOTHMOVES, 0x44)),
PREDEFNODE("mvn", MERGE(GROUP_BOTHMOVES, 0x54)),
PREDEFNODE("per", MERGE(GROUP_REL16_3, 98)),
PREDEFNODE(s_brl, MERGE(GROUP_REL16_3, 130)),
PREDEFNODE("per", MERGE(GROUP_REL16_3, 0x62)),
PREDEFNODE("brl", MERGE(GROUP_REL16_3, 0x82)),
PREDEFNODE("cop", MERGE(GROUP_MISC, IDX16COP)),
PREDEFNODE("rep", MERGE(GROUP_MISC, IDX16REP)),
PREDEFNODE("sep", MERGE(GROUP_MISC, IDX16SEP)),
PREDEFNODE("pea", MERGE(GROUP_MISC, IDX16PEA)),
PREDEFNODE("phd", MERGE(GROUP_IMPLIEDONLY, 11)),
PREDEFNODE("tcs", MERGE(GROUP_IMPLIEDONLY, 27)),
PREDEFNODE("pld", MERGE(GROUP_IMPLIEDONLY, 43)),
PREDEFNODE("tsc", MERGE(GROUP_IMPLIEDONLY, 59)),
PREDEFNODE("wdm", MERGE(GROUP_IMPLIEDONLY, 66)),
PREDEFNODE("phk", MERGE(GROUP_IMPLIEDONLY, 75)),
PREDEFNODE("tcd", MERGE(GROUP_IMPLIEDONLY, 91)),
PREDEFNODE("rtl", MERGE(GROUP_IMPLIEDONLY, 107)),
PREDEFNODE("tdc", MERGE(GROUP_IMPLIEDONLY, 123)),
PREDEFNODE("phb", MERGE(GROUP_IMPLIEDONLY, 139)),
PREDEFNODE("txy", MERGE(GROUP_IMPLIEDONLY, 155)),
PREDEFNODE("plb", MERGE(GROUP_IMPLIEDONLY, 171)),
PREDEFNODE("tyx", MERGE(GROUP_IMPLIEDONLY, 187)),
PREDEFNODE("xba", MERGE(GROUP_IMPLIEDONLY, 235)),
PREDEFLAST("xce", MERGE(GROUP_IMPLIEDONLY, 251)),
PREDEFNODE("phd", MERGE(GROUP_IMPLIEDONLY, 0x0b)),
PREDEFNODE("tcs", MERGE(GROUP_IMPLIEDONLY, 0x1b)),
PREDEFNODE("pld", MERGE(GROUP_IMPLIEDONLY, 0x2b)),
PREDEFNODE("tsc", MERGE(GROUP_IMPLIEDONLY, 0x3b)),
PREDEFNODE("phk", MERGE(GROUP_IMPLIEDONLY, 0x4b)),
PREDEFNODE("tcd", MERGE(GROUP_IMPLIEDONLY, 0x5b)),
PREDEFNODE("rtl", MERGE(GROUP_IMPLIEDONLY, 0x6b)),
PREDEFNODE("tdc", MERGE(GROUP_IMPLIEDONLY, 0x7b)),
PREDEFNODE("phb", MERGE(GROUP_IMPLIEDONLY, 0x8b)),
PREDEFNODE("txy", MERGE(GROUP_IMPLIEDONLY, 0x9b)),
PREDEFNODE("plb", MERGE(GROUP_IMPLIEDONLY, 0xab)),
PREDEFNODE("tyx", MERGE(GROUP_IMPLIEDONLY, 0xbb)),
// 0xcb is WAI
// 0xdb is STP
PREDEFNODE("xba", MERGE(GROUP_IMPLIEDONLY, 0xeb)),
PREDEFNODE("xce", MERGE(GROUP_IMPLIEDONLY, 0xfb)),
PREDEF_END("wdm", MERGE(GROUP_IMPLIEDONLY, 0x42)),
// ^^^^ this marks the last element
};
// 65ce02 has 46 new opcodes and a few changes:
static struct ronode mnemos_65ce02[] = {
static struct ronode mnemo_65ce02_tree[] = {
PREDEF_START,
// 65ce02 changes (zp) addressing of 65c02 to (zp),z addressing:
PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXeORA)),
PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDXeAND)),
PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDXeEOR)),
PREDEFNODE("and", MERGE(GROUP_ACCU, IDXeAND)),
PREDEFNODE("eor", MERGE(GROUP_ACCU, IDXeEOR)),
PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXeADC)),
PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXeSTA)), // +1 for (8,s),y
PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXeLDA)), // +1 for (8,s),y
@ -430,8 +435,8 @@ static struct ronode mnemos_65ce02[] = {
PREDEFNODE("asr", MERGE(GROUP_MISC, IDXeASR)),
PREDEFNODE("asw", MERGE(GROUP_MISC, IDXeASW)),
PREDEFNODE("cpz", MERGE(GROUP_MISC, IDXeCPZ)),
PREDEFNODE("dew", MERGE(GROUP_MISC, IDXeDEW)),
PREDEFNODE("inw", MERGE(GROUP_MISC, IDXeINW)),
PREDEFNODE("dew", MERGE(GROUP_ZPONLY, 0xc3)),
PREDEFNODE("inw", MERGE(GROUP_ZPONLY, 0xe3)),
PREDEFNODE("ldz", MERGE(GROUP_MISC, IDXeLDZ)),
PREDEFNODE("phw", MERGE(GROUP_MISC, IDXePHW | IM_FORCE16)), // when using immediate addressing, arg is 16 bit
PREDEFNODE("row", MERGE(GROUP_MISC, IDXeROW)),
@ -448,25 +453,28 @@ static struct ronode mnemos_65ce02[] = {
PREDEFNODE("tza", MERGE(GROUP_IMPLIEDONLY, 0x6b)),
PREDEFNODE("tba", MERGE(GROUP_IMPLIEDONLY, 0x7b)),
PREDEFNODE("phz", MERGE(GROUP_IMPLIEDONLY, 0xdb)),
PREDEFLAST("plz", MERGE(GROUP_IMPLIEDONLY, 0xfb)),
PREDEF_END("plz", MERGE(GROUP_IMPLIEDONLY, 0xfb)),
// ^^^^ this marks the last element
};
// 65ce02's "aug" opcode:
static struct ronode mnemos_aug[] = {
PREDEFLAST("aug", MERGE(GROUP_IMPLIEDONLY, 0x5c)), // actually a "4-byte NOP reserved for future expansion"
static struct ronode mnemo_aug_tree[] = {
PREDEF_START,
PREDEF_END("aug", MERGE(GROUP_IMPLIEDONLY, 0x5c)), // actually a "4-byte NOP reserved for future expansion"
// ^^^^ this marks the last element
};
// 4502's "map" and "eom" opcodes:
static struct ronode mnemos_map_eom[] = {
static struct ronode mnemo_map_eom_tree[] = {
PREDEF_START,
PREDEFNODE("map", MERGE(GROUP_IMPLIEDONLY, 0x5c)), // change memory mapping
PREDEFLAST("eom", MERGE(GROUP_IMPLIEDONLY, 0xea)), // actually the NOP opcode
PREDEF_END("eom", MERGE(GROUP_IMPLIEDONLY, 0xea)), // actually the NOP opcode
// ^^^^ this marks the last element
};
// m65 has a few extensions using prefix codes:
static struct ronode mnemos_m65[] = {
static struct ronode mnemo_m65_tree[] = {
PREDEF_START,
// extension 1:
// a NOP prefix changes ($ff),z addressing from using 16-bit pointers to
// using 32-bit pointers. I chose "[$ff],z" to indicate this.
@ -481,7 +489,11 @@ static struct ronode mnemos_m65[] = {
// ...now ASLQ/LSRQ/ROLQ/RORQ
// INC/DEC
// ...now INQ/DEQ
// it works with all addressing modes (beware of index register usage!)
// BIT
// ...now BITQ
// ASR
// ...now ASRQ
// it works with most addressing modes (beware of index register usage!)
// except for immediate addressing and "($ff),z", which becomes "($ff)"
// extension 3:
// extensions 1 and 2 can be combined (NEG:NEG:NOP prefix), then
@ -509,39 +521,21 @@ static struct ronode mnemos_m65[] = {
PREDEFNODE("rorq", MERGE(GROUP_MISC, IDX_ROR | PREFIX_NEGNEG)),
PREDEFNODE("inq", MERGE(GROUP_MISC, IDXcINC | PREFIX_NEGNEG)),
PREDEFNODE("deq", MERGE(GROUP_MISC, IDXcDEC | PREFIX_NEGNEG)),
PREDEFNODE("bitq", MERGE(GROUP_MISC, IDXmBITQ | PREFIX_NEGNEG)),
PREDEFNODE("asrq", MERGE(GROUP_MISC, IDXeASR | PREFIX_NEGNEG)),
// because the NOP opcode is used as a prefix code, the mnemonic was disabled:
PREDEFLAST("nop", MERGE(GROUP_PREFIX, 0xea)),
PREDEF_END("nop", MERGE(GROUP_PREFIX, 0xea)),
// ^^^^ this marks the last element
};
// Functions
// create dynamic buffer, build keyword trees
void Mnemo_init(void)
{
mnemo_dyna_buf = DynaBuf_create(MNEMO_DYNABUF_INITIALSIZE);
Tree_add_table(&mnemo_6502_tree, mnemos_6502);
Tree_add_table(&mnemo_6502undoc1_tree, mnemos_6502undoc1);
Tree_add_table(&mnemo_6502undoc2_tree, mnemos_6502undoc2);
Tree_add_table(&mnemo_c64dtv2_tree, mnemos_c64dtv2);
Tree_add_table(&mnemo_65c02_tree, mnemos_65c02);
Tree_add_table(&mnemo_bitmanips_tree, mnemos_bitmanips);
Tree_add_table(&mnemo_stp_wai_tree, mnemos_stp_wai);
Tree_add_table(&mnemo_65816_tree, mnemos_65816);
Tree_add_table(&mnemo_65ce02_tree, mnemos_65ce02);
Tree_add_table(&mnemo_aug_tree, mnemos_aug);
Tree_add_table(&mnemo_map_eom_tree, mnemos_map_eom);
Tree_add_table(&mnemo_m65_tree, mnemos_m65);
}
// Address mode parsing
// utility function for parsing indices. result must be processed via AMB_PREINDEX() or AMB_INDEX() macro!
static int get_index(boolean next)
// TODO: add pointer arg for result, use return value to indicate parse error!
static int get_index(void)
{
if (next)
GetByte();
if (!Input_accept_comma())
return INDEX_NONE;
@ -592,6 +586,7 @@ static void get_int_arg(struct number *result, boolean complain_about_indirect)
// wrapper function to detect addressing mode, and, if not IMPLIED, read arg.
// argument is stored in given result structure, addressing mode is returned.
// TODO: add pointer arg for result, use return value to indicate parse error!
static bits get_addr_mode(struct number *result)
{
struct expression expression;
@ -612,13 +607,15 @@ static bits get_addr_mode(struct number *result)
GetByte(); // proceed with next char
get_int_arg(result, FALSE);
typesystem_want_addr(result);
if (GotByte == ']')
address_mode_bits = AMB_LONGINDIRECT | AMB_INDEX(get_index(TRUE));
else
if (GotByte == ']') {
GetByte();
address_mode_bits = AMB_LONGINDIRECT | AMB_INDEX(get_index());
} else {
Throw_error(exception_syntax);
}
break;
default:
ALU_addrmode_int(&expression, 1); // direct call instead of wrapper, to allow for "(...,"
ALU_addrmode_int(&expression, 1); // direct call instead of wrapper, to allow for "(EXPR,x)" where parsing stops at comma
*result = expression.result.u.number;
typesystem_want_addr(result);
// check for indirect addressing
@ -628,14 +625,15 @@ static bits get_addr_mode(struct number *result)
if (expression.open_parentheses) {
// in case there are still open parentheses,
// read internal index
address_mode_bits |= AMB_PREINDEX(get_index(FALSE));
if (GotByte == ')')
address_mode_bits |= AMB_PREINDEX(get_index());
if (GotByte == ')') {
GetByte(); // go on with next char
else
} else {
Throw_error(exception_syntax);
}
}
// check for external index (after closing parenthesis)
address_mode_bits |= AMB_INDEX(get_index(FALSE));
address_mode_bits |= AMB_INDEX(get_index());
}
// ensure end of line
Input_ensure_EOS();
@ -672,6 +670,7 @@ static bits check_oversize(bits size_bit, struct number *argument)
// argument value and flags of parameter
// addressing_modes adressing modes (8b, 16b, 24b or any combination)
// Return value = force bit for number of parameter bytes to send (0 = error)
// TODO: add pointer arg for result, use return value to indicate error ONLY!
static bits calc_arg_size(bits force_bit, struct number *argument, bits addressing_modes)
{
// if there are no possible addressing modes, complain
@ -679,18 +678,18 @@ static bits calc_arg_size(bits force_bit, struct number *argument, bits addressi
Throw_error(exception_illegal_combination);
return 0;
}
// if command has force bit, act upon it
// if a force bit postfix was given, act upon it
if (force_bit) {
// if command allows this force bit, return it
// if mnemonic supports this force bit, return it
if (addressing_modes & force_bit)
return force_bit;
// if not, complain
Throw_error("Illegal combination of command and postfix.");
Throw_error("CPU does not support this postfix for this mnemonic.");
return 0;
}
// Command has no force bit. Check whether value has one
// mnemonic did not have a force bit postfix.
// if value has force bit, act upon it
if (argument->flags & NUMBER_FORCEBITS) {
// Value has force bit set, so return this or bigger size
@ -711,26 +710,30 @@ static bits calc_arg_size(bits force_bit, struct number *argument, bits addressi
// if only one addressing mode, use that
if ((addressing_modes == NUMBER_FORCES_8)
|| (addressing_modes == NUMBER_FORCES_16)
|| (addressing_modes == NUMBER_FORCES_24))
|| (addressing_modes == NUMBER_FORCES_24)) {
return addressing_modes; // There's only one, so use it
}
// There's more than one addressing mode. Check whether value is sure
// if value is unsure, use default size
if (argument->flags & NUMBER_EVER_UNDEFINED) {
// if there is an 8-bit addressing mode *and* the value
// is sure to fit into 8 bits, use the 8-bit mode
if ((addressing_modes & NUMBER_FORCES_8) && (argument->flags & NUMBER_FITS_BYTE))
if ((addressing_modes & NUMBER_FORCES_8) && (argument->flags & NUMBER_FITS_BYTE)) {
return NUMBER_FORCES_8;
}
// if there is a 16-bit addressing, use that
// call helper function for "oversized addr mode" warning
if (NUMBER_FORCES_16 & addressing_modes)
if (NUMBER_FORCES_16 & addressing_modes) {
return check_oversize(NUMBER_FORCES_16, argument);
}
// if there is a 24-bit addressing, use that
// call helper function for "oversized addr mode" warning
if (NUMBER_FORCES_24 & addressing_modes)
if (NUMBER_FORCES_24 & addressing_modes) {
return check_oversize(NUMBER_FORCES_24, argument);
}
// otherwise, use 8-bit-addressing, which will raise an
// error later on if the value won't fit
@ -746,17 +749,20 @@ static bits calc_arg_size(bits force_bit, struct number *argument, bits addressi
// Value is positive or zero. Check size ranges
// if there is an 8-bit addressing mode and value fits, use 8 bits
if ((addressing_modes & NUMBER_FORCES_8) && (argument->val.intval < 256))
if ((addressing_modes & NUMBER_FORCES_8) && (argument->val.intval < 256)) {
return NUMBER_FORCES_8;
}
// if there is a 16-bit addressing mode and value fits, use 16 bits
if ((addressing_modes & NUMBER_FORCES_16) && (argument->val.intval < 65536))
if ((addressing_modes & NUMBER_FORCES_16) && (argument->val.intval < 65536)) {
return NUMBER_FORCES_16;
}
// if there is a 24-bit addressing mode, use that. In case the
// value doesn't fit, the output function will do the complaining.
if (addressing_modes & NUMBER_FORCES_24)
if (addressing_modes & NUMBER_FORCES_24) {
return NUMBER_FORCES_24;
}
// Value is too big for all possible addressing modes
Throw_error(exception_number_out_of_range);
@ -766,8 +772,10 @@ static bits calc_arg_size(bits force_bit, struct number *argument, bits addressi
// Mnemonics using only implied addressing.
static void group_only_implied_addressing(int opcode)
{
//bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it?
// TODO - accept argument and complain about it? error message should tell more than "garbage data at end of line"!
// for 65ce02 and 4502, warn about buggy decimal mode
if ((opcode == 248) && (CPU_state.type->flags & CPUFLAG_DECIMALSUBTRACTBUGGY))
if ((opcode == 0xf8) && (CPU_state.type->flags & CPUFLAG_DECIMALSUBTRACTBUGGY))
Throw_first_pass_warning("Found SED instruction for CPU with known decimal SBC bug.");
Output_byte(opcode);
Input_ensure_EOS();
@ -841,7 +849,7 @@ static void far_branch(int preoffset)
// set addressing mode bits depending on which opcodes exist, then calculate
// argument size and output both opcode and argument
static void make_command(bits force_bit, struct number *result, unsigned long opcodes)
static void make_instruction(bits force_bit, struct number *result, unsigned long opcodes)
{
int addressing_modes = MAYBE______;
@ -922,52 +930,52 @@ static void group_main(int index, bits flags)
immediate_opcodes = imm_ops(&force_bit, accu_imm[index], flags & IMMASK);
// CAUTION - do not incorporate the line above into the line
// below - "force_bit" might be undefined (depends on compiler).
make_command(force_bit, &result, immediate_opcodes);
make_instruction(force_bit, &result, immediate_opcodes);
break;
case ABSOLUTE_ADDRESSING: // $ff, $ffff, $ffffff
make_command(force_bit, &result, accu_abs[index]);
make_instruction(force_bit, &result, accu_abs[index]);
break;
case X_INDEXED_ADDRESSING: // $ff,x, $ffff,x, $ffffff,x
make_command(force_bit, &result, accu_xabs[index]);
make_instruction(force_bit, &result, accu_xabs[index]);
break;
case Y_INDEXED_ADDRESSING: // $ffff,y (in theory, "$ff,y" as well)
make_command(force_bit, &result, accu_yabs[index]);
make_instruction(force_bit, &result, accu_yabs[index]);
break;
case STACK_INDEXED_ADDRESSING: // $ff,s
make_command(force_bit, &result, accu_sabs8[index]);
make_instruction(force_bit, &result, accu_sabs8[index]);
break;
case X_INDEXED_INDIRECT_ADDRESSING: // ($ff,x)
make_command(force_bit, &result, accu_xind8[index]);
make_instruction(force_bit, &result, accu_xind8[index]);
break;
case INDIRECT_ADDRESSING: // ($ff)
make_command(force_bit, &result, accu_ind8[index]);
make_instruction(force_bit, &result, accu_ind8[index]);
check_zp_wraparound(&result);
break;
case INDIRECT_Y_INDEXED_ADDRESSING: // ($ff),y
make_command(force_bit, &result, accu_indy8[index]);
make_instruction(force_bit, &result, accu_indy8[index]);
check_zp_wraparound(&result);
break;
case INDIRECT_Z_INDEXED_ADDRESSING: // ($ff),z only for 65ce02/4502/m65
make_command(force_bit, &result, accu_indz8[index]);
make_instruction(force_bit, &result, accu_indz8[index]);
check_zp_wraparound(&result);
break;
case LONG_INDIRECT_ADDRESSING: // [$ff] for 65816 and m65
// if in quad mode, m65 encodes this as NOP + ($ff),z
if (flags & LI_PREFIX_NOP)
Output_byte(234);
make_command(force_bit, &result, accu_lind8[index]);
Output_byte(0xea);
make_instruction(force_bit, &result, accu_lind8[index]);
break;
case LONG_INDIRECT_Y_INDEXED_ADDRESSING: // [$ff],y only for 65816
make_command(force_bit, &result, accu_lindy8[index]);
make_instruction(force_bit, &result, accu_lindy8[index]);
break;
case STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING: // ($ff,s),y only for 65816 and 65ce02/4502/m65
make_command(force_bit, &result, accu_sindy8[index]);
make_instruction(force_bit, &result, accu_sindy8[index]);
break;
case LONG_INDIRECT_Z_INDEXED_ADDRESSING: // [$ff],z only for m65
// if not in quad mode, m65 encodes this as NOP + ($ff),z
if (flags & LI_PREFIX_NOP)
Output_byte(234);
make_command(force_bit, &result, accu_lindz8[index]);
Output_byte(0xea);
make_instruction(force_bit, &result, accu_lindz8[index]);
break;
default: // other combinations are illegal
Throw_error(exception_illegal_combination);
@ -992,7 +1000,7 @@ static void group_misc(int index, bits immediate_mode)
immediate_opcodes = imm_ops(&force_bit, misc_imm[index], immediate_mode);
// CAUTION - do not incorporate the line above into the line
// below - "force_bit" might be undefined (depends on compiler).
make_command(force_bit, &result, immediate_opcodes);
make_instruction(force_bit, &result, immediate_opcodes);
// warn about unstable ANE/LXA (undocumented opcode of NMOS 6502)?
if ((CPU_state.type->flags & CPUFLAG_8B_AND_AB_NEED_0_ARG)
&& (result.ntype == NUMTYPE_INT)
@ -1004,13 +1012,13 @@ static void group_misc(int index, bits immediate_mode)
}
break;
case ABSOLUTE_ADDRESSING: // $ff or $ffff
make_command(force_bit, &result, misc_abs[index]);
make_instruction(force_bit, &result, misc_abs[index]);
break;
case X_INDEXED_ADDRESSING: // $ff,x or $ffff,x
make_command(force_bit, &result, misc_xabs[index]);
make_instruction(force_bit, &result, misc_xabs[index]);
break;
case Y_INDEXED_ADDRESSING: // $ff,y or $ffff,y
make_command(force_bit, &result, misc_yabs[index]);
make_instruction(force_bit, &result, misc_yabs[index]);
break;
default: // other combinations are illegal
Throw_error(exception_illegal_combination);
@ -1020,6 +1028,7 @@ static void group_misc(int index, bits immediate_mode)
// mnemonics using only 8bit relative addressing (short branch instructions).
static void group_std_branches(int opcode)
{
//bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it?
Output_byte(opcode);
near_branch(2);
}
@ -1028,6 +1037,7 @@ static void group_std_branches(int opcode)
static void group_bbr_bbs(int opcode)
{
struct number zpmem;
//bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it?
get_int_arg(&zpmem, TRUE);
typesystem_want_addr(&zpmem);
@ -1043,6 +1053,7 @@ static void group_bbr_bbs(int opcode)
// mnemonics using only 16bit relative addressing (BRL and PER of 65816, and the long branches of 65ce02)
static void group_relative16(int opcode, int preoffset)
{
//bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it?
Output_byte(opcode);
far_branch(preoffset);
}
@ -1054,6 +1065,7 @@ static void group_mvn_mvp(int opcode)
boolean unmatched_hash = FALSE;
struct number source,
target;
//bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it?
// assembler syntax: "mnemonic source, target" or "mnemonic #source, #target"
// machine language order: "opcode target source"
@ -1090,6 +1102,7 @@ static void group_mvn_mvp(int opcode)
// "rmb0..7" and "smb0..7"
static void group_only_zp(int opcode)
{
//bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it?
struct number target;
get_int_arg(&target, TRUE);
@ -1102,9 +1115,10 @@ static void group_only_zp(int opcode)
// NOP on m65 cpu (FIXME - "!align" outputs NOPs, what about that? what if user writes NEG:NEG?)
static void group_prefix(int opcode)
{
//bits force_bit = Input_get_force_bit(); // skips spaces after // TODO - accept postfix and complain about it?
char buffer[100]; // 640K should be enough for anybody
sprintf(buffer, "The chosen CPU uses opcode 0x%02x as a prefix code, do not use this mnemonic!", opcode); // FIXME - add to docs!
sprintf(buffer, "The chosen CPU uses opcode 0x%02x as a prefix code, do not use this mnemonic!", opcode);
Throw_error(buffer);
}
@ -1116,10 +1130,10 @@ static void group_jump(int index)
switch (get_addr_mode(&result)) {
case ABSOLUTE_ADDRESSING: // absolute16 or absolute24
make_command(force_bit, &result, jump_abs[index]);
make_instruction(force_bit, &result, jump_abs[index]);
break;
case INDIRECT_ADDRESSING: // ($ffff)
make_command(force_bit, &result, jump_ind[index]);
make_instruction(force_bit, &result, jump_ind[index]);
// check whether to warn about 6502's JMP() bug
if ((result.ntype == NUMTYPE_INT)
&& ((result.val.intval & 0xff) == 0xff)
@ -1127,10 +1141,10 @@ static void group_jump(int index)
Throw_warning("Assembling buggy JMP($xxff) instruction");
break;
case X_INDEXED_INDIRECT_ADDRESSING: // ($ffff,x)
make_command(force_bit, &result, jump_xind[index]);
make_instruction(force_bit, &result, jump_xind[index]);
break;
case LONG_INDIRECT_ADDRESSING: // [$ffff]
make_command(force_bit, &result, jump_lind[index]);
make_instruction(force_bit, &result, jump_lind[index]);
break;
default: // other combinations are illegal
Throw_error(exception_illegal_combination);
@ -1182,7 +1196,7 @@ static boolean check_mnemo_tree(struct ronode *tree, struct dynabuf *dyna_buf)
case GROUP_BOTHMOVES: // "mvp" and "mvn"
group_mvn_mvp(code);
break;
case GROUP_ZPONLY: // "rmb0..7" and "smb0..7"
case GROUP_ZPONLY: // "rmb0..7", "smb0..7", "inw", "dew"
group_only_zp(code);
break;
case GROUP_PREFIX: // NOP for m65 cpu

View File

@ -10,8 +10,6 @@
#include "config.h"
// create dynamic buffer, build keyword trees
extern void Mnemo_init(void);
// check whether mnemonic in GlobalDynaBuf is supported by standard 6502 cpu.
extern boolean keyword_is_6502_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by NMOS 6502 cpu (includes undocumented opcodes).

View File

@ -24,7 +24,6 @@
// constants
#define OUTBUFFERSIZE 65536
#define NO_SEGMENT_START (-1) // invalid value to signal "not in a segment"
@ -39,6 +38,7 @@ struct segment {
// structure for all output stuff:
struct output {
// output buffer stuff
intval_t bufsize; // either 64 KiB or 16 MiB
char *buffer; // holds assembled code
intval_t write_idx; // index of next write
intval_t lowest_written; // smallest address used
@ -72,13 +72,14 @@ enum output_format {
OUTPUT_FORMAT_PLAIN // code only
};
// predefined stuff
static struct ronode *file_format_tree = NULL; // tree to hold output formats (FIXME - a tree for three items, really?)
static struct ronode file_format_list[] = {
// tree to hold output formats (FIXME - a tree for three items, really?)
static struct ronode file_format_tree[] = {
PREDEF_START,
#define KNOWN_FORMATS "'plain', 'cbm', 'apple'" // shown in CLI error message for unknown formats
PREDEFNODE("apple", OUTPUT_FORMAT_APPLE),
PREDEFNODE("cbm", OUTPUT_FORMAT_CBM),
// PREDEFNODE("o65", OUTPUT_FORMAT_O65),
PREDEFLAST("plain", OUTPUT_FORMAT_PLAIN),
PREDEF_END("plain", OUTPUT_FORMAT_PLAIN),
// ^^^^ this marks the last element
};
// chosen file format
@ -110,7 +111,7 @@ static void find_segment_max(intval_t new_pc)
while (test_segment->start <= new_pc)
test_segment = test_segment->next;
if (test_segment == &out->segment.list_head)
out->segment.max = OUTBUFFERSIZE - 1;
out->segment.max = out->bufsize - 1;
else
out->segment.max = test_segment->start - 1; // last free address available
}
@ -119,8 +120,9 @@ static void find_segment_max(intval_t new_pc)
//
static void border_crossed(int current_offset)
{
if (current_offset >= OUTBUFFERSIZE)
if (current_offset >= out->bufsize)
Throw_serious_error("Produced too much code.");
// TODO - get rid of FIRST_PASS condition, because user can suppress these warnings if they want
if (FIRST_PASS) {
// TODO: make warn/err an arg for a general "Throw" function
if (config.segment_warning_is_error)
@ -139,7 +141,9 @@ void (*Output_byte)(intval_t byte);
// send low byte to output buffer, automatically increasing program counter
static void real_output(intval_t byte)
{
// did we reach segment limit?
// CAUTION - there are two copies of these checks!
// TODO - add additional check for current segment's "limit" value
// did we reach next segment?
if (out->write_idx > out->segment.max)
border_crossed(out->write_idx);
// new minimum address?
@ -182,7 +186,9 @@ void output_skip(int size)
Output_byte(0); // trigger error with a dummy byte
--size; // fix amount to cater for dummy byte
}
// did we reach segment limit?
// CAUTION - there are two copies of these checks!
// TODO - add additional check for current segment's "limit" value
// did we reach next segment?
if (out->write_idx + size - 1 > out->segment.max)
border_crossed(out->write_idx + size - 1);
// new minimum address?
@ -200,7 +206,7 @@ void output_skip(int size)
// fill output buffer with given byte value
static void fill_completely(char value)
{
memset(out->buffer, value, OUTBUFFERSIZE);
memset(out->buffer, value, out->bufsize);
}
@ -234,9 +240,6 @@ int outputfile_set_format(void)
{
void *node_body;
// make sure tree is initialised
if (file_format_tree == NULL)
Tree_add_table(&file_format_tree, file_format_list);
// perform lookup
if (!Tree_easy_scan(file_format_tree, &node_body, GlobalDynaBuf))
return 1;
@ -251,6 +254,7 @@ int outputfile_prefer_cbm_format(void)
{
if (output_format != OUTPUT_FORMAT_UNSPECIFIED)
return 0;
output_format = OUTPUT_FORMAT_CBM;
return 1;
}
@ -272,9 +276,10 @@ int outputfile_set_filename(void)
// init output struct (done later)
void Output_init(signed long fill_value)
void Output_init(signed long fill_value, boolean use_large_buf)
{
out->buffer = safe_malloc(OUTBUFFERSIZE);
out->bufsize = use_large_buf ? 0x1000000 : 0x10000;
out->buffer = safe_malloc(out->bufsize);
if (fill_value == MEMINIT_USE_DEFAULT) {
fill_value = FILLVALUE_INITIAL;
out->initvalue_set = FALSE;
@ -358,7 +363,7 @@ static void link_segment(intval_t start, intval_t length)
// check whether given PC is inside segment.
// only call in first pass, otherwise too many warnings might be thrown
// only call in first pass, otherwise too many warnings might be thrown (TODO - still?)
static void check_segment(intval_t new_pc)
{
struct segment *test_segment = out->segment.list_head.next;
@ -389,7 +394,7 @@ void Output_passinit(void)
//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!
// Currently this does not happen because segment warnings are only generated in first pass. FIXME!
// delete segment list (and free blocks)
// while ((temp = segment_list)) {
// segment_list = segment_list->next;
@ -397,13 +402,13 @@ void Output_passinit(void)
// }
// invalidate start and end (first byte actually written will fix them)
out->lowest_written = OUTBUFFERSIZE - 1;
out->lowest_written = out->bufsize - 1;
out->highest_written = 0;
// 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.max = out->bufsize - 1; // TODO - use end of bank?
out->segment.flags = 0;
out->xor = 0;
@ -455,18 +460,20 @@ void Output_end_segment(void)
// change output pointer and enable output
// TODO - this only gets called from vcpu_set_pc so could be made static!
void Output_start_segment(intval_t address_change, bits 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->write_idx = (out->write_idx + address_change) & (out->bufsize - 1);
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
// TODO - remove FIRST_PASS condition
if (FIRST_PASS) {
if (!(segment_flags & SEGMENT_FLAG_OVERLAY))
check_segment(out->segment.start);
@ -490,7 +497,7 @@ void output_set_xor(char xor)
// in addition to that, it will be called on each "*= VALUE".
void vcpu_set_pc(intval_t new_pc, bits segment_flags)
{
intval_t new_offset;
intval_t pc_change;
// support stupidly bad, old, ancient, deprecated, obsolete behaviour:
if (pseudopc_current_context != NULL) {
@ -506,12 +513,12 @@ void vcpu_set_pc(intval_t new_pc, bits segment_flags)
// stuff happens! i see no reason to try to mimic that.
}
}
new_offset = (new_pc - CPU_state.pc.val.intval) & 0xffff;
CPU_state.pc.val.intval = new_pc;
pc_change = new_pc - CPU_state.pc.val.intval;
CPU_state.pc.val.intval = new_pc; // FIXME - oversized values are accepted without error and will be wrapped at end of statement!
CPU_state.pc.ntype = NUMTYPE_INT; // FIXME - remove when allowing undefined!
CPU_state.pc.addr_refs = 1; // yes, PC counts as address
// now tell output buffer to start a new segment
Output_start_segment(new_offset, segment_flags);
Output_start_segment(pc_change, segment_flags);
}
/*
TODO - overhaul program counter and memory pointer stuff:
@ -560,7 +567,7 @@ int vcpu_get_statement_size(void)
// adjust program counter (called at end of each statement)
void vcpu_end_statement(void)
{
CPU_state.pc.val.intval = (CPU_state.pc.val.intval + CPU_state.add_to_pc) & 0xffff;
CPU_state.pc.val.intval = (CPU_state.pc.val.intval + CPU_state.add_to_pc) & (out->bufsize - 1);
CPU_state.add_to_pc = 0;
}
@ -599,7 +606,7 @@ void pseudopc_end(void)
if (config.wanted_version >= VER_DISABLED_OBSOLETE_STUFF)
Bug_found("ClosingUnopenedPseudopcBlock", 0);
} else {
CPU_state.pc.val.intval = (CPU_state.pc.val.intval - pseudopc_current_context->offset) & 0xffff; // pc might have wrapped around
CPU_state.pc.val.intval = (CPU_state.pc.val.intval - pseudopc_current_context->offset) & (out->bufsize - 1); // pc might have wrapped around
CPU_state.pc.ntype = pseudopc_current_context->ntype;
pseudopc_current_context = pseudopc_current_context->outer; // go back to outer block
}
@ -623,7 +630,7 @@ int pseudopc_unpseudo(struct number *target, struct pseudopc *context, unsigned
return 1; // error
}
// FIXME - in future, check both target and context for NUMTYPE_UNDEFINED!
target->val.intval = (target->val.intval - context->offset) & 0xffff; // FIXME - is masking really needed?
target->val.intval = (target->val.intval - context->offset) & (out->bufsize - 1); // FIXME - is masking really needed? TODO
context = context->outer;
}
return 0; // ok

View File

@ -42,7 +42,7 @@ extern void Output_passinit(void);
// outbuf stuff:
// alloc and init mem buffer (done later)
extern void Output_init(signed long fill_value);
extern void Output_init(signed long fill_value, boolean use_large_buf);
// skip over some bytes in output buffer without starting a new segment
// (used by "!skip", and also called by "!binary" if really calling
// Output_byte would be a waste of time)

View File

@ -32,17 +32,9 @@ enum eos {
};
// constants
static const char s_08[] = "08";
#define s_8 (s_08 + 1) // Yes, I know I'm sick
#define s_sl (s_asl + 1) // Yes, I know I'm sick
#define s_rl (s_brl + 1) // Yes, I know I'm sick
static const char exception_unknown_pseudo_opcode[] = "Unknown pseudo opcode.";
// variables
static struct ronode *pseudo_opcode_tree = NULL; // tree to hold pseudo opcodes
// this is not really a pseudo opcode, but similar enough to be put here:
// called when "*= EXPRESSION" is parsed, to set the program counter
void notreallypo_setpc(void) // GotByte is '*'
@ -105,8 +97,8 @@ static enum eos po_initmem(void)
// get value
ALU_defined_int(&intresult);
if ((intresult.val.intval > 0xff) || (intresult.val.intval < -0x80))
Throw_error(exception_number_out_of_range);
if ((intresult.val.intval > 255) || (intresult.val.intval < -128))
Throw_error(exception_number_out_of_8b_range);
if (output_initmem(intresult.val.intval & 0xff))
return SKIP_REMAINDER;
return ENSURE_EOS;
@ -121,8 +113,8 @@ static enum eos po_xor(void)
old_value = output_get_xor();
ALU_any_int(&change);
if ((change > 0xff) || (change < -0x80)) {
Throw_error(exception_number_out_of_range);
if ((change > 255) || (change < -128)) {
Throw_error(exception_number_out_of_8b_range);
change = 0;
}
output_set_xor(old_value ^ change);
@ -195,7 +187,7 @@ static enum eos iterate(void (*fn)(intval_t))
}
// Insert 8-bit values ("!08" / "!8" / "!by" / "!byte" pseudo opcode)
// insert 8-bit values ("!8" / "!08" / "!by" / "!byte" pseudo opcode)
static enum eos po_byte(void)
{
return iterate(output_8);
@ -329,7 +321,7 @@ static enum eos user_defined_encoding(FILE *stream)
}
encoder_current = &encoder_file; // activate new encoding
encoding_loaded_table = local_table; // activate local table
// If there's a block, parse that and then restore old values
// if there's a block, parse that and then restore old values
if (Parse_optional_block()) {
encoder_current = buffered_encoder;
} else {
@ -457,7 +449,7 @@ static enum eos po_scrxor(void)
return encode_string(&encoder_scr, xor);
}
// Include binary file ("!binary" pseudo opcode)
// include binary file ("!binary" pseudo opcode)
// FIXME - split this into "parser" and "worker" fn and move worker fn somewhere else.
static enum eos po_binary(void)
{
@ -760,13 +752,12 @@ static enum eos po_set(void) // now GotByte = illegal char
return SKIP_REMAINDER;
}
// TODO: in versions before 0.97, force bit handling was broken
// in both "!set" and "!for":
// trying to change a force bit correctly raised an error, but
// in any case, ALL FORCE BITS WERE CLEARED in symbol. only
// cases like !set N=N+1 worked, because the force bit was
// taken from result.
// maybe support this behaviour via --dialect?
// TODO: in versions before 0.97, force bit handling was broken in both
// "!set" and "!for":
// trying to change a force bit raised an error (which is correct), but
// in any case, ALL FORCE BITS WERE CLEARED in symbol. only cases like
// !set N=N+1 worked, because the force bit was taken from result.
// maybe support this behaviour via --dialect? I'd rather not...
parse_assignment(scope, force_bit, POWER_CHANGE_VALUE | POWER_CHANGE_OBJTYPE);
return ENSURE_EOS;
}
@ -814,10 +805,10 @@ static enum eos po_zone(void)
// set default values in case there is no valid title
new_title = s_untitled;
allocated = FALSE;
// Check whether a zone title is given. If yes and it can be read,
// check whether a zone title is given. if yes and it can be read,
// get copy, remember pointer and remember to free it later on.
if (BYTE_CONTINUES_KEYWORD(GotByte)) {
// Because we know of one character for sure,
// because we know of one character for sure,
// there's no need to check the return value.
Input_read_keyword();
new_title = DynaBuf_get_copy(GlobalDynaBuf);
@ -827,7 +818,7 @@ static enum eos po_zone(void)
// section type is "subzone", just in case a block follows
section_new(section_now, "Subzone", new_title, allocated);
if (Parse_optional_block()) {
// Block has been parsed, so it was a SUBzone.
// block has been parsed, so it was a SUBzone.
section_finalize(section_now); // end inner zone
*section_now = entry_values; // restore entry values
} else {
@ -1157,13 +1148,67 @@ static enum eos po_macro(void) // now GotByte = illegal char
return ENSURE_EOS;
}
/*
// trace/watch
#define TRACEWATCH_LOAD (1u << 0)
#define TRACEWATCH_STORE (1u << 1)
#define TRACEWATCH_EXEC (1u << 2)
#define TRACEWATCH_DEFAULT (TRACEWATCH_LOAD | TRACEWATCH_STORE | TRACEWATCH_EXEC)
#define TRACEWATCH_BREAK (1u << 3)
static enum eos tracewatch(boolean enter_monitor)
{
struct number pc;
bits flags = 0;
vcpu_read_pc(&pc);
SKIPSPACE();
// check for flags
if (GotByte != CHAR_EOS) {
do {
// parse flag. if no keyword given, give up
if (Input_read_and_lower_keyword() == 0)
return SKIP_REMAINDER; // fail (error has been reported)
if (strcmp(GlobalDynaBuf->buffer, "load") == 0) {
flags |= TRACEWATCH_LOAD;
} else if (strcmp(GlobalDynaBuf->buffer, "store") == 0) {
flags |= TRACEWATCH_STORE;
} else if (strcmp(GlobalDynaBuf->buffer, "exec") == 0) {
flags |= TRACEWATCH_EXEC;
} else {
Throw_error("Unknown flag (known are: load, store, exec)."); // FIXME - add to docs!
return SKIP_REMAINDER;
}
} while (Input_accept_comma());
}
// shortcut: no flags at all -> set all flags!
if (!flags)
flags = TRACEWATCH_DEFAULT;
if (enter_monitor)
flags |= TRACEWATCH_BREAK;
if (pc.ntype != NUMTYPE_UNDEFINED) {
//FIXME - store pc and flags!
}
return ENSURE_EOS;
}
// make next byte a trace point (for VICE debugging)
static enum eos po_trace(void)
{
return tracewatch(FALSE); // do not enter monitor, just output
}
// make next byte a watch point (for VICE debugging)
static enum eos po_watch(void)
{
return tracewatch(TRUE); // break into monitor
}
*/
// constants
#define USERMSG_DYNABUF_INITIALSIZE 80
#define USERMSG_INITIALSIZE 80
// variables
static struct dynabuf *user_message; // dynamic buffer (!warn/error/serious)
static STRUCT_DYNABUF_REF(user_message, USERMSG_INITIALSIZE); // for !warn/error/serious
// helper function to show user-defined messages
@ -1246,14 +1291,15 @@ static enum eos po_endoffile(void)
}
// pseudo opcode table
static struct ronode pseudo_opcode_list[] = {
static struct ronode pseudo_opcode_tree[] = {
PREDEF_START,
PREDEFNODE("initmem", po_initmem),
PREDEFNODE("xor", po_xor),
PREDEFNODE("to", po_to),
PREDEFNODE(s_8, po_byte),
PREDEFNODE(s_08, po_byte),
PREDEFNODE("by", po_byte),
PREDEFNODE("byte", po_byte),
PREDEFNODE("8", po_byte),
PREDEFNODE("08", po_byte), // legacy alias, don't ask...
PREDEFNODE("wo", po_16),
PREDEFNODE("word", po_16),
PREDEFNODE("16", po_16),
@ -1272,10 +1318,10 @@ static struct ronode pseudo_opcode_list[] = {
PREDEFNODE("convtab", po_convtab),
PREDEFNODE("tx", po_text),
PREDEFNODE("text", po_text),
PREDEFNODE(s_raw, po_raw),
PREDEFNODE(s_pet, po_pet),
PREDEFNODE(s_scr, po_scr),
PREDEFNODE(s_scrxor, po_scrxor),
PREDEFNODE("raw", po_raw),
PREDEFNODE("pet", po_pet),
PREDEFNODE("scr", po_scr),
PREDEFNODE("scrxor", po_scrxor),
PREDEFNODE("bin", po_binary),
PREDEFNODE("binary", po_binary),
PREDEFNODE("fi", po_fill),
@ -1287,13 +1333,13 @@ static struct ronode pseudo_opcode_list[] = {
PREDEFNODE("cpu", po_cpu),
PREDEFNODE("al", po_al),
PREDEFNODE("as", po_as),
PREDEFNODE(s_rl, po_rl),
PREDEFNODE("rl", po_rl),
PREDEFNODE("rs", po_rs),
PREDEFNODE("addr", po_address),
PREDEFNODE("address", po_address),
// PREDEFNODE("enum", po_enum),
PREDEFNODE("set", po_set),
PREDEFNODE(s_sl, po_symbollist),
PREDEFNODE("sl", po_symbollist),
PREDEFNODE("symbollist", po_symbollist),
PREDEFNODE("zn", po_zone),
PREDEFNODE("zone", po_zone),
@ -1308,25 +1354,19 @@ static struct ronode pseudo_opcode_list[] = {
PREDEFNODE("do", po_do),
PREDEFNODE("while", po_while),
PREDEFNODE("macro", po_macro),
/* PREDEFNODE("trace", po_trace),
PREDEFNODE("watch", po_watch), */
// PREDEFNODE("debug", po_debug),
// PREDEFNODE("info", po_info),
PREDEFNODE("warn", po_warn),
PREDEFNODE(s_error, po_error),
PREDEFNODE("error", po_error),
PREDEFNODE("serious", po_serious),
PREDEFNODE("eof", po_endoffile),
PREDEFLAST("endoffile", po_endoffile),
PREDEF_END("endoffile", po_endoffile),
// ^^^^ this marks the last element
};
// register pseudo opcodes and create dynamic buffer
void pseudoopcodes_init(void)
{
user_message = DynaBuf_create(USERMSG_DYNABUF_INITIALSIZE);
Tree_add_table(&pseudo_opcode_tree, pseudo_opcode_list);
}
// parse a pseudo opcode. has to be re-entrant.
void pseudoopcode_parse(void) // now GotByte = "!"
{

View File

@ -9,8 +9,6 @@
// call when "*= EXPRESSION" is parsed
extern void notreallypo_setpc(void);
// register pseudo opcodes
extern void pseudoopcodes_init(void);
// parse pseudo opcode. has to be re-entrant.
extern void pseudoopcode_parse(void);

View File

@ -232,6 +232,7 @@ void symbols_vicelabels(FILE *fd)
fputc('\n', fd);
// dump address symbols
Tree_dump_forest(symbols_forest, SCOPE_GLOBAL, dump_vice_address, fd);
// TODO - add trace points and watch points with load/store/exec args!
}

View File

@ -28,14 +28,15 @@ hash_t make_hash(struct ronode *node) {
}
// Link a predefined data set to a tree
void add_node_to_tree(struct ronode **tree, struct ronode *node_to_add)
static void add_node_to_tree(struct ronode **tree, struct ronode *node_to_add)
{
hash_t hash;
// compute hash value
hash = make_hash(node_to_add);
// search for NULL pointer to replace
while (*tree) {
// compare HashValue
// decide which way to go
if (hash > (*tree)->hash_value)
tree = &((*tree)->greater_than);
else
@ -48,10 +49,11 @@ void add_node_to_tree(struct ronode **tree, struct ronode *node_to_add)
// fields.
}
// Add predefined tree items to given tree. The PREDEF* macros set HashValue
// to 1 in all entries but the last. The last entry contains 0.
void Tree_add_table(struct ronode **tree, struct ronode *table_to_add)
// Add predefined tree items to given tree. The PREDEF* macros set the hash
// to 1 in all entries but the last, and to 0 in the last entry.
static void tree_from_list(struct ronode **tree, struct ronode *table_to_add)
{
//printf("Building tree from list.\n");
// Caution when trying to optimise this. :)
while (table_to_add->hash_value)
add_node_to_tree(tree, table_to_add++);
@ -72,6 +74,15 @@ int Tree_easy_scan(struct ronode *tree, void **node_body, struct dynabuf *dyna_b
b2;
hash_t hash;
// check if tree is actually ready to use. if not, build it from list.
// (list's first item does not contain real data, so "greater_than" is
// used to hold pointer to tree root)
if (tree->greater_than == NULL)
tree_from_list(&tree->greater_than, tree + 1); // real data starts at next list item
tree = tree->greater_than; // go from list head to tree root
// ok, we're done with this setup stuff.
// from now on, "greater_than" really means "greater_than"!
wanted.id_string = dyna_buf->buffer;
hash = make_hash(&wanted);
while (tree) {

View File

@ -12,9 +12,9 @@
// macros for pre-defining tree node tables
#define PREDEF_START {NULL, NULL, 0, NULL, NULL} // this is used to determine if list has been made into tree yet
#define PREDEFNODE(s, v) {NULL, NULL, 1, s, (void *) (v)}
#define PREDEFLAST(s, v) {NULL, NULL, 0, s, (void *) (v)}
#define PREDEF_END(s, v) {NULL, NULL, 0, s, (void *) (v)}
// type definitions
@ -42,8 +42,6 @@ struct rwnode {
// prototypes
// Add predefined tree items to given tree.
extern void Tree_add_table(struct ronode **tree, struct ronode *table_to_add);
// Search for a given ID string in a given tree. Store "body" component in
// node_body and return TRUE. Return FALSE if no matching item found.
struct dynabuf;

View File

@ -9,7 +9,7 @@
#define RELEASE "0.97" // update before release FIXME
#define CODENAME "Zem" // update before release
#define CHANGE_DATE "28 June" // update before release FIXME
#define CHANGE_DATE "24 Aug" // update before release FIXME
#define CHANGE_YEAR "2020" // update before release
//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/"
#define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME

8
testing/cpus/check-all-cpus.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
for CPU in 6502 65c02 r65c02 w65c02 nmos6502 c64dtv2 65ce02 4502 m65 65816 ; do
acme -v0 test-"$CPU".a || exit
cmp out-"$CPU".o expected-"$CPU".o || exit
done
echo
echo "All CPU tests passed successfully."
echo

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testing/cpus/expected-m65.o Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

155
testing/cpus/include-6502.a Normal file
View File

@ -0,0 +1,155 @@
;ACME 0.97
; all the standard 6502 instructions:
brk ; 00
ora ($01, x) ; 01
ora $05 ; 05
asl $05 ; 06
php ; 08
ora #$09 ; 09
asl ; 0a
ora $0d0e ; 0d
asl $0d0e ; 0e
bpl * + 2 ; 10
ora ($11), y ; 11
ora $15, x ; 15
asl $15, x ; 16
clc ; 18
ora $1919, y ; 19
ora $1d1e, x ; 1d
asl $1d1e, x ; 1e
jsr $0d0e ; 20
and ($01, x) ; 21
bit $05 ; 24
and $05 ; 25
rol $05 ; 26
plp ; 28
and #$09 ; 29
rol ; 2a
bit $0d0e ; 2c
and $0d0e ; 2d
rol $0d0e ; 2e
bmi * + 2 ; 30
and ($11), y ; 31
and $15, x ; 35
rol $15, x ; 36
sec ; 38
and $1919, y ; 39
and $1d1e, x ; 3d
rol $1d1e, x ; 3e
rti ; 40
eor ($01, x) ; 41
eor $05 ; 45
lsr $05 ; 46
pha ; 48
eor #$09 ; 49
lsr ; 4a
jmp $0d0e ; 4c
eor $0d0e ; 4d
lsr $0d0e ; 4e
bvc * + 2 ; 50
eor ($11), y ; 51
eor $15, x ; 55
lsr $15, x ; 56
cli ; 58
eor $1919, y ; 59
eor $1d1e, x ; 5d
lsr $1d1e, x ; 5e
rts ; 60
adc ($01, x) ; 61
adc $05 ; 65
ror $05 ; 66
pla ; 68
adc #$09 ; 69
ror ; 6a
jmp ($6c6c) ; 6c
adc $0d0e ; 6d
ror $0d0e ; 6e
bvs * + 2 ; 70
adc ($11), y ; 71
adc $15, x ; 75
ror $15, x ; 76
sei ; 78
adc $1919, y ; 79
adc $1d1e, x ; 7d
ror $1d1e, x ; 7e
sta ($01, x) ; 81
sty $05 ; 84
sta $05 ; 85
stx $05 ; 86
dey ; 88
txa ; 8a
sty $0d0e ; 8c
sta $0d0e ; 8d
stx $0d0e ; 8e
bcc * + 2 ; 90
sta ($11), y ; 91
sty $15, x ; 94
sta $15, x ; 95
stx $96, y ; 96
tya ; 98
sta $1919, y ; 99
txs ; 9a
sta $1d1e, x ; 9d
ldy #$09 ; a0
lda ($01, x) ; a1
ldx #$09 ; a2
ldy $05 ; a4
lda $05 ; a5
ldx $05 ; a6
tay ; a8
lda #$09 ; a9
tax ; aa
ldy $0d0e ; ac
lda $0d0e ; ad
ldx $0d0e ; ae
bcs * + 2 ; b0
lda ($11), y ; b1
ldy $15, x ; b4
lda $15, x ; b5
ldx $96, y ; b6
clv ; b8
lda $1919, y ; b9
tsx ; ba
ldy $1d1e, x ; bc
lda $1d1e, x ; bd
ldx $1919, y ; be
cpy #$09 ; c0
cmp ($01, x) ; c1
cpy $05 ; c4
cmp $05 ; c5
dec $05 ; c6
iny ; c8
cmp #$09 ; c9
dex ; ca
cpy $0d0e ; cc
cmp $0d0e ; cd
dec $0d0e ; ce
bne * + 2 ; d0
cmp ($11), y ; d1
cmp $15, x ; d5
dec $15, x ; d6
cld ; d8
cmp $1919, y ; d9
cmp $1d1e, x ; dd
dec $1d1e, x ; de
cpx #$09 ; e0
sbc ($01, x) ; e1
cpx $05 ; e4
sbc $05 ; e5
inc $05 ; e6
inx ; e8
sbc #$09 ; e9
!ifndef M65 {
nop ; ea (m65 re-uses this opcode as a prefix)
}
cpx $0d0e ; ec
sbc $0d0e ; ed
inc $0d0e ; ee
beq * + 2 ; f0
sbc ($11), y ; f1
sbc $15, x ; f5
inc $15, x ; f6
sed ; f8
sbc $1919, y ; f9
sbc $1d1e, x ; fd
inc $1d1e, x ; fe

View File

@ -0,0 +1,29 @@
;ACME 0.97
; extensions in CMOS re-design (65c02):
tsb $04 ; 04
tsb $0c0c ; 0c
ora ($12) ; 12
trb $04 ; 14
inc ; 1a
trb $0c0c ; 1c
and ($12) ; 32
bit $34, x ; 34
dec ; 3a
bit $3c3c, x ; 3c
eor ($12) ; 52
phy ; 5a
stz $04 ; 64
adc ($12) ; 72
stz $34, x ; 74
ply ; 7a
jmp ($7c7c, x) ; 7c
bra * + 2 ; 80
bit #$89 ; 89
sta ($12) ; 92
stz $0c0c ; 9c
stz $3c3c, x ; 9e
lda ($12) ; b2
cmp ($12) ; d2
phx ; da
sbc ($12) ; f2
plx ; fa

View File

@ -0,0 +1,76 @@
;ACME 0.97
; extensions of CMOS re-design (65c02), but with Z reg of 65ce02:
tsb $04 ; 04
tsb $0c0c ; 0c
ora ($12), z ; 12 !
trb $04 ; 14
inc ; 1a
trb $0c0c ; 1c
and ($12), z ; 32 !
bit $34, x ; 34
dec ; 3a
bit $3c3c, x ; 3c
eor ($12), z ; 52 !
phy ; 5a
stz $04 ; 64
adc ($12), z ; 72 !
stz $34, x ; 74
ply ; 7a
jmp ($7c7c, x) ; 7c
bra * + 2 ; 80
bit #$89 ; 89
sta ($12), z ; 92 !
stz $0c0c ; 9c
stz $3c3c, x ; 9e
lda ($12), z ; b2 !
cmp ($12), z ; d2 !
phx ; da
sbc ($12), z ; f2 !
plx ; fa
; extensions of 65ce02:
cle ; 02
see ; 03
tsy ; 0b
lbpl * + 2 ; 13
inz ; 1b
jsr ($2222) ; 22
jsr ($2323, x) ; 23
tys ; 2b
lbmi * + 2 ; 33
dez ; 3b
neg ; 42
asr ; 43
asr $44 ; 44
taz ; 4b
lbvc * + 2 ; 53
asr $54, x ; 54
tab ; 5b
; aug ; 5c this is different between 65ce02 and 4502
rtn #$62 ; 62
bsr * + 2 ; 63
tza ; 6b
lbvs * + 2 ; 73
tba ; 7b
sta ($82, s), y ; 82
lbra * + 2 ; 83
sty $8b8b, x ; 8b
lbcc * + 2 ; 93
stx $9b9b, y ; 9b
ldz #$62 ; a3
ldz $abab ; ab
lbcs * + 2 ; b3
ldz $8b8b, x ; bb
cpz #$62 ; c2
dew $44 ; c3
asw $abab ; cb
lbne * + 2 ; d3
cpz $44 ; d4
phz ; db
cpz $abab ; dc
lda ($82, s), y ; e2
inw $44 ; e3
row $abab ; eb
lbeq * + 2 ; f3
phw #$f4f4 ; f4
plz ; fb
phw $abab ; fc

View File

@ -0,0 +1,34 @@
;ACME 0.97
; bit-manipulation instructions:
rmb0 $07 ; 07
bbr0 $0f, * + 3 ; 0f
rmb1 $07 ; 17
bbr1 $0f, * + 3 ; 1f
rmb2 $07 ; 27
bbr2 $0f, * + 3 ; 2f
rmb3 $07 ; 37
bbr3 $0f, * + 3 ; 3f
rmb4 $07 ; 47
bbr4 $0f, * + 3 ; 4f
rmb5 $07 ; 57
bbr5 $0f, * + 3 ; 5f
rmb6 $07 ; 67
bbr6 $0f, * + 3 ; 6f
rmb7 $07 ; 77
bbr7 $0f, * + 3 ; 7f
smb0 $07 ; 87
bbs0 $0f, * + 3 ; 8f
smb1 $07 ; 97
bbs1 $0f, * + 3 ; 9f
smb2 $07 ; a7
bbs2 $0f, * + 3 ; af
smb3 $07 ; b7
bbs3 $0f, * + 3 ; bf
smb4 $07 ; c7
bbs4 $0f, * + 3 ; cf
smb5 $07 ; d7
bbs5 $0f, * + 3 ; df
smb6 $07 ; e7
bbs6 $0f, * + 3 ; ef
smb7 $07 ; f7
bbs7 $0f, * + 3 ; ff

View File

@ -0,0 +1,82 @@
;ACME 0.97
; undocumented opcodes of NMOS 6502:
jam ; 02
slo ($03, x) ; 03
dop $04 ; 04
nop $04 ; 04
slo $04 ; 07
!ifndef C64DTV2 {
anc #$0b ; 0b (dtv does not support this)
}
top ; 0c
top $0c0f ; 0c
nop $0c0f ; 0c
slo $0c0f ; 0f
slo ($13), y ; 13
dop $14, x ; 14
nop $14, x ; 14
slo $14, x ; 17
slo $1b1b, y ; 1b
top $1c1f, x ; 1c
nop $1c1f, x ; 1c
slo $1c1f, x ; 1f
rla ($03, x) ; 23
rla $04 ; 27
rla $0c0f ; 2f
rla ($13), y ; 33
rla $14, x ; 37
rla $1b1b, y ; 3b
rla $1c1f, x ; 3f
sre ($03, x) ; 43
sre $04 ; 47
alr #$0b ; 4b
asr #$0b ; 4b
sre $0c0f ; 4f
sre ($13), y ; 53
sre $14, x ; 57
sre $1b1b, y ; 5b
sre $1c1f, x ; 5f
rra ($03, x) ; 63
rra $04 ; 67
arr #$0b ; 6b
rra $0c0f ; 6f
rra ($13), y ; 73
rra $14, x ; 77
rra $1b1b, y ; 7b
rra $1c1f, x ; 7f
dop ; 80
dop #$0b ; 80
nop #$0b ; 80
sax ($03, x) ; 83
sax $04 ; 87
ane #0 ; 8b (anything non-zero gives a warning)
sax $0c0f ; 8f
sha ($13), y ; 93
sax $97, y ; 97
tas $1b1b, y ; 9b
shy $1c1f, x ; 9c
shx $1b1b, y ; 9e
sha $1b1b, y ; 9f
lax ($03, x) ; a3
lax $04 ; a7
lxa #0 ; ab (anything non-zero gives a warning)
lax $0c0f ; af
lax ($13), y ; b3
lax $97, y ; b7
las $1b1b, y ; bb
lax $1b1b, y ; bf
dcp ($03, x) ; c3
dcp $04 ; c7
sbx #$0b ; cb
dcp $0c0f ; cf
dcp ($13), y ; d3
dcp $14, x ; d7
dcp $1b1b, y ; db
dcp $1c1f, x ; df
isc ($03, x) ; e3
isc $04 ; e7
isc $0c0f ; ef
isc ($13), y ; f3
isc $14, x ; f7
isc $1b1b, y ; fb
isc $1c1f, x ; ff

12
testing/cpus/test-4502.a Normal file
View File

@ -0,0 +1,12 @@
;ACME 0.97
!cpu 4502
!to "out-4502.o", plain
*=$1000
!src "include-6502.a"
; !src "include-65c02.a" ; not used, because 65ce02 changes "(zp)" to "(zp), z"
!src "include-bitmanips.a"
!src "include-65ce02.a"
; this differs between 65ce02 and 4502:
map ; $5c
; this is an alias for NOP:
eom ; $ea, "end of mapping"

5
testing/cpus/test-6502.a Normal file
View File

@ -0,0 +1,5 @@
;ACME 0.97
!cpu 6502
!to "out-6502.o", plain
*=$1000
!src "include-6502.a"

118
testing/cpus/test-65816.a Normal file
View File

@ -0,0 +1,118 @@
;ACME 0.97
!cpu 65816
!to "out-65816.o", plain
*=$1000
!src "include-6502.a"
!src "include-65c02.a"
; !src "include-bitmanips.a" 65816 does not support these
; new instructions of 65816:
cop $02 ; 02
ora $03, s ; 03
ora [$07] ; 07
phd ; 0b
ora $0f0f0f ; 0f
ora ($13, s), y ; 13
ora [$17], y ; 17
tcs ; 1b
ora $1f1f1f, x ; 1f
jsr $0f0f0f ; 22
and $03, s ; 23
and [$07] ; 27
pld ; 2b
and $0f0f0f ; 2f
and ($13, s), y ; 33
and [$17], y ; 37
tsc ; 3b
and $1f1f1f, x ; 3f
wdm ; 42
eor $03, s ; 43
mvp $44, $54 ; 44
eor [$07] ; 47
phk ; 4b
eor $0f0f0f ; 4f
eor ($13, s), y ; 53
mvn $44, $54 ; 54
eor [$17], y ; 57
tcd ; 5b
jmp $0f0f0f ; 5c
eor $1f1f1f, x ; 5f
per * + 3 ; 62
adc $03, s ; 63
adc [$07] ; 67
rtl ; 6b
adc $0f0f0f ; 6f
adc ($13, s), y ; 73
adc [$17], y ; 77
tdc ; 7b
adc $1f1f1f, x ; 7f
brl * + 3 ; 82
sta $03, s ; 83
sta [$07] ; 87
phb ; 8b
sta $0f0f0f ; 8f
sta ($13, s), y ; 93
sta [$17], y ; 97
txy ; 9b
sta $1f1f1f, x ; 9f
lda $03, s ; a3
lda [$07] ; a7
plb ; ab
lda $0f0f0f ; af
lda ($13, s), y ; b3
lda [$17], y ; b7
tyx ; bb
lda $1f1f1f, x ; bf
rep #$c2 ; c2, see below
cmp $03, s ; c3
cmp [$07] ; c7
wai ; cb
cmp $0f0f0f ; cf
cmp ($13, s), y ; d3
pei ($d4) ; d4
cmp [$17], y ; d7
stp ; db
jmp [$dcdc] ; dc
cmp $1f1f1f, x ; df
sep #$e2 ; e2, see below
sbc $03, s ; e3
sbc [$07] ; e7
xba ; eb
sbc $0f0f0f ; ef
sbc ($13, s), y ; f3
pea $f4f4 ; f4
sbc [$17], y ; f7
xce ; fb
jsr ($fcfc, x) ; fc
sbc $1f1f1f, x ; ff
; check sizes of immediate arguments:
!macro immediates {
; arg size depends on:
; from 6502:
ora #$09 ; 09 accumulator size
and #$09 ; 29 accumulator size
eor #$09 ; 49 accumulator size
adc #$09 ; 69 accumulator size
ldy #$09 ; a0 index register size
ldx #$09 ; a2 index register size
lda #$09 ; a9 accumulator size
cpy #$09 ; c0 index register size
cmp #$09 ; c9 accumulator size
cpx #$09 ; e0 index register size
sbc #$09 ; e9 accumulator size
; from 65c02:
bit #$89 ; 89 accumulator size
; from 65816:
rep #$c2 ; c2 always 8 bits
sep #$e2 ; e2 always 8 bits
}
; before this, all sizes were 8 bits
!al
+immediates ; now repeat immediates with long accumulator
!as
!rl
+immediates ; repeat immediates with short A and long index regs
!al
!rl
+immediates ; repeat immediates with long A and long index regs

View File

@ -0,0 +1,6 @@
;ACME 0.97
!cpu 65c02
!to "out-65c02.o", plain
*=$1000
!src "include-6502.a"
!src "include-65c02.a"

View File

@ -0,0 +1,10 @@
;ACME 0.97
!cpu 65ce02
!to "out-65ce02.o", plain
*=$1000
!src "include-6502.a"
; !src "include-65c02.a" ; not used, because 65ce02 changes "(zp)" to "(zp), z"
!src "include-bitmanips.a"
!src "include-65ce02.a"
; this differs between 65ce02 and 4502:
aug ; $5c ("4-byte NOP reserved for future expansion")

View File

@ -0,0 +1,11 @@
;ACME 0.97
!cpu c64dtv2
!to "out-c64dtv2.o", plain
*=$1000
!src "include-6502.a"
C64DTV2 = 1 ; make next include skip the ANC instruction (not supported by C64DTV2)
!src "include-undoc.a"
; additional instructions of C64DTV2:
bra * + 2 ; 12, branch always
sac #$32 ; 32, set accumulator mapping
sir #$42 ; 42, set index register mapping

137
testing/cpus/test-m65.a Normal file
View File

@ -0,0 +1,137 @@
;ACME 0.97
!cpu m65
!to "out-m65.o", plain
*=$1000
M65 = 1 ; make next include skip the NOP mnemonic (re-used as prefix code by M65)
!src "include-6502.a"
; !src "include-65c02.a" ; not used, because 65ce02 changes "(zp)" to "(zp), z"
!src "include-bitmanips.a"
!src "include-65ce02.a"
; this differs between 65ce02 and 4502:
map ; $5c
; this is an alias for NOP:
eom ; $ea, "end of mapping"
; "quad mode" m65 extension using NEG:NEG prefix:
; (instructions that are commented out might be re-purposed later)
;orq ($01, x) ; 01
orq $05 ; 05
aslq $05 ; 06
aslq ; 0a
orq $0d0e ; 0d
aslq $0d0e ; 0e
;orq ($11), y ; 11
orq ($12) ; 12
;orq $15, x ; 15
aslq $15, x ; 16
;orq $1919, y ; 19
inq ; 1a
;orq $1d1e, x ; 1d
aslq $1d1e, x ; 1e
;andq ($01, x) ; 21
bitq $05 ; 24
andq $05 ; 25
rolq $05 ; 26
rolq ; 2a
bitq $0d0e ; 2c
andq $0d0e ; 2d
rolq $0d0e ; 2e
;andq ($11), y ; 31
andq ($12) ; 32
;bitq $15, x ; 34
;andq $15, x ; 35
rolq $15, x ; 36
;andq $1919, y ; 39
deq ; 3a
;bitq $1d1e, x ; 3c
;andq $1d1e, x ; 3d
rolq $1d1e, x ; 3e
;eorq ($01, x) ; 41
asrq ; 43
asrq $05 ; 44
eorq $05 ; 45
lsrq $05 ; 46
lsrq ; 4a
eorq $0d0e ; 4d
lsrq $0d0e ; 4e
;eorq ($11), y ; 51
eorq ($12) ; 52
asrq $15, x ; 54
;eorq $15, x ; 55
lsrq $15, x ; 56
;eorq $1919, y ; 59
;eorq $1d1e, x ; 5d
lsrq $1d1e, x ; 5e
;adcq ($01, x) ; 61
adcq $05 ; 65
rorq $05 ; 66
rorq ; 6a
adcq $0d0e ; 6d
rorq $0d0e ; 6e
;adcq ($11), y ; 71
adcq ($12) ; 72
;adcq $15, x ; 75
rorq $15, x ; 76
;adcq $1919, y ; 79
;adcq $1d1e, x ; 7d
rorq $1d1e, x ; 7e
;stq ($01, x) ; 81
;stq ($82, s), y ; 82
stq $05 ; 85
stq $0d0e ; 8d
;stq ($11), y ; 91
stq ($12) ; 92
;stq $15, x ; 95
;stq $1919, y ; 99
;stq $1d1e, x ; 9d
;ldq ($01, x) ; a1
ldq $05 ; a5
ldq $0d0e ; ad
ldq ($11), y ; b1
ldq ($12) ; b2
ldq $15, x ; b5
ldq $1919, y ; b9
ldq $1d1e, x ; bd
;cpq ($01, x) ; c1
cpq $05 ; c5
deq $05 ; c6
cpq $0d0e ; cd
deq $0d0e ; ce
;cpq ($11), y ; d1
cpq ($12) ; d2
;cpq $15, x ; d5
deq $15, x ; d6
;cpq $1919, y ; d9
;cpq $1d1e, x ; dd
deq $1d1e, x ; de
;sbcq ($01, x) ; e1
ldq ($e2, s), y ; e2
sbcq $05 ; e5
inq $05 ; e6
sbcq $0d0e ; ed
inq $0d0e ; ee
;sbcq ($11), y ; f1
sbcq ($12) ; f2
;sbcq $15, x ; f5
inq $15, x ; f6
;sbcq $1919, y ; f9
;sbcq $1d1e, x ; fd
inq $1d1e, x ; fe
; "long mode" m65 extension using NOP prefix:
ora [$12], z ; 12
and [$12], z ; 32
eor [$12], z ; 52
adc [$12], z ; 72
sta [$12], z ; 92
lda [$12], z ; b2
cmp [$12], z ; d2
sbc [$12], z ; f2
; "quad mode" and "long mode" combined using NEG:NEG:NOP prefix:
orq [$12] ; 12
andq [$12] ; 32
eorq [$12] ; 52
adcq [$12] ; 72
stq [$12] ; 92
ldq [$12] ; b2
cpq [$12] ; d2
sbcq [$12] ; f2

View File

@ -0,0 +1,6 @@
;ACME 0.97
!cpu nmos6502
!to "out-nmos6502.o", plain
*=$1000
!src "include-6502.a"
!src "include-undoc.a"

View File

@ -0,0 +1,7 @@
;ACME 0.97
!cpu r65c02
!to "out-r65c02.o", plain
*=$1000
!src "include-6502.a"
!src "include-65c02.a"
!src "include-bitmanips.a"

View File

@ -0,0 +1,10 @@
;ACME 0.97
!cpu w65c02
!to "out-w65c02.o", plain
*=$1000
!src "include-6502.a"
!src "include-65c02.a"
!src "include-bitmanips.a"
; WDC extensions:
wai ; cb
stp ; db

View File

@ -0,0 +1,3 @@
;ACME 0.97
a = "somestring"
a = "someotherstring" ; -> "already defined" (value has changed)

View File

@ -0,0 +1,14 @@
;ACME 0.97
*=$1000
; these must throw errors:
!by -129, 256
!wo -32769, 65536
!24 -0x800001, 16777216
; 32-bit values are not range checked atm:
!32 -0x80000001, 0x100000000
; these must work:
!by -128, 255
!wo -32768, 65535
!24 -0x800000, 16777215
!32 -0x80000000, 0xffffffff

View File

@ -0,0 +1,4 @@
;ACME 0.97
a = %........ ; eight digits
a = %.......
a = %.........