3. Rolling image on copperbar

This is the third tutorial in the series. This time we look at another famous Amiga-effect, the copperbar-roller. Here we will roll/rotate an image around a copperbar, yet again using only the Amiga hardware to do it.

THIS DEMO

Download LHA-package.

Here we try to make it seem like we are rotatin an image around a virtual colorbar. The idea is to have the image move on top of the copperbar and at the same time do 2 things to simulate the rotation (as no real rotation doesn't happen here):

  • we make all colors of the image and of the copperbar darker towards the top and bottom, as this simulates depth at some level
  • at the same time we use horizontal-shifting to simulate a depth on the copperbar. The lookup-table for the horizontal-shift-values are calculated with the standard formula for a circle, x^2 + y^2 = r^2 ==> x = sqrt(r^2 - y^2), we need only positive values

In addition to the effect mentioned above, the only other thing happening here is moving the image upwards according to the wanted speed, in this example we add #40 (1 line) each frame. But, to get an other effect here I put 5 copperbars with rotating images with a small offset regarding the startingline in the image, this makes the wave-effect between the different copperbars.

Next tutorial will show the same effect, but on a 64px high copperbar (in this demo the copperbars are 32px). With a taller copperbar there comes the need of larger horizontal-shifts than 16px, but we will not be using modulos this time.

 

 
   1 
; Rolling image on copperbars-1
   2 
 
   3 
DMACONR        EQU        $dff002
   4 
ADKCONR        EQU        $dff010
   5 
INTENAR        EQU        $dff01c
   6 
INTREQR        EQU        $dff01e
   7 
 
   8 
DMACON        EQU        $dff096
   9 
ADKCON        EQU        $dff09e
  10 
INTENA        EQU        $dff09a
  11 
INTREQ        EQU        $dff09c
  12 
 
  13 
    incdir "windows:amigavikke/"
  14 
 
  15 
; Optimizations could easily be made to the small/tight loops in the code by using
  16 
; incremental addressing (An)+ or decremental addressing -(An) and REPT <n> / ERPT
  17 
 
  18 
init:
  19 
; store hardware registers, store view- and copperaddresses, load blank view,
  20 
; wait 2x for top of frame, own blitter, wait for blitter and forbid multitasking!
  21 
; all this just to be able to exit gracely
  22 
 
  23 
    ; store data in hardwareregisters ORed with $8000
  24 
    ; (bit 15 is a write-set bit when values are written back into the system)
  25 
    move.w    DMACONR,d0
  26 
    or.w #$8000,d0
  27 
    move.w d0,olddmareq
  28 
    move.w    INTENAR,d0
  29 
    or.w #$8000,d0
  30 
    move.w d0,oldintena
  31 
    move.w    INTREQR,d0
  32 
    or.w #$8000,d0
  33 
    move.w d0,oldintreq
  34 
    move.w    ADKCONR,d0
  35 
    or.w #$8000,d0
  36 
    move.w d0,oldadkcon
  37 
 
  38 
    move.l    $4,a6                 ; execBase ==> a6
  39 
    move.l    #gfxname,a1         ; pointer to gfxname ==> a1 : used in openLibrary
  40 
    moveq    #0,d0                 ; d0 = 0 any version of graphics.library will do
  41 
    jsr    -552(a6)                ; d0 = openLibrary(a1,d0)
  42 
    move.l    d0,gfxbase             ; store the returned pointer ==> gfxbase
  43 
    move.l     d0,a6                 ; d0 ==> a6 : a6 used as addressing base below
  44 
    move.l     34(a6),oldview         ; store old Viewport
  45 
    move.l     38(a6),oldcopper     ; store old Copperlist
  46 
 
  47 
    move.l #0,a1
  48 
    jsr -222(a6)    ; LoadView
  49 
    jsr -270(a6)    ; WaitTOF
  50 
    jsr -270(a6)    ; WaitTOF
  51 
    jsr -456(a6)    ; OwnBlitter
  52 
    jsr -228(a6)    ; WaitBlit
  53 
    move.l    $4,a6
  54 
    jsr -132(a6)    ; Forbid
  55 
 
  56 
; end exit gracely preparations!
  57 
 
  58 
    ; clear Bitplanes from garbage
  59 
    ; very slow routine! should be done with the Blitter, or unrolled loop
  60 
    move.w #320/8*200/4,d0     ; d0 is a counter for number of longwords to get cleared
  61 
    move.l #bpl0,a0     ; bpl0 => a0
  62 
    move.l #bpl1,a1     ; bpl1 => a1
  63 
    screen_clear:
  64 
        move.l #0,(a0)+    ; #0 => (a0), and increment a0 to next longword (a0=a0+4)
  65 
        move.l #0,(a1)+    ; #0 => (a1), and increment a1 to next longword (a1=a1+4)
  66 
        subq.w #1,d0
  67 
        bne screen_clear
  68 
 
  69 
    ; copy bitmap to bitplanes
  70 
    move.w #320/8*94/4,d0
  71 
    move.l #bpl0,a0     ; bpl0 => a0
  72 
    move.l #bpl1,a1     ; bpl1 => a1
  73 
    move.l #img_av,a6
  74 
    add.l #2,a6
  75 
    copy_img:
  76 
        move.l 320/8*94(a6),(a1)+    ; bpl1
  77 
        move.l (a6)+,(a0)+            ; bpl0
  78 
        subq.w #1,d0
  79 
        bne copy_img
  80 
 
  81 
; setup display-HW to show 320x200px w/ 2 bitplanes, with 0 horizontal scroll and 0 modulos
  82 
    move.w    #$2200,$dff100                ; 2 bitplane lowres
  83 
    move.w    #$0000,$dff102                ; horizontal scroll 0
  84 
    move.w    #$0000,$dff108                ; odd modulo 0
  85 
    move.w    #$0000,$dff10a                ; even modulo 0
  86 
    move.w    #$2c81,$dff08e                ; DIWSTRT - topleft corner (2c81)
  87 
    move.w    #$f4d1,$dff090                ; DIVSTOP - bottomright corner (f4d1)
  88 
    move.w    #$0038,$dff092                ; DDFSTRT - max overscan $0018 ; standard 0038 & 00d0
  89 
    move.w    #$00d0,$dff094                ; DDFSTOP - max overscan $00d8 ; max os: 368x283px in PAL
  90 
    move.w     #%1000010111000000,DMACON    ; DMA set ON
  91 
    move.w     #%0000000000111111,DMACON    ; DMA set OFF
  92 
    move.w     #%1100000000000000,INTENA    ; IRQ set ON
  93 
    move.w     #%0011111111111111,INTENA    ; IRQ set OFF
  94 
 
  95 
 
  96 
mainloop:
  97 
; increase framecounter by 1
  98 
    move.l frame,d0
  99 
    addq.l #1,d0
 100 
    move.l d0,frame
 101 
 
 102 
 
 103 
; make copperlist
 104 
; doubblebuffering of copperlists, defined at copper1 and copper2,
 105 
; chosen by LSB in framecounter copper (and a6) will hold the address 
 106 
; to the copperlist we will write to (not the one currently in use)
 107 
    and.l #1,d0
 108 
    bne usecopper2
 109 
    move.l #copper1,a6
 110 
    bra usecopper1
 111 
    usecopper2:
 112 
    move.l #copper2,a6
 113 
    usecopper1:
 114 
    move.l a6,copper
 115 
 
 116 
 
 117 
; *********************************************************
 118 
;
 119 
; 32px high copperbars for rolling (p=32*pi ==> p~100 ==> image should be ~ 100px high (here 94px)
 120 
;
 121 
; *********************************************************
 122 
 
 123 
    clr.l d1                     ; d1 = 0
 124 
    move.w cbar_img_line1,d1     ; startingline index ==> d1
 125 
    add.w #40,d1                 ; d1 = d1 + 40 (320px/8 = 40 bytes)
 126 
    cmp.w #94/2*40,d1             ; compare if larger than half the height of the image
 127 
    bcs .10
 128 
    sub.w #94/2*40,d1             ; if it is, then subract the overgoing part from it
 129 
    .10:
 130 
    move.w d1,cbar_img_line1     ; store new startingline index for next frame
 131 
 
 132 
; if all copperbars below would have the same startingline in the image every bar 
 133 
; would look the same, except for the background color.
 134 
; by chaning the startingline we get a "waggling" effect between the copperbarimages
 135 
 
 136 
    ; 1st bar
 137 
    move.l d1,d3                 ; startingline in image ==> d3
 138 
    add.l #40*2,d3                 ; add 2 lines to d3
 139 
    move.l #$2c,d2                 ; starting scanline ==> d2
 140 
    move.l #cbar3,a0             ; colortable ==> a0
 141 
    jsr make_Copper_roller         ; Jump to SubRoutine (RTS will get back here)
 142 
 
 143 
    ; 2nd bar
 144 
    move.l d3,d1
 145 
    add.l #40*4,d3                 ; add 4 lines to d3
 146 
    move.l #$2c+$20,d2
 147 
    move.l #cbar2,a0
 148 
    jsr make_Copper_roller
 149 
 
 150 
    ; 3rd bar
 151 
    move.l d3,d1
 152 
    add.l #40*6,d3                 ; add 6 lines to d3
 153 
    move.l #$2c+$40,d2
 154 
    move.l #cbar3,a0
 155 
    jsr make_Copper_roller
 156 
 
 157 
    ; 4th bar
 158 
    move.l d3,d1
 159 
    add.l #40*8,d3                 ; add 8 lines to d3
 160 
    move.l #$2c+$60,d2
 161 
    move.l #cbar2,a0
 162 
    jsr make_Copper_roller
 163 
 
 164 
    ; 5th bar
 165 
    move.l d3,d1
 166 
    add.l #40*10,d3             ; add 10 lines to d3
 167 
    move.l #$2c+$80,d2
 168 
    move.l #cbar3,a0
 169 
    jsr make_Copper_roller
 170 
 
 171 
 
 172 
    ; end of copperlist (copperlist ALWAYS ends with WAIT $fffffffe)
 173 
    move.l #$fffffffe,(a6)+     ; end copperlist
 174 
 
 175 
testMouseButton:
 176 
    ; if mousebutton/joystick 1  or 2 pressed then exit
 177 
    btst.b #6,$bfe001
 178 
    beq exit
 179 
    btst.b #7,$bfe001
 180 
    beq exit
 181 
 
 182 
; display is ready,
 183 
; or atleast we have done everything we wanted and the copper continues on its own
 184 
; we have to wait for Vertical Blanking before making the next frame
 185 
 
 186 
waitVB:
 187 
    move.l $dff004,d0
 188 
    and.l #$1ff00,d0
 189 
    cmp.l #300<<8,d0
 190 
    bne waitVB
 191 
 
 192 
    ; use next copperlist
 193 
    ; as we are using doubblebuffering on copperlists we now take the new one into use
 194 
    move.l copper,d0
 195 
    move.l d0,$dff080
 196 
    bra mainloop
 197 
 
 198 
exit:
 199 
; exit gracely - reverse everything done in init
 200 
    move.w #$7fff,DMACON         ; set bits[0,14] = 0 (bit15 is a set/clear bit)
 201 
    move.w    olddmareq,DMACON     ; and set bits[0,14] as they were at init
 202 
    move.w #$7fff,INTENA         ; set bits[0,14] = 0 (bit15 is a set/clear bit)
 203 
    move.w    oldintena,INTENA     ; and set bits[0,14] as they were at init
 204 
    move.w #$7fff,INTREQ         ; set bits[0,14] = 0 (bit15 is a set/clear bit)
 205 
    move.w    oldintreq,INTREQ     ; and set bits[0,14] as they were at init
 206 
    move.w #$7fff,ADKCON         ; set bits[0,14] = 0 (bit15 is a set/clear bit)
 207 
    move.w    oldadkcon,ADKCON     ; and set bits[0,14] as they were at init
 208 
 
 209 
    move.l    oldcopper,$dff080     ; load old Copperlist
 210 
    ; graphics.library calls
 211 
    move.l     gfxbase,a6     ; gfxBase ==> a6
 212 
    move.l     oldview,a1     ; oldView ==> a1 (used in LoadView)
 213 
    jsr -222(a6)        ; LoadView : load back the view at start of program
 214 
    jsr -270(a6)        ; WaitTOF : Wait for Top Of Frame to get everything synced up
 215 
    jsr -270(a6)        ; WaitTOF : (2 times for interlaced screens)
 216 
    jsr -228(a6)        ; WaitBlit : wait for Blitter to finish running task (if any)
 217 
    jsr -462(a6)        ; DisownBlitter : release Blitter to system
 218 
    ; exec.library calls
 219 
    move.l    $4,a6         ; execBase ==> a6
 220 
    move.l    gfxbase,a1     ; gfxBase ==> a1 (used in closeLibrary)
 221 
    jsr -414(a6)         ; closeLibrary : close graphics.library
 222 
    jsr -138(a6)        ; Permit multitasking
 223 
 
 224 
    ; end program
 225 
    rts
 226 
 
 227 
; subroutines
 228 
 
 229 
make_Copper_roller:
 230 
    ; bitplane 0
 231 
    move.l #bpl0,d0
 232 
    add.l d1,d0         ; add startingline index to address
 233 
    move.w #$00e2,(a6)+    ; LO-bits of start of bitplane
 234 
    move.w d0,(a6)+        ; go into $dff0e2
 235 
    swap d0
 236 
    move.w #$00e0,(a6)+    ; HI-bits of start of bitplane
 237 
    move.w d0,(a6)+        ; go into $dff0e0
 238 
 
 239 
    ; bitplane 1
 240 
    move.l #bpl1,d0
 241 
    add.l d1,d0         ; add startingline index to address
 242 
    move.w #$00e6,(a6)+    ; LO-bits of start of bitplane
 243 
    move.w d0,(a6)+        ; go into $dff0e6
 244 
    swap d0
 245 
    move.w #$00e4,(a6)+    ; HI-bits of start of bitplane
 246 
    move.w d0,(a6)+        ; go into $dff0e4
 247 
 
 248 
    ;move.l #cbar2,a0
 249 
    move.l #cbar_c1,a1
 250 
    move.l #cbar_c2,a2
 251 
    move.l #cbar_c3,a3
 252 
    move.l #shift1,a4
 253 
    move.l #$0007fffe,d1         ; this is just a template for copper WAIT-instruction
 254 
                                ; to this template we need to add the vertical-position
 255 
                                ; into bits [24,31], 07 is the horizontal-position
 256 
    lsl.l #8,d2                 ; d2 = d2 * 2^8 (shift bits 8 steps left)
 257 
    lsl.l #8,d2                 ; d2 = d2 * 2^8 (8 is max shift, so therefore 3 times)
 258 
    lsl.l #8,d2                 ; d2 = d2 * 2^8 ==> all-in-all d2 = d2 * 2^24 
 259 
                                ; *2^24 could be made faster, but as this isn't timecritical
 260 
                                ; it isn't done here (this part is ran only once/bar/frame)
 261 
    add.l d2,d1                 ; add d2 to d1 = add vertical-position to the template
 262 
                                ; Copper WAIT <$Yy07>,<$fffe>: V:$Yy & H:$07 & mask:$fffe
 263 
    moveq #32,d0
 264 
    loop_cbar1:
 265 
        move.l d1,(a6)+     ; copper WAIT
 266 
        move.w #$0180,(a6)+ ; color0-
 267 
        move.w (a0)+,(a6)+     ;        value
 268 
        move.w #$0182,(a6)+ ; color1-
 269 
        move.w (a1)+,(a6)+     ;        value
 270 
        move.w #$0184,(a6)+ ; color2-
 271 
        move.w (a2)+,(a6)+     ;        value
 272 
        move.w #$0186,(a6)+ ; color3-
 273 
        move.w (a3)+,(a6)+     ;        value
 274 
        move.w #$0102,(a6)+    ; h-shift-
 275 
        move.w (a4)+,(a6)+     ;         value
 276 
        add.l #1<<24,d1     ; here the optimization continues,
 277 
                            ; we know that the verticalvalues are in bits[24,31],
 278 
                            ; so we add 1*2^24 to get to the next line
 279 
        subq #1,d0
 280 
        bne loop_cbar1
 281 
 
 282 
    rts                     ; end subroutine and get back to main code
 283 
 
 284 
 
 285 
 
 286 
; *******************************************************************************
 287 
; *******************************************************************************
 288 
; DATA
 289 
; *******************************************************************************
 290 
; *******************************************************************************
 291 
 
 292 
 
 293 
; storage for 32-bit addresses and data
 294 
    CNOP 0,4
 295 
oldview:    dc.l 0
 296 
oldcopper:    dc.l 0
 297 
gfxbase:    dc.l 0
 298 
frame:        dc.l 0
 299 
copper:        dc.l 0
 300 
 
 301 
; storage for 16-bit data
 302 
    CNOP 0,4
 303 
olddmareq:    dc.w 0
 304 
oldintreq:    dc.w 0
 305 
oldintena:    dc.w 0
 306 
oldadkcon:    dc.w 0
 307 
 
 308 
    CNOP 0,4
 309 
; storage for 8-bit data and text
 310 
gfxname:        dc.b 'graphics.library',0
 311 
 
 312 
    CNOP 0,4
 313 
cbar1:        dc.w $000,$100,$200,$300,$400,$500,$600,$700,$800,$900,$a00,$b00,$c00,$d00,$e00,$f00,$f00,
 314 
                $e00,$d00,$c00,$b00,$a00,$900,$800,$700,$600,$500,$400,$300,$200,$100,$000
 315 
cbar2:        dc.w $000,$101,$202,$303,$404,$505,$606,$707,$808,$909,$a0a,$b0b,$c0c,$d0d,$e0e,$f0f,$f0f,
 316 
                $e0e,$d0d,$c0c,$b0b,$a0a,$909,$808,$707,$606,$505,$404,$303,$202,$101,$000
 317 
cbar3:        dc.w $000,$001,$002,$003,$004,$005,$006,$007,$008,$009,$00a,$00b,$00c,$00d,$00e,$00f,$00f,
 318 
                $00e,$00d,$00c,$00b,$00a,$009,$008,$007,$006,$005,$004,$003,$002,$001,$000
 319 
 
 320 
cbar_c1:    dc.w $000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,
 321 
                $000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000,$000
 322 
cbar_c2:    dc.w $000,$100,$200,$300,$400,$500,$600,$700,$800,$900,$A00,$B00,$C00,$D00,$E00,$F00,$F00,
 323 
                $E00,$D00,$C00,$B00,$A00,$900,$800,$700,$600,$500,$400,$300,$200,$100,$000
 324 
cbar_c3:    dc.w $000,$110,$220,$330,$440,$550,$660,$770,$880,$990,$AA0,$BB0,$CC0,$DD0,$EE0,$FF0,$FF0,
 325 
                $EE0,$DD0,$CC0,$BB0,$AA0,$990,$880,$770,$660,$550,$440,$330,$220,$110,$000
 326 
shift1:        dc.w $00,$55,$77,$99,$AA,$BB,$CC,$DD,$DD,$EE,$EE,$EE,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$EE,
 327 
                $EE,$EE,$DD,$DD,$CC,$BB,$AA,$99,$77,$55,$00
 328 
cbar_img_line1:    dc.w 0
 329 
 
 330 
 
 331 
 
 332 
    Section ChipRAM,Data_c
 333 
 
 334 
; bitplanes aligned to 32-bit
 335 
    CNOP 0,4
 336 
bpl0:    blk.b 320/8*250,0
 337 
bpl1:    blk.b 320/8*250,0
 338 
 
 339 
; datalists aligned to 32-bit
 340 
    CNOP 0,4
 341 
copper1:    
 342 
            dc.l $ffffffe     ; CHIPMEM!
 343 
            blk.l 1023,0     ; CHIPMEM!
 344 
    CNOP 0,4
 345 
copper2:    
 346 
            dc.l $ffffffe     ; CHIPMEM!
 347 
            blk.l 1023,0     ; CHIPMEM!
 348 
 
 349 
    CNOP 0,4
 350 
img_av:    incbin "av.raw"