
;       PClock V1.0
;	by Preben Nielsen.
;	Assemble it as case-sensitive.
;	OPT O+
;	OPT O1+		;Tells when a branch could be optimised to short
;	OPT i+		;Tells when '#' is probably missing

;TWELVE_HOURS		;Uncomment this line if you don't want
			;a 24-hour clock

	incdir	"INCLUDE:"
	include "devices/timer.i"
	include "exec/memory.i"
	include "exec/devices.i"
	include "exec/exec_lib.i"
	include "graphics/graphics_lib.i"
	include "intuition/intuition.i"
	include "intuition/intuition_lib.i"
	include "libraries/dosextens.i"
	include "libraries/dos_lib.i"

;TWELVE_HOURS		;Uncomment this line if you don't want
			;a 24-hour clock

* A few macro's
LoadBase	MACRO
		IFNC		'\1','ExecBase'
		movea.l		\1(A5),A6
		ENDC
		IFC		'\1','ExecBase'
		movea.l		4.W,A6
		ENDC
		ENDM
CallLib		MACRO
		jsr		_LVO\1(A6)
		ENDM
Call		MACRO
		bsr		\1
		ENDM
Push		MACRO
		movem.l		\1,-(SP)
		ENDM
Pop		MACRO
		movem.l		(SP)+,\1
		ENDM

* Offsets into datablock
WBenchMsg	=0
DosBase		=4
GfxBase		=8
IntBase		=12
TimerPort	=16
TimerOpen	=20
Counter		=22
CWindow		=24
Rp		=28
Up		=32
Stamp		=36		;\
Days		=36		; \struct DateStamp Stamp;
Minutes		=40		; /
Ticks		=44		;/
TimeReq		=48		;struct TimeRequest TimeReq; (Size IOTV_SIZE)
RelDataSize	=48+4+IOTV_SIZE

		SECTION CLOCKCODE,CODE
DBase		EQUR		A5
PClock		Push		D0-D7/A0-A6
		lea		-RelDataSize(SP),SP	;Make room on stack for data
		movea.l		SP,DBase
		moveq		#RelDataSize-1,D0
ClrData		clr.b		(DBase)+		;Clear data area
		dbf		D0,ClrData
		movea.l		SP,DBase
		LoadBase	ExecBase
		suba.l		A1,A1
		CallLib		FindTask		;Find us
		movea.l		D0,A2
		tst.l		pr_CLI(A2)		;pr_CLI
		bne.S		CLIAndWBStartup
WBenchStartup	lea		pr_MsgPort(A2),A0	;pr_MsgPort
		CallLib		WaitPort		;Wait for a message
		lea		pr_MsgPort(A2),A0
		CallLib		GetMsg			;Then get it
		move.l		D0,WBenchMsg(DBase)	;Save it for later reply
CLIAndWBStartup	lea		DosName(PC),A1
		CallLib		OldOpenLibrary		;Open dos
		move.l		D0,DosBase(DBase)
		beq.S		Exit
		lea		GfxName(PC),A1
		CallLib		OldOpenLibrary		;Open graphics
		move.l		D0,GfxBase(DBase)
		beq.S		Exit
		lea		IntuiName(PC),A1
		CallLib		OldOpenLibrary		;Open Intuition
		move.l		D0,IntBase(DBase)
		beq.S		Exit
		lea		FileNW(PC),A0
		LoadBase	IntBase
		CallLib		OpenWindow		;Open the Window
		move.l		D0,CWindow(DBase)
		beq.S		Exit
		movea.l		D0,A0
		move.l		50(A0),Rp(DBase)	;Get RastPort
		move.l		86(A0),Up(DBase)	;Get UserPort
		suba.l		A1,A1
		lea		ScrTitle(PC),A2
		CallLib		SetWindowTitles
		lea		TxtAttr(PC),A0
		LoadBase	GfxBase
		CallLib		OpenFont
		tst.l		D0
		beq.S		Exit
		movea.l		D0,A0
		movea.l		Rp(DBase),A1
		CallLib		SetFont			;Have to be sure to use the right size of font
		moveq		#0,D0
		movea.l		Rp(DBase),A1
		CallLib		SetAPen
		moveq		#1,D0
		movea.l		Rp(DBase),A1
		CallLib		SetBPen
		Call		OpenTimer
		beq.S		Main			;Lets begin
* Here we exit
Exit		Call		CloseTimer
CloseWin	LoadBase	IntBase
		move.l		CWindow(DBase),D0
		beq.S		CloseIntui
		movea.l		D0,A0
		CallLib		CloseWindow		;Close window if it is open
CloseIntui	LoadBase	ExecBase
		move.l		IntBase(DBase),D0
		beq.S		CloseGfx
		movea.l		D0,A1
		CallLib		CloseLibrary		;Close intuition if it is open
CloseGfx	move.l		GfxBase(DBase),D0
		beq.S		CloseDos
		movea.l		D0,A1
		CallLib		CloseLibrary		;Close graphics if it is open
CloseDos	move.l		DosBase(DBase),D0
		beq.S		ReplyWB
		movea.l		D0,A1
		CallLib		CloseLibrary		;Close graphics if it is open
ReplyWB		move.l		WBenchMsg(DBase),D2
		beq.S		AllDone
		CallLib		Forbid
		movea.l		D2,A1
		CallLib		ReplyMsg		;Reply WBenchMessage if we are started from WB
AllDone		lea		RelDataSize(DBase),SP	;Restore SP
		Pop		D0-D7/A0-A6
		moveq		#0,D0
		rts

* Here we really begin	
Main
StartTimer	Call		WriteStr
EventLoop	LoadBase	ExecBase
		moveq		#0,D0
		moveq		#0,D1
		movea.l		Up(DBase),A0
		move.b		MP_SIGBIT(A0),D1
		bset		D1,D0
		movea.l		TimerPort(DBase),A0
		move.b		MP_SIGBIT(A0),D1
		bset		D1,D0
		CallLib		Wait
		movea.l		TimerPort(DBase),A0
		CallLib		GetMsg			;Message from Timer
		tst.l		D0
		bne.S		StartTimer
GetNextMsg	movea.l		Up(DBase),A0		;Has to be message from intuition
		CallLib		GetMsg			;Get the message
		tst.l		D0
		beq.S		EventLoop		;Did we get a message
		movea.l		D0,A1			;Yes, put message address to A1 and reply
		move.l		20(A1),D7
		CallLib		ReplyMsg		;Reply the message
		cmpi.l		#CLOSEWINDOW,D7
		beq		Exit
		bra.S		GetNextMsg

;Writes available memory, writes time, and sends a timer-request
WriteStr	Push		D0-D7/A0-A6
		LoadBase	DosBase
		lea		Stamp(DBase),A0
		move.l		A0,D1
		CallLib		DateStamp		;Get current time
		Call		StampToStr
		subq.w		#1,Counter(DBase)
		bgt.S		DontUpdateMem
		move.w		#4,Counter(DBase)
		Call		MemToStr		;Updates memory every 4 seconds
DontUpdateMem	LoadBase	GfxBase
		moveq		#30,D0
		moveq		#7,D1
		movea.l		Rp(DBase),A1
		CallLib		Move
		moveq		#36,D0
		lea		AllStr(PC),A0
		movea.l		Rp(DBase),A1
		CallLib		Text
		Call		SendTimerReq
		Pop		D0-D7/A0-A6
		rts

StampToStr	Push		D0-D7/A0-A6
		move.l		Minutes(DBase),D2
		divu		#60,D2
		move.l		D2,D1
		swap		D1
	IFD TWELVE_HOURS
		ext.l		D2
		divu		#12,D2
		swap		D2
	ENDC
		move.l		Ticks(DBase),D0
		divu		#50,D0
		move.w		D0,-(SP)		;Seconds
		move.w		D1,-(SP)		;Minutes
		move.w		D2,-(SP)		;Hours
		pea		TimeFormat
		pea		TimeStr			;Destination
		Call		Format			;sprintf(fmt,Destination,Hour,Minute,Second);
		lea		14(SP),SP
		Pop		D0-D7/A0-A6
		rts

MemToStr	Push		D0-D7/A0-A6
		LoadBase	ExecBase
		moveq		#MEMF_CHIP,D1
		CallLib		AvailMem	;AvailMem(MEMF_CHIP);
		move.l		D0,-(SP)
		pea		MemFormat(PC)
		pea		ChipStr(PC)
		Call		Format
		lea		12(SP),SP
		moveq		#MEMF_FAST,D1
		CallLib		AvailMem	;AvailMem(MEMF_FAST);
		move.l		D0,-(SP)
		pea		MemFormat(PC)
		pea		FastStr(PC)
		Call		Format
		lea		12(SP),SP
		lea		AllStr(PC),A0
		move.b		#' ',13(A0)
		move.b		#' ',27(A0)
		Pop		D0-D7/A0-A6
		rts

* ----------------------- Misc sub-routines ----------------------------
;Format(Buffer,Format-string,datastream);
Format		Push		D0-D1/A0-A3
		move.l		4*6+4(SP),A3	;Destination buffer
		move.l		4*6+8(SP),A0	;Format-string
		lea		4*6+12(SP),A1	;Data on stack
		lea		PutChar(PC),A2	;Function
		LoadBase	ExecBase
		CallLib		RawDoFmt
		Pop		D0-D1/A0-A3
		rts
PutChar		move.b		D0,(A3)+
		rts

;A0=Port Name, D0=Priority
;Returns 0 if it failed address of port if it had success
CreatePort	Push		D1-D7/A0-A6
		LoadBase	ExecBase
		move.l		D0,D5
		movea.l		A0,A5
		moveq		#-1,D0
		CallLib		AllocSignal
		move.b		D0,D6
		bmi.S		CPortFailed			;AllocSignal returned -1
		moveq		#MP_SIZE,D0
		move.l		#MEMF_PUBLIC!MEMF_CLEAR,D1
		CallLib		AllocMem
		move.l		D0,D7
		beq.S		CNoMemory
		movea.l		D0,A2
		move.l		A5,MP+LN_NAME(A2)		;MsgPort->mp_Node.ln_Name=Name;
		move.b		D5,MP+LN_PRI(A2)		;MsgPort->mp_Node.ln_Pri =Pri;
		move.b		#NT_MSGPORT,MP+LN_TYPE(A2)	;MsgPort->mp_Node.ln_Type=NT_MSGPORT;
		clr.b		MP_FLAGS(A2)			;MsgPort->mp_Flags	 =PA_SIGNAL;
		move.b		D6,MP_SIGBIT(A2)		;MsgPort->mp_SigBit	 =MPSigBit;
		suba.l		A1,A1
		CallLib		FindTask
		move.l		D0,MP_SIGTASK(A2)		;MsgPort->mp_SigTask	 =FindTask(0);
		movea.l		A2,A1
		CallLib		AddPort
		move.l		D7,D0
		bra.S		DoneCPort
CNoMemory	move.b		D6,D0
		CallLib		FreeSignal
CPortFailed	moveq		#0,D0
DoneCPort	Pop		D1-D7/A0-A6			;Returns 0 or address of port
		rts
;A1=MsgPort
DeletePort	Push		D0-D7/A0-A6
		LoadBase	ExecBase
		movea.l		A1,A2
		CallLib		RemPort
		moveq		#0,D0
		move.b		MP_SIGBIT(A2),D0
		CallLib		FreeSignal
		movea.l		A2,A1
		moveq		#MP_SIZE,D0
		CallLib		FreeMem
		Pop		D0-D7/A0-A6
		rts

;Send a timer request for 1 second
SendTimerReq	Push		D0-D1/A0-A1/A6
		LoadBase	ExecBase
		lea		TimeReq(DBase),A1
		move.l		TimerPort(DBase),IO+MN_REPLYPORT(A1);TimeReq.tr_node.io_Message.mn_ReplyPort=Rep;
		move.w		#TR_ADDREQUEST,IO_COMMAND(A1)	;TimeReq.tr_node.io_Command		=TR_ADDREQUEST;
		clr.b		IO_FLAGS(A1)			;TimeReq.tr_node.io_Flags		=0;
		clr.b		IO_ERROR(A1)			;TimeReq.tr_node.io_Error		=0;
		move.l		#1,IOTV_TIME+TV_SECS(A1)	;TimeReq.tr_time.tv_Secs		=1;
		clr.l		IOTV_TIME+TV_MICRO(A1)		;TimeReq.tr_time.tv_Micros		=0;
		CallLib		SendIO
		Pop		D0-D1/A0-A1/A6
		rts

;Creates port and opens timer.device
;Returns -1 if it failed and 0 if it had success
OpenTimer	Push		D1/A0-A1/A6
		lea		PortName(PC),A0
		moveq		#0,D0				;Priority
		Call		CreatePort
		move.l		D0,TimerPort(DBase)
		beq.S		OTimerFailed
		lea		TimerName(PC),A0
		lea		TimeReq(DBase),A1
		moveq		#UNIT_VBLANK,D0
		moveq		#0,D1
		LoadBase	ExecBase
		CallLib		OpenDevice
		tst.l		D0
		beq.S		DoneOTimer
		movea.l		TimerPort(DBase),A1
		Call		DeletePort
OTimerFailed	moveq		#-1,D0
DoneOTimer	move.w		D0,TimerOpen(DBase)		;0 or -1
		Pop		D1/A0-A1/A6
		rts

;Deletes port and closes timer.device
CloseTimer	Push		D0-D1/A0-A1/A6
		tst.w		TimerOpen(DBase)
		bne.S		DoneCloseTimer
		LoadBase	ExecBase
		lea		TimeReq(DBase),A1
		CallLib		AbortIO
		lea		TimeReq(DBase),A1
		CallLib		CloseDevice
		movea.l		TimerPort(DBase),A1
		Call		DeletePort
DoneCloseTimer	Pop		D0-D1/A0-A1/A6
		rts

DosName		dc.b		'dos.library',0
GfxName		dc.b		'graphics.library',0
IntuiName	dc.b		'intuition.library',0
TimerName	dc.b		'timer.device',0
PortName	dc.b		'PClock Port',0
		EVEN

FileNW		dc.w		217,0,373,10
		dc.b		0,1
		dc.l		CLOSEWINDOW,WINDOWCLOSE!WINDOWDRAG!WINDOWDEPTH!RMBTRAP,0,0,0,0,0
		dc.w		0,0,0,0,WBENCHSCREEN

TxtAttr		dc.l		FontName
		dc.w		TOPAZ_EIGHTY
		dc.b		FS_NORMAL,FPB_ROMFONT

FontName	dc.b		'topaz.font',0

ScrTitle	dc.b		'PClock V1.0 by Preben Nielsen in 1990. This is Public Domain',0

MemFormat	dc.b		'%8ld',0
TimeFormat	dc.b		'%02d:%02d:%02d',0

AllStr		dc.b		'Chip:'
ChipStr		dc.b		'       0 Fast:'
FastStr		dc.b		'       0 '
TimeStr		dc.b		'12:12:12 '
		END

