# -------------------------------------------------------------------
# BLITTER TUTORIAL                      (c) Copyright 1996 Nat! & KKP
# -------------------------------------------------------------------
# These are some of the results/guesses that Klaus and Nat! found
# out about the Jaguar with a few helpful hints by other people, 
# who'd prefer to remain anonymous. 
#
# Since we are not under NDA or anything from Atari we feel free to 
# give this to you for educational purposes only.
#
# Please note, that this is not official documentation from Atari
# or derived work thereof (both of us have never seen the Atari docs)
# and Atari isn't connected with this in any way.
#
# Please use this informationphile as a starting point for your own
# exploration and not as a reference. If you find anything inaccurate,
# missing, needing more explanation etc. by all means please write
# to us:
#    nat@zumdick.ruhr.de
# or
#    kp@eegholm.dk
#
# If you could do us a small favor, don't use this information for
# those lame flamewars on r.g.v.a or the mailing list.
#
# HTML soon ?
# -------------------------------------------------------------------
# $Id: tutorial.html,v 1.17 1997/11/16 18:14:42 nat Exp $
# -------------------------------------------------------------------

Blitter tutorial:
=-=-=-=-=-=-=-=-=

Here are a few examples, that should work. They haven't been tested (yet).
(note: not the best code, but hopefully understandable)

1. Example code:

a) Clearing 256 bytes starting from $2000
b) Setting 256 bytes starting at $2001 to 0xFF
c) Copying 256 bytes from $2000 to $3000
d) XORing 256 bytes from $2000 with $3000
e) Gouraudshading a horizontal 64 pixel line...
f) Blitting a 64x64 piece from a 128x128 pixmap...
g) Same as f) with source shading
h) Filling a block with ascending values...
i) Same as g) but in phrasemode


2. Rotation with the blitter




a) Clearing 256 bytes starting from $2000
   --------------------------------------

.wait:
   move.l   B_CMD,d0             ;; wait for blitter to finish
   ror.w    #1,d0                ;; Check if blitter is idle
   bcc.b    .wait                ;; bit was clear -> busy

   move.l   #$2000,A1_BASE       ;; point to destination
   move.l   #$0,A1_PIXEL         ;; start in front
   move.l   #PIXEL16|XADDPHR|PITCH1,A1_FLAGS ;; use 16bit pixels/phrasemode
   move.l   #0,B_PATD            ;; our value to draw

   move.w   #1,d0                ;; we wanna draw 1 'line'
   swap     d0                   ;; in upper word
   move.w   #256/2/4,d0          ;; and 256 bytes == 128 16-bit-pixels
   move.l   d0,B_COUNT           ;; == 32 phrases

   move.l   #PATDSEL,B_CMD       ;; use B_PATD to draw and GO



b)  Setting 256 bytes starting at $2001 to 0xFF:
    -------------------------------------------

   move.l   #$2000,A1_BASE       ;; point to destination
   move.l   #$1,A1_PIXEL         ;; start at offset 1
   move.l   #PIXEL8|XADDPIX|PITCH1,A1_FLAGS ;; use 8bit pixels pixelmode
   move.l   #$FF,B_PATD          ;; our value to draw

   move.w   #1,d0                ;; we wanna draw 1 'line'
   swap     d0                   ;; in upper word
   move.w   #256,d0              ;; and 256 bytes == 256 8-bit-pixels
   move.l   d0,B_COUNT           

   move.l   #PATDSEL,B_CMD       ;; use B_PATD to draw and GO



c)  Copying 256 bytes from $2000 to $3000
    ------------------------------------
   
   move.l   #$3000,A1_BASE       ;; point to destination
   move.l   #$0,A1_PIXEL         ;; start in front
   move.l   #PIXEL16|XADDPHR|PITCH1,A1_FLAGS ;; use 16bit pixels/phrasemode

   move.l   #$2000,A2_BASE       ;; point to source
   move.l   #$0,A2_PIXEL         ;; start in front
   move.l   #PIXEL16|XADDPHR|PITCH1,A2_FLAGS ;; use 16bit pixels/phrasemode

   move.w   #1,d0                ;; we wanna draw 1 'line'
   swap     d0                   ;; in upper word
   move.w   #256/2/4,d0          ;; and 256 bytes == 128 pixels
   move.l   d0,B_COUNT           ;; == 32 phrases

   move.l   #LFU_REPLACE|SRCEN,B_CMD ;; straight copy 



d)  XORing 256 bytes from $2000 with $3000
    --------------------------------------

   as c) but    

   move.l   #LFU_XOR|DSTEN|SRCEN,B_CMD 

      instead of

   move.l   #LFU_REPLACE|SRCEN,B_CMD




e)  Gouraudshading a horizontal 64 pixel line 
    on a 320x200 bitmap at $20000 starting at relative point 100,120
    --------------------------------------------------------------
   
   move.l   #$20000,A1_BASE      ;; point to destination
   move.w   #120,d0              ;; Y pos
   swap     d0                   ;; in upper word
   move.w   #100,d0              ;; X pos in lower
   move.l   d0,A1_PIXEL          ;; start in the middle
   move.l   #PIXEL16|XADDPIX|WID320|PITCH1,A1_FLAGS ;; note the WID320!!

   move.w   #$8000,B_PATD        ;; starting color
   move.l   #$00038000,B_IINC    ;; intensity increment (3.5)

   move.w   #1,d0                ;; we wanna draw 1 'line'
   swap     d0                   ;; in upper word
   move.w   #64,d0               ;; and 64 pixels
   move.l   d0,B_COUNT           

   move.l   #PATDSEL|GOURD,B_CMD 



f)  Blitting a 64x64 piece from a 128x128 pixmap  (Pos: 10, 20)
    unto a 320x200 pixmap with source shading (Pos: 100,120). 
    The source is at $30000 the destination at $20000. 
    --------------------------------------------------------------
   
   move.l   #$20000,A1_BASE      ;; point to destination
   move.l   #PIXEL16|XADDPIX|WID320|PITCH1,A1_FLAGS ;; note the WID320!!

   move.w   #120,d0              ;; Y pos
   swap     d0                   ;; in upper word
   move.w   #100,d0              ;; X pos in lower
   move.l   d0,A1_PIXEL          ;; start in the middle

   move.w   #1,d0
   swap     d0
   move.w   #-64,d0
   move.l   d0,A1_STEP   


   move.l   #$30000,A2_BASE
   move.l   #PIXEL16|XADDPIX|WID128|PITCH1,A2_FLAGS

   move.w   #12,d0               ;; Y pos
   swap     d0                   ;; in upper word
   move.w   #10,d0               ;; X pos in lower
   move.l   d0,A2_PIXEL

   move.w   #1,d0
   swap     d0
   move.w   #-64,d0
   move.l   d0,A2_STEP

   move.w   #64,d0               ;; we wanna draw 64 lines
   swap     d0                   ;; in upper word
   move.w   #64,d0               ;; and 64 pixels each
   move.l   d0,B_COUNT           
   
   move.l   #$00040000,B_IINC    ;; shade value for source

   move.l   #SRCEN|DSTEN|UPDA1|UPDA2|SRCSHADE,B_CMD 



g)  Blitting a 64x64 piece from a 128x128 pixmap  (Pos: 10, 20)
    unto a 320x200 pixmap with source shading (Pos: 100,120). 
    The pixmap will appear rotated about 45 degrees 
    The source is at $30000 the destination at $20000.   (TESTED)
    --------------------------------------------------------------
   
   move.l   #$20000,A1_BASE      ;; point to destination
   move.l   #PIXEL16|XADDINC|WID320|PITCH1,A1_FLAGS ;; note the WID320!!

   move.w   #120,d0              ;; Y pos
   swap     d0                   ;; in upper word
   move.w   #100,d0              ;; X pos in lower
   move.l   d0,A1_PIXEL          ;; start in the middle

   move.l   #0,A1_INC            ;; scaling in the X direction
   move.w   #$8000,d0            ;; and stepping diagonally
   swap     d0
   move.w   #$8000,d0
   move.l   d0,A1_FINC

   move.w   #-33,d0     
   swap     d0
   move.w   #-32,d0
   move.l   d0,A1_STEP

   move.w   #$8000,d0   
   swap     d0
   move.w   #$8000,d0
   move.l   d0,A1_FSTEP


   move.l   #$30000,A2_BASE
   move.l   #PIXEL16|XADDPIX|WID128|PITCH1,A2_FLAGS

   move.w   #12,d0               ;; Y pos
   swap     d0                   ;; in upper word
   move.w   #10,d0               ;; X pos in lower
   move.l   d0,A2_PIXEL

   move.w   #1,d0
   swap     d0
   move.w   #-64,d0
   move.l   d0,A2_STEP

   move.w   #64,d0               ;; we wanna draw 64 lines
   swap     d0                   ;; in upper word
   move.w   #64,d0               ;; and 64 pixels each
   move.l   d0,B_COUNT           
   
   move.l   #$00040000,B_IINC    ;; shade value for source

   move.l   #LFU_REPLACE|DSTEN|SRCEN|UPDA1|UPDA1F|UPDA2|SRCSHADE,B_CMD 



h) Filling a 65536 * 16 bit block with ascending values from 0 to
   $FFFF at $20000
   --------------------------------------------------------------

   move.l   #$20000,A1_BASE      ;; point to destination
   move.l   #0,A1_PIXEL          ;; start in the middle
   move.l   #PIXEL16|XADDPIX|PITCH1,A1_FLAGS    ;; phrasemode would be better!

   move.w   #$0000,B_PATD        ;; starting color
   move.l   #$00010000,B_IINC     ;; intensity increment (1.0)

   move.w   #256,d0              ;; we wanna draw 256 'lines'
   swap     d0                   ;; in upper word
   move.w   #256,d0              ;; and 256 'pixels' each
   move.l   d0,B_COUNT           

   move.l   #PATDSEL|GOURD|TOPBEN|TOPNEN,B_CMD 



i) Filling a 65536 * 16 bit block with ascending values from 0 to
   $FFFF at $20000. Doing the same in phrase mode
   --------------------------------------------------------------

   move.l   #$20000,A1_BASE      ;; point to destination
   move.l   #0,A1_PIXEL          ;; start in the middle
   move.l   #PIXEL16|XADDPHR|PITCH1,A1_FLAGS   

   move.l   #$00010000,B_IINC    ;; intensity increment (1.0)
   move.w   #$0000,B_I3          ;; starting color leftmost
   move.w   #$0001,B_I2          ;; starting color ..
   move.w   #$0002,B_I1          ;; starting color ..
   move.w   #$0003,B_I0          ;; starting color ritemost

   move.w   #256,d0              ;; we wanna draw 256 'lines'
   swap     d0                   ;; in upper word
   move.w   #64,d0               ;; and 256/4 'phrases' each
   move.l   d0,B_COUNT           

   move.l   #PATDSEL|GOURD|TOPBEN|TOPNEN,B_CMD 



Rotating with the blitter:
=-=-=-=-=-=-=-=-=-=-=-=-=

Scaling and rotation are really nice effects you can do with the 
blitter. But you got to be careful that you don't leave holes 
accidentally on the screen. 

There are two ways to produce holes, one because of using a step
size (in either direction) that is larger than 1. This should be
obvious I guess.

The other not so obvious source of holes are quantization errors.

For example to rotate a bitmap around 30 degrees, you might do these
calculations:

          a   
    A +--------+ B    our source pixmap of size 64x64
      |        |
      |        | a            a == 64
      |        |
   D  +--------+ C
               
             A   x      
      ........+.........      That's how it should look like
             / \  .           rotated                                 
            /   \ . y   
           /     \.
        D +       + B
           \     /
          a \   / a     
             \ /
              + C

     A     x
      +...........         a magnified look at the upper corner
       \m)    90 .         we notice that:
        \        .
         \       .
          \      .         x = cos( m) * a
           \     .  y      y = sin( m) * a
          a \    .
             \   .
              \  .         m = 30 deg
               \ .         x = 55.43 = $0037.6CF5
                \.         y = 32.00 = $0020.0000
                 +         
                  B
hor increment = x / a = cos( m) = 0.87 = $0.DDB4  ($1.0000 * 0.87)
ver increment = y / a = sin( m) = 0.50 = $0.8000

           Y     X
--------+-----+-----+      
A1_INC     0     0
A1_FINC  $8000 $DDB4

We wanna draw in runs from A to B. Now after a line has been drawn. We
need to step back to the point we came from (A) (-55.43,-32) and then
move in a 60 degree (180 - 90 - 30) angle backwards (-) 
and downwards (+), down the left slope (A -> D).

To get it perfect we calculate everything in integer fractional 
representation (like the Blitter does):   
                                 Y         X
        64 * A1_INC.A1_FINC = $20.0000  $37.6D00

hor step      = -x - cos( 60) = -$37.6D00 - $8000 = $FFC81300
ver step      = -y + sin( 60) = -$20.0000 + $DDB4 = $FFE0DDB4

           Y     X
--------+-----+-----+      
A1_STEP  $FFE0 $FFC8
A1_FSTEP $DDB4 $1300


Now if you blit something on the screen you'll see holes (not
drawn pixels) in your pixmap, although the step size in both
directions is 1 (sin(x)^2 + cos(x)^2 = 1). The error is introduced
while the blitter is stepping in the 'integer.fractional' domain
and then plots a pixel in the 'integer' domain. What I mean with
'integer.fractional' domain are the contents of the A1_PIXEL, A1_FPIXEL
and the A1_FPIXEL and the A1_INC, A1_FINC registers. 

The problem will be visible on adjacent lines only, because the
quantization of the pixels, which starts at a different fractional 
offset, will be slightly but decisively different for the subsequent 
runs. 
Try the following program. Run it once with BLOCK set to 1 and look 
at the resulting moiree pattern. Now set BLOCK to 0 and examine the 
colored lines closely (drawn in sequence from top to bottom) and notice 
how each line differs from the next.


BLOCK   equ     1

   .if BLOCK
HEIGHT  equ     64
WIDTH   equ     64
   .else
HEIGHT  equ     16
WIDTH   equ     16
   .endif

HINC    equ     $DDB4
VINC    equ     $8000

HSTEP   equ     -(HEIGHT*HINC)-VINC
VSTEP   equ     -(WIDTH*VINC)+HINC

blit:
.wait:   
   move.l   B_CMD,d0
   lsr.w    #1,d0
   bcc.b    .wait
   
   move.l   #$20000,A1_BASE      ;; point to destination
   move.l   #PIXEL16|XADDINC|WID256|PITCH1,A1_FLAGS ;; note the WID320

   move.w   #64,d0               ;; Y pos
   swap     d0                   ;; in upper word
   move.w   #128,d0              ;; X pos in lower
   move.l   d0,A1_PIXEL          ;; start in the middle

   move.l   #$0,A1_FPIXEL        
   move.l   #$0,A1_INC           

   move.w   #VINC,d0             
   swap     d0
   move.w   #HINC,d0             
   move.l   d0,A1_FINC

 .if BLOCK
   move.w   #VSTEP>>16,d0        
 .else
   move.w   #(VSTEP>>16)+2,d0   ;; uncomment this, comment out next
 .endif
   swap     d0
   move.w   #HSTEP>>16,d0        
   move.l   d0,A1_STEP

   move.w   #VSTEP&$FFFF,d0
   swap     d0
   move.w   #HSTEP&$FFFF,d0
   move.l   d0,A1_FSTEP

   move.w   #HEIGHT,d0              ;; we wanna draw # lines
   swap     d0                      ;; in upper word
   move.w   #WIDTH,d0               ;; and # pixels each
   move.l   d0,B_COUNT           

 .if BLOCK
   move.l   #PATDSEL|UPDA1|UPDA1F|LFU_REPLACE,B_CMD 
 .else
   move.l   #$01000000,B_IINC       
   move.l   #$00C0,B_PATD           
   move.l   #GOURD|TOPBEN|PATDSEL|UPDA1|UPDA1F|LFU_REPLACE,B_CMD 
 .endif

   rts

How do you get rid of the holes ? 

If you want to use the blitter for rotation, you probably should scale
down the bitmaps you want to rotate. Maybe using a 96x96 source 
pixmap for an 'apparent' 64x64 target pixmap size will be sufficient.
You gotta experiment. It depends on the rotation angle!


( Well there's a software method I did once, which can do rotation without 
  holes. It goes basically like this:

   Use the steepest slope for "STEP" use the other slope for your
   line drawings (like Bresenham f.e.)
   Use the most outside pixel as your starting pixel
   Draw a line with the required slope.
   Do an integer step horizontally (or vertically but not both) away
   from your first starting pixel.
   Skip as many pixels as needed until you want to start drawing.
   Draw the line.

   I can't quite see this being done with the blitter though :).
)

Bastian Schick recommends the following for really fast rotations:

1. setup blitter with the GPU
2. rotate from memory into local GPU RAM using the blitter
3. write rotated data with the GPU (sic) to destination using
   phrase mode, while
4. the blitter does step 2 again


Nat! (nat@zumdick.ruhr.de)
Klaus (kp@eegholm.dk)

$Id: tutorial.html,v 1.17 1997/11/16 18:14:42 nat Exp $