; Description:  How to use hooks in Blitz. This one is an example for the ASL
;               screenmode requester and it works. Working parts by Paul "Mildred"
;               West.
;               You need amigalibsii.res resident.
;
;               Note that some things aren't explained fully (i.e. the tags
;               for the screenmode requester). You should look at the Autodocs
;               for these.
;
; Type:         SYSTEM
;
; Author:       David McMinn (dmcminn@house-of-mojo.freeserve.co.uk)
;
; Requires:     amigalibs.res
;
; History:
;   20 Oct 1999 Removed the pointless LEA from the hook (I'm sure it used to crash when I used JSR 6(a3)???)
;               Global variable base is now stored in some reserved memory, not the h_UserData field
;
;   ?? ??? ???? Written. Thanks to Paul West who got it working (Runerrsoff/on and jump to label+6)
;

MOVE.l  a5,globalbase   ; Store a copy of Blitz's global variable base pointer
                        ; This should always be done somewhere before using the
                        ; hooks, and only needs to be done once

; These defines should be in amigalibsii.res, but they appear not to be
#DTAG_DISP=$80000000
#DTAG_DIMS=$80001000
#DTAG_MNTR=$80002000
#DTAG_NAME=$80003000

; The initial values for the width and height when opening the screenmode
; requester (silly really, as we won't see them - see later in code)
PrefDisplayWidth.w=320
PrefDisplayHeight.w=240


; Define a hook, and set it
DEFTYPE.Hook myhook             ; The hook for ASL tag as &myhook
myhook\h_Entry=?hook            ; Set address of hook stub which sorts out registers and calls Blitz function
                                ; the hook stub in this code can be used as an all purpose hook
                                ; as long as you set the address of the function in the hook h_SubEntry
                                ; field and you set up this function so that it returns a long, and takes
                                ; three long parameters (standard for a hook function). The three
                                ; parameters will be as specified in the Autodocs for the hook
                                ; callback you are trying to use except the parameters will arrive
                                ; as parameter 1 instead of a0, parameter 2 instead of a1 and parameter 3
                                ; instead of a2.
myhook\h_SubEntry = ?hook_jump  ; Set address of Blitz function that you want to use as hook callback
                                ; function
                                ; NB That you must have a label directly before the function


; Set up some tags for the ASL screenmode requester
Dim SMRtags.TagItem(18)
SMRtags(0)\ti_Tag=#ASLSM_InitialLeftEdge,160                    ; Initial left edge of requester
SMRtags(1)\ti_Tag=#ASLSM_InitialTopEdge,0                       ; Initial top edge of requester
SMRtags(2)\ti_Tag=#ASLSM_InitialWidth,300                       ; Initial width of requester
SMRtags(3)\ti_Tag=#ASLSM_InitialHeight,600                      ; Initial height of requester
SMRtags(4)\ti_Tag=#ASLSM_InitialDisplayID,$21000                ; Initial screenmode (if present, PAL:Lowres)
SMRtags(5)\ti_Tag=#ASLSM_InitialDisplayDepth,8                  ; Initial depth of screen (in bitplanes/bits per pixel)
SMRtags(6)\ti_Tag=#ASLSM_InitialDisplayWidth,PrefDisplayWidth   ; Initial width of desired screen
SMRtags(7)\ti_Tag=#ASLSM_InitialDisplayHeight,PrefDisplayHeight ; Initial height of desired screen
SMRtags(8)\ti_Tag=#ASLSM_InitialOverscanType,2                  ; Initial overscan type in cycle gadget (2 = #OSCAN_STANDARD)
SMRtags(9)\ti_Tag=#ASLSM_InitialInfoOpened,1                    ; Set information box to open
SMRtags(10)\ti_Tag=#ASLSM_InitialInfoLeftEdge,350               ; Initial left edge of info box
SMRtags(11)\ti_Tag=#ASLSM_InitialInfoTopEdge,50                 ; Initial top edge of info box
SMRtags(12)\ti_Tag=#ASLSM_DoDepth,0                             ; Don't show depth requester to user
SMRtags(13)\ti_Tag=#ASLSM_DoOverscanType,1                      ; Show overscan cycle gadget to user
SMRtags(14)\ti_Tag=#ASLSM_DoWidth,1                             ; Show width gadget to user
SMRtags(15)\ti_Tag=#ASLSM_DoHeight,1                            ; Show height gadget to user
SMRtags(16)\ti_Tag=#ASLSM_FilterFunc,&myhook                    ; Set hook for this function
SMRtags(17)\ti_Tag=#TAG_END                                     ; End of tag list

; Initialise and allocate a requester for the screenmode
*sreq.ScreenModeRequester=0
*sreq=AllocAslRequest_(#ASL_ScreenModeRequest,&SMRtags(0)\ti_Tag)

; Display the requester
ok.b=AslRequest_(*sreq,&SMRtags(0)\ti_Tag)

; If the return value was not 0 (therefore a success)
If ok<>0
    ; Display the returned values in the structure allocated earlier
    ; and wait for 3 seconds
    NPrint *sreq\sm_DisplayID
    NPrint *sreq\sm_DisplayWidth
    NPrint *sreq\sm_DisplayHeight
    Delay_ 150
EndIf
If (*sreq) Then FreeAslRequest_(*sreq)  ; If the screenmode allocation succeeded, then free theallocated memory


;*************************************************************************
; This is the Function that the hook will call.  Put the label before
; the Statement/Function you want To jump To. Note that you need to put
; Runnerrsoff and Runerrson around the statement or function.
;
; In this implementation, the ASL screenmode requester uses a hook to decide
; which screenmodes to display in the selection list. This function returns
; true for ASL to display the screenmode and false to not display the screenmode.
; The hook (and therefore this function) are called for every screenmode that
; ASL screenmode requester finds.
Runerrsoff
hook_jump:
Function.l hook{*dahook.Hook, modeID.l, *smr.ScreenModeRequester}
    DEFTYPE.DisplayInfo   DisInfoBuf ; Buffer to receive information about mode display
    DEFTYPE.DimensionInfo DimInfoBuf ; Buffer to receive information about mode dimensions
    DEFTYPE.MonitorInfo   MonInfoBuf ; Buffer to receive information about monitor (driver)
    DEFTYPE.NameInfo      NamInfoBuf ; Buffer to receive information about mode name

    ; Gets a handle to a record of display information for the screenmode in question
    IDhandle.l=FindDisplayInfo_(modeID)
    ; Fill in the various buffers about this screenmode, using the newly
    ; gotten handle
    GetDisplayInfoData_ IDhandle,&DisInfoBuf,SizeOf.DisplayInfo,#DTAG_DISP,0
    GetDisplayInfoData_ IDhandle,&DimInfoBuf,SizeOf.DimensionInfo,#DTAG_DIMS,0
    GetDisplayInfoData_ IDhandle,&MonInfoBuf,SizeOf.MonitorInfo,#DTAG_MNTR,0
    GetDisplayInfoData_ IDhandle,&NamInfoBuf,SizeOf.NameInfo,#DTAG_NAME,0

    ; Do tests. True=Mode is valid, False=mode is invalid. In these test, any mode
    ; with a maximum horizontal width of less than 400 is rejected (i.e. PAL and
    ; NTSC Lowres modes)
    If DimInfoBuf\Nominal\MaxX < 400
        Function Return False
    Else
        Function Return True
    End If
End Function
Runerrson
;*************************************************************************


;*************************************************************************
Goto HookSkip                       ; This is done so that hook code does not get
                                    ; executed out of place

; Code for calling the higher level hook functions. This piece of code is used
; for all hooks. Basically it puts whats in A0 (usually the *hook structure) into
; D0, A1 into D1 and A2 into D2. What this means is that within your high level
; function, you should have three parameters. These parameters must match up with
; what the Autodocs say will be passed in the address registers (given that they
; are put into the data registers). Remember that in Blitz, the parameters
; are in order of data register D0 first parameter, and so on.
; This hook will also return a value in D0, which is the value returned by the
; high level function. Some hook applications may need this, others may not,
; but I think it is pretty standard to return a value, even if it is a dummy
; value.
hook:   MOVEM.l d1-d7/a0-a6,-(a7)   ; Save registers to stack (NOT D0!!!)

        MOVE.l a0,d0                ; These three moves put the hook parameters
        MOVE.l a1,d1                ; (which are in A0-A2) into the data registers,
        MOVE.l a2,d2                ; so that the Blitz function can access them

        MOVEA.l globalbase(pc),a5   ; Restore the global variable base
        MOVEA.l 12(a0),a3           ; Get the address pointed to by the h_SubEntry field of the Hook structure
        JSR     6(a3)               ; Get address of Blitz function and jump to it!

        ; The value is returned in d0 from the function. As this is where
        ; we want it, nothing is done, and the hook returns.
        MOVEM.l (a7)+,d1-d7/a0-a6   ; Restore registers from stack (NOT D0, we want to keep the return value)
        RTS

globalbase: Ds.l  1                 ; Reserve 1 longword for storing Blitz's global variable base pointer

HookSkip
End

