Double buffering screen bitmap
Moderator: Moderators
- thegg
- Vic 20 Dabbler
- Posts: 70
- Joined: Mon Aug 30, 2021 4:49 am
- Location: England
- Occupation: retired
Re: Double buffering screen bitmap
Not sure about the 'cleverly' bit, but if you don't mind an extra few cycles in the point plotting code you might like to consider:
Allocate a ZP address as a variable.
Load the ZP with $00 when starting to write to bit-map 1.
Load the ZP with $08 when starting to write to bit-map 2.
In the point plotting code load the highb,x value into the accumulator and OR it with the ZP: effectively adding 8 to the high bit value.
Allocate a ZP address as a variable.
Load the ZP with $00 when starting to write to bit-map 1.
Load the ZP with $08 when starting to write to bit-map 2.
In the point plotting code load the highb,x value into the accumulator and OR it with the ZP: effectively adding 8 to the high bit value.
- Mike
- Herr VC
- Posts: 4941
- Joined: Wed Dec 01, 2004 1:57 pm
- Location: Munich, Germany
- Occupation: electrical engineer
Re: Double buffering screen bitmap
Your suggestion just adds another 3 extra cycles to the loop core of a line drawing routine that runs at 51 cycles/pixel.thegg wrote:[...] if you don't mind an extra few cycles in the point plotting code [...]
-
- Vic 20 Newbie
- Posts: 14
- Joined: Fri Dec 29, 2023 11:47 pm
- Website: https://fleischgemuese.itch.io
- Location: Germany
Re: Double buffering screen bitmap
Hi,
I'm not really using Assembler but I can show you how I did double buffering using TurboRascal (TRSE). Maybe it helps:
(A) Memory setup
I used $1000 and $1e00 as screen addr1/2 to switch between. The colour address is $9400 and $9600
The charset location is at $1800, and 192 chars are sufficient for me
(B) Variables:
currentBank 0=screen1, 1=screen2
time: Timer for counting the frames
framestatus: indicate if the update of tiles are finished
raster: rasterline (different for PAL/NTSC)
(C) VBL Raster interrupt
Explanation: The interrupt is separated into 4 parts depending on frameStatus and time
frameStatus := 1 -> start cycle set by GameLoop
frameStatus := 0 -> cycle is finished
time in cycle
time = 0: Switch bank depending on current bank
time = 1: Copy all data from bank to another
time = 2: cycle is finished and frameStatus = 0
(D) Game loop example
Only Update the game objects that are hidden after the interrupt cycle is done. If finished set framestatus =1 to start new cycle
(E) Update Example
While doing an update use the correct screenmemory
Note scr1 is a pointer to the screen location, same need to be done for the color ram
(F) Switch Bank function
(G) Start VBL interrupt
Hope this helps...
I'm not really using Assembler but I can show you how I did double buffering using TurboRascal (TRSE). Maybe it helps:
(A) Memory setup
I used $1000 and $1e00 as screen addr1/2 to switch between. The colour address is $9400 and $9600
The charset location is at $1800, and 192 chars are sufficient for me
(B) Variables:
currentBank 0=screen1, 1=screen2
time: Timer for counting the frames
framestatus: indicate if the update of tiles are finished
raster: rasterline (different for PAL/NTSC)
(C) VBL Raster interrupt
Explanation: The interrupt is separated into 4 parts depending on frameStatus and time
frameStatus := 1 -> start cycle set by GameLoop
frameStatus := 0 -> cycle is finished
time in cycle
time = 0: Switch bank depending on current bank
time = 1: Copy all data from bank to another
time = 2: cycle is finished and frameStatus = 0
Code: Select all
interrupt vbl();
begin
StartIRQ(0);
UpdateSound();
if(frameStatus = 1) then begin
if(time = 0) then begin
SwitchBank();
end
else if(time = 1 and currentBank = 0) then begin
copyHalfScreen(^@SCREEN_ADDR1, ^@SCREEN_ADDR2, 10,0,0);
copyHalfScreen(^@COLOR_ADDR1, ^@COLOR_ADDR2, 10,0,0);
end
else if(time = 1 and currentBank = 1) then begin
copyHalfScreen(^@SCREEN_ADDR2, ^@SCREEN_ADDR1, 10,0,0);
copyHalfScreen(^@COLOR_ADDR2, ^@COLOR_ADDR1, 10,0,0);
end;
end;
if(time = 2) then begin
frameStatus := 0;
end;
if(time < 4) then inc(time)
else if(frameStatus = 1) then begin
time:=0;
end;
closeIRQ();
end;
Only Update the game objects that are hidden after the interrupt cycle is done. If finished set framestatus =1 to start new cycle
Code: Select all
while(state = @STATE_PLAY) do begin
ReadJoy1();
waitnoraster(0);
if(frameStatus = 0) then begin
Update();
Animate();
CycleWater();
frameStatus := 1;
end;
end;
While doing an update use the correct screenmemory
Code: Select all
if(currentBank = 1) then screenmemory := scr1[y+1] else screenmemory := scr2[y+1];
screenmemory[x]:=c;
(F) Switch Bank function
Code: Select all
procedure SwitchBank();
begin
if (currentBank = 0) then
begin
VICCR5 := $fe;
VICCR2 := VICCR2 | $80;
currentBank := 1;
end
else
begin
VICCR5 := $ce;
VICCR2 := VICCR2 & $7F;
currentBank := 0;
end;
end;
Code: Select all
// PAL=121, NTSC=107
if (HSCROLL_REGISTER = 12) then raster := 121 else raster := 107;
VIARasterIRQ(vbl(), raster, 0);
-
- Vic 20 Drifter
- Posts: 29
- Joined: Sat Jan 26, 2013 3:30 pm
Re: Double buffering screen bitmap
after 2018 I am back into VIC coding...
i am struggling in my 3d routines and same double buffering a 16x16 char matrix.
so... 16x16 = 256 chars = $0800 similar to C64 --> $1000 charset 1 and $1800 charset 2
leaves no room for screen ram... (I am on an expanded VIC) --> $0200
now my setup of VIC regs:
while my main loop will look like this
now does that looks ok?
but Vice shows half of the matrix in rom charset...
i am struggling in my 3d routines and same double buffering a 16x16 char matrix.
so... 16x16 = 256 chars = $0800 similar to C64 --> $1000 charset 1 and $1800 charset 2
leaves no room for screen ram... (I am on an expanded VIC) --> $0200
now my setup of VIC regs:
Code: Select all
lda #16+$80 ;collums+$0200 shift
sta $9002
lda #16+1 ;lines + 8x16 chars
sta $9003
lda #%10001100 ;$0200 screenram $1000 font
sta $9005
Code: Select all
loop
lda #154
jsr vsync
lda $9005
eor #%00000010
sta $9005 ;here $1800
jsr clr_speedcode ;$1000
jsr render_scene
lda #154
jsr vsync
lda $9005
eor #%00000010
sta $9005 ;here $1000
jsr clr_speedcode2 ;clear $1800
jsr render_scene
continue2
jmp loop
now does that looks ok?
but Vice shows half of the matrix in rom charset...
-
- Vic 20 Drifter
- Posts: 29
- Joined: Sat Jan 26, 2013 3:30 pm
Re: Double buffering screen bitmap
ok... the rom chars are there because I setup the matrix like on C64 with *16 collums... but i need *8... (so just chars 00-7f and 80-ff showed rom chars)
- MrSterlingBS
- Vic 20 Devotee
- Posts: 237
- Joined: Tue Jan 31, 2023 2:56 am
Re: Double buffering screen bitmap
Thanks a lot for the answers.
I have one "trick" to save six bytes and eight cycles!
I have one "trick" to save six bytes and eight cycles!
Code: Select all
lda value1 ; for $1800
; eor #%00000010
sta $9005 ;here $1800
jsr clr_speedcode ;$1000
jsr render_scene
lda #154
jsr vsync
lda value2 ; for $1000
; eor #%00000010
sta $9005 ;here $1000
Re: Double buffering screen bitmap
Clearing an entire bitmap is slow... In "Back in the Good Old Days" demo from 2004 I allocated chars dynamically (like a ring buffer) when drawing and eor-filling the frame. Char 0 was used for empty screen, so I only needed to clear ~200-256 bytes to wipe a screen buffer.
Dynamic char allocation also allows displaying much bigger objects than what's possible with a fixed bitmap. When eor-filling, I had 3 more "preset" chars for quickly filling 4x16 pix areas of solid colors (bit patterns $55, $aa, and $ff).
Demo part with filled vectors & glenz, unexpanded VIC (attempts to load the next part "cae" after exit): https://www.youtube.com/watch?v=StlAe7kzsZg
(Only 2 axis of rotation, sorry, I didn't have a proper 3D matrix rotation formula at hand, but just adapted something from high school trigonometry )
Dynamic char allocation also allows displaying much bigger objects than what's possible with a fixed bitmap. When eor-filling, I had 3 more "preset" chars for quickly filling 4x16 pix areas of solid colors (bit patterns $55, $aa, and $ff).
Demo part with filled vectors & glenz, unexpanded VIC (attempts to load the next part "cae" after exit): https://www.youtube.com/watch?v=StlAe7kzsZg
(Only 2 axis of rotation, sorry, I didn't have a proper 3D matrix rotation formula at hand, but just adapted something from high school trigonometry )
- Mike
- Herr VC
- Posts: 4941
- Joined: Wed Dec 01, 2004 1:57 pm
- Location: Munich, Germany
- Occupation: electrical engineer
Re: Double buffering screen bitmap
That method may well be feasible if there is only one object on screen with large uniform coloured areas, but in any case we are not anymore talking about a bitmap then. In a bitmap, single pixel access can be extremely streamlined (see the other thread about line drawing here), with dynamically allocated chars, the overhead per pixel will be much higher.aeb wrote:In "Back in the Good Old Days" demo from 2004 I allocated chars dynamically (like a ring buffer) when drawing and eor-filling the frame [...] Char 0 was used for empty screen [...] I had 3 more "preset" chars for quickly filling 4x16 pix areas of solid colors (bit patterns $55, $aa, and $ff)
BTW, the original HYPER-GRAPHICS did such a scheme for a overscan 208x256 graphics screen, providing a 'sparse' bitmap already in 1986 (published in Markt & Technik 64'er Sonderheft 3), the characters 0..25 not usable because they overlapped the text screen, character 26 was the empty tile and characters 27..255 were available to hold non-empty tile definitions. Only roughly 50% of the graphics screen could be filled, which restricted the use of the graphics package to line graphics, function plots or the like.
Exactly this program then was the motivation for tokra and me to create MAXIGRAFIK, which then did the same resolution of 208x256 as true display bitmap, in 2010.