Jump to content
IGNORED

INTIM issue in Berzerk?


DavidEth

Recommended Posts

Okay, next question -- I've seen a lot of discussion about the various undocumented opcodes, but I didn't see a consensus on which ones are stable enough to use on 2600. Did any "professional" cartridges use undocumented opcodes, or is that mostly a homebrew thing? (For example, if you had to tell the 2601 dude which insns to implement, which ones would you do?)

 

Thanks,

 

-Dave

Link to comment
Share on other sites

Okay, next question -- I've seen a lot of discussion about the various undocumented opcodes, but I didn't see a consensus on which ones are stable enough to use on 2600.

http://www.oxyron.de/html/opcodes02.html

 

Did any "professional" cartridges use undocumented opcodes, or is that mostly a homebrew thing?

AFAIK that's a homebrew only "thing".

 

(For example, if you had to tell the 2601 dude which insns to implement, which ones would you do?)

Actually we already did, check his blog. :)

 

IMO: LAX, SBX, NOP, SAX, DCP

Link to comment
Share on other sites

The ones Thomas listed are a good start and will support most homebrews. Though one of my games uses ISB, I may be the only one.

 

IMO, an emulator or a 2600 clone should implement all illegal opcodes, though I sort of doubt anyone is using ANE, LAS, LXA, SHS, SHX, SHY, or SHA. Also, it's probably overkill to implement the strange decimal mode of ARR.

Link to comment
Share on other sites

The ones Thomas listed are a good start and will support most homebrews. Though one of my games uses ISB, I may be the only one.

 

IMO, an emulator or a 2600 clone should implement all illegal opcodes, though I sort of doubt anyone is using ANE, LAS, LXA, SHS, SHX, SHY, or SHA. Also, it's probably overkill to implement the strange decimal mode of ARR.

I'd be happy to implement all illegal opcodes in Stella, as soon as I get a consistent description of each one. Note that most (all?) of the stable illegal opcodes have been implemented, but I think some of the unstable ones haven't. Which leads to my original question; how to implement an unstable operation that isn't consistent??

Link to comment
Share on other sites

Latest version. Still don't understand why the timing on Halo 2600 PF2 access isn't quite right.

 

To facilitate my own hacking and because I felt like writing an assembler, I added an assembler to the emulator.

 

It can currently assemble and run Combat.asm from bjars after fixing a few minor syntax errors.

 

I was able to get it to disassemble Yar's Revenge; feeding it back in again didn't quite work, because it disassembled some code as data and produced an INC CXM0P,X that was really an absolute indexed instruction but on subsequent assembly it interpreted it as zero-page indexed. I fixed the assembly by using a .BYTE directive in that one case and it ran.

 

All the standard TIA names are built in (so it completely ignores PROCESSOR and INCLUDE directives). A few of the RIOT names are built in as well. Currently the collision registers are inserted at both 0 and 0x30 so that ROM's that use either method will disassemble correctly, but they'll always assemble as the 0-based ones. I can probably add a flag to force only one of the two sets to be understood.

 

Basically now with one relatively small executable, you can hack up some quick asm code and run it immediately.

 

I wouldn't be surprised if the assembler doesn't handle malformed input particularly well yet.

 

-Dave

my2600src.zip

Link to comment
Share on other sites

Another version -- almost makes it through a lightly modified pitfall.asm (added several more pseudo operations, but I had to hack the assembly in a few places because my IF/ELSE/ENDIF support and general expression parsing is really crude).

 

The resulting assembled game doesn't work properly, still tracking down some differences, but it exposed a bigger issue -- several of the 0x?1 opcodes were typed in wrong; I had zero_page_indexed_x in twice when one of them was supposed to be preindexed_indirect_x. The loads and stores were correct, but ADC, AND, EOR, SBC, ORA, CMP were all wrong.

 

I'm not sure if this fixes anything, since I imagine those were fairly uncommmon opcodes. I finally spotted it when an assembly produced incorrect results (albeit in something that was just supposed to be data anyway).

 

-Dave

my2600src.zip

Link to comment
Share on other sites

Did you just refer to ADC, ORA, CMP, etc. as uncommon? I wouldn't say that...they are used in virtually every program (definately in any game program). Or were you calling the indirect-x addressing mode uncommon? No argument there.

 

 

Looking at pitfall.asm, what is the <[sym1 - sym2] syntax? I know < means the lower byte of the value, but why does it use square brackets instead of parentheses?

I can't remember, but I don't think there was a special reason.

 

GroundTypeTab right near the end of the program. It's checking the offset of the rom addresses (sym1) based on distance from a single starting point (sym2). In this case, using that offset as a match to the value held in X. So using square brackets might have been a reminder or something that the program was doing this?

Edited by Nukey Shay
Link to comment
Share on other sites

Yeah, I meant the indirect indexed form of those particular opcodes was uncommon.

 

But that actually fixed more than I expected -- the fuel gauge in River Raid seems correct, and the player's red laser in Laser Blast works now as well. Halo 2600 is still wacky though, unfortunately. Must be more bugs lingering.

 

I have most of the remaining pitfall.asm divergences accounted for -- a couple were due to my code resolving the tia read registers at $00 instead of $30, and some code was subtracting $30 (which turn it into a RAM address instead of a collision register). The remainder were just due to the crudeness of my expression parser, which I'm now rewriting.

 

(I know I'm veering off target here, but I'm having fun. Don't worry, I expect to start coding proper 2600 stuff soon. My first game is going to be "2600 B.C.", the world's crappiest civ game)

 

-Dave

Link to comment
Share on other sites

New version with nicer expression parsing (moved into a header to avoid some clutter); understands basic C operator precedence. Can now compile pitfall.asm with only the following modifications:

 

1. Remove the "MAC" macro definition (although I added FILL_NOP as a pseudo operation for the hell of it)

2. The assembler will complain about the three local labels that were used multiple times. Fix them so they're globally unique.

 

The end result has a disassembly identical to the original ROM as seems to play fine.

 

My assembler also ignores the SEG, SEG.U, and SUBROUTINE directives.

 

EDIT - Oh yeah, I added a "-0" flag which tells the code to resolve the TIA read symbols at 0x00 instead of 0x30. Doing so breaks pitfall.asm, which must be based on some nonstandard version of VCS.H since they're supposed to default to 0 in that header.

 

-Dave

my2600src.zip

Edited by DavidEth
Link to comment
Share on other sites

Whatever is wrong with Halo2600 is at least partly a collision issue -- I used debug keys to turn off player0 graphics (and therefore his collisions) and was able to move around freely (the ball is used to render the player's helmet, so I was able to figure out where to go).

 

I suspect I'm not handling the wraparound case correctly on the right -- currently any sprite pixels that go off the right edge will immediately appear on the left, at which point they're colliding with the playfield that is used to render the left-hand wall.

 

Anybody happen to know at what point the counters really wrap around at, since 160->0 doesn't seem quite correct?

 

EDIT: Clearing the player serial registers once I reach color cycle 160, combined with forcing the HMOVE registers to wrap around at 168 instead of 160 seemed to be both necessary and sufficient to fix the collision issue. The player sprite himself still has some bizarre rendering problems where his horizontal timing shifts strangely on certain parts of the screen. Presumably some of my insn timings are still off or something.

 

-Dave

Edited by DavidEth
Link to comment
Share on other sites

Whatever is wrong with Halo2600 is at least partly a collision issue -- I used debug keys to turn off player0 graphics (and therefore his collisions) and was able to move around freely (the ball is used to render the player's helmet, so I was able to figure out where to go).

 

I suspect I'm not handling the wraparound case correctly on the right -- currently any sprite pixels that go off the right edge will immediately appear on the left, at which point they're colliding with the playfield that is used to render the left-hand wall.

 

Anybody happen to know at what point the counters really wrap around at, since 160->0 doesn't seem quite correct?

 

EDIT: Clearing the player serial registers once I reach color cycle 160, combined with forcing the HMOVE registers to wrap around at 168 instead of 160 seemed to be both necessary and sufficient to fix the collision issue. The player sprite himself still has some bizarre rendering problems where his horizontal timing shifts strangely on certain parts of the screen. Presumably some of my insn timings are still off or something.

 

-Dave

 

I suspect your problem is being caused by not handling the cycle 74 HMOVE correctly. You know the HMXX registers are interpreted differently in that case right? (I think someone referred you to the table to use in an earlier post.) Hope this helps,

 

-EdF

Link to comment
Share on other sites

Anybody happen to know at what point the counters really wrap around at, since 160->0 doesn't seem quite correct?

Hmm, seems correct to me. You mean 160 == 0, right?

 

Yes.

 

On a related note, time to play "spot the emulating timing bug" with Halo 2600:

 

$F353 STA WSYNC             A=00 X=30 Y=06 S=1C [ V I  ] 5625 (97,18)
$F355 LDA ($92),Y [f0b5]    A=00 X=30 Y=06 S=1C [ V I  ] 5647 (-65,19) ; 5 cycles (no crossing)
$F357 STA GRP0              A=7E X=30 Y=06 S=1C [ V I  ] 5652 (-50,19) ; 3
$F359 STX PF0               A=7E X=30 Y=06 S=1C [ V I  ] 5655 (-41,19) ; 3
$F35B LDA ($D0),Y [f10c]    A=7E X=30 Y=06 S=1C [ V I  ] 5658 (-32,19) ; 5 (no crossing)
$F35D STA COLUP0            A=C4 X=30 Y=06 S=1C [sV I  ] 5663 (-17,19) ; 3
$F35F LDA $F0C4,Y           A=C4 X=30 Y=06 S=1C [sV I  ] 5666 (-8,19)  ; 4 (no crossing)
$F362 STA ENABL             A=00 X=30 Y=06 S=1C [ V IZ ] 5670 (4,19)   ; 3
$F364 LDA $83               A=00 X=30 Y=06 S=1C [ V IZ ] 5673 (13,19)  ; 3
$F366 LDX #$1E              A=57 X=30 Y=06 S=1C [ V I  ] 5676 (22,19)  ; 2
$F368 TXS                   A=57 X=1E Y=06 S=1C [ V I  ] 5678 (28,19)  ; 2
$F369 CMP $8C               A=57 X=1E Y=06 S=1E [ V I  ] 5680 (34,19)  ; 3
$F36B PHP                   A=57 X=1E Y=06 S=1E [ V I C] 5683 (43,19)  ; 3
$F36C CMP $8B               A=57 X=1E Y=06 S=1D [ V I C] 5686 (52,19)
$F36E PHP                   A=57 X=1E Y=06 S=1D [ V I C] 5689 (61,19)
$F36F LDA $A7               A=57 X=1E Y=06 S=1C [ V I C] 5692 (70,19)
$F371 STA PF0               A=00 X=1E Y=06 S=1C [ V IZC] 5695 (79,19)
$F373 LDA #$80              A=00 X=1E Y=06 S=1C [ V IZC] 5698 (88,19)
$F375 STA HMP0              A=80 X=1E Y=06 S=1C [sV I C] 5700 (94,19)
$F377 STA HMM0              A=80 X=1E Y=06 S=1C [sV I C] 5703 (103,19)
$F379 STA HMM1              A=80 X=1E Y=06 S=1C [sV I C] 5706 (112,19)
$F37B STA HMBL              A=80 X=1E Y=06 S=1C [sV I C] 5709 (121,19)
$F37D NOP                   A=80 X=1E Y=06 S=1C [sV I C] 5712 (130,19)
$F37E PLA                   A=80 X=1E Y=06 S=1C [sV I C] 5714 (136,19)
$F37F STA HMOVE             A=80 X=1E Y=06 S=1D [sV I C] 5718 (148,19) ; **** is this the culprit?
$F381 LDA $A6               A=80 X=1E Y=06 S=1D [sV I C] 5721 (157,19) ;
$F383 STA PF0               A=30 X=1E Y=06 S=1D [ V I C] 5724 (-62,20) ; 3
$F385 PLA                   A=30 X=1E Y=06 S=1D [ V I C] 5727 (-53,20)
$F386 PLA                   A=00 X=1E Y=06 S=1E [ V IZC] 5731 (-41,20)
$F387 JMP $F2A4             A=00 X=1E Y=06 S=1F [ V IZC] 5735 (-29,20)
$F2A4 DEC $83               A=00 X=1E Y=06 S=1F [ V IZC] 5738 (-20,20) ; 5
$F2A6 LDA $A7               A=00 X=1E Y=06 S=1F [ V I C] 5743 (-5,20)
$F2A8 STA PF0               A=00 X=1E Y=06 S=1F [ V IZC] 5746 (4,20)
$F2AA LDA #$08              A=00 X=1E Y=06 S=1F [ V IZC] 5749 (13,20)
$F2AC CMP $83               A=08 X=1E Y=06 S=1F [ V I C] 5751 (19,20)
$F2AE BNE $F256             A=08 X=1E Y=06 S=1F [sV I  ] 5754 (28,20) ; 3 - taken, no crossing
$F256 LDA $83               A=08 X=1E Y=06 S=1F [sV I  ] 5757 (37,20)
$F258 SEC                   A=56 X=1E Y=06 S=1F [ V I  ] 5760 (46,20)
$F259 SBC $80               A=56 X=1E Y=06 S=1F [ V I C] 5762 (52,20)
$F25B ADC #$0C              A=F9 X=1E Y=06 S=1F [sV I  ] 5765 (61,20)
$F25D BCC $F2BA             A=05 X=1E Y=06 S=1F [   I C] 5767 (67,20) ; 2, not taken
$F25F TAY                   A=05 X=1E Y=06 S=1F [   I C] 5769 (73,20)
$F260 LDA ($92),Y [f0b5]    A=05 X=1E Y=05 S=1F [   I C] 5771 (79,20)
$F262 LDX $A6               A=5B X=1E Y=05 S=1F [   I C] 5776 (94,20)
$F264 STA WSYNC             A=5B X=30 Y=05 S=1F [   I C] 5779 (103,20)

 

There's a weird gap between scanlines 19 and 20 (The last three numbers are the cpu cycle count, color cycle, and scanline number). Looking over the code, it doesn't do a WSYNC here, so it's possible that my emulation is slightly off on one instruction, but which one? I'm printing out the ZPY address so I can check if there's a page crossing. The cpu cycle count is the count before the instruction is executed, so you need to subtract the next line down to get the actual amount consumed.

 

All the counts do seem correct, so maybe hitting HMOVE is the culprit? It's hitting HMOVE at color cycle 148, with 0x80 in HMP0, so it looks like it's going to move it either 0 or 8 pixels.

 

I guess I need to implement the logic implied by http://www.bjars.com/resources/hmove.txt to get this correct. I need to dig through the old posts and figure out whether the cycles mentioned there are relative to -68 (the start of the scanline in color cycles) or cycle 0; seems weird that the table starts at 10.

 

-Dave

Link to comment
Share on other sites

I suspect your problem is being caused by not handling the cycle 74 HMOVE correctly. You know the HMXX registers are interpreted differently in that case right? (I think someone referred you to the table to use in an earlier post.) Hope this helps,

 

-EdF

 

So would that be this line of the table?

 

74 -8 -9 -10 -11 -12 -13 -14 -15 0 -1 -2 -3 -4 -5 -6 -7 **

 

I assumed the table was in color cycles, not cpu cycles.

 

Looks like you're trying to only move player 1 at this point, which I think is the trees. I assumed that was just a normal duplicated sprite, but are you hitting HMP1 there to have more control over the tree horizontal positions?

 

-Dave

Link to comment
Share on other sites

I suspect your problem is being caused by not handling the cycle 74 HMOVE correctly. You know the HMXX registers are interpreted differently in that case right? (I think someone referred you to the table to use in an earlier post.) Hope this helps,

 

-EdF

 

So would that be this line of the table?

 

74 -8 -9 -10 -11 -12 -13 -14 -15 0 -1 -2 -3 -4 -5 -6 -7 **

 

I assumed the table was in color cycles, not cpu cycles.

 

Looks like you're trying to only move player 1 at this point, which I think is the trees. I assumed that was just a normal duplicated sprite, but are you hitting HMP1 there to have more control over the tree horizontal positions?

 

-Dave

yes, cpu cycles. I do early HMOVEs to avoid getting the ugly black line on the left side of the screen that you get with a normal HMOVE, (this is for repositioning Player 1, it gets used 3 times on each screen) but I have to use a different HMXX table (the one you give above) and I have to set all the other HMXX registers (HMP0, HMM0, HMM1, and HMBL to 0x80) or those objects will move when I issue the HMOVE command.

 

I'm enjoying watching your progress. You've done a lot in just a few days. Can't wait to see what you come up with for a game.

 

-EdF

Link to comment
Share on other sites

Transcribing that table and fudging the offsets got the player working in Halo 2600, although the visor isn't matching up because I only applied the rules to P0 and P1 since the table mentioned HMPx at the top. Should I be using the same table for everything, or just the player and ball?

 

Nevermind, looks like I should apply it to everything, Halo 2600 looks pretty good now.

 

EDIT: Latest version, fixed HMOVE mid-scanline handling. Good to know that hitting HMOVE in the middle of the scanline is useful for avoiding the ugly margin bar. Also added a scanline debugger on PgUp/PgDn and Home/End which causes instructions only for the current scanline(s) to be dumped out in the trace window.

 

EDIT 2: Also, when I implemented HMOVE properly, I was able to take out the other two hacks to avoid weird collision issues on Halo 2600.

 

Trying to think what's left here -

 

1. I'm pretty sure I don't have VDELP0/1 logic quite right yet -- the score in Yar's Revenge and the humans in Stargate both have weird bugs. Probably also explains the one-pixel gaps in Activision logos and River Raid fuel gauge.

 

2. Sound, obviously.

 

3. Hitting HMOVE and/or RESP0/1 in the middle of a scanline doesn't work right yet for Galaxian-style sprite duplication.

 

For item 3, if I understand things correctly -- if you hit RESP0/1, it doesn't take effect until the next scanline, UNLESS you're using "multiple copies" mode and you haven't picked up one of the second copies yet? So you could set your mode to two copies, then hit RESP0 on the previous scanline to set the first copy horizontal position, then hit RESP0 again multiple times on the current scanline to scan out more copies? But then how do you get the original position set again?

 

-Dave

my2600src.zip

Edited by DavidEth
Link to comment
Share on other sites

Okay, time to try to get my VDELP0/1 logic exactly correct.

 

Here's how I handle writes to GRP0:

case GRP0:
	if (tia_state[VDELP0] & 1)
		grp0_delayed = val;
	else
		grp0_current = val;
	if (tia_state[VDELP1] & 1)
		grp1_current = grp1_delayed;
	break;

 

When the player horizontal motion counter goes to zero, I load the serial register with grp0_current.

 

Does this logic seem correct? It mostly works, but there are some bugs that indicate I'm missing an edge case. Should that grp1_current = grp1_delayed actually *exchange* the two registers, by chance?

 

-Dave

Link to comment
Share on other sites

I wrote an Early HMOVE Demo that might help you debug your HMOVE issues. If your routine is working then the sprites should line up vertically on either side of the horizontal black line between them.

 

Nice! Although I have to implement the 3-cycle NOP now... looks like it's instruction 4.

 

In macro.h, the syntax is "NOP 0", is that correct? What is the 0 supposed to mean?

 

Wait -- lemme guess, that's the address it will issue a bogus read to, since it's really a "zero page direct" opcode?

 

-Dave

Edited by DavidEth
Link to comment
Share on other sites

I wrote an Early HMOVE Demo that might help you debug your HMOVE issues. If your routine is working then the sprites should line up vertically on either side of the horizontal black line between them.

 

While that demo doesn't work on my emulator at all, I clicked through your avatar picture and tracked down MedievalMayhem and added 32k ROM support for it.

 

It pretty much works (except for the gaps in the text, and no paddle support, and that VDELP0 "smear" bug)!

 

My emulator was spewing "riot_read - unexpected read from 9" and after setting a breakpoint in my emulator and digging around, I finally found this code:

 

.notDead2
       lda #7            ; draw king shape, a player still lives
       .byte $2c
.bothDead
       lda #15           ; don't draw king shape, they're both dead

 

Issue a BIT to a dummy address to skip the lda #15 insn, neat trick. Just happens to resolve to a bogus address but it's ultimately harmless. I haven't done any serious 6502 programming since the early 1980's, it's pretty fun to dig back into this stuff again.

 

-Dave

 

EDIT: Add support for NOP x instruction (I call it NP3 in the assembler although I just realized I don't really need to do that). Added 32k ROM support, and cleaned up the bank switching code so that it doesn't do a stupid parallel compare on every access.

my2600src.zip

Edited by DavidEth
Link to comment
Share on other sites

Looking at pitfall.asm, what is the <[sym1 - sym2] syntax? I know < means the lower byte of the value, but why does it use square brackets instead of parentheses?

 

DASM uses square brackets for arithmetic grouping. Ordinary parentheses will also work for arithmetic grouping in some contexts, but will be interpreted as special addressing modes in other contests. I tend to just use square brackets in DASM instructions whether or not it's necessary in any particular context.

 

For example: if FOO is $4321 and BAR is $1234, "JMP [FOO-BAR]" will be regarded as "JMP $30ED", while "JMP (FOO-BAR)" will be assembled as "JMP ($303D)", a different instruction.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...