;*****************************************************************************
;*****************************************************************************
;**                                                                         **
;**	AY-3-8910 Sound Test Program                                        **
;**	Author: Wayne Warthen -- 10/8/2017                                  **
;**                                                                         **
;*****************************************************************************
;*****************************************************************************
;
;=============================================================================
; Constants Section
;=============================================================================
;
; Hardware port addresses
;
rsel	.equ	$A4		; Register seelection port address
rdat	.equ	$A5		; Register data port address
acr	.equ	$A2		; Aux control register port address
;
; CPU speed for delay scaling
;
cpuspd	.equ	4		; CPU speed in MHz
;
; BDOS invocation constants
;
bdos	.equ	$0005		; BDOS invocation vector
print	.equ	9		; BDOS print function number
conwrt	.equ	2		; BDOS console write char
;
;=============================================================================
; Code Section
;=============================================================================
;
	.org	$100
;
	ld	(stksav),sp	; save incoming stack frame
	ld	sp,stack	; setup our private stack
;
	ld	de,banner	; load banner string address
	ld	c,print		; BDOS print function number
	call	bdos		; do it
;
	ld	a,$FF		; SCG board activation value
	out	(acr),a		; write value to ACR
;
	xor	a		; zero accum
	ld	(chan),a	; init channel number
;
chloop:
	; Test each channel
	call	tstchan		; test the current channel
	ld	hl,chan		; point to channel number
	ld	a,(chan)	; get current channel
	inc	a		; bump to next
	ld	(chan),a	; save it
	cp	3		; end of channels?
	jr	nz,chloop	; loop if not done
;
	ld	de,crlf		; newline
	ld	c,print		; BDOS print function
	call	bdos		; do it
;
	ld	sp,(stksav)	; restore stack
;
	ret			; end of program
;
tstchan:
	; Display channel being tested
	ld	de,chmsg	; point to channel message
	ld	c,print		; BDOS print function number
	call	bdos		; do it
	ld	a,(chan)	; get current channel number
	add	a,'A'		; offset to print as alpha
	ld	e,a		; put in E
	ld	c,conwrt	; BDOS console out function number
	call	bdos		; do it
	ld	de,chmsg2	; point to channel message
	ld	c,print		; BDOS print function number
	call	bdos		; do it
;
	ld	hl,0		; initial pitch value
	ld	(pitch),hl	; save it
;
	; Setup mixer register
	ld	a,(chan)	; get channel num (0-2)
	inc	a		; adjust index (1-3)
	ld	b,a		; and use as loop counter
	xor	a		; clear accum
	scf			; set carry
mixloop:
	rla			; rotate bit
	djnz	mixloop		; loop based on channel num
	cpl			; invert bits
	and	$FF		; so only target bit is cleared
	push	af		; save value
	ld	a,7		; mixer register
	out	(rsel),a	; select it
	pop	af		; recover value
	out	(rdat),a	; and set register value
;
	; Set channel volume to max
	ld	a,(chan)	; get channel
	add	a,8		; adjust for start of vol regs
	out	(rsel),a	; select register
	ld	a,$0F		; max volume
	out	(rdat),a	; write it
;
pitloop:
	; Pitch loop
	ld	a,(chan)	; get channel
	sla	a		; A := channel pitch reg, 2 bytes per chan
	out	(rsel),a	; select low byte register
	push	af		; save register
	ld	a,l		; get low byte of pitch value
	out	(rdat),a	; and write it to register
	pop	af		; recover register index
	inc	a		; inc to high byte pitch register
	out	(rsel),a	; select high byte register
	ld	a,h		; get high byte of pitch value
	out	(rdat),a	; and write it to register
;
	; Delay
	ld	b,cpuspd	; cpu speed scalar
dlyloop:
	call	dly64		; arbitrary delay
	djnz	dlyloop		; loop based on cpu speed
;
	; Next pitch value
	ld	hl,(pitch)	; get current pitch
	inc	hl		; increment
	ld	(pitch),hl	; save new value
	ld	a,h		; get high byte
	;cp	16		; end of max range?
	cp	4		; end of max range?
	jr	nz,pitloop	; loop till done
;
	; Clean up
	call	clrpsg		; shut down psg
;
	ret			; done
;
; Clear PSG registers to default
;
clrpsg:
	ld	b,16		; loop for 18 registers
	ld	c,0		; init register index
clrpsg1:
	ld	a,c		; register num to accum
	out	(rsel),a	; select it
	xor	a		; clear accum
	out	(rdat),a	; and write to register
	inc	c		; next register
	djnz	clrpsg1		; loop through all registers
	ret			; return
;
; Program PSG registers from list at HL
;
setpsg:
	ld	a,(hl)		; get psg reg number
	inc	hl		; bump index
	cp	$FF		; check for end
	ret	z		; return if end marker $FF
	out	(rsel),a	; select psg register
	ld	a,(hl)		; get register value
	inc	hl		; bump index
	out	(rdat),a	; set register value
	jr	setpsg		; loop till done
;
; Short delay functions.  No clock speed compensation, so they
; will run longer on slower systems.  The number indicates the
; number of call/ret invocations.  A single call/ret is
; 27 t-states on a z80, 25 t-states on a z180
;
dly256:	call	dly128
dly128:	call	dly64
dly64:	call	dly32
dly32:	call	dly16
dly16:	call	dly8
dly8:	call	dly4
dly4:	call	dly2
dly2:	call	dly1
dly1:	ret
;
;=============================================================================
; Data Section
;=============================================================================
;
chan	.db	0		; active audio channel
pitch	.dw	0		; current pitch
;
banner	.text	"\r\nRetroBrew Computers SCG AY-3-8910 Sound Test\r\n"
	.text	"Set SCG board base I/O address to 0x98\r\n$"
chmsg	.text	"\r\nPlaying descending tones on channel $"
chmsg2	.text	"...$"
crlf	.text	"\r\n$"
;
stksav	.dw	0		; saved stack frame
	.fill	80,$FF		; 40 level private stack
stack	.equ	$		; start of stack
;
	.end