-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FX1E and VF #2
Comments
I have read multiple COSMAC VIP manuals several times already and it seems to me that there were valid cases where One such case would be I'm currently developing an emulator in Java and I made BTW. Wikipedia has some info but it may be inaccurate at times. I did an edit recently regarding the stack (VIP had 16 levels originally but the article mentioned 24, because 48 bytes could hold 24 2byte words) |
@metteo, thanks for the reply. I'm not sure if I'm misunderstanding you, or you're misunderstanding me, but I wrote above that I'm aware that My question is specifically whether the instruction Sorry if I was unclear. I have also edited the Wikipedia article several times recently under my old handle "Spug" ;) |
Yep, I understood it and wanted to add some context (hopefully useful). |
Well, I can say that the Chip48 source and SC10 source do this (from sc10 source line 1186+)
And the sc1.1 disassembly suggests nothing has changed here (00A48 in sc10, 00B50 in schip). Rather importantly, you'll note that to perform the overflow test, it uses the instruction retcs (return if the carry bit is set), which will be the case if it increments to 0x1000. However, returning from the instruction handler with the carry bit set causes the interpreter to immediately exit on all HP48 versions, note label retloc in both source codes:
I tested and verified this behaviour with an hp48 emulator & binaries from this repository with this simple chip8 program:
Which exits out after the appropriate number of button pushes, as opposed to any not-crashing behaviour. So uh, yeah, it would appear that anything that's depending on that working is gonna have a bad time - vF is the least of one's concerns. This behaviour also exists in the save/load from memory instructions. I suppose it's a good thing the I pointer is not incremented correctly in C48, SC10 or SCHIP, as if so it would be impossible to read from 0xFFF as it would step I over the top and cause an exit. So, uh, someone says Spacefight! 2091 relies upon this? Maybe it relies upon a different quirk and wires got crossed? Do we uh, have a known good copy of the code for that game? Oh, of course we do, good ol hpcalc.org. The included readme gives a date of 16/9-92, and schip 1.1 was released mid 91 so we can assume it was intended to work with that, maybe? The game does appear to work only with 1.1 on the calculator, although, well, there appears to be a lot of problems with the game? eg shooting the leftmost enemy doesn't seem to work correctly. Attempting to run the game with SCHPC, my quirks-modified version, causes the game to never progress, so, it appears to rely on something. That's an hp48 object in that zip though so you'll want to drop the 48 50 48 50 34 38 2D 4D 2C 2A 50 70 01 from the start. Perhaps some better investigation into this game is in order? |
Good news! So, if one plays the original SpaceFight! 2091: http://johnearnest.github.io/Octo/index.html?key=fFpIZemw It has a very obvious bug where if you shoot eg Enemy 2 and then shoot Enemy 1, Enemy 2 will be accidentally be redrawn, causing a graphic error of the playfield. Other things can litter the playfield and render levels broken/unwinnable, when the enemies are moving down etc. This error occurs because, the collision handler operates as follows. When the player bullet or enemies have moved, we scan for collisions. This enemy scan loop is performed around 0x052A, but it only considers the result if v5 is set to 1 (the code pulls double duty to just draw the enemies). Effectively what happens is:
Effectively it's designed so that the only thing the enemy being drawn in Step 3 should collide with is a bullet. However, the issue arises because, while it checks if an Enemy is already defeated in Step 3, the test performed in Step 4 still occurs regardless. To my eyes, nothing in the loop deliberately clears vF. So, as vF is always set when erasing the player's bullet, with a successful hit of Enemy N, vF remains set when we scan enemy N+1. Thus, if Enemy N+1 is already defeated, Step 3 is passed over, but Step 4 still tries to 'undraw' the enemy, ultimately dumping it into the display buffer. Since the player's bullet was already consumed defeating Enemy N, the last draw will be the player's score being updated, which will leave vF empty, ending the chain. Additionally, if any other thing in the code has set vF, then the first enemy can be impacted in the same way. So, going back to the suggested quirk: The process of selecting which enemy is being considered involves I += v9, specifically the one at 0x0532. If this cleared vF, as the quirk suggests would be the case, then the aftermath of any previous hit would no longer be considered. To verify this, I jumped to the end of the program, cleared vF, and then jumped back, and the bug no longer occured. So, the important component here is that this game does appear to expect something to clear vF. However, it would appear that no version of Superchip that I have makes any attempt at interacting with vF. This game bug would not occur if I += vX specifically clears vF, rather than sets it on overflow. For some conjecture, I suppose if someone implemented a chip8 interpreter and considered vX and I to be registers, I could believe that they might accidentally link vY += vX and I += vX, and have vF set as would be appropriate in that case. But I can't see any evidence that superchip would do this. It's possible that it expects some other command to clear vF, but, there's not a lot of choices. I've attached an Octo cartridge of my reverse engineered SpaceFight with the two bugs I found fixed (the game infinite loops if you shoot the enemy the same frame it tries to shoot you) I feel this may be outside the scope of the HP48 superchip implementations, but, it does appear to have some truth to it, and have some relevance to CHIP-8 history. |
Here's Octo code describing the loop in question. The whole process by which collisions are detected is rather naive with many memory read/writes - all that should really have been needed was to see if the bullet draw hit and figure it out with a little math. With the code in this format, hopefully that will help us draw some conclusions on what's going on here.
|
Really interesting as always! Thank you for the detective work. I feel like we should be able to solve this ancient Wikipedia mystery once and for all, but it seems the answer doesn't lie on the HP48. Perhaps David Winter's CHIP-8 interpreter does something different here, and that became the basis for this game's behavior? His interpreter seems similar to CHIP48 in many ways, but of course some bugs might have snuck in. |
Man, reading comprehension once again yields many important clues. For example, reading that spacefig.doc file again, which is included in the archive from the HP calc website:
So, most likely there is a quirk in the game author's own Amiga interpreter. It is not a quirk/feature of superchip per source code/disassembly, and it is not a quirk/feature of the VIP interpreter either, if you (@tobiasvl) are correct (sadly, http://laurencescotford.co.uk/ appears to be down?). However, it could be uncertain if it is instruction FX1E (I += vX) setting vF, or if 7XNN (vX == NN) might be responsible for clearing vF. Either one of these being true would fix the game's issues, I believe. The draw position is always shifted down by 9px (v7 += 0x09) earlier in this subroutine, and the loop contains eg v8 += 1. Unless I am mistaken, either instruction would be a candidate. I don't know if it's possible to be sure either way at this time, unless that Amiga interpreter is found. The wiki note appears to be unsourced, but, I'm not sure if that was always true. Wiki edit history is not my area of expertise, though. If you are curious if the quirk could be inferred from other code in the game, the only other uses of vF are related to the player/enemy bullets. The collision check is correctly gated in both cases, so vF will always be as expected, and no conclusion can be drawn. Here's octo code describing those:
and
One important note: this game was not written for the HP48 interpreters, and was likely never tested on that platform by the author, it does depend on several superchip quirks, specifically bit shifting ignoring vY, and I remaining static on save/load. Given it was made in '92, one could guess that that the author would have tried other superchip games of that era. Perhaps implementing a vF = Carry quirk for FX1E or 7XNN, and seeing if well known games from that time suffer ill effects could hint at which instruction would be the best candidate.
I must say, I actually thoroughly enjoyed pulling the game apart and learning the approach the developer, Slammer (Carsten Soerensen) took, and the enemy sprites are great. Thank you for bringing this to my attention. |
Nice!
I'm not sure if the author of the game has his own Amiga interpreter, but it seems there is one from as early as 1990, made by Paul Hayter.
And thank you for the thorough sleuthing. |
To set my own mind at ease, I figured I'd validate that 7XNN did not affect vF on any superchip version, even though we know this game targets the schip 1.1 instruction set due to scrolling.
That is the code for instruction 7XNN, which adds a byte directly to a register, in superchip 1.0's source code. This clearly does not interact with vF in any special way. This is the code for the 7XNN instruction disassembled from superchip 1.0:
and the same routine in superchip 1.1:
Superchip 1.1 actually has a custom savecarry instruction, at 003C7 that gave me some trouble in the past. However, it is only called by certain ALU ops (and all uses of it are preceeded by a call to ALUsetup, indicating it is a command in an #XY# format. In any case, the code didn't change, and again there's no extra interaction with vF in any version I can find, and, just playing the game on the calculator exhibits the bug pretty plainly if you look for it. |
Anyway, I took a day to sort of I guess reflect on what I was thinking and, well, I think I want to reconsider some of my statements. I mistakenly thought the author of the game had written an Amiga interpreter, rather than an assembler (a tool that works with more human readable assembly code). So now, things are pretty unclear. I guess I don't know 100% which interpreter was targeted by SpaceFight! 2091. But, consider the following:
The game does not work at all without these instructions and quirks - you can't get out of the title screen or fire a bullet, even. Whatever interpreter it was played with, it would have to be contemporary to 1992, and which exhibited those features/bugs. We have to acknowledge that perhaps it was played with SCHIP 1.1 on an HP48. Maybe the author diligently tested and played it over idk a weekend and had fun with it. Maybe they always played the game the same way, killing enemies from left to right, or only occasionally saw the bug. Maybe he knew a byte to change from 5 to 1, to have only one enemy to fight, to quickly progress through the game and test everything worked. Like this dude had an Amiga and perhaps a calculator? Maybe it just slipped by him? While we can look at the code now and say 'yes of course, this would be completely* fixed if only X was true!' - but... maybe it was just a bug with the game? I mean, maybe he even fixed it like 20 minutes after sharing whatever version we have now and that other version didn't get saved. Probably shouldn't put some guy's 2 day hobby project on like a shrine. |
The Wikipedia article on CHIP-8 claims that
FX1E
should setVF
to 1 if I overflows from0x0FFF
and 0 if it doesn't.This clearly is not how
FX1E
behaves in CHIP-8 on the COSMAC VIP (see disassembly); it will let I go all the way up to0xFFFF
. However, I see that CHIP-48 actually does check for an overflow from0x0FFF
(although it doesn't seem to touchVF
).The article says it's an undocumented feature that is relied upon by the game Spacefight! 2091, which was made for SCHIP (not sure which version), so is it conceivable that this is another incompatibility?
The text was updated successfully, but these errors were encountered: