News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

double buffering

Started by ghjjkl, June 29, 2014, 04:57:28 PM

Previous topic - Next topic

ghjjkl

it's me again. And my new question is: how to implement double buffering to prevent screen from blinking when it is being re-drawn? I've tried this:



bseg segment
backbuff db BUFFER_SIZE dup (?)
bseg ends


.data

BUFFER_SIZE equ 64000


.code

(...)

;we need to clear buffer first

mov ax, bseg
mov es,ax

mov di, offset backbuff
xor al,al
mov cx,BUFFER_SIZE
rep stosb


(...)

;and now we are writting in buffer. Mode 13h.
;some functions...

(...)

;then,we need to write buffer on screen

mov ax,bseg
mov ds,ax

xor di,di
mov si, offset backbuff
mov cx, BUFFER_SIZE
rep movsb


(...)



but it is very slow   :(

dedndave

maybe i'm wrong, but

BUFFER_SIZE equ 64000

bseg segment
backbuff db BUFFER_SIZE dup (?)
bseg ends


might be better

EQUates do not have to be in a segment
they are assembler constants

also, you could use .DATA? to open a bss segment

i just woke up, so i can't read the code yet   :lol:

Tedd

Half your cx value, then use stosw and movsw -- double-speed! :greenclp:
(or stosd/movsd for 4x, if available)
Potato2

ghjjkl

Quote from: dedndave on June 29, 2014, 10:08:30 PM

also, you could use .DATA? to open a bss segment

depends on what's this  :biggrin:
i thought only "segment" to be enough....

QuoteHalf your cx value, then use stosw and movsw -- double-speed!
thank you, now it's done!

i cant just get what's wrong: my program draws new frame much slower than it responds to keyboard buttons pressed. This looks like some lags...

FORTRANS

Quote from: ghjjkl on June 29, 2014, 11:46:16 PM
Quote from: dedndave on June 29, 2014, 10:08:30 PM

also, you could use .DATA? to open a bss segment

depends on what's this
i thought only "segment" to be enough....

Hi,

   .DATA? is the new fangled simplified segment directive.  It is
supposed to be easier to use than the older SEGMENT directive.
They both end up doing something similar.  You are using "bseg
segment" to create a specific segment named bseg.  Dave uses
.DATA? to reserve space in a default segment named _BSS.   You
end up using what you like and understand.

   SEGMENT requires you to use an ENDS to limit the segment size
and scope.  .DATA? lets you skip that onerous step (if you follow
its rules of course).

Cheers,

Steve N.

dedndave

well - he's already using .DATA and .CODE   :biggrin:

here's an "empty" program template for a Small model EXE with 4K stack...

        .MODEL  Small
        .STACK  4096
        .DOSSEG
        .386
        OPTION  CaseMap:None

;####################################################################################

        .DATA

;************************************************************************************

        .DATA?

;####################################################################################

        .CODE

;************************************************************************************

_main   PROC    FAR

        mov     dx,@data
        mov     ds,dx



        mov     ax,4C00h
        int     21h

_main   ENDP

;####################################################################################

        END     _main

MichaelW

I suspect that your speed problems are due to your OS/execution-environment.

The "blinking" is commonly called "flickering" or "tearing". To eliminate it, the screen update, whether done by drawing directly to the display buffer or by copying the contents of a back buffer to the display buffer, must be synchronized with the screen refresh and must be completed within 1-2 refresh periods, depending on various details. At 60Hz, for example, the refresh period would be ~16.6ms, a fairly long time for even a slow system.

For a DOS app, and assuming VGA color, bit 3 of the Input Status #1 Register at I/O port 3DAh indicates when the display is in vertical retrace. For the common case where the screen update is faster than the screen refresh, to synchronize with the screen refresh you should synchronize with the start of vertical retrace. To do this reliably you read the port value in a double loop, waiting for bit 3 to be clear, then waiting for it to be set.

A demo:

;-----------------------------------------------------------------
; Spacebar to toggle sync, Escape to exit, any other key to loop.
;-----------------------------------------------------------------
.model small
.386
.stack
.data
    f_sync  dw  0
.fardata black
    buff0   db  64000 dup(0)
.fardata white
    buff1   db  64000 dup(7)
.code
sync  proc
    mov   dx, 3dah
  @@:
    in    al, dx
    test  al, 1000b
    jnz   @B
  @@:
    in    al, dx
    test  al, 1000b
    jz    @B   
    ret
sync  endp
.startup
    mov   eax, 13h
    int   10h
    push  0a000h
    pop   es
  L0:
    test  f_sync, 1
    jz    @F
    call  sync
  @@:
    mov   ecx, 64000/4
    xor   esi, esi
    xor   edi, edi
    push  ds
    push  seg buff0
    pop   ds
    rep   movsd
    pop   ds
    xor   ah, ah
    int   16h
    cmp   ah, 1
    je    L1
    cmp   ah, 39h
    jne   @F
    xor   f_sync, 1
  @@:
    test  f_sync, 1
    jz    @F
    call  sync
  @@:
    mov   ecx, 64000/4
    xor   esi, esi
    xor   edi, edi
    push  ds
    push  seg buff1
    pop   ds
    rep   movsd
    pop   ds
    xor   ah, ah
    int   16h
    cmp   ah, 1
    je    L1
    cmp   ah, 39h
    jne   @F
    xor   f_sync, 1
  @@:
    jmp   L0
  L1:
.exit
end

Well Microsoft, here's another nice mess you've gotten us into.

ghjjkl

MichaelW,
Thank you! I finally got what was an error. I've set sync period too long. So my program worked not fast enough inspite any cpu clock i set in dosbox   :biggrin:

dedndave

back in the days of DOS, we used to write a little speed test to run at initialization
we'd try to estimate how much text could be sent to the display in each refresh period   :P

the easy way was to make it work on a 4.77 MHz XT - lol

MichaelW

Dave,

Assuming you had an EGA/VGA adapter, for the text modes the easy way around the problem was to take advantage of the multiple display pages available. You would use an invisible page as a back buffer, and when you finished writing to the page you would make it the visible page. For every adapter that I tested the page switch was fast and, at least effectively, synced with vertical retrace.
Well Microsoft, here's another nice mess you've gotten us into.

dedndave

it wasn't so bad on EGA/VGA cards
i think a lot of them had synchronous updating or whatever

i seem to recall that TTL and Hercules adapters/monitors were also pretty good in this respect
it was the CGA cards that were the problem
and, probably really bad if you used a composite monitor

windows probably doesn't use the bios code on the video adapter for INT 10h
hell - i don't even know if modern cards have their own video bios - lol

BlueMR2

Quote from: dedndave on July 02, 2014, 05:54:48 AM
it wasn't so bad on EGA/VGA cards
i think a lot of them had synchronous updating or whatever

i seem to recall that TTL and Hercules adapters/monitors were also pretty good in this respect
it was the CGA cards that were the problem
and, probably really bad if you used a composite monitor

Yeah, the single port (V?)RAM on the CGA cards was horrible.  Can't write during a display read or it would generate snow.  There was just enough time (on the 4.77mhz 8088s) during the horizontal retrace to poke one character onto the display (if you did it right, I seem to recall that ROM used a mov, but if you did that from RAM it would still snow due to latency.  I believe the RAM version required an xchg?).  I can't recall how many characters one could write during the vertical retrace.  A couple hundred or so?  MDA and EGA on up didn't have that issue, you could write to video RAM at the same time the display controller was reading.  Also, EGA added really nice page flipping support.  The flip interrupt was rarely used, normally was just done programmatically, but I always thought that was a cool feature.

Seems hard to believe that so little work could be done during the retrace, but even serial comm was tough back then.  At max speed of the UART, you had a whopping 410 clock cycles to deal with each incoming byte!  On an 8088 with a typical 4 cycle per instruction in the core and the small prefetch queue, AND the 8 bit bottleneck of a memory path...  Most apps topped out at 56,700 bps on a 4.77mhz box (unless it was an 8086 or the equivalent NEC V-series with the 16 bit data bus and longer prefetch queue) and required 10mhz to max out the UART!  Dropped bytes were not uncommon if you had any kind of TSRs loaded...  :-)
My Code Site
https://github.com/BrianKnoblauch

ghjjkl

but why does keyboard work so slow? If i move some image using my keyboard it moves much slower than if i move picture indirectly, by changing its position in draw cycle.

MichaelW

Quote from: ghjjkl on July 03, 2014, 03:31:20 AM
but why does keyboard work so slow? If i move some image using my keyboard it moves much slower than if i move picture indirectly, by changing its position in draw cycle.

The keyboard has what is known as a "typematic" rate and delay that can be controlled in the BIOS setup, or with Interrupt 16h Function 3. Basically, when you hold a key down for longer than the typematic delay, the keyboard controller starts automatically repeating the key at the typematic rate. The repetition is known as key "rollover". If I recall correctly the default delay is 0.5 seconds, the default rate something like 10 repeats per second, and the maximum rate 30 repeats per second. I posted some code here that avoids the delay, and allows you control the effective repetition rate by inserting a millisecond-resolution delay in your run loop.
Well Microsoft, here's another nice mess you've gotten us into.

ghjjkl

i had no idea about these "features"...
i'm afraid my bad english interferes me to say more than "thank you a lot again"  :biggrin: