MAG Disk (Jul 1991) : PicSaver / PicSaver.asm

*	PicSaver
*	By Preben Nielsen
*
*	  This is a little utility that lets you cut-out a rectangular
*	piece of any screen much in the save way as brushes are cut-out
*	in Deluxe Paint and other paint-programs. The piece can then be
*	saved on disk as an IFF-ILBM file (can then be used in most
*	paint-programs).
*
*	NOTE:	There's no need to 'RUN' or 'RUNBACK' this program from the
*		CLI. It is auto-detaching.
*
*HISTORY
*          Made with Hisoft V2.12
*
*  V1.0   26-Mar-91: Can now draw/resize/erase the rectangle correctly.
*         26-Mar-91: Now it opens a window.
*         27-Mar-91: Saving screen as IFF-ILBM now works. (Unbuffered Output)
*         28-Mar-91: Cleaned up a bit.
*         31-Mar-91: Added the cross-hair. Still unbuffered Output !
*         31-Mar-91: Drawing rectangles was not perfect. When the rectangle
*                    is only one pixel on either side, it became invisible
*                    because I drew the same line twice in 'Complement' mode.
*                    Now I only draw one line in these cases.
*         05-Apr-91: Added 'AutoRequester'.
*         06-Apr-91: Added a few features. You can now easily save a window
*                    or an entire screen. Also little change in the
*                    'SaveILBM' routine.
*  V1.1   19-May-91: Help, I just found out that my "TellInputDevice"
*                    routine trashes memory-address 0 because it didn't
*                    do a "NewList" on its Message-port. It didn't cause
*                    any problems most of the time, but it has now been
*                    cured.

                   
	OPT O+
	OPT O1+			; Tells when a branch could be optimised to short
	OPT i+			; Tells when '#' is probably missing

		incdir		"AsmInc:"
		include		"exec/exec_lib.i"
		include		"exec/io.i"
		include		"exec/memory.i"
		include		"exec/interrupts.i"
		include		"devices/input.i"
		include		"devices/inputevent.i"
		include		"libraries/dosextens.i"
		include		"libraries/dos_lib.i"
		include		"graphics/graphics_lib.i"
		include		"intuition/intuition_lib.i"
		include		"intuition/intuition.i"
		include		"intuition/intuitionbase.i"

* These are the signals sent from the input-handler to the process
SIGBASE		=20
QUAL_RELEASE_B	=SIGBASE
QUAL_PRESS_B	=SIGBASE+1
LMB_RELEASE_B	=SIGBASE+2
LMB_PRESS_B	=SIGBASE+3
MOVE_B		=SIGBASE+4
WINDOW_B	=SIGBASE+5
SCREEN_B	=SIGBASE+6
QUIT_B		=SIGBASE+7
PORT_B		=SIGBASE+8
QUAL_RELEASE_F	=1<<QUAL_RELEASE_B
QUAL_PRESS_F	=1<<QUAL_PRESS_B
LMB_RELEASE_F	=1<<LMB_RELEASE_B
LMB_PRESS_F	=1<<LMB_PRESS_B
MOVE_F		=1<<MOVE_B
WINDOW_F	=1<<WINDOW_B
SCREEN_F	=1<<SCREEN_B
QUIT_F		=1<<QUIT_B
PORT_F		=1<<PORT_B
WaitMask	=QUAL_RELEASE_F|LMB_RELEASE_F|LMB_PRESS_F|MOVE_F|WINDOW_F|SCREEN_F|QUIT_F|QUAL_PRESS_F

RECTANGLE	=0
CROSSHAIR	=1
DISABLED	=2

FileBufSIZE	=50

Prepare		MACRO
		IFC		'\1','Exec_Call'
		movea.l		4.W,A6
		ENDC
		IFC		'\1','Intuition_Call'
		movea.l		IntBase(DB),A6
		ENDC
		IFC		'\1','Gfx_Call'
		movea.l		GfxBase(DB),A6
		ENDC
		IFC		'\1','Dos_Call'
		movea.l		DosBase(DB),A6
		ENDC
		ENDM
CallLib		MACRO
		jsr		_LVO\1(A6)
		ENDM
Call		MACRO
		bsr		\1
		ENDM
CallS		MACRO
		bsr.S		\1
		ENDM
Push		MACRO
		movem.l		\1,-(SP)
		ENDM
Pop		MACRO
		movem.l		(SP)+,\1
		ENDM
rAPtr		MACRO		name
DefSiz		set		DefSiz+4
DefPtr		set		DefPtr-4
\1		=		DefPtr
		ENDM
rLong		MACRO		name
DefSiz		set		DefSiz+4
DefPtr		set		DefPtr-4
\1		=		DefPtr
		ENDM
rWord		MACRO		name
DefSiz		set		DefSiz+2
DefPtr		set		DefPtr-2
\1		=		DefPtr
		ENDM
rByte		MACRO		name
DefSiz		set		DefSiz+1
DefPtr		set		DefPtr-1
\1		=		DefPtr
		ENDM
rStorage	MACRO		name,size	; Define storage
DefSiz		set		DefSiz+\2
DefPtr		set		DefPtr-\2
\1		=		DefPtr
		ENDM
rEVEN		MACRO				; Word boundary
		IFNE		DefPtr&1
DefPtr		set		DefPtr-1
DefSiz		set		DefSiz+1
		ENDC
		ENDM
rStart		MACRO				; Define var section
DefPtr		set		0
DefSiz		set		0
		ENDM
rEnd		MACRO				; End var section
RelSize		=		DefSiz
		ENDM
rAlloc		MACRO				; Allocate vars
		link		DB,#-RelSize
		ENDM
rFree		MACRO				; Deallocate vars
		unlk		DB
		ENDM
rClear		MACRO				; Reset all vars
		movem.l		D0/DB,-(SP)
		move.w		#RelSize-1,D0
rClr.\@		clr.b		-(DB)
		dbf		D0,rClr.\@
		movem.l		(SP)+,D0/DB
		ENDM
Gadget		MACRO
		dc.l		\1
		dc.w		\2,\3,\4,\5,\6,\7,\8
		ENDM
Gadget2		MACRO
		dc.l		\1,\2,\3,\4,\5
		dc.w		\6
		dc.l		\7
		ENDM
Border		MACRO
		dc.w		\1,\2
		dc.b		\3,\4,\5,\6
		dc.l		\7,\8
		ENDM
Image		MACRO
		dc.w		\1,\2,\3,\4,\5
		dc.l		\6
		dc.b		\7,\8
		dc.l		\9
		ENDM
IntuiText	MACRO
		dc.b		\1,\2,\3,0
		dc.w		\4,\5
		dc.l		TxtAttr,\6,\7
		ENDM
Detach		MACRO		; Detach <'process name'>,stacksize,processpri
		SECTION		SingleSplit,CODE
Start		Prepare		Exec_Call
		suba.l		A1,A1
		CallLib		FindTask		; Find us
		move.l		D0,A2
		tst.l		pr_CLI(A2)
		bne.S		SegSplit
		jmp		ProcessStart		; from WorkBench
SegSplit	CallLib		Forbid			; From Dos
		lea		DName(PC),A1
		CallLib		OldOpenLibrary
		move.l		D0,D5
		beq.S		3$
		moveq		#ML_SIZE+1*ME_SIZE,D0
		move.l		#MEMF_PUBLIC|MEMF_CLEAR,D1
		CallLib		AllocMem		; Allocate Memlist
		move.l		D0,A2
		tst.l		D0
		beq.S		2$
		move.l		#ProcessName,D1
		moveq		#\3,D2			; Priority
		move.l		Start-4(PC),D3
		move.l		#\2,D4			; StackSize
		move.l		D5,A6
		CallLib		CreateProc
		Prepare		Exec_Call
		tst.l		D0
		beq.S		1$
		move.l		D0,A0
		lea		-pr_MsgPort(A0),A0	; Now we have process
		not.l		pr_CLI(A0)		; All MY programs will now think they were started from the CLI
		lsl.l		#2,D3
		subq.l		#4,D3
		move.l		D3,A1
		move.w		#1,ML_NUMENTRIES(A2)	; MemList -> ml_NumEntries	= 1
		move.l		A1,ML_ME+ME_ADDR(A2)	; MemList -> ml_me[0].me_Addr	= Segment
		move.l		(A1),ML_ME+ME_LENGTH(A2); MemList -> ml_me[0].me_Length	= Length
		lea		TC_MEMENTRY(A0),A0
		move.l		A2,A1
		CallLib		AddTail			; AddTail(&Process->pr_Task.tc_MemEntry,&MemList->ml_Node);
		lea		Start-4(PC),A0
		clr.l		(A0)			; Split the segments
		bra.S		2$
1$		move.l		A2,A1			; CreateProc failed. Can't do anything then
		moveq		#ML_SIZE+1*ME_SIZE,D0
		CallLib		FreeMem
2$		move.l		D5,A1
		CallLib		CloseLibrary
3$		CallLib		Permit
		moveq		#0,D0
		rts
DName		dc.b		'dos.library',0
ProcessName	dc.b		\1,0			; CreateProc makes a copy of this name
		SECTION		ProcessCode,CODE
ProcessStart
		ENDM

DB		EQUR		A4

InitProcess	Detach		<'PicSaver Process'>,4000,0
		rAlloc					; Allocate memory for variables
		rClear					; Clear the memory
		lea		FileInfo(PC),A1
		lea		FBuffer(DB),A2
		move.l		A2,si_Buffer(A1)
		move.w		#FileBufSIZE,si_MaxChars(A1)
		Prepare		Exec_Call
		suba.l		A1,A1
		CallLib		FindTask		; Find us
		move.l		D0,PProcess(DB)
		movea.l		D0,A2
		tst.l		pr_CLI(A2)
		bne.S		GetLibs
WBStart		lea		pr_MsgPort(A2),A0
		CallLib		WaitPort		; wait for a message
		lea		pr_MsgPort(A2),A0
		CallLib		GetMsg			; then get it
		move.l		D0,WBMsg(DB)		; save it for later reply
GetLibs		CallLib		Forbid
		lea		IHS+ihs_PortName(PC),A1
		CallLib		FindPort
		move.l		D0,D2
		CallLib		Permit
		tst.l		D2
		beq.S		1$
		move.l		D2,A1			; PicSaver was already installed ! 
		move.l		MP_SIGTASK(A1),A1
		move.l		#QUIT_F,D0
		CallLib		Signal			; Signal task to quit and then exit
		bra.S		Exit
1$		lea		DosName(PC),A1
		CallLib		OldOpenLibrary
		move.l		D0,DosBase(DB)
		beq.S		Error
		lea		GfxName(PC),A1
		CallLib		OldOpenLibrary
		move.l		D0,GfxBase(DB)
		beq.S		Error
		lea		IntName(PC),A1
		CallLib		OldOpenLibrary
		move.l		D0,IntBase(DB)
		beq.S		Error
* Allocate 9 signal-bits
		moveq		#8,D2
2$		moveq		#SIGBASE,D0
		add.w		D2,D0
		CallLib		AllocSignal
		dbf		D2,2$
		bra.S		Main

Error
Exit		Prepare		Exec_Call
FreeInt		move.l		IntBase(DB),D0
		beq.S		FreeGfx
		move.l		D0,A1
		CallLib		CloseLibrary
FreeGfx		move.l		GfxBase(DB),D0
		beq.S		FreeDos
		move.l		D0,A1
		CallLib		CloseLibrary
FreeDos		move.l		DosBase(DB),D0
		beq.S		ReplyWB
		move.l		D0,A1
		CallLib		CloseLibrary
ReplyWB		move.l		WBMsg(DB),D2
		beq.S		AllDone
		CallLib		Forbid			; We were started from WB
		movea.l		D2,A1
		CallLib		ReplyMsg		; Reply WBMessage
AllDone		rFree
		moveq		#0,D0
		rts

Main		bset		#DISABLED,Status(DB)
		lea		IHS(PC),A0
		lea		PSPrepIHS1(PC),A1
		lea		PSPrepIHS2(PC),A2
		Call		InstallHandler
		beq.S		1$
		moveq		#CANTINSTALL,D0
		Call		CONMsg
		bra.S		Error
1$		moveq		#INSTALLED,D0
		Call		CONMsg
		bclr		#DISABLED,Status(DB)
EventLoop	moveq		#0,D0
		move.l		Up(DB),D1
		beq.S		1$
		move.l		D1,A0
		moveq		#0,D1
		move.b		MP_SIGBIT(A0),D1
		bset		D1,D0
1$		ori.l		#WaitMask,D0
		Prepare		Exec_Call
		CallLib		Wait
		move.l		D0,D5
		move.l		Up(DB),D1
		beq		CheckIHS
		move.l		D1,A0
		moveq		#0,D0
		move.b		MP_SIGBIT(A0),D0
		btst		D0,D5
		beq		CheckIHS
GetNextMsg	move.l		Up(DB),D1		; Recieved an IDCMP-message
		beq		CheckIHS
		move.l		D1,A0
		Prepare		Exec_Call
		CallLib		GetMsg
		tst.l		D0
		beq		CheckIHS
		move.l		D0,A1
		move.l		im_Class(A1),D2
		move.l		im_IAddress(A1),A2
		CallLib		ReplyMsg
		cmp.l		#ACTIVEWINDOW,D2
		beq.S		ActivateFS
		cmp.l		#GADGETUP,D2
		bne.S		GetNextMsg
GJ		move.w		gg_GadgetID(A2),D0	; GadgetID is offset from GJ
		jmp		GJ(PC,D0.W)
DoSave		Call		CloseW
		move.l		WWindow(DB),D0
		beq.S		1$
		move.l		D0,A0
		Prepare		Intuition_Call
		CallLib		WindowToFront
		clr.l		WWindow(DB)
1$		Call		SaveRect
		beq.S		2$
		suba.l		A0,A0
		suba.l		A2,A2
		lea		ITxtAUTOBody(PC),A1
		lea		ITxtAUTOOk(PC),A3
		moveq		#0,D0
		moveq		#0,D1
		move.w		#248,D2
		moveq		#46,D3
		Prepare		Intuition_Call
		CallLib		AutoRequest
		bra.S		DoCS
2$		suba.l		A0,A0
		Prepare		Intuition_Call
		CallLib		DisplayBeep
		bra.S		DoCS
DoCancel	Call		CloseW
DoCS		bclr		#DISABLED,Status(DB)
		bra		GetNextMsg
ActivateFS	Prepare		Intuition_Call
		lea		Gad1(PC),A0
		move.l		PWindow(DB),A1
		suba.l		A2,A2
		CallLib		ActivateGadget
		bra		GetNextMsg

CheckIHS
TestQUIT	btst		#QUIT_B,D5
		beq.S		TestMOUSE
* User pressed the qualifiers + the 'Quit_Key'
		Call		DrawIt
		Call		CloseW			; Close window if it is open
		bset		#DISABLED,Status(DB)
		moveq		#REMOVED,D7
		lea		IHS(PC),A0
		lea		PSEndIHS1(PC),A1
		lea		PSEndIHS2(PC),A2
		Call		RemoveHandler
		beq.S		1$
		moveq		#CANTREMOVE,D0
		Call		CONMsg
		bra		EventLoop		; Help !!
1$		move.l		D7,D0
		Call		CONMsg
		bra		Exit			; Hmm
TestMOUSE	btst		#DISABLED,Status(DB)	; Are most things disabled ?
		bne		EventLoop

TestQ_PRESS	btst		#QUAL_PRESS_B,D5
		beq.S		TestQ_RELEASE
* User pressed the qualifiers
		Prepare		Intuition_Call
		move.l		ib_ActiveScreen(A6),D0
		beq.S		TestQ_RELEASE
		move.l		D0,A0
		move.w		sc_MouseY(A0),D0
		bmi.S		TestQ_RELEASE
		move.w		sc_MouseX(A0),D1
		bmi.S		TestQ_RELEASE
		movem.w		D0-D1,ey(DB)		; (x,y) >= (0,0)
		move.w		sc_Width(A0),sw(DB)	; Get starting point
		move.w		sc_Height(A0),sh(DB)	; Get screen width/height
		move.l		A0,WScreen(DB)
		lea		sc_RastPort(A0),A0	; Get screen Rastport
		move.l		A0,Rp(DB)
		bset		#CROSSHAIR,Status(DB)
		bclr		#RECTANGLE,Status(DB)
		Call		DrawIt			; Draw cross-hair

TestQ_RELEASE	btst		#QUAL_RELEASE_B,D5
		beq.S		TestLMB_RELEASE
* User released the qualifiers
		Call		DrawIt			; Erase cross-hair/rectangle
		bclr		#CROSSHAIR,Status(DB)
		bclr		#RECTANGLE,Status(DB)

TestLMB_RELEASE	btst		#LMB_RELEASE_B,D5
		beq.S		TestLMB_PRESS
* User released the LMB while holding down the qualifiers
		Call		DrawIt			; Erase cross-hair/rectangle
		bclr		#CROSSHAIR,Status(DB)
		bclr		#RECTANGLE,Status(DB)
		movem.w		ey(DB),D0-D3
		cmp.w		D0,D2
		ble.S		1$
		exg		D0,D2
1$		cmp.w		D1,D3
		ble.S		2$
		exg		D1,D3
2$		sub.w		D3,D1
		sub.w		D2,D0
		addq.w		#1,D1
		addq.w		#1,D0
		movem.w		D0-D3,ph(DB)
		lea		RTitle(PC),A0
		move.l		A0,PTitle(DB)
		bra		ContactTheUser

TestLMB_PRESS	btst		#LMB_PRESS_B,D5
		beq.S		TestMOVE
* User pressed the LMB while holding down the qualifiers
		Call		DrawIt			; Erase cross-hair
		bclr		#CROSSHAIR,Status(DB)
		move.l		WScreen(DB),A0
		move.w		sc_MouseY(A0),D0
		bmi.S		TestMOVE
		move.w		sc_MouseX(A0),D1
		bmi.S		TestMOVE
		movem.w		D0-D1,sy(DB)
		movem.w		D0-D1,ey(DB)
		bset		#RECTANGLE,Status(DB)
		Call		DrawIt			; Draw rectangle

TestMOVE	btst		#MOVE_B,D5
		beq.S		TestWINDOW
* User moved the mouse while holding down the qualifiers and the LMB
		move.l		WScreen(DB),A0
		move.w		sc_MouseY(A0),D0	; If Y < 0
		bge.S		1$
		moveq		#0,D0			; then Y = 0
1$		move.w		sc_MouseX(A0),D1	; If X < 0
		bge.S		2$	
		moveq		#0,D1			; then X = 0
2$		cmp.w		ey(DB),D0		; Did mouse actually move ?
		bne.S		3$
		cmp.w		ex(DB),D1
		beq.S		TestWINDOW
3$		Call		DrawIt			; Erase old cross-hair/rectangle
		movem.w		D0-D1,ey(DB)
		Call		DrawIt			; Draw new cross-hair/rectangle

TestWINDOW	btst		#WINDOW_B,D5
		beq.S		TestSCREEN
* User pressed the qualifiers + the 'Window_Key'
		Call		DrawIt			; Erase cross-hair/rectangle
		bclr		#CROSSHAIR,Status(DB)
		bclr		#RECTANGLE,Status(DB)
		Prepare		Intuition_Call
		move.l		ib_ActiveWindow(A6),D0
		beq.S		TestSCREEN
		move.l		D0,A0
		movem.w		wd_LeftEdge(A0),D0-D1
		move.w		D0,px(DB)
		move.w		D1,py(DB)
		move.w		wd_Width(A0),pw(DB)
		move.w		wd_Height(A0),ph(DB)
		move.l		wd_Flags(A0),D0
		andi.w		#BACKDROP,D0
		bne.S		ContactTheUser
		move.l		A0,WWindow(DB)
		lea		WTitle(PC),A0
		move.l		A0,PTitle(DB)
		bra.S		ContactTheUser

TestSCREEN	btst		#SCREEN_B,D5
		beq.S		DoneTest
* User pressed the qualifiers + the 'Screen_Key'
		Call		DrawIt			; Erase cross-hair/rectangle
		bclr		#CROSSHAIR,Status(DB)
		bclr		#RECTANGLE,Status(DB)
		move.l		WScreen(DB),A0
		movem.w		sc_LeftEdge(A0),D0-D1
		move.w		D0,px(DB)
		move.w		D1,py(DB)
		move.w		sc_Width(A0),pw(DB)
		move.w		sc_Height(A0),ph(DB)
		lea		STitle(PC),A0
		move.l		A0,PTitle(DB)
ContactTheUser	Call		OpenW
		beq		EventLoop
		bset		#DISABLED,Status(DB)
		bra		EventLoop
DoneTest	bra		EventLoop

DrawIt		btst		#RECTANGLE,Status(DB)
		bne.S		DoDraw
		btst		#CROSSHAIR,Status(DB)
		bne.S		DoDraw
		rts
DoDraw		Push		D0-D5/A0-A1/A6
		Prepare		Gfx_Call
		move.l		Rp(DB),A2
		moveq		#2,D0
		move.l		A2,A1
		CallLib		SetDrMd
		btst		#RECTANGLE,Status(DB)
		bne.S		DrawRect
DrawCross	moveq		#0,D0
		move.w		ey(DB),D1
		move.l		A2,A1
		CallLib		Move
		move.w		sw(DB),D0
		move.w		ey(DB),D1
		move.l		A2,A1
		CallLib		Draw
		move.w		ex(DB),D0
		moveq		#0,D1
		move.l		A2,A1
		CallLib		Move
		move.w		ex(DB),D0
		move.w		sh(DB),D1
		move.l		A2,A1
		CallLib		Draw
		bra.S		EndDrawIt
DrawRect	move.w		sx(DB),D2	; Always draw lines clockwice
		move.w		ex(DB),D4
		cmp.w		D2,D4
		bge.S		1$
		exg		D2,D4
1$		move.w		sy(DB),D3
		move.w		ey(DB),D5
		cmp.w		D3,D5
		bge.S		2$
		exg		D3,D5
2$		move.w		D2,D0
		move.w		D3,D1
		move.l		A2,A1
		CallLib		Move
		cmp.w		D2,D4		; If same x-coordinate then only draw one line
		bne.S		3$
		move.w		D2,D0
		move.w		D5,D1
		move.l		A2,A1
		CallLib		Draw
		bra.S		EndDrawIt
3$		move.w		D4,D0
		move.w		D3,D1
		move.l		A2,A1
		CallLib		Draw
		cmp.w		D3,D5		; If same y-coordinate then only draw one line
		beq.S		EndDrawIt
		move.w		D4,D0		; Draw the rest of the rectangle
		move.w		D5,D1
		move.l		A2,A1
		CallLib		Draw
		move.w		D2,D0
		move.w		D5,D1
		move.l		A2,A1
		CallLib		Draw
		move.w		D2,D0
		move.w		D3,D1
		addq.w		#1,D1		; Prevent 'round' corner
		move.l		A2,A1
		CallLib		Draw
EndDrawIt	moveq		#1,D0
		move.l		A2,A1
		CallLib		SetDrMd
		Pop		D0-D5/A0-A1/A6
		rts

* Call:  A0 = where to put it, D0 = number, D1 = count
DecStr		subq.w		#1,D1
		bra.S		2$
1$		move.b		#' ',(A0)+
2$		dbf		D1,1$
		moveq		#'0',D1
		move.b		D1,(A0)+
		ext.l		D0
3$		tst.l		D0
		beq.S		4$
		divu		#10,D0
		swap		D0
		add.w		D1,D0
		move.b		D0,-(A0)
		clr.w		D0
		swap		D0
		bra.S		3$
4$		rts


* Open a window on the Workbench screen and bring it to the front
OpenW		Push		D0-D7/A0-A6
		move.w		pw(DB),D0
		moveq		#4,D1
		lea		TxtSize+6(PC),A0
		Call		DecStr
		move.w		ph(DB),D0
		moveq		#4,D1
		lea		TxtSize+13(PC),A0
		Call		DecStr
		moveq		#0,D0
		move.l		WScreen(DB),A0
		move.b		sc_BitMap+bm_Depth(A0),D0
		moveq		#2,D1
		lea		TxtSize+20(PC),A0
		Call		DecStr
		Prepare		Intuition_Call
		lea		NW(PC),A0
		CallLib		OpenWindow
		move.l		D0,PWindow(DB)
		beq.S		1$
		move.l		D0,A0
		move.l		wd_UserPort(A0),Up(DB)
		move.l		PTitle(DB),A1
		lea		ScrTitle(PC),A2
		CallLib		SetWindowTitles
		move.l		PWindow(DB),A0
		move.l		wd_WScreen(A0),A0
		CallLib		ScreenToFront
1$		tst.l		PWindow(DB)
		Pop		D0-D7/A0-A6
		rts

* Close the window on the Workbench screen if it is open
CloseW		Push		D0-D7/A0-A6
		Prepare		Intuition_Call
		move.l		PWindow(DB),D0
		beq.S		1$
		move.l		D0,A0
		lea		NW(PC),A1
		movem.w		wd_LeftEdge(A0),D0-D1
		movem.w		D0-D1,nw_LeftEdge(A1)
		CallLib		CloseWindow
		clr.l		Up(DB)
		clr.l		PWindow(DB)
		move.l		WScreen(DB),A0
		CallLib		ScreenToFront
1$		Pop		D0-D7/A0-A6
		rts

FHandle		EQUR		D5
* Call: D0 = Msg-number
CONMsg		Push		D0-D7/A0-A6
		Prepare		Dos_Call
		move.l		D0,D4
		moveq		#0,D6
		CallLib		Output
		move.l		D0,FHandle
		bne.S		1$
		moveq		#1,D6
		lea		CONName(PC),A0
		move.l		A0,D1
		move.l		#MODE_OLDFILE,D2
		CallLib		Open
		move.l		D0,FHandle
		beq.S		2$
1$		moveq		#INFOMSG,D0
		Call		SendMsg
		move.l		D4,D0
		Call		SendMsg
		tst.l		D6
		beq.S		2$
		moveq		#127,D1
		CallLib		Delay
		move.l		FHandle,D1
		CallLib		Close
2$		Pop		D0-D7/A0-A6
		rts

* Call: D0 = Msg-number
SendMsg		neg.l		D0
		lsl.l		#1,D0
		lea		MsgTable(PC),A0
		add.w		0(A0,D0),A0
		move.l		A0,D2
		moveq		#-1,D3
1$		addq.l		#1,D3
		tst.b		(A0)+
		bne.S		1$
		move.l		FHandle,D1
		Prepare		Dos_Call
		CallLib		Write
		rts

INFOMSG		=0
INSTALLED	=-1
REMOVED		=-2
CANTINSTALL	=-3
CANTREMOVE	=-4

MsgText		MACRO
		dc.w		\1-MsgTable
		ENDM
MsgTable	MsgText		Msg
		MsgText		Msg1
		MsgText		Msg2
		MsgText		Msg3
		MsgText		Msg4

CONName		dc.b		'CON:100/60/330/63/PicSaver',0
Msg		dc.b		10,$9B,'0;33m PicSaver V1.1',10
		dc.b		$9B,'0;31m 1991 by ',$9B,'0;33mPreben Nielsen',$9B,'0;31m',10,' ',0
Msg1		dc.b		'has just been installed...',10,0
Msg2		dc.b		'has just been removed...',10,0
Msg3		dc.b		'Error: Cannot install handler',10,0
Msg4		dc.b		'Error: Cannot remove handler',10,0
		EVEN

rtsValue	EQUR		D7
* This is general-purpose inputhandler removal-routine
* It only needs an ihs with a port-name to remove the handler
* Call:   A0 = ihs
*	  A1 = first ihs-installation-routine or NULL
*	  A2 = second ihs-installation-routine or NULL
* Return: D0 = 0 means succes
RemoveHandler	Push		D1/rtsValue/A0-A3/A6
		moveq		#-1,rtsValue
		move.l		A2,A3
		move.l		A0,A2
		move.l		A1,D1
		beq.S		1$
		jsr		(A1)		; A0 = ihs
		beq.S		2$
		move.l		D0,A2
1$		move.l		A2,A0
		Prepare		Exec_Call
		moveq		#IND_REMHANDLER,D0
		Call		TellInputDevice
		move.l		D0,rtsValue
		bne.S		2$
		lea		ihs_Port(A2),A1
		CallLib		RemPort
		moveq		#0,D0
		bra.S		3$
2$		moveq		#-1,D0
3$		move.l		A3,D1
		beq.S		4$
		move.l		A2,A0
		jsr		(A3)		; A0 = ihs, D0 = 0 means succes
4$		move.l		rtsValue,D0
		Pop		D1/rtsValue/A0-A3/A6
		rts

* This is general-purpose inputhandler installation-routine
* It only needs an ihs with a port-name to install the handler
* Call:   A0 = ihs
*	  A1 = first ihs-installation-routine or NULL
*	  A2 = second ihs-installation-routine or NULL
* Return: D0 = 0 means succes
InstallHandler	Push		D1/rtsValue/A0-A3/A6
		moveq		#-1,rtsValue
		move.l		A2,A3
		move.l		A0,A2
		move.l		A1,D1
		beq.S		1$
		jsr		(A1)		; A0 = ihs
		beq.S		2$
		move.l		D0,A2
1$		move.l		A2,A0
		moveq		#IND_ADDHANDLER,D0
		Call		TellInputDevice
		move.l		D0,rtsValue
		bne.S		2$
		lea		ihs_Port(A2),A1
		lea		ihs_PortName(A2),A0
		move.l		A0,MP+LN_NAME(A1)		;MsgPort->mp_Node.ln_Name=Name;
		clr.b		MP+LN_PRI(A1)			;MsgPort->mp_Node.ln_Pri =0;
		move.b		#NT_MSGPORT,MP+LN_TYPE(A1)	;MsgPort->mp_Node.ln_Type=NT_MSGPORT;
		move.b		#PA_IGNORE,MP_FLAGS(A1)		;MsgPort->mp_Flags	 =PA_IGNORE;
		Prepare		Exec_Call
		CallLib		AddPort
		moveq		#0,D0
		bra.S		3$
2$		moveq		#-1,D0
3$		move.l		A3,D1
		beq.S		4$
		move.l		A2,A0
		jsr		(A3)		; A0 = ihs, D0 = 0 means succes
4$		move.l		rtsValue,D0
		Pop		D1/rtsValue/A0-A3/A6
		rts

* Open the input device. Set up the I/O block to add or remove the
* input handler, and send the request to the input device. Finally,
* close the device
* Call:   A0 = ihs
*	  D0 = Function to perform (IND_ADDHANDLER/IND_REMHANDLER)
* Return: D0 = 0 means succes
TellInputDevice	Push		D1-D2/rtsValue/A0-A3/A6
		Prepare		Exec_Call
		moveq		#-1,rtsValue
		move.l		D0,D2
		move.l		A0,A2
		lea		IReq(DB),A0
		moveq		#IOSTD_SIZE,D0
		Call		MemClear
		lea		IPort(DB),A0
		moveq		#MP_SIZE,D0
		Call		MemClear
		move.l		A0,A3
		move.b		#NT_MSGPORT,MP+LN_TYPE(A3)	; mp_Node.ln_Type=NT_MSGPORT;
		move.b		#PA_SIGNAL,MP_FLAGS(A3)		; mp_Flags	=PA_SIGNAL;
		moveq		#-1,D0
		CallLib		AllocSignal
		move.b		D0,MP_SIGBIT(A3)		; mp_SigBit	=MPSigBit;
		bmi.S		2$
		suba.l		A1,A1
		CallLib		FindTask
		move.l		D0,MP_SIGTASK(A3)		; mp_SigTask 	=FindTask(0);
		lea		MP_MSGLIST(A3),A0
		NEWLIST		A0
		lea		IReq(DB),A1
		move.l		A3,IO+MN_REPLYPORT(A1)		; ExtReq->io_Message.mn_ReplyPort   =taskReplyPort;
		move.b		#NT_MESSAGE,IO+MN+LN_TYPE(A1)	; ExtReq->io_Message.mn_Node.ln_Type=NT_MESSAGE;
		lea		InputName(PC),A0		; input.device
		moveq		#0,D0				; unit#
		moveq		#0,D1				; flags
		CallLib		OpenDevice
		tst.w		D0				; flag: error if > 0
		bne.S		1$
		lea		IReq(DB),A1
		move.w		D2,IO_COMMAND(A1)
		lea		ihs_Interrupt(A2),A0
		move.l		A0,IO_DATA(A1)
		CallLib		DoIO
		move.l		D0,rtsValue
		lea		IReq(DB),A1
		CallLib		CloseDevice
1$		move.b		MP_SIGBIT(A3),D0
		CallLib		FreeSignal
2$		move.l		rtsValue,D0
		Pop		D1-D2/rtsValue/A0-A3/A6
		rts

* Call: A0    = Memory area
*	D0:16 = Count
MemClear	Push		D0-D1/A0
		moveq		#0,D1
		bra.S		2$
1$		move.b		D1,(A0)+
2$		dbf		D0,1$
		Pop		D0-D1/A0
		rts

* Call: A0   = Source
*	A1   = Destination
*	D0:16= Count
MemCopy		Push		D0/A0-A1
		bra.S		2$
1$		move.b		(A0)+,(A1)+
2$		dbf		D0,1$
		Pop		D0/A0-A1
		rts

* Each handler should have such a pair of installation-routine
* The first one is passed to InstallHandler in A1 and it
* is called immediately when entering InstallHandler
* The second one is passed to InstallHandler in A2 and it
* is called if installation of handler and message-port succeds
* -----------------------------------------------------------------
* Call:   A0 = ihs
* Return: D0 has to point to ihs to be used when installation proceeds
*	  If D0 = 0 then installation is aborted
PSPrepIHS1	Push		A0-A1
		move.b		#PORT_B,ihs_Port+MP_SIGBIT(A0)	;MsgPort->mp_SigBit	 =MPSigBit;
		move.l		PProcess(DB),ihs_Port+MP_SIGTASK(A0);MsgPort->mp_SigTask =FindTask(0);
		move.l		#HandlerSize,ihs_Length(A0)	; This will enable removal by other programs
		lea		HandlerCode-IHS(A0),A1
		move.l		A1,ihs_Interrupt+IS_CODE(A0)	; HandlerBlock.HInterrupt.is_Code = Handler
		move.l		DB,ihs_Interrupt+IS_DATA(A0)	; HandlerBlock.HInterrupt.is_Data = DB
		move.b		#HPRI,ihs_Interrupt+LN_PRI(A0)	; HandlerBlock.HInterrupt.is_Node.ln_Pri = PRI
		move.l		A0,D0
		Pop		A0-A1
		rts
* Call:   A0 = ihs
PSPrepIHS2	rts

* Each handler should have such a pair of ending-routine
* The first one is passed to RemoveHandler in A1 and it
* is called immediately when entering RemoveHandler
* The second one is passed to RemoveHandler in A2 and it
* is called if removal of handler and message-port succeds
* -----------------------------------------------------------------
* Call:   A0 = ihs
* Return: D0 has to point to ihs to be used when removal proceeds
*	  If D0 = 0 then removal is aborted
PSEndIHS1	Push		D1-D2/A0-A1/A6
		Prepare		Exec_Call
		CallLib		Forbid
		lea		ihs_PortName(A0),A1
		CallLib		FindPort
		move.l		D0,D2
		CallLib		Permit
		move.l		D2,D0			; Does Forbid/Permit destroy scratch-registers ?
		Pop		D1-D2/A0-A1/A6
		rts
* Call:   A0 = ihs
PSEndIHS2	rts

*====================== Input-handler start =========================
ihs_Port	=0
ihs_Interrupt	=MP_SIZE
ihs_ID		=MP_SIZE+IS_SIZE
ihs_Length	=MP_SIZE+IS_SIZE+4
ihs_Flags	=MP_SIZE+IS_SIZE+8
ihs_PortName	=MP_SIZE+IS_SIZE+10

ihs_Start	MACRO
		dcb.b		MP_SIZE		; Message-Port structure
		dcb.b		IS_SIZE		; Interrupt structure
		dc.l		'P_IH'		; ID
		dc.l		0		; Length of handler 
		dc.w		0		; Flags
		dc.b		\1,0
		EVEN
		ENDM

HPRI		=51
HDisabled	=0
HNoExtRemoval	=1

* This is the handler-block
IHS		ihs_Start	<'PicSaver V1.1 Port'>
* Local variables
Chain		dc.l		0
* For each event in the event list:
*  If we were waiting for this event then signal the task.
* When all the events have been checked, return the event list so that
* others can do their things.
PEvent		EQUR	A3				; Previous Event
Event		EQUR	A5				; This Event
Signals		EQUR	D7
Next		=ie_NextEvent
Class		=ie_Class
Code		=ie_Code
Qual		=ie_Qualifier
* These are the qualifier-keys the input-handler waits for
QUALIFIERS	=IEQUALIFIER_LALT|IEQUALIFIER_LSHIFT|IEQUALIFIER_CONTROL
* This is the key the input-handler exits on
Quit_Key	=$45					; ESC
Window_Key	=$11					; w
Screen_Key	=$21					; s

* Call:  A0 = List of InputEvents, A1 = HandlerData
HandlerCode	Push		D1/Signals/A0-A1/PEvent/DB/Event/A6
		moveq		#0,Signals
		move.l		A1,DB
		move.w		IHS+ihs_Flags(PC),D0
		btst		#HDisabled,D0		; Future feature
		bne		NoMoreEvents
		lea		Chain(PC),PEvent
		move.l		A0,Next(PEvent)
ieLoop		move.l		Next(PEvent),Event
		move.l		Event,D0
		beq		NoMoreEvents
		cmpi.b		#IECLASS_NULL,Class(Event)
		beq		DontRemove
		cmpi.b		#IECLASS_TIMER,Class(Event)
		beq		DontRemove
		move.w		Qual(Event),D0
		andi.w		#QUALIFIERS,D0
		cmp.w		#QUALIFIERS,D0
		bne		NoQual
		cmpi.b		#IECLASS_RAWKEY,Class(Event)
		bne.S		1$
		cmp.w		#Quit_Key,Code(Event)
		beq.S		DoQuit
		cmp.w		#Window_Key,Code(Event)
		beq.S		DoWindow
		cmp.w		#Screen_Key,Code(Event)
		beq.S		DoScreen
		bra.S		IsQual
		bra.S		DontRemove
1$		btst		#DISABLED,Status(DB)
		bne.S		DontRemove
		cmpi.b		#IECLASS_RAWMOUSE,Class(Event)
		bne.S		DontRemove
		cmpi.w		#IECODE_LBUTTON,Code(Event)
		beq.S		DoStart
		cmpi.w		#IECODE_UP_PREFIX|IECODE_LBUTTON,Code(Event)
		beq.S		DoEnd
		move.w		Qual(Event),D0
	        andi.w		#IEQUALIFIER_RELATIVEMOUSE,D0
		beq.S		DontRemove
DoMove		move.b		Status(DB),D0
		andi.b		#1<<RECTANGLE|1<<CROSSHAIR,D0
		beq.S		DontRemove
		bset		#MOVE_B,Signals
		bra.S		DontRemove
DoEnd		btst		#RECTANGLE,Status(DB)
		beq.S		DontRemove
		bset		#LMB_RELEASE_B,Signals
		bra.S		DontRemove
DoStart		bset		#LMB_PRESS_B,Signals
		bra.S		Remove
DoWindow	bset		#WINDOW_B,Signals
		bra.S		Remove
DoScreen	bset		#SCREEN_B,Signals
		bra.S		Remove
DoQuit		bset		#QUIT_B,Signals
		bra.S		Remove
IsQual		move.b		Status(DB),D0
		andi.b		#1<<RECTANGLE|1<<CROSSHAIR,D0
		bne.S		DontRemove
		bset		#QUAL_PRESS_B,Signals
		bra.S		DontRemove
NoQual		move.b		Status(DB),D0
		andi.b		#1<<RECTANGLE|1<<CROSSHAIR,D0
		beq.S		DontRemove
		bset		#QUAL_RELEASE_B,Signals
* Just move on to next Event
DontRemove	move.l		Event,PEvent
		bra		ieLoop
* Remove event from chain and move on to next Event
Remove		move.l		Next(Event),Next(PEvent)
		bra		ieLoop
* Lets return
NoMoreEvents	move.l		Signals,D0
		beq.S		1$
		Prepare		Exec_Call
		movea.l		PProcess(DB),A1
		CallLib		Signal
1$		Pop		D1/Signals/A0-A1/PEvent/DB/Event/A6
		move.l		Chain(PC),D0		; Return (shortened ?) chain
		rts
HandlerSize	=		*-IHS
*====================== Input-handler end ===========================

*====================== Picture-saver start =========================
PicScreen	=0
PicName		=4
PicX		=8
PicY		=10
PicWidth	=12
PicHeight	=14
PicCompression	=16
Pic_SIZE	=18

PicDefine	dc.l	0,0
		dc.w	0,0,0,0,0

SaveRect	Push		D0-D1/A0-A1
		lea		PicDefine(PC),A1
		lea		FBuffer(DB),A0
		move.l		A0,PicName(A1)
		move.l		WScreen(DB),PicScreen(A1)
		move.w		px(DB),PicX(A1)
		move.w		pw(DB),PicWidth(A1)
		move.w		py(DB),PicY(A1)
		move.w		ph(DB),PicHeight(A1)
		move.w		#1,PicCompression(A1)
		lea		PicDefine(PC),A0
		Call		SaveILBM
		Pop		D0-D1/A0-A1
		rts

ILBMHDSize	=20
BMHDSize	=20
ILBMHeader	dc.b		'FORM'
		dc.l		0
		dc.b		'ILBM'
		dc.b		'BMHD'
		dc.l		BMHDSize
BMHeader	dc.w		0,0	; raster width, height in pixels
		dc.w		0,0	; x,y pixel position for this image
		dc.b		0	; # source bitplanes
		dc.b		0	; masking
		dc.b		0	; compression
		dc.b		0	; unused; for consistency, put 0 here
		dc.w		0	; transparent 'color number'
		dc.b		1,1	; pixel aspect, a ratio width : height
		dc.w		0,0	; source 'page' size in pixels
CAMGHDSize	=8
CAMGHeader	dc.b		'CAMG'
		dc.l		4
		dc.l		0
CMAPHDSize	=8
CMAPHeader	dc.b		'CMAP'
		dc.l		0
BODYHDSize	=8
BODYHeader	dc.b		'BODY'
		dc.l		0

* Call:   A0 = PicDefine
* Return: D0 = 0 means succes
SaveILBM	Push		D1-D7/A0-A6
		clr.l		Pic_Total(DB)
		move.l		PicScreen(A0),Pic_Screen(DB)
		move.l		PicName(A0),Pic_FileName(DB)
		move.w		PicCompression(A0),Pic_Compression(DB)
		move.w		PicX(A0),D0
		move.w		D0,Pic_x(DB)
		move.w		PicWidth(A0),D1
		move.w		D1,Pic_Width(DB)
		add.w		D1,D0
		move.w		D0,Pic_EndCol(DB)
		move.w		PicY(A0),D0
		move.w		D0,Pic_y(DB)
		move.w		PicHeight(A0),D1
		move.w		D1,Pic_Height(DB)
		add.w		D1,D0
		move.w		D0,Pic_EndRow(DB)
		move.w		Pic_x(DB),D0
		ext.l		D0
		divu		#8,D0
		move.w		D0,Pic_SkipBytes(DB)	; How many bytes should I skip at the beginning of each row
		swap		D0
		move.w		D0,Pic_LShift(DB)	; How often should I shift bits to the left
		move.w		Pic_Width(DB),D0	; Calculate bytes per line (word aligned)
		add.w		#15,D0
		lsr.w		#3,D0
		bclr		#0,D0
		move.w		D0,Pic_BytesPerRow(DB)
		lsl.w		#3,D0
		sub.w		Pic_Width(DB),D0
		ext.l		D0
		moveq		#-1,D1
		lsl.w		D0,D1
		move.w		D1,Pic_EndMask(DB)	; Bits to cut of at the end of each line
		move.l		Pic_Screen(DB),A0
		lea		sc_BitMap(A0),A1
		move.l		A1,Pic_BitMap(DB)
		move.b		bm_Depth(A1),Pic_Depth+1(DB)
		move.w		bm_BytesPerRow(A1),Pic_BMBytesPerRow(DB)
		lea		sc_ViewPort(A0),A1
		move.l		A1,Pic_ViewPort(DB)
		move.w		vp_Modes(A1),D0
		and.w		#V_HIRES|V_HAM|V_LACE,D0
		move.w		D0,Pic_ViewMode(DB)
		move.l		vp_ColorMap(A1),A1
		move.l		cm_ColorTable(A1),Pic_ColorTable(DB)
		lea		PBuffer1(DB),A0
		move.l		A0,Pic_ByteBuffer(DB)
		lea		PBuffer2(DB),A0
		move.l		A0,Pic_PackBuffer(DB)
		lea		BMHeader(PC),A0
		move.w		Pic_Width(DB),(A0)
		move.w		Pic_Height(DB),2(A0)
		move.w		Pic_Width(DB),16(A0)
		move.w		Pic_Height(DB),16+2(A0)
		move.b		Pic_Depth+1(DB),8(A0)
		move.b		Pic_Compression+1(DB),10(A0)
		Prepare		Dos_Call
		move.l		Pic_FileName(DB),D1
		move.l		#MODE_NEWFILE,D2
		CallLib		Open
		move.l		D0,Pic_FileHandle(DB)
		beq		NoPicFile
		lea		CAMGHeader(PC),A0
		move.w		Pic_ViewMode(DB),2+8(A0)
		lea		ILBMHeader(PC),A0	; Write ILBM File Header
		moveq		#ILBMHDSize+BMHDSize+CAMGHDSize+4+CMAPHDSize-4,D0
		Call		WriteBytes
		bne		WriteError
		move.w		Pic_Depth(DB),D1
		moveq		#0,D3
		bset		D1,D3			; Number of colours (2^depth)
		move.l		D3,D4
		mulu		#3,D4
		move.l		Pic_ByteBuffer(DB),A0	; Write CMAP
		move.l		D4,(A0)+
		move.l		Pic_ColorTable(DB),A1
		moveq		#$F0-256,D5		; Tricky way to make D5=FFF0
		bra.S		2$
1$		move.w		(A1)+,D0		; Copy colours into buffer
		move.w		D0,D1
		move.w		D0,D2
		lsr.w		#4,D0
		lsl.w		#4,D2
		and.w		D5,D0
		and.w		D5,D1
		and.w		D5,D2
		move.b		D0,(A0)+
		move.b		D1,(A0)+
		move.b		D2,(A0)+
2$		dbra		D3,1$
		move.l		Pic_ByteBuffer(DB),A0
		move.l		D4,D0
		addq.l		#4,D0
		Call		WriteBytes
		bne		WriteError
		Call		WritePad		; Unnecessary
		bne		WriteError
		move.l		Pic_Total(DB),Pic_BODYPos(DB)	; We have to get back here
		lea		BODYHeader(PC),A0
		moveq		#BODYHDSize,D0
		Call		WriteBytes
		bne		WriteError
		move.w		Pic_y(DB),D7		; Current row = D7
		subq.w		#1,D7
RowLoop		addq.w		#1,D7
		cmp.w		Pic_EndRow(DB),D7	; Last row ?
		beq.S		DonePlanes
		move.l		Pic_BitMap(DB),A2
		lea		bm_Planes(A2),A2	; Get pointer to bitplane
		move.w		Pic_Depth(DB),Pic_Looper(DB)
PlaneLoop	subq.w		#1,Pic_Looper(DB)
		bmi.S		RowLoop
		move.l		(A2)+,A0
		move.w		Pic_BMBytesPerRow(DB),D0; Offset
		mulu		D7,D0
		add.l		D0,A0
		add.w		Pic_SkipBytes(DB),A0	; Skip some bytes
		move.l		Pic_ByteBuffer(DB),A1	; Copy row to buffer
		move.w		Pic_BytesPerRow(DB),D0
		bra.S		2$
1$		move.b		(A0)+,(A1)+
2$		dbra		D0,1$
		moveq		#0,D2
		move.w		Pic_LShift(DB),D2	; Shift bits to the left
		beq.S		NoPicShift
		move.l		Pic_ByteBuffer(DB),A0
		move.w		Pic_BytesPerRow(DB),D0
		addq.w		#1,D0			; If you save byte alligned
		lsr.w		#1,D0
		bra.S		4$
3$		move.l		(A0),D1			; Copy four words to d1
		lsl.l		D2,D1			; Now move bits of 2nd word into the 1st word
		swap		D1
		move.w		D1,(A0)+		; Copy 1st word back to buffer
4$		dbra		D0,3$
		move.w		Pic_EndMask(DB),D0
		and.w		D0,-(A0)
NoPicShift	moveq		#0,D0
		move.w		Pic_BytesPerRow(DB),D0
		cmp.w		#1,Pic_Compression(DB)
		bne.S		NotPacked
		Call		Packer
NotPacked	move.l		Pic_PackBuffer(DB),A0
		Call		WriteBytes
		bne.S		WriteError
		bra.S		PlaneLoop		; Next bitplane
DonePlanes	Call		WritePad
		bne.S		WriteError
		move.l		Pic_FileHandle(DB),D1	; Write BODY size
		move.l		Pic_BODYPos(DB),D2
		moveq		#OFFSET_BEGINNING,D3
		CallLib		Seek
		lea		BODYHeader(PC),A0
		move.l		Pic_Total(DB),D0
		sub.l		Pic_BODYPos(DB),D0
		subq.l		#BODYHDSize,D0
		move.l		D0,4(A0)
		moveq		#BODYHDSize,D0
		Call		WriteBytes
		bne.S		WriteError
		move.l		Pic_FileHandle(DB),D1	; Write FORM size
		moveq		#0,D2
		moveq		#OFFSET_BEGINNING,D3
		CallLib		Seek
		lea		ILBMHeader(PC),A0
		move.l		Pic_Total(DB),D0
		sub.l		#8+BODYHDSize,D0
		move.l		D0,4(A0)
		moveq		#ILBMHDSize,D0
		Call		WriteBytes
		bne.S		WriteError
		move.l		Pic_FileHandle(DB),D1
		CallLib		Close
		moveq		#0,D0
		bra.S		DoneSaveILBM
WriteError	move.l		Pic_FileHandle(DB),D1
		CallLib		Close
		move.l		Pic_FileName(DB),D1
		CallLib		DeleteFile
NoPicFile	moveq		#-1,D0
DoneSaveILBM	tst.l		D0
		Pop		D1-D7/A0-A6
		rts

WritePad	btst		#0,Pic_Total+3(DB)
		beq.S		WRet
		move.l		Pic_ByteBuffer(DB),A0
		clr.b		(A0)
		moveq		#1,D0
* Write D0 bytes from A0
* A0 = Buffer, D0=Count
WriteBytes	move.l		Pic_FileHandle(DB),D1
		move.l		A0,D2
		move.l		D0,D3
		add.l		D0,Pic_Total(DB)
		CallLib		Write
		cmp.l		D3,D0
WRet		rts

Packer		Push		D1/A0-A3
		move.l		Pic_PackBuffer(DB),A0	; A0=buffer
		move.l		Pic_ByteBuffer(DB),A1	; A1=row
		move.l		A1,A3				
		add.w		Pic_BytesPerRow(DB),A3	; A3=end of row
PackLoop	cmp.l		A3,A1
		bge.S		PackExit
		move.l		A1,A2
1$		cmp.l		A3,A2
		bge.S		2$
		move.b		(A2)+,D0
		cmp.b		(A2),D0
		bne.S		1$
		subq.l		#1,A2
2$		move.l		A2,D1
		sub.l		A1,D1
		beq.S		PackIt			; Are there bytes between two parts of identical bytes
		subq.w		#1,D1			; Code = n-1
		move.b		D1,(A0)+
3$		move.b		(A1)+,(A0)+		; Copy bytes
		dbra		D1,3$
		bra.S		PackLoop
PackIt		move.l		A1,A2
1$		cmp.l		A3,A1
		beq.S		2$
		cmp.b		(A1)+,D0
		beq.S		1$
		subq.l		#1,A1
2$		move.l		A1,D1
		sub.l		A2,D1
		neg.w		D1			; Code = -n+1
		addq.w		#1,D1
		move.b		D1,(A0)+
		move.b		D0,(A0)+
		bra.S		PackLoop
PackExit	move.l		A0,D0
		sub.l		Pic_PackBuffer(DB),D0
1$		Pop		D1/A0-A3
		rts


*====================== Picture-saver end ===========================

*====================== Data-definition start =======================
 rStart
 rAPtr		PProcess
 rAPtr		WBMsg
 rAPtr		DosBase
 rAPtr		GfxBase
 rAPtr		IntBase
 rAPtr		Rp
 rAPtr		Up
 rAPtr		WScreen
 rAPtr		WWindow
 rAPtr		PWindow
 rAPtr		PTitle
 rWord		Status
 rWord		sh
 rWord		sw
 rWord		sx
 rWord		sy
 rWord		ex
 rWord		ey
 rWord		px
 rWord		py
 rWord		pw
 rWord		ph
 rStorage	IReq,IOSTD_SIZE
 rStorage	IPort,MP_SIZE
 rStorage	FBuffer,FileBufSIZE

 rAPtr		Pic_FileName
 rAPtr		Pic_FileHandle
 rAPtr		Pic_ByteBuffer
 rAPtr		Pic_PackBuffer
 rLong		Pic_BODYPos
 rWord		Pic_x
 rWord		Pic_y
 rWord		Pic_Width
 rWord		Pic_Height
 rWord		Pic_EndCol
 rWord		Pic_EndRow
 rWord		Pic_Depth
 rWord		Pic_BytesPerRow
 rWord		Pic_BMBytesPerRow
 rWord		Pic_SkipBytes
 rWord		Pic_LShift
 rWord		Pic_EndMask
 rWord		Pic_ViewMode
 rWord		Pic_Compression
 rAPtr		Pic_Screen
 rAPtr		Pic_BitMap
 rAPtr		Pic_ViewPort
 rAPtr		Pic_ColorTable
 rWord		Pic_Looper
 rLong		Pic_Total
 rStorage	PBuffer1,164
 rStorage	PBuffer2,164
 rEnd

DosName		dc.b		'dos.library',0
GfxName		dc.b		'graphics.library',0
IntName		dc.b		'intuition.library',0
InputName	dc.b		'input.device',0
STitle		dc.b		'Save Screen as...',0
WTitle		dc.b		'Save Window as...',0
RTitle		dc.b		'Save Rectangle as...',0
ScrTitle	dc.b		'PicSaver V1.1 1991 by Preben Nielsen',0
		EVEN

IDCMP_Flags	=		GADGETUP|ACTIVEWINDOW
Other_Flags	=		NOCAREREFRESH|ACTIVATE|RMBTRAP|WINDOWDEPTH|WINDOWDRAG
WW		=240
WH		=64
NW		dc.w		300,200-WH,WW,WH
		dc.b		0,1
		dc.l		IDCMP_Flags,Other_Flags,GadgetList,0,0,0,0
		dc.w		0,0,0,0,WBENCHSCREEN
GadgetList
Gad1		Gadget		Gad2,56,30,FWIDTH,FHEIGHT,GADGHCOMP,RELVERIFY,STRGADGET
		Gadget2		FBorder,0,ITxtFile,0,FileInfo,ActivateFS-GJ,0
Gad2		Gadget		Gad3,15,46,BWIDTH,BHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET
		Gadget2		BBorder,0,ITxtPos,0,0,DoSave-GJ,0
Gad3		Gadget		0,151,46,BWIDTH,BHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET
		Gadget2		BBorder,0,ITxtNeg,0,0,DoCancel-GJ,0

FileInfo	dcb.b		si_SIZEOF,0

FWIDTH		=173
FHEIGHT		=11
FBorder		Border		-6,-3,1,0,1,9,FVectors,0
FVectors	dc.w		2,0,FWIDTH+1,0,FWIDTH+3,2,FWIDTH+3,FHEIGHT-1,FWIDTH+1,FHEIGHT+1,2,FHEIGHT+1,0,FHEIGHT-1,0,2,2,0
BWIDTH		=74
BHEIGHT		=11
BBorder		Border		-2,-1,1,0,1,9,BVectors,0
BVectors	dc.w		2,0,BWIDTH+1,0,BWIDTH+3,2,BWIDTH+3,BHEIGHT-1,BWIDTH+1,BHEIGHT+1,2,BHEIGHT+1,0,BHEIGHT-1,0,2,2,0

ITxtSize	IntuiText	1,0,1,-43,-15,TxtSize,0
ITxtFile	IntuiText	1,0,1,-43,0,TxtFile,ITxtSize
ITxtPos		IntuiText	1,0,1,21,2,TxtPos,0
ITxtNeg		IntuiText	1,0,1,14,2,TxtNeg,0
TxtSize		dc.b		'Size:    0 x    0 x  0',0
TxtFile		dc.b		'File',0
TxtPos		dc.b		'Save',0
TxtNeg		dc.b		'Cancel',0
		EVEN

ITxtAUTOBody	IntuiText	AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,8,4,TxtAUTOBody,0
ITxtAUTOOk	IntuiText	AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,6,3,TxtAUTOOk,0
TxtAUTOBody	dc.b		"PicSaver: Can't write file",0
TxtAUTOOk	dc.b		' Ok ',0

TxtAttr		dc.l		FontName
		dc.w		TOPAZ_EIGHTY
		dc.b		FS_NORMAL,FPB_ROMFONT
FontName	dc.b		'topaz.font',0
		END