assemblyx86dosx86-16vga

Assembly does reading a plane in mode x needs a different output to the VGA ports from writing?


1I am writing in TASM 3.0 on DosBox 0.74 and I am trying to write in Mode x (Tweaked 13h, unchained mode 13), and I ran into a problem, how you can see in the image, every line is printed, but in every line, every group of four pixels only the color of the first pixel is printed, this is after printing the image in a different space in the VRAM for double buffering, so all four planes are having the data of the first plane.

This is how the image should be printed (this is direct print without double buffer, yes there is a problem with the timer but it does not matter)

enter image description here

This is how the image is being printed with the double buffering

enter image description here

I do believe that the problem is that the data is different from read to write in the VGA ports when it comes to mode x, here is the code of selecting the VGA plane

proc VGAPlaneSelect
    push ax
    push dx
    push cx
    mov al, 02h
    mov dx, 03C4h
    out dx, al
    VGAPlaneSelect_start:
    mov ax, 1
    mov cl, [VGAPlane]
    shl ax, cl
    cmp [VGAPlane], 4
    jne VGAPlaneSelect_end
        mov [VGAPlane], 0
        jmp VGAPlaneSelect_start
    VGAPlaneSelect_end:
    mov dx, 03C5h
    out dx, al
    pop cx
    pop dx
    pop ax
    ret
endp VGAPlaneSelect

And if the outputs are not the problem here is the code of the memory transfer:

    proc DoubleBuffer
    mov ax, 0A000h
    mov es, ax
    mov [VGAPlane], 0
    call VGAPlaneSelect
    cli
    mov cx, 4
    DoubleBuffer_loop:
        xor di, di
        xor si, si
        push cx
        mov cx, 16000
        DoubleBuffer_loop_plane: 
            push di
            push si
            shr di, 2
            shr si, 2
            add si, NON_VISABLE_PLANE_OFFSET
            mov al, [es:si]
            stosb
            pop si
            pop di
            add di, 4
            add si, 4
        loop DoubleBuffer_loop_plane
        inc [VGAPlane]
        call VGAPlaneSelect
        pop cx
    loop DoubleBuffer_loop
    sti
    ret
endp pageFlipping

Solution

  • In the Graphics Controller of the VGA there's a separate register to specify the plane to read from.

    You select it by writing a 4 in address port 03CEh, followed by writing the plane number (0-3) in data port 03CFh.

    The 03C4h:02h register that you use is called ColorPlaneWriteEnable. The answer is already in its name! It allows writing to one or more planes at the same time. For reading use 03CEh:04h ReadPlaneSelect.


    proc DoubleBuffer
    ...
    endp pageFlipping
    

    Which is it?

    PageFlipping does not require the copying of large chunks of memory as this procedure is doing.
    The idea of DoubleBuffering is to use 'normal' RAM to build the picture and copy all or part of it to the video memory. Your program uses video memory to hold the double buffer and as a consequence reading will be very slow!

    Why is the DoubleBuffer_loop so overcomplicated?

    You don't need that shifting and preserving at all!

        xor di, di
        mov si, NON_VISABLE_PLANE_OFFSET
        mov cx, 16000
    DoubleBuffer_loop_plane: 
        mov al, [es:si]
        stosb
        inc si
        dec cx
        jnz DoubleBuffer_loop_plane
    

    This can further be reduced to:

        xor di, di
        mov si, NON_VISABLE_PLANE_OFFSET
        mov cx, 16000
        cld
        rep movs byte [di], [es:si]
    

    The syntax for the rep movs depends on the assembler you use. In NASM you would have to precede the REP MOVSB instruction by the segment override, so ES REP MOVSB