    # -------------------------------------------------------------------
    # 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

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