Compare commits

...

210 Commits

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
marcobaye
d2683cc64d Release 0.97: Now with string symbols, lists, backslash escaping,
"unpseudopc" operator, MEGA65 support, !while, else if, and a CLI switch to
mimic older versions.
Make sure to read "docs/Changes.txt" and "docs/Upgrade.txt"!


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@266 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-28 18:56:55 +00:00
marcobaye
ca6b6d8771 rewritten docs/Upgrade.txt
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@265 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-28 12:18:35 +00:00
marcobaye
7038cecfec fixed error in docs/Errors.txt
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@264 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-28 09:24:56 +00:00
marcobaye
8f432f6aa7 docs only
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@263 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-28 00:30:43 +00:00
marcobaye
9d637eee25 comments only, no change in functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@262 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-28 00:05:35 +00:00
marcobaye
c85081cbe4 allowed # characters in MVP/MVN syntax
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@261 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-26 10:45:20 +00:00
marcobaye
296ecefa6c explained more errors in docs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@260 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-23 15:30:49 +00:00
marcobaye
a5851cd51a removed a few lines that are not really needed
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@259 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-22 22:24:28 +00:00
marcobaye
6c869568cd amended last change, now depends on v0.97
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@258 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-22 20:40:38 +00:00
marcobaye
aa51fde056 Added warning about unusual number of digits in binary literals,
and a CLI switch to disable that warning.


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@257 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-22 20:32:38 +00:00
marcobaye
9f5ac5b212 fixed buffer overrun when printing long ints on 64bit machines
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@256 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-22 13:39:27 +00:00
marcobaye
1441da12ac removed useless code concerning FITS_BYTE flag
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@255 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-22 11:09:43 +00:00
marcobaye
cf167a34e4 added alternative expected output for trigonometric example
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@254 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-22 11:08:53 +00:00
marcobaye
be72f71faa simplified state machine of expression parser
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@253 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-21 22:30:06 +00:00
marcobaye
89344d34ee added more auto-conversions from 1-char-strings to integer
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@252 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-21 19:51:55 +00:00
marcobaye
1261960cad adjusted docs (added !WHILE, ELSE IF, etc)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@251 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-21 19:06:12 +00:00
marcobaye
7f736ceccb added "nmos6502" as an alias for "6510"
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@250 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-21 17:24:38 +00:00
marcobaye
3039db71e6 made sure stuff like LDA#"X" keeps working in future version
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@249 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-21 16:19:05 +00:00
marcobaye
b8679e7f06 added is_number(), is_list() and is_string() functions
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@248 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-21 15:25:37 +00:00
marcobaye
ceabdfb4a0 added backslash sequences \0, \t, \n and \r
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@247 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-21 15:21:27 +00:00
marcobaye
85f0c32ff4 fixed convert-to-lower-case function so keywords may contain underscore in future
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@246 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-21 15:16:38 +00:00
marcobaye
5b37c4d24e cleanup (moved functions between files; no change in functionality!)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@245 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-20 16:45:37 +00:00
marcobaye
beb875ff2b refactored (added a union)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@244 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-20 16:11:08 +00:00
marcobaye
562ce98f75 changing a list now needs !set
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@243 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-20 14:54:43 +00:00
marcobaye
fc913eefb6 adjusted docs to future syntax change
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@242 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-20 13:57:16 +00:00
marcobaye
28e196caab added <m65/std.a> to library, with macro to load 32-bit immediate constant
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@241 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-19 22:35:23 +00:00
marcobaye
c3e651f4ca pseudo opcodes like !by, !wo, !tx and friends now accept lists
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@240 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-18 23:38:24 +00:00
marcobaye
eca73fb335 simplified handling of special operators
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@239 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-18 10:15:53 +00:00
marcobaye
64aa52da35 fixed operator priority bug (see test program added in svn rev 232)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@238 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-17 00:37:19 +00:00
marcobaye
326f36fd4f refactoring, about to remove "try to reduce stacks" state
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@237 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-16 23:51:22 +00:00
marcobaye
c2978f7e15 "unpseudopc" operator & now also works with program counter *
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@236 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-16 23:12:44 +00:00
marcobaye
f9a2f5f698 got rid of DEFINED flag by making UNDEFINED its own number type
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@235 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-16 22:44:54 +00:00
marcobaye
929fefe0e6 added opcode table for NMOS6502 cpu
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@234 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-16 12:29:48 +00:00
marcobaye
b32320d5f3 comments only
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@233 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-16 10:46:07 +00:00
marcobaye
430b225208 added failing test program, fix will follow realsoonnow(tm)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@232 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-15 13:05:14 +00:00
marcobaye
f64780a3bd added typedef for "bits"
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@231 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-14 00:26:38 +00:00
marcobaye
2671eef384 added a bunch of "const"
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@230 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-13 23:18:51 +00:00
marcobaye
1199c75025 more test programs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@229 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-13 23:12:06 +00:00
marcobaye
4643e841f9 symbol assignment refactoring seems to be finished now
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@228 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-13 23:11:10 +00:00
marcobaye
395dcf55f3 fixed a dependency in Makefiles
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@227 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-13 22:42:14 +00:00
marcobaye
8bf6bcd6eb fixed bug introduced in svn rev 217: !for threw "too late for postfix" with wrong line number
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@226 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-13 09:32:21 +00:00
marcobaye
ec2b7515ca fixed bugs: trig functions did not clear FITS_BYTE flag, comparisons did not set it!
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@225 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-10 13:12:25 +00:00
marcobaye
7c82984075 added test source for expression parser
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@224 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-09 22:51:45 +00:00
marcobaye
da12925408 started checking in test sources
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@223 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-09 18:52:49 +00:00
marcobaye
0588f0fffe improved error handling of "--cpu" and "--format" switches. started putting m65 in docs.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@222 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-09 15:58:48 +00:00
marcobaye
1f74a6b8fd comments only
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@221 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-08 17:25:50 +00:00
marcobaye
a165279e88 changed symbol usage counter to "has been read" boolean
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@220 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-08 17:01:17 +00:00
marcobaye
93cea56d88 fixed bug introduced in svn rev 214 ("Too late for postfix" error)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@219 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-07 13:05:01 +00:00
marcobaye
b17203faa7 lists can now be concatenated using '+'
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@218 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-06 12:01:44 +00:00
marcobaye
77e945ce88 symbol assigment refactoring nearing completion...
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@217 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-05 15:15:12 +00:00
marcobaye
02a35ac468 more refactoring
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@216 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-05 09:51:10 +00:00
marcobaye
61d5144faa removed unused vars
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@215 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-05 08:57:09 +00:00
marcobaye
00d0462f74 refactored symbol assignment (unfinished)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@214 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-05 01:11:51 +00:00
marcobaye
72fc28e84c comments only
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@213 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-04 14:31:15 +00:00
marcobaye
9db5ad6fdb first step to refactor symbol assignments
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@212 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-03 16:30:13 +00:00
marcobaye
7a0f9f9528 disabled NOP mnemonic on m65 cpu
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@211 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-03 11:02:16 +00:00
marcobaye
5ea2a03174 removed warning about decimal mode from m65 cpu
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@210 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-03 00:51:09 +00:00
marcobaye
f164b737ad in 6510 mode, DOP/TOP can now also be written as NOP
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@209 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-02 15:09:36 +00:00
marcobaye
8c751f3552 removed 0.93 dialect, as it was buggy and not really useful
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@208 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-06-01 17:49:46 +00:00
marcobaye
8a3bdb265f added "--dialect" CLI switch to set which older version to mimic
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@207 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-31 20:55:38 +00:00
marcobaye
8e4857de4c can now mimic older versions, but still needs a CLI switch to select one
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@206 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-31 15:04:12 +00:00
marcobaye
c03d1145f6 made warning about "pointer at $ff" depend on cpu flag
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@205 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-31 13:07:40 +00:00
marcobaye
64a29e4504 amended docs on illegals
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@204 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-31 13:04:21 +00:00
marcobaye
47b1fab4fe more work to be able to mimic older versions
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@203 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-29 23:33:03 +00:00
marcobaye
9bbac556d3 worked on error messages
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@202 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-29 22:03:04 +00:00
marcobaye
af4a918f18 comments only
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@201 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-29 16:53:13 +00:00
marcobaye
5bcb80ac47 moved stuff around
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@200 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-29 14:30:03 +00:00
marcobaye
8169cf6e06 changed 6510 to nmos6502 internally
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@199 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-29 13:04:28 +00:00
marcobaye
8c8f425559 some renaming/cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@198 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-29 10:57:01 +00:00
marcobaye
2ad075911b enabled !while and new warning about parentheses
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@197 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-28 18:43:49 +00:00
marcobaye
dddf3f3d10 '&' operator (for un-pseudopc-ing) seems to be finished
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@196 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-28 18:40:40 +00:00
marcobaye
636080ce25 started work on '&' operator
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@195 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-27 20:25:42 +00:00
marcobaye
3c9f21cebd fixed date
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@194 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-27 20:06:11 +00:00
marcobaye
a6eae58032 internal cleanup for next change
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@193 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-27 17:32:48 +00:00
marcobaye
78e7c32507 strings now support "+", "=" and "!=" operators
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@192 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-26 12:55:14 +00:00
marcobaye
bc0cd5b8ea "!warn" and friends now support list and string symbols
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@191 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-25 23:12:19 +00:00
marcobaye
98ae73381d updated error messages
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@190 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-24 21:16:50 +00:00
marcobaye
64a4b336b0 small change so associativity of powerof operator can be made configurable later on
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@189 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-24 20:19:19 +00:00
marcobaye
d407faab1c removed old version of !if/else/!ifdef/!ifndef algo.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@188 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-24 19:52:10 +00:00
marcobaye
486febcef4 tiny refactoring, preparing to get rid of another fn
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@187 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-23 23:38:13 +00:00
marcobaye
0173eaf777 cleanup, now only output.c accesses CPU_state.pc
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@186 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-22 20:55:36 +00:00
marcobaye
dfca72688b support for m65 should be finished now
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@185 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-21 23:46:11 +00:00
marcobaye
8eddb4f4bc started suuport for MEGA65 cpu (not finished yet)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@184 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-21 19:36:59 +00:00
marcobaye
c1d8f90fae minor refactoring
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@183 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-20 16:39:19 +00:00
marcobaye
f5e7f23311 fix: values of quoted characters with msb set are now positive
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@182 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-19 21:04:57 +00:00
marcobaye
cd09855098 expression parser now knows about lists, strings, indexing and len().
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@181 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-19 20:41:12 +00:00
marcobaye
ba168a3ea5 added backslash escaping (to try it, use --test --test)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@180 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-19 17:35:04 +00:00
marcobaye
916bf9cbc8 part 4 of 4 of preparations for backslash escaping
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@179 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-19 16:28:36 +00:00
marcobaye
5b0989ac31 minor refactoring, moved a function
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@178 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-19 14:33:51 +00:00
marcobaye
768c80219b part 3 of 4 of preparations for backslash escaping
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@177 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-19 14:16:13 +00:00
marcobaye
101c04e413 more refactoring to prepare for backslash escaping
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@176 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-19 13:04:39 +00:00
marcobaye
beaf86da5b refactored a bit to prepare for backslash escaping
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@175 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-19 11:09:46 +00:00
marcobaye
3448cda3df bugfix: unterminated strings could cause crashes
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@174 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-19 09:41:54 +00:00
marcobaye
2fbbc0324e added comments for next change
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@173 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-18 23:47:15 +00:00
marcobaye
c64ffcfced fix for previous commit
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@172 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-18 22:50:11 +00:00
marcobaye
688c00f31b changed a bool to an enum
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@171 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-18 22:10:01 +00:00
marcobaye
4eb3ffa149 "Garbage data at end of statement" message now includes the unexpected character.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@170 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-18 21:51:43 +00:00
marcobaye
c07f373b53 added code for indexing (commented out atm)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@169 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-15 15:33:00 +00:00
marcobaye
665c579470 more work on special operators
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@168 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-15 13:46:17 +00:00
marcobaye
5ba17ccfc4 added error output for "unsupported operation"
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@167 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-15 11:39:00 +00:00
marcobaye
4e0d82ac69 added "len" operator, still disabled
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@166 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-14 17:36:09 +00:00
marcobaye
ccaf576e8a added text fields to operator and type structs (for better error messages later on), also renamed some stuff
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@165 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-14 14:01:30 +00:00
marcobaye
ff11dec18c tiny cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@164 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-14 10:35:54 +00:00
marcobaye
7c732fca59 renamed stuff
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@163 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-14 10:19:21 +00:00
marcobaye
c055688355 small fix in docs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@162 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-14 10:04:39 +00:00
marcobaye
a7ec38bff5 changed operator passing from enum to struct ptr, no change in functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@161 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-14 00:04:20 +00:00
marcobaye
fd2ac55392 new priority values, no change in functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@160 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-13 23:53:03 +00:00
marcobaye
365306428b added warnings about binary/octal/hex numbers without any digits, will be error in future! also renamed a function.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@159 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-13 23:45:03 +00:00
marcobaye
40afd3311a cleaned up expression handler, it's now able to handle more than just ints/floats
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@158 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-13 23:26:40 +00:00
marcobaye
3300c9d468 added some TODO/FIXME comments
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@157 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-13 16:53:52 +00:00
marcobaye
dac11ba8c7 finished ELSE IF functionality. currently needs "--test", will be enabled in next release.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@156 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-12 15:08:24 +00:00
marcobaye
16fb63deda planned ELSE IF functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@155 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-12 10:51:37 +00:00
marcobaye
20e04e22bc fix for previous commit: do not complain about () inside []
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@154 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-11 12:42:47 +00:00
marcobaye
4c938480fc added warning about parentheses for mnemonics without indirect addressing. currently needs "--test", will be enabled in next release.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@153 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-11 11:13:06 +00:00
marcobaye
abdea30e33 fixed behaviour of NOT operator concerning type checking: address refs are now negated.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@152 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-10 22:01:04 +00:00
marcobaye
e908284773 refactored handler function for "float with dyadic operator"
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@151 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-10 21:35:50 +00:00
marcobaye
f7c52d747c split handler function for dyadic operators into int and float functions. refactored int, still need to do float.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@150 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-10 12:33:41 +00:00
marcobaye
75bd395f2f more renaming
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@149 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-09 23:16:27 +00:00
marcobaye
440dc697ad split handler function for monadic operators into int and float functions
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@148 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-09 23:01:51 +00:00
marcobaye
4cad44f3ec renamed a bunch of stuff
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@147 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-09 20:58:08 +00:00
marcobaye
2b1c9d06bd refactored expression parser (more to come)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@146 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-09 19:26:40 +00:00
marcobaye
55f303c05e minor refactoring
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@145 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-08 17:42:10 +00:00
marcobaye
c1f62fdef5 step 3 to fix ALU_* calls
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@144 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-08 13:22:15 +00:00
marcobaye
4b81e40c63 minor cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@143 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-08 12:22:07 +00:00
marcobaye
38952534f4 cleanup concerning pc assignments
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@142 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-08 09:43:52 +00:00
marcobaye
7d4200faa4 step 2 to fix ALU_* calls
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@141 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-08 00:34:46 +00:00
marcobaye
60603c7350 step 1 to fix ALU_* calls
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@140 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-07 23:32:06 +00:00
marcobaye
a4943e1f40 more cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@139 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-06 12:27:32 +00:00
marcobaye
bc0efebb3e cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@138 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-06 11:40:06 +00:00
marcobaye
2c104118eb now "--test" enables "!while {}"
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@137 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-06 10:00:32 +00:00
marcobaye
5c459cad56 added "!while" pseudo opcode (will be enabled in next release)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@136 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-05 22:56:11 +00:00
marcobaye
4565070849 improved "Value not defined" output: Now all undefined symbols of expression
are reported, but never more than once.


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@135 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-02 21:59:20 +00:00
marcobaye
ef3cbbe340 fixed float macros in library to make sure they work with future versions of ACME
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@134 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-02 15:38:02 +00:00
marcobaye
d1ac849272 still more cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@133 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-02 14:58:17 +00:00
marcobaye
54defa1add cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@132 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-02 11:28:15 +00:00
marcobaye
e3e68af762 more cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@131 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-02 10:40:10 +00:00
marcobaye
7286e00855 typedef'd boolean to make source more readable. added "--test" option
(unused atm). no change in functionality.


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@130 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-01 21:01:23 +00:00
marcobaye
bf074b830d just added comments for future cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@129 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-04-30 16:34:09 +00:00
marcobaye
669f95c238 renamed a bunch of stuff, no change in functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@128 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-04-28 16:02:09 +00:00
marcobaye
0a4c13bb2e internal cleanup, no change in functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@127 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-04-28 11:18:22 +00:00
marcobaye
c4a88fa738 internal cleanup: instead of "value exists" flag, there is now a "expression is empty" field
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@126 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-04-26 22:26:05 +00:00
marcobaye
68b4409b1c internal cleanup: moved "indirect" flag from result to expression struct
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@125 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-04-26 20:14:39 +00:00
marcobaye
41f9534b99 more internal cleanup
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@124 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-04-26 18:53:14 +00:00
marcobaye
a4afd81f42 some internal cleanup concerning empty expressions
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@123 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-04-26 16:24:34 +00:00
marcobaye
c6f443d581 minor fix for output of segment list. no other change in functionality, only
internal cleanup (added some comments and TODOs)


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@122 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-04-25 10:20:52 +00:00
marcobaye
2ad798bef2 ACME release 0.96.5: Added "//" comments, added "--ignore-zeroes" and "--strict-segments" CLI switches.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@121 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-04-14 00:28:31 +00:00
marcobaye
63d26a9c82 ACME_Lib: various minor edits
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@120 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-02-28 20:40:18 +00:00
hoeppie
f22907849c Add win32 ico and properties
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@119 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-12-23 01:36:24 +00:00
hoeppie
06e2612a5b Update creating (c) year in win32 file, depends now on version.h
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@118 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-12-23 00:28:25 +00:00
marcobaye
07a7a00b1a ACME_Lib: reformatted ted.a and add 264/petscii.a
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@117 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-08-05 23:39:15 +00:00
marcobaye
c68cefcab0 ACME_Lib: minor tweaks
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@116 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-07-29 14:24:33 +00:00
marcobaye
a57a7b6c68 ACME_Lib: added a directory for the 264 series
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@115 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-07-28 23:33:43 +00:00
marcobaye
81f2d3f683 ACME_Lib: a bit more info on CIAs and VIC
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@114 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-07-28 22:16:51 +00:00
marcobaye
8fc1d4c738 updated toacme: v0.15 new removes leading '+' signs when converting
sources from "Professional Assembler".


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@113 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-04-25 20:26:10 +00:00
marcobaye
e1744c0008 added #define to allow to make segments warnings into errors (no change in
functionality)


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@112 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-04-20 21:12:05 +00:00
marcobaye
238eb5d626 tiny comment in lib
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@111 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-04-09 08:09:20 +00:00
marcobaye
cefa89c138 added 65ce02 and 4502 opcode tables to docs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@110 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-03-19 00:37:46 +00:00
marcobaye
c4487b9239 added r65c02 and w65c02 opcode tables to docs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@109 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-03-18 22:24:47 +00:00
marcobaye
c262cbe7b4 added 65816 opcode table to docs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@108 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-03-18 21:56:19 +00:00
marcobaye
6604877b54 added 65c02 opcode table to docs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@107 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-03-18 21:26:38 +00:00
marcobaye
aa611df8b7 added 6502 opcode table to docs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@106 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-03-18 21:13:41 +00:00
marcobaye
75d52177cc fixed typo
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@105 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-02-01 11:28:39 +00:00
marcobaye
99d866e2e8 Some small changes in source, no change in functionality.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@104 4df02467-bbd4-4a76-a152-e7ce94205b78
2019-02-01 11:23:28 +00:00
hoeppie
fb81d868d6 Update from 2015 to 2018
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@103 4df02467-bbd4-4a76-a152-e7ce94205b78
2018-11-16 15:26:07 +00:00
marcobaye
b31e6c4e70 ACME_Lib: changed macro parameters to cheap locals (there should be no change in functionality)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@102 4df02467-bbd4-4a76-a152-e7ce94205b78
2018-08-05 15:33:15 +00:00
marcobaye
6ff2e95da8 ACME_Lib: edited c64/float.a so register usage is mentioned consistently.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@101 4df02467-bbd4-4a76-a152-e7ce94205b78
2018-07-05 16:59:37 +00:00
marcobaye
a123239d2d ACME_Library: added ref to ioerror.a file.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@100 4df02467-bbd4-4a76-a152-e7ce94205b78
2018-06-30 21:05:16 +00:00
marcobaye
b3d818da39 ACME_Lib: added some more comments
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@99 4df02467-bbd4-4a76-a152-e7ce94205b78
2018-06-30 20:59:42 +00:00
marcobaye
bc68e36127 ACME_Lib: changed "kernel" to "kernal" in various comments
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@98 4df02467-bbd4-4a76-a152-e7ce94205b78
2018-01-29 15:59:20 +00:00
marcobaye
9628f69f4e ACME release 0.96.4: Fixed bug in zero page wrap-around warnings. Added "!xor" pseudo op. Added "-I" CLI switch.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@97 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-12-22 22:55:36 +00:00
marcobaye
f23a38de8c fixed native mode interrupt vectors in library file <65816/std.a>
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@96 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-11-03 17:48:49 +00:00
marcobaye
3867147615 Fixed docs to include two error messages. Fixed a typo, no change in functionality.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@95 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-11-03 15:14:55 +00:00
marcobaye
7cb100c480 ACME release 0.96.3: Added "!hex" and "!skip" pseudoops. Added cheap locals.
Added CLI switch to change pseudoop prefix to '.'
Fixed a bug in expression parser and added a warning.


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@94 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-10-29 23:29:07 +00:00
marcobaye
e1683b1e28 more minor comment changes
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@93 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-10-21 20:23:22 +00:00
marcobaye
47f09e9804 a bit of internal cleanup of names and comments, no change in functionality
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@92 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-10-21 19:59:56 +00:00
marcobaye
a6daa92e63 tiny change in docs
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@91 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-10-21 19:01:48 +00:00
marcobaye
071778e2c1 Library: added some comments and an alias for "k_plot"
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@90 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-10-21 18:41:09 +00:00
marcobaye
a5e984f158 comments only
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@89 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-10-16 17:01:44 +00:00
hoeppie
53a8e9ab79 Typo in setRelease.sh fixed
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@88 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-06-17 23:04:59 +00:00
marcobaye
bb484d683e updated toacme to include undocumented opcode "shx $1234,y"
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@87 4df02467-bbd4-4a76-a152-e7ce94205b78
2017-03-21 20:44:12 +00:00
151 changed files with 9721 additions and 3672 deletions

45
ACME_Lib/6502/split.a Normal file
View File

@ -0,0 +1,45 @@
;ACME 0.97
!set split_cache = [] ; start every pass with an empty cache
!ifdef lib_6502_split_a !eof
lib_6502_split_a = 1
!macro split_lo @args {
+split_putbytes 0, @args
!set split_cache = split_cache + @args
}
!macro split_hi @args {
+split_putbytes 8, @args
!set split_cache = split_cache + @args
}
!macro split_lo {
+split_putbytes 0, split_cache
!set split_cache = []
}
!macro split_hi {
+split_putbytes 8, split_cache
!set split_cache = []
}
!macro split_putbytes @shift, @bytes {
!if len(@bytes) {
!for @idx, 0, len(@bytes) - 1 {
!by (@bytes[@idx] >> @shift) & $ff
}
}
}
!eof
; these macros can be used to split address tables in two separate
; parts for high- and low-bytes. example:
table_hi
+split_hi [$0123, $4567, $89ab, $cdef] ; writes $01, $45, $89, $cd
+split_hi [$1122, $3344, $5566, $7788] ; writes $11, $33, $55, $77
table_lo
+split_lo ; writes $23, $67, $ab, $ef, $22, $44, $66, $88 from cache
; of course you can also put the low bytes first:
table_lo
+split_lo [$0123, $4567, $89ab, $cdef] ; writes $23, $67, $ab, $ef
+split_lo [$1122, $3344, $5566, $7788] ; writes $22, $44, $66, $88
table_hi
+split_hi ; writes $01, $45, $89, $cd, $11, $33, $55, $77 from cache

View File

@ -1,4 +1,4 @@
;ACME 0.95
;ACME 0.96.4
!ifdef lib_6502_std_a !eof
lib_6502_std_a = 1
@ -22,57 +22,57 @@ lib_6502_std_a = 1
}
; increase 16-bit counter
!macro inc16 .t {
inc .t
!macro inc16 @t {
inc @t
bne +
inc .t + 1
inc @t + 1
+
}
; far branches
!macro bcc .t {
!macro bcc @t {
bcs +
jmp .t
jmp @t
+
}
!macro bcs .t {
!macro bcs @t {
bcc +
jmp .t
jmp @t
+
}
!macro beq .t {
!macro beq @t {
bne +
jmp .t
jmp @t
+
}
!macro bne .t {
!macro bne @t {
beq +
jmp .t
jmp @t
+
}
!macro bmi .t {
!macro bmi @t {
bpl +
jmp .t
jmp @t
+
}
!macro bpl .t {
!macro bpl @t {
bmi +
jmp .t
jmp @t
+
}
!macro bvc .t {
!macro bvc @t {
bvs +
jmp .t
jmp @t
+
}
!macro bvs .t {
!macro bvs @t {
bvc +
jmp .t
jmp @t
+
}

View File

@ -1,7 +1,7 @@
;ACME 0.95
;ACME 0.96.4
!ifdef lib_65816_std_a !eof
lib_65816_std_a = 1
lib_65816_std_a = 2
; Labels and macros for Western Digital's 65c816 processor
@ -12,11 +12,12 @@ lib_65816_std_a = 1
cpu_e_reset = $fffc
cpu_e_irq = $fffe
cpu_n_cop = $fff4
cpu_n_brk = $fff6
cpu_n_abort = $fff8
cpu_n_nmi = $fffa
cpu_n_irq = $fffe
cpu_n_cop = $ffe4
cpu_n_brk = $ffe6
cpu_n_abort = $ffe8
cpu_n_nmi = $ffea
; no reset vector for native mode because reset always enters emulation mode
cpu_n_irq = $ffee
}
!macro cpu_emu {; switch to emulation mode
@ -71,11 +72,11 @@ lib_65816_std_a = 1
+i8
}
!macro inc24 .t {; increase 24-bit counter
inc .t
!macro inc24 @t {; increase 24-bit counter
inc @t
bne +
inc .t + 1
inc @t + 1
bne +
inc .t + 2
inc @t + 2
+
}

6
ACME_Lib/cbm/264/basic.a Normal file
View File

@ -0,0 +1,6 @@
;ACME 0.96.4
!ifdef lib_cbm_264_basic_a !eof
lib_cbm_264_basic_a = 1
!source <cbm/basic3.5.a>

View File

@ -0,0 +1,46 @@
;ACME 0.96.4
!ifdef lib_cbm_264_petscii_a !eof
lib_cbm_264_petscii_a = 1
!source <cbm/petscii.a>
; the sixteen predefined color/luma combinations available via petscii:
petscii_BLACK = 144 ; $00 here's the actual bits given to TED
petscii_WHITE = 5 ; $71 (first digit is luma bits,
petscii_RED = 28 ; $32 second digit is color nibble)
petscii_CYAN = 159 ; $63
petscii_PURPLE = 156 ; $44
petscii_GREEN = 30 ; $35
petscii_BLUE = 31 ; $46
petscii_YELLOW = 158 ; $77
petscii_ORANGE = 129 ; $48
petscii_BROWN = 149 ; $29
petscii_YELLOWGREEN = 150 ; $5a
petscii_PINK = 151 ; $6b
petscii_BLUEGREEN = 152 ; $5c
petscii_LIGHTBLUE = 153 ; $6d
petscii_DARKBLUE = 154 ; $2e
petscii_LIGHTGREEN = 155 ; $5f
; switching character set (same as C64)
petscii_LOCK = 8 ; forbid CBM-shift (C128 uses 11 instead)
petscii_UNLOCK = 9 ; allow CBM-shift (C128 uses 12 instead)
petscii_LOWERCASE = 14 ; switch to lowercase/uppercase character set
petscii_UPPERCASE = 142 ; switch to uppercase/graphics character set
; 264-specific stuff
petscii_FLASHON = 130 ; (C128 uses 15 instead)
petscii_FLASHOFF = 131 ; (C128 uses 143 instead)
; function keys
petscii_F1 = 133
petscii_F3 = 134
petscii_F5 = 135
petscii_F7 = 136
petscii_F2 = 137
petscii_F4 = 138
petscii_F6 = 139
petscii_HELP = 140 ; (C128 uses 132 instead)
petscii_ESCAPE = 27

215
ACME_Lib/cbm/264/ted.a Normal file
View File

@ -0,0 +1,215 @@
;ACME 0.96.4
!ifdef lib_cbm_264_ted_a !eof
lib_cbm_264_ted_a = 1
; Text Display (TED) 7360/8360
; Definitions:
; HiRes: High resolution (320x200 pixels, two colors per 8x8 block)
; MCM: Multi-color mode (160x200 pixels, four colors per 4x8 block)
; Text mode: Pixel data is taken from charset, index/color/luminance is
; taken from video ram.
; Bitmap mode: Pixel data is taken from 8 KiB bitmap, color/luminance is taken
; from video ram.
; ECM: Extended color mode. Like HiRes text mode, but instead of 256
; different characters on a shared background color, 64 different
; characters on four different background colors are displayed.
; MC text mode is actually a mixed mode: each character position has its own
; bit to decide whether it is displayed in HiRes or MC.
; info taken from
; http://mclauchlan.site.net.au/scott/C=Hacking/C-Hacking12/gfx.html (Document Revision B)
; http://www.zimmers.net/anonftp/pub/cbm/maps/C16.MemoryMap
; Video RAM is $0800 bytes
; in text mode:
; first $0400 bytes is color/luminance info:
; %7....... flash (ignored in MCM and ECM)
; %.654.... luminance
; %....3... MCM: multicolor flag
; %.....210 MCM: color
; %....3210 HiRes and ECM: color
; second $0400 bytes hold screen codes:
; %76543210 HiRes and MCM: screen code (in HiRes, bit 7 can
; be configured to be "hardware reverse",
; see register $ff07)
; %76...... ECM: background color index (see registers
; $ff15..$ff18)
; %..543210 ECM: screen code
; in bitmap mode:
; CAUTION!
; LOW nibble of luma corresponds to HIGH nibble of color and vice versa!
; first $0400 bytes is luminance info:
; %7....... unused
; %.654.... luminance of %0 HiRes pixels or %01 MC pixels
; %....3... unused
; %.....210 luminance of %1 HiRes pixels or %10 MC pixels
; second $0400 bytes is color info:
; %7654.... color of %1 pixels or %10 MC pixels
; %....3210 color of %0 pixels or %01 MC pixels
; colors:
tedcolor_BLACK = $0 ; ignores luminance, black is always black!
tedcolor_WHITE = $1 ; actually gray, only max luminance gives white
tedcolor_RED = $2
tedcolor_CYAN = $3
tedcolor_PURPLE = $4
tedcolor_GREEN = $5
tedcolor_BLUE = $6
tedcolor_YELLOW = $7 ; needs high luminance to look yellow...
; these cannot be used as main color in MCM:
tedcolor_ORANGE = $8
tedcolor_BROWN = $9
tedcolor_YELLOWGREEN = $a
tedcolor_PINK = $b
tedcolor_BLUEGREEN = $c
tedcolor_LIGHTBLUE = $d
tedcolor_DARKBLUE = $e
tedcolor_LIGHTGREEN = $f
; the color names are somewhat useless, because they refer not to the sixteen
; base colors, but to the sixteen pre-defined luminance/color combinations
; available by pressing the ctrl or cbm key with a digit key (see the ROM table
; at $e143 for these combined values).
; for a "nice" color display, use this order: 2,8,9,7,a,f,5,c,3,d,6,e,4,b,
; that will give a red/yellow/green/blue/purple spectrum.
; to display the whole palette, try this program:
; 10 for i = 0 to 15:color 1, 1
; 20 read c$:print " " c$ " ";
; 30 poke 194, 128:for lu = 0 to 7
; 40 color 1, dec(c$) + 1, lu
; 50 print " ";
; 60 next:print:next:color 1, 1
; 70 data 0,1,2,8,9,7,a,f
; 80 data 5,c,3,d,6,e,4,b
; TED registers
!addr ted_base = $ff00
; timers
ted_timer1_lo = ted_base + $00 ; decrements from the last value written into it
ted_timer1_hi = ted_base + $01
ted_timer2_lo = ted_base + $02 ; runs freely from $ffff
ted_timer2_hi = ted_base + $03
ted_timer3_lo = ted_base + $04 ; runs freely from $ffff
ted_timer3_hi = ted_base + $05
; mode bits
ted_vertctrl = ted_base + $06 ; mostly the same as VIC's $d011
tedvert_TEST = %#....... ; internal test, it should be 0
tedvert_EBCM = %.#...... ; extended background color mode
tedvert_BITMAP = %..#..... ; bitmap mode
tedvert_ENABLE = %...#.... ; display enable (0: blank screen)
tedvert_25ROWS = %....#... ; 0: 24 rows, 1: 25 rows
tedvert_SMOOTH = %.....### ; vertical smooth scroll (std: %011, higher values move text down)
ted_horzctrl = ted_base + $07 ; most similar VIC-reg is $d016
tedhorz_256CHRS = %#....... ; 0: 128 characters and hardware reverse, 1: 256 characters
tedhorz_NTSC = %.#...... ; 0: PAL, 1: NTSC
tedhorz_FREEZE = %..#..... ; if set, TED stops its counters and screen-generating, only single clock and refresh cycles remain.
tedhorz_MULTI = %...#.... ; multicolor mode
tedhorz_40COLS = %....#... ; 0: 38 columns, 1: 40 columns
tedhorz_SMOOTH = %.....### ; horizontal smooth scroll (std: %000, higher values move text right)
; i/o
ted_keyboard = ted_base + $08 ; keyboard input latch (write to trigger sampling, then read input byte)
; to scan keyboard, write output byte to $fd30, then write $ff here (to disable joysticks) and read input byte.
; to scan joysticks, write $ff to $fd30 (to disable keyboard), then write SELECT value here and read input byte.
tedjoy1_SELECT = %#####.## ; d2 low selects joy #1
tedjoy2_SELECT = %######.# ; d1 low selects joy #2
; CAUTION, the joystick bits are low-active, so invert input byte before using these:
tedjoy2_FIRE = %#.......
tedjoy1_FIRE = %.#......
tedjoy_RIGHT = %....#...
tedjoy_LEFT = %.....#..
tedjoy_DOWN = %......#.
tedjoy_UP = %.......#
; interrupts
ted_irq = ted_base + $09 ; interrupt request register
; if bit is set, interrupt has occurred.
; to acknowledge interrupts, write back the value read from this register
; (setting bits clears them, i.e. writing $82 clears ACTIVE and RASTER
; but does not change the COUNTER bits)
tedirq_ACTIVE = %#....... ; 1: TED is signalling interrupt to cpu
tedirq_COUNTER3 = %.#...... ; 1: counter #3 underflow
; %..#..... ; always 1 (unused)
tedirq_COUNTER2 = %...#.... ; 1: counter #2 underflow
tedirq_COUNTER1 = %....#... ; 1: counter #1 underflow
; %.....#.. ; always 1 (light pen, not implemented)
tedirq_RASTER = %......#. ; 1: raster line reached
; %.......# ; always 1 (unused)
ted_mask = ted_base + $0a ; interrupt mask register (enable/disable)
; if bit is set, interrupt is enabled.
; use the tedirq_* constants from above.
; %#....... ; always 1 (unused)
;COUNTER3 = %.#...... ; 1: enable counter #3 underflow interrupt
; = %..#..... ; always 1 (unused)
;COUNTER2 = %...#.... ; 1: enable counter #2 underflow interrupt
;COUNTER1 = %....#... ; 1: enable counter #1 underflow interrupt
; = %.....#.. ; always 1 (light pen, not implemented)
;RASTER = %......#. ; 1: enable raster line interrupt
tedmask_LINEBIT8= %.......# ; bit 8 of raster line register $ff0b (see below)
ted_line = ted_base + $0b ; raster line to trigger interrupt. bit 8 is in bit 0 of $ff0a (see above)
; hardware cursor, given as screen offset (0...999). use 1000 or greater to hide.
ted_hwcursor_hi = ted_base + $0c ; %765432.. are unused, %......10 are MSBs of hw cursor offset
ted_hwcursor_lo = ted_base + $0d ; hardware cursor offset (low byte)
; sound
; to calculate the register values:
; NTSC: reg = 1024 - (111860.781 / frequency_in_Hz)
; PAL: reg = 1024 - (111840.45 / frequency_in_Hz)
ted_sound1_low = ted_base + $0e ; low 8 bits of voice #1 frequency
ted_sound2_low = ted_base + $0f ; low 8 bits of voice #2 frequency
ted_sound2_hi = ted_base + $10 ; bits 7..2 are unused, bits 1..0 are the highmost 2 bits of voice #2 frequency
ted_sound_ctrl = ted_base + $11 ; sound control register
tedsound_DA = %#....... ; D/A mode (sound reload)
tedsound_NOISE2 = %.#...... ; voice #2 noise on/off
tedsound_SQUARE2= %..#..... ; voice #2 square wave on/off (if you set both, the square will sound)
tedsound_ONOFF1 = %...#.... ; voice #1 on/off
tedsound_VOLUME = %....#### ; volume, maximum value is 8
ted_misc = ted_base + $12 ; some memory and some sound bits:
; = %##...... ; unused
tedmisc_BITMAP = %..###... ; a15/a14/a13 of bitmap in memory
tedmisc_ROMCHAR = %.....#.. ; 0: charset is in RAM, 1: charset is in ROM
tedmisc_SOUNDMSB= %......## ; highmost bits of voice #1 frequency
ted_mem = ted_base + $13 ;
tedmem_CHARGEN = %######.. ; high address bits of character generator
tedmem_SCLOCK = %......#. ; 1: force single clock mode
tedmem_ROM = %.......# ; read-only: ROM is active over $8000
ted_videoram = ted_base + $14 ; bits 7..3 are the high address bits of video ram
; color registers
; %7....... unused
; %.654.... luminance
; %....3210 color
ted_background = ted_base + $15 ; color for %00 pixels (or %0 in HiRes)
ted_color1 = ted_base + $16 ; color for %01 pixels (MCM and ECM)
ted_color2 = ted_base + $17 ; color for %10 pixels (MCM and ECM)
ted_color3 = ted_base + $18 ; color for %11 pixels (MCM graphics and ECM)
ted_border = ted_base + $19
; current character position
ted_charpos_hi = ted_base + $1a ; bits 1..0 are highmost bits
ted_charpos_lo = ted_base + $1b ; low bits of actual character position
; current raster position (writable!)
ted_vertical_hi = ted_base + $1c ; bit 0 is msb of raster line
ted_vertical_lo = ted_base + $1d ; low 8 bits of raster line
ted_horizontal = ted_base + $1e ; horizontal position (given in double pixels)
ted_count = ted_base + $1f
; = %#....... ; always 1 (unused)
tedcount_FLASH = %.####... ; flashing counter
tedcount_CHAR = %.....### ; raster line in character row (r/w!)
; pseudo registers
; these are not really registers inside TED, but here they are, anyway:
!addr ted_enable_roms = $ff3e ; any write here enables ROMs above $8000
!addr ted_enable_rams = $ff3f ; any write here enables RAM above $8000

View File

@ -1,4 +1,4 @@
;ACME 0.94.4
;ACME 0.96.4
!ifdef lib_cbm_basic10_a !eof
lib_cbm_basic10_a = 1
@ -18,12 +18,20 @@ token_DIR = $ee ; was called DIRECTORY in basic v7
!macro b_DIR {!by token_DIR} ; aka DIRECTORY
; STASH/FETCH/SWAP are all decoded to DMA:
!macro b_DMA {!by $fe, $1f}
!macro b_DMA {!by $fe, $1f} ; command, length, source, target, subcommand
; extended token $fe $20 isn't used ($20 is ' ')
;!macro b_DMA {!by $fe, $21}
; extended token $fe $22 isn't used ($20 is '"')
;!macro b_DMA {!by $fe, $23}
; renamed function:
!macro b_LPEN {!by $ce, $04:!pet '('} ; was called PEN in basic7
; basic7 defines functions up to $ce, $0a (POINTER)
; new functions:
;!macro b_ {!by $ce, $0b:!pet '('} ; ?
!macro b_PIXEL {!by $ce, $0c:!pet '('} ; return pixel color
!macro b_RPALETTE {!by $ce, $0d:!pet '('} ; return palette color
; new instructions:
!macro b_TYPE {!by $fe, $27} ; display sequential disk file
!macro b_BVERIFY {!by $fe, $28}
@ -51,3 +59,8 @@ token_DIR = $ee ; was called DIRECTORY in basic v7
!macro b_MOUSE {!by $fe, $3e} ; set mouse parameters
!macro b_RMOUSE {!by $fe, $3f} ; read mouse position
!macro b_DISK {!by $fe, $40} ; send disc command
!macro b_CURSOR {!by $fe, $41} ; place text cursor?
;!macro b_ {!by $fe, $42} ; ?
!macro b_LOADIFF {!by $fe, $43} ; load IFF graphics
!macro b_SAVEIFF {!by $fe, $44} ; save IFF graphics
!macro b_EDIT {!by $fe, $45} ; switch between program and text editing

View File

@ -9,26 +9,28 @@ lib_cbm_c128_kernal_a = 1
!address {
k_spin_spout = $ff47
k_close_all = $ff4a
k_c64mode = $ff4d
k_c64mode = $ff4d ; enter c64 mode (so does not return)
k_dma_call = $ff50
k_boot_call = $ff53
k_phoenix = $ff56
k_lkupla = $ff59
k_lkupsa = $ff5c
k_lkupla = $ff59 ; find channel with file number A
k_lkupsa = $ff5c ; find channel with secondary address Y
k_swapper = $ff5f
k_dlchr = $ff62
k_pfkey = $ff65
k_setbnk = $ff68
k_setbnk = $ff68 ; set banks for file name and load/save calls
k_getcfg = $ff6b
k_jsrfar = $ff6e
k_jmpfar = $ff71
k_indfet = $ff74
k_indsta = $ff77
k_indcmp = $ff7a
k_primm = $ff7d
k_primm = $ff7d ; "print immediate" - output string without having to setup a pointer:
; string must follow JSR $ff7d in memory, code execution will resume after terminating zero.
; A/X/Y are preserved
k_release_number = $ff80
}
!source <cbm/kernal.a> ; $ff81-$fff5 is backward compatible to older machines
!source <cbm/kernal.a> ; $ff81-$fff5 is mostly compatible to older machines
; $fff6/$fff7 are unused (ff ff)
!address {
k_indirect128mode = $fff8 ; indirect vector, without JMP command!

View File

@ -12,10 +12,10 @@ lib_cbm_c128_mmu_a = 2
; configuration register
mmu_cr_d500 = $d500 ; same as "mmu_cr" at $ff00. Use "mmu_cr" instead, as that is *always* available.
; preconfiguration registers (internal format just like mmu_cr)
mmu_pcr_a = $d501 ; c128 kernel default is $3f (BANK 0)
mmu_pcr_b = $d502 ; c128 kernel default is $7f (BANK 1)
mmu_pcr_c = $d503 ; c128 kernel default is $01 (BANK 14)
mmu_pcr_d = $d504 ; c128 kernel default is $41 (all system roms, with ram 1)
mmu_pcr_a = $d501 ; c128 kernal default is $3f (BANK 0)
mmu_pcr_b = $d502 ; c128 kernal default is $7f (BANK 1)
mmu_pcr_c = $d503 ; c128 kernal default is $01 (BANK 14)
mmu_pcr_d = $d504 ; c128 kernal default is $41 (all system roms, with ram 1)
}
; contents of cr and all four pcr:
mmu_CR_RAMBANK_MASK = %##...... ; this controls which RAM bank is used in areas where RAM is enabled
@ -25,7 +25,7 @@ mmu_CR_RAMBANK_2 = %#....... ; on an unmodified c128, there is no ram bank 2 (0
mmu_CR_RAMBANK_3 = %##...... ; on an unmodified c128, there is no ram bank 3 (1 will be used instead)
mmu_CR_HIGH_MASK = %..##.... ; this controls the "high area" (c000..ffff), but i/o (d000..dfff) is separate
;mmu_CR_HIGH_SYSROM = %........ ; editor, charset (or i/o, see below), kernel
;mmu_CR_HIGH_SYSROM = %........ ; editor, charset (or i/o, see below), kernal
mmu_CR_HIGH_INTFUNCROM = %...#....
mmu_CR_HIGH_EXTFUNCROM = %..#.....
mmu_CR_HIGH_RAM = %..##....
@ -57,10 +57,10 @@ mmu_CR_BANK8 = $2a ; 32 KiB bank 0; 32 KiB EFROM with i/o overlay
mmu_CR_BANK9 = $6a ; 32 KiB bank 1; 32 KiB EFROM with i/o overlay
mmu_CR_BANK10 = $aa ; 32 KiB bank 2; 32 KiB EFROM with i/o overlay
mmu_CR_BANK11 = $ea ; 32 KiB bank 3; 32 KiB EFROM with i/o overlay
mmu_CR_BANK12 = $06 ; 32 KiB bank 0; 16 KiB IFROM; 16 KiB kernel with i/o overlay
mmu_CR_BANK13 = $0a ; 32 KiB bank 0; 16 KiB EFROM; 16 KiB kernel with i/o overlay
mmu_CR_BANK14 = $01 ; 16 KiB bank 0; 32 KiB basic; 16 KiB kernel with font overlay
mmu_CR_BANK15 = $00 ; 16 KiB bank 0; 32 KiB basic; 16 KiB kernel with i/o overlay
mmu_CR_BANK12 = $06 ; 32 KiB bank 0; 16 KiB IFROM; 16 KiB kernal with i/o overlay
mmu_CR_BANK13 = $0a ; 32 KiB bank 0; 16 KiB EFROM; 16 KiB kernal with i/o overlay
mmu_CR_BANK14 = $01 ; 16 KiB bank 0; 32 KiB basic; 16 KiB kernal with font overlay
mmu_CR_BANK15 = $00 ; 16 KiB bank 0; 32 KiB basic; 16 KiB kernal with i/o overlay
; An unmodified C128 does not have a "ram bank 2" or "ram bank 3".
; Whenever one of these is activated, ram banks 0 and 1 will be used instead.
; IFROM means internal function ROM (socket U36)
@ -71,13 +71,13 @@ mmu_CR_BANK15 = $00 ; 16 KiB bank 0; 32 KiB basic; 16 KiB kernel with i/o overla
mmu_mcr = $d505
}
; contents:
mmu_MCR_40COLUMNS = %#....... ; 40/80 key: 0 means pressed, 1 means released (writable! if cleared, will always read as 0!)
mmu_MCR_C64MODE = %.#...... ; setting this bit makes the MMU disappear from the memory map :)
mmu_MCR_EXROM = %..#..... ; if zero on boot, system will enter c64 mode (writable!)
mmu_MCR_GAME = %...#.... ; if zero on boot, system will enter c64 mode (writable!)
mmu_MCR_FSDIR_OUTPUT = %....#... ; direction of fast serial bus
mmu_MCR_40COLUMNS = %#....... ; (pin 48) 40/80 key: 0 means pressed, 1 means released (writable! if cleared, will always read as 0!)
mmu_MCR_C64MODE = %.#...... ; (pin 47) setting this bit makes the MMU disappear from the memory map :)
mmu_MCR_EXROM = %..#..... ; (pin 46) if zero on boot, system will enter c64 mode (writable!)
mmu_MCR_GAME = %...#.... ; (pin 45) if zero on boot, system will enter c64 mode (writable!)
mmu_MCR_FSDIR_OUTPUT = %....#... ; (pin 44) direction of fast serial bus
mmu_MCR_UNUSED = %.....##. ; always set
mmu_MCR_8502MODE = %.......# ; setting this to zero switches to Z80 cpu
mmu_MCR_8502MODE = %.......# ; (pin 43 inverted) setting this to zero switches to Z80 cpu
!address {
; ram configuration register
@ -148,10 +148,10 @@ mmu_VR_VERSION_MASK = %....#### ; mmu version 0
; load configuration registers:
; a read access will return the value of the corresponding preconfiguration register
; any write access will copy the value of the corresponding preconfiguration register to mmu_cr
mmu_lcr_a = $ff01 ; c128 kernel default is $3f (BANK 0)
mmu_lcr_b = $ff02 ; c128 kernel default is $7f (BANK 1)
mmu_lcr_c = $ff03 ; c128 kernel default is $01 (BANK 14)
mmu_lcr_d = $ff04 ; c128 kernel default is $41 (all system roms, with ram 1)
mmu_lcr_a = $ff01 ; c128 kernal default is $3f (BANK 0)
mmu_lcr_b = $ff02 ; c128 kernal default is $7f (BANK 1)
mmu_lcr_c = $ff03 ; c128 kernal default is $01 (BANK 14)
mmu_lcr_d = $ff04 ; c128 kernal default is $41 (all system roms, with ram 1)
; the c128 ROMs contain a look-up table to convert bank numbers to their
; corresponding configuration register values:

View File

@ -1,4 +1,4 @@
;ACME 0.94.4
;ACME 0.96.4
!ifdef lib_cbm_c128_petscii_a !eof
lib_cbm_c128_petscii_a = 1
@ -25,8 +25,8 @@ petscii_LBLUE = 154
petscii_GRAY3 = 155: petscii_DWHITE = 155: petscii_LGRAY = 155
; switching character set
petscii_LOCK = 11 ; forbid CBM-shift (C64 uses 8)
petscii_UNLOCK = 12 ; allow CBM-shift (C64 uses 9)
petscii_LOCK = 11 ; forbid CBM-shift (C64 uses 8 instead)
petscii_UNLOCK = 12 ; allow CBM-shift (C64 uses 9 instead)
petscii_LOWERCASE = 14 ; switch to lowercase/uppercase character set
petscii_UPPERCASE = 142 ; switch to uppercase/graphics character set
@ -39,12 +39,12 @@ petscii_F7 = 136: petscii_F8 = 140
; C128-specific stuff
petscii_UNDERLINEON = 2
petscii_UNDERLINEOFF = 130
petscii_FLASHON = 15
petscii_FLASHOFF = 143
petscii_FLASHON = 15 ; (264 series machines use 130 instead)
petscii_FLASHOFF = 143 ; (264 series machines use 131 instead)
petscii_BELL = 7
petscii_TAB = 9
petscii_SETTAB = 24
petscii_LINEFEED = 10
petscii_ESCAPE = 27
petscii_SHIFTSTOP = 131
petscii_HELP = 132
petscii_HELP = 132 ; (264 series machines use 140 instead)

View File

@ -1,19 +1,22 @@
;ACME 0.95
;ACME 0.96.4
!ifdef lib_cbm_c128_vic_a !eof
lib_cbm_c128_vic_a = 1
!source <cbm/c64/vic.a> ; registers 0..2e
!address {
; registers only present in the C128 variant of this chip:
vic_keyboard = $d02f
vic_clock = $d030
vic_keyboard = vic_base + $2f
; %76543... always set
; %.....210 output pins for extended keyboard layout
vic_clock = vic_base + $30
; %765432.. always set
; %......1. "test" bit: 0 = normal, 1 = system clock changes raster line
; %.......0 system clock: 0 = 1 MHz, 1 = 2 MHz (VIC bus cycles will be given to CPU instead)
; the c128 ROMs contain two copies of a look-up table to convert vic color
; values to their corresponding petscii color codes:
; rom4_* needs "low rom area" enabled ($4000..$7fff)
; romc_* needs "high rom area" enabled ($c000..$ffff)
rom4_vic_to_petscii_color_table = $76b5 ; 90 05 1c 9f 9c 1e 1f 9e 81 95 96 97 98 99 9a 9b
romc_vic_to_petscii_color_table = $ce4c ; 90 05 1c 9f 9c 1e 1f 9e 81 95 96 97 98 99 9a 9b
}
!addr rom4_vic_to_petscii_color_table = $76b5 ; 90 05 1c 9f 9c 1e 1f 9e 81 95 96 97 98 99 9a 9b
!addr romc_vic_to_petscii_color_table = $ce4c ; 90 05 1c 9f 9c 1e 1f 9e 81 95 96 97 98 99 9a 9b

View File

@ -1,23 +1,27 @@
;ACME 0.95
;ACME 0.96.4
!ifdef lib_cbm_c64_cia1_a !eof
lib_cbm_c64_cia1_a = 1
!address {
cia1_pra = $dc00
cia1_prb = $dc01
cia1_ddra = $dc02
cia1_ddrb = $dc03
cia1_ta_lo = $dc04
cia1_ta_hi = $dc05
cia1_tb_lo = $dc06
cia1_tb_hi = $dc07
cia1_tod10ths = $dc08
cia1_todsec = $dc09
cia1_todmin = $dc0a
cia1_todhr = $dc0b
cia1_sdr = $dc0c
cia1_icr = $dc0d
cia1_cra = $dc0e
cia1_crb = $dc0f
}
!source <cbm/cia.a> ; chip stuff (same for both cias)
; stuff for cia 1 only:
!addr cia1_base = $dc00
cia1_pra = cia1_base + cia_port_a ; PA0..PA4 are joy port 2 PA6+PA7 select paddle port(s)
cia1_prb = cia1_base + cia_port_b ; PB0..PB4 are joy port 1
; both ports are used for keyboard matrix
cia1_ddra = cia1_base + cia_data_direction_a
cia1_ddrb = cia1_base + cia_data_direction_b
cia1_ta_lo = cia1_base + cia_timer_a_low
cia1_ta_hi = cia1_base + cia_timer_a_high
cia1_tb_lo = cia1_base + cia_timer_b_low
cia1_tb_hi = cia1_base + cia_timer_b_high
cia1_tod10ths = cia1_base + cia_timeofday_10ths
cia1_todsec = cia1_base + cia_timeofday_seconds
cia1_todmin = cia1_base + cia_timeofday_minutes
cia1_todhr = cia1_base + cia_timeofday_hours
cia1_sdr = cia1_base + cia_serial_data
cia1_icr = cia1_base + cia_interrupt_control
cia1_cra = cia1_base + cia_control_a
cia1_crb = cia1_base + cia_control_b
; the interrupt output is connected to CPU's /IRQ input
; in the C128, the shift register is used for the fast serial port (burst mode)

View File

@ -1,23 +1,32 @@
;ACME 0.95
;ACME 0.96.4
!ifdef lib_cbm_c64_cia2_a !eof
lib_cbm_c64_cia2_a = 1
!address {
cia2_pra = $dd00
cia2_prb = $dd01
cia2_ddra = $dd02
cia2_ddrb = $dd03
cia2_ta_lo = $dd04
cia2_ta_hi = $dd05
cia2_tb_lo = $dd06
cia2_tb_hi = $dd07
cia2_tod10ths = $dd08
cia2_todsec = $dd09
cia2_todmin = $dd0a
cia2_todhr = $dd0b
cia2_sdr = $dd0c
cia2_icr = $dd0d
cia2_cra = $dd0e
cia2_crb = $dd0f
}
!source <cbm/cia.a> ; chip stuff (same for both cias)
; stuff for cia 2 only:
!addr cia2_base = $dd00
cia2_pra = cia2_base + cia_port_a
; %7....... DATA in (0 means GND)
; %.6...... CLK in (0 means GND)
; %..5..... DATA out (gets inverted, so 1 means GND)
; %...4.... CLK out (gets inverted, so 1 means GND)
; %....3... ATN out (gets inverted, so 1 means GND)
; %.....2.. PA2 (pin M at user port, 0 means GND)
; %......10 VIC bank (gets inverted, so value %11 means address $0000)
cia2_prb = cia2_base + cia_port_b
cia2_ddra = cia2_base + cia_data_direction_a
cia2_ddrb = cia2_base + cia_data_direction_b
cia2_ta_lo = cia2_base + cia_timer_a_low
cia2_ta_hi = cia2_base + cia_timer_a_high
cia2_tb_lo = cia2_base + cia_timer_b_low
cia2_tb_hi = cia2_base + cia_timer_b_high
cia2_tod10ths = cia2_base + cia_timeofday_10ths
cia2_todsec = cia2_base + cia_timeofday_seconds
cia2_todmin = cia2_base + cia_timeofday_minutes
cia2_todhr = cia2_base + cia_timeofday_hours
cia2_sdr = cia2_base + cia_serial_data
cia2_icr = cia2_base + cia_interrupt_control
cia2_cra = cia2_base + cia_control_a
cia2_crb = cia2_base + cia_control_b
; the interrupt output is connected to CPU's /NMI input

View File

@ -1,7 +1,7 @@
;ACME 0.95
;ACME 0.96.4
!ifdef lib_cbm_c64_float_a !eof
lib_cbm_c64_float_a = 1
lib_cbm_c64_float_a = 2
; Here are some definitions to help you call the floating-point functions of the
; C64's BASIC ROM. They work on "float registers", which are actually just
@ -18,33 +18,33 @@ lib_cbm_c64_float_a = 1
; convenience macros:
; some float functions need a memory address in A (low) and Y (high)
!macro movAY .adr {
lda #<.adr
ldy #>.adr
; some float functions need a memory address in YYAA
!macro movYYAA @addr {
lda #<@addr
ldy #>@addr
}
; ...or in X (low) and Y (high)
!macro movXY .adr {
ldx #<.adr
ldy #>.adr
; ...or in YYXX
!macro movYYXX @addr {
ldx #<@addr
ldy #>@addr
}
; other float functions expect or output a value in Y (low) and A (high)
!macro movYA .val {
ldy #<.val
lda #>.val
; other float functions expect or output a value in AAYY
!macro movAAYY @val {
ldy #<@val
lda #>@val
}
!macro ldYA .adr {
ldy .adr
lda .adr + 1
!macro ldAAYY @addr {
ldy @addr
lda @addr + 1
}
!macro stYA .adr {
sty .adr
sta .adr + 1
!macro stAAYY @addr {
sty @addr
sta @addr + 1
}
; ...or in X (low) and A (high)
!macro ldXA .adr {
ldx .adr
lda .adr + 1
; ...or in AAXX
!macro ldAAXX @addr {
ldx @addr
lda @addr + 1
}
!address {
@ -69,50 +69,71 @@ lib_cbm_c64_float_a = 1
; functions - a few points to note:
; fac1/2 might get clobbered even if not mentioned in the function's name,
; because stuff like fac1_times_memAY will load the value from memory
; because stuff like fac1_times_memYYAA will load the value from memory
; into fac2 first.
; for subtraction and division, the left operand is in fac2, the right operand in fac1.
fac1_print = $aabc ; print string representation of contents of fac1
fac1_to_signedYA = $b1aa ; might throw ILLEGAL QUANTITY
fac1_to_signedAAYY = $b1aa ; might throw ILLEGAL QUANTITY
fac1_to_signed16 = $b1bf ; might throw ILLEGAL QUANTITY
fac1_read_signedYA = $b391 ; convert 16 bit signed int to float
fac1_read_signedAAYY = $b391 ; convert 16 bit signed int to float
fac1_read_unsignedY = $b3a2 ; convert 8 bit unsigned int to float
fac1_read_string = $b7b5 ; $22/23 must point to string, A must be string length
fac1_to_unsignedYA = $b7f7 ; might throw ILLEGAL QUANTITY (result is also in $14/15)
fac1_to_unsignedAAYY = $b7f7 ; might throw ILLEGAL QUANTITY (result is also in $14/15)
fac1_add_point5 = $b849 ; for rounding, call this before fac1_int
fac1_memAY_minus_fac1 = $b850 ; subtract fac1 from mflpt value
fac1_memYYAA_minus_fac1 = $b850 ; subtract fac1 from mflpt value
fac1_fac2_minus_fac1 = $b853
fac1_add_memAY = $b867 ; add mflpt value
fac1_add_memYYAA = $b867 ; add mflpt value
fac1_add_fac2 = $b86a
fac1_log = $b9ea ; LOG()
fac1_times_memAY = $ba28 ; multiply by mflpt value
fac2_read_memAY = $ba8c ; load mflpt value from memory into fac2
fac1_times_memYYAA = $ba28 ; multiply by mflpt value
fac2_read_memYYAA = $ba8c ; load mflpt value from memory into fac2
fac2_read_mem_via0x22ptr = $ba90 ; load mflpt value from memory into fac2
fac1_times_10 = $bae2
fac1_divide_by_10 = $bafe ; CAUTION: result is always positive!
fac1_divide_memAY_by_fac1 = $bb0f ; divide mflpt value by fac1 value
fac1_read_memAY = $bba2 ; load mflpt value from memory into fac1
fac1_divide_memYYAA_by_fac1 = $bb0f ; divide mflpt value by fac1 value
fac1_read_memYYAA = $bba2 ; load mflpt value from memory into fac1
fac1_read_mem_via0x22ptr = $bba6 ; load mflpt value from memory into fac1
fac1_to_memXY = $bbd4 ; store fac1 to memory as mflpt
fac1_to_memYYXX = $bbd4 ; store fac1 to memory as mflpt
fac1_read_fac2 = $bbfc ; copy fac2 to fac1
fac2_read_fac1 = $bc0c ; copy fac1 to fac2
fac1_sign_to_A = $bc2b ; $ff, $0, $1 for negative, zero, positive
fac1_sgn = $bc39 ; SGN()
fac1_abs = $bc58 ; ABS()
fac1_compare_to_memAY = $bc5b ; compare to mflpt value in memory
fac1_compare_to_memYYAA = $bc5b ; compare to mflpt value in memory
fac1_to_signed32 = $bc9b
fac1_int = $bccc ; INT()
fac1_read_string0 = $bcf3 ; use b7b5 instead; this only works after calling CHRGET
fac1_print_unsignedXA = $bdcd
fac1_to_string = $bddd ; string is stored at $0100 (address returned in AY)
fac1_print_unsignedAAXX = $bdcd
fac1_to_string = $bddd ; string is stored at $0100 (address returned in YYAA)
fac1_sqr = $bf71 ; SQR()
fac1_fac2_to_the_power_of_memAY = $bf78
fac1_fac2_to_the_power_of_memYYAA = $bf78
fac1_negate = $bfb4
fac1_exp = $bfed ; EXP()
; end of basic rom jumps to start of kernel rom!
; end of basic rom jumps to start of kernal rom!
fac1_rnd = $e097 ; RND()
fac1_cos = $e264 ; COS()
fac1_sin = $e26b ; SIN()
fac1_tan = $e2b4 ; TAN()
fac1_atn = $e30e ; ATN()
}
; wrappers for names from older version of this file:
!macro movAY @addr { +movYYAA @addr }
!macro movXY @addr { +movYYXX @addr }
!macro movYA @val { +movAAYY @val }
!macro ldYA @addr { +ldAAYY @addr }
!macro stYA @addr { +stAAYY @addr }
!macro ldXA @addr { +ldAAXX @addr }
fac1_to_signedYA = fac1_to_signedAAYY
fac1_read_signedYA = fac1_read_signedAAYY
fac1_to_unsignedYA = fac1_to_unsignedAAYY
fac1_memAY_minus_fac1 = fac1_memYYAA_minus_fac1
fac1_add_memAY = fac1_add_memYYAA
fac1_times_memAY = fac1_times_memYYAA
fac2_read_memAY = fac2_read_memYYAA
fac1_divide_memAY_by_fac1 = fac1_divide_memYYAA_by_fac1
fac1_read_memAY = fac1_read_memYYAA
fac1_to_memXY = fac1_to_memYYXX
fac1_compare_to_memAY = fac1_compare_to_memYYAA
fac1_print_unsignedXA = fac1_print_unsignedAAXX
fac1_fac2_to_the_power_of_memAY = fac1_fac2_to_the_power_of_memYYAA

View File

@ -1,4 +1,4 @@
;ACME 0.95.7
;ACME 0.96.4
!ifdef lib_cbm_c64_memcopy_a !eof
lib_cbm_c64_memcopy_a = 1
@ -11,24 +11,23 @@ lib_cbm_c64_memcopy_a = 1
; higher addresses are copied first, so:
; - moving data to higher addresses works even if areas overlap
; - moving data to lower addresses only works if areas do not overlap
!macro basic_memcopy .src_start, .src_end, .target_start {
!address {
.z_target_end = $58
.z_src_end = $5a
.z_src_start = $5f
.fn = $a3bf
}
lda #<.src_start
ldx #>.src_start
sta .z_src_start
stx .z_src_start + 1
lda #<.src_end
ldx #>.src_end
sta .z_src_end
stx .z_src_end + 1
lda #<(.target_start + .src_end - .src_start)
ldx #>(.target_start + .src_end - .src_start)
sta .z_target_end
stx .z_target_end + 1
jsr .fn
!macro basic_memcopy @src_start, @src_end, @target_start {
!addr @z_target_end = $58
!addr @z_src_end = $5a
!addr @z_src_start = $5f
!addr @fn = $a3bf
lda #<@src_start
ldx #>@src_start
sta @z_src_start
stx @z_src_start + 1
lda #<@src_end
ldx #>@src_end
sta @z_src_end
stx @z_src_end + 1
lda #<(@target_start + @src_end - @src_start)
ldx #>(@target_start + @src_end - @src_start)
sta @z_target_end
stx @z_target_end + 1
jsr @fn
}

View File

@ -47,8 +47,8 @@ rec_COMMAND_FETCH = %#.#....# ; starting and then reload values.
; Upgraded units and clones may have more, but the REC chip will always
; "wrap around" after eight banks if crossing bank borders!
; amount of bytes to process
rec_amount_low = $df07
rec_amount_high = $df08
rec_amount_low = $df07 ; using $0000 results in
rec_amount_high = $df08 ; 64 KiB being transferred
; when to request interrupts
rec_irqctrl = $df09
}

View File

@ -1,8 +1,25 @@
;ACME 0.95
;ACME 0.96.4
!ifdef lib_cbm_c64_vic_a !eof
lib_cbm_c64_vic_a = 1
; This is from Christian Bauer's VIC text: {
; 6566: designed for static RAM, never used in C64/128
; 6567/8562: C64, NTSC
; 6569/8565: C64, PAL
; 6572: Drean C64, PAL-N
; 8564: C128, NTSC
; 8566: C128, PAL
; PAL chips generate 312 lines with 63 cycles per line.
; PAL-N chips generate 312 lines with 65 cycles per line.
; NTSC chips generate 263 lines with 65 cycles per line.
; Very early NTSC chips (6567R56A) have an off-by-one
; error, they generate 262 lines with 64 cycles per line.
; }
; The c128 version is officially called "VIC-IIe".
; It has two additional registers and more pins.
; color codes
viccolor_BLACK = $0
viccolor_WHITE = $1
@ -21,58 +38,85 @@ viccolor_LGREEN = $d
viccolor_LBLUE = $e
viccolor_GRAY3 = $f
!address {
; register addresses
vic_xs0 = $d000
vic_ys0 = $d001
vic_xs1 = $d002
vic_ys1 = $d003
vic_xs2 = $d004
vic_ys2 = $d005
vic_xs3 = $d006
vic_ys3 = $d007
vic_xs4 = $d008
vic_ys4 = $d009
vic_xs5 = $d00a
vic_ys5 = $d00b
vic_xs6 = $d00c
vic_ys6 = $d00d
vic_xs7 = $d00e
vic_ys7 = $d00f
vic_msb_xs = $d010
vic_controlv = $d011 ; vertical control (and much other stuff)
vic_line = $d012 ; raster line
vic_xlp = $d013 ; light pen coordinates
vic_ylp = $d014
vic_sactive = $d015 ; sprites: active
vic_controlh = $d016 ; horizontal control (and much other stuff)
vic_sdy = $d017 ; sprites: double height
vic_ram = $d018 ; RAM pointer
vic_irq = $d019
vic_irqmask = $d01a
vic_sback = $d01b ; sprites: background mode
vic_smc = $d01c ; sprites: multi color mode
vic_sdx = $d01d ; sprites: double width
vic_ss_collided = $d01e ; sprite-sprite collision detect
vic_sd_collided = $d01f ; sprite-data collision detect
; color registers
vic_cborder = $d020 ; border color
vic_cbg = $d021 ; general background color
vic_cbg0 = $d021
vic_cbg1 = $d022 ; background color 1 (for EBC and MC text mode)
vic_cbg2 = $d023 ; background color 2 (for EBC and MC text mode)
vic_cbg3 = $d024 ; background color 3 (for EBC mode)
vic_sc01 = $d025 ; sprite color for MC-bitpattern %01
vic_sc11 = $d026 ; sprite color for MC-bitpattern %11
vic_cs0 = $d027 ; sprite colors
vic_cs1 = $d028
vic_cs2 = $d029
vic_cs3 = $d02a
vic_cs4 = $d02b
vic_cs5 = $d02c
vic_cs6 = $d02d
vic_cs7 = $d02e
}
!addr vic_base = $d000
; sprite coordinates:
vic_xs0 = vic_base + $00
vic_ys0 = vic_base + $01
vic_xs1 = vic_base + $02
vic_ys1 = vic_base + $03
vic_xs2 = vic_base + $04
vic_ys2 = vic_base + $05
vic_xs3 = vic_base + $06
vic_ys3 = vic_base + $07
vic_xs4 = vic_base + $08
vic_ys4 = vic_base + $09
vic_xs5 = vic_base + $0a
vic_ys5 = vic_base + $0b
vic_xs6 = vic_base + $0c
vic_ys6 = vic_base + $0d
vic_xs7 = vic_base + $0e
vic_ys7 = vic_base + $0f
vic_msb_xs = vic_base + $10 ; bit 8 of x position (one bit per sprite)
vic_controlv = vic_base + $11 ; vertical control and other stuff:
; %7....... "bit 8" of $d012
; %.6...... extended background color mode (1=on)
; %..5..... 0 = text mode, 1 = bitmap mode
; %...4.... display enable: 0 = disable (border only), 1 = enable VIC only checks this once per frame!
; %....3... lines: 0 = 24, 1 = 25 ; if set, upper and lower border gain 4 pixels each
; %.....210 vertical smooth scroll (default %011), screen contents are moved down by this amount
vic_line = vic_base + $12 ; raster line (also see bit 7 of $d011) reading returns current value, writing sets interrupt value
vic_xlp = vic_base + $13 ; light pen coordinates, x (only half the resolution)
vic_ylp = vic_base + $14 ; light pen coordinates, y
vic_sactive = vic_base + $15 ; sprite enable (one bit per sprite)
vic_controlh = vic_base + $16 ; horizontal control and other stuff:
; %76...... always set
; %..5..... "test", should be 0
; %...4.... 0 = hires, 1 = multicolor
; %....3... columns: 0 = 38, 1 = 40 ; if set, left border gains 7 pixels and right border gains 9 pixels
; %.....210 horizontal smooth scroll (default %000), screen contents are moved to the right by this amount
vic_sdy = vic_base + $17 ; sprites, double height (one bit per sprite)
vic_ram = vic_base + $18 ; RAM pointer
; %7654.... which K of VIC bank is video ram (default %0001)
; %....321. text: which 2K of VIC bank is character set (default %010)
; %.......0 text: unused
; %....3... bitmap: which 8K of VIC bank is bitmap
; %.....210 bitmap: unused
vic_irq = vic_base + $19 ; writing back acknowledges!
; %7....... 1: VIC requested interrupt
; %.654.... always set
; %....3... 1: light pen active
; %.....2.. 1: sprite-sprite collision
; %......1. 1: sprite-data collision
; %.......0 1: raster interrupt
vic_irqmask = vic_base + $1a
; %7654.... always set
; %....3... 1: enable lightpen interrupt
; %.....2.. 1: enable sprite-sprite collision interrupt
; %......1. 1: enable sprite-data collision interrupt
; %.......0 1: enable raster interrupt
vic_sback = vic_base + $1b ; sprites, background (one bit per sprite)
vic_smc = vic_base + $1c ; sprites, multicolor (one bit per sprite)
vic_sdx = vic_base + $1d ; sprites, double width (one bit per sprite)
vic_ss_collided = vic_base + $1e ; sprites, sprite collision (one bit per sprite) reading clears register!
vic_sd_collided = vic_base + $1f ; sprites, data collision (one bit per sprite) reading clears register!
; color registers (high nibbles are always %1111):
vic_cborder = vic_base + $20 ; border color
vic_cbg = vic_base + $21 ; general background color
vic_cbg0 = vic_base + $21
vic_cbg1 = vic_base + $22 ; background color 1 (for EBC and MC text mode)
vic_cbg2 = vic_base + $23 ; background color 2 (for EBC and MC text mode)
vic_cbg3 = vic_base + $24 ; background color 3 (for EBC mode)
vic_sc01 = vic_base + $25 ; sprite color for MC-bitpattern %01
vic_sc11 = vic_base + $26 ; sprite color for MC-bitpattern %11
; individual sprite colors (in MC mode, these are used for bit pattern %10):
vic_cs0 = vic_base + $27 ; sprite 0 color
vic_cs1 = vic_base + $28 ; sprite 1 color
vic_cs2 = vic_base + $29 ; sprite 2 color
vic_cs3 = vic_base + $2a ; sprite 3 color
vic_cs4 = vic_base + $2b ; sprite 4 color
vic_cs5 = vic_base + $2c ; sprite 5 color
vic_cs6 = vic_base + $2d ; sprite 6 color
vic_cs7 = vic_base + $2e ; sprite 7 color
; See <cbm/c128/vic.a> for the C128's two additional registers at $d02f/$d030.
; They are accessible even in C64 mode and $d030 can garble the video output,
; so be careful not to write to it accidentally in a C64 program!

109
ACME_Lib/cbm/cia.a Normal file
View File

@ -0,0 +1,109 @@
;ACME 0.96.4
!ifdef lib_cbm_cia_a !eof
lib_cbm_cia_a = 1
; CBM's "complex interface adapter" chip, known as 6526.
; A newer version of this chip was initially called 8521, but later the name was
; changed back to 6526 again.
; There are two of these in a C64/128 computer, and one in 1570/1571 drives.
; pinout:
; ____ ____
; | V |
; GND | 1 40 | cnt clock for shift register
; / pa0 | 2 39 | sp data for shift register
; | pa1 | 3 38 | a0 \
; | pa2 | 4 37 | a1 |_ address
; Port _| pa3 | 5 36 | a2 | bus
; A | pa4 | 6 35 | a3 /
; | pa5 | 7 34 | /reset
; | pa6 | 8 33 | d0 \
; \ pa7 | 9 32 | d1 |
; / pb0 | 10 31 | d2 |
; | pb1 | 11 30 | d3 |_ data
; | pb2 | 12 29 | d4 | bus
; Port _| pb3 | 13 28 | d5 |
; B | pb4 | 14 27 | d6 |
; | pb5 | 15 26 | d7 /
; | pb6 | 16 25 | 0in clock
; \ pb7 | 17 24 | /flag
; /pc | 18 23 | /cs chip select
; tod_clk | 19 22 | r/w
; +5V | 20 21 | /irq
; |_________|
; register offsets:
; two 8-bit ports:
cia_port_a = $0
cia_port_b = $1
cia_data_direction_a = $2 ; clear means input,
cia_data_direction_b = $3 ; set means output
; two 16-bit timers, can be combined to form a single 32-bit timer:
cia_timer_a_low = $4
cia_timer_a_high = $5
cia_timer_b_low = $6
cia_timer_b_high = $7
; reading returns current counter value,
; writing sets start value.
; in 32-bit mode, timer A is "low word" and timer B is "high word".
; TOD (time of day) clock, clocked with 50 or 60 Hz:
; (CAUTION, registers use binary coded decimal format)
cia_timeofday_10ths = $8 ; %....3210 0..9, counts 10ths of seconds
cia_timeofday_seconds = $9 ; %.6543210 0..59, as two BCD digits:
; %.654.... 0..5 "tens"
; %....3210 0..9 "ones"
cia_timeofday_minutes = $a ; %.6543210 0..59, as two BCD digits:
; %.654.... 0..5 "tens"
; %....3210 0..9 "ones"
cia_timeofday_hours = $b ; %7..43210 AM/PM and 1..12 as two BCD digits:
; %7....... 0 means AM, 1 means PM
; %...4.... 0..1 "tens"
; %....3210 0..9 "ones"
; when reading or writing time, start with hours and end with 10ths:
; accessing hours uncouples registers from clock, accessing 10ths re-couples them.
; if your read access does not start with the hours register,
; you have a race condition!
; shift register:
; msb is sent/received first. send clock is half of timer A's underflow rate.
cia_serial_data = $c
; control registers:
cia_interrupt_control = $d
; %7....... read: 1 = interrupt requested (reading clears register)
; write: 0 = disable interrupts indicated by set bits
; 1 = enable interrupts indicated by set bits
; %.65..... unused
; %...4.... negative edge on /flag detected
; %....3... shift register has finished sending or receiving
; %.....2.. time of day alarm
; %......1. timer B underflow
; %.......0 timer A underflow
cia_control_a = $e
; %7....... TOD clock: 0 means 60 Hz, 1 means 50 Hz
; %.6...... shift register direction: 0 = input, 1 = output
; %..5..... timer A clock: 0 = system clock, 1 = CNT
; %...4.... 1 = force load timer start value
; %....3... timer mode: 0 = continuous, 1 = one-shot (needs restart via bit 0)
; %.....2.. 0 = pulse on PB6, 1 = invert PB6
; %......1. 1 = timer underflow shows on PB6 (this forces PB6 to output)
; %.......0 0 stops timer, 1 starts timer
cia_control_b = $f
; %7....... TOD write mode: 0 = actual time, 1 = alarm time (it is not possible to _read_ the alarm time!)
; %.65..... timer B clock:
; %00 = system clock
; %01 = CNT
; %10 = underflow of timer A
; %11 = combination of %01 and %10
; %...4.... 1 = force load timer start value
; %....3... timer mode: 0 = continuous, 1 = one-shot (needs restart via bit 0)
; %.....2.. 0 = pulse on PB7, 1 = invert PB7
; %......1. 1 = timer underflow shows on PB7 (this forces PB7 to output)
; %.......0 0 stops timer, 1 starts timer

View File

@ -1,4 +1,4 @@
;ACME 0.94.5
;ACME 0.96.4
!ifdef lib_cbm_flpt_a !eof
lib_cbm_flpt_a = 1
@ -46,48 +46,49 @@ lib_cbm_flpt_a = 1
; this is ugly, but it gets the job done
; (if it's stupid, but it works, then it's not stupid)
!macro flpt .value {
!set .float = float(.value) ; make sure to do passes until value is defined
!ifndef .float {
!by $ff, $ff, $ff, $ff, $ff, $ff ; six place holder bytes
!macro flpt @value {
!set @float = float(@value)
!ifndef @float {
!by <@float, $ff, $ff, $ff, $ff, $ff ; six place holder bytes
; (first one depends on @float just to make sure more passes are done until value is defined)
} else {
; value is defined, so split up into sign and non-negative value
!if .float < 0 {
!set .sign = $80
!set .float = -.float
!if @float < 0 {
!set @sign = $80
!set @float = -@float
} else {
!set .sign = $00
!set @sign = $00
}
!if .float = 0 {
!if @float = 0 {
!by 0, 0, 0, 0, 0, 0 ; six zeroes (zero is represented by all bits zero)
} else {
; split up into exponent and mantissa
!set .exponent = 128 + 32 ; 128 is cbm's bias, 32 is this algo's bias
!set @exponent = 128 + 32 ; 128 is cbm's bias, 32 is this algo's bias
; if mantissa is too large, shift right and adjust exponent
!do while .float >= (2.0 ^ 32.0) {
!set .float = .float >> 1
!set .exponent = .exponent + 1
!do while @float >= (2.0 ^ 32.0) {
!set @float = @float >> 1
!set @exponent = @exponent + 1
}
; if mantissa is too small, shift left and adjust exponent
!do while .float < (2.0 ^ 31.0) {
!set .float = .float << 1
!set .exponent = .exponent - 1
!do while @float < (2.0 ^ 31.0) {
!set @float = @float << 1
!set @exponent = @exponent - 1
}
!if .exponent < 1 {
!if @exponent < 1 {
!warn "FLPT underflow, using zero instead"
!set .float = 0
!set .exponent = 0
!set .sign = 0
!set @float = 0
!set @exponent = 0
!set @sign = 0
}
!if .exponent > 255 {
!if @exponent > 255 {
!error "FLPT overflow"
}
!by .exponent
!by 255 & int(.float >> 24)
!by 255 & int(.float >> 16)
!by 255 & int(.float >> 8)
!by 255 & int(.float)
!by .sign
!by @exponent
!by 255 & int(@float >> 24)
!by 255 & int(@float >> 16)
!by 255 & int(@float >> 8)
!by 255 & int(@float)
!by @sign
}
}
}

View File

@ -3,7 +3,7 @@
!ifdef lib_cbm_ioerror_a !eof
lib_cbm_ioerror_a = 1
; if kernel i/o routine exits with carry set, A holds one of these:
; if kernal i/o routine exits with carry set, A holds one of these:
ioerror_BREAK = 0
ioerror_TOO_MANY_FILES = 1
ioerror_FILE_OPEN = 2

View File

@ -8,8 +8,10 @@ lib_cbm_kernal_a = 1
; There are alternative names for some calls.
!address {
; for additional c128 calls, see <cbm/c128/kernal.a>
k_cint = $ff81
k_ioinit = $ff84
; cbm-ii rom starts here:
k_ramtas = $ff87
k_restor = $ff8a
k_vector = $ff8d
@ -27,24 +29,58 @@ lib_cbm_kernal_a = 1
k_listen = $ffb1:k_listn = $ffb1
k_talk = $ffb4
k_readss = $ffb7
k_setlfs = $ffba
k_setnam = $ffbd ; A is length, X is ptr-low, Y is ptr-high
k_open = $ffc0
k_close = $ffc3:k_close_A = $ffc3
k_chkin = $ffc6:k_chkin_X = $ffc6
k_chkout = $ffc9:k_chkout_X = $ffc9:k_ckout = $ffc9
k_clrchn = $ffcc:k_clrch = $ffcc
k_chrin = $ffcf:k_basin = $ffcf
k_chrout = $ffd2:k_basout = $ffd2:k_bsout = $ffd2
k_load = $ffd5:k_load_AXY = $ffd5 ; A means verify, YYXX is desired load address (if channel == 0), returns end+1 in YYXX
k_save = $ffd8:k_save_AXY = $ffd8 ; A is zp address of start ptr(!), YYXX is end address (+1)
k_settim = $ffdb
k_rdtim = $ffde
k_stop = $ffe1
k_getin = $ffe4:k_get = $ffe4
k_setlfs = $ffba ; set file parameters (A = logical file number, X = device, Y = secondary address)
k_setnam = $ffbd ; set file name (A = length, YYXX = pointer)
; pet rom starts here:
; i/o calls: these may set C on error. in that case, A holds error code, see <cbm/ioerror.a> for the actual values.
k_open = $ffc0 ; open channel/file (call setlfs/setnam before!)
k_close = $ffc3:k_close_A = $ffc3 ; close channel/file (A = logical file number)
k_chkin = $ffc6:k_chkin_X = $ffc6 ; set input channel (X = logical file number)
k_chkout = $ffc9:k_chkout_X = $ffc9:k_ckout = $ffc9 ; set output channel (X = logical file number)
k_clrchn = $ffcc:k_clrch = $ffcc ; restore default input/output channels
k_chrin = $ffcf:k_basin = $ffcf ; read byte from current input channel (not the same as $ffe4, see note* below)
; A is result byte
; X is preserved
; Y gets clobbered by tape access (preserved by disk access)
k_chrout = $ffd2:k_basout = $ffd2:k_bsout = $ffd2 ; send byte to current output channel
; A/X/Y are preserved
k_load = $ffd5:k_load_AXY = $ffd5 ; load file to memory, or verify (call setlfs/setnam before!)
; A: zero means LOAD, nonzero means VERIFY
; YYXX is desired load address (only used if secondary address == 0), returns end address plus 1
k_save = $ffd8:k_save_AXY = $ffd8 ; save memory to file (call setlfs/setnam before!)
; A is zp address of start ptr(!)
; YYXX is end address plus 1
k_settim = $ffdb ; set time
k_rdtim = $ffde ; read time
k_stop = $ffe1 ; check STOP key
k_getin = $ffe4:k_get = $ffe4 ; get input byte (not the same as $ffcf, see note* below)
; A is result byte
; X is preserved
; Y gets clobbered by tape access (preserved by disk access)
k_clall = $ffe7
k_udtim = $ffea
k_scrorg = $ffed
k_plot = $fff0 ; get/set cursor (to set, clear carry)
k_iobase = $fff3
; pet rom stops here!?
k_scrorg = $ffed ; returns screen size (X = number of columns, Y = number of lines)
; CAUTION: the c128 uses a new format:
; c128: X/Y now return maximum values in current window (so 0..39/0..24 instead of 40/25).
; c128: A returns max column on current screen (39 or 79)
k_plot = $fff0:k_plot_CXY = $fff0 ; get/set cursor (X is line, Y is column)
; C = 0: set cursor position.
; C = 1: read cursor position.
k_iobase = $fff3 ; returns first i/o address (i.e. memory limit) in YYXX
; cbm-ii: $dc00
; vic20: $9110
; c64: $d000
; 264: $fd00
; c128: $d000
}
;note*
; the difference between CHRIN and GETIN depends on the current input device:
; input device 0 (keyboard): CHRIN reads from input buffer, GETIN reads from keyboard buffer
; (the same difference as between INPUT and GET in basic)
; input device 2 (rs232): CHRIN does some error handling, GETIN may just return zero on error.
; roughly speaking, CHRIN returns a "processed" byte while GETIN returns a "raw" byte.
; for devices on the IEC bus there should be no difference between the two calls.
; when reading from the console (keyboard/screen), a zero byte means "no data".
; do not expect a valid Z flag in this case! some devices may clobber the Z flag.

View File

@ -41,47 +41,48 @@ lib_cbm_mflpt_a = 1
; this is ugly, but it gets the job done
; (if it's stupid, but it works, then it's not stupid)
!macro mflpt .value {
!set .float = float(.value) ; make sure to do passes until value is defined
!ifndef .float {
!by $ff, $ff, $ff, $ff, $ff ; five place holder bytes
!macro mflpt @value {
!set @float = float(@value)
!ifndef @float {
!by <@float, $ff, $ff, $ff, $ff ; five place holder bytes
; (first one depends on @float just to make sure more passes are done until value is defined)
} else {
; value is defined, so split up into sign and non-negative value
!if .float < 0 {
!set .sign = $80
!set .float = -.float
!if @float < 0 {
!set @sign = $80
!set @float = -@float
} else {
!set .sign = $00
!set @sign = $00
}
!if .float = 0 {
!if @float = 0 {
!by 0, 0, 0, 0, 0 ; five zeroes (zero is represented by all bits zero)
} else {
; split up into exponent and mantissa
!set .exponent = 128 + 32 ; 128 is cbm's bias, 32 is this algo's bias
!set @exponent = 128 + 32 ; 128 is cbm's bias, 32 is this algo's bias
; if mantissa is too large, shift right and adjust exponent
!do while .float >= (2.0 ^ 32.0) {
!set .float = .float >> 1
!set .exponent = .exponent + 1
!do while @float >= (2.0 ^ 32.0) {
!set @float = @float >> 1
!set @exponent = @exponent + 1
}
; if mantissa is too small, shift left and adjust exponent
!do while .float < (2.0 ^ 31.0) {
!set .float = .float << 1
!set .exponent = .exponent - 1
!do while @float < (2.0 ^ 31.0) {
!set @float = @float << 1
!set @exponent = @exponent - 1
}
!if .exponent < 1 {
!if @exponent < 1 {
!warn "MFLPT underflow, using zero instead"
!set .float = 0
!set .exponent = 0
!set .sign = 0
!set @float = 0
!set @exponent = 0
!set @sign = 0
}
!if .exponent > 255 {
!if @exponent > 255 {
!error "MFLPT overflow"
}
!by .exponent
!by (127 & int(.float >> 24)) | .sign
!by 255 & int(.float >> 16)
!by 255 & int(.float >> 8)
!by 255 & int(.float)
!by @exponent
!by (127 & int(@float >> 24)) | @sign
!by 255 & int(@float >> 16)
!by 255 & int(@float >> 8)
!by 255 & int(@float)
}
}
}

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

13
ACME_Lib/m65/std.a Normal file
View File

@ -0,0 +1,13 @@
;ACME 0.96.5
!ifdef lib_m65_std_a !eof
lib_m65_std_a = 1
; macro to load immediate constant:
!macro movq @v {
; going from lsb to msb, so at least the N flag is correct:
lda #<@v
ldx #>@v
ldy #^@v
ldz #@v >>> 24
}

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

@ -0,0 +1,59 @@
CFLAGS = -O3 -Wall -Wstrict-prototypes
#LIBS = -lm
CC = gcc
RM = rm
#SRC =
PROGS = toacme.exe
all: $(PROGS)
vis.o: config.h acme.h io.h mnemo.h scr2iso.h vis.c
ab3.o: config.h ab.h acme.h io.h mnemo.h scr2iso.h ab3.c
ab.o: config.h ab.h acme.h io.h scr2iso.h ab.c
f8ab.o: config.h ab.h acme.h io.h mnemo.h scr2iso.h f8ab.c
giga.o: config.h acme.h gighyp.h io.h mnemo.h pet2iso.h giga.c
gighyp.o: config.h acme.h io.h pet2iso.h gighyp.h gighyp.c
hypra.o: config.h acme.h gighyp.h io.h pet2iso.h hypra.c
obj.o: config.h acme.h io.h mnemo.h obj.c
acme.o: config.h acme.h acme.c
main.o: config.h version.h main.c
mnemo.o: config.h mnemo.c
pet2iso.o: config.h pet2iso.h pet2iso.c
platform.o: config.h platform.h platform.c
prof.o: config.h prof.c
scr2iso.o: config.h scr2iso.h scr2iso.c
version.o: config.h version.c
toacme.exe: vis.o ab.o ab3.o acme.o f8ab.o giga.o gighyp.o hypra.o io.o main.o mnemo.o obj.o pet2iso.o platform.o prof.o scr2iso.o version.o resource.res
$(CC) $(LIBS) $(CFLAGS) -o toacme vis.o ab.o ab3.o acme.o f8ab.o giga.o gighyp.o hypra.o io.o main.o mnemo.o obj.o pet2iso.o platform.o prof.o scr2iso.o version.o resource.res
strip toacme.exe
win/resource.rc: main.c
cd win; sh setRelease.sh
resource.res: win/resource.rc win/logo.ico
cd win; windres resource.rc -O coff -o ../resource.res
cp -f win/logo.ico .
clean:
-$(RM) -f *.o $(PROGS) *~ core resource.res logo.ico win/resource.rc
# DO NOT DELETE

View File

@ -78,6 +78,7 @@ const char MnemonicSLO[] = " SLO",
MnemonicSBX[] = " SBX",
MnemonicDOP[] = " DOP",
MnemonicTOP[] = " TOP",
MnemonicSHX[] = " SHX",
MnemonicJAM[] = " JAM";
// mnemonics of 65c02 instructions

View File

@ -38,6 +38,7 @@ extern const char MnemonicDCP[], MnemonicDOP[], MnemonicISC[];
extern const char MnemonicJAM[], MnemonicLAX[], MnemonicRLA[];
extern const char MnemonicRRA[], MnemonicSAX[], MnemonicSBX[];
extern const char MnemonicSLO[], MnemonicSRE[], MnemonicTOP[];
extern const char MnemonicSHX[];
// mnemonics of 65c02 instructions
extern const char MnemonicBRA[];

View File

@ -54,7 +54,7 @@ const char *mnemo_of_code[] = {
MnemonicBCC, MnemonicSTA, " JAM;0x92", NULL, // $90-$93
MnemonicSTY, MnemonicSTA, MnemonicSTX, MnemonicSAX, // $94-$97
MnemonicTYA, MnemonicSTA, MnemonicTXS, NULL, // $98-$9b
NULL, MnemonicSTA, NULL, NULL, // $9c-$9f
NULL, MnemonicSTA, MnemonicSHX, NULL, // $9c-$9f
MnemonicLDY, MnemonicLDA, MnemonicLDX, MnemonicLAX, // $a0-$a3
MnemonicLDY, MnemonicLDA, MnemonicLDX, MnemonicLAX, // $a4-$a7
MnemonicTAY, MnemonicLDA, MnemonicTAX, NULL, // $a8-$ab
@ -217,7 +217,7 @@ int (*addressing_mode_of_code[])(void) = {
am_relative, am_indirect_y, am_implied, am_implied, // $90-$93
am_abs_x8, am_abs_x8, am_abs_y8, am_abs_y8, // $94-$97
am_implied, am_abs_y16, am_implied, am_implied, // $98-$9b
am_implied, am_abs_x16, am_implied, am_implied, // $9c-$9f
am_implied, am_abs_x16, am_abs_y16, am_implied, // $9c-$9f
am_immediate, am_indirect_x, am_immediate, am_indirect_x, // $a0-$a3
am_absolute8, am_absolute8, am_absolute8, am_absolute8, // $a4-$a7
am_implied, am_immediate, am_implied, am_implied, // $a8-$ab

View File

@ -1,5 +1,5 @@
// ToACME - converts other source codes to ACME format.
// Copyright (C) 1999-2016 Marco Baye
// Copyright (C) 1999-2019 Marco Baye
// Have a look at "main.c" for further info
//
// "Professional Ass by Oliver Stiller" stuff (NOT "Profi-Ass" by Data Becker!)
@ -40,6 +40,12 @@
#define REV_START ";-=#"
#define REV_END "#=-"
enum linestate {
LINESTATE_REMOVEPLUS, // before label def
LINESTATE_DOUBLENEXTSPACE, // entered when removing '+'
LINESTATE_NOTHINGSPECIAL // afterward
};
// main
void prof_main(void)
{
@ -49,6 +55,7 @@ void prof_main(void)
byte,
hibit,
length2;
enum linestate linestate;
IO_set_input_padding(0);
IO_process_load_address();
@ -57,7 +64,8 @@ void prof_main(void)
if (length1 == EOF)
break;
if (length1 < 3) {
fprintf(stderr, "Error: Short line (%d bytes)\n", length1);
fprintf(stderr, "Error: Short line (%d bytes), stopping.\n", length1);
return;
}
// read amount of indentation and output tabs/spaces
indent = IO_get_byte();
@ -71,13 +79,34 @@ void prof_main(void)
}
// now convert line
hibit = 0;
linestate = LINESTATE_REMOVEPLUS;
for (ii = 0; ii < length1 - 3; ii++) {
byte = IO_get_byte();
if ((byte & 128) != hibit) {
hibit = byte & 128;
IO_put_string(hibit ? REV_START : REV_END);
}
IO_put_byte(SCR2ISO(byte & 127));
byte = SCR2ISO(byte & 127);
// outside of comments, remove leading '+'
if (hibit == 0) {
if (byte == '+') {
if (linestate == LINESTATE_REMOVEPLUS) {
linestate = LINESTATE_DOUBLENEXTSPACE;
continue; // eat '+'
}
} else if (byte == ' ') {
if (linestate == LINESTATE_DOUBLENEXTSPACE) {
linestate = LINESTATE_NOTHINGSPECIAL;
IO_put_byte(' '); // add space to compensate for eaten '+'
}
} else {
// any other char -> do not remove any '+'
if (linestate == LINESTATE_REMOVEPLUS) {
linestate = LINESTATE_NOTHINGSPECIAL;
}
}
}
IO_put_byte(byte);
}
if (hibit)
IO_put_string(REV_END);

View File

@ -1,15 +1,15 @@
// ToACME - converts other source codes to ACME format.
// Copyright (C) 1999-2015 Marco Baye
// Copyright (C) 1999-2019 Marco Baye
// Have a look at "main.c" for further info
//
// Version
#define RELEASE_NUMBER "0.13" // change before release (FIXME)
#define CHANGE_DATE "16 Feb" // change before release
#define CHANGE_YEAR "2016" // change before release
#define RELEASE_NUMBER "0.15" // change before release (FIXME)
#define CHANGE_DATE "25 Apr" // change before release
#define CHANGE_YEAR "2019" // change before release
#define HOME_PAGE "http://sourceforge.net/projects/acme-crossass/"
// "http://home.pages.de/~mac_bacon/smorbrod/acme/"
#define FILE_TAG ";ACME 0.95.6" // check before release
#define FILE_TAG ";ACME 0.96.4" // check before release
#include <stdio.h>
#include <string.h>

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@ -0,0 +1,58 @@
#/bin/bash
#
# Get release and create RC-File
#
function DEBUG()
{
[ "$_DEBUG" == "on" ] && $@
}
function pause()
{
read -p "Weiter mit Eingabe" $a
}
FILE="resource.rc"
RELEASE=`grep -m1 "define RELEASE" ../version.c | cut -f2 | tr -d '"'`
DEBUG echo $RELEASE
VERSION=${RELEASE//./,},0
DEBUG echo $VERSION
FILEVERSION=\""$RELEASE ${CODENAME//\"/}"\"
DEBUG echo $FILEVERSION
CHANGE_YEAR=`grep -m1 "define CHANGE_YEAR" ../version.c | cut -f2 | tr -d '"'`
DEBUG echo $CHANGE_YEAR
cat << EndOfFile > $FILE
// Iconfile (64/32/16)
ID ICON "Logo.ico"
// Infos for windows
1 VERSIONINFO
FILEVERSION $VERSION
PRODUCTVERSION $VERSION
FILEFLAGSMASK 0x3fL
FILEFLAGS 0x0L
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", "Smørbrød Software"
VALUE "FileDescription", "ToAcme Converter"
VALUE "FileVersion", $FILEVERSION
VALUE "InternalName", "ToACME Converter"
VALUE "LegalCopyright", "Copyright © $CHANGE_YEAR Marco Baye"
VALUE "OriginalFilename", "toacme.exe"
VALUE "ProductName", "ToACME Converter"
VALUE "ProductVersion", $FILEVERSION
VALUE "PorductLicence","GNU General Public License"
VALUE "WindowsPort","Dirk Höpfner hoeppie@gmx.de"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
EndOfFile

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
@ -30,12 +30,13 @@ According to WDC's official syntax for 65816 assembly language, the
argument order of the MVN and MVP instructions differs between
assembly language and machine code.
To copy bytes from bank $ab to bank $cd, use the following statement:
mvn $ab, $cd ; source bank $ab, destination bank $cd
mvn $ab, $cd ; source bank $ab, destination bank $cd
or
mvn #$ab, #$cd ; source bank $ab, destination bank $cd
ACME will then produce the following machine code:
$54 $cd $ab ; opcode mvn, destination bank $cd, source bank $ab
$54 $cd $ab ; opcode mvn, destination bank $cd, source bank $ab
ACME 0.05 and earlier did it the wrong way. Several other assemblers
still do. Make sure your sources are correct.
ACME 0.05 and earlier did it the wrong way.
----------------------------------------------------------------------
@ -54,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

@ -7,12 +7,12 @@
--- addressing modes ---
If a command can be used with different addressing modes, ACME has to
decide which one to use. Several commands of the 6502 CPU can be used
with either "absolute" addressing or "zeropage-absolute" addressing.
The former one means there's a 16-bit argument, the latter one means
there's an 8-bit argument.
And the 65816 CPU even knows some commands with 24-bit addressing...
If an instruction can be used with different addressing modes, ACME
has to decide which one to use. Several instructions of the 6502 CPU
can be used with either "absolute" addressing or "zeropage-absolute"
addressing. The former one means there's a 16-bit argument, the latter
one means there's an 8-bit argument.
And the 65816 CPU even has some instructions with 24-bit addressing...
So how does ACME know which addressing mode to use?
The simple approach is to always use the smallest possible argument,
@ -29,11 +29,10 @@ The two exceptions are:
*** 1) Symbols are defined too late
If ACME cannot figure out the argument value in the first pass, it
assumes that the command uses 16-bit addressing.
assumes that the instruction uses 16-bit addressing.
If it later finds out that the argument only needs 8 bits, ACME gives
a warning ("using oversized addressing mode") and continues. However,
@ -43,8 +42,7 @@ These problems can be solved by defining the symbols *before* using
them, so that the value can be figured out in the first pass. If this
is not possible, you can use the postfix method, effectively exactly
defining what addressing mode to use. The postfix method is described
in a separate paragraph below.
in a separate section below.
@ -85,10 +83,12 @@ will be assembled to
ad fd 00 ; lda $00fd
8f ff 00 00 ; sta $0000ff
The other possibility is to use the postfix method (described in the
next paragraph).
This feature can be disabled using the "--ignore-zeroes" CLI switch.
The other possibility is to use the postfix method (described in the
next section).
*** The postfix method
@ -129,7 +129,7 @@ will be assembled to
8c fd 00 ; sty $00fd
8f ff 00 00 ; sta $0000ff
Postfixes given directly after the command have higher priority than
Postfixes added directly to the mnemonic have higher priority than
those given to the argument. As you can see, you can add the postfix
to the symbol definition as well (equivalent to leading zeros).
@ -138,8 +138,7 @@ gives the high byte and "^" gives the bank byte of a value) to any
value will clear the argument's Force Bits 2 and 3 and set Force
Bit 1 instead. So "lda <symbol" will use 8-bit addressing, regardless
of the symbol's Force Bits. Of course, you can change this by
postfixing the command again... :)
postfixing the instruction again... :)
@ -149,7 +148,7 @@ You don't need to read this paragraph just to use ACME, I only
included it for completeness' sake. This is a description of ACME's
strategy for finding the addressing mode to use:
First, ACME checks whether the command has any postfix. If it has,
First, ACME checks whether the instruction has any postfix. If it has,
ACME acts upon it. So postfixes have the highest priority.
Otherwise, ACME checks whether the argument has any Force Bits set

View File

@ -22,17 +22,18 @@ Purpose: Insert 8-bit values.
Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: "!08", "!by", "!byte"
Examples: !08 127, symbol, -128 ; output some values
!by 14, $3d, %0110, &304, <*, "c"
!by 14, $3d, %0110, &304, <*, 'c'
!byte 3 - 4, symbol1 XOR symbol2, 2 ^ tz, (3+4)*7
Call: !16 EXPRESSION [, EXPRESSION]*
Purpose: Insert 16-bit values in chosen CPU's byte order.
Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: "!wo", "!word" (and because all currently supported CPUs are
little-endian, "!le16" is in fact another alias)
Aliases: "!wo", "!word" (and because all currently supported
CPUs are little-endian, "!le16" is in fact another
alias)
Examples: !16 65535, symbol, -32768 ; output some values
!wo 14, $4f35, %100101010010110, &36304, *, "c"
!wo 14, $4f35, %100101010010110, &36304, *, 'c'
!word 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
Call: !le16 EXPRESSION [, EXPRESSION]*
@ -41,7 +42,7 @@ Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: None (but because all currently supported CPUs are
little-endian, "!16/!wo/!word" are in fact aliases)
Examples: !le16 65535, symbol, -32768 ; output some values
!le16 14, $4f35, %100101010010110, &36304, *, "c"
!le16 14, $4f35, %100101010010110, &36304, *, 'c'
!le16 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
Call: !be16 EXPRESSION [, EXPRESSION]*
@ -49,7 +50,7 @@ Purpose: Insert 16-bit values in big-endian byte order.
Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: None
Examples: !be16 65535, symbol, -32768 ; output some values
!be16 14, $4f35, %100101010010110, &36304, *, "c"
!be16 14, $4f35, %100101010010110, &36304, *, 'c'
!be16 3000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
@ -59,7 +60,7 @@ Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: None (but because all currently supported CPUs are
little-endian, "!le24" is in fact an alias)
Examples: !24 16777215, symbol, -8388608, 14, $6a4f35
!24 %10010110100101010010110, &47336304, *, "c"
!24 %10010110100101010010110, &47336304, *, 'c'
!24 300000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
Call: !le24 EXPRESSION [, EXPRESSION]*
@ -68,7 +69,7 @@ Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: None (but because all currently supported CPUs are
little-endian, "!24" is in fact an alias)
Examples: !le24 16777215, symbol, -8388608, 14, $6a4f35
!le24 %10010110100101010010110, &47336304, *, "c"
!le24 %10010110100101010010110, &47336304, *, 'c'
!le24 300000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
Call: !be24 EXPRESSION [, EXPRESSION]*
@ -76,7 +77,7 @@ Purpose: Insert 24-bit values in big-endian byte order.
Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: None
Examples: !be24 16777215, symbol, -8388608, 14, $6a4f35
!be24 %10010110100101010010110, &47336304, *, "c"
!be24 %10010110100101010010110, &47336304, *, 'c'
!be24 300000 - 4, a1 AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
@ -86,7 +87,7 @@ Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: None (but because all currently supported CPUs are
little-endian, "!le32" is in fact an alias)
Examples: !32 $7fffffff, symbol, -$80000000, 14, $46a4f35
!32 %1001011010010101001011010010, &4733630435, *, "c"
!32 %1001011010010101001011010010, &4733630435, *, 'c'
!32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
Call: !le32 EXPRESSION [, EXPRESSION]*
@ -95,7 +96,7 @@ Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: None (but because all currently supported CPUs are
little-endian, "!32" is in fact an alias)
Examples: !le32 $7fffffff, symbol, -$80000000, 14, $46a4f35
!le32 %1001011010010101001011010010, &4733630435, *, "c"
!le32 %1001011010010101001011010010, &4733630435, *, 'c'
!le32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
Call: !be32 EXPRESSION [, EXPRESSION]*
@ -103,10 +104,27 @@ Purpose: Insert 32-bit values in big-endian byte order.
Parameters: EXPRESSION: Any formula the value parser accepts.
Aliases: None
Examples: !be32 $7fffffff, symbol, -$80000000, 14, $46a4f35
!be32 %1001011010010101001011010010, &4733630435, *, "c"
!be32 %1001011010010101001011010010, &4733630435, *, 'c'
!be32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2
Call: !hex PAIRS_OF_HEX_DIGITS
Purpose: Insert byte values with a minimum of additional
syntax. This pseudo opcode was added for easier
writing of external source code generator tools.
Parameters: PAIRS_OF_HEX_DIGITS: Just hexadecimal digits, without
any "0x" or "$" prefix. Spaces and TABs are allowed,
but not needed to separate the byte values.
Aliases: "!h"
Examples: !h f0 f1 f2 f3 f4 f5 f6 f7 ; insert values 0xf0..0xf7
!h f0f1f2f3 f4f5f6f7 ; insert values 0xf0..0xf7
!h f0f1f2f3f4f5f6f7 ; insert values 0xf0..0xf7
!h f0f 1f2 ; ERROR: space inside pair!
!h 0x00, $00 ; ERROR: "0x", "," and "$" are forbidden!
!h SOME_SYMBOL ; ERROR: symbols are forbidden!
!h ABCD ; insert value 0xAB, then 0xCD (CAUTION, big-endian)
Call: !fill AMOUNT [, VALUE]
Purpose: Fill amount of memory with value.
Parameters: AMOUNT: Any formula the value parser accepts, but it
@ -118,6 +136,18 @@ Examples: !fi 256, $ff ; reserve 256 bytes
!fill 2 ; reserve two bytes
Call: !skip AMOUNT
Purpose: Advance in output buffer without starting a new
segment.
Parameters: AMOUNT: Any formula the value parser accepts, but it
must be solvable even in the first pass (this
limitation will hopefully be lifted in a future
release).
Aliases: None
Examples: !skip BUFSIZE ; reserve some bytes
!skip 5 ; reserve five bytes
Call: !align ANDVALUE, EQUALVALUE [, FILLVALUE]
Purpose: Fill memory until a matching address is reached. ACME
outputs FILLVALUE until "program counter AND ANDVALUE"
@ -128,7 +158,7 @@ Parameters: ANDVALUE: Any formula the value parser accepts, but it
it must be solvable even in the first pass.
FILLVALUE: Any formula the value parser accepts. If it
is omitted, a default value is used (currently 234,
that's the 6502 CPU's NOP command).
that's the opcode of the 6502 CPU's NOP instruction).
Examples: !align 255, 0 ; align to page (256 bytes)
!align 63, 0 ; align to C64 sprite block (64 bytes)
@ -351,23 +381,25 @@ Section: Flow control
Call: !if CONDITION { BLOCK } [ else { BLOCK } ]
Purpose: Conditional assembly. If the given condition is true,
the first block of statements will be parsed;
if it isn't, the second block will be parsed instead
(if present).
the matching block of statements will be parsed;
if no condition is true, the ELSE block (if present)
will be parsed.
Parameters: CONDITION: Any formula the value parser accepts, but
it must be solvable even in the first pass.
BLOCK: A block of assembler statements.
Examples: !text "Black", 0 ; Choose wording according to
!if country = uk { ; content of "country" symbol.
Examples: ; Choose word according to "country" symbol:
!if country = uk {
!text "Grey"
} else if country = fr {
!text "Gris"
} else if country = de {
!text "Grau"
} else {
!text "Gray"
}
!byte 0
!text "White", 0
; Insert debug commands if symbol "debug" is not zero:
!if debug { lda #"z":jsr char_output }
; Insert debug code depending on symbol "debug":
!if debug { lda #'z':jsr char_output }
Call: !ifdef SYMBOL { BLOCK } [ else { BLOCK } ]
@ -410,12 +442,26 @@ Examples: ; this was taken from <6502/std.a>:
; further instances will be skipped.
}
; include at most one driver source code:
!ifdef RAM_REU {
!src "driver_reu.a"
} else ifdef RAM_GEORAM {
!src "driver_georam.a"
} else ifdef RAM_VDCRAM {
!src "driver_vdcram.a"
} else ifdef RAM_SUPERRAM {
!src "driver_superram.a"
} else {
!src "driver_noram.a"
}
Call: !for SYMBOL, START, END { BLOCK }
Purpose: Looping assembly. The block of statements will be
parsed a fixed number of times, as specified by the
values of START and END. For a more flexible
possibility, have a look at "!do" below.
values of START and END. For more flexible
possibilities, have a look at "!do" and "!while"
below.
Parameters: SYMBOL: Any valid symbol name.
START: Any formula the value parser accepts, but it
must be solvable even in the first pass. SYMBOL will
@ -465,12 +511,13 @@ Examples:
Miscellaneous: The old syntax ("!for SYMBOL, END { BLOCK }" where
START was always implied to be 1) is still fully
supported, but gives a warning to get people to change
to the new syntax. You can disable this warning using
the "-Wno-old-for" switch, but then you will get
to the new syntax.
You can disable this warning using the "--dialect" or
the "-Wno-old-for" switches, but then you will get
warnings for using the *new* syntax.
When migrating your sources, bear in mind that it is
no longer possible to skip the block completely by
specifying a loop count of zero.
When migrating your sources to the current syntax,
bear in mind that it is no longer possible to skip the
block completely by specifying a loop count of zero.
Also note that with the new algorithm, SYMBOL has a
different value after the block than during the last
loop cycle, while the old algorithm kept that last
@ -480,9 +527,9 @@ Miscellaneous: The old syntax ("!for SYMBOL, END { BLOCK }" where
Call: !set SYMBOL = VALUE
Purpose: Assign given value to symbol even if the symbol
already has a different value. Needed for loop
counters when using "!do", for example. Only use this
opcode for something else if you're sure you *really*
know what you are doing... :)
counters when using "!do"or "!while", for example.
Only use this opcode for something else if you're sure
you *really* know what you are doing... :)
Parameters: SYMBOL: Any valid symbol name.
VALUE: Any formula the value parser accepts.
Example: see "!do" below
@ -522,6 +569,34 @@ Examples: ; a loop with conditions at both start and end
!do until 3 = 4 { } while 3 < 4
Call: !while [CONDITION] { BLOCK }
Purpose: Looping assembly. The block of statements can be
parsed several times, depending on the given
condition.
The condition is parsed in every repetition before the
actual block. If it isn't met when first checked, the
block will be skipped.
Parameters: CONDITION: Any formula the value parser accepts, but
it must be solvable even in the first pass.
BLOCK: A block of assembler statements.
Examples: ; a loop with a counter
!set a = 0 ; init loop counter
!while a < 6 {
lda #a
sta label + a
!set a = a + 1
}
; a loop depending on program counter
!while * < $c000 { nop }
; a never ending loop - this will cause an error
!while 3 < 4 { nop }
; an empty loop - this will hang ACME
!while 3 != 4 { }
Call: !endoffile
Purpose: Stop processing the current source file. Using this
pseudo opcode you can add explanatory text inside your
@ -536,22 +611,24 @@ Example: rts ; some assembler mnemonic
"!eof" is reached.
Call: !warn STRING_VALUE [, STRING_VALUE]*
Call: !warn VALUE [, VALUE]*
Purpose: Show a warning during assembly.
Parameters: STRING_VALUE: Can be either a string given in double
quotes or any formula the value parser accepts.
Numbers will be output in decimal _and_ hex format.
Parameters: VALUE: Can be either a string given in double quotes
or any formula the value parser accepts.
Integer numbers will be output in both decimal _and_
hex formats.
Example: !if * > $a000 {
!warn "Program reached ROM: ", * - $a000, " bytes overlap."
}
Call: !error STRING_VALUE [, STRING_VALUE]*
Call: !error VALUE [, VALUE]*
Purpose: Generate an error during assembly (therefore, no
output file will be generated).
Parameters: STRING_VALUE: Can be either a string given in double
quotes or any formula the value parser accepts.
Numbers will be output in decimal _and_ hex format.
Parameters: VALUE: Can be either a string given in double quotes
or any formula the value parser accepts.
Integer numbers will be output in both decimal _and_
hex formats.
Example: rts ; end of some function
start !source "colors.a"
end !if end - start > 256 {
@ -559,12 +636,13 @@ Example: rts ; end of some function
}
Call: !serious STRING_VALUE [, STRING_VALUE]*
Call: !serious VALUE [, VALUE]*
Purpose: Generate a serious error, immediately stopping
assembly.
Parameters: STRING_VALUE: Can be either a string given in double
quotes or any formula the value parser accepts.
Numbers will be output in decimal _and_ hex format.
Parameters: VALUE: Can be either a string given in double quotes
or any formula the value parser accepts.
Integer numbers will be output in both decimal _and_
hex formats.
Example: !source "part1.a" ; sets part1_version
!source "part2.a" ; sets part2_version
!if part1_version != part2_version {
@ -584,9 +662,9 @@ Parameters: TITLE: The macro's desired name (same rules as for
could want this is beyond me).
SYMBOL: The desired name for the parameter value at
call time. Normally, these parameter symbols should be
local (first character a dot), as different macro
calls will almost for sure have different parameter
values.
local (first character a '.' or a '@'), as different
macro calls will almost for sure have different
parameter values.
If you prefix SYMBOL with a '~' character, it will be
called by reference, not by value: Changing the value
inside the macro will result in the "outer" symbol to
@ -713,8 +791,10 @@ Purpose: Set program counter to given value and start new
issued. Because some people do this overlapping
on purpose, the warnings can be suppressed using
modifier keywords.
Future versions of ACME may issue errors instead of
warnings.
Using the "--strict-segments" CLI switch, these
warnings can be turned onto errors. Future versions of
ACME may do that by default - so if needed, use the
modifier keywords.
Parameters: EXPRESSION: Any formula the value parser accepts, but
it must be solvable even in the first pass.
MODIFIER: "overlay" or "invisible" (without quotes):
@ -771,6 +851,24 @@ Examples: !to "TinyDemo", cbm ; define output file + format
; Useful if you want to store your code in an EPROM.
Call: !xor EXPRESSION [ { BLOCK } ]
Purpose: Change the value to XOR all output bytes with (the
value defaults to zero on startup). This "encryption"
facility was added to compensate for the shortcomings
of the "!scrxor" pseudo opcode, which only XORs
strings and characters, but not numbers.
When used with block syntax, the previously chosen
value is restored afterwards.
Parameters: EXPRESSION: Any formula the value parser accepts.
BLOCK: A block of assembler statements.
Examples: ; first as normal screencodes:
!scr "Hello everybody...", GROUPLOGOCHAR
; and now as inverted screencodes:
!xor $80 {
!scr "Hello everybody...", GROUPLOGOCHAR
}
----------------------------------------------------------------------
Section: Offset assembly
----------------------------------------------------------------------
@ -802,6 +900,13 @@ Examples: ldx #.shifted_end - .shifted_start
}
.shifted_end
Miscellaneous: If you need to convert a label or the program counter
from its "pseudopc" to its "real" value, you can do
that using the "&" operator. Given the example above,
the symbol ".target" will evaluate to the value $0400,
but "&.target" will evaluate to the same value as
".shifted_start" will.
----------------------------------------------------------------------
Section: CPU support pseudo opcodes (especially 65816 support)
@ -811,23 +916,25 @@ Call: !cpu KEYWORD [ { BLOCK } ]
Purpose: Select the processor to produce code for. If this PO
isn't used, ACME defaults to the 6502 CPU (or to the
one selected by the "--cpu" command line option).
ACME will give errors if you try to assemble commands
the chosen CPU does not have. You can change the
chosen CPU at any time. When used with block syntax,
the previously chosen CPU value is restored
afterwards.
ACME will give errors if you try to assemble
instructions the chosen CPU does not support. You can
change the chosen CPU at any time. When used with
block syntax, the previously chosen CPU value is
restored afterwards.
Parameters: KEYWORD: Currently valid keywords are:
6502 for the original MOS 6502
6510 6502 plus undocumented opcodes
65c02 6502 plus BRA,PHX/Y,PLX/Y,STZ,TRB/TSB
r65c02 65c02 plus BBRx, BBSx, RMBx, SMBx
w65c02 r65c02 plus STP/WAI
65816 65c02 plus 16/24-bit extensions
65ce02 r65c02 plus Z reg, long branches, ...
4502 65ce02 with MAP instead of AUG
c64dtv2 6502 plus BRA/SAC/SIR plus some of the
6502 for the original MOS 6502
nmos6502 6502 plus undocumented opcodes
6510 (alias for "nmos6502")
65c02 6502 plus BRA,PHX/Y,PLX/Y,STZ,TRB/TSB
r65c02 65c02 plus BBRx, BBSx, RMBx, SMBx
w65c02 r65c02 plus STP/WAI
65816 65c02 plus 16/24-bit extensions
65ce02 r65c02 plus Z reg, long branches, ...
4502 65ce02 with MAP instead of AUG
m65 4502 plus 32-bit extensions
c64dtv2 6502 plus BRA/SAC/SIR plus some of the
undocumented opcodes
See "docs/cputypes.txt" for more info.
See "docs/cputypes/all.txt" for more info.
BLOCK: A block of assembler statements.
Examples: !if cputype = $65c02 {
!cpu 65c02 { ; temporarily allow 65c02 stuff
@ -840,7 +947,7 @@ Examples: !if cputype = $65c02 {
pla
}
rts
!cpu 65816 ; allow 65816 commands from here on
!cpu 65816 ; now allow instructions of 65816 cpu
Call: !al [ { BLOCK } ]
@ -888,6 +995,8 @@ Parameters: BLOCK: A block of assembler statements
If no block is given, only the current statement will
be affected, which should then be an explicit symbol
definition.
To make use of this feature, you need to use the
"-Wtype-mismatch" CLI switch.
Aliases: "!addr"
Examples: !addr k_chrout = $ffd2 ; this is an address
CLEAR = 147 ; but this is not
@ -913,6 +1022,10 @@ Purpose: Use PetSCII as the text conversion table. Now
superseded by the "!convtab" pseudo opcode.
Old usage: !cbm ; gives "use !ct pet instead" error
Now use: !convtab pet ; does the same without error
If you just want to assemble an old source code
without touching it, use the "--dialect" CLI switch:
Using "--dialect 0.94.6" or earlier will assemble this
pseudo opcode without throwing an error.
Call: !subzone [TITLE] { BLOCK }
@ -927,6 +1040,10 @@ Old usage: !subzone graphics {
Now use: !zone graphics {
!source "graphics.a"
}
If you just want to assemble an old source code
without touching it, use the "--dialect" CLI switch:
Using "--dialect 0.94.6" or earlier will assemble this
pseudo opcode without throwing an error.
Call: !realpc
@ -940,3 +1057,8 @@ Old usage: !pseudopc $0400
Now use: !pseudopc $0400 {
; imagine some code here...
}
If you just want to assemble an old source code
without touching it, use the "--dialect" CLI switch:
Using "--dialect 0.94.6" or earlier will assemble this
pseudo opcode without throwing an error.
Using "--dialect 0.85", not even a warning is thrown.

View File

@ -12,6 +12,121 @@ platform used. There should be another help file in this archive
outlining the platform specific changes.
----------------------------------------------------------------------
Section: New in release 0.97
----------------------------------------------------------------------
FINALLY strings can be assigned to symbols!
"anything in double quotes" is a string, while characters in
single quotes are, just as before, immediately converted to their
character code. Go and read "docs/Upgrade.txt" to learn about
compatibility issues.
Added backslash escaping in all string literals:
\0 is a null byte, \t is a TAB, \n is a line feed, \r is a
carriage return, \" is a double quote, \' is a single quote and
\\ is a backslash. Go and read "docs/Upgrade.txt" to learn about
compatibility issues.
Added "--dialect" CLI switch:
Because string symbols and backslash escaping introduce a few
incompatibilities to older versions, ACME can now be told to mimic
the behavior of older versions. So it's still possible to assemble
older sources.
Added lists:
Lists can be used to pass an arbitrary number of arguments to
macros, or to store any number of items (including other lists) in
a single symbol. Example: my_list = [1, 2, label, "string", 9]
Added "len()" operator:
This returns the number of elements in a string or list.
Added "[]" operator (for indexing):
This returns a single element from a string or list. Negative
indices are supported as well, they access the string/list from
the other end. Examples: a = my_list[2] b = my_string[-1]
Added "&" operator:
This operator converts labels or the program counter from its
value inside a "!pseudopc" block to the value outside of that
block. Thanks to markusC64 for the suggestion!
Added "!while {}" pseudo opcode.
Added "else if", "else ifdef" and "else ifndef" possibilities.
Added "M65" cpu:
This instruction set includes the MEGA65 extensions, namely 32-bit
pointers and 32-bit data operations using prefix bytes.
Added "NMOS6502" as an alias for "6510" cpu.
Improved NMOS6502/6510 mode:
DOP and TOP can now also be written as NOP.
Improved 65816 mode:
MVN and MVP can now also be written with '#' before arguments.
Added "--test" CLI switch:
This is for people who want to help test experimental features.
Improved error messages:
"Garbage data at end of statement" now includes the unexpected
character.
"Symbol not defined" is only output once per symbol.
Added warning:
ACME complains about binary literals with an "unusual" number of
digits. Thanks to groepaz for the suggestion!
The warning can be disabled using the "-Wno-bin-len" CLI switch.
Added warning:
All mnemonics without indirect addressing now complain about
unneeded parentheses.
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".
----------------------------------------------------------------------
Section: New in release 0.96.5
----------------------------------------------------------------------
Allowed C++-style comments via "//". Thanks to awsm for the
suggestion (and please accept my apologies for taking so long to
release this)
Added "--ignore-zeroes" CLI switch. This disables the "leading zeroes
determine number size" algorithm. Thanks to groepaz for the
suggestion.
Added "--strict-segments" CLI switch. This changes warnings about
overlapping memory segments into errors. Thanks to groepaz for the
suggestion.
Added 6502 family tree to docs/cpu_types/all.txt
----------------------------------------------------------------------
Section: New in release 0.96.4
----------------------------------------------------------------------
Bugfix: Removed warnings about zero page wrap-around for the 65816's
24-bit pointers (because wrap-around does not actually happen).
Thanks to Johann Klasek for reporting this.
Added "!xor" pseudo opcode to compensate for the shortcomings of the
"!scrxor" pseudo opcode. Thanks to spider-j for the initial bug
report.
Added "-I" CLI switch to add search paths for input files. Thanks to
peiselulli for the suggestion.
----------------------------------------------------------------------
Section: New in release 0.96.3
----------------------------------------------------------------------
Added "!h"/"!hex" pseudo opcode: Now external source code generator
tools can easily put data in sources with minimal syntax overhead.
Added "!skip" pseudo opcode: "!skip N" works like "*=*+N" without
starting a new segment.
Added "cheap locals": Labels with '@' prefix have automatic scoping,
bounded by the preceding and the following global labels.
Added "--fullstop" CLI switch to change pseudo opcode prefix from '!'
to '.' (so other assemblers' sources need less conversion work)
Fixed a bug where expressions like "1)+1" crashed ACME. Thanks to
Bitbreaker for reporting this.
Added warning when using zp-indirect addressing modes where argument
is $ff because pointer wraps around to $00. Thanks to Gerrit for
the suggestion.
----------------------------------------------------------------------
Section: New in release 0.96.2
----------------------------------------------------------------------
@ -100,8 +215,9 @@ Section: New in release 0.95.3
Added "c64dtv2" cpu type so you can use its SIR, SAC and BRA opcodes;
along with the undocumented ("illegal") opcodes of the 6510.
Added Martin Piper's "--msvc" patch so error output can be configured
to be in Visual Studio format.
Added "--msvc" CLI switch so error output can be configured to be in
Visual Studio format. Thanks to Martin Piper for writing this
patch!
Merged third-party patch (who wrote it?) to output label dump in VICE
format. Still needs work to be configurable about the types of
symbols actually output.

View File

@ -38,15 +38,29 @@ Assembling buggy JMP($xxff) instruction
location ARGUMENT + 1, but from ARGUMENT - 255. Therefore ACME
issues this warning if you are about to generate such an
instruction.
Note that this warning is only given for CPU types 6502 and 6510,
because 65c02 and 65816 have been fixed in this respect.
Note that this warning is only given for some CPU types (6502,
nmos6502/6510, c64dtv2) because later ones like 65c02 and 65816
have been fixed in this regard.
Assembling unstable ANE #NONZERO instruction
Assembling unstable LXA #NONZERO instruction
This warning is only ever given for CPU type 6510. LXA is one of
the undocumented ("illegal") opcodes of this CPU (opcode 0xab),
and it only works reliably if its argument is zero. Therefore ACME
issues this warning if you are about to generate this instruction
with a non-zero argument.
These warnings are only ever given for CPU type nmos6502 (6510).
ANE and LXA are undocumented ("illegal") opcodes of this CPU, and
they only work reliably if the argument is zero or the accumulator
contains 0xff.
Therefore ACME issues these warnings if it is about to generate
these instructions with a non-zero argument.
Binary literal without any digits.
Hex literal without any digits.
A special literal was started, but then no digits followed. Expect
this to become an error in future!
Binary literal with strange number of digits.
This warning is given if the number of digits in a binary literal
is not a multiple of four. This is useful when you meant to write
%#....... but actually wrote %#........ by mistake. See? :P
You can disable this warning using the CLI switch "-Wno-bin-len".
Bug in ACME, code follows
A situation has been encountered implying there is a bug in ACME.
@ -57,7 +71,7 @@ C-style "==" comparison detected.
Converted to integer for binary logic operator.
Applying binary logic to float values does not make much sense,
therefore floats will be converted to integer in this case.
therefore floats will be converted to integer in such cases.
"EOR" is deprecated; use "XOR" instead.
This means the operator, not the mnemonic.
@ -65,7 +79,9 @@ Converted to integer for binary logic operator.
Found old "!for" syntax.
Please update your sources to use the new "!for" syntax. See
AllPOs.txt for details.
You can suppress this warning using the "-Wno-old-for" switch.
You can suppress this warning using the "--dialect" or the
"-Wno-old-for" CLI switch.
("-Wno-old-for" does _exactly_ the same as "--dialect 0.94.8")
Found new "!for" syntax.
When using the "-Wno-old-for" switch to disable the warning about
@ -96,33 +112,35 @@ 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.
Because some people might want to assemble "onto" a binary file
that was loaded before, this warning can be switched off using
modifier keywords when changing the program counter via "* =".
Future versions of ACME might throw an error instead of a warning
in this case.
that was loaded before, this warning can be inhibited using
modifier keywords when changing the program counter via "*=".
For extra safety you can also turn this warning into an error
using the "--strict-segments" CLI switch. In future versions of
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 switched
off using modifier keywords when changing the program counter via
"* =".
Future versions of ACME might throw an error instead of a warning
in this case.
binary file that was loaded before, this warning can be inhibited
using modifier keywords when changing the program counter via
"*=".
For extra safety you can also turn this warning into an error
using the "--strict-segments" CLI switch. In future versions of
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"
@ -130,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".
@ -155,6 +173,11 @@ Wrong type for loop's END value - must match type of START value.
In "!for" loops, START and END must have the same type, which then
gets used for the loop counter.
Zeropage pointer wraps around from $ff to $00
A zeropage-indirect addressing mode uses $ff as the argument. The
6502 will then fetch the second pointer byte from $00 instead of
$0100, therefore this warning is issued.
...called from here.
If warnings and/or errors are output during a macro call, messages
with this text are added to display the call stack (because you
@ -173,19 +196,35 @@ Section: Errors during assembly
"!cbm" is obsolete; use "!ct pet" instead.
This is given when the now obsolete "!cbm" pseudo opcode is
encountered.
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.
"!pseudopc/!realpc" is obsolete; use "!pseudopc {}" instead.
This is given when one of the now obsolete !pseudopc/!realpc
pseudo opcodes is encountered.
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.
"!subzone {}" is obsolete; use "!zone {}" instead.
This is given when the now obsolete "!subzone" pseudo opcode is
encountered.
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.
!error: ...
This is given when the pseudo opcode "!error" is executed. The
actual message varies according to the pseudo opcode's arguments.
After ELSE, expected block or IF/IFDEF/IFNDEF.
There is something strange after ELSE: It must be "if", "ifdef",
"ifndef" or an opening brace.
Argument out of range.
You called arcsin/arccos with something not in the [-1, 1] range.
Cannot open input file.
ACME had problems opening an input file ("!bin", "!convtab" or
"!src"). Maybe you mistyped its name.
@ -194,35 +233,62 @@ 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.
Expected ELSE or end-of-statement.
There is something after the closing brace of an IF block that is
not an ELSE.
Expected end-of-statement after ELSE block.
There is something after the closing brace of an ELSE block.
Exponent is negative.
Using negative exponents only give sensible results when using
floating point maths.
Expression did not return a number.
An expression returned a string or a list but a number (integer or
float) was expected.
File name quotes not found ("" or <>).
File names have to be given in quotes. Either "" quoting for files
located in the current directory or <> quoting for library files.
Force bits can only be given to numbers.
You tried to give a force bit to a symbol and then assign a string
or list to it.
Found '}' instead of end-of-file.
ACME encountered a '}' character when it expected the file to end
instead (because no blocks were open).
Garbage data at end of statement.
There are still arguments when there should not be any more.
Garbage data at end of statement (unexpected 'CHAR').
There are still arguments when there should not be any more. The
given character is the one where end-of-line was expected.
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.
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 postfix.
You used a postfix other than "+1", "+2" or "+3".
Index is undefined.
You attempted an indexing operation with some undefined symbol.
Index out of range.
The value for an indexing operation wasn't in the allowed range.
Macro already defined.
Macros can only be defined once. If you define a macro twice, ACME
will help you find the definitions by giving a warning for the
@ -239,6 +305,10 @@ Macro parameter twice.
The same symbol name is used two (or more) times in the same macro
parameter list.
Negative size argument.
The size argument of "!bin" or "!skip" must be zero or positive,
but cannot be negative.
Negative value - cannot choose addressing mode.
Because the argument is a negative value, ACME does not know what
addressing mode (8 bits, 16 bits, on a 65816 even 24 bits) to use.
@ -246,10 +316,18 @@ Negative value - cannot choose addressing mode.
your program to use positive addresses instead.
No string given.
ACME expects a string but doesn't find it.
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".
Operation not supported: Cannot apply "OP" to "TYPE".
Operation not supported: Cannot apply "OP" to "TYPE" and "TYPE".
You tried to use an operator on the wrong type(s) of argument(s),
like indexing a float or negating a string.
Program counter is unset.
You didn't set the program counter, so ACME didn't know where to
@ -261,27 +339,35 @@ Quotes still open at end of line.
Source file contains illegal character.
Your source code file contained a null byte.
String length is not 1.
You tried something like LDA#"X" with an illegal string length.
Symbol already defined.
You defined a symbol that already had a different value. To change
a symbol's value, use the "!set" pseudo opcode.
You defined a symbol that already had a different type or value.
To change a symbol's type or value, use the "!set" pseudo opcode.
Syntax error.
Guess what - there's a 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 in an arithmetic expression, but the string
contained more than a single character.
You used a text string containing more than one character in a
situation where only a string with length one is allowed.
Too late for postfix.
You can only postfix symbols at the start, before they are used for
@ -290,27 +376,57 @@ Too late for postfix.
Too many '('.
A formula ends before all parentheses were closed.
Too many ')'.
There are more closing than opening parentheses in a formula.
Un-pseudopc operator '&' can only be applied to labels.
You tried to apply the operator '&' to something that is not a
label. This operator only works on labels and on '*' (the program
counter), it cannot be used on other objects.
Un-pseudopc operator '&' has no !pseudopc context.
You either tried to apply the operator '&' to something that is
not an implicitly defined label, but the result of an explicit
symbol assignment (like the result of a calculation).
Or you applied the operator to a label that was defined outside of
a !pseudopc block, or, more generally, the number of '&'
characters used was larger than the number of !pseudopc blocks
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.
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.
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 ']'.
Unterminated list.
A list was started with '[' but did not end with ']'.
Value not defined (SYMBOL NAME).
A value could not be worked out. Maybe you mistyped a symbol name.
Whether this is given as a "normal" or as a serious error depends
@ -330,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
@ -341,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.
@ -359,14 +476,17 @@ Syntax error.
Too deeply nested. Recursive macro calls?
The only reason for ACME to have a limit on macro call nesting
at all is to find infinite recursions. Current limit is 64.
at all is to find infinite recursions.
The default limit is 64, this can be changed using the
"--maxdepth" CLI switch.
Too deeply nested. Recursive "!source"?
The only reason for ACME to still have a limit on "!source"
nesting at all is to find infinite recursions. Current limit is
64.
nesting at all is to find infinite recursions.
The default limit is 64, this can be changed using the
"--maxdepth" CLI switch.
Value not yet defined.
Value not defined.
A value could not be worked out. Maybe you mistyped a symbol name.
Whether this is given as a "normal" or as a serious error depends
on the currently parsed pseudo opcode.
@ -389,45 +509,89 @@ No output file specified (use the "-o" option or the "!to" pseudo opcode).
Section: Bugs in ACME
----------------------------------------------------------------------
The warning "Bug in ACME, code follows" is always followed by a
serious error message, stopping assembly. The second message
actually gives a hint about the bug's location in the source code.
If you ever get this combination of warning and serious error,
please send me an e-mail and tell me about it. If possible,
include a piece of source code that triggers it.
The warning "Bug in ACME, code follows" is always followed by a
serious error message, stopping assembly. The second message actually
gives a hint about the bug's location in the source code.
If you ever get this combination of warning and serious error, please
send me an e-mail and tell me about it. If possible, include a piece
of source code that triggers it.
Please don't get this wrong - there are no known bugs. I just left
some debugging code in place in case there is a bug I failed to notice
during testing. In practice, this warning is not expected to be given
at all. That's the reason why I want to be notified if it *does*
decide to show up.
Please don't get this wrong - there are no known bugs. I just left
some debugging code in place in case there is a bug I failed to
notice during testing. In practice, this warning is not expected
to be given at all. That's the reason why I want to be notified if
it *does* decide to show up.
The hint messages are of no real interest to the end user, but here
they are for completeness' sake:
The hint messages are of no real interest to the end user, but here
they are for completeness' sake.
IllegalGroupIndex
The mnemonic tree contains a group that I didn't add.
ArgStackEmpty
There was no data for a monadic operator to work on.
ArgStackNotEmpty
The expression parser has finished though there are still
arguments left to process.
ExtendingListWithItself
There were multiple references to the same list.
ForceBitZero
The function to handle force bits was called without a force bit.
IllegalBlockTerminator
A RAM block (macro or loop) was terminated incorrectly.
IllegalOperatorHandle
The expression parser found an operator that does not exist.
IllegalGroupIndex
The mnemonic tree contains a group that I didn't add.
IllegalIfMode
A sanity check in the if/ifdef/ifndef/else code failed.
IllegalImmediateMode
The mnemonic tree contains invalid info about the size of immediate
arguments.
OperandStackNotEmpty
The expression parser has finished though there are still operands
left to parse.
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.
IllegalSymbolNameLength
A sanity check on string lengths failed.
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.
OperatorIsNotDyadic
OperatorIsNotMonadic
A function was passed the wrong type of operator.
OperatorStackNotEmpty
The expression parser has finished though there are still
operators left to parse.
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.
StrangeInputMode
The input state machine has reached a state that does not exist.
StrangeParenthesis
StrangeOperator
The expression parser found a non-existing operator.

View File

@ -30,4 +30,6 @@ Just in case you wonder:
Please *don't* look at it. :)
"trigono.o" is a simple example written to test the floating-point
capabilities.
capabilities. Because floats are prone to rounding errors, there
are two different "expected" outputs: these were generated on
different architectures, they only differ in one byte ($7f/$80).

View File

@ -17,7 +17,7 @@ with floats and returns a float. Applies to sin(), cos(), tan(),
arcsin(), arccos(), arctan() and float(): These are always computed in
float mode and always return floats.
b) if a maths operation is useles when done with floats, it is done
b) if a maths operation is useless when done with floats, it is done
with integers and returns an integer. Applies to NOT, AND, OR, XOR,
MOD, DIV, LSR, lowbyteof, highbyteof, bankbyteof and int(). These are
always computed in integer mode and always return integers.

View File

@ -6,15 +6,15 @@
- free software -
(C) 1998-2016 Marco Baye
(C) 1998-2020 Marco Baye
----------------------------------------------------------------------
Section: Copyright
----------------------------------------------------------------------
ACME - a crossassembler for producing 6502/6510/65c02/65816 code.
Copyright (C) 1998-2016 Marco Baye
ACME - a crossassembler for producing 6502/65c02/65816 code.
Copyright (C) 1998-2020 Marco Baye
The ACME icon was designed by Wanja "Brix" Gayk
This program is free software; you can redistribute it and/or modify
@ -39,9 +39,9 @@ Section: Introduction
ACME is a crossassembler for the 65xx range of processors. It knows
about the standard 6502, the 65c02 and the 65816. It also supports
the undocumented ("illegal") opcodes of the 6510 processor (a 6502-
variant that is used in the Commodore C=64), and the extensions added
in the C64DTV2.
the undocumented ("illegal") opcodes of the NMOS versions of the 6502,
like the 6510 variant that is used in the Commodore C=64, and it also
supports extensions to the intruction set done by other parties.
This text and the other files in the same directory only describe the
basic functions independent of the platform used. There should be
@ -61,14 +61,16 @@ The files in the docs directory and what they contain:
Help.txt ...is this text.
Illegals.txt Support for undocumented opcodes
Lib.txt Information about the library
QuickRef.txt All the basic stuff about ACME
QuickRef.txt All the basic stuff about ACME <- START HERE!
Source.txt How to compile ACME
Upgrade.txt Incompatibilities to earlier versions
cputypes.txt Instruction sets of target CPUs
cputypes/ Instruction sets of target CPUs
IMPORTANT: If you upgrade from ACME 0.05 or earlier, don't forget to
read the file "Upgrade.txt" - release 0.07 and all later ones are
slightly incompatible to 0.05 and earlier.
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
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
"QuickRef.txt", it contains the main help text.
@ -79,15 +81,14 @@ Section: What it can and does
----------------------------------------------------------------------
ACME is a crossassembler.
ACME can produce code for the 6502, 6510, 65c02 and 65816 processors.
ACME can produce code for the 6502, 65c02 and 65816 processors.
It does this *fast*.
It can produce at most 64 KBytes of code.
You can use global labels, local labels and anonymous labels.
It is fast.
You can use global and local macros.
You can use conditional assembly.
You can use looping assembly (There are two ways to do this; a very
simple and a very flexible one).
You can use looping assembly.
You can include other source files.
You can include binary files (either whole or parts) directly into the
output.
@ -100,6 +101,7 @@ ACME's maths parser has no problems concerning parentheses and
indirect addressing modes.
ACME's maths parser knows a shit load of different operations.
ACME supports both integer and floating point maths operations.
In addition to numbers, symbols can also hold strings or lists.
You can dump the global symbols into a file.
ACME supports a library of commonly used macros and symbols.
It always takes as many passes as are needed.

View File

@ -8,7 +8,7 @@
In release 0.87, support for some of the undocumented opcodes of the
6502 processor was added.
NMOS 6502 processor was added.
In release 0.89, some more were added.
In release 0.94.8, another one was added (lxa).
In release 0.95.3, C64DTV2 support was added, which includes these
@ -23,15 +23,14 @@ opcodes (mnemonics in parentheses are used by other sources):
mnemonic | 8 8,x 8,y 16 16,x 16,y (8,x) (8),y | performs:
----------------+--------------------------------------+-----------
slo (aso) | 07 17 0f 1f 1b 03 13 | asl + ora
rla | 27 37 2f 3f 3b 23 33 | rol + and
rla (rln) | 27 37 2f 3f 3b 23 33 | rol + and
sre (lse) | 47 57 4f 5f 5b 43 53 | lsr + eor
rra | 67 77 6f 7f 7b 63 73 | ror + adc
rra (rrd) | 67 77 6f 7f 7b 63 73 | ror + adc
sax (axs, aax) | 87 97 8f 83 | stx + sta
lax | a7 b7 af bf a3 b3 | ldx + lda
dcp (dcm) | c7 d7 cf df db c3 d3 | dec + cmp
isc (isb, ins) | e7 f7 ef ff fb e3 f3 | inc + sbc
las (lar, lae) | bb | A,X,S = {addr} & S
These five are said to be unstable:
tas (shs, xas) | 9b | S = A & X {addr} = A&X& {H+1}
sha (axa, ahx) | 9f 93 | {addr} = A & X & {H+1}
shx (xas, sxa) | 9e | {addr} = X & {H+1}
@ -40,31 +39,37 @@ These five are said to be unstable:
| addressing mode |
mnemonic | implied #8 8 8,x 16 16,x | performs:
----------------+---------------------------------+-----------------------
anc | 0b* | A = A & arg, then C=N
asr (alr) | 4b | A = A & arg, then lsr
anc (ana, anb) | 0b* | A = A & arg, then C=N
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
top (nop, skw) | 0c** 0c 1c | skips next two bytes
nop (skb, skw) | ea 80 04 14 0c 1c | see the two lines above
jam (kil, hlt) | 02 | crash (wait for reset)
These two are somewhat unstable, because they involve an arbitrary value:
ane (xaa) | 8b*** | A = (A | ??) & X & arg
ane (xaa, axm) | 8b*** | A = (A | ??) & X & arg
lxa (lax, atx) | ab*** | A,X = (A | ??) & arg
Example:
!cpu 6510 ; activate additional mnemonics...
!cpu nmos6502 ; activate additional mnemonics...
lax (some_zp_label,x) ; ...and use them. No, this
dcp (other_zp_label),y ; example does not make sense.
*) Up until ACME version 0.95.1, anc#8 generated opcode 0x2b. Since
ACME version 0.95.2, anc#8 generates opcode 0x0b. Both opcodes work
the same way on a real 6510 CPU, but they do not work on the C64DTV2.
the same way on a real NMOS 6502 CPU, but they do not work on the
C64DTV2.
Using the "--dialect" CLI switch does not change the generated opcode!
**) Note that DOP ("double nop") and TOP ("triple nop") can be used
with implied addressing, but the generated opcodes are those for
immediate and 16-bit absolute addressing, respectively. Using dop/top
with x-indexed addressing might have its uses when timing is critical
(crossing a page border adds a penalty cycle).
immediate and 16-bit absolute addressing, respectively, This way DOP
and TOP can be used to skip the following one- or two-byte
instruction.
Using DOP/TOP with x-indexed addressing might have its uses when
timing is critical (crossing a page border adds a penalty cycle).
Unless using implied addressing, DOP/TOP can also be written as NOP.
***) ANE and LXA first perform an ORA with an arbitrary(!) value and
then perform an AND with the given argument. So they are unstable and
@ -75,10 +80,8 @@ ACME will output a warning if these opcodes get assembled with a
nonzero argument.
There is no guarantee that these opcodes actually work on a given 6502
(or 6510, or 8500, or 8502) CPU. But as far as I know, nobody ever
found an unmodified C64/C128 where these illegals didn't work. That's
why I used "6510" as the CPU keyword instead of "6502illegal" or
something like that.
(or 6510, or 8500, or 8501, or 8502) CPU. But as far as I know, nobody
ever found an unmodified C64/C128 where these illegals didn't work.
These illegals will definitely *not* work on 65c02 and 65816 CPUs. But
I really should not have to tell you that ;)
@ -88,7 +91,7 @@ people use different names for them. I hope my choices are not too
exotic for your taste.
Just for the sake of completeness: Here are all the remaining opcodes
(the ones ACME won't generate even with "6510" cpu):
(the ones ACME won't generate even with "nmos6502" cpu chosen):
Opcode| Description C64DTV2
------+--------------------------------------------------------------
@ -133,4 +136,10 @@ For more information about what these opcodes do, see these documents:
Extra Instructions Of The 65XX Series CPU, Adam Vardy, 27 Sept. 1996
6502 Undocumented Opcodes, by Freddy Offenga, 5/17/1997
AAY64 (All About Your 64)
NMOS 6510 Unintended Opcodes
...but the most comprehensive work is:
"No More Secrets - NMOS 6510 Unintended Opcodes"
Download it from https://csdb.dk/release/?id=185341
or ask google for the latest version.

View File

@ -106,6 +106,10 @@ Then there are local symbols (their names starting with a '.'
character). These can only be accessed from inside the macro or zone
they were defined in (for more about macros and zones, see the file
"AllPOs.txt").
There are also "cheap locals": their names start with an '@'.
The area where these can be accessed is limited automatically by the
previous and the following global label (cheap locals are "cheap"
because you don't have to put in any extra work to limit their range).
And then there are anonymous labels (their names being sequences of
either '-' or '+' characters). They are also local (bound to their
macro/zone), but in addition to that, the "-" labels can only be used
@ -113,6 +117,7 @@ for backward references, while the "+" labels can only be used for
forward references.
In contrast to global and local labels, anonymous labels can not be
defined explicitly (as in SYMBOL = VALUE).
Each macro call automatically gets its own scope for local symbols.
Save the given example source code to a file called "tiny.a" and start
acme by typing
@ -149,7 +154,7 @@ found in the file "AllPOs.txt". Here's just a short overview:
!convtab !pet !raw !scr !scrxor !text
...for converting and outputting strings.
!do !endoffile !for !if !ifdef !ifndef !set
!do !endoffile !for !if !ifdef !ifndef !set !while
...for flow control; looping assembly and conditional assembly.
!binary !source !to
@ -170,7 +175,7 @@ found in the file "AllPOs.txt". Here's just a short overview:
!warn !error !serious
...for generating warnings, errors and serious errors.
!addr
!address
...to mark symbols as addresses, for the optional type check system.
@ -187,33 +192,57 @@ Available options are:
This is more or less useless, because the help is also shown
if ACME is run without any arguments at all.
-f, --format FORMAT set output file format ("plain", "cbm" or "apple")
-f, --format FORMAT set output file format
Use this with a bogus format type to get a list of all
supported ones (as of writing: "plain", "cbm" and "apple")
-o, --outfile FILE set output file name
Output file name and format can also be given using the "!to"
pseudo opcode. If the format is not specified, "!to" defaults
to "cbm", while the command line option defaults to "plain".
-r, --report set report file name
This creates a text listing containing the original line
number, the resulting memory address, the byte value(s) put
there and the original text line from the source file.
-l, --symbollist FILE set symbol list file name
This can also be given using the "!symbollist"/"!sl" pseudo
opcode. The switch was called "--labeldump" in older versions,
that name still works, too.
--setpc NUMBER set program counter
This can also be given in the source code using "* = NUMBER".
--vicelabels FILE set file name for label dump in VICE format
The resulting file uses a format suited for the VICE emulator.
--cpu CPU_TYPE set processor type
--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
opcode. Defaults to 6502.
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.
--maxerrors NUMBER set number of errors before exiting
If not given, defaults to 10.
--maxdepth NUMBER set recursion depth for macro calls and the
"!source" pseudo opcode. If not given, defaults to 64.
--maxdepth NUMBER set recursion depth for macro calls and !src
The default value for this is 64.
--ignore-zeroes do not determine number size by leading zeroes
Normally, using leading zeroes forces ACME to generate
oversized addressing modes, like 3-byte absolute instructions
instead of 2-byte zero page instructions.
Using this CLI switch disables this behavior.
--strict-segments turn segment overlap warnings into errors
When changing the program counter, segment overlap warnings may
be generated. Using this CLI switch turns those warnings into
errors (which is recommended).
This strict behavior may become the default in future releases!
-vDIGIT set verbosity level
Sets how much additional informational output is generated.
@ -239,6 +268,12 @@ Available options are:
"-DSYSTEM=128" could build the C128 version of the software
(using conditional assembly in your source code file).
-I PATH/TO/DIR add search path for input files
This option allows to add a directory to the search list for
input files. If an input file cannot be found in the current
working directory, all directories in the search list are
tried (the first match is used).
-W fine-tune amount and type of warnings
-Wno-label-indent
Disables warnings about labels not being in the leftmost
@ -246,6 +281,11 @@ Available options are:
-Wno-old-for
Disables warnings about the old "!for" syntax and at the
same time enables warnings about the _new_ "!for" syntax.
Internally, this does exactly the same as what happens
when the "--dialect 0.94.8" CLI switch is used...
-Wno-bin-len
Do not complain about unusual number of digits in a binary
literal.
-Wtype-mismatch
Enables type checking system (warns about wrong types).
@ -253,6 +293,28 @@ Available options are:
With this option, errors are written to the standard output
stream instead of to the standard error stream.
--msvc output errors in MS VS format
This changes the format of the error output to that used by
a certain commercial IDE.
--color uses ANSI color codes for error output
If your terminal emulation supports ANSI escape codes, use
this option to have warnings and errors displayed in color.
--fullstop use '.' as pseudo opcode prefix
This changes the prefix character used to mark pseudo opcodes
from '!' to '.' (so sources intended for other assemblers can
be converted with less effort).
--dialect VERSION behave like different version
This CLI switch tells ACME to mimic the behavior of an older
version. Use this with a bogus version to get a list of all
supported ones.
--test enable experimental features
This is for people who want to help test new features before
they are officially announced.
-V, --version show version and exit.
Platform-specific versions of ACME might offer more options.
@ -261,61 +323,119 @@ given on the command line.
----------------------------------------------------------------------
Section: The maths parser
Section: The expression parser
----------------------------------------------------------------------
ACME has a relatively powerful maths parser. This parser is used
whenever ACME expects to read a numerical value. Supported operations
include addition, subtraction, multiplication, divisions, comparisons,
shifts, negation, boolean operations and some assembler-specific stuff
like extracting the "low byte", the "high byte" or the "bank byte"
of a value.
Calculations are done using either signed 32-bit integer arithmetic or
floating point arithmetic using the C "double" data type. Symbol
values are stored the same way.
whenever ACME expects to read a value. Supported operations include
addition, subtraction, multiplication, divisions, comparisons, shifts,
negation, boolean operations and some assembler-specific stuff like
extracting the "low byte", the "high byte" or the "bank byte" of a
value.
Calculations are done using either signed (at least 32-bit) integer
arithmetic or floating point arithmetic using the C "double" data
type. Symbol values are stored the same way.
This is a list of the value formats currently known by ACME:
Examples Notes
---------------------------------------------------------------------
128 a decimal value, integer
128.5 a decimal value, floating point
$d011 hexadecimal values are indicated by either a
0xffd2 leading "$" or a leading "0x".
&1701 an octal value, indicated by "&"
%1010 binary values are indicated by either a leading "%"
%....#... or a leading "0b". In binary values, you can
0b01100110 substitute the characters "0" and "1" by "." and
"#" respectively. This way the values are much
more readable, especially when building bitmapped
objects (like C64 sprites or fonts) in your source
code.
'p' single characters in single quotes are converted to
their character code. The actual numeric value
depends on the current conversion table chosen
using the "!ct" pseudo opcode.
"player 2" double quotes indicate text strings. See below for
more information on single vs. double quotes.
[2, 3, 5, 7] brackets indicate lists. These are useful to group
[0, [x, y], 9] data, for example when passing an arbitrary number
of arguments to a macro.
poll_joy2 a global symbol
.fail a local symbol, indicated by leading "."
@loop a "cheap local", indicated by leading "@"
* the current program counter. During offset assembly,
"*" gives the value of the "Pseudo PC". Just to
make sure: The value of the program counter is
always the value that was valid at the start of
the current statement, so
!word *, *, *, *
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.
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
one-char strings in a lot of places where actually single characters
are expected.
This is a list of the operators currently known by ACME:
Priority Example Meaning Alias
------------------------------------------------------------
14 sin(v) Trigonometric sine function
14 cos(v) Trigonometric cosine function
14 tan(v) Trigonometric tangent function
14 arcsin(v) Inverse of sin()
14 arccos(v) Inverse of cos()
14 arctan(v) Inverse of tan()
14 address(v) Mark as address addr(v)
14 int(v) Convert to integer
14 float(v) Convert to float
13 ! v Complement of NOT
12 v ^ w To the power of
11 - v Negate
10 v * w Multiply
10 v / w Divide
10 v DIV w Integer-Divide
10 v % w Remainder of DIV MOD
9 v + w Add
9 v - w Subtract
8 v << w Shift left ASL, LSL
8 v >> w Arithmetic shift right ASR
8 v >>> w Logical shift right LSR
7 < v Lowbyte of
7 > v Highbyte of
7 ^ v Bankbyte of
6 v <= w Lower or equal
6 v < w Lower than
6 v >= w Higher or equal
6 v > w Higher than
5 v != w Not equal <>, ><
4 v = w Equal
3 v & w Bit-wise AND AND
2 Bit-wise exclusive OR XOR
1 v | w Bit-wise OR OR
Priority Example Meaning Alias Note
----------------------------------------------------------------------
16 is_number(v) these three functions return 1 *3
16 is_list(v) if v is the correct symbol *3
16 is_string(v) type and 0 otherwise *3
16 len(v) length of list or string *2
16 sin(v) trigonometric sine function
16 cos(v) trigonometric cosine function
16 tan(v) trigonometric tangent function
16 arcsin(v) inverse of sin()
16 arccos(v) inverse of cos()
16 arctan(v) inverse of tan()
16 address(v) mark as address addr(v)
16 int(v) convert to integer
16 float(v) convert to float
15 &symbol "unpseudopc" symbol (see docs on "!pseudopc")
14 v[w] access v with index w *2
13 ! v bit-wise complement NOT
12 v ^ w to the power of
11 - v negate
10 v * w multiply
10 v / w divide
10 v DIV w integer divide
10 v % w remainder of DIV MOD
9 v + w add *3
9 v - w subtract
8 v << w shift left ASL, LSL
8 v >> w arithmetic shift right ASR
8 v >>> w logical shift right LSR
7 < v low byte of
7 > v high byte of
7 ^ v bank byte of
6 v <= w lower or equal
6 v < w lower than
6 v >= w higher or equal
6 v > w higher than
5 v != w not equal <>, >< *3
4 v = w equal *3
3 v & w bit-wise AND AND
2 bit-wise exclusive OR XOR
1 v | w bit-wise OR OR
Notes:
"*2" means this operator only works on lists and strings.
"*3" means this operator works on all three data types (numbers, lists
and strings).
All other operators only work on numbers.
Operations with higher priority are done first. Of course you can
change this using parentheses. If you prefer the aliases over the
shorthand characters, note that they must be written in capital
letters.
change this using parentheses.
Note that though there are operators to extract the "low byte", the
"high byte" and the "bank byte", there is no operator to extract the
fourth byte. If you want to access that, shift it down using ">>>" or
@ -332,41 +452,8 @@ Calculating 0^0 (zero to the power of zero) will give 1. If
you don't know why I'm telling you this, ask a mathematician. :)
This is a list of the value formats currently known by ACME:
Examples Notes
---------------------------------------------------------------------
128 a decimal value, integer
128.5 a decimal value, floating point
$d011 hexadecimal values are indicated by either a
0xffd2 leading "$" or a leading "0x".
&1701 an octal value, indicated by "&"
%010010 binary values are indicated by either a leading "%"
%....#... or a leading "0b". In binary values, you can
0b01100110 substitute the characters "0" and "1" by "." and
"#" respectively. This way the values are much
more readable, especially when building bitmapped
objects (like C64 sprites or fonts) in your source
code.
"p" character values are indicated by double or single
'q' quotes. The actual numeric value depends on the
current conversion table (none/petscii/screen),
chosen using the "!ct" pseudo opcode.
poll_joy2 a global symbol
.fail a local symbol, indicated by leading dot
* the current program counter. During offset assembly,
"*" gives the value of the "Pseudo PC". Just to
make sure: The value of the program counter is
always the value that was valid at the start of
the current statement, so
!word *, *, *, *
will give the same value four times. I think most
assemblers do it this way.
----------------------------------------------------------------------
Section: Almost, but not quite, entirely useless syntax
Section: Almost, but not quite, complete syntax
----------------------------------------------------------------------
Every ACME source code file consists of a non-negative number of
@ -376,7 +463,7 @@ or CRLF characters.
Every line consists of a non-negative number of "statements" and an
optional comment. Statements have to be separated from each other
using colon (":") characters, the comment has to be prefixed with a
semicolon (";") character.
semicolon (";") character or two slashes ("//").
Every statement consists of an optional "label" and an optional
"command". These are separated from each other using any number of
@ -386,15 +473,19 @@ issued (to spot typing errors - see Errors.txt for more info).
Every symbol name consists of these characters: "a" to "z", "A" to
"Z", "0" to "9", the underscore character "_" and all characters with
values beyond 127. The first character must not be a digit though. But
it can be a dot ("."), making the symbol a local one. Two other
possibilities for label names are "all-characters-are-minus" (then it
is an anonymous backward label) and "all-characters-are-plus" (then it
is an anonymous forward label).
it can be '.' or '@', making the symbol a local one.
Local symbols beginning with '.' are only valid inside the current
zone (marked using the "!zone" pseudo opcode) or the current macro.
Local symbols beginning with '@' are only valid between the enclosing
global labels (or inside the current macro).
Two other possibilities for label names are "all-characters-are-minus"
(then it is an anonymous backward label) and "all-characters-are-plus"
(then it is an anonymous forward label).
Every command is one of the following:
An assembler opcode
An assembler mnemonic with an optional argument
A pseudo opcode, beginning with a "!" character
A symbol definition (symbol=value)
An explicit symbol definition (SYMBOL = VALUE)
A pc definition, beginning with a "*" character
A macro call, beginning with a "+" character
...and the syntax of those things varies. :)

View File

@ -4,116 +4,197 @@
...the ACME Crossassembler for Multiple Environments
--- compatibility problems ---
--- upgrading from earlier versions ---
If you haven't used ACME before, you don't need to read this text.
It is only of use to people who upgraded from ACME 0.05 (or earlier)
to ACME 0.07 (or later).
You might encounter some slight incompatibilities: I have done a few
changes to ACME's workings.
Because backwards compatibility is the root of all evil (*g*), I did
not include any possibility to enforce the old behaviour. If one of
the following changes applies to your source files, assemble them with
this new release of ACME and then compare new and old output files.
Sorry for this inconvenience, but at least I think that there won't be
any further changes in the future.
----------------------------------------------------------------------
Section: Offset assembly / segment assembly
Upgrading from earlier releases to ACME release 0.97
----------------------------------------------------------------------
a) Single quotes vs. double quotes:
Since "anything in double quotes" is now considered to be a string,
problems can arise when trying to do a calculation with a character
code. Here are some examples:
lda #' ' ; loads 32 like before (ASCII code of space)
lda #' ' + 1 ; loads 33 like before (32 plus one)
lda #" " ; loads 32 like before (ASCII code of space)
lda #" " + 1 ; used to load 33, now fails with error!
The third example still works, because 1-char-strings are treated
just like single characters when returned by the expression parser as
an argument for a mnemonic.
However, the fourth example now fails because the expression parser
tries to add a string to an integer, which is an undefined operation.
Some examples for a related problem:
a = ' ' ; a is 32 (ASCII code of space)
b = ' ' + 1 ; b is 33 (32 plus one)
c = "!" ; c used to be 33, now it's a 1-char string
d = "!" + "!" ; d used to be 66, now it's a 2-char string
If you do not get any errors when compiling your old sources, you do
not need to worry about this problem.
If you _do_ get errors, just use single quotes instead of double
quotes. If you had to use double quotes because the quoted character
itself is a single quote, write '\'' instead (backslash escaping, see
below).
b) Backslash escaping:
Backslashes in single or double quotes are now used as escape
characters. You need to replace any backslash in older sources with a
sequence of two backslashes, so "some\string" becomes "some\\string",
and a single character '\' becomes '\\'.
If you have used backslashes as directory separators in path names (in
Windows/DOS environments), these also need changing - but instead of
using a double backslash, just use a single forward slash ('/')
instead. This has the added benefit of making the sources platform-
independent (*and* it's compatible to older ACME releases as well).
c) Character values are now unsigned:
When parsing a character in single quotes, ACME returns its character
code, according to the chosen encoding (raw/petscii/screencode). If
this resulted in a byte with its most significant bit set, the actual
number was architecture-dependent. Here's an example:
!ct pet ; choose PetSCII encoding
x = 'A' ; PetSCII 'A' is 0xc1, so MSB is set
Now x was either -63 or +193, depending on the host cpu architecture.
Since release 0.97, this example will give 193 on all architectures.
In most cases, this is not a problem, because the actual bit pattern
of the lower eight bits is the same. But if you have written any code
where the numerical value of a PetSCII character is used for
computations _in_the_source_code_, please check those computations.
Use the "--dialect 0.94.12" CLI switch to get the old behavior
concerning a) double quotes and b) backslashes. There is no way to get
the old behavior concerning c) character values, because, as explained
above, the old behavior was architecture-dependent, which is a bad
idea(tm).
----------------------------------------------------------------------
Upgrading from earlier releases to ACME release 0.95.2
----------------------------------------------------------------------
In 6510 mode, ACME now outputs 0x0b instead of 0x2b when assembling
the undocumented ("illegal") ANC #imm8 instruction. Both opcodes do
the same thing, this was only changed because all other mnemonics use
the lowest-numbered possible opcode as well.
Forcing the old behavior via the "--dialect" switch is not supported.
----------------------------------------------------------------------
Upgrading from earlier releases to ACME release 0.94.12
----------------------------------------------------------------------
The pseudo opcode "!for" has a new syntax. The old syntax still works,
but gives a warning.
You can use the "--dialect 0.94.8" CLI switch to get the old behavior.
----------------------------------------------------------------------
Upgrading from earlier releases to ACME release 0.94.8
----------------------------------------------------------------------
The pseudo opcodes "!cbm", "!subzone" and "!realpc" no longer give
warnings, but have now been disabled.
You can use the "--dialect 0.94.6" CLI switch to get the old behavior.
----------------------------------------------------------------------
Upgrading from earlier releases to ACME release 0.94.6
----------------------------------------------------------------------
The "to-the-power-of" operator ('^') is now right-associative, so
b^c^d will now give b^(c^d) instead of (b^c)^d
If you have never used the operator in this way, you don't need to
worry about it.
You can use the "--dialect 0.86" CLI switch to get the old behavior.
----------------------------------------------------------------------
Upgrading from earlier releases to ACME release 0.89
----------------------------------------------------------------------
The "logical shift right" operator has been changed. Note: This is
about ACME's expression parser and has nothing to do with the 6502
mnemonic called "LSR".
Older versions were supposed to work like this:
a = b >> c ; alias "LSR", do a logical shift right
But what they actually did depended on the compiler that was used to
create the ACME binary: many binaries did an "arithmetic shift right"
instead. This has now been fixed and changed to:
a = b >> c ; alias "ASR", do an arithmetic shift right
a = b >>> c ; alias "LSR", do a logical shift right
If you have never applied the old ">>"/"LSR" operator to a negative
value, you do not need to worry about this. If you have, please check
what you expected to happen in those instances (arithmetic or logical
shift) and update your source codes accordingly (use either ">>"/"ASR"
or ">>>"/"LSR").
Forcing the old behavior via the "--dialect" switch is not possible,
because as explained above, the old behavior was compiler-dependent
anyway.
----------------------------------------------------------------------
Upgrading from earlier releases to ACME release 0.07
----------------------------------------------------------------------
Re-defining the program counter via "* = NEW_VALUE" no longer starts
offset assembly. Instead, ACME will change its pointer into the output
buffer to the given value, so you can write your code in distinct
segments. These segments can be given in any order. After assembly,
ACME stores everything from the lowest address used to the highest
address used. Have a look at "AllPOs.txt" for an example on how to use
this facility.
Offset assembly is now done using a new pseudo opcode called
"!pseudopc". Have a look at "AllPOs.txt" for further information on
its syntax and usage.
The old way of just redefining the program counter by using more than
one "* = EXPRESSION" statements does something totally different now:
Whenever the program counter is redefined, ACME will actually change
its pointer into the output buffer, so you can write your code in
distinct segments. These segments can be given in any order. After
assembly, ACME stores everything from the lowest address used to the
highest address used. Have a look at "AllPOs.txt" for an example on
how to use this facility.
The pseudo opcode "!end" has been removed. Use "!eof" instead.
----------------------------------------------------------------------
Section: Argument order of MVP/MVN
----------------------------------------------------------------------
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 instruction
+bit16 ; output $2c to mask following 2-byte instruction
The syntax of the 65816 opcodes MVN and MVP is usually given as
When using the 65816 cpu, ACME now uses the correct argument order for
the MVN and MVP mnemonics, which is:
mnemonic source_bank, destination_bank
MVN source_bank, destination_bank
All previous versions of ACME did it the other way round: First the
destination bank, then the source bank. This has been fixed, ACME now
uses the syntax given above.
----------------------------------------------------------------------
Section: Typecast
----------------------------------------------------------------------
You can use leading zeros to make ACME use a bigger addressing mode
than needed. Until now, this did not work when using labels. The
source code
label1 = $fa
Using leading zeroes in hexadecimal or binary values makes ACME use
bigger addressing modes than needed. This has now been extended to
symbols as well:
label2 = $00fa
lda $fa
lda $00fa
lda label1
lda label2
will be assembled to:
ad fa 00 lda $00fa
was assembled to:
Forcing the old behavior via the "--dialect" switch is not supported.
lda $fa
lda $00fa
lda $fa
lda $fa
Release 0.07 of ACME now correctly assembles the given source code to:
lda $fa
lda $00fa
lda $fa
lda $00fa
----------------------------------------------------------------------
Section: !endoffile
Upgrading from earlier releases to ACME release 0.04 beta
----------------------------------------------------------------------
Previous versions of ACME knew a pseudo opcode called "!end" that
marks the end of a source code file. Because the word "end" doesn't
actually specify *what* is about to end, I changed this to
"!endoffile". You can also use a short version, called "!eof". The old
PO "!end" no longer works.
The pseudo opcode "!module" has been removed. Use "!zone" instead.
Forcing the old behavior via the "--dialect" switch is not supported.
----------------------------------------------------------------------
Section: Using the BIT command without parameters
Upgrading from earlier releases to ACME release 0.03 beta
----------------------------------------------------------------------
Release 0.07 of ACME will complain if you try to assemble BIT without
any parameter. Previous versions did just output the byte $2c - a
commonly known trick to mask the following 2-byte command on the 6502
processor. If you still want to do this, use
It is no longer possible to have more than one label in a single line.
Forcing the old behavior via the "--dialect" switch is not supported.
!src <6502/std.a> ; parse library file
to include some standard macros. Then you can use
+bit8 ; output $24 to mask following 1-byte command
and
+bit16 ; output $2c to mask following 2-byte command
respectively.
That's all. Again, sorry for the inconvenience...

View File

@ -7,32 +7,69 @@
--- 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
!cpu 6502
*** 6502
This is the instruction set of the original NMOS 6502 designed by MOS
(later CSG).
This is the official instruction set of the original NMOS 6502 CPU
designed by MOS (later CSG).
There are 151 documented opcodes.
ACME does not use "A" to indicate "accumulator addressing"; just write
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.
!cpu 6510
*** 65c02
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:
@ -63,33 +100,7 @@ There are 178 documented opcodes.
*** r65c02
This is a superset of 65c02, probably originally by Rockwell. It adds
bit manipulation instructions:
BBR0 $12, near_target branch on bit reset in zp
BBS0 $12, near_target branch on bit set in zp
RMB0 $12 reset memory bit in zp
SMB0 $12 set memory bit in zp
The digit in the mnemonic is the bit number, therefore it must be in
the 0..7 range.
Chips with this instruction set seem to have been available from
Rockwell, GTE/CMD and others.
There are 210 documented opcodes.
*** w65c02
This is a superset of r65c02, originating at WDC. It adds two new
instructions:
STP stop (wait for reset)
WAI wait for interrupt
There are 212 documented opcodes.
*** 65816
!cpu 65816
This is a superset of 65c02, originally designed by WDC (it seems to
have been available from GTE/CMD as well). Features:
@ -103,7 +114,33 @@ See "docs/65816.txt" for more info.
*** 65ce02
!cpu r65c02
This is a superset of 65c02, probably originally by Rockwell. It adds
bit manipulation instructions:
BBR4 $12, near_target branch on bit reset in zp
BBS5 $12, near_target branch on bit set in zp
RMB6 $12 reset memory bit in zp
SMB7 $12 set memory bit in zp
The digit in the mnemonic is the bit number, therefore it must be in
the 0..7 range.
Chips with this instruction set seem to have been available from
Rockwell, GTE/CMD and others.
There are 210 documented opcodes.
!cpu w65c02
This is a superset of r65c02, originating at WDC. It adds two new
instructions:
STP stop (wait for reset)
WAI wait for interrupt
There are 212 documented opcodes.
!cpu 65ce02
This is a superset of r65c02, originating at CSG. Features:
- Z register
@ -123,7 +160,7 @@ unconditional") instead. ACME accepts both mnemonics.
*** 4502
!cpu 4502
This is basically the same as 65ce02, but
- MAP replaces AUG
@ -133,12 +170,55 @@ There are 256 documented opcodes.
*** c64dtv2
!cpu m65
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.
This is a superset of 4502 specified by the MEGA65 project. It uses
NEG:NEG and NOP as prefix bytes to extend the instruction set.
Features:
- "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
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.
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.

View File

@ -0,0 +1,98 @@
4502 opcode table
There are no more undocumented opcodes.
All differences to the 65ce02 are marked using '!' signs (only
opcodes 5c and ea anyway)
00 brk 01 ora (zp, x) 02 cle 03 see
04 tsb zp 05 ora zp 06 asl zp 07 rmb0 zp
08 php 09 ora #imm8 0a asl 0b tsy
0c tsb abs16 0d ora abs16 0e asl abs16 0f bbr0 zp, rel8
10 bpl rel8 11 ora (zp), y 12 ora (zp), z 13 bpl rel16
14 trb zp 15 ora zp, x 16 asl zp, x 17 rmb1 zp
18 clc 19 ora abs16, y 1a inc 1b inz
1c trb abs16 1d ora abs16, x 1e asl abs16, x 1f bbr1 zp, rel8
20 jsr abs16 21 and (zp, x) 22 jsr (abs16) 23 jsr (abs16, x)
24 bit zp 25 and zp 26 rol zp 27 rmb2 zp
28 plp 29 and #imm8 2a rol 2b tys
2c bit abs16 2d and abs16 2e rol abs16 2f bbr2 zp, rel8
30 bmi rel8 31 and (zp), y 32 and (zp), z 33 bmi rel16
34 bit zp, x 35 and zp, x 36 rol zp, x 37 rmb3 zp
38 sec 39 and abs16, y 3a dec 3b dez
3c bit abs16, x 3d and abs16, x 3e rol abs16, x 3f bbr3 zp, rel8
40 rti 41 eor (zp, x) 42 neg 43 asr
44 asr zp 45 eor zp 46 lsr zp 47 rmb4 zp
48 pha 49 eor #imm8 4a lsr 4b taz
4c jmp abs16 4d eor abs16 4e lsr abs16 4f bbr4 zp, rel8
50 bvc rel8 51 eor (zp), y 52 eor (zp), z 53 bvc rel16
54 asr zp, x 55 eor zp, x 56 lsr zp, x 57 rmb5 zp
58 cli 59 eor abs16, y 5a phy 5b tab
5c! map 5d eor abs16, x 5e lsr abs16, x 5f bbr5 zp, rel8
60 rts 61 adc (zp, x) 62 rtn #imm8 63 bsr rel16
64 stz zp 65 adc zp 66 ror zp 67 rmb6 zp
68 pla 69 adc #imm8 6a ror 6b tza
6c jmp (abs16) 6d adc abs16 6e ror abs16 6f bbr6 zp, rel8
70 bvs rel8 71 adc (zp), y 72 adc (zp), z 73 bvs rel16
74 stz zp, x 75 adc zp, x 76 ror zp, x 77 rmb7 zp
78 sei 79 adc abs16, y 7a ply 7b tba
7c jmp (abs16, x) 7d adc abs16, x 7e ror abs16, x 7f bbr7 zp, rel8
80 bra rel8 81 sta (zp, x) 82 sta (o8, s), y 83 bra rel16
84 sty zp 85 sta zp 86 stx zp 87 smb0 zp
88 dey 89 bit #imm8 8a txa 8b sty abs16, x
8c sty abs16 8d sta abs16 8e stx abs16 8f bbs0 zp, rel8
90 bcc rel8 91 sta (zp), y 92 sta (zp), z 93 bcc rel16
94 sty zp, x 95 sta zp, x 96 stx zp, y 97 smb1 zp
98 tya 99 sta abs16, y 9a txs 9b stx abs16, y
9c stz abs16 9d sta abs16, x 9e stz abs16, x 9f bbs1 zp, rel8
a0 ldy #imm8 a1 lda (zp, x) a2 ldx #imm8 a3 ldz #imm8
a4 ldy zp a5 lda zp a6 ldx zp a7 smb2 zp
a8 tay a9 lda #imm8 aa tax ab ldz abs16
ac ldy abs16 ad lda abs16 ae ldx abs16 af bbs2 zp, rel8
b0 bcs rel8 b1 lda (zp), y b2 lda (zp), z b3 bcs rel16
b4 ldy zp, x b5 lda zp, x b6 ldx zp, y b7 smb3 zp
b8 clv b9 lda abs16, y ba tsx bb ldz abs16, x
bc ldy abs16, x bd lda abs16, x be ldx abs16, y bf bbs3 zp, rel8
c0 cpy #imm8 c1 cmp (zp, x) c2 cpz #imm8 c3 dew zp
c4 cpy zp c5 cmp zp c6 dec zp c7 smb4 zp
c8 iny c9 cmp #imm8 ca dex cb asw abs16
cc cpy abs16 cd cmp abs16 ce dec abs16 cf bbs4 zp, rel8
d0 bne rel8 d1 cmp (zp), y d2 cmp (zp), z d3 bne rel16
d4 cpz zp d5 cmp zp, x d6 dec zp, x d7 smb5 zp
d8 cld d9 cmp abs16, y da phx db phz
dc cpz abs16 dd cmp abs16, x de dec abs16, x df bbs5 zp, rel8
e0 cpx #imm8 e1 sbc (zp, x) e2 lda (o8, s), y e3 inw zp
e4 cpx zp e5 sbc zp e6 inc zp e7 smb6 zp
e8 inx e9 sbc #imm8 ea! nop/eom eb row abs16
ec cpx abs16 ed sbc abs16 ee inc abs16 ef bbs6 zp, rel8
f0 beq rel8 f1 sbc (zp), y f2 sbc (zp), z f3 beq rel16
f4 phw #imm16 f5 sbc zp, x f6 inc zp, x f7 smb7 zp
f8 sed f9 sbc abs16, y fa plx fb plz
fc phw abs16 fd sbc abs16, x fe inc abs16, x ff bbs7 zp, rel8
#imm8: 8-bit immediate value
#imm16: 16-bit immediate value (only for opcode f4)
zp: 8-bit zeropage address
abs16: 16-bit absolute address
rel8: 8-bit relative address offset
rel16: 16-bit relative address offset
o8: 8-bit offset (for stack-relative addressing, see opcodes 82 and e2)
The 4502 is a superset of the 65ce02. Opcode 5c (originally a "4-byte NOP
reserved for future expansion") has been changed to the "map" instruction,
now using implied addressing.
"map" uses the contents of the A/X/Y/Z registers to alter the memory map.
Because this might also change the current stack memory, all interrupts are
then implicitly disabled until an "eom" instruction gets executed.
"eom" stands for "end of mapping", but this is actually just a new alias for
opcode ea, the 6502-standard NOP.

View File

@ -0,0 +1,84 @@
6502 opcode table
All empty entries are undocumented opcodes.
Notice there are no documented opcodes in the fourth column.
00 brk 01 ora (zp, x) 02 03
04 05 ora zp 06 asl zp 07
08 php 09 ora #imm8 0a asl 0b
0c 0d ora abs16 0e asl abs16 0f
10 bpl rel8 11 ora (zp), y 12 13
14 15 ora zp, x 16 asl zp, x 17
18 clc 19 ora abs16, y 1a 1b
1c 1d ora abs16, x 1e asl abs16, x 1f
20 jsr abs16 21 and (zp, x) 22 23
24 bit zp 25 and zp 26 rol zp 27
28 plp 29 and #imm8 2a rol 2b
2c bit abs16 2d and abs16 2e rol abs16 2f
30 bmi rel8 31 and (zp), y 32 33
34 35 and zp, x 36 rol zp, x 37
38 sec 39 and abs16, y 3a 3b
3c 3d and abs16, x 3e rol abs16, x 3f
40 rti 41 eor (zp, x) 42 43
44 45 eor zp 46 lsr zp 47
48 pha 49 eor #imm8 4a lsr 4b
4c jmp abs16 4d eor abs16 4e lsr abs16 4f
50 bvc rel8 51 eor (zp), y 52 53
54 55 eor zp, x 56 lsr zp, x 57
58 cli 59 eor abs16, y 5a 5b
5c 5d eor abs16, x 5e lsr abs16, x 5f
60 rts 61 adc (zp, x) 62 63
64 65 adc zp 66 ror zp 67
68 pla 69 adc #imm8 6a ror 6b
6c jmp (abs16) 6d adc abs16 6e ror abs16 6f
70 bvs rel8 71 adc (zp), y 72 73
74 75 adc zp, x 76 ror zp, x 77
78 sei 79 adc abs16, y 7a 7b
7c 7d adc abs16, x 7e ror abs16, x 7f
80 81 sta (zp, x) 82 83
84 sty zp 85 sta zp 86 stx zp 87
88 dey 89 8a txa 8b
8c sty abs16 8d sta abs16 8e stx abs16 8f
90 bcc rel8 91 sta (zp), y 92 93
94 sty zp, x 95 sta zp, x 96 stx zp, y 97
98 tya 99 sta abs16, y 9a txs 9b
9c 9d sta abs16, x 9e 9f
a0 ldy #imm8 a1 lda (zp, x) a2 ldx #imm8 a3
a4 ldy zp a5 lda zp a6 ldx zp a7
a8 tay a9 lda #imm8 aa tax ab
ac ldy abs16 ad lda abs16 ae ldx abs16 af
b0 bcs rel8 b1 lda (zp), y b2 b3
b4 ldy zp, x b5 lda zp, x b6 ldx zp, y b7
b8 clv b9 lda abs16, y ba tsx bb
bc ldy abs16, x bd lda abs16, x be ldx abs16, y bf
c0 cpy #imm8 c1 cmp (zp, x) c2 c3
c4 cpy zp c5 cmp zp c6 dec zp c7
c8 iny c9 cmp #imm8 ca dex cb
cc cpy abs16 cd cmp abs16 ce dec abs16 cf
d0 bne rel8 d1 cmp (zp), y d2 d3
d4 d5 cmp zp, x d6 dec zp, x d7
d8 cld d9 cmp abs16, y da db
dc dd cmp abs16, x de dec abs16, x df
e0 cpx #imm8 e1 sbc (zp, x) e2 e3
e4 cpx zp e5 sbc zp e6 inc zp e7
e8 inx e9 sbc #imm8 ea nop eb
ec cpx abs16 ed sbc abs16 ee inc abs16 ef
f0 beq rel8 f1 sbc (zp), y f2 f3
f4 f5 sbc zp, x f6 inc zp, x f7
f8 sed f9 sbc abs16, y fa fb
fc fd sbc abs16, x fe inc abs16, x ff
#imm8: 8-bit immediate value
zp: 8-bit zeropage address
abs16: 16-bit absolute address
rel8: 8-bit relative address offset

164
docs/cputypes/cpu 65816.txt Normal file
View File

@ -0,0 +1,164 @@
65816 opcode table
There are no more undocumented opcodes.
All differences to the 65c02 are marked using a '+' sign, except
for the #imm8 -> #imm change.
00 brk 01 ora (dp, x) 02+ cop imm8 03+ ora offs8, s
04 tsb dp 05 ora dp 06 asl dp 07+ ora [dp]
08 php 09 ora #imm 0a asl 0b+ phd
0c tsb abs16 0d ora abs16 0e asl abs16 0f+ ora abs24
10 bpl rel8 11 ora (dp), y 12 ora (dp) 13+ ora (offs8, s), y
14 trb dp 15 ora dp, x 16 asl dp, x 17+ ora [dp], y
18 clc 19 ora abs16, y 1a inc 1b+ tcs
1c trb abs16 1d ora abs16, x 1e asl abs16, x 1f+ ora abs24, x
20 jsr abs16 21 and (dp, x) 22+ jsr abs24 23+ and offs8, s
24 bit dp 25 and dp 26 rol dp 27+ and [dp]
28 plp 29 and #imm 2a rol 2b+ pld
2c bit abs16 2d and abs16 2e rol abs16 2f+ and abs24
30 bmi rel8 31 and (dp), y 32 and (dp) 33+ and (offs8, s), y
34 bit dp, x 35 and dp, x 36 rol dp, x 37+ and [dp], y
38 sec 39 and abs16, y 3a dec 3b+ tsc
3c bit abs16, x 3d and abs16, x 3e rol abs16, x 3f+ and abs24, x
40 rti 41 eor (dp, x) 42+ wdm 43+ eor offs8, s
44+ mvp src, dst 45 eor dp 46 lsr dp 47+ eor [dp]
48 pha 49 eor #imm 4a lsr 4b+ phk
4c jmp abs16 4d eor abs16 4e lsr abs16 4f+ eor abs24
50 bvc rel8 51 eor (dp), y 52 eor (dp) 53+ eor (offs8, s), y
54+ mvn src, dst 55 eor dp, x 56 lsr dp, x 57+ eor [dp], y
58 cli 59 eor abs16, y 5a phy 5b+ tcd
5c+ jmp abs24 5d eor abs16, x 5e lsr abs16, x 5f+ eor abs24, x
60 rts 61 adc (dp, x) 62+ per rel16 63+ adc offs8, s
64 stz dp 65 adc dp 66 ror dp 67+ adc [dp]
68 pla 69 adc #imm 6a ror 6b+ rtl
6c jmp (abs16) 6d adc abs16 6e ror abs16 6f+ adc abs24
70 bvs rel8 71 adc (dp), y 72 adc (dp) 73+ adc (offs8, s), y
74 stz dp, x 75 adc dp, x 76 ror dp, x 77+ adc [dp], y
78 sei 79 adc abs16, y 7a ply 7b+ tdc
7c jmp (abs16, x) 7d adc abs16, x 7e ror abs16, x 7f+ adc abs24, x
80 bra rel8 81 sta (dp, x) 82+ brl rel16 83+ sta offs8, s
84 sty dp 85 sta dp 86 stx dp 87+ sta [dp]
88 dey 89 bit #imm 8a txa 8b+ phb
8c sty abs16 8d sta abs16 8e stx abs16 8f+ sta abs24
90 bcc rel8 91 sta (dp), y 92 sta (dp) 93+ sta (offs8, s), y
94 sty dp, x 95 sta dp, x 96 stx dp, y 97+ sta [dp], y
98 tya 99 sta abs16, y 9a txs 9b+ txy
9c stz abs16 9d sta abs16, x 9e stz abs16, x 9f+ sta abs24, x
a0 ldy #imm a1 lda (dp, x) a2 ldx #imm a3+ lda offs8, s
a4 ldy dp a5 lda dp a6 ldx dp a7+ lda [dp]
a8 tay a9 lda #imm aa tax ab+ plb
ac ldy abs16 ad lda abs16 ae ldx abs16 af+ lda abs24
b0 bcs rel8 b1 lda (dp), y b2 lda (dp) b3+ lda (offs8, s), y
b4 ldy dp, x b5 lda dp, x b6 ldx dp, y b7+ lda [dp], y
b8 clv b9 lda abs16, y ba tsx bb+ tyx
bc ldy abs16, x bd lda abs16, x be ldx abs16, y bf+ lda abs24, x
c0 cpy #imm c1 cmp (dp, x) c2+ rep #imm8 c3+ cmp offs8, s
c4 cpy dp c5 cmp dp c6 dec dp c7+ cmp [dp]
c8 iny c9 cmp #imm ca dex cb+ wai
cc cpy abs16 cd cmp abs16 ce dec abs16 cf+ cmp abs24
d0 bne rel8 d1 cmp (dp), y d2 cmp (dp) d3+ cmp (offs8, s), y
d4+ pei (dp) d5 cmp dp, x d6 dec dp, x d7+ cmp [dp], y
d8 cld d9 cmp abs16, y da phx db+ stp
dc+ jmp [abs16] dd cmp abs16, x de dec abs16, x df+ cmp abs24, x
e0 cpx #imm e1 sbc (dp, x) e2+ sep #imm8 e3+ sbc offs8, s
e4 cpx dp e5 sbc dp e6 inc dp e7+ sbc [dp]
e8 inx e9 sbc #imm ea nop eb+ xba
ec cpx abs16 ed sbc abs16 ee inc abs16 ef+ sbc abs24
f0 beq rel8 f1 sbc (dp), y f2 sbc (dp) f3+ sbc (offs8, s), y
f4+ pea abs16 f5 sbc dp, x f6 inc dp, x f7+ sbc [dp], y
f8 sed f9 sbc abs16, y fa plx fb+ xce
fc+ jsr (abs16, x) fd sbc abs16, x fe inc abs16, x ff+ sbc abs24, x
#imm: immediate value (8 or 16 bits, depends on processor status)
#imm8: 8-bit immediate value
dp: 8-bit direct page address
offs8: 8-bit offset (for stack-relative addressing)
abs16: 16-bit absolute address
abs24: 24-bit absolute address
rel8: 8-bit relative address offset
rel16: 16-bit relative address offset
src, dst: two 8-bit bank values
(CAUTION: assembler expects "mnemonic src, dst" syntax,
but machine language order is actually "opcode dst src")
The instruction set of the 65816 is a much extended superset of that
of the 65c02. Among the improvements are:
- the register widths can be switched to 16 bits
- addresses can now be 24 bits wide
- zero page is now called direct page, with an arbitrary base address
- lots of new addressing modes
- block transfer instructions
New mnemonics
-------------
02 cop imm8 coprocessor operation
6b rtl return long (fetches 24-bit address from stack)
82 brl rel16 branch long (16-bit offset)
0b phd push direct page register
4b phk push program bank register
8b phb push data bank register
62 per rel16 push effective relative address
d4 pei (dp) push effective indirect address
f4 pea abs16 push effective absolute address
2b pld pull direct page register
ab plb pull data bank register
1b tcs transfer C to stack pointer
3b tsc transfer stack pointer to C
5b tcd transfer C to direct page register
7b tdc transfer direct page register to C
('C' means the whole 16-bit accumulator, even in 8-bit mode)
9b txy transfer X to Y
bb tyx transfer Y to X
eb xba exchange high and low bytes of accumulator
fb xce exchange Carry and Emulation bits
c2 rep #imm8 clear bits in status register
e2 sep #imm8 set bits in status register
cb wai wait for interrupt
db stp wait for reset
42 wdm (reserved for future expansion)
block transfers:
44 mvp src, dst move previous (decrementing addresses)
54 mvn src, dst move next (incrementing addresses)
the arguments are bank numbers. block size minus one must be in 16-bit
accumulator. X holds source address, Y holds target address.
after these instructions, data bank register is set to "dst".
if blocks overlap:
when moving a block to a higher address, use mvp and put the highest
addresses in X/Y.
when moving a block to a lower address, use mvn and put the lowest
adresses in X/Y.
New addressing modes for existing mnemonics
-------------------------------------------
22 jsr abs24
5c jmp abs24
dc jmp [abs16]
fc jsr (abs16, x)
ora and eor adc sta lda cmp sbc
-------------------------------
03 23 43 63 83 a3 c3 e3 offs8, s
07 27 47 67 87 a7 c7 e7 [dp]
0f 2f 4f 6f 8f af cf ef abs24
13 33 53 73 93 b3 d3 f3 (offs8, s), y
17 37 57 77 97 b7 d7 f7 [dp], y
1f 3f 5f 7f 9f bf df ff abs24, x

130
docs/cputypes/cpu 65c02.txt Normal file
View File

@ -0,0 +1,130 @@
65c02 opcode table
All empty entries are undocumented opcodes.
Notice there are still no documented opcodes in the fourth column.
All differences to the original 6502 are marked using a '+' sign.
00 brk 01 ora (zp, x) 02 03
04+ tsb zp 05 ora zp 06 asl zp 07
08 php 09 ora #imm8 0a asl 0b
0c+ tsb abs16 0d ora abs16 0e asl abs16 0f
10 bpl rel8 11 ora (zp), y 12+ ora (zp) 13
14+ trb zp 15 ora zp, x 16 asl zp, x 17
18 clc 19 ora abs16, y 1a+ inc 1b
1c+ trb abs16 1d ora abs16, x 1e asl abs16, x 1f
20 jsr abs16 21 and (zp, x) 22 23
24 bit zp 25 and zp 26 rol zp 27
28 plp 29 and #imm8 2a rol 2b
2c bit abs16 2d and abs16 2e rol abs16 2f
30 bmi rel8 31 and (zp), y 32+ and (zp) 33
34+ bit zp, x 35 and zp, x 36 rol zp, x 37
38 sec 39 and abs16, y 3a+ dec 3b
3c+ bit abs16, x 3d and abs16, x 3e rol abs16, x 3f
40 rti 41 eor (zp, x) 42 43
44 45 eor zp 46 lsr zp 47
48 pha 49 eor #imm8 4a lsr 4b
4c jmp abs16 4d eor abs16 4e lsr abs16 4f
50 bvc rel8 51 eor (zp), y 52+ eor (zp) 53
54 55 eor zp, x 56 lsr zp, x 57
58 cli 59 eor abs16, y 5a+ phy 5b
5c 5d eor abs16, x 5e lsr abs16, x 5f
60 rts 61 adc (zp, x) 62 63
64+ stz zp 65 adc zp 66 ror zp 67
68 pla 69 adc #imm8 6a ror 6b
6c jmp (abs16) 6d adc abs16 6e ror abs16 6f
70 bvs rel8 71 adc (zp), y 72+ adc (zp) 73
74+ stz zp, x 75 adc zp, x 76 ror zp, x 77
78 sei 79 adc abs16, y 7a+ ply 7b
7c+ jmp (abs16, x) 7d adc abs16, x 7e ror abs16, x 7f
80+ bra rel8 81 sta (zp, x) 82 83
84 sty zp 85 sta zp 86 stx zp 87
88 dey 89+ bit #imm8 8a txa 8b
8c sty abs16 8d sta abs16 8e stx abs16 8f
90 bcc rel8 91 sta (zp), y 92+ sta (zp) 93
94 sty zp, x 95 sta zp, x 96 stx zp, y 97
98 tya 99 sta abs16, y 9a txs 9b
9c+ stz abs16 9d sta abs16, x 9e+ stz abs16, x 9f
a0 ldy #imm8 a1 lda (zp, x) a2 ldx #imm8 a3
a4 ldy zp a5 lda zp a6 ldx zp a7
a8 tay a9 lda #imm8 aa tax ab
ac ldy abs16 ad lda abs16 ae ldx abs16 af
b0 bcs rel8 b1 lda (zp), y b2+ lda (zp) b3
b4 ldy zp, x b5 lda zp, x b6 ldx zp, y b7
b8 clv b9 lda abs16, y ba tsx bb
bc ldy abs16, x bd lda abs16, x be ldx abs16, y bf
c0 cpy #imm8 c1 cmp (zp, x) c2 c3
c4 cpy zp c5 cmp zp c6 dec zp c7
c8 iny c9 cmp #imm8 ca dex cb
cc cpy abs16 cd cmp abs16 ce dec abs16 cf
d0 bne rel8 d1 cmp (zp), y d2+ cmp (zp) d3
d4 d5 cmp zp, x d6 dec zp, x d7
d8 cld d9 cmp abs16, y da+ phx db
dc dd cmp abs16, x de dec abs16, x df
e0 cpx #imm8 e1 sbc (zp, x) e2 e3
e4 cpx zp e5 sbc zp e6 inc zp e7
e8 inx e9 sbc #imm8 ea nop eb
ec cpx abs16 ed sbc abs16 ee inc abs16 ef
f0 beq rel8 f1 sbc (zp), y f2+ sbc (zp) f3
f4 f5 sbc zp, x f6 inc zp, x f7
f8 sed f9 sbc abs16, y fa+ plx fb
fc fd sbc abs16, x fe inc abs16, x ff
#imm8: 8-bit immediate value
zp: 8-bit zeropage address
abs16: 16-bit absolute address
rel8: 8-bit relative address offset
The 65c02 is the CMOS re-design of the 6502. It has a few
improvements:
New mnemonics
-------------
test and (re-)set bits against accumulator:
04 tsb zp
0c tsb abs16
14 trb zp
1c trb abs16
push/pull x/y:
5a phy
7a ply
da phx
fa plx
store zero:
64 stz zp
74 stz zp, x
9c stz abs16
9e stz abs16, x
branch always:
80 bra rel8
New addressing modes for existing mnemonics
-------------------------------------------
zp indirect addressing without indexing:
12 ora (zp)
32 and (zp)
52 eor (zp)
72 adc (zp)
92 sta (zp)
b2 lda (zp)
d2 cmp (zp)
f2 sbc (zp)
implied ("accumulator") addressing:
1a inc
3a dec
x-indexed indirect addressing:
7c jmp (abs16, x)
three more addressing modes for BIT:
34 bit zp, x
3c bit abs16, x
89 bit #imm8

View File

@ -0,0 +1,162 @@
65ce02 opcode table
There are no more undocumented opcodes.
All differences to the r65c02 are marked using '+' or '!' signs:
'!' marks (backward-compatible) changes to existing instructions.
'+' marks new instructions.
00 brk 01 ora (zp, x) 02+ cle 03+ see
04 tsb zp 05 ora zp 06 asl zp 07 rmb0 zp
08 php 09 ora #imm8 0a asl 0b+ tsy
0c tsb abs16 0d ora abs16 0e asl abs16 0f bbr0 zp, rel8
10 bpl rel8 11 ora (zp), y 12! ora (zp), z 13+ bpl rel16
14 trb zp 15 ora zp, x 16 asl zp, x 17 rmb1 zp
18 clc 19 ora abs16, y 1a inc 1b+ inz
1c trb abs16 1d ora abs16, x 1e asl abs16, x 1f bbr1 zp, rel8
20 jsr abs16 21 and (zp, x) 22+ jsr (abs16) 23+ jsr (abs16, x)
24 bit zp 25 and zp 26 rol zp 27 rmb2 zp
28 plp 29 and #imm8 2a rol 2b+ tys
2c bit abs16 2d and abs16 2e rol abs16 2f bbr2 zp, rel8
30 bmi rel8 31 and (zp), y 32! and (zp), z 33+ bmi rel16
34 bit zp, x 35 and zp, x 36 rol zp, x 37 rmb3 zp
38 sec 39 and abs16, y 3a dec 3b+ dez
3c bit abs16, x 3d and abs16, x 3e rol abs16, x 3f bbr3 zp, rel8
40 rti 41 eor (zp, x) 42+ neg 43+ asr
44+ asr zp 45 eor zp 46 lsr zp 47 rmb4 zp
48 pha 49 eor #imm8 4a lsr 4b+ taz
4c jmp abs16 4d eor abs16 4e lsr abs16 4f bbr4 zp, rel8
50 bvc rel8 51 eor (zp), y 52! eor (zp), z 53+ bvc rel16
54+ asr zp, x 55 eor zp, x 56 lsr zp, x 57 rmb5 zp
58 cli 59 eor abs16, y 5a phy 5b+ tab
5c+ aug 5d eor abs16, x 5e lsr abs16, x 5f bbr5 zp, rel8
60 rts 61 adc (zp, x) 62+ rtn #imm8 63+ bsr rel16
64! stz zp 65 adc zp 66 ror zp 67 rmb6 zp
68 pla 69 adc #imm8 6a ror 6b+ tza
6c jmp (abs16) 6d adc abs16 6e ror abs16 6f bbr6 zp, rel8
70 bvs rel8 71 adc (zp), y 72! adc (zp), z 73+ bvs rel16
74! stz zp, x 75 adc zp, x 76 ror zp, x 77 rmb7 zp
78 sei 79 adc abs16, y 7a ply 7b+ tba
7c jmp (abs16, x) 7d adc abs16, x 7e ror abs16, x 7f bbr7 zp, rel8
80 bra rel8 81 sta (zp, x) 82+ sta (o8, s), y 83+ bra rel16
84 sty zp 85 sta zp 86 stx zp 87 smb0 zp
88 dey 89 bit #imm8 8a txa 8b+ sty abs16, x
8c sty abs16 8d sta abs16 8e stx abs16 8f bbs0 zp, rel8
90 bcc rel8 91 sta (zp), y 92! sta (zp), z 93+ bcc rel16
94 sty zp, x 95 sta zp, x 96 stx zp, y 97 smb1 zp
98 tya 99 sta abs16, y 9a txs 9b+ stx abs16, y
9c! stz abs16 9d sta abs16, x 9e! stz abs16, x 9f bbs1 zp, rel8
a0 ldy #imm8 a1 lda (zp, x) a2 ldx #imm8 a3+ ldz #imm8
a4 ldy zp a5 lda zp a6 ldx zp a7 smb2 zp
a8 tay a9 lda #imm8 aa tax ab+ ldz abs16
ac ldy abs16 ad lda abs16 ae ldx abs16 af bbs2 zp, rel8
b0 bcs rel8 b1 lda (zp), y b2! lda (zp), z b3+ bcs rel16
b4 ldy zp, x b5 lda zp, x b6 ldx zp, y b7 smb3 zp
b8 clv b9 lda abs16, y ba tsx bb+ ldz abs16, x
bc ldy abs16, x bd lda abs16, x be ldx abs16, y bf bbs3 zp, rel8
c0 cpy #imm8 c1 cmp (zp, x) c2+ cpz #imm8 c3+ dew zp
c4 cpy zp c5 cmp zp c6 dec zp c7 smb4 zp
c8 iny c9 cmp #imm8 ca dex cb+ asw abs16
cc cpy abs16 cd cmp abs16 ce dec abs16 cf bbs4 zp, rel8
d0 bne rel8 d1 cmp (zp), y d2! cmp (zp), z d3+ bne rel16
d4+ cpz zp d5 cmp zp, x d6 dec zp, x d7 smb5 zp
d8 cld d9 cmp abs16, y da phx db+ phz
dc+ cpz abs16 dd cmp abs16, x de dec abs16, x df bbs5 zp, rel8
e0 cpx #imm8 e1 sbc (zp, x) e2+ lda (o8, s), y e3+ inw zp
e4 cpx zp e5 sbc zp e6 inc zp e7 smb6 zp
e8 inx e9 sbc #imm8 ea nop eb+ row abs16
ec cpx abs16 ed sbc abs16 ee inc abs16 ef bbs6 zp, rel8
f0 beq rel8 f1 sbc (zp), y f2! sbc (zp), z f3+ beq rel16
f4+ phw #imm16 f5 sbc zp, x f6 inc zp, x f7 smb7 zp
f8 sed f9 sbc abs16, y fa plx fb+ plz
fc+ phw abs16 fd sbc abs16, x fe inc abs16, x ff bbs7 zp, rel8
#imm8: 8-bit immediate value
#imm16: 16-bit immediate value (only for opcode f4)
zp: 8-bit zeropage address
abs16: 16-bit absolute address
rel8: 8-bit relative address offset
rel16: 16-bit relative address offset
o8: 8-bit offset (for stack-relative addressing, see opcodes 82 and e2)
The 65ce02 is a superset of the r65c02. It has several improvements:
new Z register
--------------
The "stz" mnemonic no longer means "store zero", but "store Z register"
(see opcodes 64, 74, 9c, 9e)
The "(zp)" addressing mode now becomes "(zp), z"
(see opcodes 12, 32, 52, 72, 92, b2, d2, f2)
Z defaults to zero after reset, so these changes are backward compatible
until Z is loaded with a non-zero value.
New instructions for this register:
1b inz increment Z
3b dez decrement Z
4b taz transfer A to Z
6b tza transfer Z to A
a3 ldz #imm8
ab ldz abs16
bb ldz abs16, x
c2 cpz #imm8
d4 cpz zp
db phz push Z
dc cpz abs16
fb plz pull Z
16-bit stack pointer
--------------------
02 cle clear stack extend disable
03 see set stack extend disable
0b tsy transfer stack_ptr_high to Y
2b tys transfer Y to stack_ptr_high
16-bit branches
---------------
13 bpl rel16
33 bmi rel16
53 bvc rel16
63 bsr rel16 relative jsr, "branch to subroutine"
73 bvs rel16
83 bra rel16 relative jmp
93 bcc rel16
b3 bcs rel16
d3 bne rel16
f3 beq rel16
To use these in ACME, use the mnemonics lbpl, lbmi, lbvc, ...
(except for "bsr", because there is no 8-bit version of it anyway)
new addressing modes for existing instructions
----------------------------------------------
22 jsr (abs16)
23 jsr (abs16, x)
82 sta (offset8, s), y
8b sty abs16, x
9b stx abs16, y
e2 lda (offset8, s), y
new instructions
----------------
42 neg negate A
43 asr
44 asr zp
54 asr zp, x
5b tab
5c aug "4-byte NOP reserved for future expansion"
62 rtn #imm8
7b tba
c3 dew zp
cb asw abs16
e3 inw zp
eb row abs16
f4 phw #imm16
fc phw abs16

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

@ -0,0 +1,86 @@
nmos6502 opcode table
This table includes all of the unintended ("illegal") opcodes. These are
marked using '+' or '!' signs:
'+' means the instruction is supported by ACME,
'!' means ACME will use a different (but functionally equivalent) opcode.
00 brk 01 ora (zp, x) 02+ jam 03+ slo (zp, x)
04+ nop zp 05 ora zp 06 asl zp 07+ slo zp
08 php 09 ora #imm8 0a asl 0b+ anc #imm8
0c+ nop abs16 0d ora abs16 0e asl abs16 0f+ slo abs16
10 bpl rel8 11 ora (zp), y 12! jam 13+ slo (zp), y
14+ nop zp, x 15 ora zp, x 16 asl zp, x 17+ slo zp, x
18 clc 19 ora abs16, y 1a! nop 1b+ slo abs16, y
1c+ nop abs16, x 1d ora abs16, x 1e asl abs16, x 1f+ slo abs16, x
20 jsr abs16 21 and (zp, x) 22! jam 23+ rla (zp, x)
24 bit zp 25 and zp 26 rol zp 27+ rla zp
28 plp 29 and #imm8 2a rol 2b! anc #imm8
2c bit abs16 2d and abs16 2e rol abs16 2f+ rla abs16
30 bmi rel8 31 and (zp), y 32! jam 33+ rla (zp), y
34! nop zp, x 35 and zp, x 36 rol zp, x 37+ rla zp, x
38 sec 39 and abs16, y 3a! nop 3b+ rla abs16, y
3c! nop abs16, x 3d and abs16, x 3e rol abs16, x 3f+ rla abs16, x
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+ 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
58 cli 59 eor abs16, y 5a! nop 5b+ sre abs16, y
5c! nop abs16, x 5d eor abs16, x 5e lsr abs16, x 5f+ sre abs16, x
60 rts 61 adc (zp, x) 62! jam 63+ rra (zp, x)
64! nop zp 65 adc zp 66 ror zp 67+ rra zp
68 pla 69 adc #imm8 6a ror 6b+ arr #imm8
6c jmp (abs16) 6d adc abs16 6e ror abs16 6f+ rra abs16
70 bvs rel8 71 adc (zp), y 72! jam 73+ rra (zp), y
74! nop zp, x 75 adc zp, x 76 ror zp, x 77+ rra zp, x
78 sei 79 adc abs16, y 7a! nop 7b+ rra abs16, y
7c! nop abs16, x 7d adc abs16, x 7e ror abs16, x 7f+ rra abs16, x
80+ nop #imm8 81 sta (zp, x) 82! nop #imm8 83+ sax (zp, x)
84 sty zp 85 sta zp 86 stx zp 87+ sax zp
88 dey 89! nop #imm8 8a txa 8b+ ane #imm8
8c sty abs16 8d sta abs16 8e stx abs16 8f+ sax abs16
90 bcc rel8 91 sta (zp), y 92! jam 93+ sha (zp), y
94 sty zp, x 95 sta zp, x 96 stx zp, y 97+ sax zp, y
98 tya 99 sta abs16, y 9a txs 9b+ tas abs16, y
9c+ shy abs16, x 9d sta abs16, x 9e+ shx abs16, y 9f+ sha abs16, y
a0 ldy #imm8 a1 lda (zp, x) a2 ldx #imm8 a3+ lax (zp, x)
a4 ldy zp a5 lda zp a6 ldx zp a7+ lax zp
a8 tay a9 lda #imm8 aa tax ab+ lxa #imm8
ac ldy abs16 ad lda abs16 ae ldx abs16 af+ lax abs16
b0 bcs rel8 b1 lda (zp), y b2! jam b3+ lax (zp), y
b4 ldy zp, x b5 lda zp, x b6 ldx zp, y b7+ lax zp, y
b8 clv b9 lda abs16, y ba tsx bb+ las abs16, y
bc ldy abs16, x bd lda abs16, x be ldx abs16, y bf+ lax abs16, y
c0 cpy #imm8 c1 cmp (zp, x) c2! nop #imm8 c3+ dcp (zp, x)
c4 cpy zp c5 cmp zp c6 dec zp c7+ dcp zp
c8 iny c9 cmp #imm8 ca dex cb+ sbx #imm8
cc cpy abs16 cd cmp abs16 ce dec abs16 cf+ dcp abs16
d0 bne rel8 d1 cmp (zp), y d2! jam d3+ dcp (zp), y
d4! nop zp, x d5 cmp zp, x d6 dec zp, x d7+ dcp zp, x
d8 cld d9 cmp abs16, y da! nop db+ dcp abs16, y
dc! nop abs16, x dd cmp abs16, x de dec abs16, x df+ dcp abs16, x
e0 cpx #imm8 e1 sbc (zp, x) e2! nop #imm8 e3+ isc (zp, x)
e4 cpx zp e5 sbc zp e6 inc zp e7+ isc zp
e8 inx e9 sbc #imm8 ea nop eb! sbc #imm8
ec cpx abs16 ed sbc abs16 ee inc abs16 ef+ isc abs16
f0 beq rel8 f1 sbc (zp), y f2! jam f3+ isc (zp), y
f4! nop zp, x f5 sbc zp, x f6 inc zp, x f7+ isc zp, x
f8 sed f9 sbc abs16, y fa! nop fb+ isc abs16, y
fc! nop abs16, x fd sbc abs16, x fe inc abs16, x ff+ isc abs16, x
#imm8: 8-bit immediate value
zp: 8-bit zeropage address
abs16: 16-bit absolute address
rel8: 8-bit relative address offset

View File

@ -0,0 +1,94 @@
r65c02 opcode table
All empty entries are undocumented opcodes.
All differences to the 65c02 (all in the fourth column) are marked
using a '+' sign.
00 brk 01 ora (zp, x) 02 03
04 tsb zp 05 ora zp 06 asl zp 07+ rmb0 zp
08 php 09 ora #imm8 0a asl 0b
0c tsb abs16 0d ora abs16 0e asl abs16 0f+ bbr0 zp, rel8
10 bpl rel8 11 ora (zp), y 12 ora (zp) 13
14 trb zp 15 ora zp, x 16 asl zp, x 17+ rmb1 zp
18 clc 19 ora abs16, y 1a inc 1b
1c trb abs16 1d ora abs16, x 1e asl abs16, x 1f+ bbr1 zp, rel8
20 jsr abs16 21 and (zp, x) 22 23
24 bit zp 25 and zp 26 rol zp 27+ rmb2 zp
28 plp 29 and #imm8 2a rol 2b
2c bit abs16 2d and abs16 2e rol abs16 2f+ bbr2 zp, rel8
30 bmi rel8 31 and (zp), y 32 and (zp) 33
34 bit zp, x 35 and zp, x 36 rol zp, x 37+ rmb3 zp
38 sec 39 and abs16, y 3a dec 3b
3c bit abs16, x 3d and abs16, x 3e rol abs16, x 3f+ bbr3 zp, rel8
40 rti 41 eor (zp, x) 42 43
44 45 eor zp 46 lsr zp 47+ rmb4 zp
48 pha 49 eor #imm8 4a lsr 4b
4c jmp abs16 4d eor abs16 4e lsr abs16 4f+ bbr4 zp, rel8
50 bvc rel8 51 eor (zp), y 52 eor (zp) 53
54 55 eor zp, x 56 lsr zp, x 57+ rmb5 zp
58 cli 59 eor abs16, y 5a phy 5b
5c 5d eor abs16, x 5e lsr abs16, x 5f+ bbr5 zp, rel8
60 rts 61 adc (zp, x) 62 63
64 stz zp 65 adc zp 66 ror zp 67+ rmb6 zp
68 pla 69 adc #imm8 6a ror 6b
6c jmp (abs16) 6d adc abs16 6e ror abs16 6f+ bbr6 zp, rel8
70 bvs rel8 71 adc (zp), y 72 adc (zp) 73
74 stz zp, x 75 adc zp, x 76 ror zp, x 77+ rmb7 zp
78 sei 79 adc abs16, y 7a ply 7b
7c jmp (abs16, x) 7d adc abs16, x 7e ror abs16, x 7f+ bbr7 zp, rel8
80 bra rel8 81 sta (zp, x) 82 83
84 sty zp 85 sta zp 86 stx zp 87+ smb0 zp
88 dey 89 bit #imm8 8a txa 8b
8c sty abs16 8d sta abs16 8e stx abs16 8f+ bbs0 zp, rel8
90 bcc rel8 91 sta (zp), y 92 sta (zp) 93
94 sty zp, x 95 sta zp, x 96 stx zp, y 97+ smb1 zp
98 tya 99 sta abs16, y 9a txs 9b
9c stz abs16 9d sta abs16, x 9e stz abs16, x 9f+ bbs1 zp, rel8
a0 ldy #imm8 a1 lda (zp, x) a2 ldx #imm8 a3
a4 ldy zp a5 lda zp a6 ldx zp a7+ smb2 zp
a8 tay a9 lda #imm8 aa tax ab
ac ldy abs16 ad lda abs16 ae ldx abs16 af+ bbs2 zp, rel8
b0 bcs rel8 b1 lda (zp), y b2 lda (zp) b3
b4 ldy zp, x b5 lda zp, x b6 ldx zp, y b7+ smb3 zp
b8 clv b9 lda abs16, y ba tsx bb
bc ldy abs16, x bd lda abs16, x be ldx abs16, y bf+ bbs3 zp, rel8
c0 cpy #imm8 c1 cmp (zp, x) c2 c3
c4 cpy zp c5 cmp zp c6 dec zp c7+ smb4 zp
c8 iny c9 cmp #imm8 ca dex cb
cc cpy abs16 cd cmp abs16 ce dec abs16 cf+ bbs4 zp, rel8
d0 bne rel8 d1 cmp (zp), y d2 cmp (zp) d3
d4 d5 cmp zp, x d6 dec zp, x d7+ smb5 zp
d8 cld d9 cmp abs16, y da phx db
dc dd cmp abs16, x de dec abs16, x df+ bbs5 zp, rel8
e0 cpx #imm8 e1 sbc (zp, x) e2 e3
e4 cpx zp e5 sbc zp e6 inc zp e7+ smb6 zp
e8 inx e9 sbc #imm8 ea nop eb
ec cpx abs16 ed sbc abs16 ee inc abs16 ef+ bbs6 zp, rel8
f0 beq rel8 f1 sbc (zp), y f2 sbc (zp) f3
f4 f5 sbc zp, x f6 inc zp, x f7+ smb7 zp
f8 sed f9 sbc abs16, y fa plx fb
fc fd sbc abs16, x fe inc abs16, x ff+ bbs7 zp, rel8
#imm8: 8-bit immediate value
zp: 8-bit zeropage address
abs16: 16-bit absolute address
rel8: 8-bit relative address offset
The r65c02 is a superset of the 65c02. It adds bit manipulation instructions:
smbB zp set bit in zp location
rmbB zp reset bit in zp location
bbsB zp, rel8 branch if bit is set in zp location
bbrB zp, rel8 branch if bit is reset in zp location
The 'B' in the mnemonic is the bit number, therefore it must be in
the 0..7 range.

View File

@ -0,0 +1,90 @@
w65c02 opcode table
All empty entries are undocumented opcodes.
All differences to the r65c02 are marked using a '+' sign (only two
opcodes anyway: cb and db)
00 brk 01 ora (zp, x) 02 03
04 tsb zp 05 ora zp 06 asl zp 07 rmb0 zp
08 php 09 ora #imm8 0a asl 0b
0c tsb abs16 0d ora abs16 0e asl abs16 0f bbr0 zp, rel8
10 bpl rel8 11 ora (zp), y 12 ora (zp) 13
14 trb zp 15 ora zp, x 16 asl zp, x 17 rmb1 zp
18 clc 19 ora abs16, y 1a inc 1b
1c trb abs16 1d ora abs16, x 1e asl abs16, x 1f bbr1 zp, rel8
20 jsr abs16 21 and (zp, x) 22 23
24 bit zp 25 and zp 26 rol zp 27 rmb2 zp
28 plp 29 and #imm8 2a rol 2b
2c bit abs16 2d and abs16 2e rol abs16 2f bbr2 zp, rel8
30 bmi rel8 31 and (zp), y 32 and (zp) 33
34 bit zp, x 35 and zp, x 36 rol zp, x 37 rmb3 zp
38 sec 39 and abs16, y 3a dec 3b
3c bit abs16, x 3d and abs16, x 3e rol abs16, x 3f bbr3 zp, rel8
40 rti 41 eor (zp, x) 42 43
44 45 eor zp 46 lsr zp 47 rmb4 zp
48 pha 49 eor #imm8 4a lsr 4b
4c jmp abs16 4d eor abs16 4e lsr abs16 4f bbr4 zp, rel8
50 bvc rel8 51 eor (zp), y 52 eor (zp) 53
54 55 eor zp, x 56 lsr zp, x 57 rmb5 zp
58 cli 59 eor abs16, y 5a phy 5b
5c 5d eor abs16, x 5e lsr abs16, x 5f bbr5 zp, rel8
60 rts 61 adc (zp, x) 62 63
64 stz zp 65 adc zp 66 ror zp 67 rmb6 zp
68 pla 69 adc #imm8 6a ror 6b
6c jmp (abs16) 6d adc abs16 6e ror abs16 6f bbr6 zp, rel8
70 bvs rel8 71 adc (zp), y 72 adc (zp) 73
74 stz zp, x 75 adc zp, x 76 ror zp, x 77 rmb7 zp
78 sei 79 adc abs16, y 7a ply 7b
7c jmp (abs16, x) 7d adc abs16, x 7e ror abs16, x 7f bbr7 zp, rel8
80 bra rel8 81 sta (zp, x) 82 83
84 sty zp 85 sta zp 86 stx zp 87 smb0 zp
88 dey 89 bit #imm8 8a txa 8b
8c sty abs16 8d sta abs16 8e stx abs16 8f bbs0 zp, rel8
90 bcc rel8 91 sta (zp), y 92 sta (zp) 93
94 sty zp, x 95 sta zp, x 96 stx zp, y 97 smb1 zp
98 tya 99 sta abs16, y 9a txs 9b
9c stz abs16 9d sta abs16, x 9e stz abs16, x 9f bbs1 zp, rel8
a0 ldy #imm8 a1 lda (zp, x) a2 ldx #imm8 a3
a4 ldy zp a5 lda zp a6 ldx zp a7 smb2 zp
a8 tay a9 lda #imm8 aa tax ab
ac ldy abs16 ad lda abs16 ae ldx abs16 af bbs2 zp, rel8
b0 bcs rel8 b1 lda (zp), y b2 lda (zp) b3
b4 ldy zp, x b5 lda zp, x b6 ldx zp, y b7 smb3 zp
b8 clv b9 lda abs16, y ba tsx bb
bc ldy abs16, x bd lda abs16, x be ldx abs16, y bf bbs3 zp, rel8
c0 cpy #imm8 c1 cmp (zp, x) c2 c3
c4 cpy zp c5 cmp zp c6 dec zp c7 smb4 zp
c8 iny c9 cmp #imm8 ca dex cb+ wai
cc cpy abs16 cd cmp abs16 ce dec abs16 cf bbs4 zp, rel8
d0 bne rel8 d1 cmp (zp), y d2 cmp (zp) d3
d4 d5 cmp zp, x d6 dec zp, x d7 smb5 zp
d8 cld d9 cmp abs16, y da phx db+ stp
dc dd cmp abs16, x de dec abs16, x df bbs5 zp, rel8
e0 cpx #imm8 e1 sbc (zp, x) e2 e3
e4 cpx zp e5 sbc zp e6 inc zp e7 smb6 zp
e8 inx e9 sbc #imm8 ea nop eb
ec cpx abs16 ed sbc abs16 ee inc abs16 ef bbs6 zp, rel8
f0 beq rel8 f1 sbc (zp), y f2 sbc (zp) f3
f4 f5 sbc zp, x f6 inc zp, x f7 smb7 zp
f8 sed f9 sbc abs16, y fa plx fb
fc fd sbc abs16, x fe inc abs16, x ff bbs7 zp, rel8
#imm8: 8-bit immediate value
zp: 8-bit zeropage address
abs16: 16-bit absolute address
rel8: 8-bit relative address offset
The w65c02 is a superset of the r65c02. It only adds two instructions:
cb wai wait for interrupt
db stp wait for reset

BIN
examples/trigono.exp2 Normal file

Binary file not shown.

View File

@ -1,4 +1,5 @@
CFLAGS = -O3 -Wall -Wstrict-prototypes
#CFLAGS = -O3 -Wall -Wextra -Wstrict-prototypes
LIBS = -lm
CC = gcc
RM = rm
@ -31,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 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
@ -43,7 +44,7 @@ output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h
platform.o: config.h platform.h platform.c
pseudoopcodes.o: acme.h alu.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c
pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h symbol.h pseudoopcodes.h pseudoopcodes.c
section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.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 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
@ -44,7 +44,7 @@ output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h
platform.o: config.h platform.h platform.c
pseudoopcodes.o: acme.h alu.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c
pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h symbol.h pseudoopcodes.h pseudoopcodes.c
section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.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 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
@ -47,7 +47,7 @@ output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h
platform.o: config.h platform.h platform.c
pseudoopcodes.o: acme.h alu.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c
pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h symbol.h pseudoopcodes.h pseudoopcodes.c
section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.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 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
@ -42,7 +43,7 @@ output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h
platform.o: config.h platform.h platform.c
pseudoopcodes.o: acme.h alu.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c
pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h symbol.h pseudoopcodes.h pseudoopcodes.c
section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c

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
//
// Platform specific stuff (in this case, for AmigaOS)
@ -13,7 +13,10 @@
#define PLATFORM_INIT
// convert UNIX-style pathname to Amiga-style pathname (no change)
#define PLATFORM_CONVERTPATHCHAR(a) (a)
//#define PLATFORM_CONVERTPATH(p)
// directory separator for include paths
#define DIRECTORY_SEPARATOR '\0' // actually '/', but paths ending on ':' are ok, so auto-adding '/' is bad)
// string containing the prefix for accessing files from the library tree
#define PLATFORM_LIBPREFIX "progdir:acme_lib/"

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
//
// Platform specific stuff (in this case, for DOS, OS/2 and Windows)
@ -34,14 +34,17 @@ void DOS_entry(void)
}
// convert UNIX-style pathname character to DOS-style pathname character
char DOS_convert_path_char(char byte)
// convert UNIX-style pathname to DOS-style pathname
void DOS_convert_path(char *p)
{
if (byte == '/')
return '\\';
if (byte == '\\')
return '/';
return byte;
while (*p) {
if (*p == '/') {
*p = '\\';
} else if (*p == '\\') {
*p = '/';
}
++p;
}
}

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
//
// Platform specific stuff (in this case, for DOS, OS/2 and Windows)
@ -16,7 +16,10 @@
#define PLATFORM_INIT DOS_entry()
// convert UNIX-style pathname to DOS-style pathname
#define PLATFORM_CONVERTPATHCHAR(a) DOS_convert_path_char(a)
#define PLATFORM_CONVERTPATH(p) DOS_convert_path(p)
// directory separator for include paths
#define DIRECTORY_SEPARATOR '\\'
// string containing the prefix for accessing files from the library tree
#define PLATFORM_LIBPREFIX DOS_lib_prefix
@ -54,8 +57,8 @@ extern char *DOS_lib_prefix; // header string of library tree
// used as PLATFORM_INIT: reads "ACME" environment variable
extern void DOS_entry(void);
// Convert UNIX-style pathname character to DOS-style pathname character
extern char DOS_convert_path_char(char);
// Convert UNIX-style pathname to DOS-style pathname
extern void DOS_convert_path(char *p);
#endif

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
//
// Platform specific stuff (in this case, for RISC OS)
@ -23,7 +23,7 @@
// variables
int RISCOS_flags = 0; // used to store platform-specific flags
bits RISCOS_flags = 0; // used to store platform-specific flags
// exit handler: if throwback was used, de-register now
@ -46,17 +46,20 @@ void RISCOS_entry(void)
// convert UNIX-style pathname to RISC OS-style pathname
char RISCOS_convert_path_char(char byte)
void RISCOS_convert_path(char *p)
{
if (byte == '.')
return '/';
if (byte == '/')
return '.';
if (byte == '?')
return '#';
if (byte == '#')
return '?';
return byte;
while (*p) {
if (*p == '.') {
*p = '/';
} else if (*p == '/') {
*p = '.';
} else if (*p == '?') {
*p = '#';
} else if (*p == '#') {
*p = '?';
}
++p;
}
}

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
//
// Platform specific stuff (in this case, for RISC OS)
@ -15,7 +15,10 @@
#define PLATFORM_INIT RISCOS_entry()
// convert UNIX-style pathname to RISC OS-style pathname
#define PLATFORM_CONVERTPATHCHAR(a) RISCOS_convert_path_char(a)
#define PLATFORM_CONVERTPATH(path) RISCOS_convert_path(path)
// directory separator for include paths
#define DIRECTORY_SEPARATOR '\0' // actually '.', but paths ending on ':' are ok, so auto-adding '.' is bad)
// string containing the prefix for accessing files from the library tree
#define PLATFORM_LIBPREFIX "ACME_Lib:"
@ -55,7 +58,7 @@ do { \
// variables
extern int RISCOS_flags; // Holds platform-specific flags
extern bits RISCOS_flags; // Holds platform-specific flags
#define RISCOSFLAG_THROWBACK (1u << 0) // use throwback protocol
#define RISCOSFLAG_THROWN (1u << 1) // throwback is active
@ -63,11 +66,11 @@ extern int RISCOS_flags; // Holds platform-specific flags
// used as PLATFORM_INIT: registers exit handler
extern void RISCOS_entry(void);
// convert UNIX-style pathname to RISC OS-style pathname
extern char RISCOS_convert_path_char(char);
extern void RISCOS_convert_path(char *path);
// setting the created files' types
extern void RISCOS_set_filetype(const char *, int);
extern void RISCOS_set_filetype(const char *filename, int type);
// use DDEUtils module's "Throwback" protocol
extern void RISCOS_throwback(const char *, int);
extern void RISCOS_throwback(const char *msg, int type);
#endif

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
//
// Platform specific stuff (in this case, for unknown OSes)
@ -13,7 +13,10 @@
#define PLATFORM_INIT AnyOS_entry()
// convert UNIX-style pathname to AnyOS-style pathname (no change)
#define PLATFORM_CONVERTPATHCHAR(a) (a)
//#define PLATFORM_CONVERTPATH(p)
// directory separator for include paths
#define DIRECTORY_SEPARATOR '/'
// string containing the prefix for accessing files from the library tree
#define PLATFORM_LIBPREFIX AnyOS_lib_prefix

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
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -62,9 +62,15 @@ static const char arg_vicelabels[] = "VICE labels filename";
#define OPTION_VERSION "version"
#define OPTION_MSVC "msvc"
#define OPTION_COLOR "color"
#define OPTION_FULLSTOP "fullstop"
#define OPTION_IGNORE_ZEROES "ignore-zeroes"
#define OPTION_STRICT_SEGMENTS "strict-segments"
#define OPTION_DIALECT "dialect"
#define OPTION_TEST "test"
// options for "-W"
#define OPTIONWNO_LABEL_INDENT "no-label-indent"
#define OPTIONWNO_OLD_FOR "no-old-for"
#define OPTIONWNO_BIN_LEN "no-bin-len"
#define OPTIONWTYPE_MISMATCH "type-mismatch"
@ -123,23 +129,29 @@ 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"
" --" OPTION_STRICT_SEGMENTS " turn segment overlap warnings into errors\n"
" -vDIGIT set verbosity level\n"
" -DSYMBOL=VALUE define global symbol\n"
// as long as there is only one -W option:
#define OPTIONWNO_LABEL_INDENT "no-label-indent"
" -I PATH/TO/DIR add search path for input files\n"
// TODO: replace these:
" -W" OPTIONWNO_LABEL_INDENT " suppress warnings about indented labels\n"
" -W" OPTIONWNO_OLD_FOR " suppress warnings about old \"!for\" syntax\n"
" -W" OPTIONWNO_OLD_FOR " (old, use \"--dialect 0.94.8\" instead)\n"
" -W" OPTIONWNO_BIN_LEN " suppress warnings about lengths of binary literals\n"
" -W" OPTIONWTYPE_MISMATCH " enable type checking (warn about type mismatch)\n"
// when there are more, use next line and add a separate function:
// with this line and add a separate function:
//" -W show warning level options\n"
" --" OPTION_USE_STDOUT " fix for 'Relaunch64' IDE (see docs)\n"
" --" OPTION_MSVC " set output error message format to that of MS Visual Studio\n"
" --" OPTION_COLOR " enable colored error output using ANSI escape codes\n"
" --" OPTION_MSVC " output errors in MS VS format\n"
" --" OPTION_COLOR " uses ANSI color codes for error output\n"
" --" OPTION_FULLSTOP " use '.' as pseudo opcode prefix\n"
" --" OPTION_DIALECT " VERSION behave like different version\n"
" --" OPTION_TEST " enable experimental features\n"
PLATFORM_OPTION_HELP
" -V, --" OPTION_VERSION " show version and exit\n");
exit(EXIT_SUCCESS);
@ -183,7 +195,7 @@ int ACME_finalize(int exit_code)
report_close(report);
if (symbollist_filename) {
fd = fopen(symbollist_filename, FILE_WRITETEXT);
fd = fopen(symbollist_filename, FILE_WRITETEXT); // FIXME - what if filename is given via !sl in sub-dir? fix path!
if (fd) {
symbols_list(fd);
fclose(fd);
@ -218,7 +230,7 @@ static void save_output_file(void)
fputs("No output file specified (use the \"-o\" option or the \"!to\" pseudo opcode).\n", stderr);
return;
}
fd = fopen(output_filename, FILE_WRITEBINARY);
fd = fopen(output_filename, FILE_WRITEBINARY); // FIXME - what if filename is given via !to in sub-dir? fix path!
if (fd == NULL) {
fprintf(stderr, "Error: Cannot open output file \"%s\".\n",
output_filename);
@ -229,12 +241,13 @@ static void save_output_file(void)
}
// perform a single pass. Returns number of "NeedValue" type errors.
static int perform_pass(void)
// increment pass number and perform a single pass
static void perform_pass(void)
{
FILE *fd;
int ii;
++pass.number;
// call modules' "pass init" functions
Output_passinit(); // disable output, PC undefined
cputype_passinit(default_cpu); // set default cpu type
@ -244,8 +257,9 @@ static int perform_pass(void)
encoding_passinit(); // set default encoding
section_passinit(); // set initial zone (untitled)
// init variables
pass_undefined_count = 0; // no "NeedValue" errors yet
pass_real_errors = 0; // no real errors yet
pass.undefined_count = 0;
//pass.needvalue_count = 0; FIXME - use
pass.error_count = 0;
// Process toplevel files
for (ii = 0; ii < toplevel_src_count; ++ii) {
if ((fd = fopen(toplevel_sources[ii], FILE_READBINARY))) {
@ -254,66 +268,63 @@ static int perform_pass(void)
fprintf(stderr, "Error: Cannot open toplevel file \"%s\".\n", toplevel_sources[ii]);
if (toplevel_sources[ii][0] == '-')
fprintf(stderr, "Options (starting with '-') must be given _before_ source files!\n");
++pass_real_errors;
++pass.error_count;
}
}
if (pass_real_errors)
Output_end_segment();
/* TODO:
if --save-start is given, parse arg string
if --save-limit is given, parse arg string
*/
if (pass.error_count)
exit(ACME_finalize(EXIT_FAILURE));
else
Output_end_segment();
return pass_undefined_count;
}
static struct report global_report;
// do passes until done (or errors occurred). Return whether output is ready.
static int do_actual_work(void)
static boolean do_actual_work(void)
{
int undefined_prev, // "NeedValue" errors of previous pass
undefined_curr; // "NeedValue" errors of current pass
int undefs_before; // number of undefined results in previous pass
report = &global_report; // let global pointer point to something
report_init(report); // we must init struct before doing passes
if (Process_verbosity > 1)
if (config.process_verbosity > 1)
puts("First pass.");
pass_count = 0;
undefined_curr = perform_pass(); // First pass
// now pretend there has been a pass before the first one
undefined_prev = undefined_curr + 1;
// As long as the number of "NeedValue" errors is decreasing but
// non-zero, keep doing passes.
while (undefined_curr && (undefined_curr < undefined_prev)) {
++pass_count;
undefined_prev = undefined_curr;
if (Process_verbosity > 1)
pass.complain_about_undefined = FALSE; // disable until error pass needed
pass.number = -1; // pre-init, will be incremented by perform_pass()
perform_pass(); // first pass
// pretend there has been a previous pass, with one more undefined result
undefs_before = pass.undefined_count + 1;
// keep doing passes as long as the number of undefined results keeps decreasing.
// stop on zero (FIXME - zero-check pass.needvalue_count instead!)
while (pass.undefined_count && (pass.undefined_count < undefs_before)) {
undefs_before = pass.undefined_count;
if (config.process_verbosity > 1)
puts("Further pass.");
undefined_curr = perform_pass();
perform_pass();
}
// any errors left?
if (undefined_curr == 0) {
if (pass.undefined_count == 0) { // FIXME - use pass.needvalue_count instead!
// if listing report is wanted and there were no errors,
// do another pass to generate listing report
if (report_filename) {
if (Process_verbosity > 1)
if (config.process_verbosity > 1)
puts("Extra pass to generate listing report.");
if (report_open(report, report_filename) == 0) {
++pass_count;
perform_pass();
report_close(report);
}
}
return 1;
return TRUE;
}
// There are still errors (unsolvable by doing further passes),
// so perform additional pass to find and show them.
if (Process_verbosity > 1)
if (config.process_verbosity > 1)
puts("Extra pass needed to find error.");
// activate error output
ALU_optional_notdef_handler = Throw_error;
++pass_count;
pass.complain_about_undefined = TRUE; // activate error output
perform_pass(); // perform pass, but now show "value undefined"
return 0;
return FALSE;
}
@ -327,30 +338,43 @@ static void keyword_to_dynabuf(const char keyword[])
}
// check output format (the output format tree must be set up at this point!)
static void set_output_format(void)
// set output format (the output format tree must be set up at this point!)
static void set_output_format(const char format_name[])
{
keyword_to_dynabuf(cliargs_safe_get_next("output format"));
if (outputfile_set_format()) {
fprintf(stderr, "%sUnknown output format (known formats are: %s).\n", cliargs_error, outputfile_formats);
exit(EXIT_FAILURE);
// caution, name may be NULL!
if (format_name) {
keyword_to_dynabuf(format_name);
if (!outputfile_set_format())
return; // ok
fputs("Error: Unknown output format.\n", stderr);
} else {
fputs("Error: No output format specified.\n", stderr);
}
fprintf(stderr, "Supported formats are:\n\n\t%s\n\n", outputfile_formats);
exit(EXIT_FAILURE);
}
// check CPU type (the cpu type tree must be set up at this point!)
static void set_starting_cpu(void)
// set CPU type (the cpu type tree must be set up at this point!)
static void set_starting_cpu(const char cpu_name[])
{
const struct cpu_type *new_cpu_type;
keyword_to_dynabuf(cliargs_safe_get_next("CPU type"));
new_cpu_type = cputype_find();
if (new_cpu_type) {
default_cpu = new_cpu_type;
// caution, name may be NULL!
if (cpu_name) {
keyword_to_dynabuf(cpu_name);
new_cpu_type = cputype_find();
if (new_cpu_type) {
default_cpu = new_cpu_type;
return; // ok
}
fputs("Error: Unknown CPU type.\n", stderr);
} else {
fprintf(stderr, "%sUnknown CPU type (known types are: %s).\n", cliargs_error, cputype_names);
exit(EXIT_FAILURE);
fputs("Error: No CPU type specified.\n", stderr);
}
fprintf(stderr, "Supported types are:\n\n\t%s\n\n", cputype_names);
exit(EXIT_FAILURE);
}
@ -363,8 +387,8 @@ static void could_not_parse(const char strange[])
// return signed long representation of string.
// copes with hexadecimal if prefixed with "$", "0x" or "0X".
// copes with octal if prefixed with "&".
// copes with binary if prefixed with "%".
// copes with octal if prefixed with "&". FIXME - add "0o" prefix?
// copes with binary if prefixed with "%". FIXME - add "0b" prefix!
// assumes decimal otherwise.
static signed long string_to_number(const char *string)
{
@ -393,22 +417,24 @@ static signed long string_to_number(const char *string)
// set program counter
static void set_starting_pc(void)
static void set_starting_pc(const char expression[])
{
start_address = string_to_number(cliargs_safe_get_next("program counter"));
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);
}
// set initial memory contents
static void set_mem_contents(void)
static void set_mem_contents(const char expression[])
{
fill_value = string_to_number(cliargs_safe_get_next("initmem value"));
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);
}
@ -432,13 +458,64 @@ static void define_symbol(const char definition[])
}
struct dialect {
enum version dialect;
const char *version;
const char *description;
};
struct dialect dialects[] = {
{VER_OLDEST_SUPPORTED, "0.85", "(the oldest version supported)"},
{VER_DEPRECATE_REALPC, "0.86", "\"!realpc\" gives a warning, \"!to\" wants a file format"},
// {VER_SHORTER_SETPC_WARNING, "0.93", "\"*=\" in offset assembly gives shorter warning but still switches off"},
{VER_RIGHTASSOCIATIVEPOWEROF, "0.94.6", "\"power of\" is now right-associative"},
// {VER_, "0.94.7", "empty code segments are no longer included in output file"},
{VER_DISABLED_OBSOLETE_STUFF, "0.94.8", "\"*=\" works inside \"!pseudopc\", disabled \"!cbm/!realpc/!subzone\""},
{VER_NEWFORSYNTAX, "0.94.12", "new \"!for\" syntax"},
// {VER_, "0.95.2", "changed ANC#8 from 0x2b to 0x0b"},
{VER_BACKSLASHESCAPING, "0.97", "backslash escaping and strings"},
// {VER_CURRENT, "default", "default"},
{VER_FUTURE, "future", "enable all experimental features"},
{0, NULL, NULL} // NULLs terminate
};
// choose dialect (mimic behaviour of different version)
static void set_dialect(const char version[])
{
struct dialect *dia;
// caution, version may be NULL!
if (version) {
// scan array
for (dia = dialects; dia->version; ++dia) {
if (strcmp(version, dia->version) == 0) {
config.wanted_version = dia->dialect;
return; // found
}
}
fputs("Error: Unknown dialect specifier.\n", stderr);
} else {
fputs("Error: No dialect specified.\n", stderr);
}
// output table of possible versions and die
fputs(
"Supported dialects are:\n"
"\n"
"\tdialect\t\tdescription\n"
"\t-------\t\t-----------\n", stderr);
for (dia = dialects; dia->version; ++dia)
fprintf(stderr, "\t%s\t\t%s\n", dia->version, dia->description);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
// handle long options (like "--example"). Return unknown string.
static const char *long_option(const char *string)
{
if (strcmp(string, OPTION_HELP) == 0)
show_help_and_exit();
else if (strcmp(string, OPTION_FORMAT) == 0)
set_output_format();
set_output_format(cliargs_get_next()); // NULL is ok (handled like unknown)
else if (strcmp(string, OPTION_OUTFILE) == 0)
output_filename = cliargs_safe_get_next(name_outfile);
else if (strcmp(string, OPTION_LABELDUMP) == 0) // old
@ -450,24 +527,35 @@ static const char *long_option(const char *string)
else if (strcmp(string, OPTION_REPORT) == 0)
report_filename = cliargs_safe_get_next(arg_reportfile);
else if (strcmp(string, OPTION_SETPC) == 0)
set_starting_pc();
set_starting_pc(cliargs_safe_get_next("program counter"));
else if (strcmp(string, OPTION_CPU) == 0)
set_starting_cpu();
set_starting_cpu(cliargs_get_next()); // NULL is ok (handled like unknown)
else if (strcmp(string, OPTION_INITMEM) == 0)
set_mem_contents();
set_mem_contents(cliargs_safe_get_next("initmem value"));
else if (strcmp(string, OPTION_MAXERRORS) == 0)
max_errors = string_to_number(cliargs_safe_get_next("maximum error count"));
config.max_errors = string_to_number(cliargs_safe_get_next("maximum error count"));
else if (strcmp(string, OPTION_MAXDEPTH) == 0)
macro_recursions_left = (source_recursions_left = string_to_number(cliargs_safe_get_next("recursion depth")));
// else if (strcmp(string, "strictsyntax") == 0)
// strict_syntax = TRUE;
else if (strcmp(string, OPTION_USE_STDOUT) == 0)
msg_stream = stdout;
config.msg_stream = stdout;
else if (strcmp(string, OPTION_MSVC) == 0)
format_msvc = TRUE;
PLATFORM_LONGOPTION_CODE
config.format_msvc = TRUE;
else if (strcmp(string, OPTION_FULLSTOP) == 0)
config.pseudoop_prefix = '.';
else if (strcmp(string, OPTION_IGNORE_ZEROES) == 0)
config.honor_leading_zeroes = FALSE;
else if (strcmp(string, OPTION_STRICT_SEGMENTS) == 0)
config.segment_warning_is_error = TRUE;
else if (strcmp(string, OPTION_DIALECT) == 0)
set_dialect(cliargs_get_next()); // NULL is ok (handled like unknown)
else if (strcmp(string, OPTION_TEST) == 0) {
config.wanted_version = VER_FUTURE;
config.test_new_features = TRUE;
} PLATFORM_LONGOPTION_CODE
else if (strcmp(string, OPTION_COLOR) == 0)
format_color = TRUE;
config.format_color = TRUE;
else if (strcmp(string, OPTION_VERSION) == 0)
show_version(TRUE);
else
@ -484,24 +572,31 @@ static char short_option(const char *argument)
case 'D': // "-D" define constants
define_symbol(argument + 1);
goto done;
case 'f': // "-f" selects output format
set_output_format(cliargs_get_next()); // NULL is ok (handled like unknown)
break;
case 'h': // "-h" shows help
show_help_and_exit();
case 'f': // "-f" selects output format
set_output_format();
break;
case 'I': // "-I" adds an include directory
if (argument[1])
includepaths_add(argument + 1);
else
includepaths_add(cliargs_safe_get_next("include path"));
goto done;
case 'l': // "-l" selects symbol list filename
symbollist_filename = cliargs_safe_get_next(arg_symbollist);
break;
case 'o': // "-o" selects output filename
output_filename = cliargs_safe_get_next(name_outfile);
break;
case 'l': // "-l" selects symbol list filename
symbollist_filename = cliargs_safe_get_next(arg_symbollist);
break;
case 'r': // "-r" selects report filename
report_filename = cliargs_safe_get_next(arg_reportfile);
break;
case 'v': // "-v" changes verbosity
++Process_verbosity;
++config.process_verbosity;
if ((argument[1] >= '0') && (argument[1] <= '9'))
Process_verbosity = *(++argument) - '0';
config.process_verbosity = *(++argument) - '0';
break;
// platform specific switches are inserted here
PLATFORM_SHORTOPTION_CODE
@ -510,13 +605,16 @@ static char short_option(const char *argument)
break;
case 'W': // "-W" tunes warning level
if (strcmp(argument + 1, OPTIONWNO_LABEL_INDENT) == 0) {
warn_on_indented_labels = FALSE;
config.warn_on_indented_labels = FALSE;
goto done;
} else if (strcmp(argument + 1, OPTIONWNO_OLD_FOR) == 0) {
warn_on_old_for = FALSE;
config.wanted_version = VER_NEWFORSYNTAX - 1;
goto done;
} else if (strcmp(argument + 1, OPTIONWNO_BIN_LEN) == 0) {
config.warn_bin_mask = 0;
goto done;
} else if (strcmp(argument + 1, OPTIONWTYPE_MISMATCH) == 0) {
warn_on_type_mismatch = TRUE;
config.warn_on_type_mismatch = TRUE;
goto done;
} else {
fprintf(stderr, "%sUnknown warning level.\n", cliargs_error);
@ -536,28 +634,20 @@ done:
// guess what
int main(int argc, const char *argv[])
{
config_default(&config);
// if called without any arguments, show usage info (not full help)
if (argc == 1)
show_help_and_exit();
msg_stream = stderr;
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));
// 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

@ -12,8 +12,8 @@
// Variables
extern const char *symbollist_filename;
extern const char *output_filename;
extern const char *report_filename;
extern const char *output_filename; // TODO - put in "part" struct
extern const char *report_filename; // TODO - put in "part" struct
// maximum recursion depth for macro calls and "!source"
extern signed long macro_recursions_left;
extern signed long source_recursions_left;

3079
src/alu.c

File diff suppressed because it is too large Load Diff

103
src/alu.h
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
//
// ALU stuff (the expression parser)
@ -10,67 +10,68 @@
#include "config.h"
struct op;
struct dynabuf;
struct type {
const char *name;
boolean (*is_defined)(const struct object *self);
boolean (*differs)(const struct object *self, const struct object *other);
void (*assign)(struct object *self, const struct object *new_value, boolean accept_change);
void (*monadic_op)(struct object *self, const struct op *op);
void (*dyadic_op)(struct object *self, const struct op *op, struct object *other);
void (*fix_result)(struct object *self);
void (*print)(const struct object *self, struct dynabuf *db);
};
extern struct type type_number;
extern struct type type_list;
extern struct type type_string;
struct expression {
struct object result;
boolean is_empty; // nothing parsed (first character was a delimiter)
int open_parentheses; // number of parentheses still open
boolean is_parenthesized; // whole expression was in parentheses (indicating indirect addressing)
// TODO - how to return reserved cpu constant (register names)?
};
// constants
// meaning of bits in "flags" of struct result: TODO - this is only for future "number" result type!
#define MVALUE_IS_FP (1u << 8) // floating point value
#define MVALUE_INDIRECT (1u << 7) // needless parentheses indicate use of indirect addressing modes
#define MVALUE_EXISTS (1u << 6) // 0: expression was empty. 1: there was *something* to parse. TODO - get rid of this, make "nothing" its own result type instead!
#define MVALUE_UNSURE (1u << 5) // value once was related to undefined
// expression. Needed for producing the same addresses in all passes; because in
// the first pass there will almost for sure be labels that are undefined, you
// can't simply get the addressing mode from looking at the parameter's value.
#define MVALUE_DEFINED (1u << 4) // 0: undefined expression (value will be zero). 1: known result
#define MVALUE_ISBYTE (1u << 3) // value is guaranteed to fit in one byte
#define MVALUE_FORCE24 (1u << 2) // value usage forces 24-bit usage
#define MVALUE_FORCE16 (1u << 1) // value usage forces 16-bit usage
#define MVALUE_FORCE08 (1u << 0) // value usage forces 8-bit usage
#define MVALUE_FORCEBITS (MVALUE_FORCE08|MVALUE_FORCE16|MVALUE_FORCE24)
#define MVALUE_GIVEN (MVALUE_DEFINED | MVALUE_EXISTS) // bit mask for fixed values (defined and existing)
// create dynamic buffer, operator/function trees and operator/operand stacks
extern void ALU_init(void);
// function pointer for "value undefined" error output.
// set to NULL to suppress those errors,
// set to Throw_error to show them.
extern void (*ALU_optional_notdef_handler)(const char *);
// flag bits in number struct:
#define NO_FORCE_BIT 0 // (just to make source more readable)
#define NUMBER_FORCES_8 (1u << 0) // value usage forces 8-bit usage
#define NUMBER_FORCES_16 (1u << 1) // value usage forces 16-bit usage
#define NUMBER_FORCES_24 (1u << 2) // value usage forces 24-bit usage
#define NUMBER_FORCEBITS (NUMBER_FORCES_8 | NUMBER_FORCES_16 | NUMBER_FORCES_24)
#define NUMBER_FITS_BYTE (1u << 3) // value is guaranteed to fit in one byte
#define NUMBER_EVER_UNDEFINED (1u << 4) // value once was related to
// undefined expression. Needed for producing the same addresses in all
// passes; because in the first pass there will almost for sure be
// labels that are undefined, we can't simply get the addressing mode
// from looking at the parameter's value. FIXME - rename to TAINTED :)
/*
// FIXME - replace all the functions below with a single one using a "flags" arg!
/* its return value would then be:
enum expression_result {
EXRE_ERROR, // error (has been reported, so skip remainder of statement)
EXRE_NOTHING, // next char after space was comma or end-of-statement
EXRE_NUMBER, // int or float (what is returned by the current functions)
EXRE_STRING, // TODO
EXRE_RESERVED, // reserved cpu constant (register names), TODO
EXRE_LIST // TODO
};
// its return value would then be "error"/"ok".
// input flags:
#define ACCEPT_EMPTY (1u << 0) // if not given, throws error
#define ACCEPT_UNDEFINED (1u << 1) // if not given, undefined throws serious error
//#define ACCEPT_INT (1u << ) needed when strings come along!
#define ACCEPT_UNDEFINED (1u << 0) // if not given, undefined throws serious error
#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
// do I need ACCEPT_INT and/or ACCEPT_ADDRESS?
#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?
*/
// stores int value if given. Returns whether stored. Throws error if undefined.
extern int ALU_optional_defined_int(intval_t *target);
// returns int value (0 if result was undefined)
extern intval_t ALU_any_int(void);
// stores int value and flags (floats are transformed to int)
extern void ALU_int_result(struct result *intresult);
// stores int value (0 if result was undefined)
extern void ALU_any_int(intval_t *target);
// stores int value and flags (floats are transformed to int)
// if result was undefined, serious error is thrown
extern void ALU_defined_int(struct result *intresult);
// stores int value and flags, allowing for one '(' too many (x-indirect addr).
// returns number of additional '(' (1 or 0).
extern int ALU_liberal_int(struct result *intresult);
// stores value and flags (result may be either int or float)
extern void ALU_any_result(struct result *result);
extern void ALU_defined_int(struct number *intresult);
// stores int value and flags, allowing for "paren" '(' too many (x-indirect addr).
extern void ALU_addrmode_int(struct expression *expression, int paren);
// stores resulting object
extern void ALU_any_result(struct object *result);
#endif

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
//
// Configuration
@ -7,21 +7,64 @@
#define config_H
// make sure the enum below works with strange compilers, too:
#ifdef FALSE
#undef FALSE
#endif
#ifdef TRUE
#undef TRUE
#endif
// types
typedef enum { FALSE = 0, TRUE } boolean; // yes, I could include <stdbool.h>, but this source should work with ancient compilers as well...
typedef unsigned int bits;
typedef unsigned int scope_t;
typedef signed long intval_t; // at least 32 bits
typedef unsigned long uintval_t; // just for logical shift right
// result structure type definition with support for floating point
// future result types: EMPTY, UNDEFINED, INT, FLOAT (, STRING)
struct result { // either int or float
int flags; // expression flags
enum numtype {
NUMTYPE_UNDEFINED,
NUMTYPE_INT,
NUMTYPE_FLOAT,
};
// structure for ints/floats
struct number {
enum numtype ntype;
bits flags; // FITS_IN_BYTE etc. (see alu.h)
union {
intval_t intval; // integer value
double fpval; // floating point value
} val; // Expression value
} val;
int addr_refs; // address reference count (only look at this if value is DEFINED)
};
struct type;
struct string;
struct listitem;
// structure for ints/floats/lists/strings (anything that can be assigned to symbol)
struct object {
struct type *type;
union {
struct number number;
struct string *string;
struct listitem *listhead;
} u;
};
struct string {
int length;
int refs;
char payload[1]; // real structs are malloc'd to correct size
};
struct listitem {
struct listitem *next;
struct listitem *prev;
union {
struct {
int length; // this does not include the head element
int refs;
} listinfo; // if item is list head
struct object payload; // if item is not list head
} u;
};
// debugging flag, should be undefined in release version
// #define FDEBUG
@ -38,11 +81,6 @@ struct result { // either int or float
#ifndef NULL
#define NULL ((void *) 0)
#endif
// Boolean values
#ifndef FALSE
#define FALSE 0
#define TRUE (!FALSE)
#endif
#endif

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
//
// CPU type stuff
@ -7,7 +7,7 @@
#include "config.h"
#include "alu.h"
#include "dynabuf.h"
#include "global.h" // FIXME - remove when no longer needed
#include "global.h"
#include "input.h"
#include "mnemo.h"
#include "output.h"
@ -17,36 +17,37 @@
// constants
static struct cpu_type cpu_type_6502 = {
keyword_is_6502_mnemo,
CPUFLAG_INDIRECTJMPBUGGY, // JMP ($xxFF) is buggy
CPUFLAG_WARN_ABOUT_FF_PTR | CPUFLAG_INDIRECTJMPBUGGY, // warn about "XYZ ($ff),y" and "jmp ($XYff)"
234 // !align fills with "NOP"
};
static struct cpu_type cpu_type_6510 = {
keyword_is_6510_mnemo,
CPUFLAG_INDIRECTJMPBUGGY | CPUFLAG_8B_AND_AB_NEED_0_ARG, // JMP ($xxFF) is buggy, ANE/LXA #$xx are unstable unless arg is $00
static struct cpu_type cpu_type_nmos6502 = {
keyword_is_nmos6502_mnemo,
CPUFLAG_WARN_ABOUT_FF_PTR | CPUFLAG_INDIRECTJMPBUGGY | CPUFLAG_8B_AND_AB_NEED_0_ARG, // ANE/LXA #$xx are unstable unless arg is $00
234 // !align fills with "NOP"
};
static struct cpu_type cpu_type_c64dtv2 = {
keyword_is_c64dtv2_mnemo,
CPUFLAG_INDIRECTJMPBUGGY | CPUFLAG_8B_AND_AB_NEED_0_ARG, // JMP ($xxFF) is buggy, ANE/LXA #$xx are unstable unless arg is $00
CPUFLAG_WARN_ABOUT_FF_PTR | CPUFLAG_INDIRECTJMPBUGGY | CPUFLAG_8B_AND_AB_NEED_0_ARG,
234 // !align fills with "NOP"
};
static struct cpu_type cpu_type_65c02 = {
keyword_is_65c02_mnemo,
0, // no flags
CPUFLAG_WARN_ABOUT_FF_PTR, // from WDC docs
234 // !align fills with "NOP"
};
static struct cpu_type cpu_type_r65c02 = {
keyword_is_r65c02_mnemo,
0, // no flags
CPUFLAG_WARN_ABOUT_FF_PTR, // from WDC docs
234 // !align fills with "NOP"
};
static struct cpu_type cpu_type_w65c02 = {
keyword_is_w65c02_mnemo,
0, // no flags
CPUFLAG_WARN_ABOUT_FF_PTR, // from WDC docs
234 // !align fills with "NOP"
};
static struct cpu_type cpu_type_65816 = {
keyword_is_65816_mnemo,
// TODO - what about CPUFLAG_WARN_ABOUT_FF_PTR? only needed for old opcodes in emulation mode!
CPUFLAG_SUPPORTSLONGREGS, // allows A and XY to be 16bits wide
234 // !align fills with "NOP"
};
@ -60,24 +61,31 @@ static struct cpu_type cpu_type_4502 = {
CPUFLAG_DECIMALSUBTRACTBUGGY, // SBC does not work reliably in decimal mode
234 // !align fills with "NOP"
};
static struct cpu_type cpu_type_m65 = {
keyword_is_m65_mnemo,
CPUFLAG_WARN_ABOUT_FF_PTR, // TODO - remove this? check datasheets/realhw!
234 // !align fills with "NOP"
};
// variables
// predefined stuff
static struct ronode *cputype_tree = NULL;
static struct ronode cputype_list[] = {
#define KNOWN_TYPES "'6502', '6510', '65c02', 'r65c02', 'w65c02', '65816', '65ce02', '4502', 'c64dtv2'" // shown in CLI error message for unknown types
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),
PREDEFNODE("6510", &cpu_type_6510),
PREDEFNODE("nmos6502", &cpu_type_nmos6502),
PREDEFNODE("6510", &cpu_type_nmos6502),
PREDEFNODE("65c02", &cpu_type_65c02),
PREDEFNODE("r65c02", &cpu_type_r65c02),
PREDEFNODE("w65c02", &cpu_type_w65c02),
PREDEFNODE("65816", &cpu_type_65816),
PREDEFNODE("65ce02", &cpu_type_65ce02),
PREDEFNODE("4502", &cpu_type_4502),
PREDEFLAST("c64dtv2", &cpu_type_c64dtv2),
PREDEFNODE("m65", &cpu_type_m65),
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
@ -87,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;
@ -102,7 +107,7 @@ const struct cpu_type *cputype_find(void)
// if cpu type and value don't match, complain instead.
// FIXME - error message might be confusing if it is thrown not because of
// initial change, but because of reverting back to old cpu type after "{}" block!
void vcpu_check_and_set_reg_length(int *var, int make_long)
void vcpu_check_and_set_reg_length(boolean *var, boolean make_long)
{
if (((CPU_state.type->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) && make_long)
Throw_error("Chosen CPU does not support long registers.");
@ -116,4 +121,6 @@ void cputype_passinit(const struct cpu_type *cpu_type)
{
// handle cpu type (default is 6502)
CPU_state.type = cpu_type ? cpu_type : &cpu_type_6502;
CPU_state.a_is_long = FALSE; // short accu
CPU_state.xy_are_long = FALSE; // short index regs
}

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
//
// CPU type stuff
@ -14,20 +14,20 @@
struct cpu_type {
// This function is not allowed to change GlobalDynaBuf
// because that's where the mnemonic is stored!
int (*keyword_is_mnemonic)(int);
int flags; // see below for bit meanings
char default_align_value;
boolean (*keyword_is_mnemonic)(int);
bits flags; // see below for bit meanings
unsigned char default_align_value;
};
#define CPUFLAG_INDIRECTJMPBUGGY (1u << 0) // warn if "jmp ($xxff)" is assembled
#define CPUFLAG_SUPPORTSLONGREGS (1u << 1) // allow "!al" and "!rl" pseudo opcodes
#define CPUFLAG_8B_AND_AB_NEED_0_ARG (1u << 2) // warn if "ane/lxa #$xx" uses non-zero arg
#define CPUFLAG_ISBIGENDIAN (1u << 3) // for 16/24/32-bit values, output msb first
#define CPUFLAG_DECIMALSUBTRACTBUGGY (1u << 4) // warn if "sed" is assembled
#define CPUFLAG_WARN_ABOUT_FF_PTR (1u << 5) // warn if MNEMO($ff) is assembled
// if cpu type and value match, set register length variable to value.
// if cpu type and value don't match, complain instead.
extern void vcpu_check_and_set_reg_length(int *var, int make_long);
extern void vcpu_check_and_set_reg_length(boolean *var, boolean make_long);
// set default value for pass
extern void cputype_passinit(const struct cpu_type *cpu_type);
// lookup cpu type held in DynaBuf and return its struct pointer (or NULL on failure)

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
@ -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));
}
@ -109,7 +116,13 @@ static char *ensure_free_space(struct dynabuf *db, int size)
void DynaBuf_to_lower(struct dynabuf *target, struct dynabuf *source)
{
char *read,
*write;
*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)
@ -117,17 +130,16 @@ void DynaBuf_to_lower(struct dynabuf *target, struct dynabuf *source)
// convert to lower case
read = source->buffer; // CAUTION - ptr may change when buf grows!
write = target->buffer; // CAUTION - ptr may change when buf grows!
while (*read)
*write++ = (*read++) | 32;
while ((byte = *read++)) {
// we want to keep underscore, so this check restricts:
if (byte <= 'Z')
byte |= 32;
*write++ = byte;
}
// Okay, so this method of converting to lowercase is lousy.
// But actually it doesn't matter, because only pre-defined
// keywords are converted, and all of those are plain
// old-fashioned 7-bit ASCII anyway. So I guess it'll do.
// 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

@ -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
//
// Character encoding stuff
@ -9,7 +9,7 @@
#include "alu.h"
#include "acme.h"
#include "dynabuf.h"
#include "global.h" // FIXME - remove when no longer needed
#include "global.h"
#include "output.h"
#include "input.h"
#include "tree.h"
@ -17,41 +17,41 @@
// struct definition
struct encoder {
char (*fn)(char);
unsigned char (*fn)(unsigned char);
// maybe add table pointer?
};
// variables
static char outermost_table[256]; // space for encoding table...
static unsigned char outermost_table[256]; // space for encoding table...
const struct encoder *encoder_current; // gets set before each pass
char *encoding_loaded_table = outermost_table; // ...loaded from file
unsigned char *encoding_loaded_table = outermost_table; // ...loaded from file
// encoder functions:
// convert raw to raw (do not convert at all)
static char encoderfn_raw(char byte)
static unsigned char encoderfn_raw(unsigned char byte)
{
return byte;
}
// convert raw to petscii
static char encoderfn_pet(char byte)
static unsigned char encoderfn_pet(unsigned char byte)
{
if ((byte >= 'A') && (byte <= 'Z'))
return (char) (byte | 0x80); // FIXME - check why SAS-C
if ((byte >= 'a') && (byte <= 'z')) // wants these casts.
return (char) (byte - 32); // There are more below.
if ((byte >= (unsigned char) 'A') && (byte <= (unsigned char) 'Z'))
return byte | 0x80;
if ((byte >= (unsigned char) 'a') && (byte <= (unsigned char) 'z'))
return byte - 32;
return byte;
}
// convert raw to C64 screencode
static char encoderfn_scr(char byte)
static unsigned char encoderfn_scr(unsigned char byte)
{
if ((byte >= 'a') && (byte <= 'z'))
return (char) (byte - 96); // shift uppercase down
if ((byte >= '[') && (byte <= '_'))
return (char) (byte - 64); // shift [\]^_ down
if ((byte >= (unsigned char) 'a') && (byte <= (unsigned char) 'z'))
return byte - 96; // shift uppercase down
if ((byte >= (unsigned char) '[') && (byte <= (unsigned char) '_'))
return byte - 64; // shift [\]^_ down
if (byte == '`')
return 64; // shift ` down
if (byte == '@')
@ -59,9 +59,9 @@ static char encoderfn_scr(char byte)
return byte;
}
// convert raw to whatever is defined in table
static char encoderfn_file(char byte)
static unsigned char encoderfn_file(unsigned char byte)
{
return encoding_loaded_table[(unsigned char) byte];
return encoding_loaded_table[byte];
}
@ -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
};
@ -97,7 +97,7 @@ static struct ronode encoder_list[] = {
// convert character using current encoding (exported for use by alu.c and pseudoopcodes.c)
char encoding_encode_char(char byte)
unsigned char encoding_encode_char(unsigned char byte)
{
return encoder_current->fn(byte);
}
@ -109,17 +109,10 @@ void encoding_passinit(void)
}
// try to load encoding table from given file
void encoding_load(char target[256], const char *filename)
void encoding_load_from_file(unsigned char target[256], FILE *stream)
{
FILE *fd = fopen(filename, FILE_READBINARY);
if (fd) {
if (fread(target, sizeof(char), 256, fd) != 256)
Throw_error("Conversion table incomplete.");
fclose(fd);
} else {
Throw_error(exception_cannot_open_input_file);
}
if (fread(target, sizeof(char), 256, stream) != 256)
Throw_error("Conversion table incomplete.");
}
// lookup encoder held in DynaBuf and return its struct pointer (or NULL on failure)
@ -127,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

@ -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
//
// Character encoding stuff
@ -7,23 +7,26 @@
#define encoding_H
#include <stdio.h> // for FILE*
//struct encoder;
extern const struct encoder *encoder_current; // gets set before each pass
extern const struct encoder *encoder_current; // gets set before each pass TODO - set for each part
extern const struct encoder encoder_raw;
extern const struct encoder encoder_pet;
extern const struct encoder encoder_scr;
extern const struct encoder encoder_file;
extern char *encoding_loaded_table; // ...loaded from file
extern unsigned char *encoding_loaded_table; // ...loaded from file
// prototypes
// convert character using current encoding
extern char encoding_encode_char(char byte);
extern unsigned char encoding_encode_char(unsigned char byte);
// set "raw" as default encoding
extern void encoding_passinit(void);
// try to load encoding table from given file
extern void encoding_load(char target[256], const char *filename);
extern void encoding_load_from_file(unsigned char target[256], FILE *stream);
// lookup encoder held in DynaBuf and return its struct pointer (or NULL on failure)
extern const struct encoder *encoding_find(void);

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
//
// Flow control stuff (loops, conditional assembly etc.)
@ -17,14 +17,39 @@
#include "alu.h"
#include "config.h"
#include "dynabuf.h"
#include "global.h" // FIXME - remove when no longer needed
#include "global.h"
#include "input.h"
#include "mnemo.h"
#include "symbol.h"
#include "tree.h"
// helper functions for "!for" and "!do"
// helper functions for if/ifdef/ifndef/else/for/do/while
// parse symbol name and return if symbol has defined value (called by ifdef/ifndef)
boolean check_ifdef_condition(void)
{
scope_t scope;
struct rwnode *node;
struct symbol *symbol;
// read symbol name
if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before
return FALSE; // there was an error, it has been reported, so return value is more or less meaningless anway
// look for it
Tree_hard_scan(&node, symbols_forest, scope, FALSE);
if (!node)
return FALSE; // not found -> no, not defined
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);
return symbol->object.type->is_defined(&symbol->object);
}
// parse a loop body (TODO - also use for macro body?)
static void parse_ram_block(struct block *block)
@ -43,55 +68,96 @@ void flow_forloop(struct for_loop *loop)
{
struct input loop_input,
*outer_input;
struct result loop_counter;
struct object loop_counter;
// switching input makes us lose GotByte. But we know it's '}' anyway!
// set up new input
loop_input = *Input_now; // copy current input structure into new
loop_input.source_is_ram = TRUE; // set new byte source
loop_input.source = INPUTSRC_RAM; // set new byte source
// remember old input
outer_input = Input_now;
// activate new input
// (not yet useable; pointer and line number are still missing)
Input_now = &loop_input;
// fix line number (not for block, but in case symbol handling throws errors)
Input_now->line_number = loop->block.start;
// init counter
loop_counter.flags = MVALUE_DEFINED | MVALUE_EXISTS;
loop_counter.val.intval = loop->counter.first;
loop_counter.addr_refs = loop->counter.addr_refs;
symbol_set_value(loop->symbol, &loop_counter, TRUE);
if (loop->old_algo) {
loop_counter.type = &type_number;
loop_counter.u.number.ntype = NUMTYPE_INT;
loop_counter.u.number.flags = 0;
loop_counter.u.number.val.intval = loop->counter.first;
loop_counter.u.number.addr_refs = loop->counter.addr_refs;
// CAUTION: next line does not have power to change symbol type, but if
// "symbol already defined" error is thrown, the type will still have
// been changed. this was done so the code below has a counter var.
symbol_set_object(loop->symbol, &loop_counter, POWER_CHANGE_VALUE);
// 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?
if (loop->force_bit)
symbol_set_force_bit(loop->symbol, loop->force_bit);
loop_counter = loop->symbol->object; // update local copy with force bit
loop->symbol->has_been_read = TRUE; // lock force bit
if (loop->use_old_algo) {
// old algo for old syntax:
// if count == 0, skip loop
if (loop->counter.last) {
do {
loop_counter.val.intval += loop->counter.increment;
symbol_set_value(loop->symbol, &loop_counter, TRUE);
loop_counter.u.number.val.intval += loop->counter.increment;
loop->symbol->object = loop_counter; // overwrite whole struct, in case some joker has re-assigned loop counter var
parse_ram_block(&loop->block);
} while (loop_counter.val.intval < loop->counter.last);
} while (loop_counter.u.number.val.intval < loop->counter.last);
}
} else {
// new algo for new syntax:
do {
parse_ram_block(&loop->block);
loop_counter.val.intval += loop->counter.increment;
symbol_set_value(loop->symbol, &loop_counter, TRUE);
} while (loop_counter.val.intval != (loop->counter.last + loop->counter.increment));
loop_counter.u.number.val.intval += loop->counter.increment;
loop->symbol->object = loop_counter; // overwrite whole struct, in case some joker has re-assigned loop counter var
} while (loop_counter.u.number.val.intval != (loop->counter.last + loop->counter.increment));
}
// restore previous input:
Input_now = outer_input;
}
// try to read a condition into DynaBuf and store copy pointer in
// read condition, make copy, link to struct
static void copy_condition(struct condition *condition, char terminator)
{
int err;
SKIPSPACE();
DYNABUF_CLEAR(GlobalDynaBuf);
while ((GotByte != terminator) && (GotByte != CHAR_EOS)) {
// append to GlobalDynaBuf and check for quotes
DYNABUF_APPEND(GlobalDynaBuf, GotByte);
if ((GotByte == '"') || (GotByte == '\'')) {
err = Input_quoted_to_dynabuf(GotByte);
// here GotByte changes, it might become CHAR_EOS
DYNABUF_APPEND(GlobalDynaBuf, GotByte); // add closing quotes (or CHAR_EOS) as well
if (err)
break; // on error, exit before eating CHAR_EOS via GetByte()
}
GetByte();
}
DynaBuf_append(GlobalDynaBuf, CHAR_EOS); // ensure terminator
condition->body = DynaBuf_get_copy(GlobalDynaBuf);
}
// try to read a condition into DynaBuf and store pointer to copy in
// given loop_condition structure.
// if no condition given, NULL is written to structure.
// call with GotByte = first interesting character
void flow_store_doloop_condition(struct loop_condition *condition, char terminator)
void flow_store_doloop_condition(struct condition *condition, char terminator)
{
// write line number
condition->line = Input_now->line_number;
// set defaults
condition->is_until = FALSE;
condition->invert = FALSE;
condition->body = NULL;
// check for empty condition
if (GotByte == terminator)
@ -100,27 +166,34 @@ void flow_store_doloop_condition(struct loop_condition *condition, char terminat
// seems as if there really *is* a condition, so check for until/while
if (Input_read_and_lower_keyword()) {
if (strcmp(GlobalDynaBuf->buffer, "while") == 0) {
//condition.is_until = FALSE;
//condition.invert = FALSE;
} else if (strcmp(GlobalDynaBuf->buffer, "until") == 0) {
condition->is_until = TRUE;
condition->invert = TRUE;
} else {
Throw_error(exception_syntax);
return;
}
// write given condition into buffer
SKIPSPACE();
DYNABUF_CLEAR(GlobalDynaBuf);
Input_until_terminator(terminator);
DynaBuf_append(GlobalDynaBuf, CHAR_EOS); // ensure terminator
condition->body = DynaBuf_get_copy(GlobalDynaBuf);
copy_condition(condition, terminator);
}
}
// check a condition expression
static int check_condition(struct loop_condition *condition)
// read a condition into DynaBuf and store pointer to copy in
// given loop_condition structure.
// call with GotByte = first interesting character
void flow_store_while_condition(struct condition *condition)
{
struct result intresult;
condition->line = Input_now->line_number;
condition->invert = FALSE;
copy_condition(condition, CHAR_SOB);
}
// check a condition expression
static boolean check_condition(struct condition *condition)
{
struct number intresult;
// first, check whether there actually *is* a condition
if (condition->body == NULL)
@ -133,19 +206,19 @@ static int check_condition(struct loop_condition *condition)
ALU_defined_int(&intresult);
if (GotByte)
Throw_serious_error(exception_syntax);
return condition->is_until ? !intresult.val.intval : !!intresult.val.intval;
return condition->invert ? !intresult.val.intval : !!intresult.val.intval;
}
// back end function for "!do" pseudo opcode
void flow_doloop(struct do_loop *loop)
// back end function for "!do" and "!while" pseudo opcodes
void flow_do_while(struct do_while *loop)
{
struct input loop_input;
struct input *outer_input;
// set up new input
loop_input = *Input_now; // copy current input structure into new
loop_input.source_is_ram = TRUE; // set new byte source
loop_input.source = INPUTSRC_RAM; // set new byte source
// remember old input
outer_input = Input_now;
// activate new input (not useable yet, as pointer and
@ -170,62 +243,11 @@ void flow_doloop(struct do_loop *loop)
}
// helper functions for "!if", "!ifdef" and "!ifndef"
// parse or skip a block. Returns whether block's '}' terminator was missing.
// afterwards: GotByte = '}'
static int skip_or_parse_block(int parse)
{
if (!parse) {
Input_skip_or_store_block(FALSE);
return 0;
}
// if block was correctly terminated, return FALSE
Parse_until_eob_or_eof();
// if block isn't correctly terminated, complain and exit
if (GotByte != CHAR_EOB)
Throw_serious_error(exception_no_right_brace);
return 0;
}
// parse {block} [else {block}]
void flow_parse_block_else_block(int parse_first)
{
// Parse first block.
// If it's not correctly terminated, return immediately (because
// in that case, there's no use in checking for an "else" part).
if (skip_or_parse_block(parse_first))
return;
// now GotByte = '}'. Check for "else" part.
// If end of statement, return immediately.
NEXTANDSKIPSPACE();
if (GotByte == CHAR_EOS)
return;
// read keyword and check whether really "else"
if (Input_read_and_lower_keyword()) {
if (strcmp(GlobalDynaBuf->buffer, "else")) {
Throw_error(exception_syntax);
} else {
SKIPSPACE();
if (GotByte != CHAR_SOB)
Throw_serious_error(exception_no_left_brace);
skip_or_parse_block(!parse_first);
// now GotByte = '}'
GetByte();
}
}
Input_ensure_EOS();
}
// parse a whole source code file
void flow_parse_and_close_file(FILE *fd, const char *filename)
{
// be verbose
if (Process_verbosity > 2)
if (config.process_verbosity > 2)
printf("Parsing source file '%s'\n", filename);
// set up new input
Input_new_file(filename, fd);

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
//
// flow control stuff (loops, conditional assembly etc.)
@ -19,7 +19,8 @@ struct block {
// struct to pass "!for" loop stuff from pseudoopcodes.c to flow.c
struct for_loop {
struct symbol *symbol;
int old_algo; // actually bool
bits force_bit;
boolean use_old_algo;
struct {
intval_t first,
last,
@ -29,32 +30,36 @@ struct for_loop {
struct block block;
};
// structs to pass "!do" loop stuff from pseudoopcodes.c to flow.c
struct loop_condition {
// structs to pass "!do"/"!while" stuff from pseudoopcodes.c to flow.c
struct condition {
int line; // original line number
int is_until; // actually bool (0 for WHILE, 1 for UNTIL)
boolean invert; // only set for UNTIL conditions
char *body; // pointer to actual expression
};
struct do_loop {
struct loop_condition head_cond;
struct do_while {
struct condition head_cond;
struct block block;
struct loop_condition tail_cond;
struct condition tail_cond;
};
// parse symbol name and return if symbol has defined value (called by ifdef/ifndef)
extern boolean check_ifdef_condition(void);
// back end function for "!for" pseudo opcode
extern void flow_forloop(struct for_loop *loop);
// try to read a condition into DynaBuf and store copy pointer in
// given loop_condition structure.
// try to read a condition into DynaBuf and store pointer to copy in
// given condition structure.
// if no condition given, NULL is written to structure.
// call with GotByte = first interesting character
extern void flow_store_doloop_condition(struct loop_condition *condition, char terminator);
extern void flow_store_doloop_condition(struct condition *condition, char terminator);
// read a condition into DynaBuf and store pointer to copy in
// given condition structure.
// call with GotByte = first interesting character
extern void flow_store_while_condition(struct condition *condition);
// back end function for "!do" pseudo opcode
extern void flow_doloop(struct do_loop *loop);
extern void flow_do_while(struct do_while *loop);
// parse a whole source code file
extern void flow_parse_and_close_file(FILE *fd, const char *filename);
// parse {block} [else {block}]
extern void flow_parse_block_else_block(int parse_first);
#endif

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
//
// Global stuff - things that are needed by several modules
@ -8,12 +8,16 @@
// 2 Jun 2014 Added warn_on_old_for and warn_on_type_mismatch
// 19 Nov 2014 Merged Johann Klasek's report listing generator patch
// 23 Nov 2014 Merged Martin Piper's "--msvc" error output patch
// 9 Jan 2018 Made '/' a syntax char to allow for "//" comments
// 14 Apr 2020 Added config vars for "ignore zeroes" and "segment warnings to errors"
#include "global.h"
#include <stdio.h>
#include "platform.h"
#include "acme.h"
#include "alu.h"
#include "cpu.h"
#include "dynabuf.h"
#include "encoding.h"
#include "input.h"
#include "macro.h"
#include "output.h"
@ -25,34 +29,23 @@
// 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_cbm[] = "cbm";
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_Zone[] = "Zone";
const char s_subzone[] = "subzone";
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.";
// default value for number of errors before exiting
#define MAXERRORS 10
@ -65,17 +58,17 @@ const char exception_syntax[] = "Syntax error.";
// 7....... Byte allowed to start keyword
// .6...... Byte allowed in keyword
// ..5..... Byte is upper case, can be lowercased by OR-ing this bit(!)
// ...4.... special character for input syntax: 0x00 TAB LF CR SPC : ; }
// ...4.... special character for input syntax: 0x00 TAB LF CR SPC / : ; }
// ....3... preceding sequence of '-' characters is anonymous backward
// label. Currently only set for ')', ',' and CHAR_EOS.
// .....210 currently unused
const char Byte_flags[256] = {
const char global_byte_flags[256] = {
/*$00*/ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// control characters
0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*$20*/ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// " !"#$%&'"
0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,// "()*+,-./"
0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,// "()*+,-./"
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,// "01234567"
0x40, 0x40, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,// "89:;<=>?"
/*$40*/ 0x00, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,// "@ABCDEFG"
@ -106,21 +99,28 @@ const char Byte_flags[256] = {
// variables
int pass_count; // number of current pass (starts 0)
char GotByte; // Last byte read (processed)
int Process_verbosity = 0; // Level of additional output
int warn_on_indented_labels = TRUE; // warn if indented label is encountered
int warn_on_old_for = TRUE; // warn if "!for" with old syntax is found
int warn_on_type_mismatch = FALSE; // use type-checking system
// global counters
int pass_undefined_count; // "NeedValue" type errors
int pass_real_errors; // Errors yet
signed long max_errors = MAXERRORS; // errors before giving up
FILE *msg_stream = NULL; // set to stdout by --use-stdout
int format_msvc = FALSE; // actually bool, enabled by --msvc
int format_color = FALSE; // actually bool, enabled by --color
struct report *report = NULL;
struct config config;
struct pass pass;
// set configuration to default values
void config_default(struct config *conf)
{
conf->pseudoop_prefix = '!'; // can be changed to '.' by CLI switch
conf->process_verbosity = 0; // level of additional output
conf->warn_on_indented_labels = TRUE; // warn if indented label is encountered
conf->warn_on_type_mismatch = FALSE; // use type-checking system
conf->warn_bin_mask = 3; // %11 -> warn if not divisible by four
conf->max_errors = MAXERRORS; // errors before giving up
conf->format_msvc = FALSE; // enabled by --msvc
conf->format_color = FALSE; // enabled by --color
conf->msg_stream = stderr; // set to stdout by --use-stdout
conf->honor_leading_zeroes = TRUE; // disabled by --ignore-zeroes
conf->segment_warning_is_error = FALSE; // enabled by --strict-segments TODO - toggle default?
conf->test_new_features = FALSE; // enabled by --test
conf->wanted_version = VER_CURRENT; // changed by --dialect
}
// memory allocation stuff
@ -137,24 +137,9 @@ void *safe_malloc(size_t size)
// Parser stuff
// Parse (re-)definitions of program counter
static void parse_pc_def(void) // Now GotByte = "*"
{
NEXTANDSKIPSPACE(); // proceed with next char
// re-definitions of program counter change segment
if (GotByte == '=') {
GetByte(); // proceed with next char
notreallypo_setpc();
Input_ensure_EOS();
} else {
Throw_error(exception_syntax);
Input_skip_remainder();
}
}
// Check and return whether first label of statement. Complain if not.
static int first_label_of_statement(int *statement_flags)
static int first_label_of_statement(bits *statement_flags)
{
if ((*statement_flags) & SF_IMPLIED_LABEL) {
Throw_error(exception_syntax);
@ -166,11 +151,83 @@ static int first_label_of_statement(int *statement_flags)
}
// Parse global symbol definition or assembler mnemonic
static void parse_mnemo_or_global_symbol_def(int *statement_flags)
// parse label definition (can be either global or local).
// name must be held in GlobalDynaBuf.
// called by parse_symbol_definition, parse_backward_anon_def, parse_forward_anon_def
// "powers" is used by backward anons to allow changes
static void set_label(scope_t scope, bits stat_flags, bits force_bit, bits powers)
{
struct symbol *symbol;
struct number pc;
struct object result;
if ((stat_flags & SF_FOUND_BLANK) && config.warn_on_indented_labels)
Throw_first_pass_warning("Label name not in leftmost column.");
symbol = symbol_find(scope);
vcpu_read_pc(&pc); // FIXME - if undefined, check pass.complain_about_undefined and maybe throw "value not defined"!
result.type = &type_number;
result.u.number.ntype = NUMTYPE_INT; // FIXME - if undefined, use NUMTYPE_UNDEFINED!
result.u.number.flags = 0;
result.u.number.val.intval = pc.val.intval;
result.u.number.addr_refs = pc.addr_refs;
symbol_set_object(symbol, &result, powers);
if (force_bit)
symbol_set_force_bit(symbol, force_bit);
symbol->pseudopc = pseudopc_get_context();
// global labels must open new scope for cheap locals
if (scope == SCOPE_GLOBAL)
section_new_cheap_scope(section_now);
}
// call with symbol name in GlobalDynaBuf and GotByte == '='
// "powers" is for "!set" pseudo opcode so changes are allowed (see symbol.h for powers)
void parse_assignment(scope_t scope, bits force_bit, bits powers)
{
struct symbol *symbol;
struct object result;
GetByte(); // eat '='
symbol = symbol_find(scope);
ALU_any_result(&result);
// if wanted, mark as address reference
if (typesystem_says_address()) {
// FIXME - checking types explicitly is ugly...
if (result.type == &type_number)
result.u.number.addr_refs = 1;
}
symbol_set_object(symbol, &result, powers);
if (force_bit)
symbol_set_force_bit(symbol, force_bit);
}
// parse symbol definition (can be either global or local, may turn out to be a label).
// name must be held in GlobalDynaBuf.
static void parse_symbol_definition(scope_t scope, bits stat_flags)
{
bits force_bit;
force_bit = Input_get_force_bit(); // skips spaces after (yes, force bit is allowed for label definitions)
if (GotByte == '=') {
// explicit symbol definition (symbol = <something>)
parse_assignment(scope, force_bit, POWER_NONE);
Input_ensure_EOS();
} else {
// implicit symbol definition (label)
set_label(scope, stat_flags, force_bit, POWER_NONE);
}
}
// Parse global symbol definition or assembler mnemonic
static void parse_mnemo_or_global_symbol_def(bits *statement_flags)
{
boolean is_mnemonic;
is_mnemonic = CPU_state.type->keyword_is_mnemonic(Input_read_keyword());
// It is only a label if it isn't a mnemonic
if ((CPU_state.type->keyword_is_mnemonic(Input_read_keyword()) == FALSE)
if ((!is_mnemonic)
&& first_label_of_statement(statement_flags)) {
// Now GotByte = illegal char
// 04 Jun 2005: this fix should help to explain "strange" error messages.
@ -178,41 +235,45 @@ static void parse_mnemo_or_global_symbol_def(int *statement_flags)
if ((*GLOBALDYNABUF_CURRENT == (char) 0xa0)
|| ((GlobalDynaBuf->size >= 2) && (GLOBALDYNABUF_CURRENT[0] == (char) 0xc2) && (GLOBALDYNABUF_CURRENT[1] == (char) 0xa0)))
Throw_first_pass_warning("Label name starts with a shift-space character.");
symbol_parse_definition(SCOPE_GLOBAL, *statement_flags);
parse_symbol_definition(SCOPE_GLOBAL, *statement_flags);
}
}
// parse local symbol definition
static void parse_local_symbol_def(int *statement_flags)
// parse (cheap) local symbol definition
static void parse_local_symbol_def(bits *statement_flags, scope_t scope)
{
if (!first_label_of_statement(statement_flags))
return;
GetByte(); // start after '.'
GetByte(); // start after '.'/'@'
if (Input_read_keyword())
symbol_parse_definition(section_now->scope, *statement_flags);
parse_symbol_definition(scope, *statement_flags);
}
// parse anonymous backward label definition. Called with GotByte == '-'
static void parse_backward_anon_def(int *statement_flags)
static void parse_backward_anon_def(bits *statement_flags)
{
if (!first_label_of_statement(statement_flags))
return;
DYNABUF_CLEAR(GlobalDynaBuf);
do
DYNABUF_APPEND(GlobalDynaBuf, '-');
while (GetByte() == '-');
DynaBuf_append(GlobalDynaBuf, '\0');
symbol_set_label(section_now->scope, *statement_flags, 0, TRUE); // this "TRUE" is the whole secret
// backward anons change their value!
set_label(section_now->local_scope, *statement_flags, NO_FORCE_BIT, POWER_CHANGE_VALUE);
}
// parse anonymous forward label definition. called with GotByte == ?
static void parse_forward_anon_def(int *statement_flags)
static void parse_forward_anon_def(bits *statement_flags)
{
if (!first_label_of_statement(statement_flags))
return;
DYNABUF_CLEAR(GlobalDynaBuf);
DynaBuf_append(GlobalDynaBuf, '+');
while (GotByte == '+') {
@ -221,8 +282,8 @@ static void parse_forward_anon_def(int *statement_flags)
}
symbol_fix_forward_anon_name(TRUE); // TRUE: increment counter
DynaBuf_append(GlobalDynaBuf, '\0');
//printf("[%d, %s]\n", section_now->scope, GlobalDynaBuf->buffer);
symbol_set_label(section_now->scope, *statement_flags, 0, FALSE);
//printf("[%d, %s]\n", section_now->local_scope, GlobalDynaBuf->buffer);
set_label(section_now->local_scope, *statement_flags, NO_FORCE_BIT, POWER_NONE);
}
@ -231,7 +292,7 @@ static void parse_forward_anon_def(int *statement_flags)
// Has to be re-entrant.
void Parse_until_eob_or_eof(void)
{
int statement_flags;
bits statement_flags;
// // start with next byte, don't care about spaces
// NEXTANDSKIPSPACE();
@ -243,46 +304,52 @@ void Parse_until_eob_or_eof(void)
statement_flags = 0; // no "label = pc" definition yet
typesystem_force_address_statement(FALSE);
// Parse until end of statement. Only loops if statement
// contains "label = pc" definition and something else; or
// if "!ifdef" is true, or if "!addr" is used without block.
// contains implicit label definition (=pc) and something else; or
// if "!ifdef/ifndef" is true/false, or if "!addr" is used without block.
do {
switch (GotByte) {
case CHAR_EOS: // end of statement
// Ignore now, act later
// (stops from being "default")
break;
case ' ': // space
statement_flags |= SF_FOUND_BLANK;
/*FALLTHROUGH*/
case CHAR_SOL: // start of line
GetByte(); // skip
break;
case '-':
parse_backward_anon_def(&statement_flags);
break;
case '+':
GetByte();
if ((GotByte == LOCAL_PREFIX)
|| (BYTEFLAGS(GotByte) & CONTS_KEYWORD))
Macro_parse_call();
else
parse_forward_anon_def(&statement_flags);
break;
case PSEUDO_OPCODE_PREFIX:
// check for pseudo opcodes was moved out of switch,
// because prefix character is now configurable.
if (GotByte == config.pseudoop_prefix) {
pseudoopcode_parse();
break;
case '*':
parse_pc_def();
break;
case LOCAL_PREFIX:
parse_local_symbol_def(&statement_flags);
break;
default:
if (BYTEFLAGS(GotByte) & STARTS_KEYWORD) {
parse_mnemo_or_global_symbol_def(&statement_flags);
} else {
Throw_error(exception_syntax);
Input_skip_remainder();
} else {
switch (GotByte) {
case CHAR_EOS: // end of statement
// Ignore now, act later
// (stops from being "default")
break;
case ' ': // space
statement_flags |= SF_FOUND_BLANK;
/*FALLTHROUGH*/
case CHAR_SOL: // start of line
GetByte(); // skip
break;
case '-':
parse_backward_anon_def(&statement_flags);
break;
case '+':
GetByte();
if ((GotByte == LOCAL_PREFIX) // TODO - allow "cheap macros"?!
|| (BYTE_CONTINUES_KEYWORD(GotByte)))
Macro_parse_call();
else
parse_forward_anon_def(&statement_flags);
break;
case '*':
notreallypo_setpc(); // define program counter (fn is in pseudoopcodes.c)
break;
case LOCAL_PREFIX:
parse_local_symbol_def(&statement_flags, section_now->local_scope);
break;
case CHEAP_PREFIX:
parse_local_symbol_def(&statement_flags, section_now->cheap_scope);
break;
default:
if (BYTE_STARTS_KEYWORD(GotByte)) {
parse_mnemo_or_global_symbol_def(&statement_flags);
} else {
Throw_error(exception_syntax);
Input_skip_remainder();
}
}
}
} while (GotByte != CHAR_EOS); // until end-of-statement
@ -321,15 +388,16 @@ int Throw_get_counter(void)
// This function will do the actual output for warnings, errors and serious
// errors. It shows the given message string, as well as the current
// context: file name, line number, source type and source title.
// TODO: make un-static so !info and !debug can use this.
static void throw_message(const char *message, const char *type)
{
++throw_counter;
if (format_msvc)
fprintf(msg_stream, "%s(%d) : %s (%s %s): %s\n",
if (config.format_msvc)
fprintf(config.msg_stream, "%s(%d) : %s (%s %s): %s\n",
Input_now->original_filename, Input_now->line_number,
type, section_now->type, section_now->title, message);
else
fprintf(msg_stream, "%s - File %s, line %d (%s %s): %s\n",
fprintf(config.msg_stream, "%s - File %s, line %d (%s %s): %s\n",
type, Input_now->original_filename, Input_now->line_number,
section_now->type, section_now->title, message);
}
@ -342,7 +410,7 @@ static void throw_message(const char *message, const char *type)
void Throw_warning(const char *message)
{
PLATFORM_WARNING(message);
if (format_color)
if (config.format_color)
throw_message(message, "\033[33mWarning\033[0m");
else
throw_message(message, "Warning");
@ -350,7 +418,7 @@ void Throw_warning(const char *message)
// Output a warning if in first pass. See above.
void Throw_first_pass_warning(const char *message)
{
if (pass_count == 0)
if (FIRST_PASS)
Throw_warning(message);
}
@ -363,12 +431,12 @@ void Throw_first_pass_warning(const char *message)
void Throw_error(const char *message)
{
PLATFORM_ERROR(message);
if (format_color)
if (config.format_color)
throw_message(message, "\033[31mError\033[0m");
else
throw_message(message, "Error");
++pass_real_errors;
if (pass_real_errors >= max_errors)
++pass.error_count;
if (pass.error_count >= config.max_errors)
exit(ACME_finalize(EXIT_FAILURE));
}
@ -380,7 +448,7 @@ void Throw_error(const char *message)
void Throw_serious_error(const char *message)
{
PLATFORM_SERIOUS(message);
if (format_color)
if (config.format_color)
throw_message(message, "\033[1m\033[31mSerious error\033[0m");
else
throw_message(message, "Serious error");
@ -396,3 +464,125 @@ void Bug_found(const char *message, int code)
fprintf(stderr, "(0x%x:)", code);
Throw_serious_error(message);
}
// insert object (in case of list, will iterate/recurse until done)
void output_object(struct object *object, struct iter_context *iter)
{
struct listitem *item;
int length;
char *read;
if (object->type == &type_number) {
if (object->u.number.ntype == NUMTYPE_UNDEFINED)
iter->fn(0);
else if (object->u.number.ntype == NUMTYPE_INT)
iter->fn(object->u.number.val.intval);
else if (object->u.number.ntype == NUMTYPE_FLOAT)
iter->fn(object->u.number.val.fpval);
else
Bug_found("IllegalNumberType0", object->u.number.ntype);
} else if (object->type == &type_list) {
// iterate over list
item = object->u.listhead->next;
while (item != object->u.listhead) {
output_object(&item->u.payload, iter);
item = item->next;
}
} else if (object->type == &type_string) {
// iterate over string
read = object->u.string->payload;
length = object->u.string->length;
// single-char strings are accepted, to be more compatible with
// versions before 0.97 (and empty strings are not really a problem...)
if (iter->accept_long_strings || (length < 2)) {
while (length--)
iter->fn(iter->stringxor ^ encoding_encode_char(*(read++)));
} else {
Throw_error("There's more than one character."); // see alu.c for the original of this error
}
} else {
Bug_found("IllegalObjectType", 0);
}
}
// output 8-bit value with range check
void output_8(intval_t value)
{
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 < -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 < -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 < -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 < -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 < -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);
}
// output 32-bit value (without range check) little-endian
void output_le32(intval_t value)
{
// 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);
}

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
//
// Global stuff - things that are needed by several modules
@ -14,65 +14,81 @@
#include <string.h>
#include "config.h"
#define PSEUDO_OPCODE_PREFIX '!' // FIXME - this is not yet used consistently!
#define LOCAL_PREFIX '.' // FIXME - this is not yet used consistently!
#define CHEAP_PREFIX '@' // prefix character for cheap locals
// Constants
#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_cbm[];
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_Zone[];
#define s_zone (s_subzone + 3) // Yes, I know I'm sick
extern const char s_subzone[];
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[];
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[];
// byte flags table
extern const char Byte_flags[];
#define BYTEFLAGS(c) (Byte_flags[(unsigned char) c])
#define STARTS_KEYWORD (1u << 7) // Byte is allowed to start a keyword
#define CONTS_KEYWORD (1u << 6) // Byte is allowed in a keyword
#define BYTEIS_UPCASE (1u << 5) // Byte is upper case and can be
// converted to lower case by OR-ing this bit(!)
#define BYTEIS_SYNTAX (1u << 4) // special character for input syntax
#define FOLLOWS_ANON (1u << 3) // preceding '-' are backward label
extern const char global_byte_flags[];
#define BYTE_STARTS_KEYWORD(b) (global_byte_flags[(unsigned char) b] & (1u << 7)) // byte is allowed at start of keyword (a-z, A-Z, _, everything>127)
#define BYTE_CONTINUES_KEYWORD(b) (global_byte_flags[(unsigned char) b] & (1u << 6)) // byte is allowed in a keyword (as above, plus digits)
//#define BYTE_TO_LOWER_CASE(b) bit 5 means: "byte is upper case, and can be converted to lower case by ORing this bit" - but this is not used at the moment!
#define BYTE_IS_SYNTAX_CHAR(b) (global_byte_flags[(unsigned char) b] & (1u << 4)) // special character for input syntax
#define BYTE_FOLLOWS_ANON(b) (global_byte_flags[(unsigned char) b] & (1u << 3)) // preceding '-' are backward label
// bits 2, 1 and 0 are currently unused
extern int pass_count;
extern int Process_verbosity; // Level of additional output
extern int warn_on_indented_labels; // warn if indented label is encountered
extern int warn_on_old_for; // warn if "!for" with old syntax is found
extern int warn_on_type_mismatch; // use type-checking system
// TODO - put in runtime struct:
extern char GotByte; // Last byte read (processed)
// global counters
extern int pass_undefined_count; // "NeedValue" type errors in current pass
extern int pass_real_errors; // Errors yet
extern signed long max_errors; // errors before giving up
extern FILE *msg_stream; // set to stdout by --errors_to_stdout
extern int format_msvc; // actually bool, enabled by --msvc
extern int format_color; // actually bool, enabled by --color
enum version {
VER_OLDEST_SUPPORTED, // v0.85 looks like the oldest version it makes sense to actually support
VER_DEPRECATE_REALPC, // v0.86 made !pseudopc/!realpc give a warning to use !pseudopc{} instead, and !to wants a file format
VER_SHORTER_SETPC_WARNING, // v0.93 claimed to allow *= inside !pseudopc blocks, but didn't. It shortened the warning, but '}' or !realpc clobbered PC
VER_RIGHTASSOCIATIVEPOWEROF, // v0.94.6 made "power of" operator right-associative
// v0.94.7 fixed a bug: empty code segments no longer included in output file
VER_DISABLED_OBSOLETE_STUFF, // v0.94.8 made *= work inside !pseudopc, disabled !cbm/!realpc/!subzone
VER_NEWFORSYNTAX, // v0.94.12 introduced the new "!for" syntax
// v0.95.2 changed ANC#8 from 0x2b to 0x0b
VER_BACKSLASHESCAPING, // v0.97 introduced backslash escaping (and therefore strings)
VER_CURRENT, // "RELEASE"
// possible changes in future versions:
// paths should be relative to file, not start dir
// ignore leading zeroes?
VER_FUTURE // far future
};
// configuration
struct config {
char pseudoop_prefix; // '!' or '.'
int process_verbosity; // level of additional output
boolean warn_on_indented_labels; // warn if indented label is encountered
boolean warn_on_type_mismatch; // use type-checking system
int warn_bin_mask; // bitmask for digit counter of binary literals
signed long max_errors; // errors before giving up
boolean format_msvc; // enabled by --msvc
boolean format_color; // enabled by --color
FILE *msg_stream; // defaults to stderr, changed to stdout by --use-stdout
boolean honor_leading_zeroes; // TRUE, disabled by --ignore-zeroes
boolean segment_warning_is_error; // FALSE, enabled by --strict-segments
boolean test_new_features; // FALSE, enabled by --test
enum version wanted_version; // set by --dialect (and --test --test)
};
extern struct config config;
struct pass {
int number; // counts up from zero
int undefined_count; // counts undefined expression results (if this stops decreasing, next pass must list them as errors)
//int needvalue_count; // counts undefined expression results actually needed for output (when this hits zero, we're done) FIXME - use
int error_count;
boolean complain_about_undefined; // will be FALSE until error pass is needed
};
extern struct pass pass;
#define FIRST_PASS (pass.number == 0)
// report stuff
#define REPORT_ASCBUFSIZE 1024
@ -86,7 +102,7 @@ struct report {
char asc_buf[REPORT_ASCBUFSIZE]; // source bytes
char bin_buf[REPORT_BINBUFSIZE]; // output bytes
};
extern struct report *report;
extern struct report *report; // TODO - put in "part" struct
// Macros for skipping a single space character
#define SKIPSPACE() \
@ -103,8 +119,13 @@ do { \
// Prototypes
// set configuration to default values
extern void config_default(struct config *conf);
// allocate memory and die if not available
extern void *safe_malloc(size_t);
extern void *safe_malloc(size_t amount);
// call with symbol name in GlobalDynaBuf and GotByte == '='
// "powers" is for "!set" pseudo opcode so changes are allowed (see symbol.h for powers)
extern void parse_assignment(scope_t scope, bits force_bit, bits powers);
// Parse block, beginning with next byte.
// End reason (either CHAR_EOB or CHAR_EOF) can be found in GotByte afterwards
// Has to be re-entrant.
@ -119,22 +140,44 @@ extern int Throw_get_counter(void);
// This means the produced code looks as expected. But there has been a
// situation that should be reported to the user, for example ACME may have
// assembled a 16-bit parameter with an 8-bit value.
extern void Throw_warning(const char *);
extern void Throw_warning(const char *msg);
// Output a warning if in first pass. See above.
extern void Throw_first_pass_warning(const char *);
extern void Throw_first_pass_warning(const char *msg);
// Output an error.
// This means something went wrong in a way that implies that the output
// almost for sure won't look like expected, for example when there was a
// syntax error. The assembler will try to go on with the assembly though, so
// the user gets to know about more than one of his typos at a time.
extern void Throw_error(const char *);
extern void Throw_error(const char *msg);
// Output a serious error, stopping assembly.
// Serious errors are those that make it impossible to go on with the
// assembly. Example: "!fill" without a parameter - the program counter cannot
// be set correctly in this case, so proceeding would be of no use at all.
extern void Throw_serious_error(const char *);
extern void Throw_serious_error(const char *msg);
// handle bugs
extern void Bug_found(const char *, int);
extern void Bug_found(const char *msg, int code);
// insert object (in case of list, will iterate/recurse until done)
struct iter_context {
void (*fn)(intval_t); // output function
boolean accept_long_strings; // if FALSE, only 1-char-strings work
unsigned char stringxor; // for !scrxor, 0 otherwise
};
extern void output_object(struct object *object, struct iter_context *iter);
// output 8-bit value with range check
extern void output_8(intval_t value);
// output 16-bit value with range check big-endian
extern void output_be16(intval_t value);
// output 16-bit value with range check little-endian
extern void output_le16(intval_t value);
// output 24-bit value with range check big-endian
extern void output_be24(intval_t value);
// output 24-bit value with range check little-endian
extern void output_le24(intval_t value);
// output 32-bit value (without range check) big-endian
extern void output_be32(intval_t value);
// output 32-bit value (without range check) little-endian
extern void output_le32(intval_t value);
#endif

View File

@ -1,14 +1,15 @@
// 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
//
// Input stuff
// 19 Nov 2014 Merged Johann Klasek's report listing generator patch
// 9 Jan 2018 Allowed "//" comments
#include "input.h"
#include "config.h"
#include "alu.h"
#include "dynabuf.h"
#include "global.h" // FIXME - remove when no longer needed
#include "global.h"
#include "platform.h"
#include "section.h"
#include "symbol.h"
@ -17,20 +18,17 @@
// Constants
const char FILE_READBINARY[] = "rb";
#define CHAR_TAB (9) // Tab character
#define CHAR_LF (10) // line feed (in file)
// (10) // start of line (in high-level format)
#define CHAR_CR (13) // carriage return (in file)
// (13) // end of file (in high-level format)
#define CHAR_STATEMENT_DELIMITER ':'
#define CHAR_COMMENT_SEPARATOR ';'
// if the characters above are changed, don't forget to adjust ByteFlags[]!
// if the characters above are changed, don't forget to adjust byte_flags[]!
// fake input structure (for error msgs before any real input is established)
static struct input outermost = {
"<none>", // file name
0, // line number
FALSE, // Faked file access, so no RAM read
INPUTSRC_FILE, // fake file access, so no RAM read
INPUTSTATE_EOF, // state of input
{
NULL // RAM read pointer or file handle
@ -49,8 +47,8 @@ void Input_new_file(const char *filename, FILE *fd)
{
Input_now->original_filename = filename;
Input_now->line_number = 1;
Input_now->source_is_ram = FALSE;
Input_now->state = INPUTSTATE_NORMAL;
Input_now->source = INPUTSRC_FILE;
Input_now->state = INPUTSTATE_SOF;
Input_now->src.fd = fd;
}
@ -78,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)
@ -115,10 +114,23 @@ static void report_srcchar(char new_char)
// Deliver source code from current file (!) in shortened high-level format
static char get_processed_from_file(void)
{
int from_file = 0;
static int from_file = 0;
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);
@ -134,7 +146,7 @@ static char get_processed_from_file(void)
// defined "from_file", trouble may arise...
Input_now->state = INPUTSTATE_NORMAL;
// EOF must be checked first because it cannot be used
// as an index into Byte_flags[]
// as an index into global_byte_flags[]
if (from_file == EOF) {
// remember to send an end-of-file
Input_now->state = INPUTSTATE_EOF;
@ -143,12 +155,12 @@ static char get_processed_from_file(void)
// check whether character is special one
// if not, everything's cool and froody, so return it
if ((BYTEFLAGS(from_file) & BYTEIS_SYNTAX) == 0)
if (BYTE_IS_SYNTAX_CHAR(from_file) == 0)
return (char) from_file;
// check special characters ("0x00 TAB LF CR SPC :;}")
// check special characters ("0x00 TAB LF CR SPC / : ; }")
switch (from_file) {
case CHAR_TAB: // TAB character
case '\t':
case ' ':
// remember to skip all following blanks
Input_now->state = INPUTSTATE_SKIPBLANKS;
@ -169,15 +181,26 @@ static char get_processed_from_file(void)
Input_now->state = INPUTSTATE_EOB;
return CHAR_EOS; // end of statement
case CHAR_STATEMENT_DELIMITER:
// just deliver an EOS instead
return CHAR_EOS; // end of statement
case CHAR_COMMENT_SEPARATOR:
case '/':
// to check for "//", get another byte:
from_file = getc(Input_now->src.fd);
IF_WANTED_REPORT_SRCCHAR(from_file);
if (from_file != '/') {
// not "//", so:
Input_now->state = INPUTSTATE_AGAIN; // second byte must be parsed normally later on
return '/'; // first byte is returned normally right now
}
// it's really "//", so act as if ';'
/*FALLTHROUGH*/
case ';':
// remember to skip remainder of line
Input_now->state = INPUTSTATE_COMMENT;
return CHAR_EOS; // end of statement
case ':': // statement delimiter
// just deliver an EOS instead
return CHAR_EOS; // end of statement
default:
// complain if byte is 0
Throw_error("Source file contains illegal character.");
@ -188,7 +211,7 @@ static char get_processed_from_file(void)
do {
from_file = getc(Input_now->src.fd);
IF_WANTED_REPORT_SRCCHAR(from_file);
} while ((from_file == CHAR_TAB) || (from_file == ' '));
} while ((from_file == '\t') || (from_file == ' '));
// re-process last byte
Input_now->state = INPUTSTATE_AGAIN;
break;
@ -239,7 +262,7 @@ static char get_processed_from_file(void)
// This function delivers the next byte from the currently active byte source
// in shortened high-level format. FIXME - use fn ptr?
// When inside quotes, use GetQuotedByte() instead!
// When inside quotes, use Input_quoted_to_dynabuf() instead!
char GetByte(void)
{
// for (;;) {
@ -247,11 +270,17 @@ char GetByte(void)
// necessary, because in RAM the source already has
// high-level format
// Otherwise, the source is a file. This means we will call
// GetFormatted() which will do a shit load of conversions.
if (Input_now->source_is_ram)
// get_processed_from_file() which will do a shit load of conversions.
switch (Input_now->source) {
case INPUTSRC_RAM:
GotByte = *(Input_now->src.ram_ptr++);
else
break;
case INPUTSRC_FILE:
GotByte = get_processed_from_file();
break;
default:
Bug_found("IllegalInputSrc", Input_now->source);
}
// // if start-of-line was read, increment line counter and repeat
// if (GotByte != CHAR_SOL)
// return GotByte;
@ -265,16 +294,18 @@ char GetByte(void)
// This function delivers the next byte from the currently active byte source
// in un-shortened high-level format.
// This function complains if CHAR_EOS (end of statement) is read.
char GetQuotedByte(void)
// TODO - check if return value is actually used
static char GetQuotedByte(void)
{
int from_file; // must be an int to catch EOF
// if byte source is RAM, then no conversion is necessary,
// because in RAM the source already has high-level format
if (Input_now->source_is_ram) {
switch (Input_now->source) {
case INPUTSRC_RAM:
// if byte source is RAM, then no conversion is necessary,
// because in RAM the source already has high-level format
GotByte = *(Input_now->src.ram_ptr++);
// Otherwise, the source is a file.
} else {
break;
case INPUTSRC_FILE:
// fetch a fresh byte from the current source file
from_file = getc(Input_now->src.fd);
IF_WANTED_REPORT_SRCCHAR(from_file);
@ -297,7 +328,9 @@ char GetQuotedByte(void)
default:
GotByte = from_file;
}
break;
default:
Bug_found("IllegalInputSrc", Input_now->source);
}
// now check for end of statement
if (GotByte == CHAR_EOS)
@ -306,6 +339,7 @@ char GetQuotedByte(void)
}
// Skip remainder of statement, for example on error
// FIXME - check for quotes, otherwise this might treat a quoted colon like EOS!
void Input_skip_remainder(void)
{
while (GotByte)
@ -318,20 +352,111 @@ void Input_ensure_EOS(void) // Now GotByte = first char to test
{
SKIPSPACE();
if (GotByte) {
Throw_error("Garbage data at end of statement.");
char buf[80]; // actually needed are 51
char quote; // character before and after
quote = (GotByte == '\'') ? '"' : '\''; // use single quotes, unless byte is a single quote (then use double quotes)
sprintf(buf, "Garbage data at end of statement (unexpected %c%c%c).", quote, GotByte, quote);
Throw_error(buf);
Input_skip_remainder();
}
}
// read string to dynabuf until closing quote is found
// returns 1 on errors (unterminated, escaping error)
int Input_quoted_to_dynabuf(char closing_quote)
{
boolean escaped = FALSE;
//DYNABUF_CLEAR(GlobalDynaBuf); // do not clear, caller might want to append to existing contents (TODO - check!)
for (;;) {
GetQuotedByte();
if (GotByte == CHAR_EOS)
return 1; // unterminated string constant; GetQuotedByte will have complained already
if (escaped) {
// previous byte was backslash, so do not check for terminator nor backslash
escaped = FALSE;
// do not actually _convert_ escape sequences to their target byte, that is done by Input_unescape_dynabuf() below!
// TODO - but maybe check for illegal escape sequences?
// at the moment checking is only done when the string
// gets used for something...
} else {
// non-escaped: only terminator and backslash are of interest
if (GotByte == closing_quote)
return 0; // ok
if ((GotByte == '\\') && (config.wanted_version >= VER_BACKSLASHESCAPING))
escaped = TRUE;
}
DYNABUF_APPEND(GlobalDynaBuf, GotByte);
}
}
// process backslash escapes in GlobalDynaBuf (so size might shrink)
// returns 1 on errors (escaping errors)
// TODO - check: if this is only ever called directly after Input_quoted_to_dynabuf, integrate that call here?
int Input_unescape_dynabuf(int read_index)
{
int write_index;
char byte;
boolean escaped;
if (config.wanted_version < VER_BACKSLASHESCAPING)
return 0; // ok
write_index = read_index;
escaped = FALSE;
// CAUTION - contents of dynabuf are not terminated:
while (read_index < GlobalDynaBuf->size) {
byte = GLOBALDYNABUF_CURRENT[read_index++];
if (escaped) {
switch (byte) {
case '\\':
case '\'':
case '"':
break;
case '0': // NUL
byte = 0;
break;
case 't': // TAB
byte = 9;
break;
case 'n': // LF
byte = 10;
break;
case 'r': // CR
byte = 13;
break;
// TODO - 'a' to BEL? others?
default:
Throw_error("Unsupported backslash sequence."); // TODO - add unexpected character to error message?
}
GLOBALDYNABUF_CURRENT[write_index++] = byte;
escaped = FALSE;
} else {
if (byte == '\\') {
escaped = TRUE;
} else {
GLOBALDYNABUF_CURRENT[write_index++] = byte;
}
}
}
if (escaped)
Bug_found("PartialEscapeSequence", 0);
GlobalDynaBuf->size = write_index;
return 0; // ok
}
// Skip or store block (starting with next byte, so call directly after
// reading opening brace).
// If "Store" is TRUE, the block is read into GlobalDynaBuf, then a copy
// is made and a pointer to that is returned.
// the block is read into GlobalDynaBuf.
// If "Store" is TRUE, then a copy is made and a pointer to that is returned.
// If "Store" is FALSE, NULL is returned.
// After calling this function, GotByte holds '}'. Unless EOF was found first,
// but then a serious error would have been thrown.
// FIXME - use a struct block *ptr argument!
char *Input_skip_or_store_block(int store)
char *Input_skip_or_store_block(boolean store)
{
char byte;
int depth = 1; // to find matching block end
@ -340,9 +465,8 @@ char *Input_skip_or_store_block(int store)
DYNABUF_CLEAR(GlobalDynaBuf);
do {
byte = GetByte();
// if wanted, store
if (store)
DYNABUF_APPEND(GlobalDynaBuf, byte);
// store
DYNABUF_APPEND(GlobalDynaBuf, byte);
// now check for some special characters
switch (byte) {
case CHAR_EOF: // End-of-file in block? Sorry, no way.
@ -350,12 +474,8 @@ char *Input_skip_or_store_block(int store)
case '"': // Quotes? Okay, read quoted stuff.
case '\'':
do {
GetQuotedByte();
// if wanted, store
if (store)
DYNABUF_APPEND(GlobalDynaBuf, GotByte);
} while ((GotByte != CHAR_EOS) && (GotByte != byte));
Input_quoted_to_dynabuf(byte);
DYNABUF_APPEND(GlobalDynaBuf, GotByte); // add closing quote
break;
case CHAR_SOB:
++depth;
@ -368,6 +488,7 @@ char *Input_skip_or_store_block(int store)
// in case of skip, return now
if (!store)
return NULL;
// otherwise, prepare to return copy of block
// add EOF, just to make sure block is never read too far
DynaBuf_append(GlobalDynaBuf, CHAR_EOS);
@ -376,33 +497,6 @@ char *Input_skip_or_store_block(int store)
return DynaBuf_get_copy(GlobalDynaBuf);
}
// Read bytes and add to GlobalDynaBuf until the given terminator (or CHAR_EOS)
// is found. Act upon single and double quotes by entering (and leaving) quote
// mode as needed (So the terminator does not terminate when inside quotes).
void Input_until_terminator(char terminator)
{
char byte = GotByte;
for (;;) {
// Terminator? Exit. EndOfStatement? Exit.
if ((byte == terminator) || (byte == CHAR_EOS))
return;
// otherwise, append to GlobalDynaBuf and check for quotes
DYNABUF_APPEND(GlobalDynaBuf, byte);
if ((byte == '"') || (byte == '\'')) {
do {
// Okay, read quoted stuff.
GetQuotedByte(); // throws error on EOS
DYNABUF_APPEND(GlobalDynaBuf, GotByte);
} while ((GotByte != CHAR_EOS) && (GotByte != byte));
// on error, exit now, before calling GetByte()
if (GotByte != byte)
return;
}
byte = GetByte();
}
}
// Append to GlobalDynaBuf while characters are legal for keywords.
// Throws "missing string" error if none.
// Returns number of characters added.
@ -411,7 +505,7 @@ int Input_append_keyword_to_global_dynabuf(void)
int length = 0;
// add characters to buffer until an illegal one comes along
while (BYTEFLAGS(GotByte) & CONTS_KEYWORD) {
while (BYTE_CONTINUES_KEYWORD(GotByte)) {
DYNABUF_APPEND(GlobalDynaBuf, GotByte);
++length;
GetByte();
@ -421,19 +515,20 @@ int Input_append_keyword_to_global_dynabuf(void)
return length;
}
// Check whether GotByte is LOCAL_PREFIX (default '.').
// If not, store global scope value.
// If yes, store current local scope value and read next byte.
// Check GotByte.
// If LOCAL_PREFIX ('.'), store current local scope value and read next byte.
// If CHEAP_PREFIX ('@'), store current cheap scope value and read next byte.
// Otherwise, store global scope value.
// Then jump to Input_read_keyword(), which returns length of keyword.
int Input_read_scope_and_keyword(scope_t *scope)
{
SKIPSPACE();
if (GotByte == LOCAL_PREFIX) {
GetByte();
*scope = section_now->scope;
/* TODO } else if (GotByte == CHEAP_PREFIX) {
*scope = section_now->local_scope;
} else if (GotByte == CHEAP_PREFIX) {
GetByte();
*scope = symbol_cheap_scope; */
*scope = section_now->cheap_scope;
} else {
*scope = SCOPE_GLOBAL;
}
@ -471,25 +566,31 @@ int Input_read_and_lower_keyword(void)
return length;
}
// Try to read a file name. If "allow_library" is TRUE, library access by using
// <...> quoting is possible as well. The file name given in the assembler
// source code is converted from UNIX style to platform style.
// Returns whether error occurred (TRUE on error). Filename in GlobalDynaBuf.
// Try to read a file name.
// If "allow_library" is TRUE, library access by using <...> quoting
// is possible as well. If "uses_lib" is non-NULL, info about library
// usage is stored there.
// The file name given in the assembler source code is converted from
// UNIX style to platform style.
// Returns nonzero on error. Filename in GlobalDynaBuf.
// Errors are handled and reported, but caller should call
// Input_skip_remainder() then.
int Input_read_filename(int allow_library)
int Input_read_filename(boolean allow_library, boolean *uses_lib)
{
int start_of_string;
char *lib_prefix,
end_quote;
terminator;
DYNABUF_CLEAR(GlobalDynaBuf);
SKIPSPACE();
// check for library access
if (GotByte == '<') {
switch (GotByte) {
case '<': // library access
if (uses_lib)
*uses_lib = TRUE;
// if library access forbidden, complain
if (allow_library == FALSE) {
if (!allow_library) {
Throw_error("Writing to library not supported.");
return TRUE;
return 1; // error
}
// read platform's lib prefix
@ -498,40 +599,46 @@ int Input_read_filename(int allow_library)
// if lib prefix not set, complain
if (lib_prefix == NULL) {
Throw_error("\"ACME\" environment variable not found.");
return TRUE;
return 1; // error
}
#endif
// copy lib path and set quoting char
DynaBuf_add_string(GlobalDynaBuf, lib_prefix);
end_quote = '>';
} else {
if (GotByte == '"') {
end_quote = '"';
} else {
Throw_error("File name quotes not found (\"\" or <>).");
return TRUE;
}
terminator = '>';
break;
case '"': // normal access
if (uses_lib)
*uses_lib = FALSE;
terminator = '"';
break;
default: // none of the above
Throw_error("File name quotes not found (\"\" or <>).");
return 1; // error
}
// read first character, complain if closing quote
if (GetQuotedByte() == end_quote) {
// remember border between optional library prefix and string from assembler source file
start_of_string = GlobalDynaBuf->size;
// read file name string
if (Input_quoted_to_dynabuf(terminator))
return 1; // unterminated or escaping error
GetByte(); // eat terminator
// check length
if (GlobalDynaBuf->size == start_of_string) {
Throw_error("No file name given.");
return TRUE;
return 1; // error
}
// read characters until closing quote (or EOS) is reached
// append platform-converted characters to current string
while ((GotByte != CHAR_EOS) && (GotByte != end_quote)) {
DYNABUF_APPEND(GlobalDynaBuf, PLATFORM_CONVERTPATHCHAR(GotByte));
GetQuotedByte();
}
// on error, return
if (GotByte == CHAR_EOS)
return TRUE;
// resolve backslash escapes
if (Input_unescape_dynabuf(start_of_string))
return 1; // escaping error
GetByte(); // fetch next to forget closing quote
// terminate string
DynaBuf_append(GlobalDynaBuf, '\0'); // add terminator
return FALSE; // no error
DynaBuf_append(GlobalDynaBuf, '\0');
#ifdef PLATFORM_CONVERTPATH
// platform-specific path name conversion
PLATFORM_CONVERTPATH(GLOBALDYNABUF_CURRENT + start_of_string);
#endif
return 0; // ok
}
// Try to read a comma, skipping spaces before and after. Return TRUE if comma
@ -547,19 +654,20 @@ int Input_accept_comma(void)
}
// read optional info about parameter length
int Input_get_force_bit(void)
// FIXME - move to different file!
bits Input_get_force_bit(void)
{
char byte;
int force_bit = 0;
bits force_bit = 0;
if (GotByte == '+') {
byte = GetByte();
if (byte == '1')
force_bit = MVALUE_FORCE08;
force_bit = NUMBER_FORCES_8;
else if (byte == '2')
force_bit = MVALUE_FORCE16;
force_bit = NUMBER_FORCES_16;
else if (byte == '3')
force_bit = MVALUE_FORCE24;
force_bit = NUMBER_FORCES_24;
if (force_bit)
GetByte();
else
@ -568,3 +676,76 @@ int Input_get_force_bit(void)
SKIPSPACE();
return force_bit;
}
// include path stuff - should be moved to its own file:
// ring list struct
struct ipi {
struct ipi *next,
*prev;
const char *path;
};
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
// add entry
void includepaths_add(const char *path)
{
struct ipi *ipi;
ipi = safe_malloc(sizeof(*ipi));
ipi->path = path;
ipi->next = &ipi_head;
ipi->prev = ipi_head.prev;
ipi->next->prev = ipi;
ipi->prev->next = ipi;
}
// open file for reading (trying list entries as prefixes)
// "uses_lib" tells whether to access library or to make use of include paths
// file name is expected in GlobalDynaBuf
FILE *includepaths_open_ro(boolean uses_lib)
{
FILE *stream;
struct ipi *ipi;
// first try directly, regardless of whether lib or not:
stream = fopen(GLOBALDYNABUF_CURRENT, FILE_READBINARY);
// if failed and not lib, try include paths:
if ((stream == NULL) && !uses_lib) {
for (ipi = ipi_head.next; ipi != &ipi_head; ipi = ipi->next) {
DYNABUF_CLEAR(pathbuf);
// add first part
DynaBuf_add_string(pathbuf, ipi->path);
// if wanted and possible, ensure last char is directory separator
if (DIRECTORY_SEPARATOR
&& pathbuf->size
&& (pathbuf->buffer[pathbuf->size - 1] != DIRECTORY_SEPARATOR))
DynaBuf_append(pathbuf, DIRECTORY_SEPARATOR);
// add second part
DynaBuf_add_string(pathbuf, GLOBALDYNABUF_CURRENT);
// terminate
DynaBuf_append(pathbuf, '\0');
// try
stream = fopen(pathbuf->buffer, FILE_READBINARY);
//printf("trying <<%s>> - ", pathbuf->buffer);
if (stream) {
//printf("ok\n");
break;
} else {
//printf("failed\n");
}
}
}
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

@ -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
//
// Input stuff
@ -8,13 +8,14 @@
#include <stdio.h> // for FILE
#include "config.h" // for scope_t
#include "config.h" // for bits and scope_t
// type definitions
// 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
@ -25,10 +26,14 @@ enum inputstate {
INPUTSTATE_EOB, // send end-of-block after end-of-statement
INPUTSTATE_EOF, // send end-of-file after end-of-statement
};
enum inputsrc {
INPUTSRC_FILE,
INPUTSRC_RAM
};
struct input {
const char *original_filename; // during RAM reads, too
int line_number, // in file (on RAM reads, too)
source_is_ram; // TRUE if RAM, FALSE if file
int line_number; // in file (on RAM reads, too)
enum inputsrc source;
enum inputstate state; // state of input
union {
FILE *fd; // file descriptor
@ -46,7 +51,7 @@ extern const char FILE_READBINARY[];
#define CHAR_EOB '}' // end of block
#define CHAR_SOL (10) // start of line (in high-level format)
#define CHAR_EOF (13) // end of file (in high-level format)
// If the characters above are changed, don't forget to adjust Byte_flags[]!
// if the characters above are changed, don't forget to adjust global_byte_flags[]!
// Variables
@ -58,34 +63,38 @@ extern struct input *Input_now; // current input structure
// let current input point to start of file
extern void Input_new_file(const char *filename, FILE *fd);
// get next byte from currently active byte source in shortened high-level
// format. When inside quotes, use GetQuotedByte() instead!
// format. When inside quotes, use Input_quoted_to_dynabuf() instead!
extern char GetByte(void);
// get next byte from currently active byte source in un-shortened high-level
// format. Complains if CHAR_EOS (end of statement) is read.
extern char GetQuotedByte(void);
// Skip remainder of statement, for example on error
extern void Input_skip_remainder(void);
// Ensure that the remainder of the current statement is empty, for example
// after mnemonics using implied addressing.
extern void Input_ensure_EOS(void);
// read string to dynabuf until closing quote is found
// returns 1 on errors (unterminated, escaping error)
extern int Input_quoted_to_dynabuf(char closing_quote);
// process backslash escapes in GlobalDynaBuf (so size might shrink)
// returns 1 on errors (escaping errors)
extern int Input_unescape_dynabuf(int start_index);
// Skip or store block (starting with next byte, so call directly after
// reading opening brace).
// If "Store" is TRUE, the block is read into GlobalDynaBuf, then a copy
// is made and a pointer to that is returned.
// the block is read into GlobalDynaBuf.
// If "Store" is TRUE, then a copy is made and a pointer to that is returned.
// If "Store" is FALSE, NULL is returned.
// After calling this function, GotByte holds '}'. Unless EOF was found first,
// but then a serious error would have been thrown.
extern char *Input_skip_or_store_block(int store);
// Read bytes and add to GlobalDynaBuf until the given terminator (or CHAR_EOS)
// is found. Act upon single and double quotes by entering (and leaving) quote
// mode as needed (So the terminator does not terminate when inside quotes).
extern void Input_until_terminator(char terminator);
extern char *Input_skip_or_store_block(boolean store);
// Append to GlobalDynaBuf while characters are legal for keywords.
// Throws "missing string" error if none. Returns number of characters added.
extern int Input_append_keyword_to_global_dynabuf(void);
// Check whether GotByte is a dot.
// If not, store global scope value.
// If yes, store current scope value and read next byte.
// Check GotByte.
// If LOCAL_PREFIX ('.'), store current local scope value and read next byte.
// If CHEAP_PREFIX ('@'), store current cheap scope value and read next byte.
// Otherwise, store global scope value.
// Then jump to Input_read_keyword(), which returns length of keyword.
extern int Input_read_scope_and_keyword(scope_t *scope);
// Clear dynamic buffer, then append to it until an illegal (for a keyword)
@ -98,18 +107,31 @@ extern int Input_read_keyword(void);
// Return its length (without terminator).
// Zero lengths will produce a "missing string" error.
extern int Input_read_and_lower_keyword(void);
// Try to read a file name. If "allow_library" is TRUE, library access by using
// <...> quoting is possible as well. The file name given in the assembler
// source code is converted from UNIX style to platform style.
// Returns whether error occurred (TRUE on error). Filename in GlobalDynaBuf.
// Try to read a file name.
// If "allow_library" is TRUE, library access by using <...> quoting
// is possible as well. If "uses_lib" is non-NULL, info about library
// usage is stored there.
// The file name given in the assembler source code is converted from
// UNIX style to platform style.
// Returns nonzero on error. Filename in GlobalDynaBuf.
// Errors are handled and reported, but caller should call
// Input_skip_remainder() then.
extern int Input_read_filename(int library_allowed);
extern int Input_read_filename(boolean library_allowed, boolean *uses_lib);
// Try to read a comma, skipping spaces before and after. Return TRUE if comma
// found, otherwise FALSE.
extern int Input_accept_comma(void);
// read optional info about parameter length
extern int Input_get_force_bit(void);
extern bits Input_get_force_bit(void);
// include path stuff - should be moved to its own file:
// add entry
extern void includepaths_add(const char *path);
// open file for reading (trying list entries as prefixes)
// "uses_lib" tells whether to access library or to make use of include paths
// file name is expected in GlobalDynaBuf
extern FILE *includepaths_open_ro(boolean uses_lib);
#endif

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
//
// Macro stuff
@ -18,12 +18,10 @@
// Constants
#define MACRONAME_DYNABUF_INITIALSIZE 128
#define NAME_INITIALSIZE 128
#define ARG_SEPARATOR ' ' // separates macro title from arg types
#define ARGTYPE_NUM_VAL 'v'
#define ARGTYPE_NUM_REF 'V'
//#define ARGTYPE_STR_VAL 's'
//#define ARGTYPE_STR_REF 'S'
#define ARGTYPE_VALUE 'v'
#define ARGTYPE_REF 'r'
#define REFERENCE_CHAR '~' // prefix for call-by-reference
#define HALF_INITIAL_ARG_TABLE_SIZE 4
static const char exception_macro_twice[] = "Macro already defined.";
@ -42,14 +40,14 @@ struct macro {
// gives us the possibility to find out which args are call-by-value and
// which ones are call-by-reference.
union macro_arg_t {
struct result result; // value and flags (call by value)
struct object result; // value and flags (call by value)
struct symbol *symbol; // pointer to symbol struct (call by reference)
};
// 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;
@ -62,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
@ -88,8 +79,10 @@ static scope_t get_scope_and_title(void)
// copy macro title to private dynabuf and add separator character
DYNABUF_CLEAR(user_macro_name);
DYNABUF_CLEAR(internal_name);
if (macro_scope != SCOPE_GLOBAL)
if (macro_scope != SCOPE_GLOBAL) {
// TODO - allow "cheap macros"?!
DynaBuf_append(user_macro_name, LOCAL_PREFIX);
}
DynaBuf_add_string(user_macro_name, GLOBALDYNABUF_CURRENT);
DynaBuf_add_string(internal_name, GLOBALDYNABUF_CURRENT);
DynaBuf_append(user_macro_name, '\0');
@ -174,6 +167,8 @@ void Macro_parse_definition(void) // Now GotByte = illegal char after "!macro"
// Valid argument formats are:
// .LOCAL_LABEL_BY_VALUE
// ~.LOCAL_LABEL_BY_REFERENCE
// @CHEAP_LOCAL_LABEL_BY_VALUE
// ~@CHEAP_LOCAL_LABEL_BY_REFERENCE
// GLOBAL_LABEL_BY_VALUE global args are very uncommon,
// ~GLOBAL_LABEL_BY_REFERENCE but not forbidden
// now GotByte = non-space
@ -181,15 +176,16 @@ void Macro_parse_definition(void) // Now GotByte = illegal char after "!macro"
do {
// handle call-by-reference character ('~')
if (GotByte != REFERENCE_CHAR) {
DynaBuf_append(internal_name, ARGTYPE_NUM_VAL);
DynaBuf_append(internal_name, ARGTYPE_VALUE);
} else {
DynaBuf_append(internal_name, ARGTYPE_NUM_REF);
DynaBuf_append(internal_name, ARGTYPE_REF);
DynaBuf_append(GlobalDynaBuf, REFERENCE_CHAR);
GetByte();
}
// handle prefix for local symbols (LOCAL_PREFIX, normally '.')
if (GotByte == LOCAL_PREFIX) {
DynaBuf_append(GlobalDynaBuf, LOCAL_PREFIX);
// handle prefix for (cheap) local symbols ('.'/'@')
if ((GotByte == LOCAL_PREFIX)
|| (GotByte == CHEAP_PREFIX)) {
DynaBuf_append(GlobalDynaBuf, GotByte);
GetByte();
}
// handle symbol name
@ -238,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)
@ -247,9 +247,8 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name
// internal_name = MacroTitle ARG_SEPARATOR (grows to signature)
// Accept n>=0 comma-separated arguments before CHAR_EOS.
// Valid argument formats are:
// EXPRESSION (everything that does NOT start with '~'
// ~.LOCAL_LABEL_BY_REFERENCE
// ~GLOBAL_LABEL_BY_REFERENCE
// ~SYMBOL call by ref
// EXPRESSION call by value (everything that does NOT start with '~')
// now GotByte = non-space
if (GotByte != CHAR_EOS) { // any at all?
do {
@ -260,14 +259,14 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name
// In both cases, GlobalDynaBuf may be used.
if (GotByte == REFERENCE_CHAR) {
// read call-by-reference arg
DynaBuf_append(internal_name, ARGTYPE_NUM_REF);
GetByte(); // skip '~' character
DynaBuf_append(internal_name, ARGTYPE_REF);
GetByte(); // eat '~'
Input_read_scope_and_keyword(&symbol_scope);
// GotByte = illegal char
arg_table[arg_count].symbol = symbol_find(symbol_scope, 0);
arg_table[arg_count].symbol = symbol_find(symbol_scope); // CAUTION, object type may be NULL!
} else {
// read call-by-value arg
DynaBuf_append(internal_name, ARGTYPE_NUM_VAL);
DynaBuf_append(internal_name, ARGTYPE_VALUE);
ALU_any_result(&(arg_table[arg_count].result));
}
++arg_count;
@ -289,7 +288,7 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name
// set up new input
new_input.original_filename = actual_macro->def_filename;
new_input.line_number = actual_macro->def_line_number;
new_input.source_is_ram = TRUE;
new_input.source = INPUTSRC_RAM;
new_input.state = INPUTSTATE_NORMAL; // FIXME - fix others!
new_input.src.ram_ptr = actual_macro->parameter_list;
// remember old input
@ -304,6 +303,7 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name
// start new section (with new scope)
// FALSE = title mustn't be freed
section_new(&new_section, "Macro", actual_macro->original_name, FALSE);
section_new_cheap_scope(&new_section);
GetByte(); // fetch first byte of parameter list
// assign arguments
if (GotByte != CHAR_EOS) { // any at all?
@ -314,20 +314,21 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name
// In both cases, GlobalDynaBuf may be used.
if (GotByte == REFERENCE_CHAR) {
// assign call-by-reference arg
GetByte(); // skip '~' character
GetByte(); // eat '~'
Input_read_scope_and_keyword(&symbol_scope);
// create new tree node and link existing symbol struct from arg list to it
if ((Tree_hard_scan(&symbol_node, symbols_forest, symbol_scope, TRUE) == FALSE)
&& (pass_count == 0))
&& (FIRST_PASS))
Throw_error("Macro parameter twice.");
symbol_node->body = arg_table[arg_count].symbol;
symbol_node->body = arg_table[arg_count].symbol; // CAUTION, object type may be NULL
} else {
// assign call-by-value arg
Input_read_scope_and_keyword(&symbol_scope);
symbol = symbol_find(symbol_scope, 0);
// FIXME - add a possibility to symbol_find to make it possible to find out
// whether symbol was just created. Then check for the same error message here
// as above ("Macro parameter twice.").
symbol->result = arg_table[arg_count].result;
symbol = symbol_find(symbol_scope);
// FIXME - find out if symbol was just created.
// Then check for the same error message here as above ("Macro parameter twice.").
// TODO - on the other hand, this would rule out globals as args (stupid anyway, but not illegal yet!)
symbol->object = arg_table[arg_count].result; // FIXME - this assignment redefines globals/whatever without throwing errors!
}
++arg_count;
} while (Input_accept_comma());

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.

File diff suppressed because it is too large Load Diff

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
//
// mnemonic definitions
@ -7,26 +7,29 @@
#define mnemo_H
// create dynamic buffer, build keyword trees
extern void Mnemo_init(void);
// check whether mnemonic in GlobalDynaBuf is supported by 6502 cpu.
extern int keyword_is_6502_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by 6510 cpu.
extern int keyword_is_6510_mnemo(int length);
#include "config.h"
// 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).
extern boolean keyword_is_nmos6502_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by C64DTV2 cpu.
extern int keyword_is_c64dtv2_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by 65c02 cpu.
extern int keyword_is_65c02_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by Rockwell 65c02 cpu.
extern int keyword_is_r65c02_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by WDC 65c02 cpu.
extern int keyword_is_w65c02_mnemo(int length);
extern boolean keyword_is_c64dtv2_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by 65C02 cpu.
extern boolean keyword_is_65c02_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by Rockwell 65C02 cpu.
extern boolean keyword_is_r65c02_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by WDC 65C02 cpu.
extern boolean keyword_is_w65c02_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by 65816 cpu.
extern int keyword_is_65816_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by CSG 65ce02 cpu.
extern int keyword_is_65ce02_mnemo(int length);
extern boolean keyword_is_65816_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by CSG 65CE02 cpu.
extern boolean keyword_is_65ce02_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by CSG 4502 cpu.
extern int keyword_is_4502_mnemo(int length);
extern boolean keyword_is_4502_mnemo(int length);
// check whether mnemonic in GlobalDynaBuf is supported by MEGA65 cpu.
extern boolean keyword_is_m65_mnemo(int length);
#endif

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
//
// Output stuff
@ -8,6 +8,7 @@
// 5 Mar 2014 Fixed bug where setting *>0xffff resulted in hangups.
// 19 Nov 2014 Merged Johann Klasek's report listing generator patch
// 22 Sep 2015 Added big-endian output functions
// 20 Apr 2019 Prepared for "make segment overlap warnings into errors" later on
#include "output.h"
#include <stdlib.h>
#include <string.h> // for memset()
@ -23,7 +24,6 @@
// constants
#define OUTBUFFERSIZE 65536
#define NO_SEGMENT_START (-1) // invalid value to signal "not in a segment"
@ -38,19 +38,24 @@ 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
intval_t highest_written; // largest address used
int initvalue_set; // actually bool
boolean initvalue_set;
struct {
intval_t start; // start of current segment (or NO_SEGMENT_START)
intval_t max; // highest address segment may use
int flags; // segment flags ("overlay" and "invisible", see header file)
bits flags; // segment flags ("overlay" and "invisible", see header file)
struct segment list_head; // head element of doubly-linked ring list
} segment;
char xor; // output modifier
};
// for offset assembly:
static struct pseudopc *pseudopc_current_context; // current struct (NULL when not in pseudopc block)
// variables
static struct output default_output;
@ -67,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(s_cbm, OUTPUT_FORMAT_CBM),
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
@ -105,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
}
@ -114,10 +120,15 @@ 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.");
if (pass_count == 0) {
Throw_warning("Segment reached another one, overwriting it.");
// 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)
Throw_error("Segment reached another one, overwriting it.");
else
Throw_warning("Segment reached another one, overwriting it.");
find_segment_max(current_offset + 1); // find new (next) limit
}
}
@ -130,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?
@ -142,7 +155,7 @@ static void real_output(intval_t byte)
// write byte and advance ptrs
if (report->fd)
report_binary(byte & 0xff); // file for reporting, taking also CPU_2add
out->buffer[out->write_idx++] = byte & 0xff;
out->buffer[out->write_idx++] = (byte & 0xff) ^ out->xor;
++CPU_state.add_to_pc;
}
@ -157,20 +170,25 @@ static void no_output(intval_t byte)
}
// call this if really calling Output_byte would be a waste of time
// FIXME - check all users of this, because future changes
// ("several-projects-at-once") may be incompatible with this!
void Output_fake(int size)
// 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)
void output_skip(int size)
{
if (size < 1)
if (size < 1) {
// FIXME - ok for zero, but why is there no error message
// output for negative values?
return;
}
// check whether ptr undefined
if (Output_byte == no_output) {
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?
@ -185,98 +203,10 @@ void Output_fake(int size)
}
// 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);
}
// 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);
}
}
// 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);
}
}
// 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);
}
}
// 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);
}
}
// output 32-bit value (without range check) big-endian
void output_be32(intval_t value)
{
// if ((Value <= 0x7fffffff) && (Value >= -0x80000000)) {
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)) {
Output_byte(value);
Output_byte(value >> 8);
Output_byte(value >> 16);
Output_byte(value >> 24);
// } else {
// Throw_error(exception_number_out_of_range);
// }
}
// fill output buffer with given byte value
static void fill_completely(char value)
{
memset(out->buffer, value, OUTBUFFERSIZE);
memset(out->buffer, value, out->bufsize);
}
@ -294,8 +224,10 @@ int output_initmem(char content)
// init memory
fill_completely(content);
// enforce another pass
if (pass_undefined_count == 0)
pass_undefined_count = 1;
if (pass.undefined_count == 0)
pass.undefined_count = 1;
//if (pass.needvalue_count == 0) FIXME - use? instead or additionally?
// pass.needvalue_count = 1;
// FIXME - enforcing another pass is not needed if there hasn't been any
// output yet. But that's tricky to detect without too much overhead.
// The old solution was to add &&(out->lowest_written < out->highest_written+1) to "if" above
@ -308,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;
@ -325,6 +254,7 @@ int outputfile_prefer_cbm_format(void)
{
if (output_format != OUTPUT_FORMAT_UNSPECIFIED)
return 0;
output_format = OUTPUT_FORMAT_CBM;
return 1;
}
@ -346,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;
@ -377,7 +308,7 @@ void Output_save_file(FILE *fd)
start = out->lowest_written;
amount = out->highest_written - start + 1;
}
if (Process_verbosity)
if (config.process_verbosity)
printf("Saving %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n",
amount, amount, start, start + amount);
// output file header according to file format
@ -432,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;
@ -443,7 +374,11 @@ static void check_segment(intval_t new_pc)
// search ring for matching entry
while (test_segment->start <= new_pc) {
if ((test_segment->start + test_segment->length) > new_pc) {
Throw_warning("Segment starts inside another one, overwriting it.");
// TODO - include overlap size in error message!
if (config.segment_warning_is_error)
Throw_error("Segment starts inside another one, overwriting it.");
else
Throw_warning("Segment starts inside another one, overwriting it.");
return;
}
@ -459,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;
@ -467,21 +402,26 @@ 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;
//vcpu stuff:
CPU_state.pc.flags = 0; // not defined yet
CPU_state.pc.ntype = NUMTYPE_UNDEFINED; // not defined yet
CPU_state.pc.flags = 0;
// FIXME - number type is "undefined", but still the intval 0 below will
// be used to calculate diff when pc is first set.
CPU_state.pc.val.intval = 0; // same as output's write_idx on pass init
CPU_state.add_to_pc = 0; // increase PC by this at end of statement
CPU_state.a_is_long = FALSE; // short accu
CPU_state.xy_are_long = FALSE; // short index regs
// pseudopc stuff:
pseudopc_current_context = NULL;
}
@ -492,7 +432,7 @@ void Output_end_segment(void)
intval_t amount;
// in later passes, ignore completely
if (pass_count)
if (!FIRST_PASS)
return;
// if there is no segment, there is nothing to do
@ -511,26 +451,30 @@ void Output_end_segment(void)
// link to segment list
link_segment(out->segment.start, amount);
// announce
if (Process_verbosity > 1)
if (config.process_verbosity > 1)
// TODO - change output to start, limit, size, name:
// TODO - output hex numbers as %04x? What about limit 0x10000?
printf("Segment size is %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n",
amount, amount, out->segment.start, out->write_idx);
}
// change output pointer and enable output
void Output_start_segment(intval_t address_change, int segment_flags)
// 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
if (pass_count == 0) {
// TODO - remove FIRST_PASS condition
if (FIRST_PASS) {
if (!(segment_flags & SEGMENT_FLAG_OVERLAY))
check_segment(out->segment.start);
find_segment_max(out->segment.start);
@ -538,19 +482,43 @@ void Output_start_segment(intval_t address_change, int segment_flags)
}
char output_get_xor(void)
{
return out->xor;
}
void output_set_xor(char xor)
{
out->xor = xor;
}
// set program counter to defined value (FIXME - allow for undefined!)
// if start address was given on command line, main loop will call this before each pass.
// in addition to that, it will be called on each "* = VALUE".
void vcpu_set_pc(intval_t new_pc, int segment_flags)
// 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;
new_offset = (new_pc - CPU_state.pc.val.intval) & 0xffff;
CPU_state.pc.val.intval = new_pc;
CPU_state.pc.flags |= MVALUE_DEFINED; // FIXME - remove when allowing undefined!
// support stupidly bad, old, ancient, deprecated, obsolete behaviour:
if (pseudopc_current_context != NULL) {
if (config.wanted_version < VER_SHORTER_SETPC_WARNING) {
Throw_warning("Offset assembly still active at end of segment. Switched it off.");
pseudopc_end_all();
} else if (config.wanted_version < VER_DISABLED_OBSOLETE_STUFF) {
Throw_warning("Offset assembly still active at end of segment.");
pseudopc_end_all(); // warning no longer said it
// would switch off, but still did. nevertheless, there
// is something different to older versions: when the
// closing '}' or !realpc is encountered, _really_ weird
// stuff happens! i see no reason to try to mimic that.
}
}
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:
@ -567,7 +535,7 @@ when encountering "!pseudopc VALUE { BLOCK }":
remember difference between current and new value
set PC to new value
after BLOCK, use remembered difference to change PC back
when encountering "* = VALUE":
when encountering "*= VALUE":
parse new value (NEW: might be undefined!)
calculate difference between current PC and new value
set PC to new value
@ -576,14 +544,14 @@ when encountering "* = VALUE":
Problem: always check for "undefined"; there are some problematic combinations.
I need a way to return the size of a generated code block even if PC undefined.
Maybe like this:
* = new_address [, invisible] [, overlay] [, &size_symbol_ref {]
*= new_address [, invisible] [, overlay] [, &size_symbol_ref {]
...code...
[} ; at end of block, size is written to size symbol given above!]
*/
// get program counter
void vcpu_read_pc(struct result *target)
void vcpu_read_pc(struct number *target)
{
*target = CPU_state.pc;
}
@ -599,6 +567,77 @@ 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;
}
// struct to describe a pseudopc context
struct pseudopc {
struct pseudopc *outer; // next layer (to be able to "unpseudopc" labels by more than one level)
intval_t offset; // inner minus outer pc
enum numtype ntype; // type of outer pc (INT/UNDEFINED)
};
// start offset assembly
void pseudopc_start(struct number *new_pc)
{
struct pseudopc *new_context;
new_context = safe_malloc(sizeof(*new_context)); // create new struct (this must never be freed, as it gets linked to labels!)
new_context->outer = pseudopc_current_context; // let it point to previous one
pseudopc_current_context = new_context; // make it the current one
new_context->ntype = CPU_state.pc.ntype;
new_context->offset = new_pc->val.intval - CPU_state.pc.val.intval;
CPU_state.pc.val.intval = new_pc->val.intval;
CPU_state.pc.ntype = NUMTYPE_INT; // FIXME - remove when allowing undefined!
//new: CPU_state.pc.flags = new_pc->flags & (NUMBER_IS_DEFINED | NUMBER_EVER_UNDEFINED);
}
// end offset assembly
void pseudopc_end(void)
{
if (pseudopc_current_context == NULL) {
// trying to end offset assembly though it isn't active:
// in current versions this cannot happen and so must be a bug.
// but in versions older than 0.94.8 this was possible using
// !realpc, and offset assembly got automatically disabled when
// encountering "*=".
// so if wanted version is new enough, choke on bug!
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) & (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
}
}
// this is only for old, deprecated, obsolete, stupid "realpc":
void pseudopc_end_all(void)
{
while (pseudopc_current_context)
pseudopc_end();
}
// un-pseudopc a label value by given number of levels
// returns nonzero on error (if level too high)
int pseudopc_unpseudo(struct number *target, struct pseudopc *context, unsigned int levels)
{
while (levels--) {
//if (target->ntype == NUMTYPE_UNDEFINED)
// return 0; // ok (no sense in trying to unpseudo this, and it might be an unresolved forward ref anyway)
if (context == NULL) {
Throw_error("Un-pseudopc operator '&' has no !pseudopc context.");
return 1; // error
}
// FIXME - in future, check both target and context for NUMTYPE_UNDEFINED!
target->val.intval = (target->val.intval - context->offset) & (out->bufsize - 1); // FIXME - is masking really needed? TODO
context = context->outer;
}
return 0; // ok
}
// return pointer to current "pseudopc" struct (may be NULL!)
// this gets called when parsing label definitions
struct pseudopc *pseudopc_get_context(void)
{
return pseudopc_current_context;
}

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
//
// Output stuff (FIXME - split into outbuf, outfile/format and vcpu parts)
@ -19,13 +19,13 @@
// current CPU state
// FIXME - move vcpu struct definition to .c file and change other .c files' accesses to fn calls
// FIXME - move vcpu struct definition to .c file and change other .c files' accesses to fn calls. then replace "struct number" with minimized version.
struct vcpu {
const struct cpu_type *type; // current CPU type (default 6502) (FIXME - move out of struct again?)
struct result pc; // current program counter (pseudo value)
struct number pc; // current program counter (pseudo value)
int add_to_pc; // add to PC after statement
int a_is_long;
int xy_are_long;
boolean a_is_long;
boolean xy_are_long;
};
@ -35,31 +35,27 @@ extern struct vcpu CPU_state; // current CPU state FIXME - restrict visibility t
// Prototypes
// alloc and init mem buffer (done later)
extern void Output_init(signed long fill_value);
// clear segment list and disable output
//TODO - does this belong to outbuf stuff?
extern void Output_passinit(void);
// call this if really calling Output_byte would be a waste of time
extern void Output_fake(int size);
// outbuf stuff:
// alloc and init mem buffer (done later)
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)
extern void output_skip(int size);
// Send low byte of arg to output buffer and advance pointer
// FIXME - replace by output_sequence(char *src, size_t size)
extern void (*Output_byte)(intval_t);
// Output 8-bit value with range check
extern void output_8(intval_t);
// Output 16-bit value with range check big-endian
extern void output_be16(intval_t);
// Output 16-bit value with range check little-endian
extern void output_le16(intval_t);
// Output 24-bit value with range check big-endian
extern void output_be24(intval_t);
// Output 24-bit value with range check little-endian
extern void output_le24(intval_t);
// Output 32-bit value (without range check) big-endian
extern void output_be32(intval_t);
// Output 32-bit value (without range check) little-endian
extern void output_le32(intval_t);
// define default value for empty memory ("!initmem" pseudo opcode)
// returns zero if ok, nonzero if already set
extern int output_initmem(char content);
// outfile stuff:
// try to set output format held in DynaBuf. Returns zero on success.
extern int outputfile_set_format(void);
extern const char outputfile_formats[]; // string to show if outputfile_set_format() returns nonzero
@ -71,18 +67,34 @@ extern int outputfile_set_filename(void);
// write smallest-possible part of memory buffer to file
extern void Output_save_file(FILE *fd);
// change output pointer and enable output
extern void Output_start_segment(intval_t address_change, int segment_flags);
extern void Output_start_segment(intval_t address_change, bits segment_flags);
// Show start and end of current segment
extern void Output_end_segment(void);
extern char output_get_xor(void);
extern void output_set_xor(char xor);
// set program counter to defined value (TODO - allow undefined!)
extern void vcpu_set_pc(intval_t new_pc, int flags);
extern void vcpu_set_pc(intval_t new_pc, bits flags);
// get program counter
extern void vcpu_read_pc(struct result *target);
extern void vcpu_read_pc(struct number *target);
// get size of current statement (until now) - needed for "!bin" verbose output
extern int vcpu_get_statement_size(void);
// adjust program counter (called at end of each statement)
extern void vcpu_end_statement(void);
struct pseudopc;
// start offset assembly
extern void pseudopc_start(struct number *new_pc);
// end offset assembly
extern void pseudopc_end(void);
// this is only for old, deprecated, obsolete, stupid "realpc":
extern void pseudopc_end_all(void);
// un-pseudopc a label value by given number of levels
// returns nonzero on error (if level too high)
extern int pseudopc_unpseudo(struct number *target, struct pseudopc *context, unsigned int levels);
// return pointer to current "pseudopc" struct (may be NULL!)
// this gets called when parsing label definitions
extern struct pseudopc *pseudopc_get_context(void);
#endif

File diff suppressed because it is too large Load Diff

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
//
// pseudo opcode stuff
@ -7,10 +7,8 @@
#define pseudoopcodes_H
// call when "* = EXPRESSION" is parsed
// 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

@ -1,20 +1,23 @@
// 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
//
// section stuff (move to symbol.h?)
#include "section.h"
#include "config.h"
#include "dynabuf.h"
#include "global.h" // FIXME - remove when no longer needed
#include "global.h"
#include "input.h"
#include "symbol.h"
#include "tree.h"
#define SCOPE_INCREMENT 2 // inc by 2 so locals are even and cheaps are odd
// fake section structure (for error msgs before any real section is in use)
static struct section initial_section = {
0, // scope value
0, // local scope value
1, // cheap scope value
"during", // "type" => normally "zone Title" or
"init", // "title" => "macro test", now "during init"
FALSE, // no, title was not malloc'd
@ -24,21 +27,37 @@ static struct section initial_section = {
// variables
struct section *section_now = &initial_section; // current section
static struct section outer_section; // outermost section struct
static scope_t scope_localcount; // highest scope number yet
static scope_t local_scope_max; // highest scope number yet
static scope_t cheap_scope_max; // highest scope number yet
// write given info into given structure and activate it
void section_new(struct section *section, const char *type, char *title, int allocated)
void section_new(struct section *section, const char *type, char *title, boolean allocated)
{
section->scope = ++scope_localcount;
// new scope for locals
local_scope_max += SCOPE_INCREMENT;
section->local_scope = local_scope_max;
// keep scope for cheap locals
section->cheap_scope = section_now->cheap_scope;
// copy other data
section->type = type;
section->title = title;
section->allocated = allocated;
// activate new section
section_now = section;
//printf("[new zone %d: %s, %s]\n", section->scope, section->type, section->title);
//printf("[new section %d: %s, %s]\n", section->local_scope, section->type, section->title);
}
// change scope of cheap locals in given section
void section_new_cheap_scope(struct section *section)
{
// new scope for cheap locals
cheap_scope_max += SCOPE_INCREMENT;
section->cheap_scope = cheap_scope_max;
}
// Tidy up: If necessary, release section title.
// Warning - the state of the component "Allocd" may have
// changed in the meantime, so don't rely on a local variable.
@ -48,9 +67,13 @@ void section_finalize(struct section *section)
free(section->title);
}
// setup outermost section
void section_passinit(void)
{
scope_localcount = SCOPE_GLOBAL; // will be incremented by next line
section_new(&outer_section, s_Zone, s_untitled, FALSE);
//printf("[old maxima: locals=%d, cheap=%d]\n", local_scope_max, cheap_scope_max);
local_scope_max = 0; // will be incremented by 2 by next line
section_new(&outer_section, "Zone", s_untitled, FALSE);
cheap_scope_max = -1; // will be incremented by 2 by next line
section_new_cheap_scope(&outer_section);
}

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
//
// section stuff
@ -12,10 +12,11 @@
// "section" structure type definition
struct section {
scope_t scope; // section's scope
scope_t local_scope; // section's scope for local symbols
scope_t cheap_scope; // section's scope for cheap locals
const char *type; // "Zone", "Subzone" or "Macro"
char *title; // zone title, subzone title or macro title
int allocated; // whether title was malloc()'d
boolean allocated; // whether title was malloc()'d
};
@ -24,7 +25,9 @@ extern struct section *section_now;
// write given info into given structure and activate it
extern void section_new(struct section *section, const char *type, char *title, int allocated);
extern void section_new(struct section *section, const char *type, char *title, boolean allocated);
// change scope of cheap locals in given section
extern void section_new_cheap_scope(struct section *section);
// setup outermost section
extern void section_passinit(void);
// tidy up: if necessary, release section title.

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
//
// symbol stuff
@ -12,7 +12,7 @@
#include "acme.h"
#include "alu.h"
#include "dynabuf.h"
#include "global.h" // FIXME - remove when no longer needed
#include "global.h"
#include "input.h"
#include "output.h"
#include "platform.h"
@ -30,34 +30,40 @@ static void dump_one_symbol(struct rwnode *node, FILE *fd)
{
struct symbol *symbol = node->body;
// if symbol is neither int nor float, skip
if (symbol->object.type != &type_number)
return;
// CAUTION: if more types are added, check for NULL before using type pointer!
// output name
if (warn_on_type_mismatch
&& symbol->result.addr_refs == 1)
if (config.warn_on_type_mismatch
&& symbol->object.u.number.addr_refs == 1)
fprintf(fd, "!addr");
fprintf(fd, "\t%s", node->id_string);
switch (symbol->result.flags & MVALUE_FORCEBITS) {
case MVALUE_FORCE16:
switch (symbol->object.u.number.flags & NUMBER_FORCEBITS) {
case NUMBER_FORCES_16:
fprintf(fd, "+2\t= ");
break;
case MVALUE_FORCE16 | MVALUE_FORCE24:
case NUMBER_FORCES_16 | NUMBER_FORCES_24:
/*FALLTHROUGH*/
case MVALUE_FORCE24:
case NUMBER_FORCES_24:
fprintf(fd, "+3\t= ");
break;
default:
fprintf(fd, "\t= ");
}
if (symbol->result.flags & MVALUE_DEFINED) {
if (symbol->result.flags & MVALUE_IS_FP)
fprintf(fd, "%.30f", symbol->result.val.fpval); //FIXME %g
else
fprintf(fd, "$%x", (unsigned) symbol->result.val.intval);
} else {
fprintf(fd, " ?");
}
if (symbol->result.flags & MVALUE_UNSURE)
fprintf(fd, "\t; ?");
if (symbol->usage == 0)
if (symbol->object.u.number.ntype == NUMTYPE_UNDEFINED)
fprintf(fd, " ?"); // TODO - maybe write "UNDEFINED" instead? then the file could at least be parsed without errors
else if (symbol->object.u.number.ntype == NUMTYPE_INT)
fprintf(fd, "$%x", (unsigned) symbol->object.u.number.val.intval);
else if (symbol->object.u.number.ntype == NUMTYPE_FLOAT)
fprintf(fd, "%.30f", symbol->object.u.number.val.fpval); //FIXME %g
else
Bug_found("IllegalNumberType4", symbol->object.u.number.ntype);
if (symbol->object.u.number.flags & NUMBER_EVER_UNDEFINED)
fprintf(fd, "\t; ?"); // TODO - write "forward" instead?
if (!symbol->has_been_read)
fprintf(fd, "\t; unused");
fprintf(fd, "\n");
}
@ -69,160 +75,141 @@ static void dump_vice_address(struct rwnode *node, FILE *fd)
struct symbol *symbol = node->body;
// dump address symbols even if they are not used
if ((symbol->result.flags & MVALUE_DEFINED)
&& !(symbol->result.flags & MVALUE_IS_FP)
&& (symbol->result.addr_refs == 1))
fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->result.val.intval, node->id_string);
if ((symbol->object.type == &type_number)
&& (symbol->object.u.number.ntype == NUMTYPE_INT)
&& (symbol->object.u.number.addr_refs == 1))
fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->object.u.number.val.intval, node->id_string);
}
static void dump_vice_usednonaddress(struct rwnode *node, FILE *fd)
{
struct symbol *symbol = node->body;
// dump non-addresses that are used
if (symbol->usage
&& (symbol->result.flags & MVALUE_DEFINED)
&& !(symbol->result.flags & MVALUE_IS_FP)
&& (symbol->result.addr_refs != 1))
fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->result.val.intval, node->id_string);
if (symbol->has_been_read
&& (symbol->object.type == &type_number)
&& (symbol->object.u.number.ntype == NUMTYPE_INT)
&& (symbol->object.u.number.addr_refs != 1))
fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->object.u.number.val.intval, node->id_string);
}
static void dump_vice_unusednonaddress(struct rwnode *node, FILE *fd)
{
struct symbol *symbol = node->body;
// dump non-addresses that are unused
if (!symbol->usage
&& (symbol->result.flags & MVALUE_DEFINED)
&& !(symbol->result.flags & MVALUE_IS_FP)
&& (symbol->result.addr_refs != 1))
fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->result.val.intval, node->id_string);
if (!symbol->has_been_read
&& (symbol->object.type == &type_number)
&& (symbol->object.u.number.ntype == NUMTYPE_INT)
&& (symbol->object.u.number.addr_refs != 1))
fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->object.u.number.val.intval, node->id_string);
}
// search for symbol. create if nonexistant. if created, give it flags "flags".
// search for symbol. if it does not exist, create with NULL object (CAUTION!).
// the symbol name must be held in GlobalDynaBuf.
struct symbol *symbol_find(scope_t scope, int flags)
struct symbol *symbol_find(scope_t scope)
{
struct rwnode *node;
struct symbol *symbol;
int node_created,
force_bits = flags & MVALUE_FORCEBITS;
boolean node_created;
node_created = Tree_hard_scan(&node, symbols_forest, scope, TRUE);
// if node has just been created, create symbol as well
if (node_created) {
// create new symbol structure
symbol = safe_malloc(sizeof(*symbol));
// finish empty symbol item
symbol->result.flags = flags;
symbol->result.addr_refs = 0;
if (flags & MVALUE_IS_FP)
symbol->result.val.fpval = 0;
else
symbol->result.val.intval = 0;
symbol->usage = 0; // usage count
symbol->pass = pass_count;
node->body = symbol;
// finish empty symbol item
symbol->object.type = NULL; // no object yet (CAUTION!)
symbol->pass = pass.number;
symbol->has_been_read = FALSE;
symbol->has_been_reported = FALSE;
symbol->pseudopc = NULL;
} else {
symbol = node->body;
}
// make sure the force bits don't clash
if ((node_created == FALSE) && force_bits)
if ((symbol->result.flags & MVALUE_FORCEBITS) != force_bits)
Throw_error("Too late for postfix.");
return symbol;
return symbol; // now symbol->object.type can be tested to see if this was freshly created.
// CAUTION: this only works if caller always sets a type pointer after checking! if NULL is kept, the struct still looks new later on...
}
// assign value to symbol. the function acts upon the symbol's flag bits and
// assign object to symbol. the function acts upon the symbol's flag bits and
// produces an error if needed.
void symbol_set_value(struct symbol *symbol, struct result *new_value, int change_allowed)
// using "power" bits, caller can state which changes are ok.
// called by:
// implicit label definitions (including anons, backward anons have POWER_CHANGE_VALUE)
// explicit symbol assignments
// explicit symbol assignments via "!set" (has all powers)
// loop counter var init via "!for" (has POWER_CHANGE_VALUE and POWER_CHANGE_NUMTYPE)
// CAUTION: actual incrementing of counter is then done directly without calls here!
void symbol_set_object(struct symbol *symbol, struct object *new_value, bits powers)
{
int oldflags = symbol->result.flags;
// if symbol has no object assigned to it yet, fine:
if (symbol->object.type == NULL) {
symbol->object = *new_value; // copy whole struct including type
// as long as the symbol has not been read, the force bits can
// be changed, so the caller still has a chance to do that.
return;
}
// value stuff
if ((oldflags & MVALUE_DEFINED) && (change_allowed == FALSE)) {
// symbol is already defined, so compare new and old values
// if different type OR same type but different value, complain
if (((oldflags ^ new_value->flags) & MVALUE_IS_FP)
|| ((oldflags & MVALUE_IS_FP) ? (symbol->result.val.fpval != new_value->val.fpval) : (symbol->result.val.intval != new_value->val.intval)))
Throw_error("Symbol already defined.");
} else {
// symbol is not defined yet OR redefinitions are allowed
symbol->result = *new_value;
// now we know symbol already has a type
// compare types
// if too different, needs power (or complains)
if (symbol->object.type != new_value->type) {
if (!(powers & POWER_CHANGE_OBJTYPE))
Throw_error(exception_symbol_defined);
// CAUTION: if above line throws error, we still go ahead and change type!
// this is to keep "!for" working, where the counter var is accessed.
symbol->object = *new_value; // copy whole struct including type
// clear flag so caller can adjust force bits:
symbol->has_been_read = FALSE; // it's basically a new symbol now
return;
}
// flags stuff
// Ensure that "unsure" symbols without "isByte" state don't get that
if ((oldflags & (MVALUE_UNSURE | MVALUE_ISBYTE)) == MVALUE_UNSURE)
new_value->flags &= ~MVALUE_ISBYTE;
if (change_allowed) {
oldflags = (oldflags & MVALUE_UNSURE) | new_value->flags;
} else {
if ((oldflags & MVALUE_FORCEBITS) == 0)
if ((oldflags & (MVALUE_UNSURE | MVALUE_DEFINED)) == 0)
oldflags |= new_value->flags & MVALUE_FORCEBITS;
oldflags |= new_value->flags & ~MVALUE_FORCEBITS;
}
symbol->result.flags = oldflags;
// now we know symbol and new value have compatible types, so call handler:
symbol->object.type->assign(&symbol->object, new_value, !!(powers & POWER_CHANGE_VALUE));
}
// parse label definition (can be either global or local).
// name must be held in GlobalDynaBuf.
void symbol_set_label(scope_t scope, int stat_flags, int force_bit, int change_allowed)
// set force bit of symbol. trying to change to a different one will raise error.
void symbol_set_force_bit(struct symbol *symbol, bits force_bit)
{
struct result pc,
result;
struct symbol *symbol;
if (!force_bit)
Bug_found("ForceBitZero", 0);
if (symbol->object.type == NULL)
Bug_found("NullTypeObject", 0);
symbol = symbol_find(scope, force_bit);
// label definition
if ((stat_flags & SF_FOUND_BLANK) && warn_on_indented_labels)
Throw_first_pass_warning("Label name not in leftmost column.");
vcpu_read_pc(&pc);
result.flags = pc.flags & MVALUE_DEFINED;
result.val.intval = pc.val.intval;
result.addr_refs = pc.addr_refs;
symbol_set_value(symbol, &result, change_allowed);
}
// parse symbol definition (can be either global or local, may turn out to be a label).
// name must be held in GlobalDynaBuf.
void symbol_parse_definition(scope_t scope, int stat_flags)
{
struct result result;
struct symbol *symbol;
int force_bit = Input_get_force_bit(); // skips spaces after
// FIXME - force bit is allowed for label definitions?!
if (GotByte == '=') {
// explicit symbol definition (symbol = <something>)
symbol = symbol_find(scope, force_bit);
// symbol = parsed value
GetByte(); // skip '='
ALU_any_result(&result);
// if wanted, mark as address reference
if (typesystem_says_address())
result.addr_refs = 1;
symbol_set_value(symbol, &result, FALSE);
Input_ensure_EOS();
} else {
symbol_set_label(scope, stat_flags, force_bit, FALSE);
if (symbol->object.type != &type_number) {
Throw_error("Force bits can only be given to numbers.");
return;
}
// if change is ok, change
if (!symbol->has_been_read) {
symbol->object.u.number.flags &= ~NUMBER_FORCEBITS;
symbol->object.u.number.flags |= force_bit;
return; // and be done with it
}
// it's too late to change, so check if the wanted bit is actually different
if ((symbol->object.u.number.flags & NUMBER_FORCEBITS) != force_bit)
Throw_error("Too late for postfix.");
}
// set global symbol to value, no questions asked (for "-D" switch)
// set global symbol to integer value, no questions asked (for "-D" switch)
// Name must be held in GlobalDynaBuf.
void symbol_define(intval_t value)
{
struct result result;
struct object result;
struct symbol *symbol;
result.flags = MVALUE_GIVEN;
result.val.intval = value;
symbol = symbol_find(SCOPE_GLOBAL, 0);
symbol_set_value(symbol, &result, TRUE);
result.type = &type_number;
result.u.number.ntype = NUMTYPE_INT;
result.u.number.flags = 0;
result.u.number.val.intval = value;
symbol = symbol_find(SCOPE_GLOBAL);
symbol->object = result;
}
@ -245,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!
}
@ -252,20 +240,37 @@ void symbols_vicelabels(FILE *fd)
// references the *next* anonymous forward label definition. The tricky bit is,
// each name length would need its own counter. But hey, ACME's real quick in
// finding symbols, so I'll just abuse the symbol system to store those counters.
void symbol_fix_forward_anon_name(int increment)
// example:
// forward anon name is "+++"
// we look up that symbol's value in the current local scope -> $12
// we attach hex digits to name -> "+++21"
// that's the name of the symbol that _actually_ contains the address
// caller sets "increment" to TRUE for writing, FALSE for reading
void symbol_fix_forward_anon_name(boolean increment)
{
struct symbol *counter_symbol;
unsigned long number;
// terminate name, find "counter" symbol and read value
DynaBuf_append(GlobalDynaBuf, '\0');
counter_symbol = symbol_find(section_now->scope, 0);
// make sure it gets reset to zero in each new pass
if (counter_symbol->pass != pass_count) {
counter_symbol->pass = pass_count;
counter_symbol->result.val.intval = 0;
counter_symbol = symbol_find(section_now->local_scope);
if (counter_symbol->object.type == NULL) {
// finish freshly created symbol item
counter_symbol->object.type = &type_number;
counter_symbol->object.u.number.ntype = NUMTYPE_INT;
counter_symbol->object.u.number.flags = 0;
counter_symbol->object.u.number.addr_refs = 0;
counter_symbol->object.u.number.val.intval = 0;
} else if (counter_symbol->object.type != &type_number) {
// sanity check: it must be a number!
Bug_found("ForwardAnonCounterNotInt", 0);
}
number = (unsigned long) counter_symbol->result.val.intval;
// make sure it gets reset to zero in each new pass
if (counter_symbol->pass != pass.number) {
counter_symbol->pass = pass.number;
counter_symbol->object.u.number.val.intval = 0;
}
number = (unsigned long) counter_symbol->object.u.number.val.intval;
// now append to the name to make it unique
GlobalDynaBuf->size--; // forget terminator, we want to append
do {
@ -274,5 +279,5 @@ void symbol_fix_forward_anon_name(int increment)
} while (number);
DynaBuf_append(GlobalDynaBuf, '\0');
if (increment)
counter_symbol->result.val.intval++;
counter_symbol->object.u.number.val.intval++;
}

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
//
// symbol stuff
@ -12,16 +12,16 @@
struct symbol {
struct result result; // expression flags and value
int usage; // usage count
struct object object; // number/list/string
int pass; // pass of creation (for anon counters)
// add flag to indicate "has already been reported as undefined"
boolean has_been_read; // to find out if actually used
boolean has_been_reported; // indicates "has been reported as undefined"
struct pseudopc *pseudopc; // NULL when defined outside of !pseudopc block
// add file ref + line num of last definition
};
// Constants
// TODO: add cheap locals (so there's SCOPE_GLOBAL, scope_zone and scope_cheap)
#define SCOPE_GLOBAL 0 // number of "global zone"
@ -29,17 +29,18 @@ struct symbol {
extern struct rwnode *symbols_forest[]; // trees (because of 8-bit hash)
// function acts upon the symbol's flag bits and produces an error if needed.
extern void symbol_set_value(struct symbol *symbol, struct result *new_value, int change_allowed);
// parse label definition (can be either global or local).
// name must be held in GlobalDynaBuf.
extern void symbol_set_label(scope_t scope, int stat_flags, int force_bit, int change_allowed);
// parse symbol definition (can be either global or local, may turn out to be a label).
// name must be held in GlobalDynaBuf.
extern void symbol_parse_definition(scope_t scope, int stat_flags);
// search for symbol. create if nonexistant. if created, assign flags.
// name must be held in GlobalDynaBuf.
extern struct symbol *symbol_find(scope_t, int flags);
// search for symbol. if it does not exist, create with NULL type object (CAUTION!).
// the symbol name must be held in GlobalDynaBuf.
extern struct symbol *symbol_find(scope_t scope);
// assign object to symbol. function acts upon the symbol's flag bits and
// produces an error if needed.
// using "power" bits, caller can state which changes are ok.
#define POWER_NONE 0
#define POWER_CHANGE_VALUE (1u << 0) // e.g. change 3 to 5 or 2.71
#define POWER_CHANGE_OBJTYPE (1u << 1) // e.g. change 3 to "somestring"
extern void symbol_set_object(struct symbol *symbol, struct object *new_obj, bits powers);
// set force bit of symbol. trying to change to a different one will raise error.
extern void symbol_set_force_bit(struct symbol *symbol, bits force_bit);
// set global symbol to value, no questions asked (for "-D" switch)
// name must be held in GlobalDynaBuf.
extern void symbol_define(intval_t value);
@ -49,7 +50,7 @@ extern void symbols_list(FILE *fd);
extern void symbols_vicelabels(FILE *fd);
// fix name of anonymous forward label (held in GlobalDynaBuf, NOT TERMINATED!)
// so it references the *next* anonymous forward label definition.
extern void symbol_fix_forward_anon_name(int increment);
extern void symbol_fix_forward_anon_name(boolean increment);
#endif

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
//
// tree stuff
@ -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) {
@ -110,7 +121,7 @@ int Tree_easy_scan(struct ronode *tree, void **node_body, struct dynabuf *dyna_b
// a new tree item, link to tree, fill with data and store its pointer. If the
// "create" flag is zero, store NULL as result.
// Returns whether item was created.
int Tree_hard_scan(struct rwnode **result, struct rwnode **forest, int id_number, int create)
int Tree_hard_scan(struct rwnode **result, struct rwnode **forest, int id_number, boolean create)
{
struct ronode wanted; // temporary storage
struct rwnode **current_node;
@ -158,7 +169,7 @@ int Tree_hard_scan(struct rwnode **result, struct rwnode **forest, int id_number
current_node = &((*current_node)->less_than_or_equal);
}
// node wasn't found. Check whether to create it
if (create == FALSE) {
if (!create) {
*result = NULL; // indicate failure
return FALSE; // return FALSE because node was not created
}
@ -178,7 +189,7 @@ int Tree_hard_scan(struct rwnode **result, struct rwnode **forest, int id_number
// Call given function for each object of matching type in the given tree.
// Calls itself recursively.
void dump_tree(struct rwnode *node, int id_number, void (*fn)(struct rwnode *, FILE *), FILE *env)
static void dump_tree(struct rwnode *node, int id_number, void (*fn)(struct rwnode *, FILE *), FILE *env)
{
if (node->id_number == id_number)

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
//
// tree stuff
@ -8,12 +8,13 @@
#include <stdio.h> // for FILE
#include "config.h"
// 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
@ -35,14 +36,12 @@ struct rwnode {
hash_t hash_value;
char *id_string; // name, zero-terminated
void *body; // macro/symbol body
unsigned int id_number; // scope number
int id_number; // scope number
};
// 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;
@ -50,10 +49,10 @@ extern int Tree_easy_scan(struct ronode *tree, void **node_body, struct dynabuf
// Search for a "RAM tree" item. Save pointer to found tree item in given
// location. If no matching item is found, check the "create" flag: If set,
// create new tree item, link to tree, fill with data and store its pointer.
// If "create" is zero, store NULL. Returns whether item was created.
extern int Tree_hard_scan(struct rwnode **result, struct rwnode **forest, int id_number, int create);
// If "create" is FALSE, store NULL. Returns whether item was created.
extern int Tree_hard_scan(struct rwnode **result, struct rwnode **forest, int id_number, boolean create);
// Calls given function for each node of each tree of given forest.
extern void Tree_dump_forest(struct rwnode **, int, void (*)(struct rwnode *, FILE *), FILE *);
extern void Tree_dump_forest(struct rwnode **, int id_number, void (*)(struct rwnode *, FILE *), FILE *);
#endif

Some files were not shown because too many files have changed in this diff Show More