mirror of
https://github.com/uffejakobsen/acme.git
synced 2024-06-01 13:41:29 +00:00
Compare commits
210 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a5bc7a48bd | ||
|
4901f44fdd | ||
|
d59b45036d | ||
|
88cc8cd886 | ||
|
5b1fabc1f5 | ||
|
beb1e178cd | ||
|
2be25080aa | ||
|
aa8d766e6c | ||
|
b2b14cb176 | ||
|
b03b217979 | ||
|
3db33bafb5 | ||
|
26168e6752 | ||
|
2acece9c60 | ||
|
6dd15f7116 | ||
|
f87ddbb5e6 | ||
|
32d59eafa3 | ||
|
a3d36ca156 | ||
|
78390cb632 | ||
|
465da8c139 | ||
|
62dd48ab9f | ||
|
b8f9bb9d36 | ||
|
ca08a1d150 | ||
|
eb138ae785 | ||
|
70b9ee222d | ||
|
a7dd713d93 | ||
|
a534d9c28a | ||
|
94f36db2e5 | ||
|
21cc635bc8 | ||
|
ecca1552d0 | ||
|
d0c824c60a | ||
|
d2683cc64d | ||
|
ca6b6d8771 | ||
|
7038cecfec | ||
|
8f432f6aa7 | ||
|
9d637eee25 | ||
|
c85081cbe4 | ||
|
296ecefa6c | ||
|
a5851cd51a | ||
|
6c869568cd | ||
|
aa51fde056 | ||
|
9f5ac5b212 | ||
|
1441da12ac | ||
|
cf167a34e4 | ||
|
be72f71faa | ||
|
89344d34ee | ||
|
1261960cad | ||
|
7f736ceccb | ||
|
3039db71e6 | ||
|
b8679e7f06 | ||
|
ceabdfb4a0 | ||
|
85f0c32ff4 | ||
|
5b37c4d24e | ||
|
beb875ff2b | ||
|
562ce98f75 | ||
|
fc913eefb6 | ||
|
28e196caab | ||
|
c3e651f4ca | ||
|
eca73fb335 | ||
|
64aa52da35 | ||
|
326f36fd4f | ||
|
c2978f7e15 | ||
|
f9a2f5f698 | ||
|
929fefe0e6 | ||
|
b32320d5f3 | ||
|
430b225208 | ||
|
f64780a3bd | ||
|
2671eef384 | ||
|
1199c75025 | ||
|
4643e841f9 | ||
|
395dcf55f3 | ||
|
8bf6bcd6eb | ||
|
ec2b7515ca | ||
|
7c82984075 | ||
|
da12925408 | ||
|
0588f0fffe | ||
|
1f74a6b8fd | ||
|
a165279e88 | ||
|
93cea56d88 | ||
|
b17203faa7 | ||
|
77e945ce88 | ||
|
02a35ac468 | ||
|
61d5144faa | ||
|
00d0462f74 | ||
|
72fc28e84c | ||
|
9db5ad6fdb | ||
|
7a0f9f9528 | ||
|
5ea2a03174 | ||
|
f164b737ad | ||
|
8c751f3552 | ||
|
8a3bdb265f | ||
|
8e4857de4c | ||
|
c03d1145f6 | ||
|
64a29e4504 | ||
|
47b1fab4fe | ||
|
9bbac556d3 | ||
|
af4a918f18 | ||
|
5bcb80ac47 | ||
|
8169cf6e06 | ||
|
8c8f425559 | ||
|
2ad075911b | ||
|
dddf3f3d10 | ||
|
636080ce25 | ||
|
3c9f21cebd | ||
|
a6eae58032 | ||
|
78e7c32507 | ||
|
bc0cd5b8ea | ||
|
98ae73381d | ||
|
64a4b336b0 | ||
|
d407faab1c | ||
|
486febcef4 | ||
|
0173eaf777 | ||
|
dfca72688b | ||
|
8eddb4f4bc | ||
|
c1d8f90fae | ||
|
f5e7f23311 | ||
|
cd09855098 | ||
|
ba168a3ea5 | ||
|
916bf9cbc8 | ||
|
5b0989ac31 | ||
|
768c80219b | ||
|
101c04e413 | ||
|
beaf86da5b | ||
|
3448cda3df | ||
|
2fbbc0324e | ||
|
c64ffcfced | ||
|
688c00f31b | ||
|
4eb3ffa149 | ||
|
c07f373b53 | ||
|
665c579470 | ||
|
5ba17ccfc4 | ||
|
4e0d82ac69 | ||
|
ccaf576e8a | ||
|
ff11dec18c | ||
|
7c732fca59 | ||
|
c055688355 | ||
|
a7ec38bff5 | ||
|
fd2ac55392 | ||
|
365306428b | ||
|
40afd3311a | ||
|
3300c9d468 | ||
|
dac11ba8c7 | ||
|
16fb63deda | ||
|
20e04e22bc | ||
|
4c938480fc | ||
|
abdea30e33 | ||
|
e908284773 | ||
|
f7c52d747c | ||
|
75bd395f2f | ||
|
440dc697ad | ||
|
4cad44f3ec | ||
|
2b1c9d06bd | ||
|
55f303c05e | ||
|
c1f62fdef5 | ||
|
4b81e40c63 | ||
|
38952534f4 | ||
|
7d4200faa4 | ||
|
60603c7350 | ||
|
a4943e1f40 | ||
|
bc0efebb3e | ||
|
2c104118eb | ||
|
5c459cad56 | ||
|
4565070849 | ||
|
ef3cbbe340 | ||
|
d1ac849272 | ||
|
54defa1add | ||
|
e3e68af762 | ||
|
7286e00855 | ||
|
bf074b830d | ||
|
669f95c238 | ||
|
0a4c13bb2e | ||
|
c4a88fa738 | ||
|
68b4409b1c | ||
|
41f9534b99 | ||
|
a4afd81f42 | ||
|
c6f443d581 | ||
|
2ad798bef2 | ||
|
63d26a9c82 | ||
|
f22907849c | ||
|
06e2612a5b | ||
|
07a7a00b1a | ||
|
c68cefcab0 | ||
|
a57a7b6c68 | ||
|
81f2d3f683 | ||
|
8fc1d4c738 | ||
|
e1744c0008 | ||
|
238eb5d626 | ||
|
cefa89c138 | ||
|
c4487b9239 | ||
|
c262cbe7b4 | ||
|
6604877b54 | ||
|
aa611df8b7 | ||
|
75d52177cc | ||
|
99d866e2e8 | ||
|
fb81d868d6 | ||
|
b31e6c4e70 | ||
|
6ff2e95da8 | ||
|
a123239d2d | ||
|
b3d818da39 | ||
|
bc68e36127 | ||
|
9628f69f4e | ||
|
f23a38de8c | ||
|
3867147615 | ||
|
7cb100c480 | ||
|
e1683b1e28 | ||
|
47f09e9804 | ||
|
a6daa92e63 | ||
|
071778e2c1 | ||
|
a5e984f158 | ||
|
53a8e9ab79 | ||
|
bb484d683e |
45
ACME_Lib/6502/split.a
Normal file
45
ACME_Lib/6502/split.a
Normal 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
|
|
@ -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
|
||||
+
|
||||
}
|
||||
|
|
|
@ -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
6
ACME_Lib/cbm/264/basic.a
Normal 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>
|
46
ACME_Lib/cbm/264/petscii.a
Normal file
46
ACME_Lib/cbm/264/petscii.a
Normal 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
215
ACME_Lib/cbm/264/ted.a
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
109
ACME_Lib/cbm/cia.a
Normal 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
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
20
ACME_Lib/cbm/msbstring.a
Normal 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
94
ACME_Lib/cbm/multicolor.a
Normal 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
13
ACME_Lib/m65/std.a
Normal 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
12
README.md
Normal 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
|
|
@ -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":
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
59
contrib/toacme/src/Makefile.mingw
Normal file
59
contrib/toacme/src/Makefile.mingw
Normal 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
|
|
@ -78,6 +78,7 @@ const char MnemonicSLO[] = " SLO",
|
|||
MnemonicSBX[] = " SBX",
|
||||
MnemonicDOP[] = " DOP",
|
||||
MnemonicTOP[] = " TOP",
|
||||
MnemonicSHX[] = " SHX",
|
||||
MnemonicJAM[] = " JAM";
|
||||
|
||||
// mnemonics of 65c02 instructions
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
BIN
contrib/toacme/src/win/logo.ico
Normal file
BIN
contrib/toacme/src/win/logo.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 119 KiB |
58
contrib/toacme/src/win/setRelease.sh
Normal file
58
contrib/toacme/src/win/setRelease.sh
Normal 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
|
|
@ -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.
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
252
docs/AllPOs.txt
252
docs/AllPOs.txt
|
@ -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.
|
||||
|
|
120
docs/Changes.txt
120
docs/Changes.txt
|
@ -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.
|
||||
|
|
344
docs/Errors.txt
344
docs/Errors.txt
|
@ -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.
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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. :)
|
||||
|
|
245
docs/Upgrade.txt
245
docs/Upgrade.txt
|
@ -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...
|
||||
|
|
|
@ -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.
|
98
docs/cputypes/cpu 4502.txt
Normal file
98
docs/cputypes/cpu 4502.txt
Normal 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.
|
84
docs/cputypes/cpu 6502.txt
Normal file
84
docs/cputypes/cpu 6502.txt
Normal 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
164
docs/cputypes/cpu 65816.txt
Normal 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
130
docs/cputypes/cpu 65c02.txt
Normal 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
|
162
docs/cputypes/cpu 65ce02.txt
Normal file
162
docs/cputypes/cpu 65ce02.txt
Normal 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
120
docs/cputypes/cpu m65.txt
Normal 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!
|
86
docs/cputypes/cpu nmos6502.txt
Normal file
86
docs/cputypes/cpu nmos6502.txt
Normal 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
|
94
docs/cputypes/cpu r65c02.txt
Normal file
94
docs/cputypes/cpu r65c02.txt
Normal 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.
|
90
docs/cputypes/cpu w65c02.txt
Normal file
90
docs/cputypes/cpu w65c02.txt
Normal 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
BIN
examples/trigono.exp2
Normal file
Binary file not shown.
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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/"
|
||||
|
|
19
src/_dos.c
19
src/_dos.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
11
src/_dos.h
11
src/_dos.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
284
src/acme.c
284
src/acme.c
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
103
src/alu.h
103
src/alu.h
|
@ -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
|
||||
|
|
60
src/config.h
60
src/config.h
|
@ -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
|
||||
|
|
45
src/cpu.c
45
src/cpu.c
|
@ -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
|
||||
}
|
||||
|
|
12
src/cpu.h
12
src/cpu.h
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
192
src/flow.c
192
src/flow.c
|
@ -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);
|
||||
|
|
33
src/flow.h
33
src/flow.h
|
@ -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
|
||||
|
|
410
src/global.c
410
src/global.c
|
@ -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);
|
||||
}
|
||||
|
|
137
src/global.h
137
src/global.h
|
@ -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
|
||||
|
|
419
src/input.c
419
src/input.c
|
@ -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;
|
||||
}
|
||||
|
|
72
src/input.h
72
src/input.h
|
@ -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
|
||||
|
|
79
src/macro.c
79
src/macro.c
|
@ -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());
|
||||
|
|
|
@ -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.
|
||||
|
|
920
src/mnemo.c
920
src/mnemo.c
File diff suppressed because it is too large
Load Diff
39
src/mnemo.h
39
src/mnemo.h
|
@ -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
|
||||
|
|
321
src/output.c
321
src/output.c
|
@ -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;
|
||||
}
|
||||
|
|
64
src/output.h
64
src/output.h
|
@ -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
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
267
src/symbol.c
267
src/symbol.c
|
@ -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++;
|
||||
}
|
||||
|
|
35
src/symbol.h
35
src/symbol.h
|
@ -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
|
||||
|
|
29
src/tree.c
29
src/tree.c
|
@ -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)
|
||||
|
|
17
src/tree.h
17
src/tree.h
|
@ -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
Loading…
Reference in New Issue
Block a user