I highly suggest reading the FAQ lists first, faq102.zip and faq202b.zip, and the online docs, inside the txi*.zip package. There is also a newsgroup, comp.os.msdos.djgpp. The main site is located at DJGPP Homepage, where the most up-to-date information is available on DJGPP and where the mail archives are kept. I find many helpful articles in the mail archives.
Additionally, looking through the header files of DJGPP might help. The more relevant files are go32.h, dpmi.h, pc.h, sys/farptr.h, sys/nearptr.h and sys/movedata.h.
How exactly? In low level assembly, one would load a segment register with the selector's value and then copy the value, "segment*16+offset", into another register. Then just use this segment:register pair as a pointer. I will show how simple it is:
short our_global_selector; ... our_global_selector = _dos_ds; ... movw _our_global_selector, %es movl $0xa0000, %edi ;** 0xA000*16 + 0x0000 = 0xA0000Note: We are using AT&T syntax for assembly.
Now we can use "es:edi" to write to video memory. How about a putpixel routine?
movw _our_global_selector, %es movl $0xA0000, %edi movw _y, %ax imulw $320, %ax addw _x, %ax addw %ax, %di movb _color, %al stosbIt is really straightforward once you get the hang of it. You could also use other register pairs:
movw _our_global_selector, %fs movl $0xA0000, %ebx ... movb _color, %al movb %al, %fs:(%ebx)
#include <go32.h> #include <dpmi.h> #include <sys/farptr.h> #define putpixel(x,y,c) _farpokeb(_dos_ds, 0xA0000 + (y)*320 + (x), (c))With optimizations turned on, DJGPP will inline the "_farpokeb" call.
Passing the selector to every "_farp*" call can be slow and cumbersome. However, we can easily take care of this:
/* circle routine */ ... _farsetsel(_dos_ds) /* loop */ ... _farnspokeb(0xA0000 + y*320 + x, color); ... /* end loop */"_farsetsel" just preloads register "fs" with "_dos_ds" for succeeding "_farns*" calls.
#include <go32.h> #include <dpmi.h> #include <sys/nearptr.h> unsigned char *videoptr = (unsigned char *)0xA0000; __djgpp_nearptr_enable(); videoptr[y*320 + x + __djgpp_conventional_base] = color; __djgpp_nearptr_disable();Easy!
#include <go32.h> #include <dpmi.h> #include <sys/movedata.h> unsigned char *videoptr = (unsigned char *)0xA0000; unsigned char *doublebuffer = (unsigned char *)malloc(320*200); void copy_buffer(void) { dosmemput(doublebuffer, 320*200, videoptr); } void copy_buffer2(void) { movedata(_my_ds(), doublebuffer, _dos_ds, videoptr, sizeof(*doublebuffer)); }These function calls are fairly obvious. By the way, "_my_ds()" just returns our code's selector;
#include <go32.h> #include <dpmi.h> #include <pc.h> void setmode(short mode) { __dpmi_regs r; r.x.ax = mode; __dpmi_int(0x10,&r); } struct rgbstruct { char red, green, blue; }; void setpalette(char color, struct rgbstruct rgb) { outportb(0x3c8, color); outportb(0x3c9, rgb.red); outportb(0x3c9, rgb.green); outportb(0x3c9, rgb.blue); }
#define PACKED __attribute__ ((packed)) #pragma pack(1) struct VBE_vInfo { char VBESig[4] PACKED; short VBEVer PACKED; ... }; struct VBE_mInfo { short ModeAttrib PACKED; char WinAAttrib PACKED; ... unsigned int PhysBasePtr PACKED; ... }; #pragma pack()
#include <go32.h> #include <dpmi.h> #include <sys/movedata.h> int VBE_detect(struct VBE_vInfo *vbeinfo) { __dpmi_regs r; assert(sizeof(*vbeinfo) < _go32_info_block.size_of_transfer_buffer); strncpy(vbeinfo->VBESig, "VBE2", 4); r.x.ax = 0x4F00; r.x.di = __tb & 0x0F; r.x.es = (__tb >> 4) & 0xFFFF; dosmemput(vbeinfo, sizeof(*vbeinfo), __tb); __dpmi_int(0x10, &r); dosmemget(__tb, sizeof(*vbeinfo), vbeinfo); ... }What did we just do? First, we know that the detect function "0x4F00" requires a memory buffer in the low 1M memory space (DOS memory) where it will return the mode tables, OEM strings, etc. So we would have to allocate space under 1M, equal to sizeof(struct VBE_vInfo). We could use "__dpmi_allocate_dos_memory()" but there is an easier way. DJGPP uses a global transfer buffer internally, usually 4K in size, and we can use this as our conventional memory buffer! After the call, we can just copy from this transfer buffer to our variable. Easy, isn't it?
void VBE_getModeInfo(unsigned short mode, struct VBE_mInfo *modeinfo) { __dpmi_regs r; assert(sizeof(*modeinfo) < _go32_info_block.size_of_transfer_buffer); r.x.ax = 0x4F01; r.x.cx = mode; r.x.di = __tb & 0x0F; r.x.es = (__tb >> 4) & 0xFFFF; __dpmi_int(0x10, &r); dosmemget(__tb, sizeof(*modeinfo), modeinfo); ... }
struct VBE_mInfo modeinfo; __dpmi_meminfo mi; unsigned int linear_address; VBE_getModeInfo(0x101, &modeinfo); mi.size = (unsigned long)(modeinfo.XRes*modeinfo.YRes); mi.address = modeinfo.PhysBasePtr; __dpmi_physical_address_mapping(&mi); linear_address = mi.address;
We have it! Using "__dpmi_physical_address_mapping()", we are able to convert the device's physical address to a linear address than we can use to poke around with, just like "0xA0000" with Mode 13h! However, before we start writing to video memory, we need to enable the video mode.
r.x.ax = 0x4F02; r.x.bx = 0x4101; __dpmi_int(0x10, &r);
unsigned char *videoptr = (unsigned char *)linear_address; __djgpp_nearptr_enable(); videoptr[y*width + x +__djgpp_conventional_base] = color; __djgpp_nearptr_disable();
unsigned char *videoptr = (unsigned char *)0x0; short our_global_selector = __dpmi_allocate_ldt_descriptors(1); __dpmi_set_segment_base_address(our_global_selector, linear_address); _farpokeb(our_global_selector, videoptr + y*width +x, color);
void copy_buffer2(void) { movedata(_my_ds(), doublebuffer, our_global_selector, videoptr, width*height); }
#pragma pack(1) struct VBE_PMInterface { short pfsetWindow PACKED; short pfsetDisplayStart PACKED; short pfsetPalette PACKED; ... }; #pragma pack()
This structure will store pointers to the VBE services, if one wants to call them directly from protected mode.
Now let's write a function that will retrieve our function pointers.
int VBE_getPMInterface(struct VBE_PMInterface *vbepmi) { __dpmi_regs r; r.x.ax = 0x4F0A; r.x.bx = 0x0000; __dpmi_int(0x10,&r); vbedpmi = (struct VBE_PMInterface *)malloc(sizeof(char)*r.x.cx); dosmemget(r.x.es*16 + r.x.di,sizeof(*vbepmi),vbepmi); }One allocates a buffer equal to size "r.x.cx" bytes after the "0x4F0A" call, then copies the protected mode interface information from DOS memory into it. Then the pointers to the VBE functions are easily retrieved:
vbepmi + vbepmi->pfsetWindow vbepmi + vbepmi->pfsetDisplayStart vbepmi + vbepmi->pfsetPalettePlease refer to the VBE 2.0 specifications for further information. By the way, do not forget to copy the interface from DOS memory after EVERY mode set, and free the buffer when shutting down the graphics system.
void bankswitch(short bank) { __dpmi_regs r; r.x.ax = 0x4F05; r.x.bx = 0x0000; r.x.dx = bank; __dpmi_int(0x10, &r); /* In djasm: __asm__ __volatile__(" movw $0x4F05, %%ax; xorw %%bx, %%bx; int $0x10" : : "d" (bank) : "ax", "bx", "dx" ); */ }In VBE mode 101h (640x480x8), you can have each bank holding 64K (65536) bytes. So the bank is computed as:
short bank = (short)((640*y + x) >> 16);To copy your double buffer to video memory using the nearptr functions, do:
void copy_buffer(void) { char *source, *dest; source = doublebuffer; dest = videoptr + __djgpp_conventional_base; __djgpp_nearptr_enable(); /* 640*480*8bpp = 307200 bytes = 4*64K + 45056 bytes */ bankswitch(0); memcpy(dest, source, 65536L); bankswitch(1<<WinGran); source += 65536L; memcpy(dest, source, 65536L); bankswitch(2<<WinGran); source += 65536L; memcpy(dest, source, 65536L); bankswitch(3<<WinGran); source += 65536L; memcpy(dest, source, 65536L); bankswitch(4<<WinGran); source += 65536L; memcpy(dest, source, 45056L); __djgpp_nearptr_disable(); }WinGran is just a 16-bit value obtained by:
VBE_getModeInfo(0x101, &modeinfo); WinGran=0; while ((unsigned)(64>>WinGran) != modeinfo.WinGranularity) WinGran++;
That's about it. Qapla'
GREETS lexi *ace*bjarne*blacky*chug*cm*dm*dM*flub*foxt*goo*griff*hh*jaw*jl*kes*lippis*mag*mblade* *midg*qs*rad*ronski*rs*saeg*sec*ser*shiv*sigz*silvr*sledge*terran*tom*xgc*x0r*zaph*zed*zhiv* *#c*#gamecode*#os2prog*
Thanks to all people behind DJGPP!
Mail me corrections/suggestions/complaints/crap at avly@castle.net
Copyright © 1996 avly@castle.net All Rights Reserved.
All trademarks mentioned are of their respective companies.