Files
RomWBW/Source/HBIOS/spk.asm
2024-07-15 12:49:27 -07:00

509 lines
14 KiB
NASM
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;======================================================================
;
; BIT MODE SOUND DRIVER FOR SBC V2 USING BIT 0 OF RTC DRIVER
;
;======================================================================
;
; LIMITATIONS - CPU FREQUENCY ADJUSTMENT LIMITED TO 1MHZ RESOLUTION
; QUARTER TONES NOT SUPPORTED
; DURATION FIXED TO 1 SECOND.
; NO VOLUME ADJUSTMENT DUE TO HARDWARE LIMITATION
;======================================================================
;
; DRIVER FUNCTION TABLE AND INSTANCE DATA
;
SP_FNTBL:
.DW SP_RESET
.DW SP_VOLUME
.DW SP_PERIOD
.DW SP_NOTE
.DW SP_PLAY
.DW SP_QUERY
.DW SP_DURATION
.DW SP_DEVICE
.DW SP_BEEP
;
#IF (($ - SP_FNTBL) != (SND_FNCNT * 2))
.ECHO "*** INVALID SND FUNCTION TABLE ***\n"
!!!!!
#ENDIF
;
SP_IDAT .EQU 0 ; NO INSTANCE DATA ASSOCIATED WITH THIS DEVICE
;
SP_TONECNT .EQU 1 ; COUNT NUMBER OF TONE CHANNELS
SP_NOISECNT .EQU 0 ; COUNT NUMBER OF NOISE CHANNELS
;
SP_RTCIOMSK .EQU 00000100B
;
; FOR OTHER DRIVERS, THE PERIOD VALUE FOR THE TONE IS STORED AT PENDING_PERIOD
; FOR THE SPK DRIVER THE ADDRESS IN THE TONE TABLE IS STORED IN PENDING_PERIOD
;
SP_PENDING_PERIOD .DW 0 ; PENDING PERIOD (16 BITS)
SP_PENDING_VOLUME .DB 0 ; PENDING VOL (8 BITS)
SP_PENDING_DURATION .DW 0 ; PENDING DURATION (16 BITS)
SP_TBLRDY .DB 0 ; IF != 0, NOTE TABLE IS READY
;
DEVECHO "SPK: IO="
DEVECHO RTCIO
DEVECHO "\n"
;
;======================================================================
; DRIVER INITIALIZATION
;======================================================================
;
SP_INIT:
LD IY, SP_IDAT ; SETUP FUNCTION TABLE
LD BC, SP_FNTBL ; POINTER TO INSTANCE DATA
LD DE, SP_IDAT ; BC := FUNCTION TABLE ADDRESS
CALL SND_ADDENT ; DE := INSTANCE DATA PTR
;
CALL NEWLINE ; ANNOUNCE DEVICE
PRTS("SPK: IO=0x$")
LD A,RTCIO
CALL PRTHEXBYTE
;
CALL SP_SETTBL ; SETUP TONE TABLE
CALL SP_RESET ; RESET PARAMETERS
;
XOR A
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - RESET
;======================================================================
;
SP_RESET:
; RESET DEFAULTS IN CASE OF AN IN-PLACE HBIOS RESTART
LD HL,0
LD (SP_PENDING_PERIOD),HL ; SET TONE PERIOD TO ZERO
LD (SP_PENDING_DURATION),HL; SET DURATION TO ZERO
XOR A ; SIGNAL SUCCESS
LD (SP_PENDING_VOLUME),A ; SET VOLUME TO ZERO
;
XOR A ; SUCCESSFULL RESET
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - VOLUME
;======================================================================
;
SP_VOLUME:
XOR A ; SIGNAL SUCCESS
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - PERIOD
;======================================================================
;
SP_PERIOD:
LD (SP_PENDING_PERIOD), HL ; SAVE AND RETURN SUCCESSFUL
XOR A
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - NOTE
;======================================================================
;
SP_NOTE:
; CALL PRTHEXWORDHL
; CALL PC_COLON
PUSH HL
PUSH DE ; ON ENTRY HL IS A NOTE INDEX
LD A,L ; CONVERT THIS NOTE INDEX
AND 00000011B ; TO THE ASSOCIATED ENTRY
JR Z,SP_NOTE1 ; IN THE TUNE TABLE.
;
LD HL,$FFFF ; QUARTER NOTES
JR SP_NOTE2 ; NOT SUPPORTED
;
SP_NOTE1:
LD DE,SP_TUNTBL ; SAVE THIS ADDRESS AS
ADD HL,DE ; THE PERIOD
SP_NOTE2:
; CALL PRTHEXWORDHL
; CALL NEWLINE
LD (SP_PENDING_PERIOD),HL
POP DE
POP HL
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - QUERY AND SUBFUNCTIONS
;======================================================================
;
SP_QUERY:
LD A, E
CP BF_SNDQ_CHCNT ; SUB FUNCTION 01
JR Z, SP_QUERY_CHCNT
;
CP BF_SNDQ_VOLUME ; SUB FUNCTION 02
JR Z, SP_QUERY_VOLUME
;
CP BF_SNDQ_PERIOD ; SUB FUNCTION 03
JR Z, SP_QUERY_PERIOD
;
CP BF_SNDQ_DEV ; SUB FUNCTION 04
JR Z, SP_QUERY_DEV
;
OR $FF ; SIGNAL FAILURE
RET
;
SP_QUERY_CHCNT:
LD BC,(SP_TONECNT*256)+SP_NOISECNT ; RETURN NUMBER OF
XOR A ; TONE AND NOISE
RET ; CHANNELS IN BC
;
SP_QUERY_PERIOD:
LD HL, (SP_PENDING_PERIOD) ; RETURN 16-BIT PERIOD
XOR A ; IN HL REGISTER
RET
;
SP_QUERY_VOLUME:
LD L, 255 ; RETURN 8-BIT VOLUME
XOR A ; IN L REGISTER
RET
;
SP_QUERY_DEV:
LD B, SNDDEV_BITMODE ; RETURN DEVICE IDENTIFIER
LD DE, (RTCIO*256)+SP_RTCIOMSK ; AND ADDRESS AND DATA PORT
XOR A
RET
;
;======================================================================
; INITIALIZE THE TONE TABLE - ONLY ACCURATE FOR 1MHZ INCREMENTS
;======================================================================
;
SP_SETTBL:
; IN CASE OF HBIOS RESTART IN PLACE, WE CHECK TO SEE IF THE
; NOT TABLE IS ALREADY INITIALIZED (READY). IF SO, WE DON'T
; WANT TO DO IT AGAIN.
LD A,(SP_TBLRDY)
OR A
RET NZ
;
LD BC,(CB_CPUMHZ) ; GET MHZ CPU SPEED (IN C).
;
SP_SETTBL3:
LD B,SP_NOTCNT ; SET NUMBER OF NOTES TO
LD HL,SP_TUNTBL+2 ; ADJUST AND START POINT
;
SP_SETTBL2:
PUSH HL
LD E,(HL) ; READ IN
INC HL ; THE 1MHZ
LD D,(HL) ; NOTE
;
PUSH BC
LD B,C
LD HL,0 ; MULTIPLY 1MHZ
SP_SETTBL1: ; NOTE VALUE BY
ADD HL,DE ; SYSTEM MHZ
JR NC,SP_SETBL4
LD HL,$FFFF ; FOR CPU > 10MHz
LD B,1 ; HANDLE OVERFLOW
SP_SETBL4:
DJNZ SP_SETTBL1
POP BC
;
LD DE,15 ; ADD OVERHEAD
ADD HL,DE ; COMPENSATION
;
POP DE ; RECALL NOTE
EX DE,HL ; ADDRESS
;
LD (HL),E ; SAVE
INC HL ; THE
LD (HL),D ; NEW
INC HL ; NOTE
INC HL ; AND MOVE
INC HL ; TO NEXT
;
DJNZ SP_SETTBL2 ; NEXT NOTE
;
OR $FF ; SIGNAL TABLE READY
LD (SP_TBLRDY),A ; SAVE IT
;
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - PLAY
;======================================================================
;
SP_PLAY:
LD HL,(SP_PENDING_PERIOD) ; SELECT NOTE
;
LD A,$FF ; EXIT WITH ERROR
CP H ; STATUS IF INVALID
JR NZ,SP_PLAY1 ; PERIOD ($FFFF)
CP L
RET Z
SP_PLAY1:
LD E,(HL) ; LOAD 1ST ARG
INC HL ; IN DE
LD D,(HL)
INC HL
;
LD C,(HL) ; LOAD 2ND ARG
INC HL ; IN BC
LD B,(HL)
INC HL
;
; LD A,$FF ; EXIT WITH ERROR
CP B ; STATUS IF INVALID
JR NZ,SP_PLAY2 ; NOTE ($FFFF)
CP C
RET Z
;
SP_PLAY2:
PUSH BC ; SETUP ARG IN HL
POP HL
;
; CALL SP_BEEPER ; PLAY
;
; RET
;
; The following SP_BEEPER routine is a modification of code from
; "The Complete SPECTRUM ROM DISSASSEMBLY" by Dr Ian Logan & Dr Frank OHara
;
; https://www.esocop.org/docs/CompleteSpectrumROMDisassemblyThe.pdf
;
; DE Number of passes to make through the sound generation loop
; HL Loop delay parameter
;
SP_BEEPER:
PUSH IX
HB_DI ; Disable the interrupt for the duration of a 'beep'.
LD A,L ; Save L temporarily.
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.
SRL L
CPL ; Go back to the original value in L and find how many were lost by taking 3-(A mod 4).
AND $03
LD C,A
LD B,$00
LD IX,SPK_DLYADJ ; The base address of the timing loop.
ADD IX,BC ; Alter the length of the timing loop. Use an earlier starting point for each '1' lost by taking INT (L/4).
LD A,(HB_RTCVAL) ; Fetch the present border colour from BORDCR and move it to bits 2, 1 and 0 of the A register.
;
; 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.
;
SPK_DLYADJ:
NOP ; Add 4 T states for each earlier entry point that is used.
NOP
NOP
INC B ; The values in the B and C registers will come from the H and L registers - see below.
INC C
BE_H_L_LP:
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.)
JR NZ,BE_H_L_LP
LD C,$3F
DEC B
JP NZ,BE_H_L_LP
;
; The loudspeaker is now alternately activated and deactivated.
;
XOR SP_RTCIOMSK ; Flip bit 2.
OUT (RTCIO),A ; Perform the 'OUT' operation, leaving other bits unchanged.
LD B,H ; Reset the B register.
LD C,A ; Save the A register.
BIT 4,A ; Jump if at the half-cycle point.
JR NZ,BE_AGAIN
;
; After a full cycle the DE register pair is tested.
;
LD A,D ; Jump forward if the last complete pass has been made already.
OR E
JR Z,BE_END
LD A,C ; Fetch the saved value.
LD C,L ; Reset the C register.
DEC DE ; Decrease the pass counter.
JP (IX) ; Jump back to the required starting location of the loop.
;
; The parameters for the second half-cycle are set up.
;
BE_AGAIN:
LD C,L ; Reset the C register.
INC C ; Add 16 T states as this path is shorter.
JP (IX) ; Jump back.
BE_END:
HB_EI
POP IX
;
; Above flow flips the speaker bit an odd number of times which
; leaves the bit set to the opposite value it started at. This
; ensures that the bit is properly reset to its original value.
;
LD A,(HB_RTCVAL) ; Get the current RTC latch value
OUT (RTCIO),A ; Set it
;
RET ; ALWAYS EXITS WITH SUCCESS STATUS (A=0)
;
;======================================================================
; SOUND DRIVER FUNCTION - DURATION
;======================================================================
;
SP_DURATION:
LD (SP_PENDING_DURATION),HL; SET TONE PERIOD TO ZERO
XOR A
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - DEVICE
;======================================================================
;
SP_DEVICE:
LD D,SNDDEV_BITMODE ; D := DEVICE TYPE
LD E,0 ; E := PHYSICAL UNIT
LD C,$00 ; C := DEVICE TYPE
LD H,0 ; H := 0, DRIVER HAS NO MODES
LD L,RTCIO ; L := BASE I/O ADDRESS
XOR A
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - BEEP
;======================================================================
;
SP_BEEP:
LD HL,SP_NOTE_B5 ; B5 (CLOSE TO 1 KHZ)
LD (SP_PENDING_PERIOD),HL
;LD A,$FF
;LD (SP_PENDING_VOLUME),A
;XOR A
;LD (SP_PENDING_DURATION),A
;
CALL SP_PLAY ; PLAY DEFAULT NOTE
CALL SP_RESET ; RESET DRIVER
;
XOR A
RET
;
;======================================================================
;
; STANDARD ONE SECOND TONE TABLES AT 1MHZ.
; FOR SP_BEEPER ROUTINE, FIRST WORD LOADED INTO DE, SECOND INTO HL
; THE PARAMETER TO SP_TONESET IS (FREQ * 100)
; DE: FREQUENCY IN HZ
; HL: T-STATES PER HALF-CYCL E / 4 AT 1 MHZ CPU SPEED
; EX. MIDDLE C IS 261.63 HZ, 523.26 HALF CYCLES PER SECOND
; 1,000,000 / 523.26 / 4 = 477
; OR ALTERNATIVELY 12,500,000 / 26163 = 477
;
;======================================================================
;
#DEFINE SP_TONESET(SP_FREQ) .DW SP_FREQ/100, 12500000/SP_FREQ
;
SP_TUNTBL:
SP_TONESET(1635) ; C0 ; DE=16, HL=7645
SP_TONESET(1732) ; C
SP_TONESET(1835) ; D0
SP_TONESET(1945) ; D
SP_TONESET(2060) ; E0
SP_TONESET(2183) ; F0
SP_TONESET(2312) ; F
SP_TONESET(2450) ; G0
SP_TONESET(2596) ; G
SP_TONESET(2750) ; A0
SP_TONESET(2914) ; A
SP_TONESET(3087) ; B0
SP_TONESET(3270) ; C1
SP_TONESET(3465) ; C
SP_TONESET(3671) ; D1
SP_TONESET(3889) ; D
SP_TONESET(4120) ; E1
SP_TONESET(4365) ; F1
SP_TONESET(4625) ; F
SP_TONESET(4900) ; G1
SP_TONESET(5191) ; G
SP_TONESET(5500) ; A1
SP_TONESET(5827) ; A
SP_TONESET(6174) ; B1
SP_TONESET(6541) ; C2
SP_TONESET(6930) ; C
SP_TONESET(7342) ; D2
SP_TONESET(7778) ; D
SP_TONESET(8241) ; E2
SP_TONESET(8731) ; F2
SP_TONESET(9250) ; F
SP_TONESET(9800) ; G2
SP_TONESET(10383) ; G
SP_TONESET(11000) ; A2
SP_TONESET(11654) ; A
SP_TONESET(12347) ; B2
SP_TONESET(13081) ; C3
SP_TONESET(13859) ; C
SP_TONESET(14683) ; D3
SP_TONESET(15556) ; D
SP_TONESET(16481) ; E3
SP_TONESET(17461) ; F3
SP_TONESET(18500) ; F
SP_TONESET(19600) ; G3
SP_TONESET(20765) ; G
SP_TONESET(22000) ; A3
SP_TONESET(23308) ; A
SP_TONESET(24694) ; B3
SP_TONESET(26163) ; C4 ; DE=261, HL=477
SP_TONESET(27718) ; C
SP_TONESET(29366) ; D4
SP_TONESET(31113) ; D
SP_TONESET(32963) ; E4
SP_TONESET(34923) ; F4
SP_TONESET(36999) ; F
SP_TONESET(39200) ; G4
SP_TONESET(41530) ; G
SP_TONESET(44000) ; A4
SP_TONESET(46616) ; A
SP_TONESET(49388) ; B4
SP_TONESET(52325) ; C5
SP_TONESET(55437) ; C
SP_TONESET(58733) ; D5
SP_TONESET(62225) ; D
SP_TONESET(65925) ; E5
SP_TONESET(69846) ; F5
SP_TONESET(73999) ; F
SP_TONESET(78399) ; G5
SP_TONESET(83061) ; G
SP_TONESET(88000) ; A5
SP_TONESET(93233) ; A
SP_NOTE_B5:
SP_TONESET(98777) ; B5
SP_TONESET(104650) ; C6
SP_TONESET(110873) ; C
SP_TONESET(117466) ; D6
SP_TONESET(124451) ; D
SP_TONESET(131851) ; E6
SP_TONESET(139691) ; F6
SP_TONESET(147998) ; F
SP_TONESET(156798) ; G6
SP_TONESET(166122) ; G
SP_TONESET(179000) ; A6
SP_TONESET(186466) ; A
SP_TONESET(197553) ; B6
SP_TONESET(209300) ; C7
SP_TONESET(221746) ; C
SP_TONESET(234932) ; D7
SP_TONESET(248902) ; D
SP_TONESET(263702) ; E7
SP_TONESET(279383) ; F7
SP_TONESET(295996) ; F
SP_TONESET(313596) ; G7
SP_TONESET(332244) ; G
SP_TONESET(352000) ; A7
SP_TONESET(372931) ; A
SP_TONESET(395107) ; B7
SP_TONESET(418601) ; C8
SP_TONESET(443492) ; C
SP_TONESET(469863) ; D8
SP_TONESET(497803) ; D
SP_TONESET(527404) ; E8
SP_TONESET(558765) ; F8
SP_TONESET(591991) ; F
SP_TONESET(627193) ; G8
SP_TONESET(664488) ; G
SP_TONESET(704000) ; A8
SP_TONESET(745862) ; A
SP_TONESET(790213) ; B8 ; DE=7902, HL=15
;
SP_NOTCNT .EQU ($-SP_TUNTBL) / 4
;