English Amiga Board

English Amiga Board (https://eab.abime.net/index.php)
-   Coders. Asm / Hardware (https://eab.abime.net/forumdisplay.php?f=112)
-   -   Combining copper scrolling with copper background (https://eab.abime.net/showthread.php?t=69491)

phx 03 June 2013 16:36

Combining copper scrolling with copper background
 
In my current game project I'm scrolling a huge map with the algorithm described here: http://aminet.net/package/dev/src/ScrollingTrick

It is a combination of copper and blitter scrolling and needs to reset the bitplane pointers at a variable line on the screen.

Now I have the problem that I would also like to have a copper background, which gives each line a different colour. It seems that the only option is to create the copper list dynamically in every new frame. But writing the dynamic part of that copper list requires nearly 50 scan lines (WAIT and COLOR for 240 lines and writing the BPLxPT at the split line)! Is there any way to improve that? Or a completely different approach to the problem?

hooverphonique 03 June 2013 16:53

if the colors set by the copper also need to shift/scroll along with the bitmap, you need to move them around as well, which will be almost like generating a new copper list each frame.

with regards to the bitplane pointers, writing the modulos on each scanline and then updating those instead might save some (presuming your bitplanes are below 32k each).

phx 03 June 2013 17:12

Quote:

Originally Posted by hooverphonique (Post 892387)
if the colors set by the copper also need to shift/scroll along with the bitmap,

Yes. Although with a different speed (I think about vertical scrolling speed /2 or /4).

Quote:

you need to move them around as well, which will be almost like generating a new copper list each frame.
Too bad. That's very expensive. Does Turrican do it the same way? ;)

Quote:

with regards to the bitplane pointers, writing the modulos on each scanline and then updating those instead might save some (presuming your bitplanes are below 32k each).
The bitmaps are quite large (IIRC 352x288x5) and double buffered. Doesn't fit.

phx 03 June 2013 17:19

Currently I'm writing the copper list like below. a2 points to the copper list. d0 is the line where I have to insert the split. Needs about 48 scan lines. :mad

Code:

        lea    Cl_colors(a2),a2
        lea    TestColors(pc),a0
        movem.l .Clistreginit(pc),d1-d4
        move.w  #0,d5                  ; @@@@ background color index
        moveq  #15,d6                  ; color index mask

.4:    move.w  (a0,d5.w),d3
        move.l  d2,(a2)+                ; WAIT
        addq.w  #2,d5
        and.w  d6,d5

        subq.w  #1,d0
        beq    .7                      ; reached the split line

.5:    move.l  d3,(a2)+                ; COLOR01 
        add.l  d4,d2                  ; next line
        bcs    .6 
        dbf    d1,.4

        moveq  #-2,d0
        move.l  d0,(a2)                ; end of copper list
        movem.w Xpos(a4),d2-d4
        rts

        ; insert extra WAIT for raster line $100
.6:    COPWAIT $ff,$de,(a2)+
        dbf    d1,.4
        illegal                        ; never reached

        ; write updated bitplane pointers for the split section
.7:    swap    d1                      ; BPL1PTH to LSW
        moveq  #PLANES-1,d0
.8:    move.w  d1,(a2)+
        swap    d7
        move.w  d7,(a2)+
        addq.w  #2,d1 
        move.w  d1,(a2)+
        swap    d7
        move.w  d7,(a2)+
        addq.w  #2,d1 
        add.l  a1,d7 
        dbf    d0,.8 
        swap    d1                      ; get loop counter back into LSW
        bra    .5

.Clistreginit:
        dc.l    BPL1PTH<<16|DISPH-1    ; d1
        dc.l    VSTART<<24|1<<16|$fffe  ; d2
        dc.l    COLOR01<<16            ; d3
        dc.l    $01000000              ; d4


Asman 05 June 2013 22:41

Some idea:
Generate two above copperlists and keep them in memory and write only things which will change, like all colors, screen pointers (for double buffering omit this)

I have question:
How many lines will have own color ? 256 ? (sorry I'm too tired to count from your source)

mc6809e 06 June 2013 05:10

Quote:

Originally Posted by phx (Post 892384)

Now I have the problem that I would also like to have a copper background, which gives each line a different colour. It seems that the only option is to create the copper list dynamically in every new frame. But writing the dynamic part of that copper list requires nearly 50 scan lines (WAIT and COLOR for 240 lines and writing the BPLxPT at the split line)! Is there any way to improve that? Or a completely different approach to the problem?

Maybe you can treat your copper list as a circular queue.

You're already doing something similar with bitplane memory. You don't build a new screen each frame. Instead you change the edges and shift the rest by changing pointers. Same thing will work with a copper list, except in one dimension instead of two.

You'll need to have the copper enter the list at a variable point, with each scanline having at least a move and a wait for the end of the line. The last instruction of the list should include the BPLxPT moves and a strobe to the top of the list. By waiting for the end of the line between instructions, you can avoid special waits like the wait for line $100.

Exiting the loop on the last displayable line might be tricky. You probably don't want the background to change after that.

phx 06 June 2013 11:32

Quote:

Originally Posted by Asman (Post 892527)
How many lines will have own color ? 256 ? (sorry I'm too tired to count from your source)

My first approach was stupid. I had 240 lines in the display window and tried to change the color in each line.

This is not necessary, when I think about it. It already looks good enough when changing the color every 10 lines or so.

Now I have a more flexible copper background definition, in this form:
Code:

        dc.w    $d5e,10
        dc.w    $d6e,1
        dc.w    $d5e,1
        dc.w    $d6e,10
        ...

Each pair of words defines the color and the number of lines it is valid for. A level has a maximum height of 4096 pixels (256 16 pixel tiles), so I want to define a copper background for 2048 lines this way. I will divide the current y-position by 2 to find the first color to display.

There will be three variables to optimize this process:
CbackPos: Current offset on the copper background, 0-2047 (not on the list).
CbackPtr: Pointer to an entry of the above list, which defines the color of the topmost visible line.
CbackOffs: Offset into the topmost color bar, i.e. number of lines which already left the display on top.

But maybe this is still too complicated? I will think about the implementation now...

phx 06 June 2013 11:38

Quote:

Originally Posted by mc6809e (Post 892579)
You'll need to have the copper enter the list at a variable point, with each scanline having at least a move and a wait for the end of the line. The last instruction of the list should include the BPLxPT moves and a strobe to the top of the list.

Hmm... that's a nice idea. I could write COPLC and then use COPJMP to jump into the list at variable points?
But as far as I understand, I still have to rewrite the VPOS-part of all the WAIT-instructions when scrolling vertically? The copper background should scroll vertically at half of the real scrolling speed.

Quote:

By waiting for the end of the line between instructions, you can avoid special waits like the wait for line $100.
Yes, that would work! :)

But now I decided that I no longer want to change the color in each line (see above).

Toni Wilen 07 June 2013 08:46

(Modification of mc6809e's suggestion)

I'd create big copper list that has as many wait/move pairs as you have vertical pixels (2048?). WAIT only waits for end of horizontal line, MOVE changes background color.

Normal copper list waits for first visible line, jumps to big copper list, matching Y-position.

When you need to move split point, replace color register write with write to COPJMPx (and restore old COPJMPx with COLOR0 write) which handles the split and color change (if also needed) and finally jumps back to big copper list.

Now you only need to change COPJMP pointer and 2 words (or more if you need to also exit the big list before end of display) in copper list when you need to change vertical position. Of course copper list will waste some time doing mostly useless waits and moves but it is still faster and more optimal than rewriting whole copperlist with CPU.

phx 07 June 2013 11:45

Quote:

Originally Posted by Toni Wilen (Post 892816)
I'd create big copper list that has as many wait/move pairs as you have vertical pixels (2048?). WAIT only waits for end of horizontal line, MOVE changes background color.

Ahhh! Now I understand! Maybe mc6809e also said that, but I didn't get it. :)

The WAIT instructions will only wait for end of line, but ignore the VPOS. Something like $00df,$00fe? After writing to COLOR the next line has started and I can wait for end of line again. Very good idea!


Quote:

When you need to move split point, replace color register write with write to COPJMPx (and restore old COPJMPx with COLOR0 write) which handles the split and color change (if also needed) and finally jumps back to big copper list.
Ok. The only question here is whether the number of available memory cycles until DDFSTART are sufficient to load the pointers for all five bitplanes, write the COPJMP and COLOR.

Toni Wilen 07 June 2013 11:55

Quote:

Originally Posted by phx (Post 892848)
Ahhh! Now I understand! Maybe mc6809e also said that, but I didn't get it. :)

The WAIT instructions will only wait for end of line, but ignore the VPOS. Something like $00df,$00fe? After writing to COLOR the next line has started and I can wait for end of line again. Very good idea!

Yeah.

Quote:

Ok. The only question here is whether the number of available memory cycles until DDFSTART are sufficient to load the pointers for all five bitplanes, write the COPJMP and COLOR.
It should fit if you only need to modify low 16-bit of bitplane pointers. If not, split copper list can be started 1 line earlier, wait for nearly end of next line and then do the split.

mc6809e 07 June 2013 22:37

Quote:

Originally Posted by phx (Post 892848)
Ok. The only question here is whether the number of available memory cycles until DDFSTART are sufficient to load the pointers for all five bitplanes, write the COPJMP and COLOR.

If you're very desperate, you can use the CPU to change the bitplane pointers. The Amiga CPU interrupt mechanism is very clever and allows the CPU to be well-synced to the display if necessary. This means you can pre-load registers with data, STOP the CPU, trigger an interrupt with the copper, and then blast the hardware registers using a MOVEM.L instruction. There is quite a delay between the interrupt and moment the CPU begins to write to the registers, but it is predictable for a 68000 with the interrupt handler and supervisor stack in chipram. Other processors introduce complications, though.

phx 08 June 2013 21:09

Quote:

Originally Posted by mc6809e (Post 892931)
There is quite a delay between the interrupt and moment the CPU begins to write to the registers, but it is predictable for a 68000 with the interrupt handler and supervisor stack in chipram. Other processors introduce complications, though.

Not an option then. Although it will be an A500 OCS game I want to make sure it works on any existing Amiga. :)

Photon 15 June 2013 18:24

An interrupt that starts with a busy-wait for the intended HPOS can be predictably triggered for a minimum busy-wait on 68000. Should you have a fast CPU you waste part of a scanline busy-waiting, but on the other hand if the game runs on A500 the CPU can waste that time and still be able to run the game. A good method to know when you are A500-centric (write for the A500 platform specifically) but don't want to rule out faster Amigas.

mc6809e 16 June 2013 07:26

Quote:

Originally Posted by Photon (Post 894313)
An interrupt that starts with a busy-wait for the intended HPOS can be predictably triggered for a minimum busy-wait on 68000. Should you have a fast CPU you waste part of a scanline busy-waiting, but on the other hand if the game runs on A500 the CPU can waste that time and still be able to run the game. A good method to know when you are A500-centric (write for the A500 platform specifically) but don't want to rule out faster Amigas.

Great idea. And the initial MOVEM.L M->R can be done inside the handler, too, followed by the wait.

Thanks!

earok 13 February 2021 11:10

This thread was really useful!

For my copper splitscreen solution, I originally was using 17 copper operations per line, which were:
- The line wait
- 12x spare ops for setting all 6x bitplane pointers (these are normally NOP but will be set to bitplane pointers on the one line that they're used on)
- 4x ops for setting Color0 with a 24Bit AGA color


Using COPJMP, I was able to reduce it to 5 - just the line wait (which can be substituted with a COPJMP to a secondary copperlist that takes care of resetting the bitplane pointers) and the color operations.

DanScott 13 February 2021 12:41

This is what I did for Chuck Rock 2 (and Wonderdog)


I had a table with an entry for each scanline, with the total number of copper instructions cumulative up to that line.

Then if the bitplanes need resetting at line X, I read value Y from table at position X, blit that part of the copperlist, then insert bitplane stuff, then blit the rest of the copperlist.

It took hardly any time in total.

This also allowed for any (variable) number of other copper instructions per line..


All times are GMT +2. The time now is 18:29.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.

Page generated in 0.04661 seconds with 11 queries