You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

380 lines
9.6 KiB

;======================================================================
;
; BIT MODE SOUND DRIVER FOR SBC V2 USING BIT 0 OF RTC DRIVER
;
;======================================================================
;
; 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
;
#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_PENDING_PERIOD .DW SP_NOTE_C8 ; PENDING PERIOD (16 BITS)
SP_PENDING_VOLUME .DB $FF ; PENDING VOL (8 BITS)
;
;======================================================================
; 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_PLAY ; PLAY DEFAULT NOTE
XOR A
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - RESET
;======================================================================
;
SP_RESET:
; 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:
PUSH HL
PUSH DE ; ON ENTRY L IS A NOTE INDEX
LD H,0 ; CONVERT THIS NOTE INDEX
ADD HL,HL ; TO THE ASSOCIATED ENTRY
ADD HL,HL ; IN THE TUNE ABLE.
LD DE,SP_TUNTBL ; SAVE THIS ADDRESS AS
ADD HL,DE ; THE PERIOD
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, BF_SND_BITMODE ; RETURN DEVICE IDENTIFIER
LD DE, 0 ; AND ADDRESS AND DATA PORT
XOR A
RET
;
;======================================================================
; INITIALIZE THE TONE TABLE
;======================================================================
;
SP_SETTBL:
LD A,(CB_CPUMHZ) ; GET CPU SPEED.
LD C,A
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
SP_SETTBL1: ; 1MHZ NOTE
ADD HL,DE ; VALUE BY
DJNZ SP_SETTBL1 ; SYSTEM MHZ
POP BC
;
LD DE,30 ; 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
RET
;
;======================================================================
; SOUND DRIVER FUNCTION - PLAY
;======================================================================
;
SP_PLAY:
LD HL,(SP_PENDING_PERIOD) ; SELECT NOTE
;
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
;
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 O’Hara
;
; 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
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,(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 %00000100 ; 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:
EI
POP IX
RET
;
; STANDARD ONE SECOND TONE TABLES AT 1MHZ (UNCOMPENSATED). FOR SP_BEEPER, FIRST WORD LOADED INTO DE, SECOND INTO HL
;
; EXCEL SPREADSHEET FOR CALCULATION CAN BE FOUND HERE:
;
; https://www.retrobrewcomputers.org/lib/exe/fetch.php?media=boards:sbc:sbc_v2:sbc_v2-004:spk_beep_tuntbl.xlsx
;
SP_TUNTBL:
.DW $13, $191A ; D
.DW $14, $17B3 ; E0
.DW $15, $165E ; F0
.DW $17, $151E ; F
.DW $18, $13EE ; G0
.DW $19, $12CF ; G
.DW $1B, $11C1 ; A0
.DW $1D, $10C1 ; A
.DW $1E, $FD1 ; B0
.DW $20, $EEE ; C1
.DW $22, $E17 ; C
.DW $24, $D4D ; D1
.DW $26, $C8E ; D
.DW $29, $BD9 ; E1
.DW $2B, $B2F ; F1
.DW $2E, $A8E ; F
.DW $31, $9F7 ; G1
.DW $33, $968 ; G
.DW $37, $8E0 ; A1
.DW $3A, $861 ; A
.DW $3D, $7E8 ; B1
.DW $41, $777 ; C2
.DW $45, $70B ; C
.DW $49, $6A6 ; D2
.DW $4D, $647 ; D
.DW $52, $5EC ; E2
.DW $57, $597 ; F2
.DW $5C, $547 ; F
.DW $62, $4FB ; G2
.DW $67, $4B3 ; G
.DW $6E, $470 ; A2
.DW $74, $430 ; A
.DW $7B, $3F4 ; B2
.DW $82, $3BB ; C3
.DW $8A, $385 ; C
.DW $92, $353 ; D3
.DW $9B, $323 ; D
.DW $A4, $2F6 ; E3
.DW $AE, $2CB ; F3
.DW $B9, $2A3 ; F
.DW $C4, $27D ; G3
.DW $CF, $259 ; G
.DW $DC, $238 ; A3
.DW $E9, $218 ; A
.DW $F6, $1FA ; B3
.DW $105, $1DD ; C4
.DW $115, $1C2 ; C
.DW $125, $1A9 ; D4
.DW $137, $191 ; D
.DW $149, $17B ; E4
.DW $15D, $165 ; F4
.DW $171, $151 ; F
.DW $188, $13E ; G4
.DW $19F, $12C ; G
.DW $1B8, $11C ; A4
.DW $1D2, $10C ; A
.DW $1ED, $FD ; B4
.DW $20B, $EE ; C5
.DW $22A, $E1 ; C
.DW $24B, $D4 ; D5
.DW $26E, $C8 ; D
.DW $293, $BD ; E5
.DW $2BA, $B2 ; F5
.DW $2E3, $A8 ; F
.DW $30F, $9F ; G5
.DW $33E, $96 ; G
.DW $370, $8E ; A5
.DW $3A4, $86 ; A
.DW $3DB, $7E ; B5
.DW $416, $77 ; C6
.DW $454, $70 ; C
.DW $496, $6A ; D6
.DW $4DC, $64 ; D
.DW $526, $5E ; E6
.DW $574, $59 ; F6
.DW $5C7, $54 ; F
.DW $61F, $4F ; G6
.DW $67D, $4B ; G
.DW $6E0, $47 ; A6
.DW $748, $43 ; A
.DW $7B7, $3F ; B6
.DW $82D, $3B ; C7
.DW $8A9, $38 ; C
.DW $92D, $35 ; D7
.DW $9B9, $32 ; D
.DW $A4D, $2F ; E7
.DW $AE9, $2C ; F7
.DW $B8F, $2A ; F
.DW $C3F, $27 ; G7
.DW $CFA, $25 ; G
.DW $DC0, $23 ; A7
.DW $E91, $21 ; A
.DW $F6F, $1F ; B7
SP_NOTE_C8:
.DW $105A, $1D ; C8
.DW $1152, $1C ; C
.DW $125A, $1A ; D8
.DW $1372, $19 ; D
.DW $149A, $17 ; E8
.DW $15D3, $16 ; F8
.DW $171F, $15 ; F
.DW $187F, $13 ; G8
.DW $19F4, $12 ; G
.DW $1B80, $11 ; A8
.DW $1D22, $10 ; A
.DW $1EDE, $F ; B8
;
SP_NOTCNT .EQU ($-SP_TUNTBL-1) / 4
;