		;;@
		;;@		C P C - A Y   M O N O & S T E R E O - S A M P L E - P L A Y E R
		;;@
		;;@		(c) 2019-2022
		;;@		Prodatron / SymbiosiS (Jörn Mika)
		;;@		Devilmarkus (Markus Hohmann)
		;;@		Arnoldemu (Kevin Thacker)
		;;@		AST (David Ast)
		;;@		OLenz (Oliver Lenz)
		;;@

		org		#8D00
begin:
		jp		start
		ds		&FD	;; Free 256 bytes incl. "jp start" for lookup table
		
		write "code.bin"
		;;write direct "a:code.bin"
		lookuptable		equ begin

		TXT_OUTPUT		equ #BB5A

start:
		;;		init conversion table for sample playback using AY-3-8912
		call		init_table

		ld		a,8
		call		set_speed2

		LD		HL,BufferRsx
		LD		BC,PtrRsx
		JP		#BCD1

BufferRsx:
		DS		4

PtrRsx:
		DW		RSX_TABLE
		JP		SetBank
		JP		playsample
		JP		playclip
		JP		setspeed
		JP		14khz
		JP		12khz
		JP		11khz
		JP		8khz
		JP		6khz
		JP		5_5khz
		JP		4khz

		JP		info_message

		jp		playmono
		jp		PlaySampled
		jp		setdriver
		jp		setmonospeed
		jp		setmonospeed
		jp		setleft
		jp		setright
		jp		setcenter
		jp		setstripes
RSX_TABLE:
		DB		"PLAYBAN","K"+#80
		DB		"PLAYSAMPL","E"+#80
		DB		"PLAYCLI","P"+#80
		DB		"PLAYSPEE","D"+#80
		DB		"FREQ.1","4"+#80
		DB		"FREQ.1","2"+#80
		DB		"FREQ.1","1"+#80
		DB		"FREQ.","8"+#80
		DB		"FREQ.","6"+#80
		DB		"FREQ.5.","5"+#80
		DB		"FREQ.","4"+#80
		DB		"PLAYERHEL","P"+#80

		defb		"PLAYMCLI","P"+&80        ;; |PLAY,<start>,<length>
		;;		Play sample using chosen driver
		;;		Sample data is 8-bit signed mono
		defb		"PLAYMON","O"+&80

		defb		"DRIVE","R"+&80    ;; |DRIVER,<index>
		;;		0 = AY
		;;		1 = digiblaster
		;;		2 = amdrum
		defb		"MONOSPEE","D"+&80
		defb		"SPEE","D"+&80
		;;		Set playback rate.
		;;
		;;		0 = 1KHz (1000hz)
		;;		1 = 1KHz (1000hz)
		;;		2 = 2Khz (2000hz)
		;;		3 = 3Khz (3000hz)
		;;		4 = 4Khz (4000hz)
		;;		5 = 5Khz (5000hz)
		;;		6 = 6Khz (6000hz)
		;;		7 = 7Khz (7000hz)
		;;		8 = 8Khz (8000hz)
		;;		9 = 9Khz (9000hz)
		;;		10 = 10Khz (10000hz)
		;;		11 = 11Khz (11025hz)
		;;		12 = 12Khz (12000hz)
		;;		13 = 13Khz (13000hz)
		;;		14 = 14Khz (14000hz)
		;;		15 = 15Khz (15000hz)
		;;		16 = 16Khz (16000hz)
		;;		17+ -> 1Khz

		defb		"LEF","T"+&80         ;; When AY driver, playback to channel A
		defb		"RIGH","T"+&80         ;; When AY driver, playback to channel B
		defb		"CENTE","R"+&80    ;; When AY driver, playback to channel C
		defb		"STRIPE","S"+&80    ;; When AY driver, playback to channel C

		DB		0

setstripes:
		cp		1
		jp		nz,PlayError ; Quit if no parameter supplied (could add error handling)

		ld		c,(ix+0)
		ld		b,(ix+1)
		ld		hl,17 ; Get the Maximum Speed Count
		sbc		hl,bc
		jp		c,SyntaxError
		ld		a,&7f
		ld		(borderstereo+2),a
		ld		(borderstereo2+2),a
		ld		(borderay+2),a
		ld		(borderdigi+2),a
		ld		(borderamdrum+2),a
		ld		a,(ix+0)
		cp		17
		jr		z,nostripes
setstripespen
		ld		(borderay+1),a
		ld		(borderdigi+1),a
		ld		(borderamdrum+1),a
		ld		(borderstereo+1),a
		ld		(borderstereo2+1),a
		ret
nostripes:
		ld		a,&1f
		ld		(borderay+2),a
		ld		(borderdigi+2),a
		ld		(borderamdrum+2),a
		ld		(borderstereo+2),a
		ld		(borderstereo2+2),a
		ld		a,0
		jr		setstripespen
info_message
		ld		a,1
		call		&bc0e
		LD		HL,MESSAGE ; Zeiger auf Fehler-Text
		CALL		MESSPRI
		RET
MESSPRI		;HL=ZEIGER             ;GIBT TEXTE AUS
		LD		A,(HL)
		OR		A
		RET		Z
		INC		HL
		CALL		TXT_OUTPUT
		JR		MESSPRI
		ret
		ds		&ac
OldStart
		jp		start ;; old code had it's entry point here, maybe users still init with CALL &9000
MESSAGE
		DB		15,3,"****************************************"
		DB		15,3,"*   ",15,2,"Stereo & Mono Sampleplayer ",164," 2022",15,3,"  *"
		DB		15,3,"* ",15,1,"Prodatron of SymbiosiS & Devilmarkus",15,3," *"
		DB		15,3,"* ",15,2,"With help from Arnoldemu, AST, OLenz",15,3," *"
		DB		15,3,"****************************************"
		DB		15,3,"  Commands:",13,10
		DB		15,1,"|PLAYERHELP - This info",13,10
		DB		15,1,"|PLAYBANK,<bank> - set RAM bank 0-256",13,10
		DB		15,1,"|STRIPES,<pen> - 0-16, 17 disables",13,10,
		DB		15,2,"Stereo section",13,10
		DB		15,1,"|PLAYSPEED,<speed> - set speed 1-255",13,10
		DB		15,1,"|FREQ.<frequency> - set accurate speed",13,10
		db		15,2,"Possible values are: 14,12,11,8,6,5.5,4",13,10
		DB		15,3,"Example: |FREQ.12 - Set player to 12khz",13,10
		DB		15,1,"|PLAYSAMPLE,<startbank>,<size>",13,10
		DB		15,2,"<size> is the number of banks to play",13,10
		DB		15,1,"|PLAYCLIP,<startaddress>,<length>",13,10
		DB		15,2,"Mono section",13,10
		DB		15,1,"|DRIVER,<d> - 0=PSG,1=DIGIBL.,2=AMDRUM",13,10
		DB		15,1,"|MONOSPEED,<speed> - Speed = khz(4-16)",13,10
		DB		15,1,"|LEFT,|CENTER,|RIGHT - PSG play channel",13,10
		DB		15,1,"|PLAYMONO,<startbank>,<size>",13,10
		DB		15,1,"|PLAYMCLIP,<startaddress>,<length>",13,10,0

SYNTAXMESSAGE
		DB		"ERROR! Bad parameters",7,13,10,0
SHORTMESSAGE
		DB		"ERROR! Samplesize too small",7,13,10,0
LARGEMESSAGE
		DB		"ERROR! Samplesize too large",7,13,10,0
ADDRESSLOW
		DB		"ERROR! Start address error, min. &4000",7,13,10,0
ADDRESSHIGH
		DB		"ERROR! Start address error, max. &7CFF",7,13,10,0
ERRORMESSAGE
		DB		"ERROR! Bank out of range",7,13,10,0
RANGEMESSAGE
		DB		"ERROR! Speed out of range",7,13,10,0
PLERRORMESSAGE
		DB		"ERROR! Command error - |PLAYERHELP for help",7,13,10,0
setspeed:
		cp		1
		jp		nz,PlayError ; Quit if no parameter supplied (could add error handling)

		ld		c,(ix+0)
		ld		b,(ix+1)
		ld		hl,255 ; Get the Maximum Speed Count
		sbc		hl,bc
		jp		c,SyntaxError

		ld		hl,0 ; Get the minimum sampleaddress
		sbc		hl,bc
		jp		nc,SyntaxError
		ld		a,(ix+0)
		ld		(speedbuffer),a
		ret
14khz:
		ld		a,1
		ld		(speedbuffer),a
		ret
12khz:
		ld		a,6
		ld		(speedbuffer),a
		ret
11khz:
		ld		a,10
		ld		(speedbuffer),a
		ret
8khz:
		ld		a,27
		ld		(speedbuffer),a
		ret
6khz:
		ld		a,47
		ld		(speedbuffer),a
		ret
5_5khz:
		ld		a,54
		ld		(speedbuffer),a
		ret
4khz:
		ld		a,89
		ld		(speedbuffer),a
		ret

SyntaxError:
		LD		HL,SYNTAXMESSAGE ; Zeiger auf Fehler-Text
		CALL		MESSPRI
		RET
TooShort:
		LD		HL,SHORTMESSAGE ; Zeiger auf Fehler-Text
		CALL		MESSPRI
		RET
TooLarge:
		LD		HL,LARGEMESSAGE ; Zeiger auf Fehler-Text
		CALL		MESSPRI
		RET
AddressTooLow:
		LD		HL,ADDRESSLOW ; Zeiger auf Fehler-Text
		CALL		MESSPRI
		RET
AddressTooHigh:
		LD		HL,ADDRESSHIGH ; Zeiger auf Fehler-Text
		CALL		MESSPRI
		RET
BankOutOfRange:
		LD		HL,ERRORMESSAGE ; Zeiger auf Fehler-Text
		CALL		MESSPRI
		RET
OutOfRange:
		LD		HL,RANGEMESSAGE ; Zeiger auf Fehler-Text
		CALL		MESSPRI
		RET
PlayError:
		LD		HL,PLERRORMESSAGE ; Zeiger auf Fehler-Text
		CALL		MESSPRI
		RET
PlaySampled
		cp		2
		jp		nz,PlayError
		ld		c,(ix+2)
		ld		b,(ix+3) ;; get lowest bank
		ld		hl,256 ; Get the Maximum Bank Count
		sbc		hl,bc
		jr		c,SyntaxError ; Quit if we're requesting an invalid bank
		ld		e,(ix+0)
		ld		d,(ix+1)  ;; get amount of banks to play
		ld		hl,256 ; Get the maximum banksize
		sbc		hl,de
		jp		c,SyntaxError
		ld		hl,&0 ; Get the minimum sampleaddress
		sbc		hl,de
		jp		nc,SyntaxError
		ld		h,b
		ld		l,c

		;;		We load &4000 as start address and also as size
SampleLoop:
		ld		(ix+0),l
		ld		(ix+1),h
		push		hl
		push		de
		call		ForceBank ;; first call of first bank
		ld		BC,&4000
		ld		(ix+0),c
		ld		(ix+1),b
		ld		(ix+2),c
		ld		(ix+3),b
		call		driver ;; Spielt den aktuellen Sample ab
		ld		a,(hl)    ;; [2]
		;		*** RETMOD ***
		cp		&FF
		jr		z,exitSample
		;		*** RETMOD ***
		pop		de
		pop		hl
		inc		hl

		call		&bb09
		CP		252
		jp		z,justexitSample

		dec		DE
		ld		a,d
		or		e
		jp		nz,SampleLoop
exitSample
		
		exx
		pop		de
		pop		hl
		exx
justexitsample:
		ei
		ret

SetBank:
		cp		1
		jp		nz,PlayError ; Quit if no parameter supplied (could add error handling)
ForceBank:
		ld		c,(ix+0)
		ld		b,(ix+1)   ; Get Bank Number Passed in Parameter
BankOffsetCalc:
		ld		hl,256 ; Get the Maximum Bank Count
		sbc		hl,bc
		jp		c,BankOutOfRange ; Quit if we're requesting an invalid bank
		LD		HL,4mb_banks  ; Point to Table
		add		hl,bc
		add		hl,bc   ; Add 2 * BC to HL to point to correct word
		ld		C,(HL)
		inc		HL
		ld		B,(HL)          ; Get Port Number

		ld		a,b
		or		c
		ret		z    ; Safety Check Quit if Zero Word Selected
		out		(C),C  ; Switch the bank in
		ret
samplesize:
		ds		2

playclip:
		cp		2
		jp		nz,PlayError
		ld		c,(ix+2)
		ld		b,(ix+3)
		ld		hl,&3fff ; Get the minimum sampleaddress
		sbc		hl,bc
		jp		nc,AddressTooLow
		ld		hl,&7D00 ; Get the maximum sampleaddress
		sbc		hl,bc
		jp		c,AddressTooHigh
		ld		c,(ix+0)
		ld		b,(ix+1)
		ld		hl,767 ; Get the minimum samplesize
		sbc		hl,bc
		jp		nc,TooShort
		ld		hl,&4001 ; Get the maximum samplesize
		sbc		hl,bc
		jp		c,TooLarge
psg2clip
		ld		hl,&4000
		ld		(psg2clip1+1),hl
		di		;lock interrupts
		exx
		ex		af,af'
		push		af
		push		bc             ;save 2nd register set
		push		de
		push		hl
		ld		bc,#f6c0         ;prepare 2nd register set
		ld		de,#0f80
		exx
		ld		(psg2clip5+1),sp   ;save stackpointer

psg2clip1
		ld		d,(ix+0)
		ld		e,(ix+1)
		scf
		ccf
		rr		D
		rr		E
		ld		l,(ix+2)
		ld		h,(ix+3)
		ld		sp,hl
		ld		bc,#f401
		ld		hl,10*256+8
psg2clip2		;*** SAMPLE LOOP
		out		(c),l           ;4      L=channel A
		exx		;1
		pop		hl              ;3      HL'=next 2 bytes
		ld		a,l              ;1      1st byte lower nibble (left channel)
		and		d               ;1      D'=15
		out		(c),c           ;4
		out		(c),0          ;4      out (c),0
		exx		;1
		out		(c),a           ;4
		exx		;1
		out		(c),e           ;4
		out		(c),0          ;4
		exx		;1 33

		out		(c),h           ;4      H=channel B
		exx		;1
		ld		a,(speedbuffer)
clippause
		dec		a
		jr		nz,clippause
		xor		a
		ld		a,l              ;1      1st byte higher nibble (right channel)
		;		*** RETMOD ***
		cp		&FF
		jp		z,psg2clip5
		;		*** RETMOD ***
		rrca:rrca:rrca:rrca		;4
		and		d               ;1
		out		(c),c           ;4
		out		(c),0          ;4
		exx		;1
		out		(c),a           ;4
		exx		;1
		out		(c),e           ;4
		out		(c),0          ;4
		exx		;1 34

		out		(c),l           ;4
		exx		;1
		ld		a,h              ;1      2nd byte lower nibble (left channel)
		and		d               ;1
		out		(c),c           ;4
		out		(c),0          ;4
		exx		;1
		out		(c),a           ;4
		exx		;1
		out		(c),e           ;4
		out		(c),0          ;4
		exx		;1 30

		out		(c),h           ;4
		exx		;1
		out		(c),c           ;4
		out		(c),0          ;4
		ld		a,h              ;1      2nd byte higher nibble (right channel)
		rrca
		rrca
		rrca
		rrca		;4
		and		d               ;1
		exx		;1
		out		(c),a           ;4
		exx		;1
		out		(c),e           ;4
		out		(c),0          ;4
		exx		;1 34

		dec		e               ;1      next samples
		jp		nz,psg2clip2       ;3 -> 33+34+30+34+4 = 135 -> 67,5nops/sample -> 14,8kHz sample rate

		ld		iy,psgcol
		ld		(ixbuffer),ix
		ld		ix,psgcolb
		exx		;1
		ex		af,af'           ;1
		ld		a,h              ;1
		and		d               ;1
		exx		;1  a'=left,a=right
borderstereo
		ld		bc,#7f01            ;2
		out		(c),c           ;4
		ld		(psgclipcol1+2),a    ;4
psgclipcol1
		ld		a,(iy+0)         ;5
		out		(c),a           ;4
		inc		c               ;1
		out		(c),c           ;4
		ex		af,af'           ;1
		ld		(psgclipcol2+2),a    ;4
psgclipcol2
		ld		a,(ix+0)         ;5
		out		(c),a           ;4
		dec		c               ;1
		;		ld b,#f4            ;2 46 / 256 = 0,18nops (insignificant)

		ld		a,#48            ;2
		ld		bc,#f782         ;3
		out		(c),c           ;4
		ld		bc,#f40e         ;3
		out		(c),c           ;4
		ld		bc,#f6c0         ;3
		out		(c),c           ;4
		out		(c),0          ;4
		ld		bc,#f792         ;3
		out		(c),c           ;4
		dec		b               ;1
		out		(c),a           ;4
		ld		b,#f4            ;2
		in		a,(c)            ;4
		ld		bc,#f782         ;3
		out		(c),c           ;4
		ld		bc,#f401         ;3
		bit		2,a             ;2

		jp		z,psg2clip5        ;2 59 / 16384 = insignificant

		ld		ix,(ixbuffer)
		dec		d
		jp		nz,psg2clip2


psg2clip5
		ld		sp,0             ;finished -> restore stack and 2nd register set
		exx
		pop		hl
		pop		de
		pop		bc
		pop		af
		ex		af,af'
		exx
		ei
		ret


playsample:
		cp		2
		jp		nz,PlayError
		ld		c,(ix+2)
		ld		b,(ix+3)
		ld		hl,256 ; Get the Maximum Bank Count
		sbc		hl,bc
		jp		c,BankOutOfRange
		ld		hl,4mb_banks
		add		hl,bc
		add		hl,bc
		ld		e,(hl)
		inc		hl
		ld		d,(hl)
		ex		de,hl
		ld		e,(ix+0)
		ld		d,(ix+1)
		push		hl
		ld		hl,256 ; Get the Maximum Bank Count
		or		a
		sbc		hl,bc
		sbc		hl,de
		jp		c,BankOutOfRange
		pop		hl
		push		de
		pop		ix
		call		psgini
		call		psg2ch
		ret


		;###		PSG2CH -> PSG Stereo sample player
		;###		Input      HL=start banking config, IX=number of 16K blocks (0-256)
		;###		Destroyed  AF,BC,DE,HL,IXL
psg2ch
		ld		(psg2ch1+1),hl
		di		;lock interrupts
		exx
		ex		af,af'
		push		af
		push		bc             ;save 2nd register set
		push		de
		push		hl
		ld		bc,#f6c0         ;prepare 2nd register set
		ld		de,#0f80
		exx
		ld		(psg2ch5+1),sp   ;save stackpointer

psg2ch1
		ld		bc,0                 ;*** BANKING LOOP
		out		(c),c
		ld		sp,#4000
		ld		de,32*256        ;32*256=8192 16bit loops (=16384 8bit samples)
		ld		bc,#f401
		ld		hl,10*256+8
psg2ch2		;*** SAMPLE LOOP
		out		(c),l           ;4      L=channel A
		exx		;1
		pop		hl              ;3      HL'=next 2 bytes
		ld		a,l              ;1      1st byte lower nibble (left channel)
		and		d               ;1      D'=15
		out		(c),c           ;4
		out		(c),0          ;4      out (c),0
		exx		;1
		out		(c),a           ;4
		exx		;1
		out		(c),e           ;4
		out		(c),0          ;4
		exx		;1 33

		out		(c),h           ;4      H=channel B
		exx		;1
		ld		a,(speedbuffer)
looppause
		dec		a
		jr		nz,looppause
		xor		a
		ld		a,l              ;1      1st byte higher nibble (right channel)
		;		*** RETMOD ***
		cp		&FF
		jp		z,psg2ch5
		;		*** RETMOD ***
		rrca:rrca:rrca:rrca		;4
		and		d               ;1
		out		(c),c           ;4
		out		(c),0          ;4
		exx		;1
		out		(c),a           ;4
		exx		;1
		out		(c),e           ;4
		out		(c),0          ;4
		exx		;1 34

		out		(c),l           ;4
		exx		;1
		ld		a,h              ;1      2nd byte lower nibble (left channel)
		and		d               ;1
		out		(c),c           ;4
		out		(c),0          ;4
		exx		;1
		out		(c),a           ;4
		exx		;1
		out		(c),e           ;4
		out		(c),0          ;4
		exx		;1 30

		out		(c),h           ;4
		exx		;1
		out		(c),c           ;4
		out		(c),0          ;4
		ld		a,h              ;1      2nd byte higher nibble (right channel)
		rrca
		rrca
		rrca
		rrca		;4
		and		d               ;1
		exx		;1
		out		(c),a           ;4
		exx		;1
		out		(c),e           ;4
		out		(c),0          ;4
		exx		;1 34

		dec		e               ;1      next samples
		jp		nz,psg2ch2       ;3 -> 33+34+30+34+4 = 135 -> 67,5nops/sample -> 14,8kHz sample rate

		ld		iy,psgcol
		ld		(ixbuffer),ix
		ld		ix,psgcolb
		exx		;1
		ex		af,af'           ;1
		ld		a,h              ;1
		and		d               ;1
		exx		;1  a'=left,a=right

borderstereo2
		ld		bc,#7f01            ;2
		out		(c),c           ;4
		ld		(psgcol1+2),a    ;4
psgcol1
		ld		a,(iy+0)         ;5
		out		(c),a           ;4
		inc		c               ;1
		out		(c),c           ;4
		ex		af,af'           ;1
		ld		(psgcol2+2),a    ;4
psgcol2
		ld		a,(ix+0)         ;5
		out		(c),a           ;4
		dec		c               ;1
		;		ld b,#f4            ;2 46 / 256 = 0,18nops (insignificant)

		ld		a,#48            ;2
		ld		bc,#f782         ;3
		out		(c),c           ;4
		ld		bc,#f40e         ;3
		out		(c),c           ;4
		ld		bc,#f6c0         ;3
		out		(c),c           ;4
		out		(c),0          ;4
		ld		bc,#f792         ;3
		out		(c),c           ;4
		dec		b               ;1
		out		(c),a           ;4
		ld		b,#f4            ;2
		in		a,(c)            ;4
		ld		bc,#f782         ;3
		out		(c),c           ;4
		ld		bc,#f401         ;3
		bit		2,a             ;2

		jp		z,psg2ch5        ;2 59 / 16384 = insignificant

		ld		ix,(ixbuffer)
		dec		d
		jp		nz,psg2ch2

		dec		lx        ;next 16K memory block
		jr		z,psg2ch5
		ld		hl,psg2ch1+1
		ld		a,(hl)
		cp		#c0
		jp		nz,notc0
		ld		a,#c3
notc0
		inc		a
		jp		z,psg2ch4
		bit		2,a
		jp		nz,psg2ch3
		add		4               ;next 64K memory bank
psg2ch3
		ld		(hl),a

		jp		psg2ch1
psg2ch4
		ld		(hl),#c4         ;next 512K memory segment
		inc		hl
		dec		(hl)
		jp		psg2ch1

psg2ch5
		ld		sp,0             ;finished -> restore stack and 2nd register set
		exx
		pop		hl
		pop		de
		pop		bc
		pop		af
		ex		af,af'
		exx
		ei
		ret


		;###		PSGREG -> sets PSG register
		;###		Input      A=register, E=value
		;###		Destroyed  BC
psgreg
		ld		b,#f4
		out		(c),a
		ld		bc,#f6c0
		out		(c),c
		out		(c),0
		ld		b,#f4
		out		(c),e
		ld		bc,#f680
		out		(c),c
		out		(c),0
		ret

		;###		PSGINI -> Resets PSG
		;###		Destroyed  AF,BC,E
psgini
		xor		a
		ld		e,a
psgini1
		call		psgreg
		inc		a
		cp		13
		jr		c,psgini1
		ld		a,7
		ld		e,#3f
		jp		psgreg

		;###		PSGCOL -> Color table for left/right channel VU meter
psgcol
		;		0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
		;		100                                               0  33  66 100
		db		#4b,#4b,#5b,#5b,#53,#53,#57,#5f,#55,#44,#54,#55,#5b,#4b,#4b,#4b  ;blue
psgcolb
		;		0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
		;		100                                               0  33  66 100
		db		#4b,#4b,#43,#4a,#4e,#4e,#4d,#45,#4c,#5c,#54,#4c,#4a,#4b,#4b,#4b  ;blue

4mb_banks:

		dw		&7FC0, &7FC4, &7FC5, &7FC6, &7FC7, &7FCC, &7FCD, &7FCE, &7FCF, &7FD4
		dw		&7FD5, &7FD6, &7FD7, &7FDC, &7FDD, &7FDE, &7FDF, &7FE4, &7FE5, &7FE6
		dw		&7FE7, &7FEC, &7FED, &7FEE, &7FEF, &7FF4, &7FF5, &7FF6, &7FF7, &7FFC
		dw		&7FFD, &7FFE, &7FFF, &7EC4, &7EC5, &7EC6, &7EC7, &7ECC, &7ECD, &7ECE
		dw		&7ECF, &7ED4, &7ED5, &7ED6, &7ED7, &7EDC, &7EDD, &7EDE, &7EDF, &7EE4
		dw		&7EE5, &7EE6, &7EE7, &7EEC, &7EED, &7EEE, &7EEF, &7EF4, &7EF5, &7EF6
		dw		&7EF7, &7EFC, &7EFD, &7EFE, &7EFF, &7DC4, &7DC5, &7DC6, &7DC7, &7DCC
		dw		&7DCD, &7DCE, &7DCF, &7DD4, &7DD5, &7DD6, &7DD7, &7DDC, &7DDD, &7DDE
		dw		&7DDF, &7DE4, &7DE5, &7DE6, &7DE7, &7DEC, &7DED, &7DEE, &7DEF, &7DF4
		dw		&7DF5, &7DF6, &7DF7, &7DFC, &7DFD, &7DFE, &7DFF, &7CC4, &7CC5, &7CC6
		dw		&7CC7, &7CCC, &7CCD, &7CCE, &7CCF, &7CD4, &7CD5, &7CD6, &7CD7, &7CDC
		dw		&7CDD, &7CDE, &7CDF, &7CE4, &7CE5, &7CE6, &7CE7, &7CEC, &7CED, &7CEE
		dw		&7CEF, &7CF4, &7CF5, &7CF6, &7CF7, &7CFC, &7CFD, &7CFE, &7CFF, &7BC4
		dw		&7BC5, &7BC6, &7BC7, &7BCC, &7BCD, &7BCE, &7BCF, &7BD4, &7BD5, &7BD6
		dw		&7BD7, &7BDC, &7BDD, &7BDE, &7BDF, &7BE4, &7BE5, &7BE6, &7BE7, &7BEC
		dw		&7BED, &7BEE, &7BEF, &7BF4, &7BF5, &7BF6, &7BF7, &7BFC, &7BFD, &7BFE
		dw		&7BFF, &7AC4, &7AC5, &7AC6, &7AC7, &7ACC, &7ACD, &7ACE, &7ACF, &7AD4
		dw		&7AD5, &7AD6, &7AD7, &7ADC, &7ADD, &7ADE, &7ADF, &7AE4, &7AE5, &7AE6
		dw		&7AE7, &7AEC, &7AED, &7AEE, &7AEF, &7AF4, &7AF5, &7AF6, &7AF7, &7AFC
		dw		&7AFD, &7AFE, &7AFF, &79C4, &79C5, &79C6, &79C7, &79CC, &79CD, &79CE
		dw		&79CF, &79D4, &79D5, &79D6, &79D7, &79DC, &79DD, &79DE, &79DF, &79E4
		dw		&79E5, &79E6, &79E7, &79EC, &79ED, &79EE, &79EF, &79F4, &79F5, &79F6
		dw		&79F7, &79FC, &79FD, &79FE, &79FF, &78C4, &78C5, &78C6, &78C7, &78CC
		dw		&78CD, &78CE, &78CF, &78D4, &78D5, &78D6, &78D7, &78DC, &78DD, &78DE
		dw		&78DF, &78E4, &78E5, &78E6, &78E7, &78EC, &78ED, &78EE, &78EF, &78F4
		dw		&78F5, &78F6, &78F7, &78FC, &78FD, &78FE, &78FF
		DW		0 ; Signify End of list DO NOT REMOVE

		;;		channel definition for ay-playback
channel:
		db		9

setleft:
		ld		a,8
		ld		(channel),a
		ret

setcenter:
		ld		a,9
		ld		(channel),a
		ret

setright:
		ld		a,10
		ld		(channel),a
		ret

		;;		set playback driver
setdriver:
		ld		a,(ix+0)
		or		a
		ld		hl,play_sample_ay
		jr		z,setdriver2
		dec		a
		ld		hl,play_sample_digi
		jr		z,setdriver2
		ld		hl,play_sample_amdrum
setdriver2:
		ld		(driver+1),hl
		ret

		;;-------------------------------------------------------------------------------

		;;		this is the time for CALL: RET combination
		time_call		equ 5+3

		;;		these are the timings for the bare code to write to the hardware
		;;		we use these to modify the timings
		sample_digi_time		equ 2+4+25+5+2+1+1+1+3+time_call
		sample_amdrum_time		equ 2+2+25+2+2+4+2+1+1+3+2+time_call
		sample_ay_time		equ 61+time_call

setmonospeed:
		ld		a,(ix+0)

set_speed2:
		cp		17
		jr		c,set_speed3
		xor		a
set_speed3:
		ld		l,a
		ld		h,0
		add		hl,hl
		ld		de,speed_table
		add		hl,de
		ld		e,(hl)
		inc		hl
		ld		d,(hl)

		ld		bc,sample_digi_time
		call		calc_speed
		ld		(speed_digi+1),hl

		ld		bc,sample_amdrum_time
		call		calc_speed
		ld		(speed_amdrum+1),hl

		ld		bc,sample_ay_time
		call		calc_speed
		ld		(speed_ay+1),hl
		ret


		;;-------------------------------------------------------------------------------
		;;		de = timing value from speed_table
		;;		bc = timing value for instructions to update hardware for playback
calc_speed:
		ld		l,e
		ld		h,d
		or		a
		sbc		hl,bc
		;;		HL = delay we require (including CALL/RET)

		;;		calculate address from end of delay so we have the correct number of NOPs
		;;		until RET for this delay
		ld		c,l
		ld		b,h
		ld		hl,end_speed_delay
		or		a
		sbc		hl,bc

		ret

		;;-------------------------------------------------------------------------------
		;;		Calculating the timings:
		;;
		;;		Hz = number of samples played per second
		;;		1,000,000 "NOP cycles" processed by Z80 per second.
		;;		A "NOP cycle" is a timing unit within the Amstrad enforced by the video hardware.
		;;		So to playback at the rate we want
		;;		we need to update the sound hardware at a rate defined in the table below.
		;;		These values don't take into account the actual time needed to read the sample,
		;;		convert it it required, write to hardware, and then update playback values
		;;
		;;		Cycles = 1,000,000/Hz

speed_table:
		defw		1000        ;; 1KHz (1000hz)
		defw		1000         ;; 1KHz (1000hz)
		defw		500         ;; 2Khz (2000hz)
		defw		333        ;; 3Khz (3000hz)
		defw		250          ;; 4Khz (4000hz)
		defw		200        ;; 5Khz (5000hz)
		defw		167        ;; 6Khz (6000hz)
		defw		143         ;; 7Khz (7000hz)
		defw		125        ;; 8Khz (8000hz)
		defw		111        ;; 9Khz (9000hz)
		defw		100        ;; 10Khz (10000hz)
		defw		91         ;; 11Khz (11025hz)
		defw		83         ;; 12Khz (12000hz)
		defw		77         ;; 13Khz (13000hz)
		defw		71         ;; 14Khz (14000hz)
		defw		67         ;; 15Khz (15000hz)
		defw		63         ;; 16Khz (16000hz)

		;;-------------------------------------------------------------------------------
		;;		The speed is  controlled by a software delay.
		;;
		;;		This part of the code has 1000 NOP instructions, enough for a playback rate of 1khz.
		;;		But we have the accuracy of finer control, so we could in theory have other rates than we have defined.
		;;		The fastest rate that the CPC can playback is around 18Khz, any faster and the CPC can't write to the hardware
		;;		fast enough.
		;;
		;;		Using the speed_table and the number of cycles that is needed to update the
		;;		hardware we calculate an address within this code here. The code CALLs to the calculated address
		;;		then the nops are executed until RET is reached, and then this returns back to the
		;;		playback code.

speed_delay:
		defs		1000
end_speed_delay:
		ret

		;;-------------------------------------------------------------------------------
		;;		this is the main RSX entry point for driver playback

playmono:
		cp		2
		ret		nz
driver:
		jp		play_sample_ay

		;;-------------------------------------------------------------------------------

play_sample_digi:
		;;		This is the RSX function for the Digiblaster "driver"

		ld		e,(ix+0)
		ld		d,(ix+1)
		ld		l,(ix+2)
		ld		h,(ix+3)
		;;		DE = length in samples
		;;		HL = start address of sample data

		;;		Play a sample using the Digiblaster/SoundPlayer
		;;
		;;		This device plays 8-bit mono signed samples.
		;;
		;;		This device is connected to the printer port on the CPC.
		;;		The recommended port number for this is &efxx.
		;;		xx = any value
		;;

		;;		disable interrupts. This stops interrupts from breaking our timing
		;;		and breaking the sound.
		di

		;;		OUTI decrements B before sending data to I/O port.
		;;		So B = &ef+1
		ld		b,&f0

		digiloop:
		;;		This performs the same as:
		;;		DEC B
		;;		LD A,(HL)
		;;		OUT (C),A
		;;		INC HL
		;;
		;;		but without effecting A register and being much quicker
		outi		;; [5]
		ld		a,(hl)    ;; [2]
		;		*** RETMOD ***
		cp		&FF
		jr		z,gooutdigi
		;		*** RETMOD ***
		push		BC
borderdigi:
		LD		BC,#7F10
		OUT		(C),C
		rlca
		rlca
		and		#2
		add		21
		OR		#40
		LD		B,#7F
		OUT		(C),a
		pop		BC
		;;		restore B back for next iteration of loop
		inc		b  ;; [1]

		;;		this CALL is modified based on the playback rate chosen
speed_digi:
		call		0

		;;		update number of samples remaining to play
		dec		de  ;; [2]
		ld		a,d  ;; [1]
		or		e  ;; [1]
		jp		nz,digiloop ;; [3]
gooutdigi:
		;;		re-enable interrupts again

		ei

		;;		return back to basic
		ret

		;;-------------------------------------------------------------------------------

play_sample_amdrum:
		;;		This is the RSX function for the Amdrum "driver"

		ld		e,(ix+0)
		ld		d,(ix+1)
		ld		l,(ix+2)
		ld		h,(ix+3)
		;;		DE = length in samples
		;;		HL = start address of sample data

		;;		Play a sample using the Amdrum
		;;
		;;		This device plays 8-bit mono unsigned samples.
		;;
		;;		The recommended port number for this is &ffxx.
		;;		xx = any value

		;;		disable interrupts. This stops interrupts from breaking our timing
		;;		and breaking the sound.
		di

		ld		b,&ff

amdrumloop:
		;;		read sample byte
		ld		a,(hl)    ;; [2]

		;		*** RETMOD ***
		cp		&FF
		jr		z,gooutamdrum
		;		*** RETMOD ***
		;;		convert from signed to unsigned
		xor		&80     ;; [2]
		;;		write to amdrum
		out		(c),a    ;; [4]
		push		BC
borderamdrum:
		LD		BC,#7F10
		OUT		(C),C
		rlca
		rlca
		and		#2
		add		8
		OR		#40
		LD		B,#7F
		OUT		(C),a
		pop		BC
		;;		update sample pointer
		inc		hl     ;; [2]

		;;		this CALL is modified based on the playback rate chosen
speed_amdrum:
		call		0

		;;		update number of bytes remaining to play
		dec		de    ;; [2]
		ld		a,d    ;; [1]
		or		e    ;; [1]
		jp		nz,amdrumloop ;; [3]
gooutamdrum
		ei

		;;		return back to BASIC
		ret

		;;-------------------------------------------------------------------------------

play_sample_ay:
		ld		l,(ix+2)
		ld		h,(ix+3)
		di

		;;		store de' for basic
		exx
		push		de
		ld		e,(ix+0)
		ld		d,(ix+1)
		exx

		;;		turn off noise
		ld		e,7
		ld		d,&3f
		call		write_ay_reg
		ld		e,6
		ld		d,0
		call		write_ay_reg


		ld		e,0
		ld		d,0
		call		write_ay_reg
		;
		ld		e,1
		ld		d,0
		call		write_ay_reg

		ld		e,2
		ld		d,0
		call		write_ay_reg
		;
		ld		e,3
		ld		d,0
		call		write_ay_reg
		;
		ld		e,4
		ld		d,0
		call		write_ay_reg
		;
		ld		e,5
		ld		d,0
		call		write_ay_reg
		;
		ld		e,12
		ld		d,0
		call		write_ay_reg
		;
		ld		e,11
		ld		d,0
		call		write_ay_reg
		;
		;

		ld		e,6
		ld		d,0
		call		write_ay_reg
		ld		e,8
		ld		d,0
		call		write_ay_reg
		ld		e,9
		ld		d,0
		call		write_ay_reg
		ld		e,10
		ld		d,0
		call		write_ay_reg



		;;		select ay register b
		;		ld  bc,&f400
		;		ld  a,(channel)
		;		out  (c),a
		;		ld  bc,&f6c0
		;		out  (c),c
		;		ld  bc,&f600
		;		out  (c),c

		;;		get ready to write it
		;		ld  bc,&f680
		;		out  (c),c


		ld		a,(channel)
		ld		bc,#f4c0
		out		(c),a
		ld		b,#F6
		out		(c),c
		dw		#71ed
		;		ld  bc,&f400
		ld		d, lookuptable /256

		;;		19968 nops per frame
		;;		50 frames per second
		;;		998400 nops per second
		;;		12000 samples per second
		;;		83.2 nops per sample

loop2:
		;;		convert 8-bit sample to 4-bit sample
		ld		e,(hl) ;; [2]
		ld		a,e
		;		*** RETMOD ***
		cp		&FF
		jr		z,exitay
		;		*** RETMOD ***
		ld		a,(de) ;; [2]

		;
		ld		b,#f4 ; 2
		;
		out		(c),a ;; [4]
		;
		ld		bc,&f680 ; 3
		out		(c),c ; 4
		dw		#71ed ; 4

		;

		push		bc ; 4
borderay
		LD		BC,#7F10 ; 3
		OUT		(C),C ; 4
		rlca		;1
		rlca		;1
		and		#07 ;2
		add		2 ;2
		OR		#40 ;2
		LD		B,#7F ;2
		OUT		(C),a ;4
		pop		BC ; 3

		inc		hl  ;; [2]

speed_ay:
		call		0
		exx		;; [1]
		dec		de  ;; [2]
		ld		a,d  ;; [1]
		or		e  ;; [1]
		exx		;; [1]
		jp		nz,loop2 ;; [3]
exitay
		;;		restore de' for basic
		exx
		pop		de
		exx
		ei
		RET

storedExit:
		db		0

write_ay_reg:
		ld		bc,&f400
		out		(c),e
		ld		bc,&f6c0
		out		(c),c
		ld		c,&00
		out		(c),c
		ld		bc,&f400
		out		(c),d
		ld		bc,&f680
		out		(c),c
		ld		c,0
		out		(c),c
		ret


		init_table:
		ld		hl,lookuptable
		ld		d,0
		ld		b,0
		init_tab2:
		ld		a,d
		cp		2
		ld		c,0
		jr		c,init_tab3

		cp		5
		ld		c,1
		jr		c,init_tab3

		cp		7
		ld		c,2
		jr		c,init_tab3

		cp		10
		ld		c,3
		jr		c,init_tab3

		cp		14
		ld		c,4
		jr		c,init_tab3

		cp		19
		ld		c,5
		jr		c,init_tab3

		cp		29
		ld		c,6
		jr		c,init_tab3

		cp		40
		ld		c,7
		jr		c,init_tab3

		cp		56
		ld		c,8
		jr		c,init_tab3

		cp		80
		ld		c,9
		jr		c,init_tab3

		cp		103
		ld		c,10
		jr		c,init_tab3

		cp		131
		ld		c,11
		jr		c,init_tab3

		cp		161
		ld		c,12
		jr		c,init_tab3

		cp		197
		ld		c,13
		jr		c,init_tab3

		cp		236
		ld		c,14
		jr		c,init_tab3

		ld		c,15

		init_tab3:
		;;		8-bit signed to 8-bit unsigned
		xor		&80
		ld		l,a
		;;		L = low byte of address in table
		;;		but also effectively the sample value
		;;		write into table
		;;		C = value for AY
		ld		(hl),c
		inc		d
		djnz		init_tab2
		ret

ixbuffer:
		ds		2
speedbuffer:
		db		1
