-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathchapter-next-sound.tex
305 lines (244 loc) · 14.9 KB
/
chapter-next-sound.tex
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
\section{Sound}
\label{zx_next_sound}
% ────────────────────────────────────────────────────────────────────────────────────
% ─██████████████─██████████████─██████──██████─██████──────────██████─████████████───
% ─██░░░░░░░░░░██─██░░░░░░░░░░██─██░░██──██░░██─██░░██████████──██░░██─██░░░░░░░░████─
% ─██░░██████████─██░░██████░░██─██░░██──██░░██─██░░░░░░░░░░██──██░░██─██░░████░░░░██─
% ─██░░██─────────██░░██──██░░██─██░░██──██░░██─██░░██████░░██──██░░██─██░░██──██░░██─
% ─██░░██████████─██░░██──██░░██─██░░██──██░░██─██░░██──██░░██──██░░██─██░░██──██░░██─
% ─██░░░░░░░░░░██─██░░██──██░░██─██░░██──██░░██─██░░██──██░░██──██░░██─██░░██──██░░██─
% ─██████████░░██─██░░██──██░░██─██░░██──██░░██─██░░██──██░░██──██░░██─██░░██──██░░██─
% ─────────██░░██─██░░██──██░░██─██░░██──██░░██─██░░██──██░░██████░░██─██░░██──██░░██─
% ─██████████░░██─██░░██████░░██─██░░██████░░██─██░░██──██░░░░░░░░░░██─██░░████░░░░██─
% ─██░░░░░░░░░░██─██░░░░░░░░░░██─██░░░░░░░░░░██─██░░██──██████████░░██─██░░░░░░░░████─
% ─██████████████─██████████████─██████████████─██████──────────██████─████████████───
% ────────────────────────────────────────────────────────────────────────────────────
Next inherits the same 3 AY-3-8912 chips setup as used in 128K Spectrums. This allows us to reuse many of the pre-existing applications and routines to play sound effects and music.
\subsection{AY Chip Registers}
AY chip has 3 sound channels, called A, B and C. Combined with 3 chips, this allows us to produce 9 channel music. Programming wise, each of the 3 chips needs to be selected first via \PortTextXRef{FFFD} register. Afterwards, we can set various parameters through \PortTextXRef{08} and \PortTextXRef{09} registers.
AY chip is controlled by 14 internal registers. To program them, we first need to select the register with \PortTextXRef{FFFD} and then write the value with \PortTextXRef{BFFD}.
\subsection{Editing and Players}
Several applications can produce sounds or music compatible with the AY chip. For sounds, Shiru's AYFX Player\footnote{\url{https://shiru.untergrund.net/software.shtml\#old}} can be used. This program also includes a Z80 native player that can directly load and play sound effects. Alternatively, Remy's AY audio generator website\footnote{\url{https://zx.remysharp.com/audio/}} can produce exactly the same results and is fully compatible with AYFX Player.
A different way of playing sounds is to convert the WAV file into 1, 2 or 4-bit per sample sound with the ChibiWave application. Sounds take a bit more memory this way but are much easier to create. You can find the application, as well as tutorial and playback source code on Chibi Akumas website\footnote{\url{https://www.chibiakumas.com/z80/platform4.php\#LessonP35}}. While there, definitely check other tutorials too - they're all high quality and available as both, written posts and YouTube videos.
For creating music there are also several options. NextDAW\footnote{\url{https://nextdaw.biasillo.com/}} is native composer that runs on ZX Spectrum Next itself. Or if you prefer cross-platform, Arkos Tracker\footnote{\url{https://www.julien-nevo.com/arkostracker/}} or Vortex Tracker\footnote{\url{https://bulba.untergrund.net/vortex_e.htm}} should do the job. All include ``drivers''; Z80 code you can include in your program that can load and play created music.
\pagebreak
\subsection{Examples}
Before we can start playing sounds, we need to enable the sound hardware. While this is usually enabled by default, it's nonetheless a good idea to ensure our program will always run under the same conditions.
\begin{tcblisting}{}
; Setup Turbo Sound chip
LD BC, &FFFD ; Turbo Sound Next Control Register
LD A, %11111101 ; Enable left+right audio, select AY2
OUT (C), A
; Setup mapping of chip channels to stereo channels
NEXTREG &08, %00010010 ; Use ABC, enable internal speaker & turbosound
NEXTREG &09, %11100000 ; Enable mono for AY0-2
\end{tcblisting}
Programming AY consists of writing various values to its registers. As mentioned, this is a two-step process: first select register number, then write the value. Multiple writes are required for each tone to set period, volume etc. To make it simpler, I created a subroutine. It takes 2 parameters: {\tt A} for register number ({\tt 0-13}) and {\tt D} with value to write.
\begin{tcblisting}{}
WriteDToAYReg:
; Select desired register
LD BC, &FFFD
OUT (C), A
; Write given value
LD A, D
LD BC, &BFFD
OUT (C), A
RET
\end{tcblisting}
Companion code on GitHub, folder {\tt sound} includes expanded code as well as a simple player that plays multiple tones in sequence. For the purposes of this book, I used Remy's AY audio generator website to load one of the example effects, then manually copied raw values into the source code. Laborious process to say the least - this is not how effects should be handled in real life. But I wanted to learn and demonstrate how to program AY chip, not how to use ready-made drivers to play effects or music. Furthermore, my ``player'' blocks the main loop; ideally, sound effects and music would play on the interrupt handler. This could be a nice homework for the reader - example in section \XRef{zx_next_interrupts} should give you an idea of how to achieve this - happy coding!
\pagebreak
\subsection{Sound Ports and Registers}
\label{zx_next_sound_registers}
\subsubsection{\PortDeclaration{FFFD}}
When bit {\tt 7} is {\tt 1}:
\begin{NextPort}
\PortBits{7}
\PortDesc{{\tt 1}}
\PortBits{6}
\PortDesc{{\tt 1} to enable left audio}
\PortBits{5}
\PortDesc{{\tt 1} to enable right audio}
\PortBits{4-2}
\PortDesc{Must be {\tt 1}}
\PortBits{1-0}
\PortDesc{Selects active chip:}
\PortDescOnly{
\begin{PortBitConfig}
\PortBitLine{00}{Unused}
\PortBitLine{01}{AY2}
\PortBitLine{10}{AY1}
\PortBitLine{11}{AY0}
\end{PortBitConfig}
}
\end{NextPort}
When bit {\tt 7} is {\tt 0}:
\begin{NextPort}
\PortBits{7}
\PortDesc{{\tt 0}}
\PortBits{6-0}
\PortDesc{Selects given AY register number for read or write from active sound chip}
\end{NextPort}
\subsubsection{\PortDeclaration{BFFD}}
\begin{NextPort}
\PortBits{7-0}
\PortDesc{Writes given value to currently selected register:}
\end{NextPort}
\begingroup
\small
\vspace*{8pt} % some vertical spacing between port table and this sectino
\setlength{\leftskip}{1.25cm} % Setup left margin as if this whole section was within port table (tried adding it there but got syntax errors)
\newcommand{\AYRegTitle}[1]{
\subsubsection{#1}
\vspace*{-1ex}
}
\begin{multicols}{2}
\AYRegTitle{0 - Channel A tone, low byte}
\begin{BitTableByte}
\BitStartMulti{8}{A tone} \\
\end{BitTableByte}
\AYRegTitle{1 - Channel A tone, high 4-bits}
\begin{BitTableByte}
\BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMulti{4}{A tone high} \\
\end{BitTableByte}
\end{multicols}
\begin{multicols}{2}
\AYRegTitle{2 - Channel B tone, low byte}
\begin{BitTableByte}
\BitStartMulti{8}{B tone} \\
\end{BitTableByte}
\AYRegTitle{3 - Channel B tone, high 4-bits}
\begin{BitTableByte}
\BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMulti{4}{B tone high} \\
\end{BitTableByte}
\end{multicols}
\begin{multicols}{2}
\AYRegTitle{4 - Channel C tone, low byte}
\begin{BitTableByte}
\BitStartMulti{8}{C tone} \\
\end{BitTableByte}
\AYRegTitle{5 - Channel C tone, high 4-bits}
\begin{BitTableByte}
\BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMulti{4}{C tone high} \\
\end{BitTableByte}
\end{multicols}
\pagebreak
\begin{multicols}{2}
\AYRegTitle{6 - Noise period}
\begin{BitTableByte}
\BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMulti{5}{Noise Period} \\
\end{BitTableByte}
\leavevmode % for some reason new line \\ doesn't work here with BitTableByte/ElegantTable
\columnbreak
\AYRegTitle{7 - Flags}
\begin{BitTableByte}
\BitMono{0} & \BitMono{0} & \BitMono{C} & \BitMono{B} & \BitMono{A} & \BitMono{C} & \BitMono{B} & \BitMono{A} \\
\hline
\BitMono{0} & \BitMono{0} & \BitMulti{3}{Noise} & \BitMulti{3}{Tone} \\
\end{BitTableByte}
\end{multicols}
\begin{multicols}{2}
\AYRegTitle{8 - Channel A volume/envelope}
\begin{BitTableByte}
\BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMulti{4}{A Volume} \\
\end{BitTableByte}
\AYRegTitle{9 - Channel B volume/envelope}
\begin{BitTableByte}
\BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMulti{4}{B Volume} \\
\end{BitTableByte}
\end{multicols}
\begin{multicols}{2}
\AYRegTitle{10 - Channel C volume/envelope}
\begin{BitTableByte}
\BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMulti{4}{C Volume} \\
\end{BitTableByte}
\columnbreak
\textbf{Note:} Registers {\tt 8-10} work as volume control if bit {\tt 4} is {\tt 0}, otherwise envelop generator is
used (see registers {\tt 11-13}). In this case bits {\tt 3-0} are ignored.
\end{multicols}
\begin{multicols}{2}
\AYRegTitle{11 - Envelope period fine}
\begin{BitTableByte}
\BitStartMulti{8}{Envelope bits 7-0} \\
\end{BitTableByte}
\AYRegTitle{12 - Envelope period coarse}
\begin{BitTableByte}
\BitStartMulti{8}{Envelope bits 15-8} \\
\end{BitTableByte}
\end{multicols}
\begin{multicols}{2}
\AYRegTitle{13 - Envelope shape}
\begin{BitTableByte}
\BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMono{0} & \BitMono{$C$} & \BitMono{$A_t$} & \BitMono{$A_l$} & \BitMono{$H$} \\
\end{BitTableByte}
\end{multicols}
\hspace*{1.3cm}
\begin{tabular}{lllp{18cm}}
$H$ & \multicolumn{3}{l}{``Hold''} \\
& {\tt 1} & \multicolumn{2}{p{10cm}}{envelope generator performs 1 cycle then holds the end value} \\
& {\tt 0} & \multicolumn{2}{p{10cm}}{cycles continuously} \\
$A_l$ & \multicolumn{3}{l}{``Alternate''} \\
& \multicolumn{3}{l}{If ``hold'' set} \\
& & {\tt 1} & the value held is initial value \\
& & {\tt 0} & the value held is the final value \\
& \multicolumn{3}{l}{If ``hold'' not set} \\
& & {\tt 1} & envelope generator alters direction after each cycle \\
& & {\tt 0} & resets after each cycle \\
$A_t$ & \multicolumn{3}{l}{``Attack''} \\
& {\tt 1} & \multicolumn{2}{l}{the generator counts up} \\
& {\tt 0} & \multicolumn{2}{l}{the generator counts down} \\
$C$ & \multicolumn{3}{l}{``Continue''} \\
& {\tt 1} & \multicolumn{2}{l}{``hold'' is followed} \\
& {\tt 0} & \multicolumn{2}{p{12cm}}{the envelope generator performs one cycle then drops volume to 0 and stays there, overriding ``hold''} \\
\end{tabular}
\endgroup
\subsubsection{\PortDeclaration{06}}
\begin{NextPort}
\PortBits{7}
\PortDesc{{\tt 1} to enable CPU speed mode key "F8", {\tt 0} to disable ({\tt 1} after soft reset)}
\PortBits{6}
\PortDesc{Core 3.1.2+: Divert BEEP-only to internal speaker ({\tt 0} after hard reset)}
\PortDescOnly{Pre core 3.1.2: DMA mode, {\tt 0} zxnDMA, {\tt 1} Z80 DMA ({\tt 0} after hard reset)}
\PortBits{5}
\PortDesc{Core 2.0+: {\tt 1} to enable "F3" key (50/60 Hz switch) ({\tt 1} after soft reset)}
\PortDescOnly{Pre core 2.0: "Enable Lightpen"}
\PortBits{4}
\PortDesc{{\tt 1} to enable DivMMC automap and DivMMC NMI by DRIVE button ({\tt 0} after hard reset)}
\PortBits{3}
\PortDesc{{\tt 1} to enable multiface NMI by M1 button ({\tt 0} after hard reset)}
\PortBits{2}
\PortDesc{{\tt 1} to set primary device to mouse in PS/2 mode, {\tt 0} to set to keyboard}
\PortBits{1-0}
\PortDesc{Audio chip mode:}
\PortDescOnly{
\begin{PortBitConfig}
\PortBitLine{00}{YM}
\PortBitLine{01}{AY}
\PortBitLine{10}{Disabled}
\PortBitLine{11}{Core 3.0+: Hold all AY in reset}
\end{PortBitConfig}
}
\end{NextPort}
\subsubsection{\PortDeclaration{08}}
\begin{NextPort}
\PortBits{7}
\PortDesc{{\tt 1} unlock / {\tt 0} lock port \PortTextXRef{7FFD} paging}
\PortBits{6}
\PortDesc{{\tt 1} to disable RAM and I/O port contention ({\tt 0} after soft reset)}
\PortBits{5}
\PortDesc{AY stereo mode ({\tt 0} = ABC, {\tt 1} = ACB) ({\tt 0} after hard reset)}
\PortBits{4}
\PortDesc{Enable internal speaker ({\tt 1} after hard reset)}
\PortBits{3}
\PortDesc{Enable 8-bit DACs (A,B,C,D) ({\tt 0} after hard reset)}
\PortBits{2}
\PortDesc{Enable port \MemAddr{FF} Timex video mode read ({\tt 0} after hard reset)}
\PortBits{1}
\PortDesc{Enable Turbosound (currently selected AY is frozen when disabled) ({\tt 0} after hard reset)}
\PortBits{0}
\PortDesc{Implement Issue 2 keyboard (port \MemAddr{FE} reads as early ZX boards) ({\tt 0} after hard reset)}
\end{NextPort}
\subsubsection{\PortTextXRef[]{09}}
\PortDeclarationXRef{Sprites}{09}
\pagebreak
\IntentionallyEmpty
\pagebreak