Debugging the S3 driver is difficult because the kernel debugger does not support source-level debugging of C code. The bulk of the code that must be modified is in assembler, and the kernel debugger handles this adequately. However, there will be occasions when the C portions of the driver must be modified and debugged. Some of the C functions are quite large, and have no internal symbols that are public. Therefore, if you trap in the middle of a large C function, it is often difficult to determine the problem. Also, if you need to set a breakpoint in the middle of a C function, it is often difficult to know precisely where to put it. The way around this particular problem is to put INT 3 instructions near the portion of the code where you suspect the problem to occur. There are two ways to do this. One way is to insert a call to the function haltproc() into the C code. The other is to add the following statement:
_asm {int 3};
Usually it is more convenient to add the _asm {INT 3} statement, as this breaks into the debugger at the point in the code at which you are interested. The haltproc() routine will also break into the kernel debugger, but it is less convenient because you have to trace out of haltproc() to see the real breakpoint location.
Most of the data in this driver is passed around in data structures, such as AIxfer. It is possible to dump these structures easily, even if they are being manipulated by C code. This tends to be easier than dumping stack frames. The ability to recognize which assembler code corresponds to a particular C language statement is necessary when debugging the portions of the driver that are written in C. However, using the C compiler to generate an assembler language version of the module is also helpful. This is accomplished using the /Fa compiler option. Also, when debugging C code, use /Od, which disables optimizations. This makes the code easier to follow.
The first time you build and test your driver, there is a possibility that it might not load, or that it will load, trap, and then immediately unload. The first challenge you face is getting the driver to load, initialize, and set the desired video mode. The first place to set a breakpoint is either loadproc(), which is in DYNA32.ASM, or in XGA_DLLInit, which is in INIT.C. Also, OS2_PM_DRV_ENABLE, in EDDENABL.C, is a good place to break, as it is called very early in the initialization process. FillLdb(), in EDDEFLDB.C, is another place to break. It is the first function in the driver that does any real work. FillPdb()in EDDEFPDB.C, and QueryAndSelectNativeMode(), in EDDESRES.C, are also good candidates for break points. You might want to trace through FillLdb(), FillPdb(), and QueryAndSelectNativeMode() until the driver initialization phase is stable. Any problems thus far are very likely to be in one of these three functions, or in a function that they call.
Some other good breakpoint locations include eddh_SrcDestBlt, eddh_PatDestBlt, eddh_DestOnlyBlt, eddh_DrawText, eddh_PMLINES, eddh_PMSCANLINE, and eddh_PMIMAGEDATA. Many early problems will be drawing related and will be confined to one of these functions.
If a trap error occurs, such as trap d (General protection error) or trap e, use the "vcf*" debugger command to break on instructions that cause traps. When the debugger breaks on the offending instruction, use "ln" (list near) to give you a nearby label, and also the "ks" to give you a stack trace, which gives a "map" detailing where you have been.
Often graphics chips must be polled to determine if they are ready to accept another command. Consider making the polling loop a macro. In the macro, for debugging mode only, add a drop-dead timer, such as counting down "ecx" from 1,000,000. If the "timer" expires, fall through to an INT 3, so the debugger breaks. This enables you to find situations where the driver will hang while waiting on the video chip. Typically, this is caused by a programming error such as an invalid command to the graphics chip.
Consider using Debug32Output to print a trace of the functions called in the driver when you are unable to determine your present location. The following is an example that traces every major driver entry point.
pmtrace.asm: .386p _DATA segment dword use32 public 'DATA' _DATA ends trace macro fn _DATA segment s_&fn db '&fn, ', 0 _DATA ends extrn fn : near public T&fn T&fn proc near push offset ds:s_&fn call Debug32Output pop eax jmp fn T&fn endp endm _TEXT segment use32 dword public 'CODE' assume cs:FLAT, ds:FLAT, es:FLAT extrn Debug32Output : near trace eddl_PolyLine trace eddl_GetCurrentPosition trace eddl_SetCurrentPosition trace eddl_DrawLinesInPath trace eddl_PolyShortLine trace edds_PolyScanLine trace DrawBits trace eddb_DeviceCreatebit map trace eddb_DeviceDeletebit map trace eddb_DeviceSelectbit map trace eddb_BitBlt trace eddb_GetPel trace eddb_SetPel trace eddb_ImageData trace ScanLR trace eddm_SaveScreenBits trace eddb_DrawBorder trace eddm_DeviceSetCursor trace Getbit mapBits trace Setbit mapBits trace eddm_SetColorCursor trace eddt_CharString trace eddt_CharStringPos trace eddb_PolyMarker trace eddv_CharRect trace eddv_CharStr trace eddv_ScrollRect trace eddv_UpdateCursor trace edda_DeviceGetAttribut trace eddv_DeviceSetAVIOFont2 trace edda_GetPairKerningTable trace edda_DeviceSetAttributes trace edda_DeviceSetGlobalAttribute trace edda_NotifyClipChange trace eddm_NotifyTransformChange trace edda_RealizeFont trace eddm_ErasePS trace eddl_SetStyleRatio trace edda_DeviceQueryFontAttributes trace edda_DeviceQueryFonts trace edda_DeviceInvalidateVisRegion trace eddg_GetPickWindow trace eddg_SetPickWindow trace eddg_ResetBounds trace eddg_GetBoundsData trace eddg_AccumulateBounds trace edda_GetCodePage trace edda_SetCodePage trace eddm_LockDevice trace eddm_UnlockDevice trace eddm_Death trace eddm_Resurrection trace edda_GetDCOrigin trace edda_DeviceSetDCOrigin trace eddl_GetLineOrigin trace eddl_SetLineOrigin trace eddl_GetStyleRatio trace eddc_QueryColorData trace eddc_QueryLogColorTable trace eddc_CreateLogColorTable trace eddc_RealizeColorTable trace eddc_UnrealizeColorTable trace eddc_QueryRealColors trace eddc_QueryNearestColor trace eddc_QueryColorIndex trace eddc_QueryRGBColor trace eddq_QueryDevicebit maps trace eddq_QueryDeviceCaps trace eddq_Escape trace eddq_QueryHardcopyCaps trace eddm_QueryDevResource trace DeviceCreatePalette trace DeviceDeletePalette trace DeviceSetPaletteEntries trace DeviceAnimatePalette trace DeviceResizePalette trace RealizePalette trace QueryHWPaletteInfo trace UpdateColors trace QueryPaletteRealization _TEXT ends end
This file, along with the following changes to EDDEFLDB.C will cause the name of every driver entry point that is called to be printed on the debug terminal:
//Changes to eddefldb.c - Replace real driver entry points with dummies //that print a trace, and then call the real entry point. DSPENTRY Teddl_PolyLine (); DSPENTRY Teddl_GetCurrentPosition (); DSPENTRY Teddl_SetCurrentPosition (); DSPENTRY Teddl_DrawLinesInPath (); DSPENTRY Teddl_PolyShortLine (); DSPENTRY Tedds_PolyScanLine (); DSPENTRY TGetScreenBits (); DSPENTRY TSetScreenBits (); DSPENTRY TDrawBits (); DSPENTRY Teddb_DeviceCreatebit map (); DSPENTRY Teddb_DeviceDeletebit map (); DSPENTRY Teddb_DeviceSelectbit map (); DSPENTRY Teddb_BitBlt (); DSPENTRY Teddb_GetPel (); DSPENTRY Teddb_SetPel (); DSPENTRY Teddb_ImageData (); DSPENTRY TScanLR (); DSPENTRY Teddm_SaveScreenBits (); DSPENTRY Teddb_DrawBorder (); DSPENTRY Teddm_DeviceSetCursor (); DSPENTRY TGetbit mapBits (); DSPENTRY TSetbit mapBits (); DSPENTRY Teddm_SetColorCursor (); DSPENTRY Teddt_CharString (); DSPENTRY Teddt_CharStringPos (); DSPENTRY Teddb_PolyMarker (); DSPENTRY Teddv_CharRect (); DSPENTRY Teddv_CharStr (); DSPENTRY Teddv_ScrollRect (); DSPENTRY Teddv_UpdateCursor (); DSPENTRY Tedda_DeviceGetAttributes (); DSPENTRY Teddv_DeviceSetAVIOFont2 (); DSPENTRY Tedda_GetPairKerningTable (); DSPENTRY Tedda_DeviceSetAttributes (); DSPENTRY Tedda_DeviceSetGlobalAttribute (); DSPENTRY Tedda_NotifyClipChange (); DSPENTRY Teddm_NotifyTransformChange (); DSPENTRY Tedda_RealizeFont (); DSPENTRY Teddm_ErasePS (); DSPENTRY Teddl_SetStyleRatio (); DSPENTRY Tedda_DeviceQueryFontAttributes (); DSPENTRY Tedda_DeviceQueryFonts (); DSPENTRY Tedda_DeviceInvalidateVisRegion (); DSPENTRY Teddg_GetPickWindow (); DSPENTRY Teddg_SetPickWindow (); DSPENTRY Teddg_ResetBounds (); DSPENTRY Teddg_GetBoundsData (); DSPENTRY Teddg_AccumulateBounds (); DSPENTRY Tedda_GetCodePage (); DSPENTRY Tedda_SetCodePage (); DSPENTRY Teddm_LockDevice (); DSPENTRY Teddm_UnlockDevice (); DSPENTRY Teddm_Death (); DSPENTRY Teddm_Resurrection (); DSPENTRY Tedda_GetDCOrigin (); DSPENTRY Tedda_DeviceSetDCOrigin (); DSPENTRY Teddl_GetLineOrigin (); DSPENTRY Teddl_SetLineOrigin (); DSPENTRY Teddl_GetStyleRatio (); DSPENTRY Teddc_QueryColorData (); DSPENTRY Teddc_QueryLogColorTable (); DSPENTRY Teddc_CreateLogColorTable (); DSPENTRY Teddc_RealizeColorTable (); DSPENTRY Teddc_UnrealizeColorTable (); DSPENTRY Teddc_QueryRealColors (); DSPENTRY Teddc_QueryNearestColor (); DSPENTRY Teddc_QueryColorIndex (); DSPENTRY Teddc_QueryRGBColor (); DSPENTRY Teddq_QueryDevicebit maps (); DSPENTRY Teddq_QueryDeviceCaps (); DSPENTRY Teddq_Escape (); DSPENTRY Teddq_QueryHardcopyCaps (); DSPENTRY Teddm_QueryDevResource (); DSPENTRY TDeviceCreatePalette (); DSPENTRY TDeviceDeletePalette (); DSPENTRY TDeviceSetPaletteEntries (); DSPENTRY TDeviceAnimatePalette (); DSPENTRY TDeviceResizePalette (); DSPENTRY TRealizePalette (); DSPENTRY TQueryHWPaletteInfo (); DSPENTRY TUpdateColors (); DSPENTRY TQueryPaletteRealization (); #define C(F) F /* Just call it */ #if 1 #define T(F) T##F /* Print trace message and call */ #else #define T(F) F /* Print trace message and call */ #endif PPFNL EnginesDispatchTable; PFNL DriversDispatchTable[] = { C(0), //GreGetArcParameters 0x4000 C(0), //GreSetArcParameters 0x4001 C(0), //GreArc 0x4002 C(0), //GrePartialArc 0x4003 C(0), //GreFullArcInterior 0x4004 C(0), //GreFullArcBoundary 0x4005 C(0), //GreFullArcBoth 0x4006 C(0), //GreBoxInterior 0x4007 C(0), //GreBoxBoundary 0x4008 C(0), //GreBoxBoth 0x4009 C(0), //GrePolyFillet 0x400A C(0), //GrePolyFilletSharp 0x400B C(0), //GrePolySpline 0x400C C(0), //GreDrawConicsInPath 0x400D C(0), //GreCookWholePath 0x400E C(0), //GreCookPathCurves 0x400F C(0), // 0x4010 C(0), //GreRenderPath 0x4011 #ifdef DCAF //DCAF C(OpenScreenChangeArea), //GreOpenScreenChangeArea 0x4012//DCAF C(GetScreenChangeArea), //GreGetScreenChangeArea 0x4013//DCAF C(CloseScreenChangeArea), //GreCloseScreenChangeArea 0x4014//DCAF #else //DCAF C(0), // 0x4012 C(0), // 0x4013 C(0), // 0x4014 #endif //DCAF C(0), // 0x4015 T(eddl_PolyLine), //GreDisjointLines 0x4016 T(eddl_GetCurrentPosition), //GreGetCurrentPosition 0x4017 T(eddl_SetCurrentPosition), //GreSetCurrentPosition 0x4018 T(eddl_PolyLine), //GrePolyLine 0x4019 T(eddl_DrawLinesInPath), //GreDrawLinesInPath 0x401A T(eddl_PolyShortLine), //GrePolyShortLine 0x401B T(edds_PolyScanLine), //GrePolyScanline 0x401C #ifdef DCAF //DCAF T(GetScreenBits), //GreGetScreenBits 0x401D//DCAF T(SetScreenBits), //GreSetScreenBits 0x401E//DCAF #else //DCAF C(0), // 0x401D C(0), // 0x401E #endif //DCAF C(0), // 0x401F C(0), // 0x4020 C(0), // 0x4021 T(DrawBits), //GreDrawBits 0x6022 T(eddb_DeviceCreatebit map), //GreDeviceCreatebit map 0x6023 T(eddb_DeviceDeletebit map), //GreDeviceDeletebit map 0x4024 C(eddb_DeviceSelectbit map), //GreDeviceSelectbit map 0x4025 T(eddb_BitBlt), //GreBitblt 0x6026 T(eddb_GetPel), //GreGetPel 0x6027 T(eddb_SetPel), //GreSetPel 0x4028 T(eddb_ImageData), //GreImageData 0x4029 T(ScanLR), //GreScanLR 0x602A C(0), //GreFloodFill 0x602B T(eddm_SaveScreenBits), //GreSaveScreenBits 0x402C C(0), //GreRestoreScreenBits 0x402D T(eddb_DrawBorder), //GreDrawBorder 0x602E T(eddm_DeviceSetCursor), //GreDeviceSetCursor 0x402F T(Getbit mapBits), //GreGetbit mapBits 0x6030 T(Setbit mapBits), //GreSetbit mapBits 0x6031 T(eddm_SetColorCursor), //GreSetColorCursor 0x4032 C(0), // 0x4033 C(0), // 0x4034 T(eddt_CharString), //GreCharString 0x5035 T(eddt_CharStringPos), //GreCharStringPos 0x7036 C(0), //GreQueryTextBox 0x5037 C(0), //GreQueryCharPositions 0x5038 C(0), //GreQueryWidthTable 0x5039 T(eddb_PolyMarker), //GrePolyMarker 0x403A T(eddv_CharRect), //GreCharRect 0x403B T(eddv_CharStr), //GreCharStr 0x403C T(eddv_ScrollRect), //GreScrollRect 0x403D T(eddv_UpdateCursor), //GreUpdateCursor 0x403E C(0), // 0x403F C(0), // 0x4040 C(0), // 0x4041 C(0), // 0x4042 C(0), // 0x4043 C(0), // 0x4044 C(0), // 0x4045 C(0), //GreBeginArea 0x4046 C(0), //GreEndArea 0x4047 C(0), //GreBeginPath 0x4048 C(0), //GreEndPath 0x4049 C(0), //GreCloseFigure 0x404A C(0), //GreFillPath 0x404B C(0), //GreOutlinePath 0x404C C(0), //GreModifyPath 0x404D C(0), //GreStrokePath 0x404E C(0), //GreSelectClipPath 0x404F C(0), //GreSavePath 0x4050 C(0), //GreRestorePath 0x4051 C(0), //GreClip1DPath 0x4052 C(0), //GreDrawRawPath 0x4053 C(0), //GreDrawCookedPath 0x4054 C(0), //GreAreaSetAttributes 0x6055 C(0), //GrePolygon 0x4056 C(0), //GrePathToRegion 0x4057 C(0), //GreDrawRLE 0x4058 C(0), // 0x4059 C(0), // 0x405A C(0), // 0x405B C(0), // 0x405C C(0), //GreGetRegionBox 0x405D C(0), //GreGetRegionRects 0x405E C(0), //GreOffsetRegion 0x405F C(0), //GrePtInRegion 0x4060 C(0), //GreRectInRegion 0x4061 C(0), //GreCreateRectRegion 0x4062 C(0), //GreDestroyRegion 0x4063 C(0), //GreSetRectRegion 0x4064 C(0), //GreCombineRegion 0x4065 C(0), //GreCombineRectRegion 0x4066 C(0), //GreCombineShortLineRegion 0x4067 C(0), //GreEqualRegion 0x4068 C(0), //GrePaintRegion 0x4069 C(0), //GreSetRegionOwner 0x406A C(0), //GreFrameRegion 0x406B C(0), // 0x406C C(0), // 0x406D C(0), //GreGetClipBox 0x406E C(0), //GreGetClipRects 0x406F C(0), //GreOffsetClipRegion 0x4070 C(0), //GrePtVisible 0x4071 C(0), //GreRectVisible 0x4072 C(0), //GreQueryClipRegion 0x4073 C(0), //GreSelectClipRegion 0x4074 C(0), //GreIntersectClipRectangle 0x4075 C(0), //GreExcludeClipRectangle 0x4076 C(0), //GreSetXformRect 0x4077 C(0), // 0x4078 C(0), // 0x4079 C(0), // 0x407A C(0), //GreSaveRegion 0x407B C(0), //GreRestoreRegion 0x407C C(0), //GreClipPathCurves 0x407D C(0), //GreSelectPathRegion 0x407E C(0), //GreRegionSelectbit map 0x407F C(0), //GreCopyClipRegion 0x4080 C(0), //GreSetupDC 0x4081 C(0), // 0x4082 C(0), //GreGetPageUnits 0x4083 C(0), //GreSetPageUnits 0x4084 C(0), //GreGetModelXform 0x4085 C(0), //GreSetModelXform 0x4086 C(0), //GreGetWindowViewportXform 0x4087 C(0), //GreSetWindowViewportXform 0x4088 C(0), //GreGetGlobalViewingXform 0x4089 C(0), //GreSetGlobalViewingXform 0x408A C(0), //GreSaveXformData 0x408B C(0), //GreRestoreXformData 0x408C C(0), //GreGetPageViewport 0x408D C(0), //GreSetPageViewport 0x408E C(0), // 0x408F C(0), // 0x4090 C(0), //GreGetGraphicsField 0x4091 C(0), //GreSetGraphicsField 0x4092 C(0), //GreGetViewingLimits 0x4093 C(0), //GreSetViewingLimits 0x4094 C(0), //GreQueryViewportSize 0x4095 C(0), //GreConvert 0x4096 C(0), //GreConvertPath 0x4097 C(0), //GreSaveXform 0x4098 C(0), //GreRestoreXform 0x4099 C(0), //GreMultiplyXforms 0x409A C(0), //GreConvertWithMatrix 0x409B C(0), // 0x409C T(edda_DeviceGetAttributes), //GreDeviceGetAttributes 0x609D T(eddv_DeviceSetAVIOFont2), //GreDeviceSetAVIOFont2 0x409E C(0), // 0x409F T(edda_GetPairKerningTable), //GreGetPairKerningTable 0x40A0 C(0), //GreDeviceSetAVIOFont 0x40A1 T(edda_DeviceSetAttributes), //GreDeviceSetAttributes 0x60A2 C(edda_DeviceSetGlobalAttribute), //GreDeviceSetGlobalAttribut 0x60A3 C(edda_NotifyClipChange), //GreNotifyClipChange 0x40A4 T(eddm_NotifyTransformChange), //GreNotifyTransformChange 0x40A5 T(edda_RealizeFont), //GreRealizeFont 0x40A6 T(eddm_ErasePS), //GreErasePS 0x40A7 T(eddl_SetStyleRatio), //GreSetStyleRatio 0x40A8 T(edda_DeviceQueryFontAttributes),//GreDeviceQueryFontAttributes 0x40A9 T(edda_DeviceQueryFonts), //GreDeviceQueryFonts 0x40AA C(edda_DeviceInvalidateVisRegion),//GreDeviceInvalidateVisRegion 0x40AB T(eddg_GetPickWindow), //GreGetPickWindow 0x40AC T(eddg_SetPickWindow), //GreSetPickWindow 0x40AD C(eddg_ResetBounds), //GreResetBounds 0x40AE C(eddg_GetBoundsData), //GreGetBoundsData 0x40AF C(eddg_AccumulateBounds), //GreAccumulateBounds 0x40B0 C(0), //GreGetExtraError 0x40B1 C(0), //GreSetExtraError 0x40B2 C(edda_GetCodePage), //GreGetCodePage 0x40B3 C(edda_SetCodePage), //GreSetCodePage 0x40B4 C(eddm_LockDevice), //GreLockDevice 0x40B5 C(eddm_UnlockDevice), //GreUnlockDevice 0x40B6 T(eddm_Death), //GreDeath 0x40B7 T(eddm_Resurrection), //GreResurrection 0x40B8 C(0), // 0x40B9 C(edda_GetDCOrigin), //GreGetDCOrigin 0x40BA C(edda_DeviceSetDCOrigin), //GreDeviceSetDCOrigin 0x40BB T(eddl_GetLineOrigin), //GreGetLineOrigin 0x40BC T(eddl_SetLineOrigin), //GreSetLineOrigin 0x40BD T(eddl_GetStyleRatio), //GreGetStyleRatio 0x40BE C(0), // 0x40BF C(0), // 0x40C0 C(0), // 0x40C1 C(0), // 0x40C2 T(eddc_QueryColorData), //GreQueryColorData 0x60C3 T(eddc_QueryLogColorTable), //GreQueryLogColorTable 0x60C4 C(eddc_CreateLogColorTable), //GreCreateLogColorTable 0x60C5 T(eddc_RealizeColorTable), //GreRealizeColorTable 0x60C6 T(eddc_UnrealizeColorTable), //GreUnrealizeColorTable 0x60C7 T(eddc_QueryRealColors), //GreQueryRealColors 0x40C8 C(eddc_QueryNearestColor), //GreQueryNearestColor 0x40C9 C(eddc_QueryColorIndex), //GreQueryColorIndex 0x60CA T(eddc_QueryRGBColor), //GreQueryRGBColor 0x60CB C(0), // 0x40CC C(0), // 0x40CD C(0), // 0x40CE C(0), // 0x40CF T(eddq_QueryDevicebit maps), //GreQueryDevicebit maps 0x40D0 C(eddq_QueryDeviceCaps), //GreQueryDeviceCaps 0x40D1 T(eddq_Escape), //GreEscape 0x40D2 T(eddq_QueryHardcopyCaps), //GreQueryHardcopyCaps 0x40D3 C(eddm_QueryDevResource), //GreQueryDevResource2 0x40D4 T(DeviceCreatePalette), //GreDeviceCreatePalette 0x40D5 T(DeviceDeletePalette), //GreDeviceDeletePalette 0x40D6 T(DeviceSetPaletteEntries), //GreDeviceSetPaletteEntries 0x40D7 T(DeviceAnimatePalette), //GreDeviceAnimatePalette 0x40D8 T(DeviceResizePalette), //GreDeviceResizePalette 0x40D9 T(RealizePalette), //GreRealizePalette 0x40DA T(QueryHWPaletteInfo), //GreQueryHWPaletteInfo 0x40DB T(UpdateColors), //GreUpdateColors 0x40DC T(QueryPaletteRealization), //GreQueryPaletteRealization 0x40DD C(0), //GreGetVisRects 0x40DE C(0), //GreDevicePolySet 0x40DF };
The T() macro in EDDEFLDB.C appends a "T" to the beginning of each entry point in the driver. The trace macro in PMTRACE.ASM creates a function with the name "T", in addition to the name of the real entry point. It then prints out the name of the entry point with Debug32Output, and then calls the real driver entry point.
If your driver supports cached-monochrome bit maps, it is often difficult to determine if the code is working correctly while copying monochrome bit maps from system memory to the display in eddh_SrcDestBlt. Almost any monochrome bit map will fit into the bit-map cache, because monochrome bit maps are small. As a result, the code in eddh_SrcDestBlt that handles monochrome data is rarely used. Disabling monochrome bit-map caching in PixBltThroughClipsViaPhunk() enables you to exercise the monochrome code in eddh_SrcDestBlt. (See BitBlt for further details on this.)
If you are familiar with the Microsoft** Windows** debugger, wdeb386, the OS/2 kernel debugger lacks a "z" command to eliminate INT 3 instructions you added to your code but now no longer need. The command e eip 90;g will perform the same function (by examining the byte at the current instruction pointer, replacing it with a no-op, and restarting execution). Another useful debugger command is the DP command, which dumps the page tables. This is useful for getting information about any linear pointer to the video aperture that you obtain.