[comp.sys.handhelds] HP48SX display controller registers

ervin@pinbot.enet.dec.com (Joseph James Ervin) (04/05/91)

Hello fellow HP48SX hackers,

Here are my current findings on the display.  Basically, the previous
posting  on this topic by Johan Thornton back in DEC-1990 was largely
correct  (as far as I can tell), but the descriptions of locations #128h
and #129h appear to have been inaccurate, and I have added the definition of
#130h-#134h.  I have also added more information regarding the register at
#100h, and some of the other registers.

********************************************************************
 
>Pressing ON-D, backspace, enter causes the memory scanner/editor to go to
>address #00100h, which is where the HP48SX's display controller is.
> 
>
> 
>  #00100h: bits 0-1 control the 4 pixel offset of the display.  Useful for 
>           smooth scrolling.  Bit 2 is also involved here but messes up the 
>           scan length.  Bit 3 seems to put the machine into a coma. 

The "4 pixel offset of the display" is not a good description.  Bits <2:0>
of this location define the number of bits to ignore at the beginning of
each horizontal scan line.  Thus setting <2:0>=1 causes the whole display
to shift 1 pixel to the left; <2:0>=2 causes the display to shift 2 pixels
to the left, etc..  Thus, this is really an 8-pixel "scan-start offset",
defining how many pixels to skip (from 0 to 7) at the beginning of each
scan line.  It's not quite that easy, however, so read on.

Setting bit <2> when in memory scanner mode appears to "screw up" the
display, making it unreadable (the rows of pixels no longer line up
properly to form characters).  I offer the following explanation. Setting
bit <2:0>=100b, for example, tells the display controller to skip 4 pixels
at the start of each line, which it does very nicely. The problem is that
whenever bit <2> is set, the display controller also skips an additional byte
at the END of each scan line.  I'm not sure exactly _why_ this happens, but
it does.  In a graphic which can be scrolled, this is not a problem because
the overscan register (#125h-#127h, below) can be decreased by one to
compensate.  This, however, is not possible when there are no "extra" bits
in the memory pattern being skipped over.

For example, imagine your image is wider than the screen.  In that case,
the overscan register (see below) is programmed to cause the display
controller to skip over the bits on each row that exist to the right of the
current row (and to the left of the next row) of the display.  When
scrolling to the right, then, the overscan register can just be decremented
by one (when necessary) to make the horizontal scroll effect of register
#100h work.

Note, the scan-start offset register at #100h does not seem to effect the
menu portion of the display (see below).  

>  #00101h: LSBs of the 5 bit contrast control word.
>  #00102h: bit 0 is the MSB of the contrast.  Bits 1 and 3 control the
>           voltage difference across the LCD; this is what you modified.
>           Can't see what bit 2 does.
>  #00103h: bit 3 is also involved in the LCD voltage control.
> 
>  #0010Bh: bit 0,1,2,3: \<-, \->, alpha, and alarm indicators
>  #0010Ch: bit 0,1: busy and I/O indicators; bit 3 must be set for the
>           indicators to be on.

No arguments here.

>  #00120h - #00124h: display address base register.  Ususally set to #F097Ch
>           (or #F09BC with the equation card installed) but you can change it,
>           to look at any memory you want.  Note that you can't read this
>           register, just write to it.  Remember to type addresses in
>           backwards.

This should probably be renamed something like "stack display address
pointer", since there is another display pointer at #130h-#134h used to map
the menu portion of the screen.  Note that bit <0> of both of these address
pointers is ignored, so this is effectively a byte address.  In other
words, the display memory must start on a byte boundary.

>  #00125h - #00127: amount of overscan per pixel line

The author of the original post neglected to say that this is measured in
_bytes_, so <127,126,125> = 001 means skip 1 byte of memory after the end
of each scan line before starting the next scan line.  Note this is needed
for storing image data that is wider than the screeen. I suspect that this
is how the system handles the scrolling of graphics images wider than the
display.  When you scroll the plotting display, the system uses the above
mentioned pixel scan-start offset (#100h) in conjuction with the overscan,
and also modifies the address pointer at 120-124.

>  #00128h : offset to menu area bitmap, normally 7.
>  #00129h : bits 0-1 control the location of the menu area, normally 3.  Don't
>           mess with bit 2, which can totally remap your calculator's display!
>           ON-C does not fix what happens, but turning it off does, strangely
>           enough.

Locations #128 and #129 actually work together to provide a pointer to
where the menu portion of the display starts; call it the "menu row index". 
Location #128<3:0> contains the lower four bits and #129<1:0> comprise the
upper two bits.  Recall that there are 64 rows of pixels on the screen. 
Note also that this field is 6 bits wide, and 2^6=64. This field thus
provides the display controller with a pointer to the row on which the menu
portion of the screen begins.  

Numbering the screen rows 0 through 63 from the top, the display controller
causes the menu to start on the row pointed to by the menu row index
register PLUS ONE.  In other words, a menu row index of 0d causes the menu
to start at the second row from the top (row #1d by the above numbering
scheme), and a menu row index of 62d causes the menu to start at the very
last row of the display (row #63d), yielding a 1 pixel high menu.  Note that
this 6-bit field normally contains 110111b=55d, which is the row where the
menu normally resides (rows #56d-#63d).  

Notice how when viewing a plot, you can choose to have the menu area
visible or use the whole screen for the plot?  There's a way of telling the
display controller to not display the  menu by mapping it off the screen. 
In this case, the display controller fills the screen with data starting at
the "stack address pointer", #120h-#124h.   The menu can be mapped off the
screen by setting the menu row index to 111111b=63d, which points to the
"row" below the bottom row of the display.  Because of interference from
the memory scanner, it is difficult to determine whether writing a value of
0 to the menu row index actually maps the menu to row 2, or whether it
turns the menu off altogether.  Further experimentation from ML should
reveal which is actually happening.

So, the display controller actually has two sets of pointers for two areas
on the screen.  The first is the "stack address pointer" at #120h-#124h,
and the second is a "menu address pointer" at #130h-#134h.  Experimentation
has shown that when they overlap, the menu data seems to win.  It is my
belief that on each screen refresh, the display controller takes the stack
address pointer and drives that data onto the screen as directed by the
overscan and pixel offset values.  It seems that the display controller may
keep track of how far down to write the stack image based on the contents
of the menu row index registers (#128h-#129h).

The display controller then apparently writes the menu portion of the
screen, starting at the memory address pointed to by the menu address
pointer register (#130h-#134h) and at the screen row pointed to by the
contents of the menu row index register (#128h-#129h).  The display
controller writes through to the end of the screen.  Thus, you can divide
up the screen between the "stack" and "menu" portions in any proportions,
with the apparent limitation that the menu must be below the top row.  


Note that the display refresh is not program controlled.  This can be
verified by the fact that you can see my screen dissolver working as it
runs (I make no effort to update the display in my code).  I suspect that
the display controller operates in one of the following modes:


  1. Display controller is supplied with an timing signal from the hardware 
     timer (directly or indirectly through some interrupt) every 64th of a 
     second or so, at which time the display controller does DMA, reading 
     the memory and updating the display with the information.

  2. There is a generic interrupt generated by the hardware timer every 64th 
     of a second or so.  The handler for that interrupt then manually writes
     the appropriate registers to push the image data onto the display one 
     nibble at a time.  Note that this implies the existence of a display 
     "data register" which, when written, puts the written data directly on 
     the screen.  I have seen no evidence of such a register, so I am 
     skeptical as to whether this is really done.

I am guessing at the 64th of a second screen refresh based on the way it
flickers under flourescent lighting, which operates at 60 Hz. 


Of course,  I make no guarantees as to the accuracy of the above
information, although I believe it to be correct.  Still,  be sure to
verify any of the above information through your own experimentation before
using it.


>>>Joe