**
**	file: utility.a
**
** Utility functions (that is, the emulator would work without them,
** but you wouldn't want to be without them).

** The compilation of these routines does not depend on any flags.


Z80_NOXREF SET 1  ;Tell include files not to compile cross-references.

	INCLUDE Z80.i

	INCLUDE Z80_struct.i


	XDEF Z80_SetByte
	XDEF Z80_SetWordLH
	XDEF Z80_GetByte
	XDEF Z80_GetWordLH
	XDEF Z80_SetBlock
	XDEF Z80_ReadBlock
	XDEF Z80_WriteBlock
	XDEF Z80_SetMemAttr
	XDEF Z80_GetMemAttr

** Note that the functions should only be called after the control structure
** has been initialised through a Z80_Init() call.

** =========================================================================


** Routines to access the Z80 address space:
**
** These routines mirror any memory changes in the cache and handle the
** wraparound of addresses transparently. I recommend that they are used
** rather than writing directly into the Z80 address space. They do *not*
** respect any memory protection attributes - a write always takes effect.
**   All need a pointer to the control structure in a0. The Z80 address
** (word-sized) is passed in d0 (for block functions this is the block start
** address).
**
**	Z80_SetByte is passed a byte value to be written in d1. It
**	returns nothing.
**
**	Z80_SetWordLH is passed a (big-endian) word-sized value in d1
**	and writes it (little-endian) to Z80 memory. It returns nothing.
**
**	Z80_GetByte returns the read byte value in d0.
**
**	Z80_GetWordLH reads a (little-endian) word-sized value and
**	returns it (in big-endian form) in d0.
**
** All block functions are passed the block size (an unsigned longword) in
** d1. They return nothing.
**
**	Z80_SetBlock is passed the byte value to be written in d2.
**
** Z80_ReadBlock and Z80_WriteBlock are passed the buffer address (in the
** 680x0 address space) in a1.
**
** All Z80 address arithmetic is word-sized. For instance, calling
** Z80_SetWordLH(ctrl, $ffff, $1234) will store $34 in Z80 address $ffff and
** $12 in address $0000. The block functions will also wrap at $ffff.


Z80_SetByte	;(a0,d0,d1)	(ctrl, addr, BYTE val)
		move.l	a1,-(a7)
		move.l	Z80_Zero(a0),a1
		move.b	d1,(a1,d0.w)
		move.l	Z80_CacheZero(a0),a1
		add.w	d0,a1
		clr.w	(a1,d0.w)
		move.l	(a7)+,a1
		rts	;return nothing
** -----

Z80_SetWordLH	;(a0,d0,d1)	(ctrl, addr, WORD val)
		move.l	a1,-(a7)
		movea.l Z80_Zero(a0),a1
		move.b	d1,(a1,d0.w)
		lsr.w	#8,d1
		addq.w	#1,d0	;second byte
		move.b	d1,(a1,d0.w)
		move.l	Z80_CacheZero(a0),a1
		movea.l a1,a0 ;keep cachezero
		add.w	d0,a1
		clr.w	(a1,d0.w) ;mark cache
		subq.w	#1,d0	;first byte
		movea.l a0,a1 ;get cachezero
		add.w	d0,a1
		clr.w	(a1,d0.w) ;mark cache
		move.l	(a7)+,a1
		rts	;return nothing
** -----

Z80_GetByte	;(a0,d0)		(ctrl, addr)
		movea.l Z80_Zero(a0),a0
		move.b	(a0,d0.w),d0
		ext.w	d0	;sign extend
		ext.l	d0
		rts	;return (signed) byte in d0
** -----

Z80_GetWordLH	;(a0,d0)		(ctrl, addr)
		movea.l Z80_Zero(a0),a0
		move.w	d0,-(sp)	;create word on stack
		move.b	(a0,d0.w),1(sp) ;low byte
		addq.w	#1,d0
		move.b	(a0,d0.w),(sp)	;high byte
		move.w	(sp)+,d0
		ext.l	d0	;sign extend
		rts	;return (signed) word in d0
** -----

Z80_SetBlock	;(a0,d0,d1,d2)	(ctrl, start_addr, size, BYTE val)
		movem.l a1/a2,-(a7)
		movea.l Z80_Zero(a0),a1
		movea.l Z80_CacheZero(a0),a2
.loop		move.b	d2,(a1,d0.w)
		movea.l a2,a0
		adda.w	d0,a0
		clr.w	(a0,d0.w) ;mark cache
		addq.w	#1,d0
		subq.l	#1,d1
		bne.s	.loop
		movem.l (a7)+,a1/a2
		rts	;return nothing
** -----

Z80_ReadBlock	;(a0,a1,d0,d1)	(ctrl, buf, start_addr, size)
		movea.l Z80_Zero(a0),a0
.loop		move.b	(a0,d0.w),(a1)+
		addq.w	#1,d0
		subq.l	#1,d1
		bne.s	.loop
		rts	;return nothing
** -----

Z80_WriteBlock	;(a0,a1,d0,d1)	(ctrl, buf, start_addr, size)
		movem.l a2/a3,-(a7)
		movea.l Z80_Zero(a0),a2
		movea.l Z80_CacheZero(a0),a3
.loop		move.b	(a1)+,(a2,d0.w)
		movea.l a3,a0
		adda.w	d0,a0
		clr.l	(a0,d0.w) ;mark cache
		addq.w	#1,d0
		subq.l	#1,d1
		bne.s	.loop
		movem.l (a7)+,a2/a3
		rts	;return nothing

** -------------------------------------------------------------------------


** This routine sets Z80 memory control attributes. If the Z80_Attrmem field
** in the control structure is zero, the function has no effect.
**
**	Z80_SetMemAttr	(a0,d0,d1,d2)	(ctrl, start_addr, size, value)
**
** As for the memory access functions, the Z80 address is word-sized and
** passed in d0, and the size is an unsigned longword. The attribute is a
** byte-sized value, and its possible meanings are defined in Z80.i. Nothing
** is returned.

Z80_SetMemAttr	;(a0,d0,d1,d2)	(ctrl, start_addr, size, value)
		tst.l	Z80_AttrMem(a0)
		beq.s	.end		;assure nonzero pointer

		move.l	Z80_AttrZero(a0),a0
		move.l	a3,-(a7)	;get a scratch register
		and.b	#$7F,d2		;make sure bit 7 of value is reset

.loop		moveq	#-$80,d3
		and.b	(a0,d0.w),d3	;get bit 7 from old attribute
		or.b	d2,d3		;insert value (bits 0-6)
		move.b	d3,(a0,d0.w)	;write back result
		addq.w	#1,d0
		subq.l	#1,d1
		bne.s	.loop

		move.l	(a7)+,d3
.end
		rts	;return nothing

** -----

** This routine returns the memory control attribute for an address. If
** the Z80_Attrmem field in the control structure is zero, it has no
** effect and returns zero (RAM).
**
**	Z80_GetMemAttr	(a0,d0) (ctrl, addr)
**
** The Z80 address is word-sized and passed in d0. The attribute value is
** returned in (the whole of) d0, and has the range -128 to +127.

Z80_GetMemAttr	;(a0,d0)	(ctrl, addr)
		tst.l	Z80_AttrMem(a0)
		beq.s	.undef		;assure nonzero pointer

		move.l	a1,-(a7)
		move.l	Z80_AttrZero(a0),a1
		move.b	(a1,d0.w),d0
		and.w	#$007F,d0	;clear bit 7 of read value
		ext.l	d0

		movea.l (a7)+,a1
		rts

.undef		clr.l	d0
		rts

** =========================================================================
