-> patchfunc is called as:
-> result_of_call := patchfunc(regs:PTR TO registers,
-> librarybase, original_function, userdata)

-> next stop: document this bugger

OPT MODULE,PREPROCESS

-> the define OS37_CODE makes code that is slightly optimised, but only
-> runs on Workbench 2.0 or better.
#define OS37_CODE

#ifdef OS37_CODE
  OPT OSVERSION=37
#endif

-> see the patchcode for details
CONST USECOUNT = 68, ENABLED = 72, CODESIZE = 74

MODULE 'exec/memory'

EXPORT OBJECT registers
  d0, d1, d2, d3, d4, d5, d6, d7
  a0, a1, a2, a3, a4, a5, a6, a7:PTR TO LONG
ENDOBJECT

EXPORT OBJECT patch
  userdata
PRIVATE
  entry
  base, code, offset:INT
  running:INT
ENDOBJECT

EXPORT PROC disable() OF patch IS PutLong(self.code + ENABLED, FALSE)
EXPORT PROC enable()  OF patch IS PutLong(self.code + ENABLED, TRUE)

EXPORT PROC remove() OF patch
  DEF oldf
  IF self.running
    Disable()
    IF Long(self.code + USECOUNT) THEN RETURN Enable() BUT FALSE
    IF self.code <> (oldf := SetFunction(self.base, self.offset, self.entry))
      SetFunction(self.base, self.offset, oldf)
      RETURN Enable() BUT FALSE
    ENDIF
    self.running := FALSE
    Enable()
  ENDIF
ENDPROC TRUE

EXPORT PROC end() OF patch
  self.disable()
  WHILE self.remove() = FALSE DO Delay(50)

#ifdef OS37_CODE
  FreeVec(self.code)
#endif
#ifndef OS37_CODE
  IF self.code THEN FreeMem(self.code, CODESIZE)
#endif
ENDPROC

EXPORT PROC install(base, offset, patchfunc, userdata=0) OF patch
  DEF globvec, code
  MOVE.L A4, globvec

#ifdef OS37_CODE
  IF (code := AllocVec(CODESIZE, MEMF_PUBLIC))=NIL THEN Raise("MEM")
#endif
#ifndef OS37_CODE
  IF (code := AllocMem(CODESIZE, MEMF_PUBLIC))=NIL THEN Raise("MEM")
#endif

  Disable()

  self.entry    := SetFunction(base, offset, code)
  self.userdata := userdata
  self.base     := base
  self.code     := code
  self.offset   := offset
  self.running  := TRUE

  CopyMem({patchcode}, code, CODESIZE)

  -> set up all the addresses in the patchcode
  -> see the patchcode for more information
  PutLong(code +  2, code + ENABLED)
  PutLong(code + 10, self.entry)
  PutLong(code + 16, code + USECOUNT)
  PutLong(code + 30, self.entry)
  PutLong(code + 36, userdata)
  PutLong(code + 42, globvec)
  PutLong(code + 48, patchfunc)
  PutLong(code + 62, code + USECOUNT)

#ifdef OS37_CODE
  CacheClearU()
#endif
#ifndef OS37_CODE
  IF KickVersion(36) THEN CacheClearU()
#endif
  Enable()
ENDPROC

patchcode:				-> instruction offset [data offset]
	TST.L	$00000000.L		-> 0   [2] = code + 72 	; 6
	BNE.S	runs			-> 6			; 2
	JMP	$00000000.L		-> 8  [10] = self.entry	; 6
runs:	ADDQ.L	#1,$00000000.L		-> 14 [16] = code + 68	; 6
	MOVEM.L	D0-D7/A0-A7, -(A7)	-> 20			; 4
	MOVE.L	A7, -(A7)		-> 24			; 2
	MOVE.L	A6, -(A7)		-> 26			; 2
	PEA	$00000000.L		-> 28 [30] = self.entry	; 6
	PEA	$00000000.L		-> 34 [36] = userdata	; 6
	LEA	$00000000.L, A4		-> 40 [42] = globvec	; 6
	JSR	$00000000.L		-> 46 [48] = patchfunc	; 6
	LEA	20(A7), A7		-> 52			; 4
	MOVEM.L	(A7)+, D1-D7/A0-A7	-> 56			; 4
	SUBQ.L	#1,$00000000.L		-> 60 [62] = code + 68	; 6
	RTS				-> 66			; 2
	LONG	0			-> 68 [USECOUNT]	; 4
	INT	0			-> 72 [ENABLED]		; 2
					-> size = 74
