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.

Boann
  • 44,932
  • 13
  • 106
  • 138
Erik Gelfat
  • 191
  • 7
  • 2
    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. – Jonathon Reinhart Jan 22 '20 at 12:22
  • Are you running this code on bare metal? A VM? What version of hypervisor and VGA firmware? It could be a bug there too. – Jonathon Reinhart Jan 22 '20 at 12:25
  • @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. – Erik Gelfat Jan 22 '20 at 12:31
  • 2
    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, cl` – fuz Jan 22 '20 at 12:32
  • So no hypervisor then, just provide the version of DOSBox you're using. – Jonathon Reinhart Jan 22 '20 at 12:33
  • @fuz right,in http://www.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. – Erik Gelfat Jan 22 '20 at 12:36
  • @JonathonReinhart I use the DosBox 0.74 – Erik Gelfat Jan 22 '20 at 12:37
  • @ErikGelfat Did my suggestion fix your problem? If yes, write this up as an answer so others know that your question has been solved. – fuz Jan 22 '20 at 12:37
  • @fuz umm, unfortunately no, it now prints nothing – Erik Gelfat Jan 22 '20 at 12:43
  • @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. – fuz Jan 22 '20 at 12:46
  • @fuz 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! – Erik Gelfat Jan 22 '20 at 12:53
  • @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. – fuz Jan 22 '20 at 12:54
  • @fuz 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 function – Erik Gelfat Jan 22 '20 at 13:02
  • @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. – fuz Jan 22 '20 at 17:07

1 Answers1

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
Erik Gelfat
  • 191
  • 7
  • 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< – Peter Cordes Jan 22 '20 at 23:52
  • @PeterCordes Hello, true thing, I use .286 but I got it to work the with SHL – Erik Gelfat Jan 23 '20 at 12:14
  • @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 perfectly – Erik Gelfat Jan 23 '20 at 12:17
  • @PeterCordes nvm got it – Erik Gelfat Jan 23 '20 at 16:45
  • 2
    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. – Peter Cordes Jan 24 '20 at 02:25