[comp.sys.atari.st] Display 22 levels of gray on color mon.

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