;---------------T

******** Z80 Emul AY Player, (C) 1994-1997 Patrik Rak - Raxoft ********
******************** Part of PlayAY distribution **********************

* 1.0	original release
* 1.1	added support for AMSTRAD (I think)
* 1.2	first 256 bytes are filled with 201 (so JP 56 and like is possible)
*	(except the launch sequence, of course)
* 1.2a	fixed behavior of AMSTRAD CPC port handling

AMSTRAD	equ	1	;emulate also AMSTRAD CPC peripherals

COLOROUT	equ	0	;map border (out 254) as $dff180
DEBUG	equ	0

	ifne	DEBUG
	bsr.w	initplayer
	tst.l	d0
	bne.b	error
	lea	test(pc),a0
	bsr.w	initsound
	bsr.w	interrupt
	bsr.w	endsound
	bsr.w	endplayer
error	rts

test	dc.b	0,1,2,3
	dc.w	1000
	dc.w	100
	dc.w	$1234
	dc.w	testl-*
	dc.w	testb-*
testl	dc.w	40000	;stack
	dc.w	0	;init
	dc.w	49152	;interrupt
testb	dc.w	32768
	dc.w	1
	dc.w	testc1-*
	dc.w	49152
	dc.w	9
	dc.w	testc2-*
	dc.w	0
testc2	dc.b	62,0		;ld a,0
	dc.b	61		;dec a
	dc.b	50,1,$c0	;ld ($c001),a
	dc.b	211,254		;out (254),a
testc1	dc.b	$ed,0,201		;bug,rts
	even
	endc

**** Include stuff ****

	incdir	include:
	include exec/funcdef.i
	include	exec/exec_lib.i
	include	exec/execbase.i
	include	exec/memory.i
	include	dos/dos_lib.i
	include	dos/dos.i
	include	dos/var.i
	include	dos/dostags.i
	incdir 	deliay:dist/dev/src/include40/
	include	misc/AYPlayer.i
	include	misc/mine.i
	incdir	deliay:
	include	z80.i
	incdir	''

***** How single EMUL_Song looks like ******

	STRUCTURE	EMUL_Song,0
	UBYTE	ems_assignA	;0-3 specifies, what amiga channel will be
	UBYTE	ems_assignB	;assigned to each ay channel (A-C, noise)
	UBYTE	ems_assignC
	UBYTE	ems_assignN
	UWORD	ems_songlen	;how long the song is in VBIs
	UWORD	ems_fadelen	;how many VBIs to fade
	UWORD	ems_initnumber	;this will be stored to HL,DE,BC,IX,IY,A (MSB)
				;and all ' registers before calling ems_init
	RPTR	ems_launch	;Allows same Launch with different initnumbers
	RPTR	ems_firstblock	;which block(s) install to z80 RAM.
	LABEL	ems_sizeof

	STRUCTURE EMUL_Launch,0
	UWORD	eml_stack	;what will be loaded into SP register
	UWORD	eml_init	;Routine which set ups the player.
				;if zero, use ems_firstblock->emd_address
				;or use $1b just for calling ret instruction
	UWORD	eml_interrupt	;to what address jump to do one VBI play
				;if zero, (I*$100+$FF) will be used
	LABEL	eml_sizeof

	STRUCTURE EMUL_Block,0
	UWORD	emd_address	;or null for last block in table
	UWORD	emd_length	;simply said: take block at emd_binary
	RPTR	emd_binary	;emd_length bytes long and install it in Z80
	LABEL	emd_sizeof	;RAM at emd_address

**** Real start **********

dd	AYPLAYERHEADER	EMUL
	dc.b	0		;no transpose + terminator for GetVar
AYbase	ds.l	1		;where ay registers should be "outed"
AYass	ds.l	1		;where ay channel assignment should be copied
AYfreq	ds.l	1		;we ignore this anyway
	dc.w	initplayer-*
	dc.w	endplayer-*
	dc.w	initsound-*
	dc.w	endsound-*
	dc.w	interrupt-*
	dc.w	0,0		;no patterns
name	dc.b	'Z80 Emul Player 1.2a',0
	dc.b	'Unknown - original Z80 code is running',0
	dc.b	'(C) 1994-1997 Patrik Rak - Raxoft',0

dosn	dc.b	'dos.library',0
	even

;Allocate memory for RAM and Emulator, and install both. Report failure
;in d0 (0=OK)

initplayer

	exec	a6
	lea	b(pc),a5
	moveq	#37,d0
	lea	dosn(pc),a1
	jsrlib	OpenLibrary
	move.l	d0,dosb-b(a5)
	beq.b	endplayer

	moveq	#-8,d4	;8 byte pod romkou jako rezerva na stack
	swap	d4	;jeden staci, ale nejaky potize s allocabs...
	move.l	#zxsize,d2
	bsr.w	Alloc64k
	beq.b	endplayer
	addq.l	#8,d0
	move.l	d0,zxbase-b(a5)

	moveq	#0,d4
	move.l	#emullen,d2
	bsr.w	Alloc64k
	beq.b	endplayer
	move.l	d0,emulbase-b(a5)

	move.l	d0,a4
	move.l	d0,a1
	lea	emulator(pc),a0
	move.l	#emullen,d0
	jsrlib	CopyMem
	jsrlib	CacheClearU		;Flush the Caches

	btst	#AFB_68010,AttnFlags+1(a6)
	beq.b	.keepmovesrdx
	jsr	z80_MODE68010(a4)
	jsrlib	CacheClearU		;flush caches
.keepmovesrdx
	lea	z80_out(a4),a1
	lea	out(pc),a0
	move.l	a0,(a1)+	;out
	lea	return(pc),a0
	move.l	a0,(a1)+	;in
	move.l	a0,(a1)+	;ldir delay
	move.l	a0,(a1)		;lddr delay

	moveq	#0,d0
return	rts

;Free the memory.

endplayer
	exec	a6
	lea	b(pc),a5

	move.l	zxbase(pc),d0
	beq.b	.nozx
	subq.l	#8,d0
	move.l	d0,a1
	move.l	#zxsize,d0
	jsrlib	FreeMem
	clr.l	zxbase-b(a5)
.nozx
	move.l	emulbase(pc),d0
	beq.b	.noz80
	move.l	d0,a1
	move.l	#emullen,d0
	jsrlib	FreeMem
	clr.l	emulbase-b(a5)
.noz80
	move.l	dosb(pc),d0
	beq.b	.nodos
	move.l	d0,a1
	jsrlib	CloseLibrary
	clr.l	dosb-b(a5)
.nodos	moveq	#-1,d0
	rts

;Install channel asignement
;Install data to the RAM
;then run the emulator

initsound
	exec	a6
	lea	b(pc),a5

	move.l	AYass,a1
	move.l	(a0)+,(a1)	;assignement
	
	move.l	(a0)+,(a5)+	;songlen+fadelen
	
	clr.l	(a5)+	;ay_selected(2)+ay_regs(14)=16 bytes
	clr.l	(a5)+
	clr.l	(a5)+
	clr.l	(a5)+

	move.l	(a5),a1		;zxbase
	move.l	#$c9c9c9c9,d1
	move.w	#256/4-1,d0	;fill begin of rom with ret (201)
.rom0	move.l	d1,(a1)+
	dbra	d0,.rom0
	moveq	#-1,d1
	move.w	#$3f00/4-1,d0	;fill rom with 255
.rom	move.l	d1,(a1)+
	dbra	d0,.rom
	moveq	#0,d1
	move.w	#$c000/4-1,d0	;fill ram with 0
.ram	move.l	d1,(a1)+
	dbra	d0,.ram
	move.b	#243,(a1)	;di
	
	move.l	(a5)+,a3	;zxbase
	move.l	(a5)+,a4	;emulbase
	move.l	(a0),d1		;ems_initnumber<<16!ems_initnumber
	move.w	(a0)+,d1
	move.l	a0,a1		;launch
	add.w	(a0)+,a1
	move.l	a0,a2		;block
	add.w	(a0),a2

	lea	z80_pc(a4),a0
	clr.w	(a0)+		;reset pc to zero
	move.w	(a1)+,(a0)+	;set sp to eml_stack
	move.l	d1,(a0)+	;af,af'
	move.l	d1,(a0)+	;bc,bc' to ems_initnumber
	move.l	d1,(a0)		;ix,iy
	move.l	d1,z80_d(a4)	;de,hl
	move.l	d1,z80_d_(a4)	;de',hl'

	move.b	#$cd,(a3)+	;call		install z80 startup code
	move.w	(a1)+,d0	;eml_init
	bne.b	.useit
	move.w	(a2),d0		;emb_address
.useit
	move.b	d0,(a3)+	;lsb
	move.w	d0,(a3)+	;msb
	move.b	#$21,-1(a3)	;ld	hl,
	move.b	1(a1),(a3)+	;eml_interrupt lsb
	move.b	(a1),(a3)+	;msb
	moveq	#z80launchlen-1,d0
.launch	move.b	(a5)+,(a3)+
	dbra	d0,.launch
	
.copy	move.l	a3,d1		;zxbase+something
	move.w	(a2)+,d1	;emb_address
	beq.b	.copydone
	move.l	d1,a1		;zxbase+emb_address
	moveq	#0,d0
	move.w	(a2)+,d0
	move.l	a2,a0
	add.w	(a2)+,a0
	add.w	d1,d0		;if address+len > 65536
	bcc.b	.ok
	moveq	#0,d0		;protect the innocent
.ok	sub.w	d1,d0
	jsrlib	CopyMem
	bra.b	.copy
.copydone
	jsrlib	Forbid		;before starting the process
	move.l	(a5)+,a6	;dosbase
	lea	var(pc),a0
	move.l	a0,d1
	lea	priority(pc),a0
	move.b	#127,(a0)	;set pri to maximum
	move.l	a0,d2
	moveq	#2,d3		;because of bug in V37 (nullterm)
	move.l	#GVF_BINARY_VAR,d4
	jsrlib	GetVar
	lea	processtags(pc),a0
	move.l	a0,d1
	jsrlib	CreateNewProc
	move.l	d0,(a5)		;process
	exec	a6
	jmplib	Permit		;now let process do what it wants...
	
;stop the emulator

endsound
	exec	a6
	move.l	process(pc),d0
	beq.b	.none
	move.l	d0,a1
	jsrlib	RemTask
	clr.l	process
.none	rts

;Copy AY registers to AY, then signal Interrupt received to the emulator.
;(Freqs multiplied by 8).
;Return fadelen or 0 in d0.

interrupt
	exec	a6
	lea	ay_regs(pc),a0
	move.l	AYbase(pc),a1
	moveq	#3-1,d1
.freqs	move.w	(a0)+,d0
	ror.w	#8-3,d0		;swap bytes & multiple word by 8
	move.w	d0,(a1)+	;copy first six bytes
	dbra	d1,.freqs
	move.l	(a0)+,(a1)+	;+4
	move.l	(a0),(a1)	;+4 = 14 registers

	move.l	process(pc),d0	;if no process exit
	beq.b	.exit
	move.l	d0,a1
	move.l	#SIGBREAKF_CTRL_C,d0
	jsrlib	Signal
	lea	songlen(pc),a0
	moveq	#0,d0
	tst.w	(a0)
	beq.b	.exit
	subq.w	#1,(a0)+
	bne.b	.exit
	move.w	(a0),d0
.exit	rts

************************* Support routines *********************

Alloc64k	moveq	#MEMF_FAST!MEMF_PUBLIC,d3
	bsr.b	.main
	bne.b	.exit
	moveq	#MEMF_CHIP!MEMF_PUBLIC,d3

;d2 size, d3 type of mem, d4 word allignment (eg. -1 will allocate $xxxxffff)
;			in upper word


.main	lea	MemList(a6),a2
.loop	move.l	(a2),a2
	move.l	(a2),d0
	beq.b	.exit
	move	MH_ATTRIBUTES(a2),d0
	and	d3,d0
	cmp	d3,d0
	bne.b	.loop
	move.l	d4,d0
	move.w	MH_LOWER(a2),d0
	bra.b	.in
.loop2	swap	d0
	addq	#1,d0
.in	swap	d0
	cmp.l	MH_UPPER(a2),d0
	bcc.b	.loop
	move.l	d0,a1
	push	d0
	move.l	d2,d0
	jsrlib	AllocAbs
	tst.l	d0
	popm	d0
	beq.b	.loop2
.exit	rts

****************************** Z80 out routine ***********************

out	
	ifne	COLOROUT
	btst	#0,d1
	bne.b	.nofe
	and	#7,d0
	add.w	d0,d0
	lea	.tab(pc,d0.w),a0
	move.w	(a0),$dff180
	rts
.tab	dc.w	$000
	dc.w	$00f
	dc.w	$f00
	dc.w	$f0f
	dc.w	$0f0
	dc.w	$0ff
	dc.w	$ff0
	dc.w	$fff
	endc
.nofe	
	cmp.w	#$fffd,d1
	bne.b	.notselect
.outreg	cmp.b	#14,d0
	bcc.b	.exit
.outregamstrad	move.b	d0,ay_selected+1
.exit	rts
.notselect
	cmp.w	#$bffd,d1
	bne.b	.notdata
.outay	lea	ay_regs(pc),a0
	add.w	ay_selected(pc),a0
	move.b	d0,(a0)

	ifne	AMSTRAD

	rts
.notdata	
	clr.b	d1
	cmp.w	#$f600,d1
	bne.b	.notcontrolport
	move.b	d0,d1
	move.w	porta(pc),d0
	add.b	d1,d1
	bcc.b	.exit
	bmi.b	.outregamstrad
	lea	ay_regs(pc),a0
	move.w	ay_selected(pc),d1
	cmp.b	#14,d1
	bcc.b	.exit
	add.w	d1,a0
	move.b	d0,(a0)
	rts
.notcontrolport
	cmp.w	#$f400,d1
	bne.b	.notdataport
	move.b	d0,porta+1
.notdataport
	rts

	else

.notdata
	rts

	endc

************************** variables ************************

	ifne	AMSTRAD
porta	dc.w	0
	endc

;warning - do not change the order of variables!!! used in initsound!

b
songlen	dc.w	0
fadelen	dc.w	0
	
ay_selected	dc.w	0	;to which ay register we are going to "out"

ay_regs	dc.w	0,0,0	;freqs
	dc.b	0	;noise
	dc.b	0	;strobe
	dc.b	0,0,0	;volumes
	dc.b	0	;envtype
	dc.b	0,0	;envfreq

zxbase	dc.l	0
emulbase	dc.l	0

*************************** Z80 launch code ***********************

z80launch
;	dc.b	$cd	;call	xx
;init	dc.b	0,0
;	dc.b	$21	;ld	hl,xx
;rupt	dc.b	0,0
	dc.b	$7c	;ld	a,h
	dc.b	$b5	;or	l
	dc.b	$20,$08	;jr	nz,ok
	dc.b	$ed,$57	;ld	a,i
	dc.b	$67	;ld	h,a
	dc.b	$2d	;dec	l
	dc.b	$5e	;ld	e,(hl)
	dc.b	$23	;inc	hl
	dc.b	$56	;ld	d,(hl)
	dc.b	$eb	;ex	de,hl
	dc.b	$22,$17,0;ok ld	(here+1),hl
	dc.b	$76	;loop	halt
	dc.b	$cd,0,0	;here	call	xx
	dc.b	$18,$fa	;jr	loop
	dc.b	201	;ret
z80launchlen	equ	*-z80launch

********************* Process Data  *********************

dosb	dc.l	0
process	dc.l	0
	dc.b	'RAM:'
var	dc.b	'Z80debug',0
format	dc.b	'Error: %08lx Emulator: %08lx RAM: %08lx',10,10
	dc.b	'PC:%04x SP :%04x',10
	dc.b	'AF:%04x AF'':%04x',10
	dc.b	'BC:%04x BC'':%04x',10
	dc.b	'DE:%04x DE'':%04x',10
	dc.b	'HL:%04x HL'':%04x',10
	dc.b	'IX:%04x IY :%04x',10
	dc.b	'I :  %02x R  :  %02x',10
	dc.b	0

processtags
	dc.l	NP_Entry,entry
	dc.l	NP_Name,name
	dc.l	NP_Priority
	dc.b	0,0,0
priority dc.b	127			;must be before TAG_DONE!!!
	dc.l	TAG_DONE		;Because of nullterm bug in v37 GetVar

entry	lea	zxbase(pc),a0
	move.l	(a0)+,d7		;zxbase
	move.l	(a0),a0			;emulbase
	jsr	z80_RUN(a0)
	exec	a6			;hopefully, this never gets executed
	jsrlib	Forbid			;no one can break us now
	lea	dosb(pc),a0
	move.l	(a0)+,a6		;dosbase
	clr.l	(a0)+			;process (no one will try RemTask() now)
	move.l	a0,d1			;debugname
	move.l	#MODE_NEWFILE,d2
	jsrlib	Open
	move.l	d0,d6
	beq.b	.exit		;nochance
	move.l	emulbase(pc),a5
	move.l	a7,a2
	lea	z80_i(a5),a4
	move.l	(a4),d0
	lsr.l	#8,d0
	and.w	#$ff,d0
	push	d0		;r,i
	move.l	-(a4),-(sp)	;ix,iy
	lea	z80_which(a5),a3
	add.w	(a3),a3		;correct exx set
	move.w	2(a3),d0	;other set
	move.w	(a3,d0),-(sp)	;hl'
	move.w	(a3),-(sp)	;hl
	move.w	-2(a3,d0),-(sp)	;de'
	move.w	-(a3),-(sp)	;de
	move.l	-(a4),-(sp)	;bc,bc'
	move.l	-(a4),-(sp)	;af,af'
	move.l	-(a4),-(sp)	;pc,sp'
	lea	zxbase(pc),a0
	push	(a0)+		;zxbase
	push	(a0)		;emulbase
	push	d7		;return code from emulator
	lea	format(pc),a0
	move.l	d6,d1
	move.l	a0,d2
	move.l	a7,d3
	jsrlib	VFPrintf
	move.l	a2,a7
	move.l	d6,d1
	jsrlib	Close
.exit	rts
	
*********************** z80 emulator *************************

emulator	incbin	'deliay:Z80'	;sorry, not distributed :-)
emullen	equ	*-emulator
zxsize	equ	8+65536+8

