*
* MapRZ2.asm CN20.12.95-02.01.95
*

		MACHINE	68030


;*** Exec function offsets ***

Supervisor	equ	-$001E
Disable		equ	-$0078
Enable		equ	-$007E
AllocMem	equ	-$00c6
FreeMem		equ	-$00d2
CloseLibrary	equ	-$019e
OpenLibrary	equ	-$0228


;*** other constants ***

RB_BOARDADDRESS	equ	$2E				;offset in RetinaBase
RB_SEG_ADDRESS	equ	$32				;offset in RetinaBase

MEMF_PUBLIC 	equ	(1<<0)

SIZE_32K	equ	$00008000
SIZE_4M		equ	$00400000

MASK_32K	equ	$00007fff
MASK_128K	equ	$0001ffff
MASK_4M		equ	$003fffff

RETINAMEMSIZE	equ	SIZE_4M				;maximum amount of memory is 4 Mb

RETINA_REGNUM	equ	$43C4				;choose a Retina hardware register at BoardAddress + this offset
RETINA_REGVAL	equ	$03C4				;access the chosen Retina hardware register

SEG_PTR_0H	equ	$18				;Retina register numbers for RETINA_REGNUM
SEG_PTR_0L	equ	$19				;(used to control the 64k memory window)
SEG_PTR_1H	equ	$1c
SEG_PTR_1L	equ	$1d
SEG_MODE	equ	$1e
SEG_CTRL1	equ	$2
SEG_CTRL2	equ	$20
SEG_CTRL3	equ	$26

MASK_SEG_MODE	equ	%00001100			;bits not used for segment mode
MODE_TWO_SEG	equ	%11010011			;sets two 32k segments mode

SEG_0_OFFSET	equ	$00010000			;offsets of 32K segments
SEG_1_OFFSET	equ	$00018000			;relative to Retina board address


;*** constants for MMU usage ***

TIA		equ	8				;  4Gb / 256 =  16Mb
TIB		equ	6				; 16Mb /  64 = 256Kb
TIC		equ	3				;256Kb /   8 =  32Kb
TID		equ	0				;(no D-Level used)

PAGE_SHIFT	equ	15				;2^15 = 32K
PAGE_SIZE	equ	1<<PAGE_SHIFT			;one page is 32k big

A_ENTRIES	equ	(1<<TIA)			;number of table entries at each level
B_ENTRIES	equ	(1<<TIB)
C_ENTRIES	equ	(1<<TIC)

A_TSIZE		equ	A_ENTRIES*4			;size of one table at each level
B_TSIZE		equ	B_ENTRIES*4			;(using short descriptors)
C_TSIZE		equ	C_ENTRIES*4

C_PSIZE		equ	PAGE_SIZE			;size of pages at each level
B_PSIZE		equ	C_PSIZE<<TIC
A_PSIZE		equ	B_PSIZE<<TIB

A_OFFSET	equ	0				;offsets in table mem block
							;
B_16M1_OFFSET	equ	A_OFFSET + A_TSIZE		;first 16 Mb (standard Amiga mem)
B_16M2_OFFSET	equ	B_16M1_OFFSET + B_TSIZE		:second 16 Mb (used for Retina)
							;
C_AC1_OFFSET	equ	B_16M2_OFFSET + B_TSIZE		;AutoConfig 00e80000-00ebffff
C_AC2_OFFSET	equ	C_AC1_OFFSET + C_TSIZE		;AutoConfig 00ec0000-00efffff
C_RB_OFFSET 	equ	C_AC2_OFFSET + C_TSIZE		;mirrored Retina board
C_RL_OFFSET	equ	C_RB_OFFSET + C_TSIZE		;4Mb linear Retina RAM
							;
TABLE_SIZE	equ	C_RL_OFFSET + 16*C_TSIZE	;end and size of table

A_START		equ	$00000000			;table entries point to these
							;addresses in the logical mem space
B_16M1_START	equ	$00000000			;
B_16M2_START	equ	$01000000			;
							;
C_AC1_START	equ	$00e80000			;
C_AC2_START	equ	$00ec0000			;
C_RB_START	equ	$01000000			;
C_RL_START	equ	$01c00000			;

PAGE_PTR_MASK	equ	$FFFFFF00			;low descriptor byte is not part of ptr
TABLE_PTR_MASK	equ	$FFFFFFF0			;low 4 bits are not part of ptr
TABLE_ALIGN	equ	$10				;tables have to be aligned at 16 bytes boundary

PG_CI		equ	1<<6				;page attributes: cache inhibit
PG_M		equ	1<<4				;modified
PG_U		equ	1<<3				;used
PG_WP		equ	1<<2				;write protect

SHORT_INVALID	equ	$BAD00000			;DT bits = 00 = INVALID

DT_INVALID	equ	$0				;descriptor types: invalid descriptor
DT_PAGE		equ	$1				;page descriptor
DT_VALID_4BYTE	equ	$2				;table pointer to short table
DT_VALID_8BYTE	equ	$3				;table pointer to long table

TC_ENABLE	equ	1<<31				;codes for TC register
TC_PSIZE	equ	(%1111)<<20			;select 32K pages
TC_TIA		equ	TIA<<12
TC_TIB		equ	TIB<<8
TC_TIC		equ	TIC<<4
TC_TID		equ	TID




		SECTION segment0,CODE

;************
;main program
;************

main		movem.l D2-D7/A2-A6,-(SP)		;save regs

		bsr	retina_addr			;read Retina board address
		tst.l	d0
		bne	1$

		bsr	alloc_mem			;allocate memory
		tst.l	d0
		bne	1$

		bsr	gen_MMU_tree			;generate MMU tree

		bsr	turn_it_on			;turn mapping on

		movem.l (SP)+,D2-D7/A2-A6		;restore regs
		moveq	#0,d0				;returncode = 0,
		rts

1$		movem.l (SP)+,D2-D7/A2-A6		;restore regs
		moveq	#-1,d0				;returncode = -1,
		rts




;************************
;get Retina board address
;************************

retina_addr	;open RetinaBase
		lea	RetinaName,a1
		moveq	#3,d0
		move.l	(4).w,a6
		jsr	OpenLibrary(a6)
		move.l	d0,RetinaBase
		beq	1$

		;get address
		move.l	d0,a0
		move.l	RB_BOARDADDRESS(a0),BoardAddress

		;close RetinaBase
		move.l	RetinaBase,a1			
		move.l	(4).w,a6
		jsr	CloseLibrary(a6)

		moveq	#0,d0
		rts

1$		moveq	#-1,d0
		rts




;***************
;allocate memory
;***************

alloc_mem	;allocate memory
		move.l	(4).w,a6
		move.l	#end_bus_err_handler,d0
		sub.l	#bus_err_handler,d0
		add.l	#TABLE_ALIGN+TABLE_SIZE,d0
		move.l	d0,BlockSize
		move.l	#MEMF_PUBLIC,d1
		jsr	AllocMem(a6)
		move.l	d0,MemBlock
                beq     1$

		;align table at 16 bytes boundary
		and.l	#TABLE_PTR_MASK,d0
		add.l	#TABLE_ALIGN,d0
		move.l	d0,TableStart

		;remember end of table / begin of own Bus Error handler
		add.l	#TABLE_SIZE,d0
		move.l	d0,HandlerStart

		moveq	#0,d0
		rts

1$		moveq	#-1,d0
		rts




;*****************
;generate MMU tree
;*****************

gen_MMU_tree	move.l	TableStart,a3			;start of whole tree (stays in a3 until end of subroutine)

		;A table
		move.l	a3,a0
		move.w	#A_ENTRIES,d0
		move.l	#DT_PAGE+A_START,d1
1$		move.l	d1,(a0)+
		add.l	#A_PSIZE,d1
		subq.w	#1,d0
		bne	1$

		;A exceptions
		move.l  a3,d0
		add.l	#DT_VALID_4BYTE+B_16M1_OFFSET,d0
		move.l	d0,(a3)				;pointer to B_16M1
		add.l	#B_TSIZE,d0
		move.l	d0,4(a3)			;pointer to B_16M2


		;B_16M1 table
		move.l  a3,a2
		add.l	#B_16M1_OFFSET,a2		;start of B_16M1 table

		move.l  a2,a0
		move.w	#B_ENTRIES,d0
		move.l	#DT_PAGE+B_16M1_START,d1
2$		move.l	d1,(a0)+
		add.l	#B_PSIZE,d1
		subq.w  #1,d0
		bne	2$

		;B_16M1 exceptions
		move.l	a2,a0				;don't cache chip RAM
		move.w	#$200000/B_PSIZE,d0		;number of chip RAM pages
3$		add.l	#PG_CI,(a0)+
		subq.w	#1,d0
		bne	3$

		add.l	#PG_CI,$00bc0000/B_PSIZE*4(a2)	;don't cache CIAs
		add.l	#PG_CI,$00dc0000/B_PSIZE*4(a2)	;don't cache hardware clock and custom chips

		move.l  a3,d0
		add.l	#DT_VALID_4BYTE+C_AC1_OFFSET,d0
		move.l	d0,C_AC1_START/B_PSIZE*4(a2)	;pointer to C_AC1
		add.l	#C_TSIZE,d0
		move.l	d0,C_AC2_START/B_PSIZE*4(a2)	;pointer to C_AC2

		add.l   #PG_WP,$00f80000/B_PSIZE*4(a2)	;write protect ROM
		add.l   #PG_WP,$00fc0000/B_PSIZE*4(a2)	;(FastROM option would be useful!)


		;B_16M2 table
		move.l  a3,a2
		add.l	#B_16M2_OFFSET,a2		;start of B_16M2 table

		move.l  a2,a0
		move.w	#B_ENTRIES,d0
4$		move.l	#SHORT_INVALID,(a0)+
		subq.w  #1,d0
		bne	4$

		;B_16M2 exceptions
		move.l  a3,d0
		add.l	#DT_VALID_4BYTE+C_RB_OFFSET,d0
		move.l	d0,(a2)				;pointer to C_RB

		move.l  a2,a0
		add.l	#(C_RL_START-B_16M2_START)/B_PSIZE*4,a0
		move.l	a3,d1
		add.l	#DT_VALID_4BYTE+C_RL_OFFSET,d1
		move.w	#RETINAMEMSIZE/B_PSIZE,d0

5$		move.l	d1,(a0)+			;16 pointers to C_RL
		add.l	#C_TSIZE,d1
		subq.w	#1,d0
		bne	5$


		;C_AC tables
		move.l  a3,a2
		add.l	#C_AC1_OFFSET,a2		;start of C_AC table

		move.l	a2,a0
		move.w	#C_ENTRIES*2,d0
		move.l	#DT_PAGE+PG_CI+C_AC1_START,d1
6$		move.l	d1,(a0)+
		add.l	#C_PSIZE,d1
		subq.w	#1,d0
		bne	6$

		;C_AC things to remember
		move.l	BoardAddress,d0
		sub.l	#C_AC1_START,d0
		moveq	#PAGE_SHIFT-2,d1
		lsr.l	d1,d0				;divide by page size and multiply by 4 to get long word offset
		add.l	a2,d0
		move.l	d0,MMU_RB

		move.l	d0,a0				;remember valid MMU_RB table entries
		lea	ValidMMU_RB,a1
		move.l	(a0)+,(a1)+
		move.l	(a0)+,(a1)+
		move.l	(a0)+,(a1)+
		move.l	(a0),(a1)


		;C_RB table
		move.l  a3,a2
		add.l	#C_RB_OFFSET,a2			;start of C_RB table

		move.l	a2,a0
		move.w	#C_ENTRIES,d0
7$		move.l	#SHORT_INVALID,(a0)+
		subq.w	#1,d0
		bne	7$

		;C_RB exceptions
		move.l	a2,a1
		move.l	#DT_PAGE+PG_CI,d0
		add.l	BoardAddress,d0
		move.l	d0,(a1)+
		add.l	#PAGE_SIZE,d0
		move.l	d0,(a1)+
		add.l	#PAGE_SIZE,d0
		move.l	d0,(a1)+
		add.l	#PAGE_SIZE,d0
		move.l	d0,(a1)


		;C_RL tables
		move.l  a3,a2
		add.l	#C_RL_OFFSET,a2			;start of C_RL table

		move.l	a2,a0
		move.w	#C_ENTRIES*16,d0
8$		move.l	#SHORT_INVALID,(a0)+
		subq.w	#1,d0
		bne	8$

		;C_AC things to remember
		move.l	a2,MMU_RL

		rts




;***************
;turn mapping on
;***************

turn_it_on	move.l	(4).w,a6
		jsr	Disable(a6)			;disable interrupts and task scheduling

		lea	use_MMU_tree,a5
		jsr	Supervisor(a6)

		move.l	CurrentVBR,a3
		move.l	$8(a3),OldHandler		;is called from new handler

		lea	bus_err_handler,a0
		lea	end_bus_err_handler,a1
		move.l	HandlerStart,a2

1$		move.w	(a0)+,(a2)+			;copy handler
		cmp.l	a0,a1
		bne	1$

		move.l	HandlerStart,$8(a3)		;activate own handler

		jsr	Enable(a6)			;enable interrupts and task scheduling again
		rts




;turn MMU tree on (Supervisor mode)

use_MMU_tree	lea	OffTC,a0
		pmove.l	(a0),TC				;disable MMU translation

		lea	NewCRP,a0
		pmove.d	(a0),CRP			;point MMU to our new tree

		lea	NewTC,a0
		pmove.l	(a0),TC				;re-enable MMU with new tree

		movec.l	VBR,d0				;get VBR pointer
		move.l	d0,CurrentVBR
		rte




;*****************
;Bus Error handler
;*****************

bus_err_handler	movem.l	d0-d1/d6-d7/a0-a5,-(sp)		;save regs
		move.l	(4+6)*4+$10(sp),d7		;get faulty address that caused exception

		move.l	d7,d0				;faulty address
		and.l	#~MASK_128K,d0			;mask out lower bits; doesn't work, if Retina hardware starts at an odd 64k page!
		cmp.l	BoardAddress(pc),d0		;Retina board hardware accessed?
		beq	board_acc			;yes

		move.l	d7,d0				;faulty address
		and.l	#~MASK_4M,d0			;mask out lower bits
		cmpi.l	#C_RL_START,d0			;linear Retina RAM accessed?
		beq	linear_RAM_acc			;yes

		movem.l	(sp)+,d0-d1/d6-d7/a0-a5		;restore regs
		move.l	OldHandler(pc),-(sp)		;jump to normal Bus Error handler
		rts




;Retina board hardware accessed -> change to segmented mode

board_acc	ori.w   #$0700,SR			;turn interrupts off (are turned on again by rte)
		move.l	#C_RB_START+RETINA_REGVAL,a4	;access the chosen Retina hardware register
		move.l	#C_RB_START+RETINA_REGNUM,a5	;choose Retina hardware register

		lea	RetinaSegCtrl3(pc),a0
		move.b	#SEG_CTRL3,(a5)			;restore Retina registers
		move.b	(a0),(a4)
		move.b	#SEG_CTRL2,(a5)
		move.b	-(a0),(a4)
		move.b	#SEG_CTRL1,(a5)
		move.b	-(a0),(a4)
		move.b	#SEG_MODE,(a5)
		move.b	-(a0),(a4)
		move.b	#SEG_PTR_1L,(a5)
		move.b	-(a0),(a4)
		move.b	#SEG_PTR_1H,(a5)
		move.b	-(a0),(a4)
		move.b	#SEG_PTR_0L,(a5)
		move.b	-(a0),(a4)
		move.b	#SEG_PTR_0H,(a5)
		move.b	-(a0),(a4)
		move.b	-(a0),(a5)			;register B after all A registers

		lea	MMU_S0(pc),a2			;set MMU_RL table entry 0 invalid
		move.l  (a2),a0				;is entry already invalid?
		cmpa.l	#0,a0				;(movea doesn't set flags, tst.l ax doesn't exist)
		beq	1$				;yes
		move.l	#SHORT_INVALID,(a0)		;mark MMU table entry
		move.l	#0,(a2)				;clear pointer
1$
		lea	MMU_S1(pc),a2			;set MMU_RL table entry 1 invalid
		move.l  (a2),a0				;is entry already invalid?
		cmpa.l	#0,a0				;(movea doesn't set flags, tst.l ax doesn't exist)
		beq	2$				;yes
		move.l	#SHORT_INVALID,(a0)		;mark MMU table entry
		move.l	#0,(a2)				;clear pointer
2$
		lea	ValidMMU_RB(pc),a0		;set MMU_RB table entries valid
		move.l	MMU_RB(pc),a1
		move.l	(a0)+,(a1)+
		move.l	(a0)+,(a1)+
		move.l	(a0)+,(a1)+
		move.l	(a0),(a1)

		pflusha					;flush MMU cache
		movem.l	(sp)+,d0-d1/d6-d7/a0-a5		;restore regs
		rte					;restart instruction




;linear Retina RAM accessed

linear_RAM_acc	ori.w   #$0700,SR			;turn interrupts off (are turned on again by rte)
		move.l	#C_RB_START+RETINA_REGVAL,a4	;access the chosen Retina hardware register
		move.l	#C_RB_START+RETINA_REGNUM,a5	;choose Retina hardware register

		move.l	MMU_S0(pc),d0			;is linar mode already on? (tst.b d(pc) doesn't exist)
		bne	linear_is_on			;yes
		move.l	MMU_S1(pc),d0			;is linar mode already on? (tst.b d(pc) doesn't exist)
		bne	linear_is_on			;yes


		;change to linear mode

		lea	RetinaRegB(pc),a0		;save Retina registers
		move.b	(a5),(a0)+			;register B before all A registers
		move.b	#SEG_PTR_0H,(a5)
		move.b	(a4),(a0)+
		move.b	#SEG_PTR_0L,(a5)
		move.b	(a4),(a0)+
		move.b	#SEG_PTR_1H,(a5)
		move.b	(a4),(a0)+
		move.b	#SEG_PTR_1L,(a5)
		move.b	(a4),(a0)+
		move.b	#SEG_MODE,(a5)
		move.b	(a4),(a0)+
		move.b	(a4),d0				;set two 32k segments mode
		and.b	#MASK_SEG_MODE,d0
		or.b	#MODE_TWO_SEG,d0
		move.b	d0,(a4)
		move.b	#SEG_CTRL1,(a5)
		move.b	(a4),(a0)+
		move.b	#$f,(a4)			;set normal mode
		move.b	#SEG_CTRL2,(a5)
		move.b	(a4),(a0)+
		bset.b	#1,(a4)				;set normal mode
		move.b	#SEG_CTRL3,(a5)
		move.b	(a4),(a0)
		andi.b	#$f5,(a4)			;set normal mode

		move.l	#SHORT_INVALID,d0		;set MMU_RB table entries invalid
		move.l	MMU_RB(pc),a0
		move.l	d0,(a0)+
		move.l	d0,(a0)+
		move.l	d0,(a0)+
		move.l	d0,(a0)


linear_is_on	move.l	d7,d0				;faulty address
		and.l	#MASK_4M&~MASK_32K,d0		;mask out upper & lower bits
		move.l	d0,d6
		lsr.l	#6,d6				;address for Retina segment pointer

		moveq	#PAGE_SHIFT-2,d1		;replaces 'divu.l #C_PSIZE/4,d0'
		lsr.l	d1,d0				;divide by page size and multiply by 4 to get long word offset
		add.l	MMU_RL(pc),d0			;add MMU_RL base address
		move.l	d0,a3				;address in MMU table

		move.b	NextSegment(pc),d0		;which segment is next? (tst.b d(pc) doesn't exist)
		bne	segment_1


		;use segment 0

		lea	MMU_S0(pc),a2			;set old MMU_RL table entry 0 invalid
		move.l  (a2),a0				;is entry already invalid?
		cmpa.l	#0,a0				;(movea doesn't set flags, tst.l ax doesn't exist)
		beq	1$				;yes
		move.l	#SHORT_INVALID,(a0)		;mark MMU table entry
1$
		move.l	ValidMMU_Seg0(pc),(a3)		;set new MMU_RL table entry 0 valid
		move.l	a3,(a2)				;store its address in MMU_S0

		move.b	#SEG_PTR_0L,(a5)		;write address into Retina segment pointer
		move.b	d6,(a4)
		lsr.w	#8,d6
		move.b	#SEG_PTR_0H,(a5)
		move.b	d6,(a4)

		lea	NextSegment(pc),a0		;set next segment to 1
		move.b	#1,(a0)

		pflusha					;flush MMU cache
		movem.l	(sp)+,d0-d1/d6-d7/a0-a5		;restore regs
		rte					;restart instruction


		;use segment 1

segment_1	lea	MMU_S1(pc),a2			;set old MMU_RL table entry 1 invalid
		move.l  (a2),a0				;is entry already invalid?
		cmpa.l	#0,a0				;(movea doesn't set flags, tst.l ax doesn't exist)
		beq	1$				;yes
		move.l	#SHORT_INVALID,(a0)		;mark MMU table entry
1$
		move.l	ValidMMU_Seg1(pc),(a3)		;set new MMU_RL table entry 1 valid
		move.l	a3,(a2)				;store its address in MMU_S1

		sub.w	#SIZE_32K>>6,d6			;subtract 32K>>6 due to second segment
		move.b	#SEG_PTR_1L,(a5)		;write address into Retina segment pointer
		move.b	d6,(a4)
		lsr.w	#8,d6
		move.b	#SEG_PTR_1H,(a5)
		move.b	d6,(a4)

		lea	NextSegment(pc),a0		;set next segment to 0
		move.b	#0,(a0)

		pflusha					;flush MMU cache
		movem.l	(sp)+,d0-d1/d6-d7/a0-a5		;restore regs
		rte					;restart instruction




;values provided by the main program

OldHandler	ds.l	1				;pointer to old handler

BoardAddress	ds.l	1				;Retina board address

MMU_RB		ds.l	1				;MMU tables to Retina board
MMU_RL		ds.l	1				;MMU tables to linear Retina RAM

ValidMMU_RB	ds.l	1				;valid MMU_RB entries to Retina board
		ds.l	1				;
ValidMMU_Seg0	ds.l	1				;valid MMU entry to 32K segment No. 0
ValidMMU_Seg1	ds.l	1				;valid MMU entry to 32K segment No. 1


;values filled in by exception handler

MMU_S0		dc.l	0				;MMU table to segment No. 0 in the linear Retina RAM
MMU_S1		dc.l	0				;MMU table to segment No. 1 in the linear Retina RAM

NextSegment	dc.b	0				;next segment to choose

RetinaRegB	ds.b	1				;copies of important Retina registers
RetinaPtr0h	ds.b	1
RetinaPtr0l	ds.b	1
RetinaPtr1h	ds.b	1
RetinaPtr1l	ds.b	1
RetinaSegMode	ds.b	1
RetinaSegCtrl1	ds.b	1
RetinaSegCtrl2	ds.b	1
RetinaSegCtrl3	ds.b	1


		even
end_bus_err_handler					;copy handler up to here




;library names

RetinaName	dc.b	"retina.library",0


;library base pointers

RetinaBase	ds.l	1


;other data

MemBlock	ds.l	1				;reserved mem block
BlockSize	ds.l	1				;size of mem block
HandlerStart	ds.l	1				;start of own Bus Error handler
OffTC		dc.l	$0				;TC register value to turn MMU off
NewTC		dc.l	TC_ENABLE+TC_PSIZE+TC_TIA+TC_TIB+TC_TIC+TC_TID	;new TC-Register value
NewCRP		dc.l	$7FFF<<16+DT_VALID_4BYTE	;\ new CRP-Register value with
TableStart	ds.l	1				;/ begin of MMU tree
CurrentVBR	ds.l	1

VersionString	dc.b	"$VER: MapRZ2 1.0 (02.01.95) Christoph Niedeggen",$00

		END
