Mode X in Assembly x86-16, Why is plane 1 not printing and all the other planes are not in the correct order?
Asked Answered
P

1

6

I am writing in TASM 3.0 on DosBox 0.74 and I am trying to write in Mode x (Tweaked 13h, unchained mode 13), but here you can see in the image, it's not quite right. It seems that plane 1 (second plane) is not printing at all, and all the others are not in the right order. I know that the code here is inefficient, but I want to make it work and then clean it up.

Output

proc showBMP
    push cx
    mov ax, 0A000h
    mov es, ax
    mov cx, [BMPHeight]
    mov ax, [BMPWidth]
    xor dx, dx
    mov si, 4
    div si
    mov bp, dx
    mov dx, [BMPX]
    showBMP_nextLine:
        call VGAPlaneStartBMP
        push cx
        push dx
        mov di, cx
        add di, [BMPY]
        mov cx, di
        shl cx, 6
        shl di, 8
        add di, cx
        add di, dx
        mov ah, 3fh
        mov cx, [BMPWidth]
        add cx, bp
        mov dx, offset BMPMaxLine
        int 21h
        cld
        mov cx, [BMPWidth]
        mov si, offset BMPMaxLine
        showBMP_nextLine_movsbLoop:
            push cx
            push di
            shr di, 2
            mov cl, [ds:si]
            mov [es:di], cl
            inc [VGAPlane]
            inc si
            pop di
            inc di
            pop cx
            call VGAPlaneSelect
        loop showBMP_nextLine_movsbLoop
        pop dx
        pop cx
    loop showBMP_nextLine
    pop cx
    ret
endp showBMP

Here you can see a procedure for printing a bitmap file, which worked perfectly on chain-4 mode 13.

  • BMPHeight - as name suggest is the height of the picture
  • BMPWidth - same
  • BMPX - where the picture starts on the screen (x coordinate)
  • BMPY - same but Y coordinate
  • BMPMaxLine - array of 320 works as a buffer
  • VGAPlane - 0/1/2/3 one of the planes
  proc VGAPlaneStartBMP
       push ax
       push bx
       mov ax, [BMPX]
       mov bx, offset PlaneByX
       add bx, ax
       mov al, [bx]
       mov [VGAPlane], al
       pop bx
       pop ax
       call VGAPlaneSelect
       ret
   endp VGAPlaneStartBMP

This procedure, for each line of printing, chooses the plane by the starting x of a line:

  • PlaneByX - MAX_WIDTH / NUMBER_OF_PLANES dup (PLANES), RESET

  • MAX_WIDTH is 320, NUMBER_OF_PLANES is 4, PLANES is 0, 1, 2, 3,

proc VGAPlaneSelect
        push ax
        push dx
        mov al, 02h
        mov dx, 03C4h
        out dx, al
        VGAPlaneSelect_start:
        cmp [VGAPlane], 0
        jne VGAPlaneSelect_0
            mov al, 0h
            jmp VGAPlaneSelect_end
        VGAPlaneSelect_0:
        cmp [VGAPlane], 1
        jne VGAPlaneSelect_1
            mov al, 1h
            jmp VGAPlaneSelect_end
        VGAPlaneSelect_1:
        cmp [VGAPlane], 2
        jne VGAPlaneSelect_2
            mov al, 4h
            jmp VGAPlaneSelect_end
        VGAPlaneSelect_2:
        cmp [VGAPlane], 3
        jne VGAPlaneSelect_3
            mov al, 8h
            jmp VGAPlaneSelect_end
        VGAPlaneSelect_3:
            mov [VGAPlane], 0
            jmp VGAPlaneSelect_start
        VGAPlaneSelect_end:
        inc dx
        out dx, al
        pop dx
        pop ax
        ret
    endp VGAPlaneSelect

And lastly this code is when selecting a plane.

Prankster answered 22/1, 2020 at 12:16 Comment(14)
Interesting problem! Remember, Stack Overflow is a question and answer site. You have lots of details, and that's great. But can you edit your post to actually include a question? The more specific the better. Also, 16-bit direct VGA programming is very niche these days, so links to references (e.g. on Mode X) might be helpful.Bromic
Are you running this code on bare metal? A VM? What version of hypervisor and VGA firmware? It could be a bug there too.Bromic
@JonathonReinhart Thank Jonathon, I have edited the post so it would be more clear, I am running the code on DosBox, I don't know how can I check the version of hypervisor and VGA firmware but I guess that in DosBox it's emulating a stable one, I guess.Prankster
You have 0, 1, 4, 8 instead of 1, 2, 4, 8 for the plane bits. Also, why don't you replace the whole VGAPlaneSelect logic with mov al, 1; mov cl, [VgAPlane]; shl al, clPharmacognosy
So no hypervisor then, just provide the version of DOSBox you're using.Bromic
@Pharmacognosy right,in brackeen.com/vga/unchain.html, it says to choose a plane you need to send 2^plane to the 0x3C5 port, for your second point, you are right my code is a mess right now, but I will drastically clean up the code after I will see it working but thanks for that I will use that.Prankster
@JonathonReinhart I use the DosBox 0.74Prankster
@ErikGelfat Did my suggestion fix your problem? If yes, write this up as an answer so others know that your question has been solved.Pharmacognosy
@Pharmacognosy umm, unfortunately no, it now prints nothingPrankster
@ErikGelfat That's unfortunate. Try just swapping out 0, 1, 4, 8 for 1, 2, 4, 8. It is likely that the shl-based code I wrote doesn't work as-is; it was more intended to be an inspiration for an implementation of your own design.Pharmacognosy
@Pharmacognosy oh wow, it's weird because I tried it before, I guess my head is a mess right now I need to clear my mind, but thank you 2^0 should be 1, about the shl, yeah I tried to play with it created also system that checks if it should put 0 in VGAPlate, it doesn't work I guess I will find a way to it to work, Thank you Fuz!Prankster
@ErikGelfat My pleasure! Please write up your findings as an answer to your own question so others with the same problem can benefit from the solution we found.Pharmacognosy
@Pharmacognosy done, I guess SHL does not work because 0 times 2 is 0 and not 1, and 2 times two is 4, instead of two, so I guess the numbers are too small for using shift as a power functionPrankster
@ErikGelfat You shift 1 left, not 0. 1 shifted left 0 places is 1, shifted 1 place is 2, shifted 2 places is 4, and so on.Pharmacognosy
P
2

Thanks to Fuz for finding an answer and Jonathon Reinhart for making my question clearer. in the VGAPlaneSelect procedure, the al values, which are outputs for the 0x3c5 VGA address, should be 2^(the plane that you want to choose), and for plane 0 2^0 it should be 1, and I wrote 0

so:

        cmp [VGAPlane], 0
        jne VGAPlaneSelect_0
            mov al, 1h
            jmp VGAPlaneSelect_end
        VGAPlaneSelect_0:

a better way to do the VGAPlaneSelect procedure is:

    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
Prankster answered 22/1, 2020 at 13:0 Comment(5)
And that's overcomplicated anyway. Use a variable-count shift like fuz suggested. Or the most efficient way to turn n into 2^n if you have a 386-compatible is movzx dx, byte [VGAPlane] ; xor ax, ax ; bts ax, dx. You don't care about the FLAGS result, just the ax |= 1<<dx part. (You can pick any register, and it ignores high garbage, so you could only load into DL instead of zero-extending into DX or EDX, but then still read DX with BTS.)Vander
@PeterCordes Hello, true thing, I use .286 but I got it to work the with SHLPrankster
@PeterCordes the thing is it does not work perfectly, the original answer also, every bitmap that I am trying to print from the sixth to 21st pixel, the plane 0 is messed up, after these pixels 5 pixels works perfectly then the same and so on, it's very weird because if I print from the 0 pixel everything prints perfectlyPrankster
@PeterCordes nvm got itPrankster
Your updated code using a shift is still seriously overcomplicated! It seems you just need to clamp the shift count to 0..3. You can do that before shifting, and you can cmp cl, 4 instead of checking memory again and redoing the shift. Or you could just mov cl, [VGAPlane] ; mov ax, 1 ; and cl, 3 (mask off the high bits) ; shl ax, cl if you don't need to actually reset the value in memory.Vander

© 2022 - 2024 — McMap. All rights reserved.