Sega Master System / Mark III / Game Gear|
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew
Light phaser code/algorithmPosted: Fri Feb 15, 2019 8:45 pm
I've been looking at some games' light phaser code and the official Sega docs, and I'm finding it quite confusing.
The docs say that the TH signal is pulsed for 20-30us when the gun sees light. This seems not to be a feature of the gun, so presumably it's the VDP (or IO chip?) analysing the gun signal and denoising/edge detecting/pulse generating - but if so, why only 20us? That's only ~70 CPU cycles. Why not set a per-line flag?
The docs also say that the H position is latched when the edge is seen. Therefore the code must poll TH, grab the H and V positions, and the values returned will trace the left edge of a circle "seen" by the light sensor. The game must then calculate where it thinks the gun is pointing (the middle of the circle) based on this.
So I disassembled Missile Defense 3D. It does a few interesting (and possibly unexplained) things:
1. It polls TH much faster than the docs say is needed. Every 25 cycles (7us), in fact. This doesn't leave time for it to ever exit the polling loop, so it does some funky call stack rewriting in the frame interrupt to terminate the loop.
2. It has what look like delay opcodes between each time it gets triggered, so it won't see points on the same line - but then it also uses the V count to discard points seen on the same line
3. It discards the first two lines it sees
4. It seems to compute the X position as the mean of the points it sees, which ought to mean it mis-tracks to the left of the cursor
5. It scales and offsets the values it gets from the H counter, suggesting the values don't map to the pixel count
It's possible this is just horrible code that happens to come out with a reasonable result because one part (the skew to the left) cancels the other (the scaling/offsetting) in normal use cases. Certainly when I played it a few years ago on a small CRT it seemed OK.
Averaging might be reasonable if the TH signal was a raw "do I see light" signal, and you polled really fast, because then you'd aim to sample points all over the circle and averaging them will be sensible. But the code is careful to ignore multiple points on the same scanline.
I also had a look at Porkopolis. This seems to act entirely differently (if my understanding is correct):
1. It only checks TH in the HBlank interrupt
2. If low, it grabs the H and V counts
3. It then doubles the H count and subtracts 32 to map it to pixel space
If the TH signal is only held low for 30us, then I don't see how this can work on a real system (where each line is ~63us) - but forum posts suggest it does. Can someone confirm that?
Does anyone have any other insight? I thought maybe I should have a look at another game, maybe Missile Defense 3D is not "normal". I am thinking of making a test ROM, but I don't actually have any hardware to test it with.
||Posted: Sat Feb 16, 2019 9:57 am|
|I can't speak for the MS version, but I've been doing some C64 light gun coding. There is a delay in the signal, once you press the trigger and/or it sees the light beam, the signal takes time to travel down the cable. While on say the pad, this amount of time in tiny and trivial, when you are measuring the pixel clock, its a significant delay. And such you will get a H reading that is further to the right than where the user is aiming. Also hands are shaky and need to be factored in.|
||Posted: Sat Feb 16, 2019 10:37 am|
|That's certainly true, and I expect the alignment of sensor is going to be fairly dodgy, but I guess I still need to make a test ROM to collect the raw data from a real system to see what the gun really "sees".|
||Posted: Wed Feb 20, 2019 2:29 pm|
Every filter in between the SMS and light gun input adds delay too. It's not a direct connection from light sensor to an input as shown by the schematic that someone drew in a previous post : http://www.smspower.org/forums/files/lightphaser3050_145.png
It's interesting to see how different games use the light gun.
||Posted: Wed Feb 20, 2019 9:21 pm|
|I'm hoping to find a standard function in several games... but it's clearly not a simple copy paste job.|
Posted: Tue Feb 26, 2019 10:02 am
Last edited by Maxim on Wed Feb 27, 2019 10:22 am; edited 2 times in total
Here's a commented/reformatted/refactored version of the sample code in the Sega docs for reading the light phaser...
1. Waits for the trigger to be pressed
2. Blanks the screen to white
3. Polls TH, waiting for it to be 0
4. Captures the H and V counts to a buffer (unless the buffer is full)
5. Polls TH, waiting for it to be 1, before going back to 3
6. When the frame is up, it tries to rewrite the stack (but I think it's buggy...)
7. It then looks through the data it captured and segments it into runs of H, V pairs where the V counts are <3 lines apart
8. It selects the longest run and then the H, V in the middle of that run
9. If H >= $a0, the gun is not pointing at the screen
10. Else X = (H - 22) * 33/16 and Y = V - 1
The code seems untested and the algorithm seems oddly tolerant of missing lines or triggers on the same line, tries quite hard to deal with "seeing" multiple things (which I don't see how it is possible to do), and then totally fails to try to compute a valid X coordinate anyway.
||Posted: Tue Feb 26, 2019 9:51 pm|
I disassembled Marksman Shooting / Trap Shooting / Safari Hunt and it actually uses almost exactly the same code as the official docs sample above. The byte sequence for the part where it scales the H-counter to an X coordinate is found in these games:
* Hang On & Safari Hunt
* Hang On & Safari Hunt [BIOS]
* Marksman Shooting & Trap Shooting & Safari Hunt
* Marksman Shooting & Trap Shooting.sms
...suggesting they all use the same code as their source.
Posted: Wed Feb 27, 2019 9:38 am
Last edited by Maxim on Wed Feb 27, 2019 12:23 pm; edited 2 times in total
|Gangster Town is almost identical, except it subtracts 24 from the H count before scaling it up and it rounds the result to a multiple of 8 pixels.|
||Posted: Wed Feb 27, 2019 9:55 am|
Similar code (subtracting 22 from the H counter) with a single opcode optimisation (sla a -> add a, a) is in:
Assault City [Light Phaser]
||Posted: Wed Feb 27, 2019 10:15 am|
Laser Ghost is similar again, but with some changes to the code:
* It does not rewrite the stack to break the polling loop, instead it uses the V count to decide to stop polling the gun
* It reads the H and V counts after seeing a 1 -> 0 -> 1 transition on TH, the earlier code captures them on the 1 -> 0 transition - but as I understand it this ought to work much the same.
* It subtracts 27 from the H count instead of 22
* Then it clamps the result to the range 0..112
* It applies the same scaling, then subtracts another 16 at the end
* It takes Y = V + 4
So still no intelligent way to compute X, just (different) fudge factors which result in an X offset of about 5.7 pixels.