;---------------------------- ; From: Albert Brown ; Subject: 640x480x256 PutPixel Q. Does anyone know of a quick, small routine that puts a pixel on the screen with X, Y coordinates, and COLOR??? A. I am assuming you mean the default chained mode. These formulas should work for every 256 colour mode: Pixel Address = (Y * Row Size [640 in this case] ) + X Now that we have the address we can find the bank number. Bank = Pixel Address / 64K (or whatever your bank size is) Now that we have the bank number we can find the Pixel Offset within the bank. Pixel Offset = Pixel Address - (64K * Bank Number) Here is a simple routine that will display a point on the screen. The inputs are: X The X coordinate to display a pixel Y The Y coordinate to display a pixel Colour The colour value you wish to display BytesPerRow The number of bytes per row. ex. 640x480 will have 640 bytes per row ex. 320x200 will have 320 bytes per row LastBank This is the last bank that was used. It should be initialized to zero once and should be kept as a static variable. This is the only value that gets returned. The following code is in Assembly. Plotting a 256 colour pixel just happens to work out perfectly in assembly, as you will see. Pascal and C accept assembly in there code so all you have to do is add your function header and compile as normal. This routine only works for 64K banks. Therefore you should switch your card to 64K bank mode. Put your Pascal or C procedure/function header thingeroonie here Function Name: PlotPointInAnyNormal256ColourModeWithBankSwitching IN X,Y,Colour,BytesPerRow,LastBank OUT LastBank REGISTERS DESTROYED A whole lot if you don't save them. ; Save your registers here as well mov ax,0a000h mov es,ax ; Load the 64K Graphic segment for the 256 colour mode mov ax,Y ; Get the Y value mov bx,BytesPerRow ; Get the number of bytes per row or Row Size mul bx ; Multiply Y by BytesPerRow... (Y * RowSize) ; The result of Y * Row Size will be placed in DX:AX add ax,X ; Add the X value... (Y * RowSize) + X adc dx,0 ; If the result of addition exceeded the limits of ; the register then place the remainder in DX. ; The AX register is only 16 bits wide which means that the largest ; number available is 65535 or FFFF. If we have two 16 bit numbers added ; together the result would overflow. ; FFFF ; + FFFF ; ----- ; 1FFFE ; /\ ; I.E. the largest number that we can store in AX register, in this case, is ; FFFE and the carry bit is set meaning that we have an error. What we would ; have liked is that extra one be added to DX (the top 16 bits part of our ; 32 bit result).... AX is the lower 16 bits. ; We are in luck, the ADC instruction does just that. ; adc dx,0 is saying DX = DX + Carry Bit (1) + 0 ; If the result did not overflow (did not go over FFFF) then the carry bit ; would be 0... DX = DX + Carry bit (0) + 0. Which is the same as adding ; zero to DX. Which is what we want. ; Now we that we have the Pixel Address we can find the Bank Number. ; We know that a Bank Size is 64K which is 16 bits which is a full register ; therefore our bank number is just DX. DX tells us how many times the AX ; register went over FFFF (or how many times it went over our bank size). ; ; To prove this take the number in DX multiply it by 64K and add the ; number in AX and you will have Pixel Address. ; ; You probably guess it by now. The AX register holds the offset into the ; bank. So all we have to do is tell the hardware what bank we want and ; the offset from this bank or, in other words, the offset from 64K * bank ; size. mov di,ax ; Store the offset in this register because we ; will have to use the AX register to access the port ; or for calling the VESA routine. And this is one ; of the few registers that we can do indexing on ; with the 8086. cmp dx,LastBank ; Did we change the bank changed from last time? jz SameBank ; Nope, then don't switch bank. ; The above could be a cmp dl,LastBank which would be a byte compare rather ; then the word mov LastBank,dx ; Save the current bank number ; Put your own Bank switch code here or your own call to the VESA Bank ; routine. DX or DL holds the bank you wish to switch to. SameBank: mov al,Colour ; Get the colour value you wish to display mov es:[di],al ; And display it. ; Restore the registers that you saved ret ; Return to the caller Pascal or C procedure/C closure thingy Optimizations can be made for the above code. For example, instead of doing a mul bx. You can replace that with a look up table. This can easily be changed in the routine. Replace BytesPerRow with the address for that particular look up table. i.e. x320 would have its own look up table and so would x640. An Example for the 8086 of getting the result mov di,Y ; Get the Y value mov bx,LookUpTable ; Get the address of one of the look up tables shl di,1 ; Since each element in the index table is shl di,1 ; 32 bits long (4 bytes) we must multiply Y by ; 4 to get the correct answer. This is just ; a fast way of multiplying Y by 4. mov dx,[bx+di] ; Get the high 16 bit part inc di ; Point to the next value mov ax,[bx+di] ; Get the low 16 bit part An Example for the 286 mov di,Y ; Get the Y value mov bx,LookUpTable ; Get the address of one of the look up tables add di,di ; Another way of multiplying by 4. Faster then add di,di ; two shifts or a shl,2 on the 286 (I think). mov dx,[bx+di] ; Get the high 16 bit part inc di ; Point to the next value mov dx,[bx+di] ; Get the low 16 bit part An Example for the 386 movzx edi,Y ; Clear the 32 bit register and get the Y ; value movzx ebx,LookUpTable ; Clear the 32 bit register and get the ; Look Up Table address mov dx,[ebx+edi*4] ; Get the high 16 bit part mov ax,[ebx+edi*4+1] ; Get the low 16 bit part However, if you are coding for the 386 the whole display routine can be shortened and optimized far more than you can on the 286. For example, I would just use one memory move instead of two for the above. mov edx,[ebx+edi*4] ; Get the high 16 bits and the low 16 bits ; in one shot. * Origin: Burst Mode (1:163/528)