aboutsummaryrefslogtreecommitdiff
path: root/src-z80/core/main.z80
blob: 383a0be5a0a8043e157f95a3d18678b81a015230 (plain)
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
;****************************************************************************
; EntryPoint
; Where the program starts
;****************************************************************************

EntryPoint:
    xor a                       ; Reset Echo status (we don't clear
    ld (RAM_Status), a          ; RAM_Command since Echo_Init fills in values
    ld (RAM_Command2), a        ; before Echo gets to run!)

    ld sp, RAM_Stack            ; Init stack

    ld hl, $7F11                ; Mute PSG
    ld (hl), $9F
    ld (hl), $BF
    ld (hl), $DF
    ld (hl), $FF
    xor a
    ld (RAM_PSGData), a
    ld (RAM_PSGData+16), a
    ld (RAM_PSGData+32), a
    ld (RAM_PSGData+48), a
    dec a
    ld (RAM_PSGData+11), a
    ld (RAM_PSGData+11+16), a
    ld (RAM_PSGData+11+32), a
    ld (RAM_PSGData+11+48), a

    ld hl, $6000                ; Set default bank
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l

    ld ix, $4000                ; YM2612 I/O ports base address
    ld iy, $4000

    exx                         ; Init PCM playback status
    ld b, $00                   ; Not playing
    exx

    ld (ix+0), $2B              ; Disable DAC by default
    ld (ix+1), $00

    ld e, $7F                   ; Mute all FM channels
    ld a, $40
    ld b, 4
.mutefm:
    ld (ix+0), a
    ld (ix+1), e
    ld (ix+2), a
    ld (ix+3), e
    inc a
    ld (ix+0), a
    ld (ix+1), e
    ld (ix+2), a
    ld (ix+3), e
    inc a
    ld (ix+0), a
    ld (ix+1), e
    ld (ix+2), a
    ld (ix+3), e
    inc a
    inc a
    djnz .mutefm

    ld (ix+0), $B4              ; Ensure all channels can be heard from both
    ld (ix+1), $C0              ; speakers (by default they're mute!)
    ld (ix+0), $B5
    ld (ix+1), $C0
    ld (ix+0), $B6
    ld (ix+1), $C0
    ld (ix+2), $B4
    ld (ix+3), $C0
    ld (ix+2), $B5
    ld (ix+3), $C0
    ld (ix+2), $B6
    ld (ix+3), $C0

    ld (ix+0), $24              ; Init timers
    ld (ix+1), $FE
    ld (ix+0), $25
    ld (ix+1), $03
    ld (ix+0), $26
    ld (ix+1), $C9
    ld (ix+0), $27
    ld (ix+1), $3F
    
    jp IdleLoop                 ; Go into idle loop

;****************************************************************************
; RunCommand
; Checks which command to run
;----------------------------------------------------------------------------
; notes: doesn't return
;----------------------------------------------------------------------------
; To-do: replace with pointer list?
;****************************************************************************

RunCommand:
    dec a                       ; Command $01: load list
    jp z, LoadList
    dec a                       ; Command $02: play SFX
    jp z, PlaySFX
    dec a                       ; Command $03: stop SFX
    jp z, StopSFXCmd
    dec a                       ; Command $04: play BGM
    jp z, PlayBGM
    dec a                       ; Command $05: stop BGM
    jp z, StopBGMCmd
    dec a                       ; Command $06: resume BGM
    jp z, ResumeBGM
    dec a                       ; Command $07: set PCM rate
    jp z, SetPCMRate
    dec a                       ; Command $08: pause BGM
    jp z, PauseBGM
    dec a                       ; Command $09: set stereo
    jp z, SetStereo

    PollPCM

    ; Bad command, ignore >:(

;****************************************************************************
; EndOfCommand
; Cleans up when a command finishes
;----------------------------------------------------------------------------
; notes: doesn't return
;****************************************************************************

EndOfCommand:
    ld hl, ($1FF8)              ; Copy second slot into first
    ld ($1FFC), hl
    ld hl, ($1FFA)
    ld ($1FFE), hl
    
    xor a                       ; Free up second slot
    ld (RAM_Command2), a
    
    PollPCM

;****************************************************************************
; IdleLoop
; Loop that runs when not processing SFX or BGM
;----------------------------------------------------------------------------
; notes: doesn't return (d'oh)
;****************************************************************************

IdleLoop:
    ld a, (RAM_Command)         ; Look for commands
    or a
    jr nz, RunCommand

    PollPCM                     ; Poll PCM

    ld a, ($4000)               ; Tick?
    bit 1, a
    jr nz, DoTick
    bit 0, a                    ; Poll PCM again
    call nz, UpdatePCM          ; Not using macro for optimization purposes

    jp IdleLoop                 ; Keep idling

;****************************************************************************
; DoTick
; Called whenever a new tick triggers
;----------------------------------------------------------------------------
; notes: doesn't return
;****************************************************************************

DoTick:
    PollPCM

    ld (ix+0), $27              ; Retrigger the timer
    ld (ix+1), $2F
DoTick_Tick:

    PollPCM
    
    ld a, ($1FF1)               ; Refresh volume if needed
    or a
    call nz, RefreshVolume

DoTick_SFX:                     ; Process SFXs
    jp DoTick_SFXSkip
DoTick_SFXSkip:

    PollPCM
    
    call ProcessDirect            ; Process direct events

    PollPCM
    
    ld a, (RAM_Paused)          ; BGMs are paused?
    or a
    jr nz, DoTick_BGMSkip
DoTick_BGM:                     ; Process BGMs
    jp DoTick_BGMSkip
DoTick_BGMSkip:

    PollPCM

    jp UpdatePSG                ; Update PSG envelopes
DoTick_PSGSkip:

    PollPCM

    jp IdleLoop                 ; End of subroutine

;****************************************************************************
; LoadList [command $01]
; Loads the pointer list
;----------------------------------------------------------------------------
; notes: doesn't return
;****************************************************************************

LoadList:
    ld hl, (RAM_ComAddr)        ; Get command parameters
    ld a, (RAM_ComBank)
    ld c, a

    ld de, RAM_PointerList      ; Where the pointer list starts

.loadloop:
    call GetParam               ; Get high byte address
    ld a, b                     ; Is it the end of the list?
    or a
    jp z, .loadend
    ld (de), a                  ; Store high byte address
    inc d
    
    call GetParam               ; Get low address byte
    ld a, b
    ld (de), a
    inc d
    
    call GetParam               ; Get bank byte
    ld a, b
    ld (de), a
    
    dec d                       ; Go for next byte
    dec d
    inc e
    jp .loadloop

.loadend:
    jp EndOfCommand             ; End of subroutine

;****************************************************************************
; GetParam
; Subroutine for getting the parameter byte
;----------------------------------------------------------------------------
; input c .... current bank
; input hl ... current address
;----------------------------------------------------------------------------
; output b .... value
; output c .... new bank
; output hl ... new address
;----------------------------------------------------------------------------
; breaks: af
;----------------------------------------------------------------------------
; note: the C value gets incremented *only* when HL hits $0000 (this is
; relevant if you consider using it to fetch from Z80 RAM, which should
; never result in HL becoming $0000).
;****************************************************************************

GetParam:
    ld a, (RAM_LastBank)        ; Bank switch?
    cp c
    jp z, .noswitchp
    ld a, c
    ld (RAM_LastBank), a
    push hl
    ld hl, $6000
    BankSwitch
    pop hl
.noswitchp:
    ld b, (hl)                  ; Get value

    inc l                       ; Get next address
    jp nz, .nonewbankp
    inc h
    jp nz, .nonewbankp
    ld h, $80
    inc c
.nonewbankp:

    ret                         ; End of subroutine