; THE ULTIMATE SCREEN SAVER ; by Richard Leinecker ; Copyright 1989 by ST-LOG Kbdvbase = $22 Supexec = $26 Ptermres = $31 GEMDOS = 1 XBIOS = 14 ********************************************************************** *************************** start of program ************************* ********************************************************************** .text start: movea.l a7,a5 ; copy a7 for base page calc movea.l #ustk,a7 ; init adr for local stack move.l #setup,-(sp) ; put address of set up routine on stack move.w #Supexec,-(sp) ; SUPER mode command trap #XBIOS ; execute setup routine in SUPERVISOR mode addq.l #6,sp ; restore the stack pointer ********************************************************************** * Insert keyboard patch * ********************************************************************** move.w #Kbdvbase,-(sp) ; XBIOS to get vectors trap #XBIOS ; do the trap addq.l #2,sp ; clean up stack add.l #32,d0 ; +32 to get the keyboard vector address movea.l d0,a0 ; put it in a0 move.l (a0),keyvector ; move the vector to keyvector movea.l #insert,a0 ; address of the jmp in the patch adda.l #2,a0 ; adjust it move.l keyvector,(a0) ; now put the keyboard vector in place movea.l d0,a0 ; get address of patch move.l #trapper,(a0) ; install it move.l #newmem,d0 ; new screen memory into d0 andi.l #$fffffe00,d0 ; assure 512 byte boundary addi.l #512,d0 move.l d0,newscreen ; put this value in newscreen ********************************************************************** * calculate amount of memory to keep, then terminate! * ********************************************************************** movea.l 4(a5),a5 ; basepage address move.l $c(a5),d0 ; length of text segment add.l $14(a5),d0 ; length of data segment add.l $1c(a5),d0 ; length of bss segment add.l #$100,d0 ; allow for basepage clr.w -(sp) ; specify exit code move.l d0,-(sp) ; length of block to keep move.w #Ptermres,-(sp) ; Terminate and stay resident trap #GEMDOS ; This doesn't return! ********************************************************************** * Install the vertical blank interrupt * ********************************************************************** setup: move.w #0,d1 ; set the counter to zero movea.l #$456,a0 ; load a0 with address of the pointer to the vector table movea.l (a0),a1 ; load a1 with the address of the vector table move.w #0,d0 ; zero the color move.w d0,vbiflag ; set the flag to 0 movea.l #$454,a2 ; nvbls move.w (a2),d3 ; get the number of vector slots adda.l #4,a1 check: move.l (a1),d2 ; put the vector address in d2 cmpi.l #0,d2 ; see if it is a null beq install ; if it is null then go to the install phase addq.w #1,d1 ; add one to the counter cmp.w d3,d1 ; see if all 8 slots have been checked beq skip ; skip it if there are no slots left adda.l #4,a1 ; increment a1 to the next vector slot bra check ; go back and check the next vector slot install: move.l #vbi,(a1) ; install the vbi address in the vacant slot rts ; then leave skip: rts ; leave ********************************************************************** * Here is the interrupt code * ********************************************************************** vbi: move.w vbiflag,d0 ; see if the fireworks are going off cmpi.w #1,d0 ; 1 means yes bne continue ; if not, keep counting ; firework 1 movea.l #fw1,a0 ; copy firework data to the movea.l #fcolor,a1 ; work area - first assign the jsr assign ; pointers then jsr 'assign' jsr realvbi ; do the actual firework stuff movea.l #fw1,a1 ; copy the work area back to movea.l #fcolor,a0 ; the firework data jsr assign ; firework 2 movea.l #fw2,a0 ; copy firework data to the movea.l #fcolor,a1 ; work area - first assign the jsr assign ; pointers then jsr 'assign' jsr realvbi ; do the actual firework stuff movea.l #fw2,a1 ; copy the work area back to movea.l #fcolor,a0 ; the firework data jsr assign ; firework 3 movea.l #fw3,a0 ; copy firework data to the movea.l #fcolor,a1 ; work area - first assign the jsr assign ; pointers then jsr 'assign' jsr realvbi ; do the actual firework stuff movea.l #fw3,a1 ; copy the work area back to movea.l #fcolor,a0 ; the firework data jsr assign ; firework 4 movea.l #fw4,a0 ; copy firework data to the movea.l #fcolor,a1 ; work area - first assign the jsr assign ; pointers then jsr 'assign' jsr realvbi ; do the actual firework stuff movea.l #fw4,a1 ; copy the work area back to movea.l #fcolor,a0 ; the firework data jsr assign rts ; leave, all 4 fireworks done realvbi: move.w fnum,d0 ; see if there are fireworks going off cmpi.w #0,d0 ; are there any? bne makefw ; If so, go there move.w fcolor,d0 ; load the color move.w ty,d1 ; load the y coordinate move.w tx,d2 ; load the x coordinate jsr pixel ; XOR the old pixel move.w ty,d1 ; load the y coordinate move.w tx,d2 ; load the x coordinate subq.w #1,d1 ; decrement by delta y move.w trajectory,d4 ; load the trajectory number cmpi.w #0,d4 ; is it zero? bne notleft addq.w #1,d2 ; is it one? bra notside notleft: cmpi.w #3,d4 bne notside subq.w #1,d2 notside: move.w d2,tx ; save tx move.w d1,ty ; save ty cmp.w fheight,d1 ; compare with the intended height bgt makepixel ; if not there, just make a pixel move.w #0,numcount ; zero the explosion counter move.w #1,fnum ; set fnum to the small pattern move.w #0,fwvblanks ; zero the counter jsr drawfw ; jump and XOR the fw explosion rts ; leave makefw: move.w fwvblanks,d0 ; load the counter addq.w #1,d0 ; increment it cmpi.w #4,d0 ; see if a delay has passed beq delaygone ; go on if so move.w d0,fwvblanks ; store the value rts ; leave delaygone: move.w #0,fwvblanks ; zero the counter jsr drawfw ; jump and XOR the fw explosion move.w fnum,d0 ; load from fnum addq.w #1,d0 ; increment the counter move.w d0,fnum ; store it cmpi.w #9,d0 ; have there been seven patterns beq is_nine ; go on if not jsr drawfw ; jump and XOR the fw explosion rts ; leave is_nine: move.w numcount,d0 ; load numcount addq.w #1,d0 ; increment counter move.w d0,numcount ; store it move.w numexplode,d1 ; load explosion number cmp.w d0,d1 ; compare them beq is_done ; firework explosions finished! move.w #1,fnum ; reset fnum to 1 movea.l #$4bc,a0 ; pointer to system clock move.w (a0),d0 ; get the value move.w colormask,d1 and.w d1,d0 ; mask to use maximum colors only cmpi.w #0,d0 bne nzero1 ori.w #1,d0 ; insure plane one nzero1: move.w d0,fcolor ; store in fcolor move.w (a0),d0 ; get a number andi.w #15,d0 ; and it to assure value in range move.w (a0),d3 ; get another number andi.w #7,d3 ; and it to assure value in range move.w tx,d1 ; load x coordinate move.w ty,d2 ; load y coordinate subi.w #7,d1 ; subtract 4 subi.w #4,d2 ; subtract 4 add.w d0,d1 ; add the random value add.w d3,d2 ; add the other random value move.w d1,tx ; store it move.w d2,ty ; store it jsr drawfw ; make the pattern rts ; leave is_done: move.w #0,fnum ; zero fnum movea.l #$4bc,a0 ; pointer to system clock move.w (a0),d0 ; get the value move.w colormask,d1 and.w d1,d0 ; mask to use maximum colors only cmpi.w #0,d0 bne nzero2 ori.w #1,d0 ; insure plane one nzero2: move.w d0,fcolor ; store in fcolor move.w (a0),d4 ; now get another value andi.w #31,d4 ; and it to insure value is in range addi.w #40,d4 ; add 31 move.w d4,fheight ; store in fheight move.w (a0),d4 ; get another random number andi.w #3,d4 ; and it to assure value is in range addq.w #1,d4 ; add one move.w d4,numexplode ; store the value move.w (a0),d4 ; get yet another value andi.w #3,d4 ; and it to assure value is in range move.w d4,trajectory ; store in trajectory mulu onethird,d4 ; adjust the starting point add.w fromleft,d4 ; according to resolution move.w #199,ty ; store the y coordinate starting point move.w #199,d1 ; put 199 in d1 move.w d4,tx ; store the x coordinate starting point move.w d4,d2 ; store x value makepixel: move.w fcolor,d0 ; move the color into d0 jsr pixel ; goto the pixel routine rts ; leave continue: move.w vblanks,d0 ; get the vblanks counter value addq.w #1,d0 ; increment the counter move.w d0,vblanks ; store the vblanks counter value cmpi.w #32000,d0 ; see if the appropriate time has passed bne gohome ; otherwise skip it move.l newscreen,d0 ; get the screen address movea.l d0,a0 ; move the newscreen address into a0 move.w #0,d0 ; set the loop counter to zero clearit: move.l #0,(a0)+ ; clear the screen addq.w #1,d0 ; increment loop counter cmpi #8000,d0 ; have 8000 longs been cleared? bne clearit ; otherwise do some more movea.l #$44e,a0 ; set to _v_bas_ad - contains the screen address move.l (a0),oldscreen ; get the old screen location movea.l #$ffff8201,a0 ; set to the Physbase location movea.l #newscreen,a1 ; move the address of newscreen into a1 adda.l #1,a1 ; increment the pointer to get correct byte move.b (a1),(a0) ; move the byte to the Physbase location adda.l #2,a0 ; increment to the other byte location adda.l #1,a1 ; add 1 to get the correct byte of newscreen move.b (a1),(a0) ; move the second byte movea.l #$ff8260,a0 ; set to ff8260 - contains the resolution move.b (a0),d0 ; put resolution in d0 andi.w #$0003,d0 ; mask off any rubish move.w d0,rez ; store it in 'rez' move.w #0,oldrez ; store '0' in oldrez cmpi.w #1,d0 ; see if we are in medium resolution bne dontchange ; if not, don't attempt to change res movea.l #$ff8260,a0 ; set to the hardware resolution location move.w (a0),d1 ; get the old resolution value move.w d1,oldrez ; save it move.w #0,d0 ; move a zero into d0 movea.l #$44c,a0 ; set to the system variable location move.b d0,(a0) ; store the new res movea.l #$ff8260,a0 ; set to the hardware location move.b d0,(a0) ; store the new res move.w d0,rez ; store the new res in 'rez' dontchange: cmpi.w #0,d0 ; assign resolution dependant values bne notlow move.w #8,wordbytes ; bytes per 16 screen pixels move.w #$000f,colormask; number of colors to be used move.w #80,onethird ; horizontal distance (c. one third of screen) move.w #40,fromleft ; value to start from left of screen move.w #1,xadjust ; x factor bra gotrez notlow: move.w #2,wordbytes ; bytes per 16 screen pixels move.w #0,colormask ; number of colors to be used move.w #160,onethird ; horizontal distance (c. one third of screen) move.w #40,fromleft ; value to start from left of screen move.w #2,xadjust ; x factor gotrez: movea.l #oldpalette,a1 ; set destination of palette copy movea.l #$ff8240,a0 ; set source of palette copy jsr getsetpalette ; jump and do the copy movea.l #newpalette,a0 ; set source of palette copy movea.l #$ff8240,a1 ; set destination of palette copy jsr getsetpalette ; jump and do the copy move.w #1,vbiflag move.w #0,countfw ; zero the counter move.w #0,d7 move.w #0,fnum move.w #0,numcount four_fw: movea.l #$4bc,a0 ; pointer to system clock move.w (a0),d0 ; get the value move.w colormask,d1 and.w d1,d0 ; mask to use maximum colors only cmpi.w #0,d0 bne nzero3 ori.w #1,d0 ; insure plane one nzero3: move.w d0,fcolor ; store in fcolor move.w d7,d4 addq.w #1,d4 mulu #10,d4 addi.w #40,d4 move.w d4,fheight ; store in fheight move.w (a0),d4 ; get another random number andi.w #3,d4 ; and it to assure value is in range addq.w #1,d4 ; add one move.w d4,numexplode ; store the value move.w d7,d4 move.w d4,trajectory ; store in trajectory mulu onethird,d4 ; adjust the starting point add.w fromleft,d4 ; according to resolution move.w #199,ty ; store the y coordinate starting point move.w #199,d1 ; put 199 in d1 move.w d4,tx ; store the x coordinate starting point move.w d4,d2 ; store x value move.w countfw,d7 addq.w #1,d7 move.w d7,countfw cmpi.w #1,d7 bne pastfw1 movea.l #fcolor,a0 movea.l #fw1,a1 jsr assign move.w fcolor,d0 move.w ty,d1 move.w tx,d2 jsr pixel ; make the pixel bra four_fw * Generate four sets of random firework data pastfw1: cmpi.w #2,d7 bne pastfw2 movea.l #fcolor,a0 movea.l #fw2,a1 jsr assign move.w fcolor,d0 move.w ty,d1 move.w tx,d2 jsr pixel ; make the pixel bra four_fw pastfw2: cmpi.w #3,d7 bne pastfw3 movea.l #fcolor,a0 movea.l #fw3,a1 jsr assign move.w fcolor,d0 move.w ty,d1 move.w tx,d2 jsr pixel ; make the pixel bra four_fw pastfw3: cmpi.w #4,d7 bne gohome movea.l #fcolor,a0 movea.l #fw4,a1 jsr assign move.w fcolor,d0 move.w ty,d1 move.w tx,d2 jsr pixel ; make the pixel gohome: rts ; leave ********************************************************************** * Here is the keyboard patch * ********************************************************************** trapper: move.w #0,vblanks ; reset the vbi counter to zero movem.l a0-a1/d0/d7,-(sp) ; save addresses and loop counter move.w vbiflag,d0 ; get the value in the vbi flag cmpi.w #1,d0 ; see if it is one (enabled) bne getlost ; if not leave... movea.l #$ffff8201,a0 movea.l #oldscreen,a1 adda.l #1,a1 move.b (a1),(a0) adda.l #2,a0 adda.l #1,a1 move.b (a1),(a0) move.w #0,fnum ; zero fnum in case of fireworks going off movea.l #$ff8240,a1 ; set destination of palette copy movea.l #oldpalette,a0 ; set source of palette copy jsr getsetpalette ; jump and do the copy move.w #0, vbiflag ; set the vbi flag to zero move.w oldrez,d0 cmpi.w #0,d0 beq getlost move.w #1,d0 movea.l #$44c,a0 move.b d0,(a0) movea.l #$ff8260,a0 move.b d0,(a0) getlost: movem.l (sp)+,a0-a1/d0/d7 ; restore addresses and loop counter insert: jmp $11111111 ; this address is changed upon program init ********************************************************************** * This routine XORs a pixel on the screen * ********************************************************************** pixel: * Pixel color in d0 * y coordinate in d1 * x coordinate in d2 * use a3 for screen address * use a4 for bit mask movea.l newscreen,a3 ; put the screen address in a3 mulu #160,d1 ; multiply the y coordinate by 16 andi.l #$0000ffff,d1 adda.l d1,a3 ; add the y value to a3 andi.l #$0000ffff,d2 divu #16,d2 ; divide the x by 16 to get the number of words move.l d2,d1 ; store the result in order to ascertain the remainder andi.l #$0000ffff,d2 mulu wordbytes,d2 ; multiply by wordbytes to find the correct byte offset andi.l #$0000ffff,d2 adda.l d2,a3 ; add the byte offset to a3 lsr.l #8,d1 ; shift 8 lsr.l #7,d1 ; shift 7 andi.l #$000000fe,d1 ; offset in d1 movea.l #pixbits,a4 ; put the pixbit address in a4 adda.w d1,a4 ; add the correct offset to a4 move.w (a4),d3 ; move the data into d3 * Now the pixel data is in d3 and the screen address is in a3 btst #0,d0 beq secondplane ; if not, skip to the second plane move.w (a3),d2 ; get the screen byte eor.w d3,d2 ; XOR it with the data move.w d2,(a3) ; put it back on the screen secondplane: adda.w #2,a3 ; add the correct number of bytes btst #1,d0 beq thirdplane ; if not, skip to third plane move.w (a3),d2 ; get the screen byte eor.w d3,d2 ; XOR it with the data move.w d2,(a3) ; put it back on the screen thirdplane: adda.w #2,a3 ; add the correct number of bytes btst #2,d0 beq fourthplane ; if not, skip to the fourth plane move.w (a3),d2 ; get the screen byte eor.w d3,d2 ; XOR it with the data move.w d2,(a3) ; put it back on the screen fourthplane: btst #3,d0 beq lastplane ; if not then leave adda.w #2,a3 ; add the correct number of bytes move.w (a3),d2 ; get the screen byte eor.w d3,d2 ; XOR it with the data move.w d2,(a3) ; put it back on the screen lastplane: rts ; leave the routine ************************************************************************* * Get/Set current pallete (must be in Supervisor) * ************************************************************************* getsetpalette: move.w #0,d7 ; zero the counter gsploop:move.w (a0)+,(a1)+ ; move the data addq.w #1,d7 ; increment the counter cmpi.w #16,d7 ; have you copied 16? bne gsploop ; if not, do some more rts ; leave subroutine ************************************************************************ * Draw a firework explosion part * ************************************************************************ drawfw: move.w fcolor,d0 ; get the firework color move.w #0,d7 ; zero the counter movea.l #fpattern,a5 ; get the address of fpattern move.w fnum,d6 ; load the firework number move.w d6,d5 ; save the firework number in d5 for future use mulu #3,d6 ; figure out the radius do_dots: move.w tx,d2 ; get the x coordinate sub.w d6,d2 ; adjust to top left of firework pattern move.w xadjust,d4 ; load xadjust cmpi.w #2,d4 ; see if it is 2 bne xa_not_2 ; go on otherwise sub.w d6,d2 ; adjust additional amount xa_not_2: move.w ty,d1 ; get the y coordinate sub.w d6,d1 ; adjust to top left of firework pattern move.w (a5)+,d4 ; get the x pattern element mulu d5,d4 ; multiply according to the size of the fw mulu xadjust,d4 ; resolution factor add.w d4,d2 ; add the x position to the top left of pattern move.w (a5)+,d4 ; get the y pattern element mulu d5,d4 ; multiply according to the size of the fw add.w d4,d1 ; add the y position to the top left of pattern addq.w #1,d7 ; increment the counter jsr pixel ; now, make the pixel cmpi.w #8,d7 ; see if you have done the 8 dots bne do_dots ; do some more if not rts ; leave the routine ************************************************************************ * Move the specified firework data to the work area * ************************************************************************ assign: move.w #0,d0 do_more: move.w (a0)+,(a1)+ addq.w #1,d0 cmpi.w #10,d0 bne do_more rts ********************************************************************** * Data Storage Segment * ********************************************************************** .even .data pixbits: .dc.w $8000,$4000,$2000,$1000,$0800,$0400,$0200,$0100 .dc.w $0080,$0040,$0020,$0010,$0008,$0004,$0002,$0001 newpalette: .dc.w $0000,$0070,$0700,$0700,$0770,$0707,$0077,$0777 .dc.w $0770,$0704,$0074,$0740,$0704,$0740,$0400,$0700 fpattern: .dc.w $0000,$0003,$0001,$0001,$0003,$0000,$0005,$0001 .dc.w $0006,$0003,$0005,$0005,$0003,$0006,$0001,$0005 fw1: .dc.w $0000,$0000,$0000,$0000,$0000,$0000 .dc.w $0000,$0000,$0000,$0000 fw2: .dc.w $0000,$0000,$0000,$0000,$0000,$0000 .dc.w $0000,$0000,$0000,$0000 fw3: .dc.w $0000,$0000,$0000,$0000,$0000,$0000 .dc.w $0000,$0000,$0000,$0000 fw4: .dc.w $0000,$0000,$0000,$0000,$0000,$0000 .dc.w $0000,$0000,$0000,$0000 ************************************************************************** * Block Storage Segment .bss rez: .ds.w 1 ; current resolution oldrez: .ds.w 1 oldscreen: .ds.l 1 ; old screen pointer keyvector: .ds.l 1 ; keyboard vector oldpalette: .ds.w 16 wordbytes: .ds.w 1 ; bytes per screen word colormask: .ds.w 1 onethird: .ds.w 1 fromleft: .ds.w 1 xadjust: .ds.w 1 newmem: .ds.l 8200 newscreen: .ds.l 1 vblanks: .ds.w 1 ; vblank counter countfw: .ds.w 1 vbiflag: .ds.w 1 ; status of the screen saver ; 0=not enabled ; 1=enabled fcolor: .ds.w 1 numexplode: .ds.w 1 trajectory: .ds.w 1 fheight: .ds.w 1 ty: .ds.w 1 tx: .ds.w 1 fnum: .ds.w 1 numcount: .ds.w 1 fwvblanks: .ds.w 1 .ds.l 255 ; the stack can grow down 1K before ustk: .ds.l 1 ; it starts to eat the program!! .end