;========================================================================
;60Hz emulator (PAL-NTSC) V1.05 by P. de Boer in 1991
;
;The program was inspired by Power Utility from Amicon and
;BootPAL/BootNTSC by Nico Francois.
;
;PS: my monitor (1084s) synchronizes from 47.5Hz to 65Hz
;    when VBI frequency is 55Hz then the monitor switches.
;
;========================================================================
;Options:
;Press : <LEFT-ALT>+<LEFT-SHIFT>+<CTRL> and :
;
; 1 - Toggle color bar
; 2 - Default values
; 3 - Add ± one line to timer buffer
; 4 - Sub ± one line to timer buffer
; 5 - Add one line to total of rasterlines (decreases VBI frequenzy)
; 6 - Sub one line from total of rasterlines (increases VBI frequenzy)
; 7 - Add one line to end of display
; 8 - Sub one line from end of display

; ESC - To kill the emulator
; 9 - Toggle between Software and Hardware emulation
; 0 - Toggle between 50Hz and 60Hz
;
;========================================================================
;Now some stuff we need !
;========================================================================

			RSRESET		;little structure for my flags
Flag_60Hz		RS.B	1
Flag_Color		RS.B	1
Flag_OverScan		RS.B	1
Flag_Enable		RS.B	1
Flag_FatAgnus		RS.B	1
Flag_Resident		RS.B	1

NULL			EQU	0

is_data			EQU	14	;some stuff from the system's include
is_code			EQU	18	;files

gb_DisplayFlags		EQU	206	;graphics
gb_DisplayRows		EQU	212
gb_NormalDisplayRows	EQU	216
VBlankFrequency		EQU	530

_LVODisplayAlert	EQU	-90	;intuition

_LVOOpen		EQU	-30	;dos
_LVOClose		EQU	-36
_LVORead		EQU	-42
_LVOWrite		EQU	-48
_LVOInput		EQU	-54
_LVOOutput		EQU	-60

MODE_OLD		EQU	1005	;dos
MODE_NEW		EQU	1006
PR_CLI			EQU	$AC
PR_MSGPORT		EQU	$5C

_LVOAlert		EQU	-108	;exec
_LVODisable		EQU	-120
_LVOEnable		EQU	-126
_LVOForbid		EQU	-132
_LVOPermit		EQU	-138
_LVOAddIntServer	EQU	-168
_LVORemIntServer	EQU	-174
_LVOAllocMem		EQU	-198
_LVOFreeMem		EQU	-210
_LVOAddPort		EQU	-354
_LVORemPort		EQU	-360
_LVOPutMsg		EQU	-366
_LVOGetMsg		EQU	-372
_LVOFindPort		EQU	-390
_LVOCloseLibrary	EQU	-414
_LVOSumLibrary		EQU	-426
_LVOOpenResource	EQU	-498
_LVOOpenLibrary		EQU	-552
_LVOSumKickData		EQU	-612
_LVOCopyMem		EQU	-624

ExecBase		EQU	$4

LIBF_CHANGED		EQU	2
LIB_FLAGS		EQU	14

			RSRESET		;node structure
LN			RS.B	0
LN_SUCC			RS.L	1
LN_PRED			RS.L	1
LN_TYPE			RS.B	1
LN_PRI			RS.B	1
LN_NAME			RS.L	1
LN_SIZE			RS.W	0

NT_MSGPORT		EQU	4
NT_MESSAGE		EQU	5

			RSRESET		;memlist structure
ML			RS.B	LN_SIZE
ML_NUMENTRIES		RS.W	1
ML_ME			RS.W	0
ML_SIZE			RS.W	0

			RSRESET
ME			RS.B	0
ME_REQS			RS.W	0
ME_ADDR			RS.L	1
ME_LENGTH		RS.L	1
ME_SIZE			RS.W	0

MEMF_PUBLIC		EQU	1<<0
MEMF_CHIP		EQU	1<<1

			RSRESET		;resident structure
RT			RS.B	0
RT_MATCHWORD		RS.W	1
RT_MATCHTAG		RS.L	1
RT_ENDSKIP		RS.L	1
RT_FLAGS		RS.B	1
RT_VERSION		RS.B	1
RT_TYPE			RS.B	1
RT_PRI			RS.B	1
RT_NAME			RS.L	1
RT_IDSTRING		RS.L	1
RT_INIT			RS.L	1
RT_SIZE			RS.W	0

RTC_MATCHWORD		EQU	$4AFC
RTB_COLDSTART		EQU	0
RTF_COLDSTART		EQU	1<<0
RTB_AUTOINIT		EQU	7
RTF_AUTOINIT		EQU	1<<7
RTM_WHEN		EQU	1
RTW_NEVER		EQU	0
RTW_COLDSTART		EQU	1

KickMemPtr		EQU	546	;execbase equ's
KickTagPtr		EQU	550
KickCheckSum		EQU	554	

DMACONR			EQU	$002	;custom chip addresses
VPOSR			EQU	$004
VHPOSR			EQU	$006
VPOSW			EQU	$02A
VHPOSW			EQU	$02C
DIWSTRT			EQU	$08E
DIWSTOP			EQU	$090
DMACON			EQU	$096
COLOR00			EQU	$180
BEAMCON0		EQU	$1DC
CUSTOM			EQU	$DFF000

CIAB		EQU	$BFD000
CIAA		EQU	$BFE001

PRA		EQU	$000
PRB		EQU	$100
DDRA		EQU	$200
DDRB		EQU	$300
TALO		EQU	$400
TAHI		EQU	$500
TBLO		EQU	$600
TBHI		EQU	$700
SPx		EQU	$C00
ICR		EQU	$D00
CRA		EQU	$E00
CRB		EQU	$F00

;========================================================================
CALL		MACRO
		IFC	'EXEC','\1'
		MOVEA.L	(ExecBase).W,A6
		ENDC
		IFNC	'EXEC','\1'
		MOVEA.L	\1Base,A6
		ENDC
		JSR	_LVO\2(A6)
		ENDM

OPENLIB		MACRO
		LEA	\1Name(PC),A1
		CLR.L	D0
		CALL	EXEC,OpenLibrary
		MOVE.L	D0,\1Base
		BEQ	\2
		ENDM

CLOSELIB	MACRO
		MOVEA.L	\1Base,A1
		CALL	EXEC,CloseLibrary
		ENDM

;========================================================================
;Here we go !!!
;========================================================================
		opt	o+

InstallEmulator	move.l	a0,a5			;store pointer to cmd line
		st	Flag_Resident+Flags

;===============================================
CheckForAgnus	move.w	$DFF004,d0		;check what kind of agnus
		and.w	#$2000,d0		;we got
		beq.s	.OldAgnus
		st	Flag_FatAgnus+Flags
.OldAgnus
;===============================================
OpenDosLib	OPENLIB	DOS,NoDOS		;open dos library
		CALL	DOS,Output		;get standart output chanel
		move.l	d0,OutHandle		;store it
		beq.s	.Skip			;if not available then skip
		move.l	OutHandle(pc),d1
		move.l	#Hello.TXT,d2
		move.l	#Hello.LEN,d3
		CALL	DOS,Write		;print message
.Skip
;===============================================
SearchFlagOpt	movea.l	a5,a0			;search for any flag options
.SearchLoop	move.b	(a0)+,d0
		cmp.b	#$0A,d0			;jump out of loop if we
		beq.s	Search			;find a return
		cmp.b	#"?",d0			;an request for info ?
		beq.s	.PrintSyntax
		ori.b	#$20,d0			;make lower case (quick &dirty)
		cmp.b	#"a",d0			;ignore BIG-FAT-Agnus ?
		beq.s	.OverRide
		cmp.b	#"r",d0			;non resident install ?
		bne.s	.SearchLoop

.Resident	SF	Flag_Resident+Flags	;clear Resident flag
		bra.s	.SearchLoop
.OverRide	SF	Flag_FatAgnus+Flags	;clear FAT agnus flag
		bra.s	.SearchLoop

.PrintSyntax	move.l	#Syntax.TXT,d2		;Print syntax if we've found
		move.l	#Syntax.LEN,d3		;an '?'
		jmp	PrintMsgAndQuit(pc)

;===============================================
Search		movea.l	a5,a0			;search for install options
.SearchLoop	move.b	(a0),d0
		cmp.b	#"i",d0
		beq.s	InstallNTSC
		cmp.b	#"I",d0
		beq.s	InstallPAL
		ori.b	#$20,d0			;always lower case
		cmp.b	#"k",d0
		beq.s	Remove
		cmp.b	#$0A,(a0)+
		bne.s	.SearchLoop

		lea	MyPortName(PC),a1	;search emulator msg.port
		CALL	EXEC,FindPort		;find my port
		tst.l	d0
		beq.s	InstallNTSC

		move.l	#Already.TXT,d2
		move.l	#Already.LEN,d3
		jmp	PrintMsgAndQuit(pc)

;===============================================
Remove		BSR	RemoveTag
		jmp	PrintMsgAndQuit(pc)

InstallNTSC	sf	Flag_60Hz+Flags		;install and start NTSC
		bra.s	Install
InstallPAL	st	Flag_60Hz+Flags		;install and Start PAL

Install		lea	MyPortName(PC),a1	;search emulator msg.port
		CALL	EXEC,FindPort		;find my port
		tst.l	d0
		beq.s	.NotInstalled

		lea	Message(pc),a1		;send message to emulator
		move.l	#'50'<<16,d2
		tst.b	Flag_60Hz+Flags
		bne.s	.pal
		move.l	#'60'<<16,d2

.pal		move.b	Flag_FatAgnus+Flags(pc),d2
		move.l	d2,20(a1)

		move.l	d0,a0			;found port
		CALL	EXEC,PutMsg

.Waitting	cmp.l	Message.TXT(pc),d2	;waiting for return msg
		beq.s	.Waitting

		tst.l	Message.TXT		;message = 0 ??
		bne.s	.Error			;no -> something wrong

		move.l	#Changed.TXT,d2
		move.l	#Changed.LEN,d3
		bra.w	PrintMsgAndQuit

.Error		move.l	#UnChanged.TXT,d2
		move.l	#UnChanged.LEN,d3
		bra.s	PrintMsgAndQuit

;===============================================
.NotInstalled	BSR	InstallTag		;install Tag
		bne.s	PrintMsgAndQuit

TagInstalled	jsr	4(a0)			;Initialize emulator
		beq.s	EmulatorOn

		BSR	RemoveTag		;kill romtag if something wrong

		move.l	#Failure.TXT,d2
		move.l	#Failure.LEN,d3
		bra.s	PrintMsgAndQuit

EmulatorOn	tst.b	Flag_FatAgnus+Flags
		beq.s	.Skip

		move.l	#NewAgnus.TXT,d2
		move.l	#NewAgnus.LEN,d3
		move.l	OutHandle(pc),d1
		beq.s	.Skip
		CALL	DOS,Write		;print agnus message

.Skip		tst.b	Flag_Resident+Flags
		bne.s	.Skip2

		move.l	#NResident.TXT,d2
		move.l	#NResident.LEN,d3
		move.l	OutHandle(pc),d1
		beq.s	.Skip2
		CALL	DOS,Write		;print resident message

.Skip2		move.l	#Installed.TXT,d2
		move.l	#Installed.LEN,d3

;===============================================
PrintMsgAndQuit	BSR.S	DoMessage

		CLOSELIB	DOS		;close dos library

NoDOS		clr.l	d0
		rts

DoMessage	move.l	OutHandle(pc),d1
		beq.s	.Skip
		CALL	DOS,Write

.Skip		rts

RemoveTag	lea	MyPortName(PC),a1	;search emulator msg.port
		CALL	EXEC,FindPort		;find my port
		tst.l	d0
		bne.s	FoundMyPort

		move.l	#Unable.TXT,d2		;can't find port !
		move.l	#Unable.LEN,d3
		moveq	#0,d0
		rts

FoundMyPort	move.l	d0,a0			;found port
		lea	Message(pc),a1		;send message to emulator
		move.l	#'KILL',20(a1)		;kill MSG
		CALL	EXEC,PutMsg		;to remove itself.

Waitting	cmp.l	#'KILL',Message.TXT	;waiting for return msg
		beq.s	Waitting

		tst.l	Message.TXT		;message = 0 ??
		bne.s	.Error			;no -> something wrong

.Killed		move.l	#Removed.TXT,d2		;all well, print remove
		move.l	#Removed.LEN,d3		;message
		moveq	#-1,d0
		rts

.Error		move.l	#Unable.TXT,d2		;no -> something went
		move.l	#Unable.LEN,d3		;wrong
		moveq	#-1,d0
		rts


;========================================================================
Message		DC.L	0			;message to emulator
		DC.L	0
		DC.B	NT_MESSAGE
		DC.B	0
		DC.L	0
		DC.L	0			;reply port: geen
		DC.W	Message.LEN
Message.TXT	DC.B	"KILL"
Message.LEN	EQU	*-Message.TXT


DOSName		DC.B	"dos.library",$0
		EVEN
DOSBase		DC.L	0
CodeMem		DC.L	0
OutHandle	DC.L	0

Hello.TXT	DC.B	$9B,"33;1m60Hz Emulator"
		DC.B	$9B,"0m V1.05 by P. de Boer",$0A
Hello.LEN	EQU	*-Hello.TXT

Syntax.TXT	DC.B	"This program is shareware, NOT public domain !"
		DC.B	$0A
		DC.B	"Read the 60Hz.DOC file for more information !"
		DC.B	$0A,$0A
		DC.B	$9B,"1;33mSyntax....:"
		DC.B	$9B,"0m 60Hz <OPTIONS>",$0A
		DC.B	$9B,"1;33mOptions...:",$9B,"0m "
		DC.B	"i   = Install emulator and jump in NTSC mode.",$0A
		DC.B	"            "
		DC.B	"I   = Install emulator and jump in PAL mode.",$0A
		DC.B	"            "
		DC.B	"K/k = Kill emulator !",$0A
		DC.B	$9B,"1;33mAdditional:",$9B,"0m "
		DC.B	"R/r = Install emulator NON-resident in memory",$0A
		DC.B	"            "
		DC.B	"A/a = Ignore BIG FAT-Agnus, i.e. always software"
		DC.B	" emulation",$0A,$0A
		DC.B	"When the emulator is installed, ",$0A
		DC.B	"press <LEFT-ALT>+<LEFT-SHIFT>+<CTRL> and ",$0A
		DC.B	"  ESC - To remove the emulator from memory",$0A
		DC.B	"    0 - To toggle between PAL and NTSC",$0A
		DC.B	"    9 - To toggle between Software and Hardware"
		DC.B	" emulation",$0A
		DC.B	"  1-8 - Reserved options (read 60Hz.DOC)",$0A
		DC.B	$0A
Syntax.LEN	EQU	*-Syntax.TXT

NoMem.TXT	DC.B	"Not enough memory for emulator (I only need "
		DC.B	"±2800 Bytes) !",$0A
NoMem.LEN	EQU	*-NoMem.TXT

Changed.TXT	DC.B	"60Hz Emulator flags updated !",$0A
Changed.LEN	EQU	*-Changed.TXT

UnChanged.TXT	DC.B	"Unable to update 60Hz Emulator flags !",$0A
UnChanged.LEN	EQU	*-UnChanged.TXT

Installed.TXT	DC.B	"60Hz Emulator installed !",$0A
Installed.LEN	EQU	*-Installed.TXT

Already.TXT	DC.B	"60Hz Emulator was already installed !",$0A
Already.LEN	EQU	*-Already.TXT

Removed.TXT	DC.B	"60Hz Emulator removed !",$0A
Removed.LEN	EQU	*-Removed.TXT

Unable.TXT	DC.B	"Unable to find Emulator in memory !",$0A
Unable.LEN	EQU	*-Unable.TXT

Failure.TXT	DC.B	"Installation failed !",$0A
Failure.LEN	EQU	*-Failure.TXT

NewAgnus.TXT	DC.B	"Fantastic, you got a BIG FAT-Agnus !!",$0A
NewAgnus.LEN	EQU	*-NewAgnus.TXT

NResident.TXT	DC.B	"Emulator will be gone after reset !",$0A
NResident.LEN	EQU	*-NResident.TXT

		EVEN

;========================================================================
InstallTag	move.l	#TagCodeLen,d0		;allocate memory for
		moveq	#MEMF_CHIP,d1		;emulator
		CALL	EXEC,AllocMem
		tst.l	d0
		bne.s	CopyTagCode

		move.l	#NoMem.TXT,d2		;not enough memory ??
		moveq	#NoMem.LEN,d3
		moveq	#-1,d0
		rts

CopyTagCode	movea.l	d0,a5			;a5 holds address of copy
		lea	TagCode(PC),a0		;copy code
		movea.l	d0,a1
		move.l	#TagCodeLen,d0
		CALL	EXEC,CopyMem

		lea	Flags(pc),a0
		tst.b	Flag_Resident(a0)
		beq.s	InitPort

		CALL	EXEC,Forbid		;shut down system
		CALL	EXEC,Disable

		lea	RomTagPtrs-TagCode(a5),a0
		move.l	KickTagPtr(a6),4(a0)
		beq.s	NoTagYet
		bset	#7,4(a0)		;set bit 31 if tag was set

NoTagYet	move.l	a0,KickTagPtr(a6)	;install my romtag
		lea	MyRomTag-TagCode(a5),a1
		move.l	a1,(a0)

InitMemList	lea	MyMemList-TagCode(a5),a1
		move.l	a5,ML_SIZE+ME_ADDR(a1)
		move.l	KickMemPtr(a6),d0
		move.l	d0,LN_SUCC(a1)
		move.l	a1,KickMemPtr(a6)	;install my memlist

InitRomTag	lea	MyRomTag-TagCode(a5),a1
		move.l	a1,RT_MATCHTAG(a1)
		lea	RT_SIZE(a1),a2
		move.l	a2,RT_ENDSKIP(a1)
		lea	ProjectName-TagCode(a5),a2
		move.l	a2,RT_NAME(a1)
		move.l	a5,RT_INIT(a1)
		CALL	EXEC,SumKickData
		move.l	d0,KickCheckSum(a6)	;recalculate checksum

InitPort	lea	MyPortName-TagCode(a5),a0
		lea	MyPort-TagCode(a5),a1
		clr.l	LN_SUCC(a1)
		clr.l	LN_PRED(a1)
		move.l	a0,LN_NAME(a1)
		CALL	EXEC,AddPort		;add my port

		CALL	EXEC,Enable		;powerup system
		CALL	EXEC,Permit

Exit		movea.l	a5,a0			;pass code address
		clr.l	d0
		rts

;========================================================================
;This piece of code will be resident in memory !
;========================================================================

		opt	p+		;code MUST be PC-relative !!!!

TagCode		JMP	InitTagCode(pc)		;jump table
		JMP	InitEmulator(pc)

;========================================================================
InitTagCode	movem.l	d0-d7/a0-a6,-(SP)	;this routine
						;will be executed
		lea	MyPort(pc),a1		;in the reset routine
		lea	MyPortName(PC),a0
		clr.l	LN_SUCC(a1)
		clr.l	LN_PRED(a1)
		move.l	a0,LN_NAME(a1)
		CALL	EXEC,AddPort

		BSR.S	InitEmulator		;re-init emulator

EndTagCode	movem.l	(SP)+,d0-d7/a0-a6	;return to kicky
		rts

;========================================================================
InitEmulator	BSR.S	InitStructures

		lea	Flags(pc),a0
		sf	Flag_Enable(a0)	;prevent interrupt
		BSR.S	OpenIntLib	;open int library (for alerts)
		BSR.S	OpenResource	;open ciab.resource (for timer)
		BNE.S	.Failure
		BSR	PatchSystem	;patch system to pal/ntsc
		BNE.S	.Failure
		BSR	AddServers	;add ciaa & vblank servers

		clr.l	d0
.Failure	rts

;========================================================================
Alert		clr.l	d0			;display alert
		moveq	#32,d1
		movea.l	INTBase(pc),a6
		jsr	_LVODisplayAlert(a6)
		rts
;===============================================
OpenIntLib	lea	INTName(pc),a1
		clr.l	d0
		CALL	EXEC,OpenLibrary
		lea	INTBase(pc),a0
		move.l	d0,(a0)
		bne.s	.Skip
		rts
		move.l	#$04030000,d7		;software failure
		lea	$FC00D2,a5		;if intuition.library
		CALL	EXEC,Alert		;didn't open
.Skip		rts
;===============================================
InitStructures	lea	TagCode(pc),a0		;relocate structures !
		move.l	a0,d0
		lea	CIAA_Server(pc),a0
		BSR.S	.InitStruct
		lea	CIAB_Server(pc),a0
		BSR.S	.InitStruct
		lea	VBI_Server(pc),a0
.InitStruct	add.l	d0,LN_NAME(a0)
		add.l	d0,is_data(a0)
		add.l	d0,is_code(a0)
		lea	InitStructures(pc),a0
		move.w	#$4E75,(a0)		;put RTS at InitStruct
		rts
;===============================================
OpenResource	lea	CIABName(pc),a1		;open ciab.resource
		clr.l	d0
		CALL	EXEC,OpenResource
		lea	CIABBase(pc),a0
		move.l	d0,(a0)
		beq.s	.Error
		moveq	#0,d0
		rts
.Error		lea	NoResourceAlert(pc),a0	;no resource ??
		BSR	Alert
		moveq	#-1,d0
		rts

;===============================================
AllocateTimer	move.b	#0,d0			;timer a interrupt
		lea	CIAB_Server(pc),a1
		move.l	CIABBase(pc),a6
		jsr	-6(a6)			;set interrupt
		lea	TimerStat(pc),a0
		move.l	d0,(a0)			;store timer status
		beq.s	.Ok

.Error		lea	NoTimerAlert(pc),a0	;no timer !
		BSR	Alert
		moveq	#-1,d0
.Ok		rts

;===============================================
DeAllocateTimer	lea	TimerStat(pc),a0
		tst.l	(a0)
		bne.s	.Skip
		move.b	#0,d0
		move.l	CIABBase(pc),a6
		jsr	-12(a6)			;clr interrupt

		lea	TimerStat(pc),a0
		moveq	#-1,d0
		move.l	d0,(a0)

.Skip		rts

;===============================================
AddServers	lea	Flags(pc),a0
		sf	Flag_Enable(a0)

		move.w	#$0005,d0		;vertical blank interrupt
		lea	VBI_Server(pc),a1
		CALL	EXEC,AddIntServer
		move.w	#$0003,d0		;keyboard interrupt
		lea	CIAA_Server(pc),a1
		CALL	EXEC,AddIntServer

		lea	Flags(pc),a0
		st	Flag_Enable(a0)

		rts
;===============================================
PatchSystem	lea	Flags(pc),a0		;this routine does it !
		tst.b	Flag_60Hz(a0)
		beq.s	Go60

;===============================================
Go50		tst.b	Flag_FatAgnus(a0)	;put system in PAL mode
		bne.s	.Go50
		BSR.S	DeAllocateTimer		;free timers

.Go50		move.w	#$0020,BEAMCON0!CUSTOM	;very simple if we have a FAT

		lea	Flags(pc),a0
		sf	Flag_Enable(a0)		;stop it

		lea	GFXName(PC),a1
		clr.l	d0
		CALL	EXEC,OpenLibrary	;open graphics library
		move.l	d0,a1
		move.w	gb_DisplayFlags(a1),d0	;adjust display flags
		and.b	#%11111110,d0		;erase NTSC flag
		or.b	#%00000100,d0		;set PAL flag
		move.w	d0,gb_DisplayFlags(a1)
		move.w	#256,gb_NormalDisplayRows(a1)
		move.w	#311,gb_DisplayRows(a1)
		or.b	#LIBF_CHANGED,LIB_FLAGS(a1)
		movea.l	a1,a2
		CALL	EXEC,SumLibrary		;caluclate checksum
		movea.l a2,a1
		CALL	EXEC,CloseLibrary	;close lib again
		move.b	#50,VBlankFrequency(a6)	;change EXEC library
		or.b	#LIBF_CHANGED,LIB_FLAGS(a6)
		CALL	EXEC,SumLibrary		;calculate checksum
		moveq	#0,d0
		rts

;===============================================
Go60		tst.b	Flag_FatAgnus(a0)	;put system in NTSC mode
		beq.s	.NoFatty

		move.w	#$0000,BEAMCON0!CUSTOM	;to NTSC please
		bra.s	.Go60

.NoFatty	move.w	#$0020,BEAMCON0!CUSTOM
		BSR	AllocateTimer		;allocate timers again
		bne.s	.Error			;not available

.Go60		lea	GFXName(PC),a1		;modify graphics.library
		clr.l	d0
		CALL	EXEC,OpenLibrary
		move.l	d0,a1
		move.w	gb_DisplayFlags(a1),d0
		and.b	#%11111011,d0
		or.b	#%00000001,d0
		move.w	d0,gb_DisplayFlags(a1)
		move.w	#200,gb_NormalDisplayRows(a1)
		move.w	#262,gb_DisplayRows(a1)
		or.b	#LIBF_CHANGED,LIB_FLAGS(a1)
		movea.l	a1,a2
		CALL	EXEC,SumLibrary
		movea.l	a2,a1
		CALL	EXEC,CloseLibrary
		move.b	#60,VBlankFrequency(a6)
		or.b	#LIBF_CHANGED,LIB_FLAGS(a6)
		CALL	EXEC,SumLibrary

		lea	Flags(pc),a0
		st	Flag_Enable(a0)		;start it
		moveq	#0,d0
		rts

.Error		lea	Flags(pc),a0
		st	Flag_60Hz(a0)
		sf	Flag_Enable(a0)		;stop it
		moveq	#-1,d0
		rts

;========================================================================
CIAA_Server_Int	movem.l	d0-d7/a0-a6,-(SP)	;keyboard interrupt

		st	d1			;server
		lea	EnterKeys(pc),a0
		move.b	CIAA!SPx,d5
		not.b	d5
		ror.b	#1,d5
		bmi.s	.NotPressed
		sf	d1
.NotPressed	andi.b	#$7F,d5
.TestCTRL	cmp.b	#$63,d5
		bne.s	.TestSHIFT
		move.b	d1,(a0)		
.TestSHIFT	cmp.b	#$60,d5
		bne.s	.TestALT
		move.b	d1,1(a0)		
.TestALT	cmp.b	#$64,d5
		bne.s	.EndTest
		move.b	d1,2(a0)		
.EndTest	tst.b	d1
		bne.s	CIAA_Return

AreWeInControl	tst.l	(a0)
		bne.s	CIAA_Return
		cmp.b	#$45,d5
		beq.s	.Go
		cmp.b	#$0A,d5
		bgt.s	CIAA_Return

.Go		ori.b	#$40,CIAA!CRA
		nop
		nop
		andi.b	#$BF,CIAA!CRA
		nop
		nop
		clr.b	CIAA!SPx

		lea	Flags(pc),a5
		BSR	CheckESC
		tst.b	Flag_Enable(a5)
		beq.s	.Skip
		tst.b	Flag_FatAgnus(a5)
		bne.s	.Skip
		BSR	CheckColor		;check keys
		BSR	CheckTime
		BSR	CheckStartPos
		BSR	CheckEndPos
		BSR	CheckDefault
.Skip		BSR	Check60Hz
		BSR	CheckAgnus
CIAA_Return	movem.l	(SP)+,d0-d7/a0-a6
		DC.W	$003C,$0004
		rts

;========================================================================
VBI_Server_Int	movem.l	d0-d7/a0-a6,-(SP)	;vertical blank interrupt

		lea	Flags(pc),a0		;server
		tst.b	Flag_Enable(a0)		;are we in 60Hz mode ?
		beq.s	.Skip			;if not then skip

		lea	CUSTOM,a0		;custom chip base
		move.w	DMACONR(a0),d1		;read DMACON
		move.w	#$0440,DMACON(a0)	;clear blitpri & blitdma

		clr.w	d0
		move.b	VHPOSR(a0),d0		;read actual beam pos
		neg.w	d0			;make it negative
		add.w	EndPos(pc),d0		;add endpos
		mulu	#45,d0			;multiply with cycle/line
		sub.w	VBITime(pc),d0		;subtrack buffer time
		move.b	d0,$bfd400		; Timer A high byte
		lsr.w	#8,d0			;get low byte
		move.b	d0,$bfd500		; Timer A low byte
		move.b	#%00011001,$BFDE00	;load timer-a

		andi.w	#$0440,d1		;reinstall dma again
		ori.w	#$8000,d1
		move.w	d1,DMACON(a0)		;write it in dmacon

.Skip		BSR.S	CheckForMSG		;check for incoming msg

		movem.l	(SP)+,d0-d7/a0-a6
		DC.W	$003C,$0004		;or.w #$4,SR ;set Z-Flag
		rts

;========================================================================
CIAB_Server_Int	movem.l	d0-d2/a0,-(a7)
		lea	Flags(pc),a0
		tst.b	Flag_Enable(a0)
		beq.s	GetOutOfHere
		lea	CUSTOM,a0		;get custom base address
		move.w	DMACONR(a0),d2		;read dma
		move.w	#$0440,DMACON(a0)	;clear blitpri&blitdma
		move.w	EndPos(pc),d1
		lsl.w	#8,d1
		ori.w	#$D1,d1
StartColor	move.w	#$0505,$1FE(a0)		;put colorbar if needed
		move.w	EndPos(pc),d0		;wait for line 'EndPos'
WaitLine	cmp.b	VHPOSR(a0),d0
		bgt.s	WaitLine
		move.w	d1,DIWSTOP(a0)
EndColor	move.w	#$0000,$1FE(a0)
		move.l	StartPos(pc),d1
		move.l	VPOSR(a0),d0		;prevents shocking
		sub.l	VPOSR(a0),d0		;of menu bar
		sub.l	d0,d1			;subtract it from StartPos
		add.l	VPOSR(a0),d1		;add current beam pos
		move.l	d1,VPOSW(a0)		;write new beampos
		andi.w	#$0440,d2		;
		ori.w	#$8000,d2
		move.w	d2,DMACON(a0)		;enable blitpri&blitdma
GetOutOfHere	movem.l	(a7)+,d0-d2/a0		;return to system
		rts

;========================================================================
CheckForMSG	lea	MyPort(pc),a0		;check message port
		CALL	EXEC,GetMsg		;get message
		tst.l	d0
		beq.s	.NoMSG			;mmmm.. no message

		move.l	d0,a5			;got message, yeah !
		cmp.l	#"KILL",20(a5)		;is it a 'KILL' message ??
		beq.s	MSG.Kill

		move.l	20(a5),d2

		lea	Flags(pc),a4
		cmp.b	Flag_FatAgnus(a4),d2	;change in emulation mode ?
		beq.s	.Skip			;no then skip

		tst.b	Flag_FatAgnus(a4)
		beq.s	.DeAllocTimer
		BSR	AllocateTimer
		bne.s	.Skip

.DeAllocTimer	BSR	DeAllocateTimer
		not.b	Flag_FatAgnus(a4)

.Skip		swap	d2
		cmp.w	#"50",d2
		beq.s	MSG.50Hz
		cmp.w	#"60",d2
		beq.s	MSG.60Hz

.Exit		move.l	d0,20(a5)

.NoMSG		rts

MSG.50Hz	lea	Flags(pc),a0		;recieved 50Hz message
		tst.b	Flag_60Hz(a0)
		bne.s	.Already50Hz
		st	Flag_60Hz(a0)
		BSR	PatchSystem
.Already50Hz	clr.l	20(a5)
		rts

MSG.60Hz	lea	Flags(pc),a0		;recieved 60Hz message
		tst.b	Flag_60Hz(a0)
		beq.s	.Already60Hz
		sf	Flag_60Hz(a0)
		BSR	PatchSystem
		move.l	d0,20(a5)
		rts
.Already60Hz	clr.l	20(a5)
		rts

MSG.Kill	BSR	KillEmulator		;yes? -> kill me (?)
		move.l	d0,20(a5)
		rts

;========================================================================
;Subroutines for key checking !
;
;=======================================
CheckColor	cmp.b	#$01,d5
		bne.s	.NoColorFlag
		lea	EndColor+4(pc),a1	;self-modifying code
		lea	StartColor+4(pc),a2	;not so nice, but it works
		not.b	Flag_Color(a5)
		beq.s	.ColorOff
.ColorOn	move.w	#COLOR00,(a1)		;
		move.w	#COLOR00,(a2)
		bra.s	.NoColorFlag
.ColorOff	move.w	#$01FE,(a1)		;copper instruction $1FE
		move.w	#$01FE,(a2)		;is NO-OP (no operation)
.NoColorFlag	rts
;=======================================
CheckAgnus	cmp.b	#$09,d5
		bne.s	.NoAgnusCheck

		move.w	$DFF004,d0		;check what kind of agnus
		and.w	#$2000,d0		;we got
		beq.s	.NoAgnusCheck		;no big agnus ? how lame !

		tst.b	Flag_FatAgnus(a5)
		beq.s	.DeAllocTimer
		BSR	AllocateTimer
		bne.s	.NoAgnusCheck

.DeAllocTimer	BSR	DeAllocateTimer
		not.b	Flag_FatAgnus(a5)

.Skip		BSR	PatchSystem

.NoAgnusCheck	rts
;=======================================
CheckTime	lea	VBITime(pc),a1
		cmp.b	#$03,d5
		beq.s	.NoAdd
		sub.w	#45,(a1)
.NoAdd		cmp.b	#$04,d5
		beq.s	.NoSub
		add.w	#45,(a1)
.NoSub		rts
;=======================================
CheckStartPos	lea	StartPos(pc),a1
		cmp.b	#$05,d5
		beq.s	.NoAdd
		add.l	#$0100,(a1)
.NoAdd		cmp.b	#$06,d5
		beq.s	.NoSub
		sub.l	#$0100,(a1)
.NoSub		rts
;=======================================
CheckEndPos	lea	EndPos(pc),a1
		cmp.b	#$07,d5
		beq.s	.NoAdd
		addq.w	#$0001,(a1)
.NoAdd		cmp.b	#$08,d5
		beq.s	.NoSub
		subq.w	#$0001,(a1)
.NoSub		rts
;=======================================
CheckDefault	cmp.b	#$02,d5
		bne.s	.Skip
		lea	EndPos(pc),a1
		move.w	#$0104,(a1)
		lea	StartPos(pc),a1
		move.l	#$3203,(a1)
		lea	VBITime(pc),a1
		move.w	#8*45,(a1)
.Skip		rts
;=======================================
Check60Hz	cmp.b	#$0A,d5
		bne.s	.Skip
		not.b	Flag_60Hz(a5)
		BSR	PatchSystem
.Skip		rts
;=======================================
CheckESC	cmp.b	#$45,d5
		bne.s	.Skip
		BSR.S	KillEmulator
.Skip		rts
;========================================================================
KillEmulator	CALL	EXEC,Disable
		move.w	#$0005,d0		;vertical blank interrupt
		lea	VBI_Server(pc),a1
		CALL	EXEC,RemIntServer
		move.w	#$0003,d0		;keyboard interrupt
		lea	CIAA_Server(pc),a1
		CALL	EXEC,RemIntServer
		lea	Flags(pc),a0
		st	Flag_60Hz(a0)
		BSR	PatchSystem
		BSR.S	RemoveRomTag
		move.l	d0,d2
		CALL	EXEC,Enable
		rts
;========================================================================
RemoveRomTag	lea	MyPortName(PC),a1
		CALL	EXEC,FindPort		;find my port
		tst.l	d0
		bne.s	FoundPort
		moveq	#-1,d0			;no port found ??
		rts

FoundPort	move.l	d0,a4			;a4 = pointer to MyPort
		move.l	a4,a1
		CALL	EXEC,RemPort		;remove it !

		lea	Flags(pc),a0
		tst.b	Flag_Resident(a0)
		beq.s	FreeMemory

		CALL	EXEC,Forbid		;shut down system
		CALL	EXEC,Disable

		lea	MyMemList-MyPort(a4),a1	;find my memlist
		lea	KickMemPtr(a6),a0
FindMyMemList	move.l	LN_SUCC(a0),d0
		cmp.l	d0,a1
		beq.s	FoundMyList
		move.l	d0,a0
		bra.s	FindMyMemList

FoundMyList	move.l	LN_SUCC(a1),d0		;clear memlist from system
		move.l	d0,LN_SUCC(a0)		; LN_SUCC = 0, so this also
						; works for KickMemPtr

		move.l	RomTagPtrs-MyPort+4(a4),KickTagPtr(a6)
		beq.s	NoSecondTag
		bclr	#7,KickTagPtr(a6)	;clear bit 31

NoSecondTag	CALL	EXEC,SumKickData

		tst.l	KickTagPtr(a6)		;check if one of the
		bne.s	.Set			;ptrs is set
		tst.l	KickMemPtr(a6)		;if so, then no need
		bne.s	.Set			;to calc checksum
		clr.l	d0			;so, clear it !

.Set		move.l	d0,KickCheckSum(a6)	;restore old 

		CALL	EXEC,Enable		;power up system
		CALL	EXEC,Permit

FreeMemory	lea	TagCode-MyPort(a4),a1	;free tag memory
		move.l	#TagCodeLen,d0
		CALL	EXEC,FreeMem

		clr.l	d0
		rts

;========================================================================
CIAA_Server	DC.L	0			;interrupt server structure
		DC.L	0
		DC.B	2			;type = interrupt
		DC.B	127			;pri
		DC.L	ProjectName-TagCode
		DC.L	CIAA_Server-TagCode
		DC.L	CIAA_Server_Int-TagCode

CIAB_Server	DC.L	0
		DC.L	0
		DC.B	2			;type = interrupt
		DC.B	127			;pri
		DC.L	ProjectName-TagCode
		DC.L	CIAB_Server-TagCode
		DC.L	CIAB_Server_Int-TagCode

VBI_Server	DC.L	0
		DC.L	0
		DC.B	2			;type = interrupt
		DC.B	127			;pri
		DC.L	ProjectName-TagCode
		DC.L	VBI_Server-TagCode
		DC.L	VBI_Server_Int-TagCode

MyMemList	DS.B	LN_SIZE			;memlist structure
		DC.W	1
		DC.L	NULL
		DC.L	TagCodeLen

MyPort		DC.L	NULL			;message port structure
		DC.L	NULL
		DC.B	NT_MSGPORT
		DC.B	0
		DC.L	NULL
		DC.B	0
		DC.B	0
		DC.L	NULL
		DS.B	14

MyRomTag	DC.W	RTC_MATCHWORD		;resident structure
		DC.L	NULL
		DC.L	NULL
		DC.B	RTF_COLDSTART
		DC.B	1
		DC.B	0
		DC.B	-10
		DC.L	NULL
		DC.L	NULL
		DC.L	0

;========================================================================
ProjectName	DC.B	"60Hz emulator V1.05 by P. de Boer",$0
MyPortName	DC.B	"60Hz emulator V1.05 (port)",$0
		EVEN

CIABName	DC.B	"ciab.resource",$0
INTName		DC.B	"intuition.library",$0
GFXName		DC.B	"graphics.library",$0
		EVEN

NoTimerAlert	DC.W	20
		DC.B	17
		DC.B	"Emulator failure: "
		DC.B	"CIA-B Timer-A already in use !",$0
		EVEN

NoResourceAlert	DC.W	20
		DC.B	17
		DC.B	"Emulator failure: "
		DC.B	"Unable to open ciab.resource !",$0
		EVEN

StartPos	DC.L	$3202		;Start pos
EndPos		DC.W	$0104

Flags		DCB.B	8
EnterKeys	DC.L	$FFFFFF00
VBITime		DC.W	8*45		;buffer time (important !)

TimerStat	DC.L	-1
INTBase		DC.L	0
CIABBase	DC.L	0
RomTagPtrs	DCB.L	2,$0

TagCodeLen	EQU	*-TagCode

;========================================================================
;|
