home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Otherware
/
Otherware_1_SB_Development.iso
/
mac
/
sysext
/
da
/
insect.sit
/
Insect.asm
< prev
next >
Wrap
Assembly Source File
|
1989-05-15
|
16KB
|
623 lines
; This is the crawling insect DA, disassembled and tweaked.
; Should work on all kinds of screens now.
; Fixed crashes due to calling _Random in VBL with invalid A5.
; Added option-open and cmd-opt-open features to kill bugs.
; E.M.Vishniac
; P.O. Box 1357
; East Arlington, MA 02174
; March 7, 1987
; Thanks to MacNosy for invaluable assistance.
; When opened, the crawling insect DA starts a bug working its way
; up the screen. Opening the DA repeatedly creates more bugs.
; Option-opening the DA kills one bug (the youngest).
; Command-option-opening the DA kills all the bugs.
; Though the DA is useless, except for laughs, it is an interesting
; example of a VBL task.
; A lesson:
; The original Insect DA crashed at Random times. The
; reason was that ResetBug calls _Random, which
; uses the quickdraw globals. In a VBL task, A5 may not
; be current. To guard against problems, load A5 with
; CurrentA5 in the VBL.
; Actually, that's not perfect, since CurrentA5 might
; not be valid at some times! So, we won't use A5 at
; all - we'll use our own Random routine. 5/15/89.
Include Traps.D
Include SysEquX.D
Include QuickEquX.D
Include ToolEqu.D
Include SysErr.D
macro POP p1 =
move (A7)+,{P1} |
macro POP.L p1 =
move.L (A7)+,{P1} |
macro PUSH p1 =
move {P1},-(a7) |
macro PUSH.L p1 =
move.L {P1},-(a7) |
jHideCursor equ $800 ; address of HideCursor routine
BugInterval equ 7 ; ticks between VBL task runs
BugHeight equ 16 ; bug is 16 rows high
RESOURCE 'DRVR' 31 'Insect' 00
Insect
DC.W $4F,0,0,0 ; flags, delay, emask, menu
DC.W Open ; open routine
DC.W Close ; no prime
DC.W Close ; no control
DC.W Close ; no status
DC.W Close ; no close
; No name?
; In case someone gets the DA without source code...
DC.B ' For source code, send a diskette '
DC.B 'and a self-addressed, stamped envelope to: '
DC.B 'Ephraim Vishniac / P.O. Box 1357 '
DC.B '/ East Arlington, MA 02174. '
DC.B 'No postage = no reply. '
.align 2
; Starting here, the code and data are copied
; into the system heap when the DA is opened.
BugBase
DC.B 'BUG ' ; signature
MyVBLTask
DC.L 0 ; qLink
DC.W 1 ; qType
DC.L 0 ; vblAddr
DC.W BugInterval ; vblCount
DC.W 0 ; vblPhase
oldHide DC.L 0 ; jHideCursor
oldShow DC.L 0 ; jShowCursor
oldShield DC.L 0 ; jShieldCursor
; Here's the local data used by the VBL task.
LocalData equ *
PaintLoc equ *-LocalData
DC.L 0 ; [long] address to paint bug
Generation equ *-LocalData
DC.W 0 ; [word] bug generation
Saved equ *-LocalData
DC.B 0 ; [byte] We've saved the screen data
DC.B 0 ; [byte] for aligment
MyScrnBase equ *-LocalData
DC.L 0 ; [long] start of screen bitmap
MyScrnRow equ *-LocalData
DC.W 0 ; [word] size of screen low in bytes
MyScrnRect equ *-LocalData
DC.W 0,0,0,0 ; [rect] screen rectangle
ShieldRect equ *-LocalData
DC.W 0,0,0,0 ; [rect] rectangle for ShieldCursor
ShieldReturn equ *-LocalData
DC.L 0 ; [long] return addr from ShieldCursor
ShieldSR equ *-LocalData
DC.W 0 ; [word] SR in ShieldCursor
ScreenData equ *-LocalData
DCB.W 16,0 ; [16 words] saved screen data
RandomSeed equ *-LocalData
DC.L 0 ; [long] seed for random numbers
; This is the main bug task, called every BugInterval clock ticks
; by the VBL manager.
MyVBLproc
Move SR,-(A7) ; save status
OrI #$100,SR ; mask off some interrupts. needed?
Lea MyVBLTask,A0
Move #1,vblCount(A0) ; we might have to defer...
Tst.B CrsrBusy ; Cursor busy?
Bne @0 ; exit if so
Move #BugInterval,vblCount(A0) ; renew our task
Bsr Generate ; do our stuff
@0 Move (A7)+,SR ; restore status
Rts ; and exit
; This routine produces a new generation of the bug.
Generate
MoveM.L D7/A2-A4,-(A7) ; save registers
Lea LocalData,A4
Move CrsrState,D7 ; D7 = Cursor state
Tst.L (A4) ; anything doing?
Beq @3 ; exit if not
Bsr DoOldHide ; hide the cursor
Bsr PaintOverBug ; restore the screen
Move Generation(A4),D0 ; D0.W = bug generation
AddQ #1,D0 ; increment generation
CmpI #6,D0 ; there are six generations
Bne @1 ; skip if not wrapped
Clr D0 ; else wrap to zero
@1 Move D0,Generation(A4) ; update generation
Move.L (A4),A0 ; A0 = place to paint
Sub.W MyScrnRow(A4),A0 ; previous row
Move.L A0,(A4) ; update
CmpA.L MyScrnBase(A4),A0 ; off top?
Bge @2 ; skip if not
Bsr ResetBug ; else pick a new location
@2 Bsr DrawBugPlus ; Draw bug if cursor wasn't obscure
Bsr DoOldShow ; show the cursor
@3 MoveM.L (A7)+,D7/A2-A4 ; restore registers
MoveQ #0,D0 ; and end update
Rts
; This substitute for HideCursor makes sure that the bug is hidden
; whenever the cursor is hidden.
MyHide
Move SR,-(A7) ; simulate exception
OrI #$100,SR ; disable some interrupts
Bsr DoOldHide ; hide the cursor
MoveM.L D0-D1/A0-A4,-(A7) ; save registers
Lea LocalData,A4 ; We hide when the cursor hides
Tst.L (A4) ; are we active?
Beq @1 ; exit if not
Bsr PaintOverBug ; restore the screen
@1 MoveM.L (A7)+,D0-D1/A0-A4 ; restore registers
Move (A7)+,SR ; restore status register
Rts
DoOldHide
Push.L oldHide ; run the original HideCursor
Rts
; This substitute for ShowCursor makes sure that the bug is visible
; whenever the cursor is visible.
MyShow
Move SR,-(A7) ; simulate exception
OrI #$100,SR ; disable interrupts
MoveM.L D0-D1/A0-A4,-(A7) ; save registers
Lea LocalData,A4
Tst.L (A4) ; are we active?
Beq @2 ; exit if not
Move CrsrState,D0 ; D0 = Cursor State
Beq @1 ; branch if State = Visible
CmpI #-1,D0 ; State = obscure?
Bne @2 ; exit if not
@1 Bsr DrawBug ; cursor state is Visible or Obscure
@2 MoveM.L (A7)+,D0-D1/A0-A4 ; restore registers
Bsr DoOldShow ; restore cursor
Move (A7)+,SR ; restore SR
Rts
DoOldShow
Push.L oldShow ; run the original ShowCursor
Rts
; This substitute for ShieldCursor does for the bug what
; ShieldCursor does for the cursor.
vak_1 EQU 4
param2 EQU 8
param1 EQU 12
MyShield
Link A6,#0
Push.L A4
Lea LocalData,A4
Move SR,ShieldSR(A4) ; save SR
OrI #$100,SR
Move.L vak_1(A6),ShieldReturn(A4) ; save return address
Move.L param2(A6),ShieldRect(A4) ; save rectangle
Move.L param1(A6),ShieldRect+4(A4)
Pop.L A4 ; end use of local vars
UnLk A6
AddQ.L #4,A7 ; trash return address so ShieldCursor can work
Bsr DoOldShield ; shield the cursor
MoveM.L D0-D1/A1-A4,-(A7) ; save regs
Lea LocalData,A4
Tst.L (A4) ; are we active?
Beq @2 ; exit if not
Bsr GetBugRect ; get bug rectangle on stack
Move (A7),D0 ; D0 = bug bottom
Cmp.W ShieldRect+4(A4),D0 ; compare to ShieldRect top
Ble @1 ; exit if bug's clear above
Move 2(A7),D0 ; D0 = bug right
Cmp.W ShieldRect+6(A4),D0 ; compare to ShieldRect left
Ble @1 ; exit if bug's clear left
Move 4(A7),D0 ; D0 = bug top
Cmp.W ShieldRect(A4),D0 ; compare to ShieldRect bottom
Bge @1 ; exit if bug's clear below
Move 6(A7),D0 ; D0 = bug left
Cmp.W ShieldRect+2(A4),D0 ; compare to ShieldRect right
Bge @1 ; exit if bug's clear right
; Bug's in the affected rectangle
Bsr DoOldHide ; hide the cursor
Bsr PaintOverBug ; restore the screen
Bsr DoOldShow ; restore the cursor
@1 AddQ.L #8,A7 ; remove rect from stack
@2 MoveA.L ShieldReturn(A4),A0 ; A0 = return address
Move ShieldSR(A4),SR ; restore SR
MoveM.L (A7)+,D0-D1/A1-A4 ; restore registers
Jmp (A0)
DoOldShield
Push.L oldShield ; call old ShieldCursor
Rts
; Construct the Rect for the bug's current location and return it
; on the stack, of all places.
GetBugRect
Pop.L A0 ; A0 = return address
Move.L (A4),D1 ; D1 = place to paint screen
Sub.L MyScrnBase(A4),D1 ; D1 = screen offset
DivU MyScrnRow(A4),D1 ; D1.W = row; high word = column
Move D1,D0 ; D0.W = row
Swap D1 ; low word = column byte
Ext.L D1 ; D1 = left edge, byte offset
LSL #3,D1 ; D1 = left edge, bit offset
Push D1 ; push for caller
AddI #16,D1 ; D1 = right edge, bit offset
Ext.L D0 ; D0 = top edge, row number
Push D0 ; push for caller
AddI #16,D0 ; D0.L = bottom edge, row #
Push D1 ; push right edge
Push D0 ; push bottom edge
Jmp (A0) ; return with Rect on stack
; Pick a starting location for the bug and reset the bug generation.
ResetBug
Clr -(A7) ; for random number
Bsr MyRandom ; random number generator
Clr.L D0 ; clear high word
Pop D0 ; D0.W = random number
DivU MyScrnRow(A4),D0 ; pick random column
Swap D0 ; D0.W = some random column
BClr #0,D0 ; force word alignment
Ext.L D0
Add.L MyScrnBase(A4),D0 ; add start of screen
Move.W MyScrnRect+botRight(A4),D1 ; depth of screen
Sub.W #16,D1 ; less height of bug
MulU MyScrnRow(A4),D1 ; offset that many bytes
Add.L D1,D0 ; screen + lines
Move.L D0,(A4) ; save as place to paint
Clr Generation(A4) ; start at first generation
Rts
; Save the screen image which would be covered by the bug.
SaveUnderBug
Lea ScreenData(A4),A0 ; A0 = pointer to screen data area
MoveA.L (A4),A1 ; A1 = place to paint bug
Move #BugHeight-1,D1
@1 Move (A1),(A0)+ ; save a long
AddA.W MyScrnRow(A4),A1 ; next row
DBra D1,@1 ; all the rows
Rts
; Draw the bug if the cursor is not obscured.
DrawBugPlus
Tst.B D7 ; cursor obscure? (obscure = -1)
Bpl DrawBug ; branch if visible or nested
Rts ; exit if obscure
; Draw the bug if it's not drawn already.
; (Previously saved screen data means the bug is out there.)
DrawBug
BSet.B #0,Saved(A4) ; saved screen data?
Bne @2 ; exit if already saved
Bsr SaveUnderBug ; save screen data
Lea BugImages,A0
Lea BugMasks,A2
Move Generation(A4),D0 ; D0.W = bug generation
Ext.L D0 ; D0.L = ditto
ASL #5,D0 ; generation * 32
AddA.L D0,A0 ; offset into bug images
AddA.L D0,A2 ; offset into bug masks
MoveA.L (A4),A1 ; A1 = place to paint
Move #BugHeight-1,D1
@1 Move (A2)+,D0 ; fetch mask
Not D0
And D0,(A1)
Move (A0)+,D0 ; fetch image
Eor D0,(A1)
AddA.W MyScrnRow(A4),A1 ; next row
DBra D1,@1
@2 Rts
; Restore the screen data that the bug stepped on.
PaintOverBug
BClr.B #0,Saved(A4) ; Screen saved before? (It's not now.)
Beq @2 ; exit if no screen data
Lea ScreenData(A4),A0 ; A0 = pointer to saved screen data
MoveA.L (A4),A1 ; A1 = place to start painting on screen
Move #BugHeight-1,D1
@1 Move (A0)+,(A1) ; copy a word
AddA.W MyScrnRow(A4),A1 ; skip to next row
DBra D1,@1 ; and copy them all
@2 Rts
; Generate a random number. This is stolen from the Mac Plus ROM code.
; Instead of using the QD global seed, it uses one from our local vars.
MyRandom
Move.W #$41A7,D0
Move.W D0,D2
Mulu.W RandomSeed+2(A4),D0
Move.L D0,D1
Clr.W D1
Swap D1
Mulu.W RandomSeed(A4),D2
Add.L D1,D2
Move.L D2,D1
Add.L D1,D1
Clr.W D1
Swap D1
Andi.L #$FFFF,D0
Subi.L #$7FFFFFFF,D0
Andi.L #$7FFF,D2
Swap D2
Add.L D1,D2
Add.L D2,D0
Bpl.S @0
Addi.L #$7FFFFFFF,D0
@0 Move.L D0,RandomSeed(A4)
Cmpi.W #$8000,D0
Bne.S @1
Clr.W D0
@1 Move.W D0,4(A7)
Rts
; Here's the actual bug data.
; First are the bug images, then the bug masks.
; There are six sets of data for six bug generations.
BugImages
DC.L $00102010,$11A00E40,$02400181,$8662781C
DC.L $08100811,$781E8810,$0810381E,$442183C0
DC.L $20041008,$09900660,$02400180,$0661781E
DC.L $88108810,$781E0811,$8810781C,$042203C1
DC.L $08000804,$05880270,$02408180,$4660381E
DC.L $08110811,$781E8810,$0811781E,$842003C0
DC.L $20001000,$09800678,$02440181,$8662781C
DC.L $08108810,$781E0811,$0810381E,$442183C0
DC.L $00002004,$11880E70,$02400180,$0661781E
DC.L $88100811,$781E8810,$8810781C,$042203C1
DC.L $00040008,$01901E60,$22408180,$4660381E
DC.L $08118810,$781E0811,$0811781E,$842003C0
BugMasks
DC.L $00102010,$11A00FC0,$03C00181,$87E27FFC
DC.L $0FF00FF1,$7FFE8FF0,$0FF03FFE,$47E183C0
DC.L $20041008,$099007E0,$03C00180,$07E17FFE
DC.L $8FF08FF0,$7FFE0FF1,$8FF07FFC,$07E203C1
DC.L $08000804,$058803F0,$03C08180,$47E03FFE
DC.L $0FF10FF1,$7FFE8FF0,$0FF17FFE,$87E003C0
DC.L $20001000,$098007F8,$03C40181,$87E27FFC
DC.L $0FF08FF0,$7FFE0FF1,$0FF03FFE,$47E183C0
DC.L $00002004,$11880FF0,$03C00180,$07E17FFE
DC.L $8FF00FF1,$7FFE8FF0,$8FF07FFC,$07E203C1
DC.L $00040008,$01901FE0,$23C08180,$47E03FFE
DC.L $0FF18FF0,$7FFE0FF1,$0FF17FFE,$87E003C0
StaticSize equ *-MyVBLProc ; size of non-variable stuff
FullSize equ *-BugBase ; size of everything
; Open: Install the bug in the system heap, hook it into the cursor
; routines, and put it in the VBL queue.
Open MoveM.L A3-A5/D7,-(A7) ; save registers
Move.L CurrentA5,A5 ; make sure A5 is OK
; Why are we here?
Link A6,#-evtBlkSize ; space for event record
Clr.W -(A7) ; for event-avail result
Clr.W -(A7) ; we want no events!
Pea -evtBlkSize(A6) ; our event block
_EventAvail ; see what's going down
Tst.W (A7)+ ; toss result
Move.W evtMeta(A7),D7 ; save modifiers
UnLk A6 ; discard event record
BTst.L #optionKey,D7 ; option key down?
Bne KillBug ; if so, go kill a bug
; No option key, so it's time to spawn a bug
; Get some sysheap space
Move.L #FullSize,D0 ; stuff to copy into sysheap
Move.L D0,D4
_NewPtr ,SYS+CLEAR ; (D0/byteCount:Size):A0\Ptr
Tst D0 ; got the storage?
Bmi com_2 ; beep if error
; A0 = our pointer in SysHeap
Move.L A0,A3 ; save our pointer
; save the addresses of the cursor routines
Lea jHideCursor,A0 ; A0 = addr of jHideCursor
Lea oldHide,A1 ; our storage for old hooks
Move.L (A0)+,(A1)+ ; jHideCursor
Move.L (A0)+,(A1)+ ; jShowCursor
Move.L (A0),(A1) ; jShieldCursor
; initialize bug's local data
Lea LocalData,A4 ; A4 = local data for bug
Move.L Time,RandomSeed(A4) ; seed our number generator
Move.L (A5),A1 ; A1 = qd globals
Push.L thePort(A1) ; save current port
Link A6,#-GrafSize ; space for a GrafPort
Push.L A7 ; our GrafPort
_OpenPort ; get a full-screen port
Move.L portBits+BaseAddr(A7),MyScrnBase(A4)
; start of screen
Move.W portBits+RowBytes(A7),MyScrnRow(A4)
; bytes in row
Move.L portBits+bounds+topLeft(A7),MyScrnRect(A4)
Move.L portBits+bounds+botRight(A7),MyScrnRect+4(A4)
; the screen's rectangle
Push.L A7 ; our short-lived GrafPort
_ClosePort ; say goodbye!
UnLk A6 ; restore stack
_SetPort ; restore user's port
Jsr ResetBug ; set bug's starting point
; copy the essential stuff into our sysheap block
MoveA.L A3,A1 ; A1 = our sysheap pointer
Lea BugBase,A0
Move.L D4,D0 ; D0 = our pointer's length
_BlockMove ; (A0/srcPtr, A1/destPtr:Ptr; D0/byteCount:Size)
; replace the cursor hooks with our own
Lea jHideCursor,A0 ; A0 = addr of jHideCursor
Lea MyHide-BugBase(A3),A1 ; our HideCursor
Move.L A1,(A0)+ ; replace jHideCursor
Lea MyShow-BugBase(A3),A1 ; our ShowCursor
Move.L A1,(A0)+ ; replace jShowCursor
Lea MyShield-BugBase(A3),A1 ; our ShieldCursor
Move.L A1,(A0) ; replace jShieldCursor
; start our VBL task
Lea MyVBLTask-BugBase(A3),A0 ; A0 = MyVBLTask
Lea MyVBLproc-BugBase(A3),A1 ; A1 = MyVBLproc
Move.L A1,vblAddr(A0) ; set VBL procedure
_VInstall ; (A0/VBLTaskPtr:QElemPtr):D0\OSErr
Feature
OpenExit
MoveM.L (A7)+,A3-A5/D7 ; restore registers
Move.W #OpenErr,D0 ; return an error (we're not open)
Rts
;-refs - Open
com_2 Push #15 ; give a beep!
_SysBeep ; (duration:INTEGER)
Bra OpenExit
; Let's kill a bug!
KillBug
Move.L jHideCursor,A3 ; A3 = HideCursor routine
CmpI.L #'BUG ',BugBase-MyHide(A3) ; check signature
Bne Feature ; branch if not a bug
Lea MyVBLProc-MyHide(A3),A0 ; start of static stuff
Lea MyVBLProc,A1 ; compare to our copy
Move.W #StaticSize-1,D0 ; bytes to check
@0 CmpM.B (A0)+,(A1)+ ; compare
Bne Feature ; exit if not our bug
DBra D0,@0
; It looks like one of our bugs. Kill it.
Lea MyVBLTask-MyHide(A3),A0 ; our task pointer
_VRemove ; stop the bug!
Bne com_2 ; exit if error
_HideCursor ; Hides bugs too!
; Restore the old cursor routines
Lea oldHide-MyHide(A3),A0 ; saved routine pointers
Lea jHideCursor,A1 ; where they came from
Move.L (A0)+,(A1)+ ; restore old routines
Move.L (A0)+,(A1)+
Move.L (A0),(A1)
; Release the memory
Lea BugBase-MyHide(A3),A0 ; start of block
_DisposPtr
_ShowCursor ; restore cursor state
Push #5 ; give a beep
_SysBeep ; (duration:INTEGER)
Move.L #10,A0 ; take a break
_Delay
BTst.L #cmdKey,D7 ; kill all bugs?
Bne KillBug ; Yes!
Bra OpenExit ; else exit
Close MoveQ #0,D0
Rts