Volume FKEY
Volume Number: | | 6
|
Issue Number: | | 5
|
Column Tag: | | Assembly Lab
|
Volume FKEY--Dialog Lists in Memory
By John Holder, Eureka, CA
Whats the deal here anyway?
My first article for MacTutor showed you how to create an Fkey that used its own menus and acted just like an application (refer to MacTutor Vol.4 No.6). This article will show you how to create a very simple FKEY using a dialog to allow you to change the volume level of the Macintosh speaker. It also shows how to create a Dialog ITem List (the data normally stored in a DITL resource) in memory so your FKEY doesnt need any other resources.
How do I use this thing?
This Fkey was created with the Macintosh 68000 Development System. All applications are on a disk titled MDS1 and all .D files are on a disk titled MDS2. First assemble the file Volume-Fkey.Asm, then run the linker on the file Volume-Fkey.Link, and finally run RMaker on the file Volume-Fkey.R. You will now have an FKEY ready to put into your System file!
Volume-Fkey, How it Works
When the Fkey starts, there is a short jump over its header, then the applications current port and all registers are saved, next it brings up the standard arrow cursor with _InitCursor. When weve successfully allocated storage for the FKEYs globals and room for the dialog item list handle, we get the current value of the speakers volume by getting the low 3 bits of the global variable sdVolume (a byte) and putting the value in our own global CurrentVol(A4).
Now we jump to a routine that puts the data (defined as contants at the end of the code) into our item list handle. Now the dialog is created and the handle to our item list is passed to the _NewDialog routine which draws the dialog and all of the items in the list. Then we want to set the appropriate radio button depending on the current volume level. We do this by adding 3 (the item number of the first radio button) to the current volume level to determine which radio button to hilight and call _SetCtlValue to set it. Now, its onward to the main loop!
The Main Loop
Since were using a dialog we can let _ModalDialog handle all the events for us. All we have to do is wait for _ModalDialog to return an item number and jump to an appropriate routine to handle it. If the Enter or Return key is pushed _ModalDialog returns item number 1, which is the default button in a dialog and in this case is the Quit button.
A click in a radio button
Weve spotted a click on one of the radio buttons! So we put the item hit in register D7 for safekeeping and jump to a routine to handle changing the volume. We clear all the radio buttons (makes it a bit easier than remembering what the last hilighted button was, guess Im kind of lazy) and then hilight the one clicked on. Now we take its item number and subtract 3 from it to figure the new volume level.
The next step is to change the low order 3 bits of three different variables which the Mac stores the sound level in (each one is a byte) without disturbing any other data in the other bits of the byte. These are; sdVolume, SPVolCtl and the VIA data register A (you get at this by getting the base address of the VIA and adding the offset vBufA to it to access the byte containing the sound volume level, see Inside Mac, Vol. III-21 & III-39).
When those are set we call _WriteParam to write the new volume level to the clock chip, beep the speaker and return to the main loop!
A click in the Info button
When the mouse is clicked in the Info button, we change the dialogs font to 9 point Monaco with _TextFont and _TextSize. Now we set up the stack to call _TextBox to display the Info text (which is defined at the end of the code between AboutTextBegin & AboutTextEnd which makes it very easy to add or change any message youd like to have displayed when the Info button is clicked). _TextBox calls _EraseRect which temporarily clears the dialogs window before displaying the text. We simply wait for a button or key down event and erase the window, reset the dialogs font and call _DrawDialog to redraw all the controls in the dialog and return to the main loop.
Doing the Quit
When an Fkey quits it must dispose of any memory it created. After the dialog is disposed of with a call to _DisposDialog (which in turn releases the memory we allocated for the item list), the memory we allocated for the globals is released, all events are flushed, the applications current port and registers are restored, and a return from subroutine RTS returns command to the application!
The Dialog Item List
Ive defined my Item List at the end of the source file as constants. Basically theres a word at the beginning of the item list which tells how many items are in the list (-1). After that, each item in the list is defined, the first long word is a space reserved for the controls handle when its created, the next 4 words are the items rectangle in local coordinates, then a byte defining the control type, a byte (which must be even) which tells how many bytes are in the last field (which is the Title of the control).
When creating your own item list you must make sure the Title of each control has an even amount of bytes (each of mine is four bytes long except the last item, the Volume-Fkey string) and that you make sure to allocate enough memory for your item list by setting the BytesInItemList equate near the beginning of the code. For more information on the format of an Item List refer to Inside Mac Vol. I-427.
Final Comments...
If theres anything I havent explained fully just take a look at the source code as Ive commented it quite well. You should always take the time to comment your code AS youre coding! You wouldnt want to take a look at your code later and ask yourself what the heck you were doing would you? Well, thats it, I hope youve learned something useful from this article. See you next time...
;File: Volume-Fkey.Asm
;------------------------------------------------
;An FKEY that lets you change the speakers volume
;© 1989 by John Holder for MacTutor
;By John Holder Thu May 4, 1989
;------------------------------------------------
;The purpose of this Fkey is to show how to set-up & use
;an item list in memory for a dialog (instead of using a
;DITL resource) and to show how to check and set the Macs
;speaker volume
INCLUDETraps.D
Include ToolEqu.D; Use ToolBox equates
IncludeSysEqu.D ; Use System equates
;some useful macros
MACRO MakePointerHowManyBytes,StorageRegister =
MOVE.L #{HowManyBytes},D0
_NewPtr,CLEAR
MOVE.L A0,{StorageRegister}
|
MACRO MakeHandle HowManyBytes,StorageRegister =
MOVE.L #{HowManyBytes},D0
_NewHandle,CLEAR
MOVE.L A0,{StorageRegister}
|
MACRO SaveRegs =
MOVEM.LA0-A4/D0-D7,-(SP)
|
MACRO RestoreRegs=
MOVEM.L(SP)+,A0-A4/D0-D7
|
MACRO beep=
MOVE.W #10,-(SP)
_SysBeep
|
;------------------------------------------------
;All equates go here
;------------------------------------------------
;these are offsets into our global storage.
;(a non-relocatable block we created pointed to by A4)
;------------------------------------------------
EventBlockequ 0
what equ 0 ; Event number
message equ 2 ; Additional information
when equ 6 ; Time event was posted
where equ 10 ; Mouse coordinates
modify equ 14 ; State of keys and button
TheWindow equ 16; var used by _DialogSelect
windowpointer equ 20; Dialog ptr
ItemListHandle equ 24; Item List handle
ItemHit equ 28 ; word
CurrentVolequ 30; current Volume (byte)
padding equ 31 ; next byte (unused)
DispRectequ 32
ItemNumberequ 40
ItemHandleequ 42
ItemTypeequ 46
MemNeeded equ 100 ;how much memory needed
;for globals
mDownMask equ 2
keyDownMask equ 8
true equ $0100
false equ 0
nilequ 0
;Buttons used in dialog
QuitButtonequ 1
InfoButtonequ 2
ZeroBut equ 3
OneBut equ 4
TwoBut equ 5
ThreeButequ 6
FourBut equ 7
FiveBut equ 8
SixBut equ 9
SevenButequ 10
BytesInItemList equ 300 ;how many bytes we want
;allocated for the dialogs
;item list
SysParamequ $1F8 ;location of copy of Parameter
;RAM (PRAM) settings (20 bytes)
;used by _WriteParam
SPVolCtlequ $208 ;location of the speaker vol.
;setting in the copy of PRAM
;settings
VIAequ $1D4;contains address of the
;VIA buffer
vBufA EQU $1E00;Offset to Buffer A (of the
; VIA) (for the sound chip)
;------------------------------------------------
;The start of the Fkey
;------------------------------------------------
JumpIt
BRA.S Start ;Jump the FKEY header info
DC.W 0
DC.B FKEY
DC.W 7 ;(FKEYs res id#)
DC.W 0
Start
SaveRegs
;save current port
SUBQ #4,SP
MOVE.L SP,-(SP)
_GetPort
_InitCursor
;allocate storage for our globals
MakePointerMemNeeded,A4
cmp.l #0,A4 ;if returned ptr is not
bne.s Pointer_Is_Fine ;zero it was allocated ok
;not enough mem if it gets here, so beep & quit
bsr Theres_Been_an_Error
bra No_Mem_Must_Quit
Pointer_Is_Fine ;will get here if pointer was allocated!
;set up storage for our dialogs Item List
MakeHandle BytesInItemList,ItemListHandle(A4)
cmp.l #0,ItemListHandle(A4) ;if returned handle
bne.s Handle_Is_Fine ; is not zero it was
;allocated ok
;not enough mem if it gets here, so beep,
;release global storage we just allocated & quit
bsr Theres_Been_an_Error
bra No_Handle_Jump
Handle_Is_Fine ;will get here if handle was allocated!
;save current speaker volume level
;speaker volume level is store in low
;3 bits of global var sdVolume
move.b sdVolume,D0
and.b #$07,D0 ;clear all but low 3 bits
move.b D0,CurrentVol(A4)
bsr ShowDialog
;------------------------------------------------
;The Main loop
;------------------------------------------------
MainLoop
;let _ModalDialog handle all events
clr.l -(sp) ;filter proc
pea ItemHit(A4);returns dialog item hit
_ModalDialog
;which item was clicked?
cmp #QuitButton,ItemHit(A4)
beq QuitRoutine
cmp #InfoButton,ItemHit(A4)
beq DoAbout
;if it wasnt on of the two buttons
;check for click in the radio buttons
;if ItemHit(A4) is < the first radio
;button or > the last one we dont
;handle it
cmp #ZeroBut,ItemHit(A4)
blt MainLoop
cmp #SevenBut,ItemHit(A4)
bgt MainLoop
move ItemHit(A4),D7
bsr ChangeAndBeep
bra MainLoop ;back to the Main loop
;------------------------------------------------
;Turn on button selected and beep
;------------------------------------------------
ChangeAndBeep
saveregs
;it was a click in a radio button
;first, unhilite all the radio buttons!
bsr Clear_Radio_Buttons
;now turn on the one that was clicked on!
move.l windowpointer(A4),-(SP)
move D7,-(sp)
pea ItemType(A4)
pea ItemHandle(A4)
pea DispRect(A4)
_GetDItem
move.l ItemHandle(A4),-(sp)
move #1,-(sp)
_SetCtlValue
;calculate new volume setting by which button was
;clicked.
;button clicked (3-10) - first button (3) = new
;volume setting
move D7,D2
sub #ZeroBut,D7
and.b #$07,D7 ;clears all but low 3 bits
;set the sdVolume global var to sound
;level chosen!
move D7,D2 ;put new setting into D2
move.b sdVolume,D1;get current value
and.b #$F8,D1 ;clears low 3 bits (leaves
;the rest of the sdVolume
;bits unchanged)
or.b D1,D2 ;combine the two
move.b D2,sdVolume;set the global to new value
;save new speaker volume level
move.b sdVolume,D0
and.b #$07,D0 ;clear all but low 3 bits
move.b D0,CurrentVol(A4)
;set the SPVolCtl (part of Parameter RAM vars kept
;in low memory globals)
move D7,D2 ;put new setting into D2
move.b SPVolCtl,D1;get current value
and.b #$F8,D1 ;clears low 3 bits (leaves
;the rest of the SPVolCtl
;bits unchanged)
or.b D1,D2 ;combine the two
move.b D2,SPVolCtl;set the global to new value
;last, set the VIA Buffer A global var
;to sound level chosen (this is the sound
;chip buffer A)
move D7,D2 ;put new setting into D2
move.l VIA,A0 ;pointer to Vbase
move.b vBufA(A0),D1 ;get current value
and.b #$F8,D1 ;clears low 3 bits (leaves
;the rest of the
;bits unchanged)
or.b D2,D1 ;combine the two
move.l VIA,A0 ;pointer to Vbase
move.b D1,vBufA(A0) ;set the global to new value
;now change parameter RAM
;Saves settings in clock chip
lea SysParam,A0
move.l minusOne,D0
_WriteParam
beep ;beep to hear new setting
restoreregs
rts
;------------------------------------------------
;Do about (info button was pushed)
;------------------------------------------------
DoAbout
saveregs
;set to Monaco 9pt
MOVE.W #Monaco,-(SP)
_TextFont
MOVE.W #9,-(SP)
_TextSize
;use _TextBox to show About info
pea AboutTextBegin
MOVE.L #AboutTextEnd-AboutTextBegin,-(SP)
PEA TextRect
MOVE.W #0,-(SP) ;left justification
_TextBox
bsr Wait_For_Event ;wait for mouse or
;key click
;erase About text
pea TextRect
_EraseRect
;restore normal text attributes for the dialog
MOVE.W #0,-(SP) ;0 = systemFont
_TextFont
MOVE.W #12,-(SP);12 point
_TextSize
;redraw the dialog to show all controls
move.l windowpointer(A4),-(sp)
_DrawDialog
restoreregs
BRA MainLoop
;------------------------------------------------
;Quit, rel6.5 Volume FKEY
;--------------------------------------------
QuitRoutine
;This will also dispose of the handle
;we created for the item list so we dont
;have to
MOVE.L windowpointer(A4),-(SP)
_DisposDialog
No_Handle_Jump
move.l A4,A0
_DisposPtr
No_Mem_Must_Quit
MOVE.L #$0000FFFF,D0
_FlushEvents ;flush of all events
_SetPort ;restore port
restoreregs
rts
Theres_Been_an_Error ;will jump here if space
;couldnt be allocated!
beep
Rts
;------------------------------------------------
;Set up the dialog
;------------------------------------------------
ShowDialog
saveregs
bsr SetUpItmList
;create the dialog
CLR.L -(SP) ;For ptr
clr.l -(SP) ;dialog storage
pea WindowRect ;rect of dialog
clr.l -(sp) ;title
MOVE #true,-(SP);vis
MOVE.W #dboxproc,-(SP)
MOVE.L #-1,-(SP);behind wind ptr
MOVE #false,-(SP) ;goaway
clr.l -(sp) ;refcon
move.l ItemListHandle(A4),-(sp) ;handle to item list
_NewDialog
MOVE.L (SP),windowpointer(A4) ;leave ptr on stack
_SetPort
bsr SetUpControl ;show current vol. level
restoreregs
rts
;------------------------------------------------
;Set up our item list
;------------------------------------------------
SetUpItmList
;blockmove our item list into space we
;allocated for it at the beginning
move.l ItemListHandle(A4),A0
_HLock ;dont want it
;moving!
lea ItmList,A0 ;from ptr
move.l ItemListHandle(A4),A1
move.l (A1),A1
move.l #BytesInItemList,D0 ;lgth of data
_BlockMove
move.l ItemListHandle(A4),A0 ;unlock it
_HUnLock
rts
;------------------------------------------------
;Turn on button (depending on the current volume)
;------------------------------------------------
SetUpControl
;depending on the value of sdVolume, turn on the appropriate
;radio button!
saveregs
clr D6
move.b CurrentVol(A4),D6
add #ZeroBut,D6;make volume level
;correspond to a button
;in the dialog
move.l windowpointer(A4),-(SP)
move D6,-(sp)
pea ItemType(A4)
pea ItemHandle(A4)
pea DispRect(A4)
_GetDItem
;turn on the button
move.l ItemHandle(A4),-(sp)
move #1,-(sp)
_SetCtlValue
restoreregs
rts
;------------------------------------------------
;Wait for key or mouse press
;------------------------------------------------
Wait_For_Event
LINK A6,#-evtBlkSize
Here
LEA -evtBlkSize(A6),A0
MOVEQ #mDownMask!KeyDownMask,D0
_GetOSEvent
BEQ M_Down
BRA Here;keep looping until mouse press!
M_Down
UNLK A6
rts
;------------------------------------------------
;Clear all radio buttons
;------------------------------------------------
Clear_Radio_Buttons
;Unhilite all the radio buttons!
saveregs
move #7,D7 ;how many times to loop
;(8 buttons -1 for dbra)
move #ZeroBut,D6;button to clear
Clear_Loop
move.l windowpointer(A4),-(SP)
move D6,-(sp)
pea ItemType(A4)
pea ItemHandle(A4)
pea DispRect(A4)
_GetDItem
move.l ItemHandle(A4),-(sp)
move #0,-(sp)
_SetCtlValue
add #1,D6 ;inc. dialog item counter
dbra D7,Clear_Loop
restoreregs
rts
;------------------------------------------------
;Constants
;------------------------------------------------
WindowRectDC.W 105,145,265,365
TextRectDC.W5,5,150,215 ;for About info
;Item list of our dialog (this data
;get copied into a handle
ItmList dc10;number of items in list - 1
dc.l 0 ;place for handle
dc130,40,150,95 ;item rect
dc.b btnCtrl+4 ;Item type (Button Ctrl)
dc.b 4 ;length of following data
dc.b Quit;Title
dc.l 0
dc130,125,150,180
dc.b btnCtrl+4
dc.b 4
dc.b Info
dc.l 0
dc30,30,45,70
dc.b radCtrl+4 ;(Radio Button Ctrl)
dc.b 4
dc.b 0 ; 0 + 3 spaces (same with
;all radio buttons below)
dc.l 0
dc50,30,65,70
dc.b radCtrl+4
dc.b 4
dc.b 1
dc.l 0
dc70,30,85,70
dc.b radCtrl+4
dc.b 4
dc.b 2
dc.l 0
dc90,30,105,70
dc.b radCtrl+4
dc.b 4
dc.b 3
dc.l 0
dc30,150,45,190
dc.b radCtrl+4
dc.b 4
dc.b 4
dc.l 0
dc50,150,65,190
dc.b radCtrl+4
dc.b 4
dc.b 5
dc.l 0
dc70,150,85,190
dc.b radCtrl+4
dc.b 4
dc.b 6
dc.l 0
dc90,150,105,190
dc.b radCtrl+4
dc.b 4
dc.b 7
dc.l 0
dc5,62,15,170
dc.b statText
dc.b 12
dc.b Volume-Fkey ; Volume-Fkey + 1 space
EndItmList
AboutTextBegin
dc.b Volume-Fkey (1.0),$0D
dc.b (c) 1989 by John Holder,$0D
dc.b for MacTutor,$0D,$0D
dc.b This Fkey lets you change the speaker volume.
dc.b Click on a button to set the volume.
.ALIGN 2
AboutTextEnd
END
* File: Volume-Fkey.R
* RMaker source file
* for Volume-Fkey FKEY
MDS1:Volume-Fkey.Fkey
FKEYSPFK
TYPE FKEY = PROC
Volume-Fkey,7
MDS2:Volume-Fkey.Code
; File: Volume-Fkey.Link
; Linker source file
; for Volume-Fkey
/Output Volume-Fkey.Code
/Type TEMP
Volume-Fkey
$