Discussion:
Difficult questions about HP48 hardware
(too old to reply)
Cyan
2019-01-02 04:35:25 UTC
Permalink
Hello

I'm looking for information on the way the HP48 handle its screen,
and though I have found some by collecting documentation on the topic,
it's still relatively fuzzy.

Do you know if a doc could help ?

Here is below a copy/paste from the question list (in markdown format) :



Questions on HP48
================

Screen
---------

Questions related to grey level support

#### What's the exact refresh rate of HP48 screen ?
All docs seem to mention 64 Hz,
but that sounds too "round".
Could it be 64.1 , or 63.9 ?
Where does the refresh rate is controlled ?
Is refresh rate tied to cpu speed ?

#### What is the exact refresh policy ?
Are there 64 refreshes per second, meaning the whole screen is refreshed in a single scan ?
Or is there one refresh per line, meaning there are actually 64x64=4096 refresh events ?

#### _What_ is pushing pixel data onto screen ?
Is it the cpu directly ? or another component accessing RAM ?

If cpu : I assume there is some kind of "interrupt", and the cpu capability is "reserved" during the refresh. But in such case, it's supposed to send data to some kind of bus, with some kind of address ? What would be this address ?

If dedicated component : I assume the RAM access is likely reserved by the component during the refresh, blocking the CPU. But could the CPU still work using its own registers during that period ?

#### Pixel latency
My own tests let me believe that interleaving up to 3 screens is fine.
I've read people saying that the cycle can be more lengthy, but I wasn't convinced by the results.
Is there any study on pixel fading latency ?

#### Contrast control
It's relatively straightforward to change the contrast of the screen, through a shared value in RAM.
Is it a good idea to play with this setting to generate more nuanced grey levels ? Or is there a crippling latency involved ?


Interrupts
--------------
All docs I could find provide a list of interrupts like below which does not include anything regarding screen :

- Character received by the UART
- Character placed in the UART transmit holding register
- Keyboard button down/repeat
- Timer expiry
- Low battery condition
- IR emission/receipt
- ON-key press
- VLBI (very low battery interrupt)

Now, I understand that it's possible to know which line has been (or will be) drawn thanks to RAM value stored at `LINECOUNT` address, but checking that requires some "busy loop", which is bad for battery consumption, and does not allow doing something else in between.

Is there a way to catch the `$LINECOUNT == 0` event as an interrupt ? or something equivalent ?
I suspect this topic has been solved by arcade games using 3-grey shade graphics.
Hic quondam locutus sum
2019-03-31 18:49:19 UTC
Permalink
Post by Cyan
Hello
I'm looking for information on the way the HP48 handle its screen,
and though I have found some by collecting documentation on the topic,
it's still relatively fuzzy.
Do you know if a doc could help ?
?
Questions on HP48
================
Screen
---------
Questions related to grey level support
#### What's the exact refresh rate of HP48 screen ?
All docs seem to mention 64 Hz,
but that sounds too "round".
Could it be 64.1 , or 63.9 ?
Where does the refresh rate is controlled ?
Is refresh rate tied to cpu speed ?
The timers and the screen refresh logic are all directly controlled by a crystal
oscillator in the HP48 series AFAIK. Small variations in this oscillator's speed
may cause the refresh rate to fluctuate. There is an internal counter, running
off of the clock signal from crystal oscillator, that refreshes the screen at a
constant rate after a certain amount of clock cycles have elapsed. So, assuming
that the oscillator is perfectly stable, the nominal refresh rate is 64Hz. From
the display controller's POV, the refresh rate is always 64Hz.
Post by Cyan
#### What is the exact refresh policy ?
Are there 64 refreshes per second, meaning the whole screen is refreshed in a single scan ?
Or is there one refresh per line, meaning there are actually 64x64=4096 refresh events ?
The display controller drives each row at a time. The CPU is halted for a
certain amount of time for each row of display memory, so that the display
controller can access RAM and drive a row. So, since there are 64 rows, you
could, I guess, say that there are "64x64=4096" "refresh events" per second.
But, these "refresh events" only start every 1/64th of a second and don't last
very long*.

*( "Long" is a relative term here as the display controller does contribute to a
measurable slowdown on the HP48. The slowdown is around 13% or so IIRC. )
Post by Cyan
#### _What_ is pushing pixel data onto screen ?
Is it the cpu directly ? or another component accessing RAM ?
It's the display controller which is external to the Saturn CPU on the
Yorke/Clarke SoCs.
Post by Cyan
If cpu : I assume there is some kind of "interrupt", and the cpu capability is "reserved" during the refresh. But in such case, it's supposed to send data to some kind of bus, with some kind of address ? What would be this address ?
The HALT line to the CPU is driven high when the display controller needs to
access memory to drive a row onto the display.
Post by Cyan
If dedicated component : I assume the RAM access is likely reserved by the component during the refresh, blocking the CPU.
Correct. The CPU is halted for a small amount of time when the display
controller is reading data from memory.
Post by Cyan
But could the CPU still work using its own registers during that period ?
In-between row refreshes, you have about 220 microseconds in which the CPU can
resume whatever it was doing before it was halted.
Post by Cyan
#### Pixel latency
My own tests let me believe that interleaving up to 3 screens is fine.
I've read people saying that the cycle can be more lengthy, but I wasn't convinced by the results.
Is there any study on pixel fading latency ?
I might have to get back to you on this question. The LCD pixel response time
for the original HP48 ( non-"black" screen ) G/GXs is quite high, but I don't
have any specific numbers ATM.
Post by Cyan
#### Contrast control
It's relatively straightforward to change the contrast of the screen, through a shared value in RAM.
Is it a good idea to play with this setting to generate more nuanced grey levels ? Or is there a crippling latency involved ?
In my experience temporal dithering is the best method of creating greyscale
images, especially when the temporal component is combined with randomness so
that one doesn't experience flicker.
Post by Cyan
Interrupts
--------------
- Character received by the UART
- Character placed in the UART transmit holding register
- Keyboard button down/repeat
- Timer expiry
- Low battery condition
- IR emission/receipt
- ON-key press
- VLBI (very low battery interrupt)
Now, I understand that it's possible to know which line has been (or will be) drawn thanks to RAM value stored at `LINECOUNT` address, but checking that requires some "busy loop", which is bad for battery consumption, and does not allow doing something else in between.
Is there a way to catch the `$LINECOUNT == 0` event as an interrupt ? or something equivalent ?
I suspect this topic has been solved by arcade games using 3-grey shade graphics.
Well, there is no interrupt generated by the display controller when LINECOUNT
is equal to 0. One method to mostly get around this is to configure a user
interrupt service routine which sets TIMER2 to generate an interrupt *just
before* LINECOUNT is equal to zero. After the interrupt is generated, the CPU
would have to perform some busy waiting, but it wouldn't last long.

--------------------------------------------------------------------------------

Send e-mails to "NOhp48 Sat Palephnaught Adot Morg"

( Remove the capitalized letters spelling "NOSPAM" )
Cyan
2019-04-02 21:18:16 UTC
Permalink
Thanks for the detailed answer !
It's way more accurate than anything I could find so far, and helps a lot !
Post by Hic quondam locutus sum
There is an internal counter, running
off of the clock signal from crystal oscillator, that refreshes the screen at a
constant rate after a certain amount of clock cycles have elapsed. So, assuming
that the oscillator is perfectly stable, the nominal refresh rate is 64Hz. From
the display controller's POV, the refresh rate is always 64Hz.
I read that as : the CPU and the Display are both driven by the same crystal oscillator, and therefore, ignoring potential oscillator variations, the amount of CPU cycles between 2 refresh is simply hard-wired. It should be constant, no matter what (except obviously when display is turned off).
Is this number of cycles known / documented somewhere ?
Post by Hic quondam locutus sum
The display controller drives each row at a time. The CPU is halted for a
certain amount of time for each row of display memory, so that the display
controller can access RAM and drive a row. So, since there are 64 rows, you
could, I guess, say that there are "64x64=4096" "refresh events" per second.
But, these "refresh events" only start every 1/64th of a second and don't last
very long*.
Thanks to your explanation on the display controller, I now get that the CPU gets HALTed when a line is refreshed. It lasts "a number of cycles", and I'm not sure how much. Then it can resume.

In my initial message, I feared that the CPU was actually being interrupted, meaning that it has to stop doing its stuff, save some essential registers and context information, proceed on driving the refresh operation, then restore initial state, and resume. All this context switching would have costed "something", and it would have made a difference wether it was happening 64 or 4096 times per second.

The difference between 4096 short HALT of 64 longer HALT is moot if they equal the same number of HALTed cycles. Actually, 4096 would be better, as it would spread the load more evenly.
Post by Hic quondam locutus sum
there is no interrupt generated by the display controller when LINECOUNT
is equal to 0. One method to mostly get around this is to configure a user
interrupt service routine which sets TIMER2 to generate an interrupt *just
before* LINECOUNT is equal to zero. After the interrupt is generated, the CPU
would have to perform some busy waiting, but it wouldn't last long.
Well, thanks. I've been trying exactly that strategy.
But the results were disappointing, to say the least.

Even though I'm sure I selected TIMER2 value with a _very_ conservative wait amount, basically guaranteeing that the CPU would be busy-waiting, it was still resulting in large tearing.

An important thing though : since my real HP48SX stopped working recently (I suspect a RAM corruption problem though can't be sure), I've been relying on emulator (emu48) to continue development. emu48 is said to be cycle-exact, so I had high hopes that it would be representative of a real hardware. But I guess that at that level of details, the emulation may reach its limits.

It could be that the level of tearing observed was in fact emulator-related, I could not confirm it on my real calculator.
Hic quondam locutus sum
2019-04-03 18:33:06 UTC
Permalink
Post by Cyan
Thanks for the detailed answer !
It's way more accurate than anything I could find so far, and helps a lot !
Post by Hic quondam locutus sum
There is an internal counter, running
off of the clock signal from crystal oscillator, that refreshes the screen at a
constant rate after a certain amount of clock cycles have elapsed. So, assuming
that the oscillator is perfectly stable, the nominal refresh rate is 64Hz. From
the display controller's POV, the refresh rate is always 64Hz.
I read that as : the CPU and the Display are both driven by the same crystal oscillator,
Well, there is an important distinction : The display and the timers are
*directly* driven by the crystal oscillator whereas the Saturn CPU's clock
signal is derived from a PLL which generates a high frequency multiple of the
crystal oscillator's frequency.
Post by Cyan
and therefore, ignoring potential oscillator variations, the amount of CPU cycles between 2 refresh is simply hard-wired.
Well, because of the Saturn CPU's PLL's small fluctuations ( there may be some
"spread spectrum" functionality, but I'm not sure ) with respect to the crystal
oscillator, the number of clock cycles is not going to be exactly the same for
each refresh. But, you're correct in that the refresh frequency is "hard-wired".
Post by Cyan
It should be constant, no matter what (except obviously when display is turned off).
Is this number of cycles known / documented somewhere ?
It's not known because it's not exactly the same in-between each display refresh
due to the display and timer parts of the Clarke / Yorke SoC being driven
directly by the crystal oscillator and the CPU clock, while being derived from
the same oscillator, is generated by a PLL. So, one cannot calculate an *exact*
cycle time, but only an average. Depending on one's HP48's clock frequency, the
number of cycles in 1/64th of a second can vary as the CPU clock varies between
as low as 3.69 MHz to 3.9 MHz on an HP48-G/GX ( Personally, I've never seen a
clock speed that is 4MHz or greater ).
Post by Cyan
Post by Hic quondam locutus sum
The display controller drives each row at a time. The CPU is halted for a
certain amount of time for each row of display memory, so that the display
controller can access RAM and drive a row. So, since there are 64 rows, you
could, I guess, say that there are "64x64=4096" "refresh events" per second.
But, these "refresh events" only start every 1/64th of a second and don't last
very long*.
Thanks to your explanation on the display controller, I now get that the CPU gets HALTed when a line is refreshed. It lasts "a number of cycles", and I'm not sure how much. Then it can resume.
The halt lasts 22 to 23 microseconds.
Post by Cyan
In my initial message, I feared that the CPU was actually being interrupted, meaning that it has to stop doing its stuff, save some essential registers and context information, proceed on driving the refresh operation, then restore initial state, and resume. All this context switching would have costed "something", and it would have made a difference wether it was happening 64 or 4096 times per second.
The difference between 4096 short HALT of 64 longer HALT is moot if they equal the same number of HALTed cycles. Actually, 4096 would be better, as it would spread the load more evenly.
Conservatively, the number of halted cycles, for each 1/64th of a second refresh
period, should be around 3.9e6 Hz * (22.5e-6 s * 64) = 5616 cycles, for a G/GX
at least. This is about a 10% slowdown.
Post by Cyan
Post by Hic quondam locutus sum
there is no interrupt generated by the display controller when LINECOUNT
is equal to 0. One method to mostly get around this is to configure a user
interrupt service routine which sets TIMER2 to generate an interrupt *just
before* LINECOUNT is equal to zero. After the interrupt is generated, the CPU
would have to perform some busy waiting, but it wouldn't last long.
Well, thanks. I've been trying exactly that strategy.
But the results were disappointing, to say the least.
Even though I'm sure I selected TIMER2 value with a _very_ conservative wait amount, basically guaranteeing that the CPU would be busy-waiting, it was still resulting in large tearing.
That's interesting, as I've used the same method, but without any tearing, and
so have many greyscale action games on the 48. Note that I always use
double-buffering where the new display data is written to display memory after
the refresh cycle ends.
Post by Cyan
An important thing though : since my real HP48SX stopped working recently (I suspect a RAM corruption problem though can't be sure), I've been relying on emulator (emu48) to continue development. emu48 is said to be cycle-exact, so I had high hopes that it would be representative of a real hardware. But I guess that at that level of details, the emulation may reach its limits.
It could be that the level of tearing observed was in fact emulator-related, I could not confirm it on my real calculator.
My guess is that it's emulator related.

Regards,

Hic quondam locutus sum

--------------------------------------------------------------------------------

Send e-mails to "NOhp48 Sat Palephnaught Adot Morg"

( Remove the capitalized letters spelling "NOSPAM" )
Cyan
2019-04-03 21:17:08 UTC
Permalink
Thanks for the detailed answers.
It's very clear.
You seem to have an excellent grasp of HP48 internal hardware.


It seems that my only way up, on top of finding a way to repair my trusted HP48SX,
would be to improve an existing emulator, with proper gray level support.
I'm sure it can be done, the main issue will be time availability,
as usual ...
Post by Hic quondam locutus sum
Post by Cyan
Thanks for the detailed answer !
It's way more accurate than anything I could find so far, and helps a lot !
Post by Hic quondam locutus sum
There is an internal counter, running
off of the clock signal from crystal oscillator, that refreshes the screen at a
constant rate after a certain amount of clock cycles have elapsed. So, assuming
that the oscillator is perfectly stable, the nominal refresh rate is 64Hz. From
the display controller's POV, the refresh rate is always 64Hz.
I read that as : the CPU and the Display are both driven by the same crystal oscillator,
Well, there is an important distinction : The display and the timers are
*directly* driven by the crystal oscillator whereas the Saturn CPU's clock
signal is derived from a PLL which generates a high frequency multiple of the
crystal oscillator's frequency.
Post by Cyan
and therefore, ignoring potential oscillator variations, the amount of CPU cycles between 2 refresh is simply hard-wired.
Well, because of the Saturn CPU's PLL's small fluctuations ( there may be some
"spread spectrum" functionality, but I'm not sure ) with respect to the crystal
oscillator, the number of clock cycles is not going to be exactly the same for
each refresh. But, you're correct in that the refresh frequency is "hard-wired".
Post by Cyan
It should be constant, no matter what (except obviously when display is turned off).
Is this number of cycles known / documented somewhere ?
It's not known because it's not exactly the same in-between each display refresh
due to the display and timer parts of the Clarke / Yorke SoC being driven
directly by the crystal oscillator and the CPU clock, while being derived from
the same oscillator, is generated by a PLL. So, one cannot calculate an *exact*
cycle time, but only an average. Depending on one's HP48's clock frequency, the
number of cycles in 1/64th of a second can vary as the CPU clock varies between
as low as 3.69 MHz to 3.9 MHz on an HP48-G/GX ( Personally, I've never seen a
clock speed that is 4MHz or greater ).
Post by Cyan
Post by Hic quondam locutus sum
The display controller drives each row at a time. The CPU is halted for a
certain amount of time for each row of display memory, so that the display
controller can access RAM and drive a row. So, since there are 64 rows, you
could, I guess, say that there are "64x64=4096" "refresh events" per second.
But, these "refresh events" only start every 1/64th of a second and don't last
very long*.
Thanks to your explanation on the display controller, I now get that the CPU gets HALTed when a line is refreshed. It lasts "a number of cycles", and I'm not sure how much. Then it can resume.
The halt lasts 22 to 23 microseconds.
Post by Cyan
In my initial message, I feared that the CPU was actually being interrupted, meaning that it has to stop doing its stuff, save some essential registers and context information, proceed on driving the refresh operation, then restore initial state, and resume. All this context switching would have costed "something", and it would have made a difference wether it was happening 64 or 4096 times per second.
The difference between 4096 short HALT of 64 longer HALT is moot if they equal the same number of HALTed cycles. Actually, 4096 would be better, as it would spread the load more evenly.
Conservatively, the number of halted cycles, for each 1/64th of a second refresh
period, should be around 3.9e6 Hz * (22.5e-6 s * 64) = 5616 cycles, for a G/GX
at least. This is about a 10% slowdown.
Post by Cyan
Post by Hic quondam locutus sum
there is no interrupt generated by the display controller when LINECOUNT
is equal to 0. One method to mostly get around this is to configure a user
interrupt service routine which sets TIMER2 to generate an interrupt *just
before* LINECOUNT is equal to zero. After the interrupt is generated, the CPU
would have to perform some busy waiting, but it wouldn't last long.
Well, thanks. I've been trying exactly that strategy.
But the results were disappointing, to say the least.
Even though I'm sure I selected TIMER2 value with a _very_ conservative wait amount, basically guaranteeing that the CPU would be busy-waiting, it was still resulting in large tearing.
That's interesting, as I've used the same method, but without any tearing, and
so have many greyscale action games on the 48. Note that I always use
double-buffering where the new display data is written to display memory after
the refresh cycle ends.
Post by Cyan
An important thing though : since my real HP48SX stopped working recently (I suspect a RAM corruption problem though can't be sure), I've been relying on emulator (emu48) to continue development. emu48 is said to be cycle-exact, so I had high hopes that it would be representative of a real hardware. But I guess that at that level of details, the emulation may reach its limits.
It could be that the level of tearing observed was in fact emulator-related, I could not confirm it on my real calculator.
My guess is that it's emulator related.
Regards,
Hic quondam locutus sum
--------------------------------------------------------------------------------
Send e-mails to "NOhp48 Sat Palephnaught Adot Morg"
( Remove the capitalized letters spelling "NOSPAM" )
Loading...