; KNOBS
; Andrew Forrest
; Started 6 January 1994
; $VER: Knobs.asm 1.0 (12.02.94)

; Assembled with DEVPAC 3.02
	nolist

; Starting from V36, the Amiga OS has several bit switches embedded in
; various control structures. Several programs allow the user to alter these
; settings, including my mouse-accelerator/swiss-army-knife program MyMouse.
; I didn't like having all of these switch options in MyMouse, especially
; since there were several other switches I could think of adding, so I have
; split these extra options off into a seperate program--KNOBS!

	include	everything.i
	
Execbase	equ	4

	;*»»  Code entrypoint
	;*»»  Allocate global memory for variables
MAIN:	move.l	Execbase,a6
	moveq	#Globals_SIZEOF,d0
	move.l	#MEMF_CLEAR,d1
	just	AllocMem
	tst.l	d0
	  beq	  Unhappy
	move.l	d0,a5
	move.l	a6,execbase(a5) ; Cache local (fast?) copy of execbase
	
	;*»»  Open the DOS library
	lea	dosname(pc),a1
	moveq	#0,d0	; Open any version
	just	OpenLibrary
	move.l	d0,dosbase(a5)
	  beq	  Unhappy

	;*»»  Open the utility library
	moveq	#msg_NoUtility,d7
	lea	utilityname(pc),a1
	moveq	#37,d0
	just	OpenLibrary
	move.l	d0,utilitybase(a5)
	  beq	  Error
	
	moveq	#msg_OldDos,d7
	move.l	dosbase(a5),a6
	cmp.w	#36,LIB_VERSION(a6)	; Need at least V36 DOS for
	  blo	  Error		; command-line parsing
	lea	Template(pc),a0
	move.l	a0,d1
	lea	Options(a5),a0
	move.l	a0,d2
	moveq	#0,d3
	moveq	#msg_NoArgs,d7
	just	ReadArgs
	move.l	d0,RdArgs(a5)
	  beq	  Error
	
	;*»»  Handle 'STAR' option.
	move.l	WildStarOption(a5),d1
	beq.s	.NoStar
	  move.l	  dosbase(a5),a0
	  move.l	  dl_Root(a0),a0	; (a6==dosbase(a5))
	  lea	  rn_Flags(a0),a0
	  moveq	  #0,d0
	  move.l	  d1,a1
	  bsr	  StringBit
.NoStar

	;*»»  Perform NoClick, if desired.
	;  (d2=charflag;d3=unit#;d4=^MsgPort;a2=argument;a3=^IORequest)
	move.l	execbase(a5),a6
	move.l	NoClickOption(a5),d0
	  beq	  .NoNoClick
	move.l	d0,a2
	;*»»  Create an IORequest structure
	just	CreateMsgPort
	move.l	d0,d4	; MsgPort in d4
	  beq	  .NoNoClick
	move.l	d4,a0
	moveq	#IOSTD_SIZE,d0
	just	CreateIORequest
	tst.l	d0
	  beq.s	  .fail
	move.l	d0,a3	; IORequest in a3
	moveq	#-1,d3
.ClickLoop	addq.l	#1,d3	; Count trackdisk units
	move.b	(a2)+,d2	; Get next character of flag string
	beq.s	.endloop	; Quit on string null-terminator
	  lea	  TrackName(pc),a0	;device name
	  move.l	  d3,d0	;unit number
	  lea	  (a3),a1	;IO request
	  moveq.l	  #0,d1	;flags
	  just	  OpenDevice
	  tst.l	  d0
	bne.s	.ClickLoop
	  move.l	  IO_UNIT(a3),a0
	  lea	  TDU_PUBFLAGS(a0),a0
	  moveq	  #TDPB_NOCLICK,d0
	  move.b	  d2,d1
	  bsr	  CharBit
	  lea	  (a3),a1
	  just	  CloseDevice
	bra.s	.ClickLoop
.endloop	lea	(a3),a0
	just	DeleteIORequest
.fail	move.l	d4,a0
	just	DeleteMsgPort
.NoNoClick

	;*»»  Border-blanking option.
	tst.l	BorderBlankOption(a5)
	beq.s	.NoBBlank
	  moveq	  #msg_BBlankGfx,d7
	  lea	  gfxname(pc),a1
	  moveq	  #39,d0
	  syscall	  OpenLibrary
	  move.l	  d0,graphicsbase(a5)
	    beq	    Error
	  move.l	  d0,a0
	  lea	  gb_BP3Bits(a0),a0
	  moveq	  #5,d0
	  move.l	  BorderBlankOption(a5),a1
	  bsr	  StringBit
	  moveq	  #msg_BBlankInt,d7
	  lea	  intname(pc),a1
	  moveq	  #33,d0
	  just	  OpenLibrary
	  move.l	  d0,intuitionbase(a5)
	    beq.s	    Error
	  intcall	  RemakeDisplay
.NoBBlank

	;*»»  Alert-timer option.
	move.l	AlertTimerOption(a5),d0
	beq.s	.NoAlertTimer
	  moveq	  #msg_ATiOldExec,d7
	  move.l	  d0,a0
	  move.l	  (a0),d0
	  move.l	  execbase(a5),a0
	  cmp.w	  #39,LIB_VERSION(a0)
	    blo.s	    Error
	  move.l	  d0,LastAlert+3*4(a0)
.NoAlertTimer

Satisfied:	moveq	#RETURN_OK,d0	; Assume a happy ending
	bra.s	Deallocate
Unhappy:	moveq	#RETURN_FAIL,d0	; Or maybe not

; Jump to this routine with d0=Returncode
Deallocate:	push.l	d0
	move.l	a5,d0
	beq.s	.nothing	; Global structure not even there
	  tst.l	  dosbase(a5)
	  beq.s	  .no_dos	; Got to check, since we're _calling_ dos
	    move.l	    RdArgs(a5),d1
	    doscall	    FreeArgs	; Does nothing if passed NULL
.no_dos	  lea	  LibraryBases(a5),a2
	  move.l	  execbase(a5),a6
	  moveq	  #NumLibraries-1,d2
.librryloop	    move.l	    (a2)+,a1
	    just	    CloseLibrary	; Does nothing if passed NULL
	  dbra	  d2,.librryloop
	  lea	  (a5),a1
	  moveq	  #Globals_SIZEOF,d0
	  just	  FreeMem
.nothing	pop.l	d0	; Get RETURN_ code back and...
	rts	; Return to DOS

;***************************************************************************
;**  Error conditions

; Branch to here on error condition with d7=errn
Error:	move.w	ErrorTable(pc,d7.w),d7
	lea	ErrorTable(pc,d7.w),a0
	bsr	Message
	bra	Unhappy

errn	set	0
Err	macro	; Message-tag
	  dc.w	  _\1-ErrorTable	;The error-message-table-entry
msg_\1	  equ	  errn	; The identifier (offset) for that entry
errn	  set	  errn+2	; Next table offset
	endm

ErrorTable:	Err	OldDos
	Err	NoUtility
	Err	NoArgs
	Err	BBlankGfx
	Err	BBlankInt
	Err	ATiOldExec

_OldDos	dc.b	"You need Release 2 of AmigaDOS to use this program.",0
_NoUtility	dc.b	"Can't open utility.library V37.",0
_NoArgs	dc.b	"Unable to read command-line arguments.",0
_BBlankGfx	dc.b	"Can't open graphics.library V39 for the BORDERBLANK option.",0
_BBlankInt	dc.b	"Can't open intuition.library for the BORDERBLANK option.",0
_ATiOldExec	dc.b	"You need Release 3 to use the ALERTTIMER option.",0

	even
;***************************************************************************
;***************************************************************************
;**  Useful routines

; Print out the name of the program, colon, null-term string and end-of-line.
; Takes a0=^string\0; a5=^Globals (with dosbase); Returns, Trashes nil;
Message:
	pushm.l	a0-a2/a6/d0-d4
	lea	(a0),a2	; Store stringpointer for safe-keeping
	doscall	Output	; \ Get the output channel
	move.l	d0,d4	; / And store in d4
	move.l	d4,d1
	lea	ProgramName(pc),a0
	move.l	a0,d2
	moveq	#NameLength,d3
	just	Write	; Write the header: "KNOBS: "
	move.l	d4,d1
	move.l	a2,d2
	moveq	#-1,d3	; Assume string of zero length
.loop	  addq.l	  #1,d3	; ...add one to length
	  tst.b	  (a2)+	; ...test for null
	bne.s	.loop	; ...and exit loop when we get it
	just	Write	; Write out this string
	move.l	d4,d1
	lea	CarriageReturn(pc),a0
	move.l	a0,d2
	moveq	#1,d3
	just	Write	; Write out the Carriage-Return
	popm.l	a0-a2/a6/d0-d4
	rts
ProgramName:	dc.b	"KNOBS: "
NameLength	equ	*-ProgramName
CarriageReturn:	dc.b	10

; Uses Bit to change a bit based on a string which contains "ON", "OFF" or 
; "CHANGE"--any other string has no effect. Uses utility library to do a 
; case-insensitive string comparison.
; Takes a0=^flags; d0=bit#; a1=^string; Returns, Trashes nothing.
; Uses a2=^search string; d2=loopcounter=[CHANGE,OFF,ON]; d3=str offset;
StringBit:	push.w	d1
	pushm.l	a0-a3/a6/d0/d2-d3
	move.l	utilitybase(a5),a6
	lea	(a1),a2	; Store user string for safekeeping
	moveq	#0,d3	; Clear top of d3
	moveq	#3,d2	; Initialise loopcounter==codenumber
.loop	  lea	  (a2),a0	;   String supplied by user
	  move.b	  Table-1(pc,d2.w),d3	;   Look up offset from table
	  lea	  Table(pc,d3.w),a1	;   Obtain string from offset
	  just	  Stricmp	;   Compare the two strings
	  tst.l	  d0
	dbeq	d2,.loop	; Loop until match or counter reaches -1
.endloop	move.b	d2,d1
	popm.l	a0-a3/a6/d0/d2-d3
	bsr.s	Bit
	pop.w	d1
	rts
Table:	dc.b	On-Table,Off-Table,Change-Table
On	dc.b	"ON",0
Off	dc.b	"OFF",0
Change	dc.b	"CHANGE",0
	even

; Changes a bit based on a single character, d1, which is `1' (SET), `0'
; (CLEAR), `*' (CHANGE) or `-' (IGNORE).
; Takes a0=^flags; d0=bit#; d1.b=character; Trashes, returns nothing.
CharBit:	pushm.l	a1/d2
	lea	CharTable(pc),a1
	moveq	#3,d2
.loop	  cmp.b	  (a1)+,d1
	dbeq	d2,.loop	; Keep looping til char=d1 or count=-1
.endloop	move.b	d2,d1
	bsr.s	Bit
	popm.l	a1/d2
	rts
CharTable:	dc.b	"*01-"

; (Possibly) changes a bit in a memory location based on whether d1
; is BITSET=1, BITCLEAR=2, BITCHANGE=3 or BITIGNORE=anything else.
; Takes a0=^flags; d0=bit#; d1.b=action; Returns & trashes nothing.
Bit:	push.w	d1
	subq.b	#1,d1
	bne.s	.noset	; IF d1==BITSET THEN
.set	 bset	 d0,(a0)	;   set
	bra.s	.end	; ELSE
.noset	 subq.b	 #1,d1
	 bne.s	 .noclear	;   IF d1==BITCLEAR THEN
.clear	  bclr	  d0,(a0)	;      clear
	 bra.s	 .end	;   ELSE
.noclear	  subq.b	  #1,d1
	  bne.s	  .end	;      IF d1==BITCHANGE THEN
.change	   bchg	   d0,(a0)	;         change
.end	pop.w	d1	; ENDIF
	rts


;***************************************************************************
;**  Static data
	dc.b	"$VER: Knobs 1.0 (06.02.94)"

dosname	dc.b	"dos.library",0
gfxname	dc.b	"graphics.library",0
intname	dc.b	"intuition.library",0
utilityname	dc.b	"utility.library",0

TrackName:	dc.b	"trackdisk.device",0

;***************************************************************************

; Structure of global data area
	STRUCTURE	Globals,0

	APTR	execbase
	LABEL	LibraryBases
	APTR	dosbase
	APTR	graphicsbase
	APTR	intuitionbase
	APTR	utilitybase
	LABEL	EndLibs
NumLibraries	equ	(EndLibs-LibraryBases)/4

	APTR	RdArgs

	LABEL	Options
	LONG	AlertTimerOption
	LONG	BorderBlankOption
	LONG	NoClickOption
	LONG	WildStarOption
	LABEL	Globals_SIZEOF

Template:	dc.b	"ALERTTIMER/K/N,"
	dc.b	"BBLANK=BORDERBLANK/K,"
	dc.b	"NOCLICK=NODISKCLICK/K,"
	dc.b	"STAR=WILDSTAR/K"
	dc.b	0

