Hi,
GLneo wrote:O.K. back on topic, Brendan, do you have any more info on your 12h mode pixel func. thx
Sorry - forgot I said I'd post more when I got home
First, you'd need to set the bit mask register to allow all bits of all writes to go to video memory. This is VGA graphics controller register #8, and is done like this:
Code: Select all
mov dx,0x03ce ;Set bit mask register
mov ax,0xff08
out dx,ax
It only needs to be done once depending on how you write to display memory (I do it just after switching to video mode 0x12).
To select which bit plane/s you're writing to there's the "map mask" register - Sequencer register #2. This register contains 4 bits where each bit corresponds to a bit plane, 0 = disabled and 1 = enabled. If you set this register to 0x0F and write to video display memory then you'd be writing to all planes at the same time. For example, to set 16 pixels to white at the top left of the screen you could do:
Code: Select all
mov bx,0xa000
mov es,bx ;es = segment for display memory
mov ax,0x0F02 ;ax = map mask register for plane 0
mov dx,0x03c4 ;dx = IO port for map mask register
out dx,ax ;Set map mask register
mov [es:0],0xFFFF
The problem here is that this only works for writing and not for reading. If you tried to do "or byte [es:0],1" I'm not sure exactly what would be or'ed with 1 (I think it depends on the contents of the "Read Map Select Register", which is register #4 of the Graphics Controller Registers). In any case you can only read from one plane, even though you can write to more than one.
To write a single pixel you can set the bit mask register to the bit that corresponds to your pixel, and then set the map mask register to 0x0F, then write a 0x00 (to make the pixel black), then set the map mask register to the colour you want and write a 0xFF to video to set the pixel to the correct colour.
Anyway, regardless of what you do writing a pixel at a time to video display memory is completely slow because it requires several IO port instructions, some bit shifting and masking, etc. I couldn't find any way to get acceptable performance.
To solve this I use a double buffer, where each plane is buffered in memory and I can read/write without messing about with slow IO ports. To avoid writing to display memory when I don't need to I also use a "video state buffer". The idea is that the video state buffer contains what was sent to the screen last time, so I can compare the new data (in the buffer) with the old data (in the state buffer) and only write to display memory if the new data is different. The code I use to blit the buffer to display memory is:
Code: Select all
blitBuffer:
pushes eax,edx,esi,edi
mov ax,0xa000
clr esi
mov dx,0x03c4 ;dx = IO port for map mask register
mov es,ax ;es = segment for display memory
mov ax,0x0102 ;ax = map mask register for plane 0
call .doPlane
mov ax,0x0202 ;ax = map mask register for plane 1
call .doPlane
mov ax,0x0402 ;ax = map mask register for plane 2
call .doPlane
mov ax,0x0802 ;ax = map mask register for plane 3
call .doPlane
sti
mov ax,cs
mov es,ax
pops eax,edx,esi,edi
ret
.doPlane:
out dx,ax ;Set map mask register
clr di ;es:di = address of display memory
cli
.l1: mov eax,[gs:esi+PMEMVIDbufferAddress] ;eax = new data
cmp eax,[gs:esi+PMEMVIDstateAddress] ;Has this dword changed?
je .l2 ; no, don't update display memory
stosd ; yes, update display memory
mov [gs:esi+PMEMVIDstateAddress],eax ; and the state buffer
add esi,4
cmp di,640*480/8
jb .l1
ret
.l2: add di,4
add esi,4
cmp di,640*480/8
jb .l1
ret
For this code I get very good performance (even on Bochs), but it costs 300 KB to do it (150 KB for the buffer and another 150 KB for the state). The reason I get good performance is that I'm only doing 4 IO port writes per screen update, and only writing to (slower) display memory when necessary. For example, if the screen is filled with a blue background (colour 0001b) and I draw a white "window" (colour 1111b), then plane 0 doesn't change at all.
I've attached the code to set a single pixel in the buffer. All the code is designed for real mode where GS has been set to "flat" (base = 0 and limit = 4 GB) - it's actually part of my boot menu's code.
I've also got more code to draw horizontal & vertical lines, draw 8*8 and 8*16 characters, etc but the code to plot a pixel should give you the idea...
Cheers,
Brendan