sexta-feira, 15 de novembro de 2019

O beeper do Spectrum

O ZX Spectrum 48K apenas tem um altifalante simples, de 20mW, 40 ohms.


Não tem um chipset para fazer controle complexo de som. O som é apenas gerado por 1 bit. O altifalante é activado com o bit a 0, e desactivado com o bit a 1.  É necessário alternar o bit a 0 e 1 para gerar frequências (sons).

Enquanto se fala com o altifalante (ou se conta ciclos T), a CPU fica "presa", por o ZX Spectrum não ter um chipset dedicado para controlar o som.

Um OUT para a porta da ULA $FE controla o altifalante no bit 4.


Saliente-se que no carregamento de cassetes, o som que se ouve é por ao nível de hardware, quer o EAR, quer o MIC, estarem conectados ao speaker. O som não é enviado ao speaker por software.

Ao nível de código máquina, ou se pode falar directamente com o altifalante, ou invocar a rotina da ROM para gerar frequências 03B5: THE 'BEEPER' SUBROUTINE

A rotina da ROM, monopoliza a CPU enquanto gera som, e desliga os interrupts.

As frequências em Hz de notas musicais são as seguintes:


Para usar a rotina BEEPER, sendo f em Hz e t segundos, a rotina BEEPER da ROM espera como argumentos:

HL = atraso do loop
DE = número de iterações para o ciclo de geração de som

HL = INT (( t * 6689 / 4 ) - 30.125 )
DE = INT ( f * t )

Portanto, para tocar a nota C, durante 1 seg:

HL = INT (( 1 * 6689 / 4 ) - 30.125 ) = 1642
DE = INT ( 216.63 * 1 ) = 262

Sendo assim:

LD HL,1462
LD DE, 262
CALL $03B5
RET

A própria rotina da ROM, é um bom exemplo como se controla directamente o BEEPER:

Input

DE
 Number of passes to make through the sound generation loop
HL 
Loop delay parameter

BEEPER
03B5
DI
Disable the interrupt for the duration of a 'beep'.
03B6
LD A,L
Save L temporarily.
03B7
SRL L
Each '1' in the L register is to count 4 T states, but take INT (L/4) and count 16 T states instead.
03B9
SRL L
03BB
CPL
Go back to the original value in L and find how many were lost by taking 3-(A mod 4).
03BC
AND $03
03BE
LD C,A
03BF
LD B,$00
03C1
LD IX,$03D1
The base address of the timing loop.
03C5
ADD IX,BC
Alter the length of the timing loop. Use an earlier starting point for each '1' lost by taking INT (L/4).
03C7
LD A,($5C48)
Fetch the present border colour from BORDCR and move it to bits 2, 1 and 0 of the A register.
03CA
AND $38
03CC
RRCA
03CD
RRCA
03CE
RRCA
03CF
OR $08
Ensure the MIC output is 'off'.
Now enter the sound generation loop. DE complete passes are made, i.e. a pass for each cycle of the note.
The HL register holds the 'length of the timing loop' with 16 T states being used for each '1' in the L register and 1024 T states for each '1' in the H register.

03D1
NOP
Add 4 T states for each earlier entry point that is used.
03D2
NOP
03D3
NOP
03D4
INC B
The values in the B and C registers will come from the H and L registers - see below.
03D5
INC C
BE_H_L_LP
03D6
DEC C
The 'timing loop', i.e. BC*4 T states. (But note that at the half-cycle point, C will be equal to L+1.)
03D7
JR NZ,BE_H_L_LP
03D9
LD C,$3F
03DB
DEC B
03DC
JP NZ,BE_H_L_LP
The loudspeaker is now alternately activated and deactivated.
03DF
XOR $10
Flip bit 4.
03E1
OUT ($FE),A
Perform the 'OUT' operation, leaving the border unchanged.
03E3
LD B,H
Reset the B register.
03E4
LD C,A
Save the A register.
03E5
BIT 4,A
Jump if at the half-cycle point.
03E7
JR NZ,BE_AGAIN
After a full cycle the DE register pair is tested.
03E9
LD A,D
Jump forward if the last complete pass has been made already.
03EA
OR E
03EB
JR Z,BE_END
03ED
LD A,C
Fetch the saved value.
03EE
LD C,L
Reset the C register.
03EF
DEC DE
Decrease the pass counter.
03F0
JP (IX)
Jump back to the required starting location of the loop.
The parameters for the second half-cycle are set up.
BE_AGAIN
03F2
LD C,L
Reset the C register.
03F3
INC C
Add 16 T states as this path is shorter.
03F4
JP (IX)
Jump back.
Upon completion of the 'beep' the maskable interrupt has to be enabled.
BE_END
03F6
EI
Enable interrupt.
03F7
RET
Finally return.


Nota: embora se use $FE para controlar a ULA, a ULA responde a todos as portas de I/O pares. (i.e. a ULA é activada com portas de I/O com o bit 0 a 0).

O chipset AY só foi introduzido no modelo 128K.

Hz são Hertzs (ciclos por segundo).

Note-se que devido à contenção nos primeiros 16KB de RAM, rotinas que controlem directamente o beeper não devem estar nesta àrea de RAM.

Para rotinas de assembly para gerar som, ver Short beeper routines , Beep the ZX Spectrum e Loudspeaker Sound Effects.

Sem comentários:

Publicar um comentário