Difference between revisions of "AGI/Specifications/Other/AGI256/Implementation Details"
(Added raw info dump about AGI v2.936 and AGI256's exe's differences.) |
(Added disclaimer and some headings.) |
||
Line 17: | Line 17: | ||
==Differences between AGI v2.936 and AGI256's hacked AGI executable== | ==Differences between AGI v2.936 and AGI256's hacked AGI executable== | ||
This is a raw info dump from a personal file | ===Disclaimer=== | ||
This is a raw info dump from a personal file so things may be funny, wrong, | |||
weird or <insert your favorite adjective here> but there are details to be sure: | |||
===The raw info dump=== | |||
<pre> | <pre> |
Revision as of 22:45, 16 July 2007
In the original PC AGI interpreter v2.936 the engine holds the color and priority/control line information for each pixel in a 160x200 byte buffer. The color data for each pixel is held in the byte's lower 4 bits and the priority/control line data is held in the upper 4 bits.
AGI256 extends this 160x200 byte buffer to a 320x200 byte buffer so that the 256-color data is held in the right 160x200 half of the buffer and the original style 16-color and priority/control line information is held in the left 160x200 half of the buffer.
Used external info
I used Nick Sonneveld's disassemblies of various PC AGI interpreter versions when trying to figure out AGI256. Those IDA databases were highly useful for understanding. Thanks Nick. You can check out the disassemblies if you're interested at the AGI Development Site.
Differences between AGI v2.936 and AGI256's hacked AGI executable
Disclaimer
This is a raw info dump from a personal file so things may be funny, wrong, weird or <insert your favorite adjective here> but there are details to be sure:
The raw info dump
Differences between original Sierra On-Line's AGI interpreter version 2.936 and the hacked AGI256's AGI.EXE: ///////////////////////////////////////////////////////////////////////////// Copyright string changed and some other bytes... not 100% sure if they are used or not... let's see. If changed bytes from seg000:0065 to seg000:0077 to all zeroes and to all semirandomly typed in numbers and all it did was that it made the AGI256 demo have some white color pixels where they shouldn't be in the Larry's bar screen. So... it may very well be that they're used. NOTE THAT: E8 49 00 = call sub_C4 = db 'FI',0 AND there's a real subroutine starting at seg000:00C4 so it might be used as code! - ORIGINAL 2.936 INTERPRETER: seg000:0000 aAdventureGameI db 'Adventure Game Interpreter',0Dh,0Ah seg000:0000 ; DATA XREF: seg001:0000�o seg000:0000 ; sub_401D+59�r ... seg000:0000 db 'Copyright (C) 1984, 1985, 1986 Sierra On-Line, Inc.',0Dh,0Ah seg000:0000 db 'Authors: Jeff Stephenson & Chris Iden',0Dh,0Ah seg000:0000 db 'FI',0 -> AGI256 INTERPRETER: seg000:0000 aAgi256ColorsCS db 'AGI 256 colors ',0Dh,0Ah ; DATA XREF: seg001:0000�o seg000:0000 ; sub_401D+59�r ... seg000:0000 db '(C) Sierra On-Line - by Jeff Stephenson and Chris Iden',0Dh,0Ah seg000:0000 db 'Hacked by Dark Minister',0Dh,0Ah seg000:0000 db 'FI',0 seg000:0065 db 87h ; Unknown data from 0x0065 to 0x0077 seg000:0066 db 49h ; I seg000:0067 db 29h ; ) seg000:0068 db 23h ; # ; DATA XREF: sub_4125+12�r seg000:0068 ; sub_4125+27�r ... seg000:0069 db 92h seg000:006A db 99h seg000:006B db 99h seg000:006C db 11h seg000:006D db 11h ; seg000:006E db 11h seg000:006F db 19h ; seg000:0070 db 88h seg000:0071 db 37h ; 7 seg000:0072 db 83h seg000:0073 db 83h ; â seg000:0074 db 73h ; s seg000:0075 db 87h ; ç seg000:0076 db 38h ; 8 seg000:0077 db 83h ; â ///////////////////////////////////////////////////////////////////////////// In function _StatLineWrite (Function name taken from Agi2917.idb): Changes _WindowLineClr call to setWhiteMenuBar call which clears first 8 rows of video memory with white. So changes the code that clears the status line before writing to it. Rationale: Probably because of wanting VGA support. - ORIGINAL 2.936 INTERPRETER: seg000:34D7 E8 CC F6 call sub_2BA6 ; _WindowLineClr in Agi2917.idb -> AGI256 INTERPRETER: seg000:34D7 E8 80 65 call near ptr 9A5Ah 0x9A5A - 0x9800 = 0x025A in the AGIGRAF.OVL: seg000:025A ; Clears first 8 rows (320*8 bytes) of video memory seg000:025A ; with white pixels (Color 15). seg000:025A seg000:025A setWhiteMenuBar proc near seg000:025A push cx seg000:025B push es seg000:025C push di seg000:025D push ax seg000:025E mov ax, 0A000h seg000:0261 mov es, ax seg000:0263 assume es:nothing seg000:0263 xor di, di seg000:0265 mov ax, 0F0Fh seg000:0268 mov cx, 500h seg000:026B repe stosw seg000:026D pop ax seg000:026E pop di seg000:026F pop es seg000:0270 assume es:nothing seg000:0270 pop cx seg000:0271 retn seg000:0271 setWhiteMenuBar endp ///////////////////////////////////////////////////////////////////////////// In _ArrangeMem (Name from Agi2917.idb): Changed to allocating twice the memory than with the original interpreter. Saves the allocated memory's segment to SBuff_Seg in Agi2917.idb and to agi256PicSeg in AGI256's disassembly. Rationale: Need another screen besides the 16 color & control line & priority info screen for 256 color info so that doubles the memory need. 0x690 in paragraphs (16 bytes) is 160*168 bytes. 0xD20 is twice that. - ORIGINAL 2.936 INTERPRETER: seg000:4240 mov bx, 690h -> AGI256 INTERPRETER: seg000:4240 mov bx, 0D20h ///////////////////////////////////////////////////////////////////////////// In _ArrangeMem (Name from Agi2917.idb): Changed to allocating twice the memory than with the original interpreter. Saves the allocated memory's segment to wHGCFontData in Agi2917.idb. Rationale: Don't really know. Maybe not needed at all? Or it's also possible that the wHGCFontData is not a correct name for the variable and/or the allocate area is used for something else and Hercules font data. 0x690 in paragraphs (16 bytes) is 160*168 bytes. 0xD20 is twice that. - ORIGINAL 2.936 INTERPRETER: seg000:4259 add ax, 690h -> AGI256 INTERPRETER: seg000:4259 add ax, 0D20h ///////////////////////////////////////////////////////////////////////////// In an AGI screen buffer filling function (_SBuffClear in Agi2917.idb): Fills the AGI screen buffer with AX contents. Filling space doubled in AGI256. (There's some obsolete Hercules code in this function, but it's not used so it doesn't matter). Use cases: AX = 0x4040 (Fill with lowest priority i.e. 4 and black color i.e. 0). AX = 0x4F4F (Fill with lowest priority i.e. 4 and white color i.e. 15). Rationale: Needed because the 256 color screen and the normal 16 color screen are horizontally adjacent. Probably only needed for clearing the 16 color screen because the 256 color screen is always fully filled when loading an AGI256 picture resource into it. 160*168 = 0x3480 * 2 160*168*2 = 0x6900 * 2 There's a repe stosw using cx after this instruction. - ORIGINAL 2.936 INTERPRETER: seg000:525E mov cx, 3480h -> AGI256 INTERPRETER: seg000:525E mov cx, 6900h ///////////////////////////////////////////////////////////////////////////// In _SBuffXLine (Name from Agi2917.idb): Changes direct pixel manipulation code to a function call. Functionally changes nothing as the called function is functionally identical to the code that was replaced. Rationale: Maybe not needed at all? Maybe some leftover modification that wasn't used after all? 0x9A4F must be pointing to: seg000:024E ; Sets and/or clears current pixel bits seg000:024E ; AL = ES:[DI] = PIXEL seg000:024E ; (PIXEL |= BH) &= BL seg000:024E seg000:024E setClrCurrPixel proc near seg000:024E nop seg000:024F mov al, es:[di] seg000:0252 or al, bh seg000:0254 and al, bl seg000:0256 mov es:[di], al seg000:0259 retn seg000:0259 setClrCurrPixel endp in AGIGRAF.OVL! So that makes their relocation value 0x9A4F - 0x24E = 0x9801... wait... that's not even... let's see, there's a nop in setClrCurrPixel's start. Let's throw that away so we'd jump right into 0x24F so the relocation value would be 0x9A4F - 0x24E = 0x9800... now that's better :-). - ORIGINAL 2.936 INTERPRETER: seg000:5298 loc_5298: ; CODE XREF: sub_526F+34�j seg000:5298 inc di seg000:5299 mov al, es:[di] ; CHANGED seg000:529C or al, bh ; CHANGED seg000:529E and al, bl ; CHANGED seg000:52A0 mov es:[di], al ; CHANGED seg000:52A3 loop loc_5298 -> AGI256 INTERPRETER: seg000:5298 loc_5298: ; CODE XREF: sub_526F+34�j seg000:5298 inc di seg000:5299 call near ptr 9A4Fh ; CHANGED seg000:52A3 loop loc_5298 ///////////////////////////////////////////////////////////////////////////// In _SBuffYLine (Name from Agi2917.idb): This converts the vertical drawing loop to use 320 rather than 160 as the offset between rows. Also changes direct pixel manipulation code to a function call with functionally identical code in it. Rationale: As there are now two AGI screens horizontally adjacent to each other (Left 160x168 one is the normal 16 color screen and the right 160x168 one is the new 256 color screen) the vertical line drawing loop has to use 320 as the offset between rows rather than 160. The pixel manipulation code replacement with a function call is probably not needed as it doesn't functionally change anything. - ORIGINAL 2.936 INTERPRETER: seg000:52E3 loc_52E3: ; CODE XREF: sub_52AB+34�j seg000:52E3 add di, 0A0h ; 'á' seg000:52E7 mov al, es:[di] seg000:52EA or al, bh seg000:52EC and al, bl seg000:52EE mov es:[di], al seg000:52F1 loop loc_52E1 -> AGI256 INTERPRETER: seg000:52E3 loc_52E3: ; CODE XREF: sub_52AB+34�j seg000:52E3 add di, 140h seg000:52E7 call near ptr 9A4Fh seg000:52F1 loop loc_52E1 which is effectively the same as: seg000:52E3 loc_52E3: ; CODE XREF: sub_52AB+34�j seg000:52E3 add di, 140h seg000:024F mov al, es:[di] seg000:0252 or al, bh seg000:0254 and al, bl seg000:0256 mov es:[di], al seg000:52F1 loop loc_52E1 ///////////////////////////////////////////////////////////////////////////// In _SBuffPlotPixel (Name from Agi2917.idb): Changes multiplication from 160 to 320 in calculation x + y * 320. (16 to 32 really but they're multiplied by 10 before these changes). Rationale: As there are now two AGI screens horizontally adjacent to each other (Left 160x168 one is the normal 16 color screen and the right 160x168 one is the new 256 color screen) the pixel plotting code has to use 320 as the offset between rows rather than 160. - ORIGINAL 2.936 INTERPRETER: seg000:5311 shl di, 1 seg000:5313 shl di, 1 seg000:5315 shl di, 1 seg000:5317 shl di, 1 -> AGI256 INTERPRETER: seg000:5311 shl di, 4 seg000:5314 shl di, 1 ///////////////////////////////////////////////////////////////////////////// In _SBuffPlotPixel (Name from Agi2917.idb): Changes direct pixel manipulation code to a function call. Functionally changes nothing as the called function is functionally identical to the code that was replaced. Rationale: Maybe not needed at all? Maybe some leftover modification that wasn't used after all? - ORIGINAL 2.936 INTERPRETER: seg000:532F mov al, es:[di] seg000:5332 or al, bh seg000:5334 and al, bl seg000:5336 mov es:[di], al -> AGI256 INTERPRETER: seg000:532F call near ptr 9A4Fh ///////////////////////////////////////////////////////////////////////////// In _SBuffPicFill (Name from Agi2917.idb): Changes multiplication from 160 to 320 in calculation x + y * 320. (16 to 32 really but they're multiplied by 10 before these changes). Rationale: As there are now two AGI screens horizontally adjacent to each other (Left 160x168 one is the normal 16 color screen and the right 160x168 one is the new 256 color screen) the pixel plotting code has to use 320 as the offset between rows rather than 160. - ORIGINAL 2.936 INTERPRETER: seg000:5483 shl di, 1 seg000:5485 shl di, 1 seg000:5487 shl di, 1 seg000:5489 shl di, 1 -> AGI256 INTERPRETER: seg000:5483 shl di, 4 seg000:5486 shl di, 1 ///////////////////////////////////////////////////////////////////////////// In _Pic_Show (Name from Agi2917.idb): Before calling j_EGAPutBlock (Name from Agi2917.idb) or j_AG_agi256PutBlock (Name from AGI256's disassembly) rotate the AGI screen buffers' pixels right by four in place (Writes the rotated values back to the buffer, that is) if a certain variable is one (rotate_sbuff). Probably exchanges the places of color and priority information. Rationale: As there are now two AGI screens horizontally adjacent to each other (Left 160x168 one is the normal 16 color screen and the right 160x168 one is the new 256 color screen) priority screen showing code needs to use 320 as the offset between rows rather than 160. But because the code is just a single loop for rotating the whole screen and rotating the 256 color values (If they aren't shown) doesn't matter (As presumably they are rotated back to their normal values afterwards) it is easier to just rotate both screens. 160*168 = 0x6900 160*168*2 = 0xD200 - ORIGINAL 2.936 INTERPRETER: seg000:5563 mov cx, 6900h -> AGI256 INTERPRETER: seg000:5563 mov cx, 0D200h ///////////////////////////////////////////////////////////////////////////// In _FBuffOffset (Agi2917.idb), agiToScreenCoords (AGI256 disassembly): Changes graphics driver dependent (CGA, EGA, Hercules etc) specific switches to a single VGA specific code. Rationale: AGI256 requires VGA so there's no need for the other switches. // The original 2.936 interpreter's part in pseudo C: { uint16 display = *(ds:0x1130); // enum {CGA, Tandy, HGC, EGA, VGA?}; uint16 computer = *(ds:0x112e); // enum { IBM_PC?, PCJr?, Tandy?, ...?} if (display == VGA) x *= 2; if (computer == IBM_PC) { x /= 2; if (display == EGA) x /= 2; } if (computer == Tandy) { if (display == EGA) x /= 4; } offset += x; retn; } // AGI256 interpreter's part in pseudo C: di += bx * 2; retn; - ORIGINAL 2.936 INTERPRETER: seg000:5636 cmp word ptr ds:1130h, 4 seg000:563B jnz loc_563F seg000:563D shl bx, 1 seg000:563F seg000:563F loc_563F: ; CODE XREF: seg000:563B�j seg000:563F cmp word ptr ds:112Eh, 0 seg000:5644 jnz loc_5651 seg000:5646 shr bx, 1 seg000:5648 cmp word ptr ds:1130h, 3 seg000:564D jnz loc_5651 seg000:564F shr bx, 1 seg000:5651 seg000:5651 loc_5651: ; CODE XREF: seg000:5644�j seg000:5651 ; seg000:564D�j seg000:5651 cmp word ptr ds:112Eh, 2 seg000:5656 jnz loc_5663 seg000:5658 cmp word ptr ds:1130h, 3 seg000:565D jnz loc_5663 seg000:565F shr bx, 1 seg000:5661 shr bx, 1 seg000:5663 seg000:5663 loc_5663: ; CODE XREF: seg000:5656�j seg000:5663 ; seg000:565D�j seg000:5663 add di, bx seg000:5665 retn -> AGI256 INTERPRETER: seg000:5636 shl bx, 1 seg000:5638 add di, bx seg000:563A retn ///////////////////////////////////////////////////////////////////////////// In _PicBuffOffset (Agi2917.idb), screenOffset (AGI256 disassembly): Changes multiplication from 160 to 320 in calculation x + y * 320. (16 to 32 really but they're multiplied by 10 before these changes). Rationale: As there are now two AGI screens horizontally adjacent to each other (Left 160x168 one is the normal 16 color screen and the right 160x168 one is the new 256 color screen) the AGI screen pixel position calculation has to use 320 as the offset between rows rather than 160. - ORIGINAL 2.936 INTERPRETER: seg000:5676 shl di, 1 seg000:5678 shl di, 1 seg000:567A shl di, 1 seg000:567C shl di, 1 -> AGI256 INTERPRETER: seg000:5676 shl di, 4 seg000:5679 shl di, 1 seg000:567B nop seg000:567C nop seg000:567D nop ///////////////////////////////////////////////////////////////////////////// Function that is called near the _EnablePicDraw's beginning in Agi2917.idb: If the relocation value 0x9800 works here too (Why wouldn't it?) then we're changing jump from ???GRAF.OVL's first subroutine (Not from the jump table in its head but after that at 0x0015 is the first subroutine) to a straight return. The first subroutine the the ???GRAF.OVL is the video mode setting routine. At least in AGIGRAF.OVL it sets 320x200 256 color video mode (Mode 13h), sets ds:[videoOfs] to 0xA000 and clears first 64 KiB of video memory with zeroes. Rationale: So this basically throws away some video mode specific stuff and replaces it with a VGA specific "nothing needed here" code :). // The original 2.936 interpreter's part in pseudo C: { uint16 display = *(ds:0x1130); // enum {CGA, Tandy, HGC, EGA, VGA?}; uint16 computer = *(ds:0x112e); // enum { IBM_PC?, PCJr?, Tandy?, ...?} if (computer == IBM_PC && display != HGC && display != EGA) setVideoMode(); // goto near ptr 0x9815; else retn; } // AGI256 interpreter's part in pseudo C (Eh :)): retn; - ORIGINAL 2.936 INTERPRETER: seg000:569F jmp near ptr 9815h -> AGI256 INTERPRETER: seg000:569F retn ///////////////////////////////////////////////////////////////////////////// If the relocation value 0x9800 works here too (Why wouldn't it?) then we're changing jump from ???GRAF.OVL's second subroutine (Not from the jump table in its head but after that at 0x0037 is the second subroutine) to a straight return. The second subroutine the the ???GRAF.OVL is the text mode setting routine. At least in AGIGRAF.OVL it sets 40x25 16 color text mode, enables background intensity (Colors 8-15), sets some cursor stuff and clears the text screen using some attribute. So this basically throws away some video mode specific stuff and replaces it with a VGA specific "nothing needed here" code :). // The original 2.936 interpreter's part in pseudo C: { uint16 display = *(ds:0x1130); // enum {CGA, Tandy, HGC, EGA, VGA?}; uint16 computer = *(ds:0x112e); // enum { IBM_PC?, PCJr?, Tandy?, ...?} if (computer == IBM_PC && display != HGC && display != EGA) setTextMode(); // goto near ptr 0x9837; else retn; } // AGI256 interpreter's part in pseudo C (Eh :)): retn; - ORIGINAL 2.936 INTERPRETER: seg000:5937 jmp near ptr 9837h -> AGI256 INTERPRETER: seg000:5937 retn ///////////////////////////////////////////////////////////////////////////// 'GAMEID' -string's (No trailing zero btw) start is turned into 'DM', 0 Rationale: DM is probably short for "Dark Minister" who was the hacker who made the AGI256 hack. Is this modification needed? Is this checked as the game ID or something? Don't know... - ORIGINAL 2.936 INTERPRETER: seg000:5B6C 'GAMEID' (A string without the trailing zero) -> AGI256 INTERPRETER: seg000:5B6C 'DM', 0 (, 'eID') ///////////////////////////////////////////////////////////////////////////// In _MenuInput (Agi2917.idb): Changes _WindowLineClr (Agi2917.idb) call near the function's beginning to AGIGRAF.OVL's setWhiteMenuBar which clears first 8 rows (320*8 bytes) of video memory with white pixels (Color 15). Rationale: Probably needed for VGA support when handling the menu/status line. - ORIGINAL 2.936 INTERPRETER: seg000:93ED call sub_2BA6 -> AGI256 INTERPRETER: seg000:93ED call near ptr 9A5Ah ///////////////////////////////////////////////////////////////////////////// - ORIGINAL 2.936 INTERPRETER & AGI256 INTERPRETER -info combined: seg001:0000 dd 0 ; seg001:0004 db 0 ; seg001:0005 db 0 ; seg001:0006 dd unk_9800 seg001:000A dw seg seg006 seg001:000C db 86h ; å seg001:000D db 0 ; seg001:000E db 0 ; seg001:000F db 0 ; seg001:0010 db 0 ; seg001:0011 db 0 ; seg001:0012 db 2Bh ; + seg001:0013 db 0 ; seg001:0014 db 0 ; seg001:0015 db 0 ; seg001:0016 dd unk_9800 seg001:001A dw seg seg004 seg001:001C db 93h ; ô seg001:001D db 0 ; seg001:001E db 0 ; seg001:001F db 0 ; seg001:0020 db 0 ; seg001:0021 db 0 ; seg001:0022 db 1Eh ; seg001:0023 db 0 ; seg001:0024 db 0 ; seg001:0025 db 0 ; seg001:0026 dd unk_9800 seg001:002A dw seg seg005 seg001:002C db 159 seg001:002D db 0 ; seg001:002E db 0 ; seg001:002F db 0 ; seg001:0030 db 0 ; seg001:0031 db 0 ; seg001:0032 db 37 ; Changed 0x25 (37) -> 0x28 (40) in AGI256 hack, size in paragraphs seg001:0033 db 0 ; seg001:0034 db 0 ; seg001:0035 db 0 ; seg001:0036 dd unk_9800 seg001:003A dw seg seg007 seg001:003C db 172 seg001:003D db 0 ; seg001:003E db 0 ; seg001:003F db 0 ; seg001:0040 db 0 ; seg001:0041 db 0 ; seg001:0042 db 91 seg001:0043 db 0 ; seg001:0044 db 0 ; seg001:0045 db 0 ; seg001:0046 dd unk_9800 seg001:004A dw seg seg003 seg001:004C db 185 seg001:004D db 0 ; seg001:004E db 0 ; seg001:004F db 0 ; seg001:0050 db 0 ; seg001:0051 db 0 ; seg001:0052 db 23 ; Changed 0x17 (23) -> 0x28 (40) in AGI256 hack, size in paragraphs seg001:0053 db 0 ; seg001:0054 db 0 ; seg001:0055 db 0 ; seg001:0056 dd unk_9DB0 seg001:005A dw seg seg008 seg001:005C db 197 seg001:005D db 0 ; seg001:005E db 0 ; seg001:005F db 0 ; seg001:0060 db 0 ; seg001:0061 db 0 ; seg001:0062 db 22 ; Changed 0x16 (22) -> 0x19 (25) in AGI256 hack, size in paragraphs seg001:0063 db 0 ; seg001:0064 db 0 ; seg001:0065 db 0 ; seg001:0066 dd unk_9DB0 seg001:006A dw seg seg009 seg001:006C dw 210 seg001:006E db 0 ; seg001:006F db 0 ; seg001:0070 db 0 ; seg001:0071 db 0 ; seg001:0072 dw 38 seg001:0074 db 0 ; seg001:0075 db 40h seg001:0076 db 0 ; seg001:0077 db 0 ; seg001:0078 dd seg seg009+0BF10000h seg001:007C dw 0DFh seg001:007E db 0 ; seg001:007F db 0 ; seg001:0080 db 0 ; seg001:0081 db 0 ; seg001:0082 db 0E8h ; F seg001:0083 db 1 ; seg001:0084 db 0FFh ; seg001:0085 db 0FFh ; seg001:0086 aCga_graf_ovl db 'CGA_GRAF.OVL',0 ; Changed to an empty string in AGI256 hack seg001:0093 aJr_graf_ovl db 'JR_GRAF.OVL',0 ; Changed to an empty string in AGI256 hack seg001:009F aEga_graf_ovl db 'EGA_GRAF.OVL',0 ; Changed to 'AGIGRAF.OVL',0 in AGI256 hack seg001:00AC aHgc_graf_ovl db 'HGC_GRAF.OVL',0 ; Changed to an empty string in AGI256 hack seg001:00B9 aVg_graf_ovl db 'VG_GRAF.OVL',0 ; Changed to an empty string in AGI256 hack seg001:00C5 aIbm_objs_ovl db 'IBM_OBJS.OVL',0 ; Changed to 'AGIOBJS.OVL',0 in AGI256 hack seg001:00D2 aHgc_objs_ovl db 'HGC_OBJS.OVL',0 ; Changed to an empty string in AGI256 hack seg001:00DF aAgidata_ovl db 'AGIDATA.OVL',0 seg001:00EB aAgi_exe db 'AGI.EXE',0 /////////////////////////////////////////////////////////////////////////////