logajan@ns.network.com (John Logajan) (05/20/91)
; ; gray22 -- original code by John Logajan 5-19-91, PUBLIC DOMAIN. ; logajan@ns.network.com ; ; This subroutine is called by the ATARI ST COLOR user to go into ; 22 level grayscale display mode (dithered flicker.) Any keys pressed ; from the keyboard will return control back to the user's calling program. ; Keys pressed will not be disturbed and will be available for inspection ; by normal TOS routines. This routine uses 64k just below the normal ; screen RAM area. ; ; This will display a 320x200 minimum window in 22 level grayscale. ; You can have arbitrarily large virtual displays. Scrolling the ; window across the virtual image is accomplished merely by calling this ; program with a new starting (upper left corner) address. ; ; The virtual image must be in the form of one byte per pixel. Legal ; intensity values in each byte range from 0 to 21 (decimal.) The image ; is organized as left to right and top to bottom. ; ; The call to gray22 must include: ; ; A0 set to the window starting point (upper left corner) in the image. ; D0 set to the length of the virtual image horizontal line (bytes). ; D0 should never change unless the virtual image size changes. ; D0 is *not* 320 unless the virtual image size is the same as the ; window size (the window size *is* always 320.) ; ; There are three subroutines below. One initializes everything, one ; restores everything on exit, one displays the window and also has ; an entry point for simply going back into display mode without ; changing the current screen contents (useful for quick returns ; from invalid keypressed keys.) ; ; jsr grayinit -- should be called once to go into low resolution mode. ; Calling grayinit twice or more without intervening ; calls to grayexit will cause original modes to be ; forgotten. ; ; jsr grayexit -- should be called once on exit to restore previous modes. ; Don't call grayexit if you haven't yet called grayinit. ; ; jsr gray22 -- should be called with A0 and D0 set, to update display. ; Don't call gray22 if you haven't yet called grayinit. ; ; jsr grayshow -- should be called to bypass update and continue previous ; display. gray22 automatically executes grayshow. ; Don't call grayshow if you haven't yet called grayinit ; and gray22 at least once. ; ; Here is gray22, be sure to have called grayinit before calling this. ; Also be sure to set A0 and D0 before each gray22 call. ; gray22: move.l a0,corner ; save the starting corner sub.l #320,d0 ; compute and save the window factor move.l d0,wrapit ; ; We get 22 shades of gray by flipping between three screens each ; with different combinations of eight shades of gray (the atari ; hardware limit.) The table below shows the general theory: ; ; Screen Screen Screen ; 0 1 2 Shade 0 1 2 Shade 0 1 2 Shade ; ; 7 7 7 21 4 4 5 13 2 2 1 5 ; 7 7 6 20 4 4 4 12 1 1 2 4 ; 6 6 7 19 4 4 3 11 1 1 1 3 ; 6 6 6 18 3 3 4 10 1 1 0 2 ; 6 6 5 17 3 3 3 9 0 0 1 1 ; 5 5 6 16 3 3 2 8 0 0 0 0 ; 5 5 5 15 2 2 3 7 ; 5 5 4 14 2 2 2 6 ; ; In addition, to keep flicker to a minimum, we phase dither each intensity ; with a three horizontal by three vertical pixel intensity interlace. ; For instance, a block of intensity-18 pixels would be displayed as such: ; ; ... First Screenful ... ... Second Screenful ... ...Third Screenful... ; ... 6 6 7 6 6 7 6 6 ... ... 6 7 6 6 7 6 6 7 ... ... 7 6 6 7 6 6 7 ... ; ... 6 7 6 6 7 6 6 7 ... ... 7 6 6 7 6 6 7 6 ... ... 6 6 7 6 6 7 6 ... ; ... 7 6 6 7 6 6 7 6 ... ... 6 6 7 6 6 7 6 6 ... ... 6 7 6 6 7 6 6 ... ; ... etc ... ... etc ... ... etc ... ; ; ; Do first screen full. ; move.w #-1,-(sp) ; keep resolution move.l phy0,-(sp) ; switch to phys addr screen #0 move.l phy0,-(sp) ; switch to logical addr screen #0 move.w #5,-(sp) ; setscreen trap #14 add.l #12,sp move.l #gtable,a5 ; conversion table base address move.l corner,a2 ; get start point move.l phy0,a1 ; get screen start address ; ; First of three grouped horizontal lines. ; move.w #66,d7 ; 200 lines b0r1: move.l #templine,a3 ; where we build a temporary line move.w #106,d6 ; 320 bits per line b0r2: clr.l d0 move.b (a2)+,d0 move.b (a5,d0),d0 ; dither pixel horz 0 -- line 0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 22(a5,d0),d0 ; dither pixel horz 1 -- line 0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 44(a5,d0),d0 ; dither pixel horz 2 -- line 0 move.b d0,(a3)+ dbf d6,b0r2 add.l wrapit,a2 ; window correction factor subq.l #1,a2 ; backup 1 pixel jsr repac320 ; ; Second of three grouped horizontal lines ; move.l #templine,a3 move.w #106,d6 b0r3: clr.l d0 move.b (a2)+,d0 move.b 22(a5,d0),d0 ; dither pixel horz 0 -- line 1 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 44(a5,d0),d0 ; dither pixel horz 1 -- line 1 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b (a5,d0),d0 ; dither pixel horz 2 -- line 1 move.b d0,(a3)+ dbf d6,b0r3 add.l wrapit,a2 ; window correction factor subq.l #1,a2 ; backup 1 pixel jsr repac320 ; ; Third of three grouped horizontal lines. ; move.l #templine,a3 move.w #106,d6 b0r4: clr.l d0 move.b (a2)+,d0 move.b 44(a5,d0),d0 ; dither pixel horz 0 -- line 2 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b (a5,d0),d0 ; dither pixel horz 1 -- line 2 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 22(a5,d0),d0 ; dither pixel horz 2 -- line 2 move.b d0,(a3)+ dbf d6,b0r4 add.l wrapit,a2 ; window correction factor subq.l #1,a2 ; backup 1 pixel jsr repac320 ; paint a screen line dbf d7,b0r1 ; go do next three screen lines ; ; Do second screen. ; move.l corner,a2 ; get start point move.l phy1,a1 ; get screen start address move.w #66,d7 ; 200 lines b1r1: move.l #templine,a3 ; where we build a temporary line move.w #106,d6 ; 320 bits per line b1r2: clr.l d0 move.b (a2)+,d0 move.b 22(a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 44(a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b (a5,d0),d0 move.b d0,(a3)+ dbf d6,b1r2 add.l wrapit,a2 ; window correction factor subq.l #1,a2 ; backup 1 pixel jsr repac320 move.l #templine,a3 move.w #106,d6 b1r3: clr.l d0 move.b (a2)+,d0 move.b 44(a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b (a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 22(a5,d0),d0 move.b d0,(a3)+ dbf d6,b1r3 add.l wrapit,a2 ; window correction factor subq.l #1,a2 ; backup 1 pixel jsr repac320 move.l #templine,a3 move.w #106,d6 b1r4: clr.l d0 move.b (a2)+,d0 move.b (a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 22(a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 44(a5,d0),d0 move.b d0,(a3)+ dbf d6,b1r4 add.l wrapit,a2 ; window correction factor subq.l #1,a2 ; backup 1 pixel jsr repac320 ; paint a screen line dbf d7,b1r1 ; go do next three screen lines ; ; Do third screen ; move.l corner,a2 ; get start point move.l phy2,a1 ; get screen start address move.w #66,d7 ; 200 lines b2r1: move.l #templine,a3 ; where we build a temporary line move.w #106,d6 ; 320 bits per line b2r2: clr.l d0 move.b (a2)+,d0 move.b 44(a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b (a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 22(a5,d0),d0 move.b d0,(a3)+ dbf d6,b2r2 add.l wrapit,a2 ; window correction factor subq.l #1,a2 ; backup 1 pixel jsr repac320 move.l #templine,a3 move.w #106,d6 b2r3: clr.l d0 move.b (a2)+,d0 move.b (a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 22(a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 44(a5,d0),d0 move.b d0,(a3)+ dbf d6,b2r3 add.l wrapit,a2 ; window correction factor subq.l #1,a2 ; backup 1 pixel jsr repac320 move.l #templine,a3 move.w #106,d6 b2r4: clr.l d0 move.b (a2)+,d0 move.b 22(a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b 44(a5,d0),d0 move.b d0,(a3)+ clr.l d0 move.b (a2)+,d0 move.b (a5,d0),d0 move.b d0,(a3)+ dbf d6,b2r4 add.l wrapit,a2 ; window correction factor subq.l #1,a2 ; backup 1 pixel jsr repac320 ; paint a screen line dbf d7,b2r1 ; go do next three screen lines, else show ; ; This section of the code continuously flips through the three video ; screens to generate the flicker grayscale effect. Any keypress will ; cause a subroutine exit back to the user calling program. ; ; You can call this directly if you want to avoid the delay of updating ; a screen that doesn't need to be updated. However, it makes no sense ; to call this if you haven't already called grayinit and gray22 at least ; once prior. ; grayshow: move.w #37,-(sp) ; wait for vsync trap #14 addq.l #2,sp move.w #-1,-(sp) ; keep resolution move.l phy1,-(sp) ; switch phys addr to screen #1 move.l #-1,-(sp) ; keep logical addr move.w #5,-(sp) ; setscreen trap #14 add.l #12,sp move.w #37,-(sp) ; wait for vsync trap #14 addq.l #2,sp move.w #-1,-(sp) ; keep res move.l phy2,-(sp) ; switch phys addr to screen #2 move.l #-1,-(sp) ; keep logical addr move.w #5,-(sp) ; setscreen trap #14 add.l #12,sp move.w #37,-(sp) ; wait for vsync trap #14 addq.l #2,sp move.w #-1,-(sp) ; keep res move.l phy0,-(sp) ; switch phys addr to screen #1 move.l #-1,-(sp) ; keep logical addr move.w #5,-(sp) ; setscreen trap #14 add.l #12,sp move.w #$0b,-(sp) ; check for keypress trap #1 addq.l #2,sp tst.w d0 beq grayshow ; keep flipping screens if no keypress rts ; else exit back to calling user program ; ; Subroutine to distribute one line's worth of color register numbers ; into goofy Atari video ram format. (This is five times faster than ; using the Atari Line-A Put-Pixel A001 routine.) ; repac320: move.w #19,d6 ; do 20 groups of 16 pixels (320) move.l #templine,a0 ; point to temp line of color registers. repack16: move.w #15,d5 ; do 16 pixels repack: move.b (a0)+,d0 ; get each color register number. roxr.b #1,d0 roxl.w #1,d1 ; pick off lsb roxr.b #1,d0 roxl.w #1,d2 ; pick off lsb+1 roxr.b #1,d0 roxl.w #1,d3 ; pick off lsb+2 dbf d5,repack move.w d1,(a1)+ ; 16 pixel LSB's (a1 points to video ram) move.w d2,(a1)+ move.w d3,(a1)+ clr.w (a1)+ ; 16 pixel MSB's (MSB always zero) dbf d6,repack16 rts ; return to calling program ; ; This routine allocates an additional 64k bytes for two more screens, ; saves the original palette and resolution for later restore. And ; switches to low resolution and installs the grayscale palette. ; grayinit: move.w #2,-(sp) ; get screen physical base address trap #14 addq.l #2,sp move.l d0,phy0 ; save address of screen #0 sub.l #32768,d0 move.l d0,phy1 ; save address of screen #1 sub.l #32768,d0 move.l d0,phy2 ; save address of screen #2 move.l #oldpal,a5 ; save each color number of the old palette move.w #0,d5 move.w #15,d6 gncolor: move.w #-1,-(sp) move.w d5,-(sp) ; color number move.w #7,-(sp) trap #14 addq.l #6,sp move.w d0,(a5)+ ; previous color saved addq.w #1,d5 dbf d6,gncolor move.w #4,-(sp) ; get and save original resolution trap #14 addq.l #2,sp move.w d0,oldres move.w #0,-(sp) ; switch to low res move.l #-1,-(sp) ; keep phys addr move.l #-1,-(sp) ; keep logical addr move.w #5,-(sp) ; setscreen trap #14 add.l #12,sp move.l #newpal,-(sp) ; install new palette move.w #6,-(sp) trap #14 addq.l #6,sp rts ; ; This routine restores original resolution, screen, and palette ; grayexit: move.w oldres,-(sp) ; old resolution move.l phy0,-(sp) ; old phys addr move.l phy0,-(sp) ; old logical addr move.w #5,-(sp) ; setscreen trap #14 add.l #12,sp move.l #oldpal,-(sp) ; install old palette move.w #6,-(sp) trap #14 addq.l #6,sp rts ; ; Variable and storage area ; even newpal: dc.w 0,$111,$222,$333,$444,$555,$666,$777,0,0,0,0,0,0,0,0 oldpal: dc.w 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 gtable: dc.b 0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7 dc.b 0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7 dc.b 0,1,0,1,2,1,2,3,2,3,4,3,4,5,4,5,6,5,6,7,6,7 phy0: dc.l 0 phy1: dc.l 0 phy2: dc.l 0 oldres: dc.l 0 wrapit: dc.l 0 corner: dc.l 0 templine: blk.l 81 -- - John Logajan @ Network Systems; 7600 Boone Ave; Brooklyn Park, MN 55428 - logajan@ns.network.com, 612-424-4888, Fax 612-424-2853