ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³°±²Û Technical documentation for GrafX 2.00 - Version 1.08 (10/05/1997) Û²±°³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This file deals with: - the PKM picture format - the values to send to the CRTC to access all the amazing video modes available in GrafX 2.00 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±²Û The PKM picture format - by Karl Maritaud Û²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ First of all, I'd like to say that I made this file format some years ago when I didn't knew how to load any good format (eg. GIF) and wanted to have my own format. PKM format was designed to be very simple, easy to encode and decode. Its header is very simple (short) and evolutive. The only real default I can find in this format is that you can only save 256-color pictures. I know that you will think: "Oh no just another fucking format! I'll never use it! Its compression is too poor and I prefer GIF!". And I'll answer: "Yeah! You're right. But if you dunno how to load GIF and want a simple format with a quite good compression rate (on simple pictures at least), it could be useful." So, here comes the format documentation... The HEADER: ÍÍÍÍÍÍÍÍÍÍÍ The header is the following 780-byte-structure. (Don't worry about the size. That's just because the palette is considered as a part of the header). ÚÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Pos ³ Field ³ Type ³ Size ³ Description ³ ÆÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͵ ³ 0 ³ Signature ³ char ³ 3 ³ Constant string "PKM" (with NO size ³ ³ ³ ³ ³ ³ delimitation '\0' or so...) ³ ÃÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 3 ³ Version ³ byte ³ 1 ³ For the moment, it can take only the ³ ³ ³ ³ ³ ³ value 0. ³ ³ ³ ³ ³ ³ Other packing methods may change this ³ ³ ³ ³ ³ ³ field but there is only one for now... ³ ÃÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 4 ³ Pack_byte ³ byte ³ 1 ³ Value of the recognition byte for color ³ ³ ³ ³ ³ ³ repetitions that are coded on 1 byte. ³ ³ ³ ³ ³ ³ (See the picture packing section for a ³ ³ ³ ³ ³ ³ better explanation) ³ ÃÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 5 ³ Pack_word ³ byte ³ 1 ³ Value of the recognition byte for color ³ ³ ³ ³ ³ ³ repetitions that are coded on 2 bytes. ³ ³ ³ ³ ³ ³ (See the picture packing section...) ³ ÃÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 6 ³ Width ³ word ³ 2 ³ Picture width (in pixels) ³ ÃÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 8 ³ Height ³ word ³ 2 ³ Picture height (in pixels) ³ ÃÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 10 ³ Palette ³ byte ³ 768 ³ RGB palette (RGB RGB ... 256 times) with ³ ³ ³ ³ ³ ³ values from 0 to 63. I know the standard ³ ³ ³ ³ ³ ³ in picture files is 0 to 255 but I find ³ ³ ³ ³ ³ ³ it stupid! It is really easier to send ³ ³ ³ ³ ³ ³ the whole palette in port 3C9h with a ³ ³ ³ ³ ³ ³ REP OUTSB without palette convertion. ³ ÃÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 778 ³ PH_size ³ word ³ 2 ³ Post-header size. This is the number of ³ ³ ³ ³ ³ ³ bytes between the header and the picture ³ ³ ³ ³ ³ ³ data. This value can be equal to 0. ³ ÀÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Data of type "word" are stored with Intel conventions: lower byte first. The POST-HEADER: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ The post-header has a variable size. It was designed to support new features for this file format without changing the whole format. It consists in field identifiers followed by their size and their value. A field identifier is coded with 1 byte and a field size also. These field identifiers are: (this list may be updated...) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 0 : Comment on the picture 1 : Original screen dimensions 2 : Back color (transparent color) If you encounter a field that you don't know just jump over it. But if a field tells you to jump to a position that is over the beginning of the picture data, there is an error in the file. The fields: ÄÄÄÄÄÄÄÄÄÄÄ * Comment: With this field, artists will be able to comment their pictures. Note that GrafX 2 has a comment size limit of 32 chars. But you can comment a picture with up to 255 chars if you make your own viewer since GrafX 2 will just ignore extra characters. Example: [0],[16],[Picture by X-Man] This sequence means: - the field is a comment - the comment takes 16 characters (there is no end-of-string character since you know its size) - the comment is "Picture by X-Man" * Original screen dimensions: Since GrafX 2 supplies a huge range of resolutions, it seemed convenient to add a field that indicates what were the original screen dimensions. Example: [1],[4],[320],[256] This sequence means: - the field is a screen dimensions descriptor - the dimensions are 2 words (so this value must be always equal to 4) - the original screen width was 320 pixels - the original screen height was 256 pixels Note that words stored in fields are written Intel-like. The 90% BETA version did not respect this norm. I'm really sorry about this. This is not very serious but pictures saved with version 90% and loaded with a latest version (91% and more) won't set the right resolution. * Back color: Saving the back color (transparent color) is especially useful when you want to save a brush. The size of this field is 1 byte (index of the color between 0 and 255). Example: [2],[1],[255] This sequence means: - the field is a screen dimensions descriptor - the value takes 1 byte - the transparent color is 255 The PICTURE PACKING METHOD: ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ The PKM compression method is some sort of Run-Length-Compression which is very efficient on pictures with long horizontal color repetitions. Actually, the compression is efficient if there are often more than 3 times the same color consecutively. I think that it would be better to give you the algorithm instead of swim- ming in incomprehensible explanations. BEGIN /* functions: Read_byte(File) reads and returns 1 byte from File Draw_pixel(X,Y,Color) draws a pixel of a certain Color at pos. (X,Y) File_length(File) returns the total length in bytes of File variables: type of Image_size is dword type of Data_size is dword type of Packed_data_counter is dword type of Pixels_counter is dword type of Color is byte type of Byte_read is byte type of Word_read is word type of Counter is word type of File is */ /* At this point you've already read the header and post-header. */ Image_size <- Header.Width * Header.Height Data_size <- File_length(File) - (780+Header.PH_size) Packed_data_counter <- 0 Pixels_counter <- 0 /* Depacking loop: */ WHILE ((Pixels_counterHeader.Pack_byte) AND (Byte_read<>Header.Pack_word)) THEN { Draw_pixel(Pixels_counter MOD Header.Width, Pixels_counter DIV Header.Width, Byte_read) Pixels_counter <- Pixels_counter + 1 Packed_data_counter <- Packed_data_counter + 1 } ELSE /* Is the number of pixels to repeat coded... */ { /* ... with 1 byte */ IF (Byte_read = Header.Pack_byte) THEN { Color <- Read_byte(File) Byte_read <- Read_byte(File) FOR Counter FROM 0 TO (Byte_read-1) STEP +1 Draw_pixel((Pixels_counter+Counter) MOD Header.Width, (Pixels_counter+Counter) DIV Header.Width, Color) Pixels_counter <- Pixels_counter + Byte_read Packed_data_counter <- Packed_data_counter + 3 } ELSE /* ... with 2 bytes */ { Color <- Read_byte(File) Word_read <- (word value) (Read_byte(File) SHL 8)+Read_byte(File) FOR Counter FROM 0 TO (Word_read-1) STEP +1 Draw_pixel((Pixels_counter+Counter) MOD Header.Width, (Pixels_counter+Counter) DIV Header.Width, Color) Pixels_counter <- Pixels_counter + Word_read Packed_data_counter <- Packed_data_counter + 4 } } } END For example, the following sequence: (we suppose that Pack_byte=01 and Pack_word=02) 04 03 01 05 06 03 02 00 01 2C will be decoded as: 04 03 05 05 05 05 05 05 03 00 00 00 ... (repeat 0 300 times (012Ch=300)) Repetitions that fit in a word must be written with their higher byte first. I know that it goes against Intel standard but since I read bytes from the file thru a buffer (really faster), I don't care about the order (Sorry :)). But words in the header and post-header must be written and read Intel-like! Packing advices: ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ * As you can see, there could be a problem when you'd want to pack a raw pixel with a color equal to Pack_byte or Pack_word. These pixels should always be coded as a packet even if there is only one pixel. Example: (we suppose that Pack_byte=9) 9 will be encoded 9,9,1 (The 1st 9 in the encoded... 9,9 will be encoded 9,9,2 ... sequence is Pack_byte) etc... * It seems obvious to find values for Pack_byte and Pack_word that are (almost) never used. So a small routine that finds the 2 less used colors in the image should be called before starting to pack the picture. This can be done almost instantaneously in Assembler. * When you want to pack a 2-color-sequence, just write these 2 colors one after the other (Color,Color) because it only takes 2 bytes instead of 3 if you had to write a packet (Pack_byte,Color,2). * If you pack a very simple picture which has a sequence of more than 65535 same consecutive bytes, you must break the sequence and continue with a new packet. Example: you have to pack 65635 same consecutive bytes (eg. color 0) (we suppose that Pack_byte=01 and Pack_word=02) You'll write: 02 00 FF FF 01 00 64 (FFFFh=65535, 64h=100) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±²Û Setting GrafX 2.00 video modes Û²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ All set-mode procs are in 386 ASM. Anyway, if you can't understand any line of ASM, I really can't see the use you'll have of these procedures. They are designed to be used in FLAT memory model. Anyway, it wouldn't take too much time for you to adapt them to the model you use since only memory indexations can be affected by this (so use DS:SI instead of ESI, ES:DI instead of EDI, and beware to the address 0A0000h that will become 0A000h:0000h). MCGA: (Standard VGA mode) ÍÍÍÍÍ Is there anybody in this world who still don't now how to set the MCGA 320x200 256 colors mode ??!? Well... I hope you are a novice if you read the 2 following lines :) mov ax,0013h int 10h X-Modes: (Extended VGA modes) ÍÍÍÍÍÍÍÍ Well... I think the original Mode X was 320x240 but now, many people call "X-Modes" (or Modes X, or Tweaked modes) all the VGA modes that use more that 64Kb of video memory with the "Unchained" structure. Setting a pixel in any X-Mode can be done by one same function (but I won't explain to you how to do that. You just have to tell the function what the plane width (screen_width/4) is). If you can't understand anything about what I say (unchained, planes...), just read any good documentation about Mode X. We'd like to thank the authors of XLIB2 for saving our time by having made this useful function. We slightly optimized it for our needs but the most important parts are here. mov ax,13h ; Yeah! The MCGA mode again! All X-Modes must start from int 10h ; the standard VGA mode, but many things change after. mov dx,3C6h ; During the initialization, we'll turn the palette into xor al,al ; black in order to avoid the user to see all our out dx,al ; manipulations. mov dx,3C4h ; We will inform the TIMING SEQUENCER register to switch mov ax,0604h ; in unchained mode (mode-X), without odd/even management out dx,ax ; and with an access to the 256Kb of the video card. mov ax,0100h ; Now we will engage the synchronous reset of the TS out dx,ax ; register because we're about to play with registers. mov al,01h ; Like with the palette, we ask the video card not to out dx,al ; peek the memory to display it anymore. Thus, it's inc dx ; one more way to avoid interferences in the display, in al,dx ; which happens until the mode is completely initialized mov ah,al ; and stable. In addition, we can expect that asking a mov al,01h ; memory reading interruption will turn the system push ax ; faster, and thus speed up the initialization of the mov al,ah ; graphic mode (hope makes you live :)) or al,20h ; out dx,al ; mov esi,X_ptr ; Pointer on the list of constants to send to the CRTC. cld lodsb ; This loads in AL a value that will tell what to do ; with the MISCELLANEOUS register, and increases ESI. ; The value is equal to ZERO => Nothing to do ; or ELSE => Send AL to MISCELLANEOUS or al,al ; Shall we modify the basic video mode? jz NoThankYou ; No?Ä¿ Actually the answer is always "Yes". mov dx,3C2h ; ³ Except for a few modes such as out dx,al ; ³ 320x200 in Mode X NoThankYou: ; <ÄÄÄÙ (but our 320x200 is MCGA...) mov dx,3C4h ; Manipulations with MISCELLANEOUS register are over, we mov ax,0300h ; can now disengage the synchronous register of the TS. out dx,ax ; Now, what about teasing the CRTC? mov dx,3D4h ; In the 18th register of the CRTC, we will disengage the mov al,11h ; protection bit. Without this, the values we would have out dx,al ; sent to the CRTC registers would have been ignored. inc dx in al,dx and al,7Fh out dx,al dec dx ; DX points back to the CRTC register entry lodsb ; This loads in AL the number of CRTC registers to modify xor ecx,ecx ; You must clear ECX before... mov cl,al ; ... starting to repeat AL (CL) times OUTSW rep outsw ; Let's send all the CRTC parameters! ; Just in case the 20th CRTC register would have been forgotten in the ; initialisation table, we can compute it by ourselves (Yeah, we are good ; guys). mov ax,Screen_width ; You must tell the routine what the Screen width is shr ax,3 mov ah,al mov al,13h out dx,ax mov dx,3C4h ; Now you have the good resolution but there can be mov ax,0F02h ; shitty pixels on the screen coming from the uncleared out dx,ax ; memory areas. mov edi,0A0000h ; So we'll clean memory starting from 0A0000h with the xor eax,eax ; value 0 (which is the standard black) and on a range mov ecx,4000h ; of 4000h dwords (256Kb). rep stosd ; Let's wipe all this out. mov dx,3C4h ; We can ask the VGA to read again the memory to display pop ax ; it on the screen... out dx,ax ; mov dx,3C6h ; ... and turn on the palette so the picture appears to mov al,0FFh ; the user. out dx,al ; The table of constants you must send is one of these: (These are tables for C but they can be easily used in other languages) word X320Y224[] = { 0x0BA3, 0x6F06, 0xBA07, 0x0008, 0x4109, 0x0810, 0x8A11, 0xBF12, 0x0014, 0xC715, 0x0416, 0xE317 }; word X320Y240[] = { 0x0AE3, 0x0D06, 0x3E07, 0x4109, 0xEA10, 0xAC11, 0xDF12, 0x0014, 0xE715, 0x0616, 0xE317 }; word X320Y256[] = { 0x0CE3, 0x2306, 0xB207, 0x0008, 0x6109, 0x0A10, 0xAC11, 0xFF12, 0x2013, 0x0014, 0x0715, 0x1A16, 0xE317 }; word X320Y270[] = { 0x0BE7, 0x3006, 0xF007, 0x0008, 0x6109, 0x2010, 0xA911, 0x1B12, 0x0014, 0x1F15, 0x2F16, 0xE317 }; word X320Y282[] = { 0x0CE3, 0x6206, 0xF007, 0x6109, 0x310F, 0x3710, 0x8911, 0x3312, 0x2F13, 0x0014, 0x3C15, 0x5C16, 0xE317 }; word X320Y300[] = { 0x0DE3, 0x4606, 0x1F07, 0x0008, 0x4009, 0x3110, 0x8011, 0x2B12, 0x2013, 0x0014, 0x2F15, 0x4416, 0xE317 }; word X320Y360[] = { 0x09E3, 0x4009, 0x8810, 0x8511, 0x6712, 0x2013, 0x0014, 0x6D15, 0xBA16, 0xE317 }; word X320Y400[] = { 0x03E3, 0x4009, 0x0014, 0xE317 }; word X320Y448[] = { 0x0BA3, 0x6F06, 0xBA07, 0x0008, 0x4009, 0x0810, 0x8A11, 0xBF12, 0x0014, 0xC715, 0x0416, 0xE317 }; word X320Y480[] = { 0x0AE3, 0x0D06, 0x3E07, 0x4009, 0xEA10, 0xAC11, 0xDF12, 0x0014, 0xE715, 0x0616 , 0xE317}; word X320Y512[] = { 0x0CE3, 0x2306, 0xB207, 0x0008, 0x6009, 0x0A10, 0xAC11, 0xFF12, 0x2013, 0x0014, 0x0715, 0x1A16, 0xE317 }; word X320Y540[] = { 0x0BE7, 0x3006, 0xF007, 0x0008, 0x6009, 0x2010, 0xA911, 0x1B12, 0x0014, 0x1F15, 0x2F16, 0xE317 }; word X320Y564[] = { 0x0CE7, 0x6206, 0xF007, 0x0008, 0x6009, 0x3E10, 0x8911, 0x3312, 0x2013, 0x0014, 0x3C15, 0x5C16, 0xE317 }; word X320Y600[] = { 0x0BE7, 0xBE06, 0xF007, 0x0008, 0x6009, 0x7C10, 0x8C11, 0x5712, 0x0014, 0x5815, 0x7016, 0xE317 }; word X360Y200[] = { 0x09E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x2D13, 0x0014, 0xE317 }; word X360Y224[] = { 0x12A7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x6F06, 0xBA07, 0x0008, 0x4109, 0x0810, 0x8A11, 0xBF12, 0x2D13, 0x0014, 0xC715, 0x0416, 0xE317 }; word X360Y240[] = { 0x11E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x0D06, 0x3E07, 0x4109, 0xEA10, 0xAC11, 0xDF12, 0x2D13, 0x0014, 0xE715, 0x0616, 0xE317 }; word X360Y256[] = { 0x12E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x2B06, 0xB207, 0x0008, 0x6109, 0x0E10, 0xAC11, 0xFF12, 0x2D13, 0x0014, 0x0715, 0x1A16, 0xE317 }; word X360Y270[] = { 0x12E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x3006, 0xF007, 0x0008, 0x6109, 0x2010, 0xA911, 0x1B12, 0x2D13, 0x0014, 0x1F15, 0x2F16, 0xE317 }; word X360Y282[] = { 0x12E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x6206, 0xF007, 0x6109, 0x310F, 0x3710, 0x8911, 0x3312, 0x2D13, 0x0014, 0x3C15, 0x5C16, 0xE317 }; word X360Y300[] = { 0x12E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x4606, 0x1F07, 0x0008, 0x4009, 0x3110, 0x8011, 0x2B12, 0x2D13, 0x0014, 0x2F15, 0x4416, 0xE317 }; word X360Y360[] = { 0x0FE7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x4009, 0x8810, 0x8511, 0x6712, 0x2D13, 0x0014, 0x6D15, 0xBA16, 0xE317 }; word X360Y400[] = { 0x0AE7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x4009, 0x2D13, 0x0014, 0xE317 }; word X360Y448[] = { 0x12A7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x6F06, 0xBA07, 0x0008, 0x4009, 0x0810, 0x8A11, 0xBF12, 0x2D13, 0x0014, 0xC715, 0x0416, 0xE317 }; word X360Y480[] = { 0x11E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x0D06, 0x3E07, 0x4009, 0xEA10, 0xAC11, 0xDF12, 0x2D13, 0x0014, 0xE715, 0x0616, 0xE317 }; word X360Y512[] = { 0x12E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x2B06, 0xB207, 0x0008, 0x6009, 0x0E10, 0xAC11, 0xff12, 0x2D13, 0x0014, 0x0715, 0x1A16, 0xE317 }; word X360Y540[] = { 0x12E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x3006, 0xF007, 0x0008, 0x6009, 0x2010, 0xA911, 0x1B12, 0x2D13, 0x0014, 0x1F15, 0x2F16, 0xE317 }; word X360Y564[] = { 0x12EB, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0x6206, 0xF007, 0x0008, 0x6009, 0x3E10, 0x8911, 0x3312, 0x2D13, 0x0014, 0x3C15, 0x5C16, 0xE317 }; word X360Y600[] = { 0x12E7, 0x6B00, 0x5901, 0x5A02, 0x8E03, 0x5E04, 0x8A05, 0xBE06, 0xF007, 0x0008, 0x6009, 0x7C10, 0x8C11, 0x5712, 0x2D13, 0x0014, 0x5815, 0x7016, 0xE317 }; word X400Y200[] = { 0x09E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x3213, 0x0014, 0xE317 }; word X400Y224[] = { 0x12A7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x6F06, 0xBA07, 0x0008, 0x4109, 0x0810, 0x8A11, 0xBF12, 0x3213, 0x0014, 0xC715, 0x0416, 0xE317 }; word X400Y240[] = { 0x12E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x0D06, 0x3E07, 0x0008, 0x4109, 0xEA10, 0xAC11, 0xDF12, 0x3213, 0x0014, 0xE715, 0x0616, 0xE317 }; word X400Y256[] = { 0x12E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x2B06, 0xB207, 0x0008, 0x6109, 0x1310, 0xAC11, 0xFF12, 0x3213, 0x0014, 0x0715, 0x1A16, 0xE317 }; word X400Y270[] = { 0x12E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x3006, 0xF007, 0x0008, 0x6109, 0x2010, 0xA911, 0x1B12, 0x3213, 0x0014, 0x1F15, 0x2F16, 0xE317 }; word X400Y282[] = { 0x12E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x6206, 0xF007, 0x6109, 0x310F, 0x3710, 0x8911, 0x3312, 0x3213, 0x0014, 0x3C15, 0x5C16, 0xE317 }; word X400Y300[] = { 0x12E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x4606, 0x1F07, 0x0008, 0x4009, 0x3110, 0x8011, 0x2B12, 0x3213, 0x0014, 0x2F15, 0x4416, 0xE317 }; word X400Y360[] = { 0x0FE7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x4009, 0x8810, 0x8511, 0x6712, 0x3213, 0x0014, 0x6D15, 0xBA16, 0xE317 }; word X400Y400[] = { 0x0AE7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x4009, 0x3213, 0x0014, 0xE317 }; word X400Y448[] = { 0x12A7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x6F06, 0xBA07, 0x0008, 0x4009, 0x0810, 0x8A11, 0xBF12, 0x3213, 0x0014, 0xC715, 0x0416, 0xE317 }; word X400Y480[] = { 0x11E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x0D06, 0x3E07, 0x4009, 0xEA10, 0xAC11, 0xDF12, 0x3213, 0x0014, 0xE715, 0x0616, 0xE317 }; word X400Y512[] = { 0x12E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x2B06, 0xB207, 0x0008, 0x6009, 0x1310, 0xAC11, 0xFF12, 0x3213, 0x0014, 0x0715, 0x1A16, 0xE317 }; word X400Y540[] = { 0x12E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x3006, 0xF007, 0x0008, 0x6009, 0x2010, 0xA911, 0x1B12, 0x3213, 0x0014, 0x1F15, 0x2F16, 0xE317 }; word X400Y564[] = { 0x12EB, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0x6206, 0xF007, 0x0008, 0x6009, 0x3E10, 0x8911, 0x3312, 0x3213, 0x0014, 0x3C15, 0x5C16, 0xE317 }; word X400Y600[] = { 0x12E7, 0x7100, 0x6301, 0x6402, 0x9203, 0x6604, 0x8205, 0xBE06, 0xF007, 0x0008, 0x6009, 0x7C10, 0x8C11, 0x5712, 0x3213, 0x0014, 0x5815, 0x7016, 0xE317 }; The structure: (example) ÚÄÄÄÄThis is the number of values to send to the CRTC registers. This ³ is actually the number of words in the tables minus 1 (because the ³ 1st word of the table is not sent to the CRTC but contains a value ³ to send to the MISCELLANEOUS register and the number of values to ³ send to the CRTC registers ;) ). ³ ³ ÚÄÄThis is the value to send to the MISCEALLANEOUS register (or ZERO ³ ³ if no value must be sent to it). ³ ³ ³ ³ ÚÄÄÄThis is a value to send to a register of the CRTC. ³ ³ ³ ³ ³ ³ ÚÄThis is the index of the CRTC register that will receive ³ ³ ³ ³ the value. ÿÿ ÿÿ { 0x0AE3, 0x0D06, 0x3E07, 0x4109, 0xEA10, 0xAC11, 0xDF12, 0x0014, 0xE715, 0x0616, 0xE317 }; You can notice that CRTC registers 0 to 5 (and 13h) define the screen width while registers 6 to 17h (except 13h) define the screen height. We have more modes in our pocket than the "few" :) ones we included in GrafX 2.00, but they aren't really useful or stable. But we may decice to include them anyway in a next version. If some of your favourite modes are missing, just send us the list of constants we must shoot at the CRTC just following the structure we use above. IMPORTANT! The constant values listed above are not supported by every monitor or video card. We have tested GrafX2 with several different configurations and we constated that some modes don't work at all with some video cards while some others can be overscanned, out of center, dark, too bright, or shrunk. But they all work fine with our poor little Tseng Labs ET4000... If you already have a good knowledge about CRTC and have different values than ours for certain modes, please let us know. We'll use them if they work better with a majority of computers. VESA: (A "pseudo-standard" for Super-VGA modes) ÍÍÍÍÍ We use VESA for modes that require a width of 640, 800 or 1024 pixels. But there is a way to combine X-Modes height with VESA so it's possible to have modes as weird as in X-Mode. mov ax,4F02h mov bx,Video_mode int 10h 256-color-VESA video modes are: 100h : 640x400 101h : 640x480 103h : 800x600 105h : 1024x768 107h : 1280x1024 (not available in GrafX2 because only supported with video cards with 2 or more Megabytes of video memory) As with X-Modes, you can modify CRTC registers to access "Xtd-VESA" modes! (Note that some video cards don't support the modification of the VGA CRTC registers in VESA modes.) To enter these extended VESA modes, set a standard VESA mode with the right width, and then call Modify_CRTC_registers with the proper Height table. Example (640x512) : VESA_Set_mode(101h) /* Set a video mode with the same width */ Modify_CRTC_registers(Y512) /* Modify height */ * Height tables: word Y224[] = { 0x09A3, 0x6F06, 0xBA07, 0x0008, 0x4109, 0x0810, 0x8A11, 0xBF12, 0xC715, 0x0416 }; word Y240[] = { 0x09E3, 0x0D06, 0x3E07, 0x0008, 0x4109, 0xEA10, 0xAC11, 0xDF12, 0xE715, 0x0616 }; word Y256[] = { 0x0900, 0x2B06, 0xB207, 0x0008, 0x6109, 0x0A10, 0xAC11, 0xFF12, 0x0715, 0x1A16 }; word Y270[] = { 0x09E7, 0x3006, 0xF007, 0x0008, 0x6109, 0x2010, 0xA911, 0x1B12, 0x1F15, 0x2F16 }; word Y282[] = { 0x0AE3, 0x6206, 0xF007, 0x0008, 0x6109, 0x310F, 0x3710, 0x8911, 0x3312, 0x3C15, 0x5C16 }; word Y300[] = { 0x09E3, 0x4606, 0x1F07, 0x0008, 0x4009, 0x3110, 0x8011, 0x2B12, 0x2F15, 0x4416 }; word Y350[] = { 0x09A3, 0xBF06, 0x1F07, 0x0008, 0x4009, 0x8310, 0x8511, 0x5D12, 0x6315, 0xBA16 }; word Y360[] = { 0x07E3, 0x0008, 0x4009, 0x8810, 0x8511, 0x6712, 0x6D15, 0xBA16 }; word Y400[] = { 0x01E3, 0x4009 }; word Y448[] = { 0x09A3, 0x6F06, 0xBA07, 0x0008, 0x4009, 0x0810, 0x8A11, 0xBF12, 0xC715, 0x0416 }; word Y480[] = { 0x09E3, 0x0D06, 0x3E07, 0x0008, 0x4009, 0xEA10, 0xAC11, 0xDF12, 0xE715, 0x0616 }; word Y512[] = { 0x0900, 0x2B06, 0xB207, 0x0008, 0x6009, 0x0A10, 0xAC11, 0xFF12, 0x0715, 0x1A16 }; word Y540[] = { 0x09E7, 0x3006, 0xF007, 0x0008, 0x6009, 0x2010, 0xA911, 0x1B12, 0x1F15, 0x2F16 }; word Y564[] = { 0x09E7, 0x6206, 0xF007, 0x0008, 0x6009, 0x3E10, 0x8911, 0x3312, 0x3C15, 0x5C16 }; word Y600[] = { 0x09E7, 0xBE06, 0xF007, 0x0008, 0x6009, 0x7C10, 0x8C11, 0x5712, 0x5815, 0x7016 }; Modifying CRTC registers: (inspired by X-Modes init... See above for more ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ details or comments) mov esi,XVESA_Ptr cld lodsb or al,al ; Shall we modify the basic video mode? jz NoThankYou ; No?Ä¿ The answer can be "No" because initialisations mov dx,3C2h ; ³ of certain VESA modes directly set the right out dx,al ; ³ value for the Miscellaneous register. NoThankYou: ; <ÄÄÄÙ mov dx,3D4h mov al,11h out dx,al inc dx in al,dx and al,7Fh out dx,al dec dx lodsb xor ecx,ecx mov cl,al rep outsw If you are cunning enough, you'll be able to combine constants used in X-Modes to get more "Xtd-VESA" modes such as 640x200, 800x480, etc... (but I don't think this will work with 1024x??? because this mode is generally interlaced... But who knows?...) The most difficult is to find the right value for the MISCELLANEOUS register.