Tutorial on Using Bitmaps

Why and How

The HP9845 is a system capable of high quality graphics (at least compared to other systems of the time). The 9845C was one of the first high-performance desktop system, which brought color into the science and engineering scene. Typical applications were scientific and technical visualizations, like measure curves, color coded two-dimensional diagrams, and CAD. Another, probably less important feature, was the generation of business charts and Gantt diagrams for project planning.

Creating impressive graphics on the 9845 nearly automatically induces the whish for saving those graphics, and probably transferring them to a PC, or even to create images on the PC and transfer them for further use to the 9845 system.

One method, of course, is creating screen shots. I mean in the original sense, by taking a photograph from the screen. Actually, this is the most authentic procedure, since this is the only way capturing the "feel" of the real graphics. However, it still may be useful to recover 9845 graphics in a commonly used bitmap format. See the 9845C demo screen dump gallery for a couple of impressive examples produced on a 9845C system.

Before we can do this, a closer look to how graphics information is represented in the diverse 9845 systems is helpful.

Background: 9845 Graphics Representation

There are three different graphics systems in use within the 9845 family:

All three graphics systems share a couple of common parameters, like dot resolution (the resolution is always 560 by 455 pixels) and pixel aspect ratio. All graphics systems have their hardware installed in the CRT monitor, and each needs a special graphics option ROM for operation. The interface between the 9845 mainframe and the graphics subsystems is nearly identical, all graphics systems are controlled via the same IOD bus. Differences lie in the actual dimensions of the CRT, the number of color planes, the type of hardware acceleration and the graphics command sets which are provided with the specific graphics option ROMs.

All graphics data is held in the graphics memory, which differs in size between the monochrome display types (32 kBytes) and the color display (96 kBytes). All operations on this graphics memory is performed via graphics primitives, which are used to write words to the graphics memory, read words from the graphics memory, clear words, control the cursor and for using special hardware accelerated features like vector drawing, polygon drawing/filling, and rubber band operation. Data transfer to and from the display usually makes use of DMA for best-possible performance. All graphics operations are in the responsibility of the peripheral processor, the PPU. The language processor (LPU) only passes the graphics requests to the PPU.

For access to the graphics memory as a whole, BASIC provides two special commands, the GSTORE and the GLOAD command. These commands can be used to transfer the graphics data between the graphics memory and a sufficiently dimensioned array of integers. Each integer represents 16 horizontally aligned pixels (one bit for each pixel). In fact, GSTORE and GLOAD do not much more but copy the content of the graphics memory into the integer array and vice versa. The copy process is started at the upper left corner of the screen and is performed line by line down to the lower right screen corner or until the last integer of the array is filled, whatever happens first. So, by "underdimensioning" the integer array, the copy process can be limited to a certain area.

Monochrome Representation

Monochrome Graphics Representation

 

Color Representation

Color Graphics Representation

However, there are differences in the implementation of GSTORE and GLOAD, so depending of what type of display (standard, enhanced or color) you have, the data is stored differently into the integer array.

Standard Monochrome Display

The standard monochrome display uses a dummy integer at the end of each line. For a complete graphics image, the array must be dimensioned to 16,381 integers. Each lines holds 560 pixels, which are represented by 35 integers plus one preceding dummy integer, which makes 36 integers per line. 455 lines times 36 integers 16,380 integers, plus one positioning value makes 16,381 integers.

Standard Monochrome Graphics

Another speciality is that the first integer is used for storing a positioning value. This positioning value determines where the copy in the graphics memory starts, and where the data should be restored when transferring the data back into the graphics memory. Normally, this value should be set to -1, which indicates the copy starts at the beginning of the graphics memory. If another location is intended, the number of data words which shall be skipped when reading from or to the graphics memory plus one has to be stored as a negative (!) value into this first member of the integer array (actually, it is the 2's complement of the number of words that shall be skipped).

Example:

OPTION BASE 1
INTEGER Array(16381)
Array(1)=-(3*36+1)           ! 3 rows with 36 bytes each, plus 1
GSTORE Array(*)

skips the first three rows, and starts copying from the fourth row.

Enhanced Monochrome Display

The enhanced monochrome display doesn't use a positioning pointer, nor does it append a dummy integer after each line, so just the raw pixel data is stored. Each line is represented by 35 integers, times 455 makes exactly 15,925 integers.

Enhanced Monochrome Graphics

Instead of using a positioning pointer, the offset can be specified within the GSTORE and/or GLOAD calls, using two new parameters for starting x-column and y-row (with lower left screen corner as origin for the coordinates):

OPTION BASE 1
INTEGER Array(15925)
GSTORE Array(*)
,0,454-3

Skips the first three rows (counted from row 454 down), and starts copying from the fourth row.

Color Display

The color display doesn't use a positioning pointer or trailing dummy integers as well, but stores color data by using three color planes of 15,925 integers each, which makes a total of 47,775 integers for storage. In each line, the planes are stored interleaved, which means that the first integer represents 16 pixels of the first plane, the second integer represents the same 16 pixels of the second plane, the third integer represents the same 16 pixels of the third plane, the fourth integer represents the next 16 pixel of the first plane and so on.

Color Graphics

Note that the maximum dimension supported within BASIC is 32,767. As a consequence, there are at least two subsequent calls to GSTORE or GLOAD required to capture the whole graphics memory. Recommended is to use three arrays:

OPTION BASE 1
INTEGER Array1(15855),Array2(15960),Array3(15960)
GSTORE Array1(*)          ! Saves rows 454 down to 304
GSTORE Array2(*),0,303    ! Saves rows 303 down to 152
GSTORE Array3(*),0,151    ! Saves rows 151 down to 0

The integer array Array1 has to be dimensioned to 15,855, and Array2 & Array3 each to 15,960 integers. To save RAM space, you can also DIM a single array of size 15,960, and REDIM it appropriately before using GSTORE and saving to mass storage (see the examples in "Saving and Restoring Graphics Data" below). Note that the positioning parameter with GSTORE is only available for the enhanced and the color graphics and has to be repeated with the GLOAD statement when re-storing the image.

Graphics Data Files

Once copied into the array, the data can be saved to mass storage, or once loaded from mass storage, the array can be copied back to the graphics memory. Transfer to and from mass storage can be done in two ways. The "compatible" way is to save into a file of type DATA using the MAT PRINT and the MAT READ commands, but the much faster way is to store and load the data to and from files of type BDAT with the FPRINT and FREAD commands. Another advantage of the BDAT procedure is that it produces compact files with half the size. The single drawback ist that it won't work with tapes.

The DATA format uses two words or four bytes for each integer (one word with content hex 00A0 indicating the value as an integer, and one word for the integer data itself), and terminates the last integer with an EOR (hex 001E) marker. The BDAT format doesn't include typing information or EOR marker, and stores each integer as one word (half the space of the DATA format), however includes a 256-byte system record before the actual data starts. Both the DATA and the BDAT format fill up the graphics data with padding bytes, so that the required 256-byte record alignment is accomplished.

  9845B Standard
Graphics
9845B Enhanced
Monochrome Graphics
9845C Color
Graphics
Resolution 560 x 455 560 x 455 560 x 455
No. of Pixels 254,800 254,800 254,800
No. of Planes 1 1 3
No. Integers per Row 361) 35 1052)
Size of the Integer Array for GSTORE3) 16,381 15,925 15,855/15,9604)
Filesize in 256-byte Records (BDAT) 129 126 1264)
Filesize in 256-byte Records (DATA) 256 249 2484)
Positioning Stored in file
(first integer)
Additional GSTORE/
GLOAD Parameter
Additional GSTORE/
GLOAD Parameter

1) For 9845B standard graphics, each stored line is followed by an extra integer without special meaning, so the total storage for one line is 1 + 35 = 36 integers.

2) For the 9845C color graphics, each line is read as one integer for 16 bits of the first plane, then one integer for 16 bits of the second plane, then one integer for 16 bits of the third plane and repeating these steps until the whole line is read.

3) Subtract 1 for the INTEGER statement if OPTION BASE is 0.

4) Has to be stored in at least two separate files (maximum array size is 32,767 and FPRINT only allows saving one single array). Recommended is using three files to match the file size of monochrome graphics.

As a conclusion, we do have three types of integer array representation, and two types of storage, resulting in all together six types of graphics files.

Saving and Restoring Graphics Data

The way of saving a graphics screen is first to copy the screen content with the GSTORE command into a sufficiently dimensioned array of integers, then create a file with sufficient size, if appropriate open the file, and then write the integer array to the file with the FPRINT or the MAT PRINT command, respectively.

BDAT Example (Fast Saving, Standard Monochrome)

Here is an example for standard monochrome graphics and using BDAT files for storage (works with disk storage only):

OPTION BASE 1
INTEGER Array(16381)      ! Dimension the integer array
Array(1)=-1               ! No offset
GSTORE Array(*)           ! Copy the screen content into the array
FCREATE "Image",129       ! Create a BDAT file "Image" of size 129 records
FPRINT "Image",Array(*)   ! Write the array into the file

The image file can be re-stored to graphics memory with the following sequence:

OPTION BASE 1
INTEGER Array(16381)      ! Dimension the integer array
FREAD "Image",Array(*)    ! Read the file content into the array
GLOAD Array(*)            ! Copy the the array back into the screen

BDAT Example (Fast Saving, Color)

Here is an example for color graphics and using BDAT files for storage (works with disk storage only). Note that only one single array is used via redimensioning (good if you are short of free RAM):

OPTION BASE 1
INTEGER Array(15960)      ! Dimension the integer array
REDIM Array(15855)        ! Tailor array for first part
GSTORE Array(*)           ! Copy upper third of screen into the array
FCREATE "Img1",129        ! Create a first BDAT file "Img1" of size 129 records
FPRINT "Img1",Array(*)    ! Write the array into the file
REDIM Array(15960)        ! Expand the array for the remaining parts
GSTORE Array(*),0,303     ! Copy next third of screen into the array
FCREATE "Img2",129        ! Create a second BDAT file "Img2" of size 129 records
FPRINT "Img2",Array(*)    ! Write the array into the second file
GSTORE Array(*),0,151     ! Copy last third of screen into the array
FCREATE "Img3",129        ! Create a third BDAT file "Img3" of size 129 records
FPRINT "Img3",Array(*)    ! Write the array into the third file

The image file can be re-stored to graphics memory with the following sequence:

OPTION BASE 1
INTEGER Array(15960)      ! Dimension the integer array
REDIM Array(15885)        ! Tailor array for first part
FREAD "Img1",Array(*)     ! Read the first file's content into the array
GLOAD Array(*)            ! Copy the the array back into upper screen third
REDIM Array(15960)        ! Expand the array for the remaining parts
FREAD "Img2",Array(*)     ! Read the second file's content into the array
GLOAD Array(*),0,303      ! Copy the the array back into second screen part
FREAD "Img3",Array(*)     ! Read the third file's content into the array
GLOAD Array(*),0,151      ! Copy the the array back into remaining screen part

DATA Example (Standard Saving, Standard Monochrome)

Another, alternative approach is using DATA files (which is the only method working on tapes, but consumes more than twice the storage space and five times the time for saving to/from storage):

OPTION BASE 1
INTEGER Array(16381)      ! Dimension the integer array
Array(1)=-1               ! No offset
GSTORE Array(*)           ! Copy the screen content into the array
CREATE "Image",256        ! Create a DATA file "Image" of size 256 records
ASSIGN #1 TO "Image"      ! Open the file
MAT PRINT#1;Array         ! Write the array into the file
ASSIGN #1 TO *            ! Close the file

The image file can be re-stored to graphics memory with the following sequence:

OPTION BASE 1
INTEGER Array(16381)      ! Dimension the integer array
ASSIGN #1 TO "Image"      ! Open the file
MAT READ#1;Array          ! Read the file content into the array
ASSIGN #1 TO *            ! Close the file
GLOAD Array(*)            ! Copy the the array back into the screen

Have a look at the Mouse Graphics Demo within the 9845 Mouse Project for a sample application using standard monochrome graphics with both the BDAT (Fast Save) and the DATA (Standard) format.

Capturing Screenshots from a Running Program

In many cases it is desired to take a screenshot from a running program (like it is done with CTRL-PRINT within MS Windows). With a HP 9845, this can easily be achieved by adding the following code sequence to the program you want to document. The example uses special function key #1 to take the screenshots from a running program, and file name "X". Of course, the subroutine Dump_graphics can also be called within the program at any location:

10 ON KEY #1 CALL Dump_graphics
...
1000 SUB Dump_graphics
1010 INTEGER Array(16381)
1020 !
1030 ON ERROR GOTO Gr_next
1040 PURGE "X"
1050 Gr_next: !
1060 OFF ERROR
1070 DISP "Storing graphics..."
1080 GSTORE Array(*)
1090 FCREATE "X",129
1100 FPRINT "X",Array(*)

1110 !
1120 DISP "Done."
1130 SUBEND

And here is the appropriate example for color graphics:

10 ON KEY #1 CALL Dump_graphics
...
1000 SUB Dump_graphics
1010 INTEGER Array(15959)
1020 !
1030 ON ERROR GOTO Gr_next
1040 PURGE "X1"
1050 PURGE "X2"
1060 PURGE "X3"
1070 Gr_next: !
1080 OFF ERROR
1090 DISP "Storing graphics part 1 of 3..."
1100 REDIM Array(15854)
1110 GSTORE Array(*)
1120 FCREATE "X1",126
1130 FPRINT "X1",Array(*)
1140 !
1150 DISP "Storing graphics part 2 of 3..."
1160 REDIM Array(15959)
1170 GSTORE Array(*),0,303
1180 FCREATE "X2",126
1190 FPRINT "X2",Array(*)
1200 !
1210 DISP "Storing graphics part 3 of 3..."
1220 GSTORE Array(*),0,151
1230 FCREATE "X3",126
1240 FPRINT "X3",Array(*)
1250 !
1260 DISP "Done."
1270 SUBEND

Please change functions key, file names and line numbers as appropriate.

Some of the techniques described above are used in the Mouse Demo program, a sample application for demonstrating how to implement a drawing program with serial mouse support utilizing the graphics features of both 9845 monochrome and color graphics. See the 9845 Mouse Project for details.

Exchanging and Converting

Once stored, 9845 bitmap images can easily be transferred to a PC with the HPDrive and/or the HPDir utility(s). At the PC side, the 9845 bitmap file can be converted with the Bmpconvert utility into a bilevel or color Windows BMP file. Also, a bilevel or color BMP file can be converted into either a BDAT or DATA file with the same utility, and then transferred back to the 9845 where it can be loaded into the graphics memory. All three graphics types (standard monochrome, enhanced monochrome and color) as well as both file formats (BDAT and DATA) are supported. Note that the BMP file must have the right dimensions (560 by 455) in order to be converted into an HP image file.

Example:

bmpconvert 9845A.BDAT 9845A.bmp

creates a bilevel Windows BMP file called 9845A.bmp if 9845A.BDAT is a valid graphics save file. In turn,

bmpconvert 9845A.bmp 9845A.BDAT

creates a 9845 graphics file in BDAT format from the bilevel BMP file 9845A.bmp.

The same procedure for color data (you of course need a working 9845C for using those images) looks like the following:

bmpconvert -color SHUTT1.BDAT shuttle.bmp

creates a color Windows BMP file called shuttle.bmp if SHUTT1.BDAT, SHUTT2.BDAT and SHUTT3.BDAT are the three parts of a valid graphics save file. The program automatically looks for the second and third part in the current directory.

Bmpconvert automatically detects the file format and always tries to make the proper decision for converting, however its behavior can be customized by a number of options. Use bmpconvert without any other data to get usage information.

See the HPDrive Project, HPDir Project and 9845 Utilities Project sections for program details & downloads.

More Information

The internal graphics structures are documented in the later revisions of the "Assembly Development ROM" manual (p/n 09845-91085), which cover the low-level programming of standard, enhanced monochrome and color graphics. The manual can be downloaded from hpmuseum.net. Usage of graphics commands like GLOAD and GSTORE is documented in the appropriate manuals "Graphics ROM" (p/n 09845-91051, also available at hpmuseum.net), "Monochromatic Graphics" (p/n 09845-93050), and "Color Graphics" (p/n 09845-92051).