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

EntryPoint:
    ld sp, RAM_Stack            ; Init stack

    xor a                       ; Reset Echo status
    ld (RAM_Status), a

    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

    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 iyh, $40

    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

;****************************************************************************
; PollPCM
; Used to update PCM while not idle
;****************************************************************************

PollPCM: macro
    ld a, ($4000)
    rrca
    call c, UpdatePCM
    endm

;****************************************************************************
; RunCommand
; Checks which command to run
;****************************************************************************

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

    PollPCM

    xor a                       ; Bad command, ignore >:(
    ld (RAM_Command), a

    PollPCM

;****************************************************************************
; IdleLoop
; Loop that runs when not processing SFX or BGM
;****************************************************************************

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
;****************************************************************************

DoTick:
    ld a, (ix+0)
    bit 0, a
    call nz, UpdatePCM

    ld (ix+0), $27
    ld (ix+1), $2F

    PollPCM

DoTick_SFX:                     ; Process SFXs
    jp DoTick_SFXSkip
DoTick_SFXSkip:

    PollPCM

DoTick_BGM:                     ; Process BGMs
    jp DoTick_BGMSkip
DoTick_BGMSkip:

    PollPCM

    jp UpdatePSG                ; Update PSG envelopes
DoTick_PSGSkip:

    PollPCM

    jp IdleLoop                 ; End of subroutine

;****************************************************************************
; BankSwitch
; Switches into a new bank (won't update player status!)
;
; input A .... New bank to switch into
; input HL ... Must be $6000
; breaks ..... AF
;****************************************************************************

BankSwitch: macro
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    ld (hl), h
    rrca
    ld (hl), a
    endm

;****************************************************************************
; LoadList [command $01]
; Loads the pointer list
;****************************************************************************

LoadList:
    ld hl, RAM_ComBank          ; Get command parameters
    ld c, (hl)
    inc l
    ld e, (hl)
    inc l
    ld d, (hl)
    ex de, hl

    xor a                       ; Command parsed
    ld (RAM_Command), a

    ld de, RAM_PointerList      ; Where the pointer list starts

.loop:
    call GetParam               ; Get high byte address
    
    ld a, b                     ; Is it the end of the list?
    or a
    jp z, .end
    
    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 .loop

.end:
    jp IdleLoop                 ; End of subroutine

;****************************************************************************
; GetParam
; Subroutine for getting the parameter byte
;****************************************************************************

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 volume

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

    ret                         ; End of subroutine