Now that the shooting assemblage is done and dusted, it’s clip to move to a portion of our first circuit of the ZX Spectrum that has been near untouched: sound. There isn’t a whole batch caller present compared to what has travel earlier connected different platforms, but that successful move intends that the halfway principles of our solutions to it get pre-baked; we’ll conscionable person to adhd the Spectrum-specific frosting.
The mainline Spectrum systems connection 2 sound options:
- A 1-bit beeper nether package control. This is coming connected each systems, and the underlying hardware is not dissimilar what we saw connected the Apple II aliases the PC speaker.
- A General Instrument AY-3-8910 synthesizer chip, coming successful each models from the 128K Spectrum connected and disposable arsenic a peripheral for the 16K and 48K variants. These are fundamentally the aforesaid arsenic the 1 successful the Atari ST but it’s clocked otherwise and we don’t person a built-in BIOS telephone to pass pinch it.
This is portion of our wide survey, alternatively for illustration our text mode and bitmap mode surveys, truthful our goals will beryllium modest; we’ll extremity this article pinch 3 programs that play america a C-major scale.
Using the Beeper Through the BIOS
The 1-bit beeper is very akin to the Apple II’s; the $10 spot connected the I/O larboard whitethorn beryllium group aliases cleared to group the speaker’s status. Specific tones are produced by cautiously timing the intervals betwixt toggling that bit. We’ll get to codification that does that later, but we tin put that disconnected because we don’t person to do it ourselves— the BEEP bid successful BASIC already manages this and it does truthful by forwarding to a generic regular that we may, ourselves, make usage of. The BEEPER regular lies astatine memory location $03B5 and it’s honestly much clever than the versions I’ve written for 6502 systems. The HL registry specifies the hold clip successful units of 4 cycles (the magnitude of a no-op) and it acquires that precision utilizing a method not dissimilar the 1 we utilized for programmable cycle-exact 6502 delays. The DE registry specifies the long of the note, which is simply a touch inconvenient because it really specifies the number of wavelengths the statement should have. Notes of adjacent magnitude but different pitches will request different long codes.
The BEEP bid itself, though, relies connected being told notes successful half-steps disconnected mediate C and durations measured successful seconds. We tin look astatine its ain implementation, astatine $03F8, to spot really to get the values we need. This is done via the system’s floating-point engine, truthful we really get neat formulae for some correct retired of the commentary:
- The wave codification HL for a wave of f Hz is provided by the look 437500/f – 30.125.
- The long codification DE for a statement pinch wave f Hz and long t seconds is simply f×t.
A fewer lines of Python fto america use these to the frequencies of the C awesome scale, giving america wave and long codes to provender to the routine. Here is our playback program:
org $7000 ld b,8 ; Play 8 notes ld hl,scale ; Load from pointer loop: push bc ld c,(hl) ; Load wave into BC inc hl ld b,(hl) inc hl ld e,(hl) ; Load long into HL inc hl ld d,(hl) inc hl push hl ; Stash pointer... ld h,b ; ... Copy BC to HL... ld l,c telephone $03b5 ; ... and fto the ROM do the rest popular hl popular bc djnz loop ret scale: dw $066a,$0082,$05b3,$0092,$0511,$00a4,$04c6,$00ae dw $043d,$00c3,$03c4,$00dc,$0357,$00f6,$0325,$0105This is simply a “half-wavelength” strategy for illustration my first Apple II sound playback routine, but its overmuch finer timing controls mean that the wide precision of the tones is much better. Essentially nary of the tradeoffs I faced successful my first article show up present astatine all, and we person the benignant of power we’d expect from a dedicated PSG. That’s honestly really great. The only existent downside present is that it’s a small awkward to request to tally our ain divisions connected some sides for illustration this; if we want a generic regular that manages tones of arbitrary frequencies and long we’ll request to bring our ain divider. However, arsenic we’ll spot shortly, connected a 16KB Spectrum we don’t person immoderate different choice, and we person nary action different than leaning connected this routine.
We’re getting up of ourselves, though. Let’s look astatine really we’d do it by manus earlier really moving into the issues that we’d deed connected the 16K systems.
Using the Beeper Directly
The BIOS regular is bully capable that if if I’m going to constitute my ain oscillator usability I want it to tally connected an wholly different principle. Fortunately, I person conscionable specified a rule available: the 16-bit strategy based connected wave alternatively of half-wavelength that I created inspired by the internals of the SID and the Ensoniq chip. With the Z80’s built-in support for 16-bit mathematics it should beryllium overmuch much straightforward than the 6502 code.
The basal thought is to many times adhd our wave codification to a moving antagonistic and toggle the speaker each clip that antagonistic overflows. The tricky portion is really making judge that we don’t messiness pinch thing else, because I/O larboard $FE does triple duty:
- The debased 3 bits group the separator color. We want to sphere immoderate worth is successful the BORDCR strategy adaptable here.
- The $08 spot controls the audio output erstwhile controlling the portion platform alternatively than erstwhile controlling the loudspeaker. These messiness pinch the voltages somehow, and Korth’s documentation linked successful my level guide screen the gory details, but we will conscionable do what the strategy ROMs do and unit this spot connected astatine each times.
- The $10 spot is the 1 we really want to activity with, controlling the loudspeaker audio.
We commencement by conscionable straight implementing each this, and pinch arsenic fewer branches arsenic we tin get distant with:
sound: ld hl,0 ; Counter starts astatine 0 ld a,($5c48) ; BORDCR and $38 ; Extract separator color rrca ; and displacement to debased 3 bits rrca rrca aliases $08 ; Disable portion audio di ; Don't get interrupted by IRQ push af .loop: popular af adhd hl,de ; Add wave to counter jr nc,1F ; If it overflows... xor $10 ; ... toggle speaker output retired ($fe),a 1 dec bc ; Decrement timer push af ; Check for 0. Can't popular AF ld a,b ; until aft the branch or aliases c ; we suffer the Z emblem value! jr nz,.loop popular af ; Clean up ei retWe now request to rework the codification truthful that each way done each loop takes precisely the aforesaid magnitude of time. Our force present is the JR instruction: it takes 12 cycles connected a branch we return and 7 connected 1 we don’t. That intends that we request to delay, connected the way wherever we don’t toggle the output, 7 (JR) + 7 (XOR) + 11 (OUT) – 12 (the JR successful the branch-taken case) = 13 cycles. The unconditional JR backmost also takes 12 cycles, which intends we request to hold a azygous rhythm connected an architecture wherever each instruction takes a minimum of four. Gross.
The solution, funnily enough, is to simply not usage the JR instruction— the absolute-address JP versions are 1 byte longer but person perfectly accordant timing moreover erstwhile conditional, astatine 10 cycles each. That makes this overmuch easier; our hold branch turns into 2 NOPs and and a JP backmost to the main line. Here’s our adjacent draft:
sound: ld hl,0 ; Counter starts astatine 0 ld a,($5c48) ; BORDCR and $38 ; Extract separator color rrca ; and displacement to debased 3 bits rrca rrca aliases $08 ; Disable portion audio di ; Don't get interrupted by IRQ push af .loop: popular af adhd hl,de ; Add wave to counter jp nc,2F ; If it overflows... xor $10 ; ... toggle speaker output retired ($fe),a 1 dec bc ; Decrement timer push af ; Check for 0. Can't popular AF ld a,b ; until aft the branch or aliases c ; we suffer the Z emblem value! jr nz,.loop popular af ; Clean up ei ret 2 nop ; If nary overflow, stall 18 cycles nop jp 1BCounting it up, this 1 clocks successful astatine 84 cycles per iteration. This gives america linear wave power successful increments of conscionable complete 3Hz each the measurement up to astir 100kHz, which is much than fine. Our durations tin only spell up to astir 1.5 seconds, though, which isn’t great.
We person a bigger problem, though; the reside produced by this regular sounds awful, some wildly off-pitch and inconsistent successful its timing, rattling and warbling arsenic it goes.
The problem, arsenic it turns out, is that the Spectrum’s video circuitry needs to fetch pixel and colour information from the RAM, and it tin unit the CPU to hold while it does those things. This is not dissimilar the “badline” arena connected the C64 that dominated the early years of this blog, but it spaces itself retired much regularly and is fundamentally happening each the clip erstwhile we aren’t successful VBLANK. The rumor is restricted, connected the early Spectrums, to the $4000–$7FFF range… but connected the 16K Spectrum that is all of our RAM and this method is nonviable. On the 48K we whitethorn relocate the programme to $9000 alternatively of our default of $7000 and it useful fine.
It still breaks erstwhile we load astatine $8000, though. It turns retired that BASIC’s CLEAR command, which shrinks the representation BASIC uses to time off room dedicated for instrumentality codification programs, also adjusts the stack pointer to enactment beneath the CLEAR limit. That meant that while our codification started astatine $8000, the stack pointer itself had been pushed down into the $7Fxx scope and was now taxable to representation entree hold states.
I decided successful this codification to conscionable group speech a byte of RAM further up successful representation and usage it straight arsenic a world adaptable alternatively of trying to insist connected the stack being anyplace successful particular. While I was astatine it, I besides unrolled the loop a spot truthful that the long codes could beryllium half arsenic large arsenic before. A full statement successful a opus playing astatine 100BPM will past 2.4 seconds, and we are now capable to comfortably clasp that note.
Here’s the last codification for the playback routine:
sound: ld hl,0ld a,($5c48) ; BORDCR
and $38
rrca
rrca
rrca
aliases $08
di
.lp: adhd hl,de ; +11 = 11
jp nc,2F ; +10 = 21
xor $10 ; + 7 = 28
retired ($fe),a ; +11 = 39
1 nop ; + 4 = 43
nop ; + 4 = 47
nop ; + 4 = 51
nop ; + 4 = 55
nop ; + 4 = 59
nop ; + 4 = 63
nop ; + 4 = 67
nop ; + 4 = 71
nop ; + 4 = 75
nop ; + 4 = 79
jp 3F ; +10 = 89
3 adhd hl,de ; +11 = 11
jp nc,5F ; +10 = 21
xor $10 ; + 7 = 28
retired ($fe),a ; +11 = 39
4 dec bc ; + 6 = 45
ld (.scratch),a ; +13 = 58
ld a,b ; + 4 = 62
aliases c ; + 4 = 66
ld a,(.scratch) ; +13 = 79
jp nz,.lp ; +10 = 89
ei
ret
2 nop ; + 4 = 25
nop ; + 4 = 29
jp 1B ; +10 = 39
5 nop ; + 4 = 25
nop ; + 4 = 29
jp 4B ; +10 = 39
.scratch # 1
And present is the programme that exercises it to play a scale:
org $8000representation $9000
ld b,8
ld hl,scale
loop: ld e,(hl)
inc hl
ld d,(hl)
inc hl
push hl
push bc
ld bc,$2000
telephone sound
popular bc
popular hl
djnz loop
ret
scale: dw $0367,$03d2,$044a,$048b,$051a,$05ba,$066e,$06cf
Programming the AY-3 Sound Chip
We’ve seen the AY-3-8910 before; the Atari ST has one. The wide programming of the spot is the same, pinch 16 8-bit registers we whitethorn constitute to; the only differences are really we constitute them and really we compute for the wave codes. For the astir portion I will beryllium deferring to that aged article because everything location still holds. Here’s what’s new, that we request to cognize connected the Spectrum 128 and its successors:
- To constitute a worth to an AY register, first constitute the registry number to larboard $FFFD, past constitute the worth to larboard $BFFD.
- The main timepiece driving the spot runs astatine 3.5469 MHz alternatively of the 4MHz the ST used; arsenic specified the codification for a wave f Hz is 3546900/(32f).
- Similarly, the letter cover magnitude codification for an letter cover of magnitude t seconds is 3546900/(512t).
That’s… really it. I wrote a elemental usability to constitute the byte D to registry A:
ayreg: ld bc,$fffd ; AY-3 Index retired (c),a ld b,$bf ; AY-3 Value ld a,d retired (c),a retAnd past made a macro that makes calling it much convenient, whether I’m providing information arsenic a changeless worth aliases reference it done a pointer:
macro AY index,val:(hl) ld a,index ld d,val telephone ayreg endmacro(Sjasm macros do textual replacement of their arguments and besides fto you group defaults; arsenic a result, the LD D,val instruction will person different opcodes depending connected whether we walk successful a worth aliases a register.)
And that makes the main codification present the shortest of them each moreover if we usage the letter cover system:
org $7000 ;; Initialize AY-3 voice AY $0b,$10 ; Envelope length: 1sec AY $0c,$1b AY $08,$10 ; Use letter cover connected transmission A ld b,8 ld hl,scale 1 push bc AY $00 ; Read wave from (HL) table inc hl ; And beforehand pointer arsenic we go AY $01 inc hl AY $0d,$09 ; Start a decaying envelope AY $07,$fe ; Enable Channel A telephone region ; Then hold half a second popular bc djnz 1B AY $07,$ff ; Disable transmission A ret pause: push af ; Wait 25 frames push hl ld hl,$5c78 ; FRAMES ld a,(hl) adhd 25 1 halt cp (hl) jr nz,1B popular hl popular af ret scale: dw $01a7,$0179,$0150,$013d,$011a,$00fb,$00e0,$00d3What We Can Do With It
The beeper is really only bully for sound effects and jingles little capable that we tin get distant pinch stopping each the action while we do them. This tracks the usage of sound connected the Apple II beautiful closely. The PC speaker, contempt having a akin “beeper” circuit, also offered dedicated interrupts and independent timers to thrust it, allowing sound processing to not needfully return complete the afloat system.
The AY-3, connected the different hand, mostly runs itself and it should beryllium conscionable arsenic amenable to things for illustration euphony drivers and ambient sound effects arsenic immoderate different systems that usage it. Of the systems I’ve looked astatine here, that includes not simply the Atari ST but besides the MSX line.
English (US) ·
Indonesian (ID) ·