*   StopClick  version 1.0  for SetCPU1.6 users
*      Written by Ismo Suihko,  August 4, 1990    (isuihko@ujocs.joensuu.fi)
*      Released to the Public Domain
*
*      Routines _GetMMUType and TestFlags are borrowed from SetCPU1.6,
*      which is written by Dave Haynie.
*      Also include file setcpu.i is borrowed from SetCPU sources.
*
* Stops clicking of empty drives by patching 1.3 KS V34.5 ROM (check
* it with command 'version') image loaded in 2-bit RAM with
* 'SetCPU FASTROM'. Disk drive heads will after the patch be stepped
* (silently) in a negative rather than positive direction during the
* regular system checks for a disk change.
* Version 1.6 of SetCPU isn't doing that patch anymore.
* StopClick also fixes trackdisk read/write bug (just like NoClick did).
* This patching is possible, because ROM image isn't protected at all.
* StopClick does nothing, if there isn't MMU and the given address
* doesn't seem to contain right version of the ROM image, so it
* is safe to run this even if you sometimes boot on 68000 mode.
*
* Based on the NoClick program, which is written by Norman Iscove.
* It didn't work on my system, because of SetPatch and TDpatch13
* (TDpatch13 comes with CrossDos (a great product!)).
*
* USAGE:
*   First check the address of the KERNEL by using command 'SetCPU VERBOSE'
*   If you have A2620 or A2630 with 2 MB 32-bit RAM, then output may
*   contain this line
*     KERNEL: (PADDR: $  3a0000) (VADDR: $  fc0000) (SIZE: 256K)
*                       ^^^^^^
*   Then give that address as a parameter to StopClick, like
*     StopClick 3a0000   ;(you can use numbers 0-9 and letters A-F and a-f)
*   Don't put '$' before the address, because it is used by shell.
*   Put StopClick to a startup-sequence after SetCPU FASTROM when you have
*   checked what the address of the KERNEL is. If you add there more memory,
*   you have to check the address again and do modifications if necessary.
*
*   e.g.
*      SetCPU FASTROM
*      stopclick 3a0000    ; KERNEL is at address $3a0000
*
* There aren't any checks if there is a FASTROM image REALLY IN USE.
* We only check, if there are signs of a 1.3 KS V34.2 ROM image..
* If FASTROM has been actice once, but then disactivated, the image could
* still be in the 32-bit RAM, so it is found from there, and patches are
* made but without any effect (and no harm either).
*
* Return codes:
*   0  complete success, fixes made now or they had been made earlier
*   5  no MMU (Memory Management Unit)
*  10  no KS ROM image found, or wrong version of ROM
*  15  KS ROM image found, but modifications couldn't be done??
*  99  invalid parameter
* We get return code 5 always if there isn't a MMU.
*
* BUGS:
*   No known bugs.
*   Maybe the address of KERNEL could be found by this program somehow...
*
* Sometimes clicking will end only after a couple of seconds, just like
* with NoClick. Don't ask me why it happens so, as I don't know.
* And there may be some odd floppy disk drives which don't like to step
* in a negative direction. I haven't yet heard anyone who owns such a drive.
*
* History:
*  V0.9  Jun 23, 1990  works, do we need more?
*  V1.0  Aug 04, 1990  some more checking, now we check if there is a MMU,
*                     earlier we checked only if there was a '020 or better.
*                     program is now 480 bytes long
*  V?.?  ??? ??, ????  will find the address of the ROM image by itself
*
* To compile first get the setcpu.i include file from the SetCPU1.6 sources,
* comment out (add ';'s) the last two lines containing the text
* ;     machine mc68020
* ;             mc68881
* and then compile with commands
*   Assem StopClick.a -o StopClick.o -i INC:
*   Blink StopClick.o to StopClick
* Makefile is also provided for easier compilation and linking.


* exec.library routines
LVODisable     EQU -120
LVOEnable      EQU -126
LVOCloseLibrary EQU -414
LVOOpenLibrary EQU -552
* dos.library routines
LVOWrite       EQU -48
LVOOutput      EQU -60

* some macros, etc.
 include "setcpu.i"


       movem.l d0/a0,-(sp)     save information of command line parameters

       bsr     _GetMMUType
       tst.l   d0              is there a MMU? 68851 or 68030?
       bne.s   scan
* no MMU
       addq.l  #8,sp           forget parameters before quitting
       moveq   #5,d0           no MMU
       bra     exit

scan
       movem.l (sp)+,d0/a0
* Now check, if there is a valid hexadesimal address given
       subq.l  #1,d0           take out end of line (null)
       beq.s   scan_err        is there any parameters at all

       subq.l  #1,d0           counter (length of the parameter)
       moveq   #0,d1           result
       moveq   #0,d2           character from the command line
scan_loop
       move.b  (a0)+,d2        get a character from the command line

       cmpi.b  #' ',d2         check for the end of hex number
       beq.s   scan_ready      is it a space
       cmpi.b  #';',d2
       beq.s   scan_ready      is it a start of a comment
       cmpi.b  #9,d2
       beq.s   scan_ready      is it a tab

       moveq   #'0',d3
       cmpi.b  #'9',d2
       ble.s   scan_number
       bclr.l  #5,d2           [a-f] -> [A-F]
       cmpi.b  #'A',d2
       blt.s   scan_err        less than 'A'
       cmpi.b  #'F',d2
       bgt.s   scan_err        greater than 'F'
       moveq   #'A'-10,d3
scan_number
       sub.b   d3,d2
       blt.s   scan_err        less than '0'
       asl.l   #4,d1           multiply by $10
       add.l   d2,d1
       dbra    d0,scan_loop
scan_ready
       tst.w   d1              should be of form xxxx0000
       beq.s   check

scan_err
       moveq   #99,d0
       bra.s   exit

check
       movea.l d1,a2           address of possible ROM image

* Now check if the given address may contain ROM image with exec of V34.2
       cmpi.w  #' 3',$1c(a2)
       bne.s   bad_rom
       cmpi.l  #'4.2 ',$1e(a2)
       bne.s   bad_rom

* add offset of trackdisk.device, should be of version 34.1
       adda.l  #$29564,a2
       cmpi.l  #'34.1',$36(a2)
       beq.s   patch
bad_rom
       moveq   #10,d0          no ROM image found
       bra.s   exit

patch
       moveq   #2,d7           2 patches
* disable interrupts
       CALLSYS Disable

* first we check, if we need to patch, then we check was the patch successful

* Stop the clicks
* fix 'bchg #$01,$41(a3)' to 'bset #$01,$41(a3)'
       lea     $104(a2),a0
       cmpi.w  #$086b,(a0)
       bne.s   1$
       move.w  #$08eb,(a0)
1$
       cmpi.w  #$08eb,(a0)
       bne.s   2$
       subq    #1,d7

2$
* Repair trackdisk read/write bug originally at $feaf9c (1.3)
* fix 'cmp.l $8000,d0' to 'cmpi.l #$8000,d0'
       lea     $1a38(a2),a0
       cmpi.w  #$b0b9,(a0)
       bne.s   3$
       move.w  #$0c80,(a0)
3$
       cmpi.w  #$0c80,(a0)
       bne.s   4$
       subq    #1,d7
4$
* enable interrupts
       CALLSYS Enable

       move.l  d7,d0           if d7 = 0, patching was succesful
       beq.s   exit
       moveq   #15,d0          patches failed??

exit
       tst.b   d0
       beq.s   real_exit

* print the usage text
       move.l  d0,-(sp)        push the return code
       lea     dosname(pc),a1
       moveq   #0,d0
       CALLSYS OpenLibrary
       tst.l   d0
       beq.s   2$
       move.l  d0,a5
       exg     a5,a6
       jsr     LVOOutput(a6)
       move.l  d0,d1
       beq.s   1$
       lea     usage(pc),a0
       move.l  a0,d2
       moveq   #usagelen,d3
       jsr     LVOWrite(a6)
1$
       exg     a5,a6
       move.l  a5,a1
       CALLSYS CloseLibrary
2$
       move.l  (sp)+,d0        pop the return code

real_exit
       rts



* The following routines are borroved from SetCPU1.6 sources.
* I optimized the code a slightly to make my program smaller than 488
* bytes, because otherwise it would have taken 1+2 disk blocks using OFS.
* I removed also the 68040 check, because at this context we don't need
* to know whether there is exactly '030 or '040 as both have MMU.


;======================================================================
;
;      This routine checks CPU flags early in ExecBase for extended
;      CPUs that test as a 68020 under 1.3.  If these flags are set,
;      the actual CPU/MMU type test can be skipped.
;
;======================================================================

TestFlags:
       moveq.l #0,d0
*       btst.b  #AFB_68040,ATNFLGS(a6)  ; Does the OS think an '040 is here?
*       beq.s   NoEarly40
*       move.l  #68040,d0
*       rts
*NoEarly40:
       btst.b  #AFB_68030,ATNFLGS(a6)  ; Does the OS think an '030 is here?
       beq.s   NoEarly30
       move.l  #68030,d0               ; Sure does...
NoEarly30:
       rts


;======================================================================
;
;      This function returns 0L if the system contains no MMU,
;      68851L if the system does contain an 68851, or the CPU number
;      for CPUs with integral CPUs.
;
;      This routine seems to lock up on at least some CSA 68020
;      boards, though it runs just fine on those from Ronin and
;      Commodore, as well as all 68030 boards it's been tested on.
;
;      ULONG GetMMUType()
;
;======================================================================

_GetMMUType:
       move.l  4,a6                    ; Get ExecBase
       bsr     TestFlags               ; Check extended CPU types
       tst.l   d0
       beq.s   MMURealTest
       rts

       ; For any other machine, a real test must be done.  The test will
       ; try an MMU instruction.  The instruction will fail unless we're
       ; on a "bogus MMU" system, where the FPU responds as an MMU.
MMURealTest:
       movem.l a3/a4/a5,-(sp)          ; Save this stuff
       suba.l  a1,a1
       CALLSYS FindTask                ; Call FindTask(0L)
       move.l  d0,a3

       move.l  TC_TRAPCODE(a3),a4      ; Change the exception vector
       lea     MMUTraps(pc),a0
       move.l  a0,TC_TRAPCODE(a3)

       moveq.l #-1,d0                  ; Try to detect undecode FPU
       subq.l  #4,sp                   ; Get a local variable
       PMOVE_  tc,(sp)                 ; Let's try an MMU instruction
       addq.l  #4,sp                   ; Return that local
       move.l  a4,TC_TRAPCODE(a3)      ; Reset exception stuff
       movem.l (sp)+,a3/a4/a5          ; and return the registers
       rts

       ; This is the exception code.  No matter what machine we're on,
       ; we get an exception.  If the MMU's in place, we should get a
       ; privilige violation; if not, an F-Line emulation exception.
MMUTraps:
       move.l  (sp)+,d0                ; Get Amiga supplied exception #
       cmpi    #11,d0                  ; Is it an F-Line?
       beq.s   MMUNope                 ; If so, go to the fail routine
       move.l  #68851,d0               ; We have MMU
       addq.l  #4,2(sp)                ; Skip the MMU instruction
       rte
MMUNope:
       moveq.l #0,d0                   ; It dinna woik,
       addq.l  #4,2(sp)                ; Skip the MMU instruction
       rte



* some strings

dosname dc.b   'dos.library',0
usage  dc.b    'StopClick v1.0 by IS.  '
       dc.b    'Usage: StopClick address-of-1.3-KS-FASTROM-image',10
usagelen EQU   *-usage

       END

