Jump to content
IGNORED

Basic Double Buffering


Recommended Posts

I had been looking for a complete example of how to do double buffering on the Atari 800, but although there are some extracts from very sophisticated programs, the basics were not represented.  I decided to create my own example to build upon.

 

My example creates two graphics mode 0 display lists, and plants a couple of words to show that the screens are being swapped.  I use a deferred mode vertical blank interrupt routine to switch between the two.

 

Enjoy!

 

        *= $3000
Counter		= $A2
ListInUse	= $A3

.include "hardware.s"

init		
        ; load display list 0
        LDA #<dlist_0
        STA sdlstl
        LDA #>dlist_0
		STA sdlstl+1

		; for graphics mode 0, we can set the background color and a foreground luminance 
		; wheich here will be the text color
		LDA #$0				; Mode 0 background color		
		STA COLOR2
		LDA #$0E			; Mode 0 foreground luminance
		STA COLOR1
		
		; set up counter and current disply list in use variable
		LDA #0
		STA Counter
		STA ListInUse
		
		; set up registers to insert our vertical blank interrupt routine
		LDY #<vbi_isr		; Y gets low address byte of ISR
		LDX #>vbi_isr		; X gets high byte of ISR
		LDA #7				; use deferred routine
		JSR SETVBV			; execute call
forever	
        JMP forever 

		  * = $3500
vbi_isr	PHA					; save accumulator
		INC Counter			; increment timer by 1/60 sec
		LDA Counter
		CMP #$30			; has half a second gone by?
		BCS	SwapDL			; Yes -> so go swap the display lists
		JMP	vbidone			; no -> do nothing and return
SwapDL	LDA #0				; before swapping...
		STA Counter			; reset counter
		LDA ListInUse		; get current display list in use
		BEQ ToDL1			; LDX sets Z flag; if 0 -> switch to DL1
		LDA #0				; Currently using DL1 -> switch to DL0
		STA ListInUse
        LDA #<dlist_0		; point OS to DL0
        STA sdlstl
        LDA #>dlist_0
		STA sdlstl+1
		JMP	vbidone	
ToDL1	LDA #1				; Currently using DL0 -> switch to DL1
		STA ListInUse
        LDA #<dlist_1		; Point OS to DL1
        STA sdlstl
        LDA #>dlist_1
		STA sdlstl+1
vbidone	PLA					; before exiting ISR, reset former accumulator state
		JMP	XITVBV							
		
; mode 0 display lists

          * = $7800
dlist_0
        .byte $70,$70,$70								; 24 blank scan lines
        .byte $42,$00,$90								; Line of mode 0, LMS @ $9000
        .byte $2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,			; 11 lines of mode 0
        .byte $2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,		; 12 lines of mode 0    
        .byte $41,<dlist_0,>dlist_0

		* = $7900
dlist_1
        .byte $70,$70,$70								; 24 blank scan lines
        .byte $42,$00,$95								; Line of mode 0, LMS @ $9500
        .byte $2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,			; 11 lines of mode 0
        .byte $2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,		; 12 lines of mode 0    
        .byte $41,<dlist_1,>dlist_1
		
		* = $91F2
		.sbyte "Tick"
		* = $971A
		.sbyte "Tock"
; tell DOS where to run the program when loaded
        * = $2e0
        .word init

 

Bufferx2mode0.s

Edited by CaptMatteus
  • Like 1
Link to comment
Share on other sites

Not so much double buffering, but I'm currently writing a utility that uses 2 different screen modes Graphics 0 and graphics 3.

I'm writing in cc65 and what I do is use keyboard input to switch between screens.

 

First reserve memory for the screens and save each screens details

// make both screens and reserve space
void makescreens(void)
{

    _graphics(0);
    textdl=PEEKW(560);
    textscn=PEEKW(88);
    textmode=PEEK(87);
    textlines=PEEK(703);
    OS.ramtop=OS.ramtop-8;
    
    _graphics(3);
    spritedl=PEEKW(560);
    spritescn=PEEKW(88);
    spritemode=PEEK(87);
    spritelines=PEEK(703);
    OS.ramtop=OS.ramtop-4;
    POKE(752,1);

}

 

then when menu key pressed 

 

    // open text screen
    POKEW(560,textdl);
    POKEW(88,textscn);
    POKE(87,textmode);
    POKE(703,textlines);
    POKE(752,1);

    POKE(HPOSP0,0); // hide players
    POKE(HPOSP1,0);
    POKE(HPOSP2,0);

 

********* CODE GOES HERE

 

    // restore PM screen
    POKEW(560,spritedl);
    POKEW(88,spritescn);
    POKE(87,spritemode);
    POKE(703,spritelines);
    OS.sdmctl=46;        
    POKE(GRACTL,3);
    POKE(752,1);
    POKE(HPOSP0,HP0); // hide players
    POKE(HPOSP1,HP1);
    POKE(HPOSP2,HP2);

 

 

Link to comment
Share on other sites

With deferred mode - the actual swap will be a frame behind since the VBD vector is called after the DList pointer and other shadow registers are copied.

Also, you have the real risk of missing interrupts since VBD is skipped if IRQs are masked - which is a common thing when there's keyboard activity.

Link to comment
Share on other sites

12 hours ago, Rybags said:

With deferred mode - the actual swap will be a frame behind since the VBD vector is called after the DList pointer and other shadow registers are copied.

Also, you have the real risk of missing interrupts since VBD is skipped if IRQs are masked - which is a common thing when there's keyboard activity.

 Thanks for the feedback.  I had not considered the update of the shadow registers.  After reviewing De Re Atari I see that I need to place my ISR in the VBI immediate slot so that I update the DLIST pointer prior to the update of the shadow registers.

Link to comment
Share on other sites

So with that change the code looks like this:

 

        *= $3000
Counter		= $A2
ListInUse	= $A3

.include "hardware.s"

init		
        ; load display list 0
        LDA #<dlist_0
        STA sdlstl
        LDA #>dlist_0
		STA sdlstl+1

		; for graphics mode 0, we can set the background color and a foreground luminance 
		; wheich here will be the text color
		LDA #$0				; Mode 0 background color		
		STA COLOR2
		LDA #$0E			; Mode 0 foreground luminance
		STA COLOR1
		
		; set up counter and current disply list in use variable
		LDA #0
		STA Counter
		STA ListInUse
		
		; set up registers to insert our vertical blank interrupt routine
		LDY #<vbi_isr		; Y gets low address byte of ISR
		LDX #>vbi_isr		; X gets high byte of ISR
		LDA #6				; use immediate routine
		JSR SETVBV			; execute call
forever	
        JMP forever 

		  * = $3500
vbi_isr	INC Counter			; increment timer by 1/60 sec
		LDA Counter
		CMP #$30			; has half a second gone by?
		BCS	SwapDL			; Yes -> so go swap the display lists
		JMP	SYSVBV			; no -> do nothing and return
SwapDL	LDA #0				; before swapping...
		STA Counter			; reset counter
		LDA ListInUse		; get current display list in use
		BEQ ToDL1			; LDX sets Z flag; if 0 -> switch to DL1
		LDA #0				; Currently using DL1 -> switch to DL0
		STA ListInUse
        LDA #<dlist_0		; point OS to DL0
        STA sdlstl
        LDA #>dlist_0
		STA sdlstl+1
		JMP	SYSVBV	
ToDL1	LDA #1				; Currently using DL0 -> switch to DL1
		STA ListInUse
        LDA #<dlist_1		; Point OS to DL1
        STA sdlstl
        LDA #>dlist_1
		STA sdlstl+1
		JMP	SYSVBV							
		
; mode 0 display lists

          * = $7800
dlist_0
        .byte $70,$70,$70								; 24 blank scan lines
        .byte $42,$00,$90								; Line of mode 0, LMS @ $9000
        .byte $2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,			; 11 lines of mode 0
        .byte $2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,		; 12 lines of mode 0    
        .byte $41,<dlist_0,>dlist_0

		* = $7900
dlist_1
        .byte $70,$70,$70								; 24 blank scan lines
        .byte $42,$00,$95								; Line of mode 0, LMS @ $9500
        .byte $2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,			; 11 lines of mode 0
        .byte $2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,$2,		; 12 lines of mode 0    
        .byte $41,<dlist_1,>dlist_1
		
		* = $91F2
		.sbyte "Tick"
		* = $971A
		.sbyte "Tock"
; tell DOS where to run the program when loaded
        * = $2e0
        .word init

 

Link to comment
Share on other sites

  • 2 years later...
On 1/29/2024 at 3:59 AM, Qwe said:

Hi CaptMatteus,

could you share the included file "hardware.s" ?
Thank you

hello,

 

this file just defines some equates for memory locations.  it looks like only five are used in the code...  i think you can find a similar 'include' in the CC65/CA65 sources.

 

sdlstl =$0240

setvbv = $E45C

sysvbv = $E45F

color0 = $02C4

color1 = $02C5

 

https://www.atariarchives.org/mapping/memorymap.php

 

good luck!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...