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

Main Menu

VGA Planes -Plot Pixel and Page Flipping

Started by tda0626, June 22, 2024, 03:28:48 AM

Previous topic - Next topic


@tda0626: Look for topics by avcaballero in the Game Development board.  :smiley:


Quote from: tda0626 on June 23, 2024, 06:29:14 AMIf you do, please let me know. I still would like to know how to draw and page flip in unchained mode.
Here is a pretty nice description, with some source code (in C).


Quote from: sinsi on June 23, 2024, 07:53:45 AM
Quote from: tda0626 on June 23, 2024, 06:29:14 AMIf you do, please let me know. I still would like to know how to draw and page flip in unchained mode.
Here is a pretty nice description, with some source code (in C).

That is a good site and I have been to it a few times for its good info. It is how I found out about PC-GPE because in that same article it mentions that website also. However, I tried going by his example and still can't get my program to work.



I seem to remember 30 years ago  that there is additional invisible bytes added to each line of screen code. I had to use a book on vga programming to figure it out. But I'm old and don't remember any of it.



Quote from: tda0626 on June 22, 2024, 11:42:18 PMOK I think it does. So each plane is 64kb in size but you can only access 16kb of each plane in mode Y ( 320 x 200 unchained) because that is the maximum amount of bytes for that resolution 16kb x 4. I was under the impression that you could access all of the video memory regardless of resolution like it was regular memory. At 360x480, would you use A000:0000 to B800:FFFF plot a pixel on screen?

   If I am reading the Wilton book correctly, the memory planes
are mapped to A000:0000 to A000:FFFF, for the non-chained 256
color modes.

   In a different part of the book he says "Control of the VGA
video buffer addressing with bits 2 and 3 in the graphics
controller Miscellaneous register."

Bit 3  Bit 2   Video Buffer Address Range   Buffer Size
    0      0       A000:0000-B000:FFFF        128 KB
    0      1       A000:0000-A000:FFFF         64 KB
    1      0       B000:0000-B000:7FFF         32 KB
    1      1       B000:0000-B800:7FFF         32 KB

   I have not done either in my own code.  I did not work with the
non-BIOS 256 color modes.  I went from standard VGA modes to VESA
coding when I wanted better resolution.


Steve N.


Quote from: sinsi on June 22, 2024, 11:56:21 PMDOS would need to use the VESA interface which, unless you are using banked mode (VBE2), would require some sort of protected mode to access the linear frame buffer (VBE3) e.g. a 1024x768 32-bit colour mode needs 3MB of memory.

For DOS real-mode apps, one can use "unreal mode" or the INT 15h, AH=87 API if a DOS extender seems too complicated.

QuoteWindows is even more abstracted, with GDI and DirectX/OpenGL.

For creating a simple arcade game, GDI seems too "abstracted", DIrectX+DirectInput or SDL probably would be the best way to go.
Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.


Quote from: sinsi on June 22, 2024, 11:56:21 PMsome sort of protected mode
Quote from: _japheth on June 25, 2024, 03:07:25 PMone can use "unreal mode"
Details, details  :biggrin:

Unreal mode doesn't play very well with HIMEM.SYS though? I thought it used unreal mode for memory copying but reset the selectors back to proper real-mode limits.


Quote from: sinsi on June 25, 2024, 04:14:19 PMUnreal mode doesn't play very well with HIMEM.SYS though? I thought it used unreal mode for memory copying but reset the selectors back to proper real-mode limits.

IIRC MS Himem does not reset the selectors. But actually, that doesn't matter at all, because if your app is to use unreal mode, it should install an "INT 0D handler". This proc then has to query the PIC if it's a real interrupt request and if not, it switches to protected-mode and restores the segment cache limits to 4GB-1. I vaguely remember that MS Himem does this as well - installs the handler and then starts to copy...
Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.


Easier to bank-switch :badgrin:

It might have been the BIOS Move Extended Memory that does the selector thing, or at least some BIOSes.


To the OP:
Here's my GDI demo. It shows how basic shapes are drawn. You can play around with it and see stuff drawn in a window: lines, rectangles, ellipses. Pretty simple stuff.

Code is included. There's a lot of user-interface stuff in there you can ignore for the meantime (although you might want to look at it to see how Windows programs work). The interesting GDI stuff is all in the window proc for the display window, GDIdisplayProc().

Since you're working with 16-bit stuff and may not be all that familiar with how Windows programs work, a short primer:

In your code (16-bit), if you want to draw something, you set things up and then (probably) call a subroutine to draw the thing. Windows doesn't quite work that way.

In Windows, if you want to draw something (in a window, natch), you first have to set things up. But you don't actually call a routine called "DrawCircle" or whatever: the way you get something drawn is by calling a function called InvalidateRect().

WTF? How does that work?

Windows is a message-driven (or "event driven" if you prefer) OS. This means that it works by passing messages back and forth between the OS and an application program (and sometimes within the app). Drawing stuff on screen is done through a message called WM_PAINT, which says "draw or paint something in this window". Except that you (the application program) don't actually send the WM_PAINT message.

What you do instead is call InvalidateRect(X), where X is a handle to the window that you want the drawing to take place in. This tells the OS (Windows) that the content in that window is no longer "valid", so make it so by sending one or more WM_PAINT messges to that window. The "window proc" for that window receives that message; it's what's called a "callback function", as in "don't call us; we'll call you". Literally.

When that window receives that message, then it can draw whatever "object" is needed into the window (including text).

So look at that WM_PAINT handler to see how various things are drawn. For instance, to draw an unfilled rectangle:

; Draw a rectangle (unfilled):
INVOKE CreatePen, PS_SOLID, [EBX].OBJ.thickness, [EBX].OBJ.color
MOV pen, EAX
INVOKE SelectObject, hDC, EAX

; Next we need a brush to fill the interior of the rectangle. Since this is an unfilled
; shape, we'll use a "null" (transparent) brush:
INVOKE SelectObject, hDC, EAX

; Now draw the rectangle; the 4 position parameters define the upper-left and lower-right corners of the shape:
INVOKE Rectangle, hDC, [EBX].OBJ.rect.left, [EBX], [EBX].OBJ.rect.right, [EBX].OBJ.rect.bottom
JMP pntcnt

The actual drawing here is done by the Rectangle() function.

One added complication in this demo is that when the user selects a thing to be drawn, it doesn't just draw that object to the screen. Instead, it adds it to a list of things to be drawn. Then when InvalidateRect() is called, the paint handler goes through the entire list and draws all the things. The reason for this is that when I first wrote this, an object would get drawn, but then when the next object was drawn, if something covered the drawing window (like the Choose Color dialog, for instance, if the user wanted to change the color), then anything in the window would get wiped out and only the new thing would get drawn. Which is exactly as expected, since the WM_PAINT handler was only drawing that one object. While you (the application) can call InvalidateRect() to invalidate the contents of a window, the same thing happens silently if another window covers that window, which will also "invalidate" the window contents.

So by having a list and drawing the entire list each time, all the things are drawn properly. This is probably the way you'd do it in an actual application, where you want to retain all the stuff you've previously drawn in a window.

Having said all this, I'm afraid that that other commenter was correct when they said that GDI probably isn't what you'd ever want to use to create, say, a game. It's far too abstract and slow for that purpose. Still, this demo should let you at least get your feet wet in Win32 graphics.

Assembly language programming should be fun. That's why I do it.



Attached is a "Space Invaders" clone written in Masm syntax, using mode 13h...

Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.