Drawing directly on the screen by Brian Parker

Editor's note: Brian, who's working on a Palm emulator for the Newton, describes here a method to draw on the screen buffer directly on a MP2x00. This isn't the only faster way to perform graphics operations but probably the fastest, lowest and less portable way. Your program should check that it's running on a MP2x00 before doing anything like this (use Gestalt to know about this). Other methods include drawing into a Bitmap (like VNC) or using Quickdraw (like Fractal).

The graphics capabilities of the Newton have prevented Many programs from being made or running well. Mainly access to the screen drawing has been limited to the NewtonScript space. With this information C++ code can read and write to the screen buffer of the 2000 and 2100. The data immediately appears on the screen instead of waiting for a Draw Script.

There are a few possible problems with this drawing mode. First is the OS doesn't know about your drawing, so it has no problem redrawing over what you did. This also means drawing does not generate refresh commands. Second there is also no clipping. This means you are free to draw anywhere. As long as you stay in your space and do not include a destructive DrawScript these are not really problems.

The screen buffer is a chunk of memory starting at $E0000000. It can be accessed simply by setting a char* pointer to this address. The data is arranged as packed 4 bits per pixel (16 grays). Pixels are ordered as if the Newton is in landscape mode with the Apple symbol on the right. Rotating does NOT rearrange the memory. The screen is 480 x 320, so the data is 240 bytes per row. The upper left pixel is at location 0, the pixel right below it is at location 240.

This pseudo function makes any pixel black:

Black (int Col, int Row)
{
  unsigned char* PixelAddress = $E0000000 + (240 * Row) + (Col / 2);

  if Col is even
    //replace high half of byte
    &PixelAddress = &PixelAddress or $F0
  else
    //replace low half of byte
    &PixelAddress = &PixelAddress or $0F
}


The hex F can be changed to other values for levels of grays. Additional offsets can be specified to use local instead of global coordinates.

When dumping a black and white bitmap to the screen for Napalm, I made a lookup table to process a byte at a time. Redrawing in C instead of NewtonScript allowed changes to appear instantly, making the emulator feel faster. The table could be regenerated for other bit depths and would stay the same size. Here is some drawing code:


  const unsigned long BITMAP_TABLE[256] =
  {
    0x00000000, 0x0000000F, 0x000000F0, 0x000000FF, 0x00000F00, 0x00000F0F, 0x00000FF0, 0x00000FFF,
    0x0000F000, 0x0000F00F, 0x0000F0F0, 0x0000F0FF, 0x0000FF00, 0x0000FF0F, 0x0000FFF0, 0x0000FFFF,
    0x000F0000, 0x000F000F, 0x000F00F0, 0x000F00FF, 0x000F0F00, 0x000F0F0F, 0x000F0FF0, 0x000F0FFF,
    0x000FF000, 0x000FF00F, 0x000FF0F0, 0x000FF0FF, 0x000FFF00, 0x000FFF0F, 0x000FFFF0, 0x000FFFFF,
    0x00F00000, 0x00F0000F, 0x00F000F0, 0x00F000FF, 0x00F00F00, 0x00F00F0F, 0x00F00FF0, 0x00F00FFF,
    0x00F0F000, 0x00F0F00F, 0x00F0F0F0, 0x00F0F0FF, 0x00F0FF00, 0x00F0FF0F, 0x00F0FFF0, 0x00F0FFFF,
    0x00FF0000, 0x00FF000F, 0x00FF00F0, 0x00FF00FF, 0x00FF0F00, 0x00FF0F0F, 0x00FF0FF0, 0x00FF0FFF,
    0x00FFF000, 0x00FFF00F, 0x00FFF0F0, 0x00FFF0FF, 0x00FFFF00, 0x00FFFF0F, 0x00FFFFF0, 0x00FFFFFF,
    0x0F000000, 0x0F00000F, 0x0F0000F0, 0x0F0000FF, 0x0F000F00, 0x0F000F0F, 0x0F000FF0, 0x0F000FFF,
    0x0F00F000, 0x0F00F00F, 0x0F00F0F0, 0x0F00F0FF, 0x0F00FF00, 0x0F00FF0F, 0x0F00FFF0, 0x0F00FFFF,
    0x0F0F0000, 0x0F0F000F, 0x0F0F00F0, 0x0F0F00FF, 0x0F0F0F00, 0x0F0F0F0F, 0x0F0F0FF0, 0x0F0F0FFF,
    0x0F0FF000, 0x0F0FF00F, 0x0F0FF0F0, 0x0F0FF0FF, 0x0F0FFF00, 0x0F0FFF0F, 0x0F0FFFF0, 0x0F0FFFFF,
    0x0FF00000, 0x0FF0000F, 0x0FF000F0, 0x0FF000FF, 0x0FF00F00, 0x0FF00F0F, 0x0FF00FF0, 0x0FF00FFF,
    0x0FF0F000, 0x0FF0F00F, 0x0FF0F0F0, 0x0FF0F0FF, 0x0FF0FF00, 0x0FF0FF0F, 0x0FF0FFF0, 0x0FF0FFFF,
    0x0FFF0000, 0x0FFF000F, 0x0FFF00F0, 0x0FFF00FF, 0x0FFF0F00, 0x0FFF0F0F, 0x0FFF0FF0, 0x0FFF0FFF,
    0x0FFFF000, 0x0FFFF00F, 0x0FFFF0F0, 0x0FFFF0FF, 0x0FFFFF00, 0x0FFFFF0F, 0x0FFFFFF0, 0x0FFFFFFF,
    0xF0000000, 0xF000000F, 0xF00000F0, 0xF00000FF, 0xF0000F00, 0xF0000F0F, 0xF0000FF0, 0xF0000FFF,
    0xF000F000, 0xF000F00F, 0xF000F0F0, 0xF000F0FF, 0xF000FF00, 0xF000FF0F, 0xF000FFF0, 0xF000FFFF,
    0xF00F0000, 0xF00F000F, 0xF00F00F0, 0xF00F00FF, 0xF00F0F00, 0xF00F0F0F, 0xF00F0FF0, 0xF00F0FFF,
    0xF00FF000, 0xF00FF00F, 0xF00FF0F0, 0xF00FF0FF, 0xF00FFF00, 0xF00FFF0F, 0xF00FFFF0, 0xF00FFFFF,
    0xF0F00000, 0xF0F0000F, 0xF0F000F0, 0xF0F000FF, 0xF0F00F00, 0xF0F00F0F, 0xF0F00FF0, 0xF0F00FFF,
    0xF0F0F000, 0xF0F0F00F, 0xF0F0F0F0, 0xF0F0F0FF, 0xF0F0FF00, 0xF0F0FF0F, 0xF0F0FFF0, 0xF0F0FFFF,
    0xF0FF0000, 0xF0FF000F, 0xF0FF00F0, 0xF0FF00FF, 0xF0FF0F00, 0xF0FF0F0F, 0xF0FF0FF0, 0xF0FF0FFF,
    0xF0FFF000, 0xF0FFF00F, 0xF0FFF0F0, 0xF0FFF0FF, 0xF0FFFF00, 0xF0FFFF0F, 0xF0FFFFF0, 0xF0FFFFFF,
    0xFF000000, 0xFF00000F, 0xFF0000F0, 0xFF0000FF, 0xFF000F00, 0xFF000F0F, 0xFF000FF0, 0xFF000FFF,
    0xFF00F000, 0xFF00F00F, 0xFF00F0F0, 0xFF00F0FF, 0xFF00FF00, 0xFF00FF0F, 0xFF00FFF0, 0xFF00FFFF,
    0xFF0F0000, 0xFF0F000F, 0xFF0F00F0, 0xFF0F00FF, 0xFF0F0F00, 0xFF0F0F0F, 0xFF0F0FF0, 0xFF0F0FFF,
    0xFF0FF000, 0xFF0FF00F, 0xFF0FF0F0, 0xFF0FF0FF, 0xFF0FFF00, 0xFF0FFF0F, 0xFF0FFFF0, 0xFF0FFFFF,
    0xFFF00000, 0xFFF0000F, 0xFFF000F0, 0xFFF000FF, 0xFFF00F00, 0xFFF00F0F, 0xFFF00FF0, 0xFFF00FFF,
    0xFFF0F000, 0xFFF0F00F, 0xFFF0F0F0, 0xFFF0F0FF, 0xFFF0FF00, 0xFFF0FF0F, 0xFFF0FFF0, 0xFFF0FFFF,
    0xFFFF0000, 0xFFFF000F, 0xFFFF00F0, 0xFFFF00FF, 0xFFFF0F00, 0xFFFF0F0F, 0xFFFF0FF0, 0xFFFF0FFF,
    0xFFFFF000, 0xFFFFF00F, 0xFFFFF0F0, 0xFFFFF0FF, 0xFFFFFF00, 0xFFFFFF0F, 0xFFFFFFF0, 0xFFFFFFFF
  };
  
  unsigned long* screenBitmap = $E0000000;

  unsigned long newtAddress = (yPixel * 240) + (xPixel * 4);

  *((unsigned long*) &(screenBitmap[newtAddress])) = BITMAP_TABLE[iByteValue];
  
  
  

Fast line and shape drawing algorithms are well known, so QuickDraw like C code should be fairly easy to produce.

Another useful option might be to make a virtual binary object and draw to it in the same organization screen drawing works. Then the very fast binary copying functions can be used on each row to place it in the screen buffer. This way if your screen area gets refreshed a fast copy is needed to restore instead of a full regeneration of the image.

Drawing is not the only thing that can be done with the screen buffer. It could also be read for use in a screen shot program. Unless the screen buffer is in DRAM, it can be seen that higher pixel count is not supported by this chipset. More than 16 grays would also not fit.