VOGONS


Software for the IBM PC Light Pen?

Topic actions

First post, by GloriousCow

User metadata
Rank Member
Rank
Member

Having a hard time tracking down any software that utilized a light pen on the IBM PC. Anyone aware of any titles? Extra points if it's actually available somewhere.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 1 of 30, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie

I don't know of any other than the ones I wrote myself some years ago while playing with one I had made myself: https://github.com/reenigne/reenigne/tree/mas … 88/cga/lightpen . The hardware didn't work very well, but the I think the software was correct. It should be easy to build as a .com file if you change "defaults_bin" to "defaults_com". I have since acquired a couple of commercially produced light pens so I should try those out.
I didn't implement it in these test programs, but I think the way that light pens are expected to work is that you press a button on them to register a "click", which causes the software to flash the screen to fully white for a frame (which can be done without modifying VRAM - set the palette register to 15 and the mode register to +GRPH-ENABLE-1BPP) as the light pen wouldn't be able to detect when the CRT beam passes if the image is dark.

Reply 2 of 30, by GloriousCow

User metadata
Rank Member
Rank
Member
reenigne wrote on 2023-09-03, 07:07:

I don't know of any other than the ones I wrote myself some years ago while playing with one I had made myself: https://github.com/reenigne/reenigne/tree/mas … 88/cga/lightpen . The hardware didn't work very well, but the I think the software was correct. It should be easy to build as a .com file if you change "defaults_bin" to "defaults_com". I have since acquired a couple of commercially produced light pens so I should try those out.
I didn't implement it in these test programs, but I think the way that light pens are expected to work is that you press a button on them to register a "click", which causes the software to flash the screen to fully white for a frame (which can be done without modifying VRAM - set the palette register to 15 and the mode register to +GRPH-ENABLE-1BPP) as the light pen wouldn't be able to detect when the CRT beam passes if the image is dark.

Kinda like the NES Zapper.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 3 of 30, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
GloriousCow wrote on 2023-09-03, 15:07:

Kinda like the NES Zapper.

Kind of, except that if I remember correctly the NES zapper didn't actually find the position of sensor using the timing of the raster beam - it replaced the target with a white square on a black background for a frame when you pressed the trigger, and just recorded whether a light pulse was received (hit) or not (miss). So you could hit the target every time by pointing the zapper at a light bulb!

Reply 4 of 30, by superfury

User metadata
Rank l33t++
Rank
l33t++

In my emulator I just cheated the light system a bit. When the CRT beam passes the light pen's position (when pointing at the screen, which is the location the user points it at a location on the last rendered frame essentially), it will simply record said position. If the user isn't pointing, it simply won't latch instead. So that basically makes it able to record the position even if the screen isn't bright enough for the point it's pointing at.

The button on the other hand is just a direct live binary value sent to the card itself (1 for depressed or not, don't remember which way around it was (1 for pressed or 1 for not pressed)).

So basically just a 'better' light pen input without the weakness of the light pen itself (which was that it requires the position on the screen to be bright enough to detect the CRT raster beam to latch). UniPCemu will simply latch whenever the beam coordinates passes the light pen's position (if pointed at the screen of course, otherwise it'll simply be at a negative screen position (thus never latching)).

As for how UniPCemu maps the light pen, it differs on the used host. Hosts with mice will be able to use the right and left mouse buttons when not in direct mode. Pressing the right button points to a place on the screen. Pressing the left button while the right button is pressed presses the button.

Touch devices have an alternative input as well: the top centered 1/3 (centered horizontally) screen (originally the middle mouse button) will enable the lightpen mode (which can be observed by the input mode indicator changing). Then touching a place on the screen sets the location of the light pen (just like the right mouse button on mouse-based PCs). While touching a location to point, touching with another finger will press the button.

The system is pretty simple: touching the top 1/3ths center area of the screen (think splitting vertically and horizontally into 3 parts (producing a 3x3 grid), the 0,1 (0-based) location of the grid becomes the toggle to enable two finger pointing/button inputs for two other fingers.
A nice thing that UniPCemu supports with this is that only the starting location of the pressed finger is recorded (basically it's using the text-based layer button inputs for that, just like the buttons in the bottom right corner of the screen). So you can move the finger out of the way to reach other locations with your two other fingers, as only the starting position of the touch matters.

Although you can't press the button without setting a location using any of the supplied input methods (one requiring the right mouse button and the other a touch together with touching the top-center 1/3ths of the window/screen).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 5 of 30, by GloriousCow

User metadata
Rank Member
Rank
Member
superfury wrote on 2023-09-04, 02:05:

In my emulator I just cheated the light system a bit. When the CRT beam passes the light pen's position (when pointing at the screen, which is the location the user points it at a location on the last rendered frame essentially), it will simply record said position. If the user isn't pointing, it simply won't latch instead. So that basically makes it able to record the position even if the screen isn't bright enough for the point it's pointing at.
...

That's all well and good but how do you test it if there's no software for it?

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 6 of 30, by superfury

User metadata
Rank l33t++
Rank
l33t++
GloriousCow wrote on 2023-09-04, 13:15:
superfury wrote on 2023-09-04, 02:05:

In my emulator I just cheated the light system a bit. When the CRT beam passes the light pen's position (when pointing at the screen, which is the location the user points it at a location on the last rendered frame essentially), it will simply record said position. If the user isn't pointing, it simply won't latch instead. So that basically makes it able to record the position even if the screen isn't bright enough for the point it's pointing at.
...

That's all well and good but how do you test it if there's no software for it?

I remember validating it with reenigne's lightpen program at least. It ran without issue.
Although it doesn't verify the button, but that should work (it's a live input from the user mouse/finger touch status). Simply know that my app sets the bit and clears it when pressing/releasing the button. Although I don't remember if it was inverted in the hardware when the CPU reads it from the CGA or not (it's a simple 1-bit flag). Afaik until the readout by the CPU (which might invert (XOR 1) it) it's directly reading the input flag, which had been tested to work. It matches the CGA documentation at least.

Edit: Yup. I verified it already:
Re: CGA light pen strobe and latching?

So basically, MS-DOS itself displays the button status (as it's text mode cursor changes based pn it for some reason) and reenigne's lightpen program tests the latching behaviour.

UniPCemu literally handles the EGA in the same way as the CGA (unless EGA documentation tells otherwise. It's been a while, so I don't remember much about the EGA side of it). The basic lightpen circuitry on EGA and CGA is using the exact same code, so it should work properly.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 8 of 30, by VileR

User metadata
Rank l33t
Rank
l33t

^ Interesting... nice to know!

I'm positive that I encountered an obscure free/shareware/PD game from the early 1980s which had the option for light pen input; it was a simple game that ran in text mode - a "row swap" puzzle thing IIRC. I know that doesn't help much, but I'm out of town and won't be able to dig up the actual game til next month... so maybe that rings a bell for someone else who can.

OTOH it's very likely that the game was written in BASIC; so in terms of testing, it may not reveal anything beyond what you'd get with BASICA's built-in light pen support.

[ WEB ] - [ BLOG ] - [ TUBE ] - [ CODE ]

Reply 11 of 30, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmm... INT 10h AH=04h: http://vitaly_filatov.tripod.com/ng/asm/asm_023.5.html

ripsaw8080 wrote on 2023-09-06, 08:37:

Sargon III supports light pen. It uses function 4 of INT 10h to read the pen, so perhaps it would be useful to test around that function of the BIOS.

Since CGA 2BPP is a multiple of 4 and 1BPP is a multiple of 8, does that mean the CGA essentially reads VRAM pretty much just like a VGA does (it also loads caches 4(x2, high values unused) and 8(x4, 3 upper bytes unused?) pixels in those modes respectively)?

Edit: Hmmm... The bbcode for the url part between vitaly and filatov fails somehow? It's not a normal _ character it seems.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 12 of 30, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote on 2023-09-06, 17:52:

Since CGA 2BPP is a multiple of 4 and 1BPP is a multiple of 8, does that mean the CGA essentially reads VRAM pretty much just like a VGA does (it also loads caches 4(x2, high values unused) and 8(x4, 3 upper bytes unused?) pixels in those modes respectively)?

Kind of but also not. The CGA's VRAM bus is 8 bits wide, so it's loading the next byte while streaming the current byte's pixels to the screen. But the CRTC's addresses are counted in characters which are always 2 bytes on CGA. So the light pen address is actually only accurate to 8 pixels in 2bpp mode or 16 pixels in 1bpp mode.

Reply 13 of 30, by superfury

User metadata
Rank l33t++
Rank
l33t++
reenigne wrote on 2023-09-06, 18:56:
superfury wrote on 2023-09-06, 17:52:

Since CGA 2BPP is a multiple of 4 and 1BPP is a multiple of 8, does that mean the CGA essentially reads VRAM pretty much just like a VGA does (it also loads caches 4(x2, high values unused) and 8(x4, 3 upper bytes unused?) pixels in those modes respectively)?

Kind of but also not. The CGA's VRAM bus is 8 bits wide, so it's loading the next byte while streaming the current byte's pixels to the screen. But the CRTC's addresses are counted in characters which are always 2 bytes on CGA. So the light pen address is actually only accurate to 8 pixels in 2bpp mode or 16 pixels in 1bpp mode.

What about the first pixel of a scanline/screen? It needs those two bytes together, but if it can only load 1 byte, isn't the attribute incorrect in that case?

At what clock tick is that second 'streaming' byte loaded?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 14 of 30, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie

You're right - I mis-stated. The shift registers are loaded every character, not every byte. So while it's loading a byte from VRAM it's actually streaming out the pixels from two bytes previous. And in text modes, both character and attribute are loaded before the character starts.

Reply 15 of 30, by superfury

User metadata
Rank l33t++
Rank
l33t++
reenigne wrote on 2023-09-07, 10:36:

You're right - I mis-stated. The shift registers are loaded every character, not every byte. So while it's loading a byte from VRAM it's actually streaming out the pixels from two bytes previous. And in text modes, both character and attribute are loaded before the character starts.

So on the first clock of a scanline it needs one or two bytes buffered already (and starts loading the first byte of the next character clock) in text mode?

When are those two bytes loaded exactly? What if the last clock was the clock before horizontal total (before the new scanline) and active display?
Is the 'second' (attribute) byte for the text modes loaded at each half clock (4th pixel clock of a character clock)? What about the order (character vs attribute)? Low(character), then high(attribute) or the other way around (jumping address in multiple of 2)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 16 of 30, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote on 2023-09-07, 10:50:

So on the first clock of a scanline it needs one or two bytes buffered already (and starts loading the first byte of the next character clock) in text mode?

When are those two bytes loaded exactly? What if the last clock was the clock before horizontal total (before the new scanline) and active display?
Is the 'second' (attribute) byte for the text modes loaded at each half clock (4th pixel clock of a character clock)? What about the order (character vs attribute)? Low(character), then high(attribute) or the other way around (jumping address in multiple of 2)?

The CGA is continually loading bytes from VRAM and turning them into pixels, even during overscan. And the CRTC is continually generating addresses. The "display enable" output from the CRTC controls whether the CGA displays character/pixel data or overscan. This signal is delayed so that the first character displayed on a scanline corresponds to "column 0" in terms of the addresses that the CRTC generates.

Loading a byte isn't something that happens on a specific pixel - it's a whole process that lasts about 4 hdots. The row address and column address are sent to the VRAM, then the output is copied into a latch. The timings of these things may be slightly different in the four different fetches that can occur on any 16-hdot cycle.

I'm not actually sure if the CGA loads the character or attribute first, now you mention it. I had been assuming character, but it's not actually very easy to tell from the schematic (and not observable from software either - at least not easily). I think the most direct way would be to look at the signals on the CGA card with an oscilloscope.

Reply 17 of 30, by superfury

User metadata
Rank l33t++
Rank
l33t++
reenigne wrote on 2023-09-07, 12:30:
The CGA is continually loading bytes from VRAM and turning them into pixels, even during overscan. And the CRTC is continually g […]
Show full quote
superfury wrote on 2023-09-07, 10:50:

So on the first clock of a scanline it needs one or two bytes buffered already (and starts loading the first byte of the next character clock) in text mode?

When are those two bytes loaded exactly? What if the last clock was the clock before horizontal total (before the new scanline) and active display?
Is the 'second' (attribute) byte for the text modes loaded at each half clock (4th pixel clock of a character clock)? What about the order (character vs attribute)? Low(character), then high(attribute) or the other way around (jumping address in multiple of 2)?

The CGA is continually loading bytes from VRAM and turning them into pixels, even during overscan. And the CRTC is continually generating addresses. The "display enable" output from the CRTC controls whether the CGA displays character/pixel data or overscan. This signal is delayed so that the first character displayed on a scanline corresponds to "column 0" in terms of the addresses that the CRTC generates.

Loading a byte isn't something that happens on a specific pixel - it's a whole process that lasts about 4 hdots. The row address and column address are sent to the VRAM, then the output is copied into a latch. The timings of these things may be slightly different in the four different fetches that can occur on any 16-hdot cycle.

I'm not actually sure if the CGA loads the character or attribute first, now you mention it. I had been assuming character, but it's not actually very easy to tell from the schematic (and not observable from software either - at least not easily). I think the most direct way would be to look at the signals on the CGA card with an oscilloscope.

I've been working a bit on my emulation of the CGA/MDA adapters (they use shared code).
Now VRAM is always in VGA-compatible 'byte' mode for the CGA/MDA adapters.
The memory now is addressed in byte addresses by the CPU.
The renderer doesn't use 'word' addresses anymore in text mode. Instead it uses byte mode addresses (to walk through VRAM one byte address at a time). Then every on the half character clock, it loads different kinds of data in text mode (the first half clock of a scanline will be latched(character data), the second half clock reloads the shift registers based on it (the character) and uses the byte read at that clock for the attribute). Graphics mode will either skip it each other clock (monochrome mode, still loading every 8 pixels) or load the shift register as usual (4 color graphics mode) each half clock (thus every 4 pixels).

Although text mode now renders properly with the latest bugfixes, graphics mode seems to be buggy now (at least running Ultima 2's intro).

Edit: Fixed that 40 column text mode issue. Ultima II seems to have black blocks interleaved now, somehow (every other character clock a black block is inserted on all scanlines)? Every 4 pixels it seems? Hmmm...
Perhaps something specific to 4-color graphics mode that's affecting this in some way? As thinking in VGA rendering basics with the latches unchanged.

Filename
1757-Ultima2_halfscreenweirddoubling_20230907_2207.png
File size
5.11 KiB
Downloads
No downloads
File comment
Weird doubling of 4 or 8 pixel units.
File license
Fair use/fair dealing exception

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 18 of 30, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
reenigne wrote on 2023-09-07, 12:30:

I'm not actually sure if the CGA loads the character or attribute first, now you mention it. I had been assuming character, but it's not actually very easy to tell from the schematic (and not observable from software either - at least not easily).

After looking at the schematics and the timing diagrams I made a while ago, I'm now pretty sure that the character is loaded first. But in looking into whether it's possible to determine this from software or not, I've hit against another oddity. In each 16-hdot CGA cycle, the timing logic generates four CAS signals (which is correct because we can access four bytes of VRAM in that cycle) but only two RAS signals. This isn't normally a problem since the row address (CPU address bits A1-A7, CRTC address bits MA0-MA6) is going to be the same for both character and attribute. But in the +HRES CPU access case that means we'll use the CPU's row address for both the character and the attribute. In other words, during CGA snow both the character and attribute are affected, not just the character as I had previously thought. Half of the address bits that the attribute is fetched from will be correct, though. So the attribute will be loaded from the correct 256-byte page, but not necessarily the right location within that page.

Reply 19 of 30, by superfury

User metadata
Rank l33t++
Rank
l33t++
reenigne wrote on 2023-09-07, 21:48:
reenigne wrote on 2023-09-07, 12:30:

I'm not actually sure if the CGA loads the character or attribute first, now you mention it. I had been assuming character, but it's not actually very easy to tell from the schematic (and not observable from software either - at least not easily).

After looking at the schematics and the timing diagrams I made a while ago, I'm now pretty sure that the character is loaded first. But in looking into whether it's possible to determine this from software or not, I've hit against another oddity. In each 16-hdot CGA cycle, the timing logic generates four CAS signals (which is correct because we can access four bytes of VRAM in that cycle) but only two RAS signals. This isn't normally a problem since the row address (CPU address bits A1-A7, CRTC address bits MA0-MA6) is going to be the same for both character and attribute. But in the +HRES CPU access case that means we'll use the CPU's row address for both the character and the attribute. In other words, during CGA snow both the character and attribute are affected, not just the character as I had previously thought. Half of the address bits that the attribute is fetched from will be correct, though. So the attribute will be loaded from the correct 256-byte page, but not necessarily the right location within that page.

I've just managed to get the CGA/MDA implementation to run correctly again.
Now the text mode loading mechanism is only 2 byte fetching, with the first byte being fetched on the 4th cycle of the previous clock (4 cycles before horizontal total or vertical total for the first active display pixels). That value is latched in the character generator at that time instead of during the first clock of active display. When the time comes to actually read the first pixel (on the first clock of the active display starting), it's takes the latched pixel of the previous clock (4 (out of the 8 clocks of the character to display, 40 column mode and double pixels (double width CGA mode) double this previous clock count) clocks ago) as the character code for the character ROM lookup and the byte read on the first pixel as the attribute.
The graphics modes don't perform like this. They just fetch on the first out of 8 VRAM pixels (for monochrome graphics mode) and every 4 VRAM pixels (for 4-color graphics modes) that need rendering (so pixel clocks 0, 4, 8, 12, 16 etc. of active display. Pixel doubling will double the 4 pixel count to 8 pixel clocks (as they're only rendered at half the speed due to that)).
Basically, the 4-color graphics mode ticks like the text modes, but instead of preloading the character clock on the last clock it doesn't preload anything and just renders 4 pixel chunks.

The hidden layer of CGA emulation and compatiblity is still done using VGA registers (which drive most of the VRAM to pixel renderings), although it has various CGA/MDA modifications added to it to provide a full CGA/MDA implementation (which is fully functional and working properly afaik).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io