-
Notifications
You must be signed in to change notification settings - Fork 69
/
sonic3k.macros.asm
323 lines (276 loc) · 9.22 KB
/
sonic3k.macros.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
; simplifying macros and functions
; nameless temporary symbols should NOT be used inside macros because they can interfere with the surrounding code
; normal labels should be used instead (which automatically become local to the macro)
; sign-extends a 32-bit integer to 64-bit
; all RAM addresses are run through this function to allow them to work in both 16-bit and 32-bit addressing modes
ramaddr function x,(-(x&$80000000)<<1)|x
; makes a VDP command
vdpComm function addr,type,rwd,(((type&rwd)&3)<<30)|((addr&$3FFF)<<16)|(((type&rwd)&$FC)<<2)|((addr&$C000)>>14)
; values for the type argument
VRAM = %100001
CRAM = %101011
VSRAM = %100101
; values for the rwd argument
READ = %001100
WRITE = %000111
DMA = %100111
; tells the VDP to copy a region of 68k memory to VRAM or CRAM or VSRAM
dma68kToVDP macro source,dest,length,type
lea (VDP_control_port).l,a5
move.l #(($9400|((((length)>>1)&$FF00)>>8))<<16)|($9300|(((length)>>1)&$FF)),(a5)
move.l #(($9600|((((source)>>1)&$FF00)>>8))<<16)|($9500|(((source)>>1)&$FF)),(a5)
move.w #$9700|(((((source)>>1)&$FF0000)>>16)&$7F),(a5)
move.w #(vdpComm(dest,type,DMA)>>16)&$FFFF,(a5)
move.w #vdpComm(dest,type,DMA)&$FFFF,(DMA_trigger_word).w
move.w (DMA_trigger_word).w,(a5)
; From ' § 7 DMA TRANSFER' of https://emu-docs.org/Genesis/sega2f.htm:
;
; "In the case of ROM to VRAM transfers,
; a hardware feature causes occasional failure of DMA unless the
; following two conditions are observed:
;
; --The destination address write (to address $C00004) must be a word
; write.
;
; --The final write must use the work RAM.
; There are two ways to accomplish this, by copying the DMA program
; into RAM or by doing a final "move.w ram address $C00004""
endm
; tells the VDP to fill a region of VRAM with a certain byte
dmaFillVRAM macro byte,addr,length
lea (VDP_control_port).l,a5
move.w #$8F01,(a5) ; VRAM pointer increment: $0001
move.l #(($9400|((((length)-1)&$FF00)>>8))<<16)|($9300|(((length)-1)&$FF)),(a5) ; DMA length ...
move.w #$9780,(a5) ; VRAM fill
move.l #vdpComm(addr,VRAM,DMA),(a5) ; Start at ...
move.w #(byte)|((byte)<<8),(VDP_data_port).l ; Fill with byte
.loop: move.w (a5),d1
btst #1,d1
bne.s .loop ; busy loop until the VDP is finished filling...
move.w #$8F02,(a5) ; VRAM pointer increment: $0002
endm
; calculates initial loop counter value for a dbf loop
; that writes n bytes total at 4 bytes per iteration
bytesToLcnt function n,n>>2-1
; calculates initial loop counter value for a dbf loop
; that writes n bytes total at 2 bytes per iteration
bytesToWcnt function n,n>>1-1
; calculates initial loop counter value for a dbf loop
; that writes n bytes total at x bytes per iteration
bytesToXcnt function n,x,n/x-1
; fills a region of 68k RAM with 0
clearRAM macro addr,length
if ((addr)&$8000)==0
lea (addr).l,a1
else
lea (addr).w,a1
endif
moveq #0,d0
if ((addr)&1)
move.b d0,(a1)+
endif
move.w #bytesToLcnt(length - ((addr)&1)),d1
.loop: move.l d0,(a1)+
dbf d1,.loop
if ((length - ((addr)&1))&2)
move.w d0,(a1)+
endif
if ((length - ((addr)&1))&1)
move.b d0,(a1)+
endif
endm
; tells the Z80 to stop, and waits for it to finish stopping (acquire bus)
stopZ80 macro
move.w #$100,(Z80_bus_request).l ; stop the Z80
.loop: btst #0,(Z80_bus_request).l
bne.s .loop ; loop until it says it's stopped
endm
; tells the Z80 to start again
startZ80 macro
move.w #0,(Z80_bus_request).l ; start the Z80
endm
; function to make a little-endian 16-bit pointer for the Z80 sound driver
z80_ptr function x,(x)<<8&$FF00|(x)>>8&$7F|$80
; macro to declare a little-endian 16-bit pointer for the Z80 sound driver
rom_ptr_z80 macro addr
dc.w z80_ptr(addr)
endm
; macros to convert from tile index to art tiles, block mapping or VRAM address.
make_art_tile function addr,pal,pri,((pri&1)<<15)|((pal&3)<<13)|(addr&tile_mask)
tiles_to_bytes function addr,((addr&$7FF)<<5)
; function to calculate the location of a tile in plane mappings with a width of 40 cells
planeLocH28 function col,line,(($50 * line) + (2 * col))
; macro for generating water palette transition tables
watertransheader macro {INTLABEL}
__LABEL__ label *
; Number of entries in list minus one
dc.w (((__LABEL___End - __LABEL__ - 2) / 2) - 1)
endm
; macro for generating level select strings
levselstr macro str
save
codepage LEVELSELECT
dc.b strlen(str)-1, str
restore
endm
; codepage for level select
save
codepage LEVELSELECT
charset '0','9', 16
charset 'A','Z', 30
charset 'a','z', 30
charset '*', 26
charset $A9, 27 ; '?'
charset ':', 28
charset '.', 29
charset ' ', 0
restore
; macros for defining animated PLC script lists
zoneanimstart macro {INTLABEL}
__LABEL__ label *
zoneanimcount := 0
zoneanimcur := "__LABEL__"
dc.w zoneanimcount___LABEL__ ; Number of scripts for a zone (-1)
endm
zoneanimend macro
zoneanimcount_{"\{zoneanimcur}"} = zoneanimcount-1
endm
zoneanimdeclanonid := 0
zoneanimdecl macro duration,artaddr,vramaddr,numentries,numvramtiles
zoneanimdeclanonid := zoneanimdeclanonid + 1
start:
dc.l (duration&$FF)<<24|artaddr
dc.w tiles_to_bytes(vramaddr)
dc.b numentries, numvramtiles
zoneanimcount := zoneanimcount + 1
endm
; macro for declaring a "main level load block" (MLLB)
levartptrs macro plc1,plc2,palette,art1,art2,map16x161,map16x162,map128x1281,map128x1282
dc.l (plc1<<24)|art1
dc.l (plc2<<24)|art2
dc.l (palette<<24)|map16x161
dc.l (palette<<24)|map16x162
dc.l map128x1281
dc.l map128x1282
endm
; macro for a pattern load request list header
; must be on the same line as a label that has a corresponding _End label later
plrlistheader macro {INTLABEL}
__LABEL__ label *
dc.w (((__LABEL___End - __LABEL__Plc) / 6) - 1)
__LABEL__Plc:
endm
; macro for a pattern load request
plreq macro toVRAMaddr,fromROMaddr
dc.l fromROMaddr
dc.w tiles_to_bytes(toVRAMaddr)
endm
; macro for a debug object list header
; must be on the same line as a label that has a corresponding _End label later
dbglistheader macro {INTLABEL}
__LABEL__ label *
dc.w ((__LABEL___End - __LABEL__ - 2) / $A)
endm
; macro to define debug list object data
dbglistobj macro obj, mapaddr, subtype, frame, vram
dc.l frame<<24|obj
dc.l subtype<<24|mapaddr
dc.w vram
endm
tribyte macro val
if "val"<>""
dc.b (val >> 16)&$FF,(val>>8)&$FF,val&$FF
shift
tribyte ALLARGS
endif
endm
; macro to define a palette script pointer
palscriptptr macro header, data
dc.w data-header, 0
dc.l header
._headpos := header
endm
; macro to define a palette script header
palscripthdr macro palette, entries, value
dc.w (palette)&$FFFF
dc.b entries-1, value
endm
; macro to define a palette script data
palscriptdata macro frames, data
.framec := frames-1
shift
dc.w ALLARGS
dc.w .framec
endm
; macro to define a palette script data from an external file
palscriptfile macro frames, data
.framec := frames-1
shift
binclude ALLARGS
dc.w .framec
endm
; macro to repeat script from start
palscriptrept macro header
dc.w -4
endm
; macro to define loop from start for x number of times, then initialize with new header
palscriptloop macro header
dc.w -8, header-._headpos
._headpos := header
endm
; macro to run the custom script routine
palscriptrun macro header
dc.w -$C
endm
; Function to turn a sample rate into a djnz loop counter
pcmLoopCounterBase function sampleRate,baseCycles, 1+(Z80_Clock/(sampleRate)-(baseCycles)+(13/2))/13
pcmLoopCounter function sampleRate, pcmLoopCounterBase(sampleRate,102) ; 102 is the number of cycles zPlaySEGAPCM takes to deliver one sample.
dpcmLoopCounter function sampleRate, pcmLoopCounterBase(sampleRate,297/2) ; 297 is the number of cycles zPlayDigitalAudio takes to deliver two samples.
; Function to make a little endian (z80) pointer
k68z80Pointer function addr,((((addr&$7FFF)+$8000)<<8)&$FF00)+(((addr&$7FFF)+$8000)>>8)
little_endian function x,(x)<<8&$FF00|(x)>>8&$FF
startBank macro {INTLABEL}
align $8000
__LABEL__ label *
soundBankStart := __LABEL__
soundBankName := "__LABEL__"
endm
DebugSoundbanks := 0
finishBank macro
if * > soundBankStart + $8000
fatal "soundBank \{soundBankName} must fit in $8000 bytes but was $\{*-soundBankStart}. Try moving something to the other bank."
elseif (DebugSoundbanks<>0)&&(MOMPASS=1)
message "soundBank \{soundBankName} has $\{$8000+soundBankStart-*} bytes free at end."
endif
endm
; macro to declare an entry in an offset table rooted at a bank
offsetBankTableEntry macro ptr
dc.ATTRIBUTE k68z80Pointer(ptr-soundBankStart)
endm
; Special BINCLUDE wrapper function
DACBINCLUDE macro file,{INTLABEL}
__LABEL__ label *
BINCLUDE file
__LABEL___Len := little_endian(*-__LABEL__)
__LABEL___Ptr := k68z80Pointer(__LABEL__-soundBankStart)
__LABEL___Bank := soundBankStart
endm
; Setup macro for DAC samples.
DAC_Setup macro rate,dacptr
dc.b dpcmLoopCounter(rate)
dc.w dacptr_Len
dc.w dacptr_Ptr
endm
; Setup a null entry for a DAC sample.
DAC_Null_Setup macro rate
dc.b dpcmLoopCounter(rate)
dc.w $0000,$0000
endm
; Setup a chain-linked invalid entry for a DAC sample.
; The sample's length is correctly stored for the sample,
; while the pointer (usually) goes towards the DAC pointer
; entry of another DAC sample setup.
DAC_Null_Chain macro rate,linkptr
dc.b dpcmLoopCounter(rate)
dc.w $0000,k68z80Pointer(linkptr+3-soundBankStart)
endm