Here's the copper demo "how it works" explanation.
There are 64 steps of rotation, at 5.625 degrees each. I calculated the slopes up to 45 degrees, and tried to make repeating slope character chunks that fit in the fewest number of character patterns.
Angle 1 is horizontal bars, and uses 1 character for each bar:
Angle 2 uses 10x1 characters per slope: (the most of any angle)
Angle 3 uses 5x1:
Angle 4 uses 3x1 or 4x1: (in an attempt to keep the width of the bars somewhat consistent during rotation)
Angle 5 uses 5x2, with 6 characters:
Angle 6 uses 2x1:
Angle 7 uses 3x2, with 4 chars:
Angle 8 uses 4x3, with 6 chars:
Angle 9 uses 2x1, but with two different offset characters (for consistent bar width)
Angles 10 to 16 are the same as angles 8 down to 2, except flipped diagonally.
Angle 17 is vertical bars:
Angles 18 to 32 are the same as angles 16 down to 2, except flipped vertically.
Angles 33 to 64 are the same as angles 1 to 32, except the color bars are in reverse order. So all the image and pattern data is the same, except the copying a palette to the color table is reversed.
The "COPPER 99/4A" text and wide wipe bars exist on a different screen image table and color table. The black rectangle in the rotated bars images, above, is the same location as the "COPPER 99/4A" text, below.
The demo interleaves two images together, drawing one line from the rotating bars and one line from the "COPPER 99/4A" image. The video screen on the TMS9918A is drawn from the top-left to the bottom-right, one scanline at a time, 60 times per second. On every video scanline, the demo changes the screen image table and color table registers between the two. This requires that the character patterns also be interleaved: all 8 of the rotation character pattern tables have the wide bars and copper text on the 2nd, 4th, 6th, 8th lines. So no matter which rotation (pattern table) is being used, the wide bar/copper text image stays the same.
VDP Memory Layout
0000:02FF Screen image table A (rotation)
0300:037F Sprite list table
0400:07FF Character pattern table for angles 7-9
0800:0AFF Screen image table B (rotation)
0B00:0B1F Color table (rotation)
0B20:0B3F Color table (copper 99/4a)
0C00:0FFF Character pattern table for angles 10-12
1000:12FF Screen image table C (copper 99/4a)
1400:17FF Character pattern table for angles 26-28
1800:187F Sprite pattern table (limited to 4 sprites)
1A00:1FFF Character pattern table for angles 17,22-25
2200:27FF Character pattern table for angles 1-5
2A00:2FFF Character pattern table for angles 13-16
3200:37FF Character pattern table for angles 29-32
3A00:3FFF Character pattern table for angles 18-21
Pattern tables actually overlap the other tables in memory, except the characters in overlapping areas are not actually used. Some rotation angle pattern sets use 24 characters per bar, and other pattern sets use 16 characters per color bar - allowing room for the screen image and other tables in the upper half. The black rectangle on rotation images does use character 0, but the color table is set to black on black so the pattern doesn't matter.
Changing the rotation requires double-buffering the screen image table because 32*24 (768) bytes cannot be written to the VDP memory fast enough during vertical blanking. The two halves are copied over two frames, and then the register flips to the new image to be displayed. So the main loop looks like this:
Copy upper half of rotation image to table A (or B next iteration)
Do interleaving scanlines loop
Copy lower half of rotation image to table A (or B)
Do interleaving scanlines loop
Change character pattern table register to the appropriate set for the current angle
Change the screen image table register to table A (or B)
Furthermore, there still wasn't enough time in the vblank even for a fast-ram unrolled loop copy... maybe 256 bytes per update would have worked, but it also reduce the frame rate from 30Hz to 20Hz, no thanks. Instead the data is converted to asm instructions that Load Immediate each byte into a workspace register mapped to the VDP Write Data register. Like this:
... 384 total LI instructions
Unfortunately this quadruples the size of the data in ROM... 384 bytes becomes 1.5KB, so I can fit only 4 halves (6KB) into one cart bank (8KB.) So all 32 angles would require 16 banks, and that's why the cartridge is 128KB.
(More to come...)