An early development unit for High Density from Palm, Inc

Table of Contents

  1. A mystery device
  2. High Density API
    1. How it is meant to be
    2. How I found it
    3. History
  3. Hacks
    1. Testing
  4. Improvements
  5. Downloads
  6. Comments...

A mystery device

A Palm m520

It looked like an m505, because it was built in an m505 case. But while an m505 was a common-at-the-time Palm device with a 160x160 display, this one was special. I had in my hands a device that was used at Palm to develop the High-Density API. This API is best known for supporting 320x320 screens of the PalmOS 5 devices, but in reality it was also supported in PalmOS 4. As the last device in the m500 family was the low-resolution m515, and this high-resolution device never shipped (or was even intended to ship), i called this one "m520".

Devices that shipped with PalmOS4 and High Density API are the Tungsten W and the Acer s65. The High Density API is documented in the PalmOS reference and further in the include files in the SDK. The first version of the High Density API is available in version 4 of the Window Manager. The device I had in front of me was wearing the skin of a Palm m505, bit it had a 320x320 screen. All the icons were low-resolution and upscaled, so the only thing the betrayed the device's high resolution was the text rendering - it was smooth and used the real screen resolution. The device was labeled using a permanent marker: "FONTS", so I suspect that it was used for development and testing of high-density fonts. Loading applications with High Density icons proves that basic High Density image drawing also works - the launcher successfully draws High Density icons.

However, most applications I loaded crashed or malfunctioned randomly. I spent some time investigating, and found something very interesting. What this device is, is sort of a missing-link between the final High Density API and the low-resolution devices that preceded it.

High Density API

How it is meant to be

The High Density API on PalmOS4 is exposed via a single SysTrap - sysTrapHighDensityDispatch (0xA3EC). It takes a selector in d2 register that specifies the actual function to perform. As documented, the first version of the High Density API has the following functions with the following selectors:

0x00: BitmapType* BmpGetNextBitmapAnyDensity(BitmapType* bitmapP); 0x01: UInt8 BmpGetVersion(const BitmapType* bitmapP); 0x02: BitmapCompressionType BmpGetCompressionType(const BitmapType* bitmapP); 0x03: UInt16 BmpGetDensity(const BitmapType* bitmapP); 0x04: Err BmpSetDensity(BitmapType* bitmapP, UInt16 density); 0x05: Boolean BmpGetTransparentValue(const BitmapType* bitmapP, UInt32* transparentValueP); 0x06: void BmpSetTransparentValue(BitmapType* bitmapP, UInt32 transparentValue); 0x07: BitmapTypeV3* BmpCreateBitmapV3(const BitmapType* bitmapP, UInt16 density, const void* bitsP, const ColorTableType* colorTableP); 0x08: UInt16 WinSetCoordinateSystem(UInt16 coordSys); 0x09: UInt16 WinGetCoordinateSystem(void); 0x0a: void WinScalePoint(PointType* pointP, Boolean ceiling) 0x0b: void WinUnscalePoint(PointType* pointP, Boolean ceiling); 0x0c: void WinScaleRectangle(RectangleType* rectP); 0x0d: void WinUnscaleRectangle(RectangleType* rectP)' 0x0e: Err WinScreenGetAttribute(WinScreenAttrType selector, UInt32* attrP); 0x0f: void WinPaintTiledBitmap(BitmapType* bitmapP, RectangleType* rectP); 0x10: Err WinGetSupportedDensity(UInt16* densityP); 0x11: void EvtGetPenNative(WinHandle winH, Int16* pScreenX, Int16* pScreenY, Boolean* pPenDown); 0x12: Coord WinScaleCoord(Coord coord, Boolean ceiling); 0x13: Coord WinUnscaleCoord(Coord coord, Boolean ceiling); 0x14: void WinPaintRoundedRectangleFrame(const RectangleType *rP, Coord width, Coord cornerRadius, Coord shadowWidth);

The later "1.5 density feature set" (which could in theory be used on a 68k pre-PalmOS5 device, but never existed) added two more:

0x15: UInt32 WinSetScalingMode(UInt32 mode); 0x16: UInt32 WinGetScalingMode(void);

The WinScreenGetAttribute function itself takes a selector for what attribute is to be returned. The offical API defined the following attributes:

0x00: winScreenWidth //real width in real pixels with no scaling 0x01: winScreenHeight //real heght in real pixels with no scaling 0x02: winScreenRowBytes //number of bytes in each row of framebuffer 0x03: winScreenDepth //current screen depth in bits per pixel 0x04: winScreenAllDepths //a bitmap of all the supported depths (bit N is set if N + 1 bpp is supported) 0x05: winScreenDensity //screen density (72 is standard, 144 is double) 0x06: winScreenPixelFormat //screen native pixel format (drawing bitmaps in this formwat is fastest) 0x07: winScreenResolutionX //actual number of pixels per inch of screen in the X direction 0x08: winScreenResolutionY //actual number of pixels per inch of screen in the Y direction

How I found it

Given that the High Density API was misbehaving, it made sence to look at its implementation. In PalmOS 5+, the High Density API is simply part of the OS, but in PalmOS 4, it is a separate extension, contained in HighDensityDisplay.prc. This extension patches a large number of system functions to enable the High Density display to be used. I disasembled it, and found some interesting things. The API it exported was also called via sysTrapHighDensityDispatch, but the selectors were not the same. Here are the supported selectors on this device:

0x00: BitmapType* BmpGetNextBitmapAnyDensity(BitmapType* bitmapP); 0x01: UInt8 BmpGetVersion(const BitmapType* bitmapP); 0x02: BitmapCompressionType BmpGetCompressionType(const BitmapType* bitmapP); 0x03: UInt16 BmpGetDensity(const BitmapType* bitmapP); 0x04: Err BmpSetDensity(BitmapType* bitmapP, UInt16 density); 0x05: Boolean BmpGetTransparentValue(const BitmapType* bitmapP, UInt32* transparentValueP); 0x06: void BmpSetTransparentValue(BitmapType* bitmapP, UInt32 transparentValue); 0x07: UInt16 WinSetCoordinateSystem(UInt16 coordSys); 0x08: UInt16 WinGetCoordinateSystem(void); 0x09: void WinScalePoint(PointType* pointP, Boolean ceiling) 0x0a: void WinUnscalePoint(PointType* pointP, Boolean ceiling); 0x0b: void WinScaleRectangle(RectangleType* rectP); 0x0c: void WinUnscaleRectangle(RectangleType* rectP)' 0x0d: Err WinScreenGetAttribute(WinScreenAttrType selector, UInt32* attrP); 0x0e: void WinPaintTiledBitmap(BitmapType* bitmapP, RectangleType* rectP); 0x0f: Err WinGetSupportedDensity(UInt16* densityP); 0x10: void EvtGetPenNative(WinHandle winH, Int16* pScreenX, Int16* pScreenY, Boolean* pPenDown);

In addition, the selectors supported for WinScreenGetAttribute were:

0x00: winScreenWidth //real width in real pixels with no scaling 0x01: winScreenHeight //real heght in real pixels with no scaling 0x02: winScreenRowBytes //number of bytes in each row of framebuffer 0x03: winScreenDepth //current screen depth in bits per pixel 0x04: winScreenAllDepths //a bitmap of all the supported depths (bit N is set if N + 1 bpp is supported) 0x05: winScreenResolutionX //actual number of pixels per inch of screen in the X direction 0x06: winScreenResolutionY //actual number of pixels per inch of screen in the Y direction

History

The cool part is that this shows how the High Density API developed. This pre-release state seems like it is complete and usable. One can manage High Density bitmaps, scale coordinates, and change coordinate systems. It seems like it is enough. I imagine this was slated for release, until some internal developers tried to actually use it. They likely ran into a few issues. First: it is common in PalmOS development to generate bitmaps from raw data. One would normally use BmpCreate() for this. But such a bitmap will always, for backwards-compatibility, be a version two bitmap - always low-density. There is nothing in this API to create a version 3 bitmap (required to represent a High Density bitmap). Clearly something was needed. I imagine this is how BmpCreateBitmapV3() was born. Why was it added into the middle of the table? Well that I do not know, especially given that the other added API were added at the end. I suspect that it was only after BmpCreateBitmapV3() was added that the result was deemed to be the first version "stable enough to release". After this, only additions to the end of the table were possible, as there were likely already internal users of the new API. I suspect that density and pixel format selectors for WinScreenGetAttribute() were added around the same time as BmpCreateBitmapV3(), before the first "stable enough to release" API version. I guess that WinScaleCoord(), WinUnscaleCoord() were added as convenience utilities - they are simply a multiply/divide operation with rounding so they aren't mandatory. I guess WinPaintRoundedRectangleFrame() was a late addition, by someone's request, since WinDrawRectangleFrame() has a thickness limit of 2 pixels, which is very thin on a High Density display in High Density mode.

Hacks

Clearly, this current code on this device is not binarily-compatible with any PalmOS software expecting to use the documented API. For example, an application attempting to call BmpCreateBitmapV3() would call sysTrapHighDensityDispatch with a selector of 0x07. On this device, that would be a call to WinSetCoordinateSystem(). The chances that the top 16 bits of a bitmap pointer (first parameter to BmpCreateBitmapV3()) are a valid density for a call to WinSetCoordinateSystem() are small, so WinSetCoordinateSystem() will just return the current coordinate system, which will be 72 or 144. Due to a peculiarity of the calling convention used by PalmOS, integers are returned in the d0 register, while pointers are returned in the a0 register. That means that the return value of WinSetCoordinateSystem() will not even be seen, and whatever garbage was left over in a0 will be seen as the return value by the calling application, after what it thought was a call to BmpCreateBitmapV3(). Treating random garbage as a Bitmap pointer is unlikely to end well. Any other function call past selector 7 will suffer the same fate. Any calls to WinScreenGetAttribute() with a selector value over 5 would also produce nonsense. Luckily almost nobody ever used WinScreenGetAttribute(). The existing code on the device did call the APIs correctly for this device, of course.

I wanted to allow normal High Density-supporting software to run on this device. But how? The dispatcher is at the very start of the code resource (important) and looks like this:

HighDensityDispatch: cmp.w #$11,d2 bge.s invalid_selector add.w d2,d2 add.w d2,d2 jmp jumptbl(pc,d2.w) jumptbl: jmp BmpGetNextBitmapAnyDensity(pc) ; 0 - HDSelectorBmpGetNextBitmapAnyDensity jmp BmpGetVersion(pc) ; 1 - HDSelectorBmpGetVersion jmp BmpGetCompressionType(pc) ; 2 - HDSelectorBmpGetCompressionType jmp BmpGetDensity(pc) ; 3 - HDSelectorBmpGetDensity jmp BmpSetDensity(pc) ; 4 - HDSelectorBmpSetDensity jmp BmpGetTransparentValue(pc) ; 5 - HDSelectorBmpGetTransparentValue jmp BmpSetTransparentValue(pc) ; 6 - HDSelectorBmpSetTransparentValue jmp WinSetCoordinateSystem(pc) ; 7 - HDSelectorWinSetCoordinateSystem (should be 8) jmp WinGetCoordinateSystem(pc) ; 8 - HDSelectorWinGetCoordinateSystem (should be 9) jmp WinScalePoint(pc) ; 9 - HDSelectorWinScalePoint (should be 10) jmp WinUnscalePoint(pc) ; 10 - HDSelectorWinUnscalePoint (should be 11) jmp WinScaleRectangle(pc) ; 11 - HDSelectorWinScaleRectangle (should be 12) jmp WinUnscaleRectangle(pc) ; 12 - HDSelectorWinUnscaleRectangle (should be 13) jmp WinScreenGetAttribute(pc) ; 13 - HDSelectorWinScreenGetAttribute(should be 14) jmp WinPaintTiledBitmap(pc) ; 14 - HDSelectorWinPaintTiledBitmap (should be 15) jmp WinGetSupportedDensity(pc) ; 15 - HDSelectorWinGetSupportedDensity (should be 16) jmp EvtGetPenNative(pc) ; 16 - HDSelectorEvtGetPenNative (should be 17) invalid_selector: pea err_msg(pc) systrap ErrDisplayFileLineMsg() err_msg: dc.b 'Unsupported high density trap',0

Well, I could just add a jump between the jumps to BmpSetTransparentValue and WinSetCoordinateSystem, append some code to the code resource, and thus implement BmpCreateBitmapV3(). With that, the ordering of all the calls will be correct and likely most applications will work. I did not add the additional selectors past EvtGetPenNative(), nor did I change WinScreenGetAttribute() at all. Even with this simple plan, it was actually a bit of a pain, binary-patching the code resource, but it worked. I reassembled the ROM using some tools I had made for rePalm, and it worked on the emulator.

Testing

Wait, what emulator? Indeed the conventional PalmOS emulator (POSE) does not support this device. But I was able to convince Christian Speckner (the author of CloudPilot - a web port and improvement on POSE) to support the device. Given the ROM and some time, it worked! You can actually load the ROM unto CloudPilot now and emulate it! This helped immensely with the testing. After some tweaking, the ROM booted with my patched HighDensityDisplay.prc and some applications ran. Many did not.

Improvements

PalmOS devices divide the RAM into storage heap (where user data lives) and dynamic heap (where temporary allocations are made from). Typically in PalmOS 4, just under 1/16 of the RAM is allocated to dynamic heap, up to a max of 256K. The issue is that in devices with High Density, all images are 4x the size, so the dynamic heap would need to be bigger to avoid running out of memory quickly. As this device was an early development unit, this was not done, and it had a woefully-undersized dynamic heap. I patched BigHal.prc to adjust the heap to 1MB, and most things worked fully. Sweet. With emulator testing completed, it was time to flash the real device. The image works on the device (as you can see in the photo album). Since only one such device exists, modifying its ROM was a tough call. To make sure the stock ROM is never lost, I compressed it, and left it as a database in the new ROM. Now it can be recovered and flashed back at will.

Downloads

If you'd like to play with this device in emulation, you can download both the stock and my ROMs here: [LINK]. If you'd like to see some pictures of the device inside and out, here is a photo album: [LINK]

Comments...

© 2012-2024