Jump to content
IGNORED

Propoasal for alternate implementation of "emulation exit"


Andrew Davie

Recommended Posts

Currently, PlusCart looks for simultaneous joystick-right + reset to exit from cart emulation back to the PlusCart menu.

This works fine if the game being played happens to read the joystick AND the reset switch.  Most do. Some don't.

 

I have a proposal, which I think will work but would like to throw up for discussion.  The idea is to make joystick-right+reset work for ALL games.

Actually, I don't really like that combination, and would prefer reset+select together.  But in any case, the method would be the same.

 

I believe that nearly all (not all, but 99.9%) of games will use INTIM for timing frames.

That is, each frame there will be at least one wait loop that will look something like this....

 

wait   lda INTIM
       bne wait

Now, as we know, the PlusCart (STM) is looking at the address bus, and serving appropriate data, and the 6507 is happily incrementing its PC accordingly.  So, first the "lda" which is opcode $a5 if memory serves correctly... and that gets served to the 6507 bus when the PC gets to "wait" and 6507 is looking for the opcode.  Immediately after that, the PC is incremented and the bus address goes to $284 (INTIM).  At this point, PlusCart (I believe) doesn't service that address because it's handled by the 2600 hardware itself.  And then the PC increases and we once again serve data (in this case the bne).

 

My idea is to first detect (only ONCE per frame, so the PlusCart keeps a track of how-long-since)...   to detect the "lda INTIM" (or ldx/ldy) -- basically any access to INTIM.

 

Once detected, of course we allow the 6507 to continue, but (1) save the PC of the next instruction (which in this case just happens to be a "bne" but it could be anything - we don't care).  Now, instead of serving the "bne" to the 6507 data bus, we hijack things, and effectively create a "shadow" ROM containing the following...  While we're in the INTIM handler, so to speak, instead of serving the correct ROM data to the '2600, we instead serve the following...  "lda SWCHB", followed by "jmp <addr>" where <addr> is the previously saved PC address (at (1) above).

 

At the point just after the "lda SWCHB", the PlusCart can look at the data bus to detect the state of the RESET+SELECT switch (or, with a slightly more complex implementation the RESET+joystick-right combination), and detect an emulation exit.

 

But, let's say it's NOT an emulation exit, so the 6507 has just executed the "lda SWCHB" and now it's asking for the next PC address (which since the PlusCart is still in its hijacking mode will be the "jmp" followed by "addr" as a 16-bit word, as the PC increments and 6507 requests the address.  So note, that this PC address is the address of the *original* "bne" (or whatever instruction was there in the original code).

 

At this point, the PlusCart switches out of "hijack" mode, and back to normal services.  The 6507 will request the instruction, and PlusCart will service it as per normal.

 

Now, at the start I mentioned "once per frame" -- the idea is that we don't want to keep hijacking while inside a type INTIM loop.  In fact, we'll only hijack one of the INTIM loops in a frame (because PlusCart is timing to only do this once/frame maximum).  It could even be changed to only do it once/second. Or whatever. But I digress.

 

So the actual cost (to a game) will be once per frame, execution of two extra instructions at the very start of what's usually a fairly long wait loop.  Specifically, rather than the "bne", the 6507 will execute "lda SWCHA" followed by "jmp <addr>" followed by the original "bne".  In other words, an extra 7 cycles.  And, as noted... perhaps once per frame, or once per second. Whatever makes sense.

 

 

So, that's it in a nutshell.  Not knowing the system well enough, perhaps this won't work.  I'm throwing it out there for discussion. Shoot it down, as you wish!

 

 

 

 

 

 

Edited by Andrew Davie
Link to comment
Share on other sites

Using "Select" + "Reset" as exit signal would make the exit function easier to implement, and it would work with more ROMs, we should discuss that with @batari, because he want's to implement the same exit function with the Harmony cart. So we have a consistent exit function on all multicarts.

 

4 hours ago, Andrew Davie said:

Now, as we know, the PlusCart (STM) is looking at the address bus, and serving appropriate data, and the 6507 is happily incrementing its PC accordingly.  So, first the "lda" which is opcode $a5 if memory serves correctly... and that gets served to the 6507 bus when the PC gets to "wait" and 6507 is looking for the opcode.  Immediately after that, the PC is incremented and the bus address goes to $284 (INTIM).  At this point, PlusCart (I believe) doesn't service that address because it's handled by the 2600 hardware itself.  And then the PC increases and we once again serve data (in this case the bne).

Injection code into INTIM or VSYNC might be possible, but it is a much bigger effort and I am not sure if we could squeeze it into every bankswitching emulation function.

 

4 hours ago, Andrew Davie said:

My idea is to first detect (only ONCE per frame, so the PlusCart keeps a track of how-long-since)...   to detect the "lda INTIM" (or ldx/ldy) -- basically any access to INTIM.

 

...

 

Now, at the start I mentioned "once per frame" -- the idea is that we don't want to keep hijacking while inside a type INTIM loop.  In fact, we'll only hijack one of the INTIM loops in a frame (because PlusCart is timing to only do this once/frame maximum).  It could even be changed to only do it once/second. Or whatever. But I digress.

 

So the actual cost (to a game) will be once per frame, execution of two extra instructions at the very start of what's usually a fairly long wait loop.  Specifically, rather than the "bne", the 6507 will execute "lda SWCHA" followed by "jmp <addr>" followed by the original "bne".  In other words, an extra 7 cycles.  And, as noted... perhaps once per frame, or once per second. Whatever makes sense.

The PlusCart's (and UnoCart's and I suppose the Harmony's too) emulation routines don't know what a frame is, they don't even know what a second is. We would have to use some counter or timers to implement this (in some emulations like DPC+ the internal timer is already in use). Some bankswitching routines are already full of code and exceptions and I am afraid that one more mint leaf will blow them up.

 

 

Link to comment
Share on other sites

I have a couple of questions:

 

1) Has anyone tested the track ball hacks for early exit? It is very likely they would trigger as the trackball could be resting in a state where the bit for right would be low. Here are some bit patterns for the trackball.

 

https://atariage.com/forums/topic/243453-atari-2600-trak-ball-games/?do=findComment&comment=3342740

 

8 hours ago, Andrew Davie said:

I believe that nearly all (not all, but 99.9%) of games will use INTIM for timing frames.

That is, each frame there will be at least one wait loop that will look something like this....

 


wait   lda INTIM
       bne wait

 

Most do for sure. There are a lot of homebrews the use TIMINT though, and at least some of the old era games that I can't remember which right now.

 

I've also seen some game use mirrors of these addresses, and the mirrors in some cases have different behavoir for the RIOT.

 

https://atariage.com/forums/topic/202736-create-roms-to-determine-correct-intimtimint-behaviour/?do=findComment&comment=2598805

 

 

 

Link to comment
Share on other sites

1 hour ago, Omegamatrix said:

I have a couple of questions:

 

1) Has anyone tested the track ball hacks for early exit? It is very likely they would trigger as the trackball could be resting in a state where the bit for right would be low. Here are some bit patterns for the trackball.

 

https://atariage.com/forums/topic/243453-atari-2600-trak-ball-games/?do=findComment&comment=3342740

the exit will be triggered, if the last SWCHA value bit 7 is (or was) not set and the reset switch is pressed.

 

I have experienced some (rare) situations when the last joystick position read in the game loop was right, and the  joystick is not read at the title screen, then the reset button triggers the exit function at the title screen too.

 

So maybe using "Select" + "Reset" might be a safer (and easier) solution.

Link to comment
Share on other sites

6 hours ago, Al_Nafuur said:

The PlusCart's (and UnoCart's and I suppose the Harmony's too) emulation routines don't know what a frame is, they don't even know what a second is. We would have to use some counter or timers to implement this (in some emulations like DPC+ the internal timer is already in use). Some bankswitching routines are already full of code and exceptions and I am afraid that one more mint leaf will blow them up.

 

Couple of points here.

 

1) If you look for usage of VSYNC, then you don't need a counter or timer. Just every time VSYNC is set for the start of TV frame, then obviosly you know it's a new frame. Easy.

 

2) You are not adding code to the ROM - you are "injecting" fake code. And you're not touching the bankswitching routines at all.  So I don't understand the concern about "full of code and exceptions".

Nevermind. I see you mean the PlusCart routines. Well, for me it's not a strong argument, because those routines are *already* checking for joystick-right+RESET and this isn't that much different in complexity.

 

 

 

 

Edited by Andrew Davie
Link to comment
Share on other sites

3 hours ago, Omegamatrix said:

Most do for sure. There are a lot of homebrews the use TIMINT though, and at least some of the old era games that I can't remember which right now.

 

ALL games will be triggering VSYNC, though.  A less perfect place to do it, but maybe that's the best place to hijack exit-detection.

 

Link to comment
Share on other sites

On 11/7/2020 at 11:41 PM, Andrew Davie said:

Couple of points here.

 

1) If you look for usage of VSYNC, then you don't need a counter or timer. Just every time VSYNC is set for the start of TV frame, then obviosly you know it's a new frame. Easy.

 

2) You are not adding code to the ROM - you are "injecting" fake code. And you're not touching the bankswitching routines at all.  So I don't understand the concern about "full of code and exceptions".

Nevermind. I see you mean the PlusCart routines. Well, for me it's not a strong argument, because those routines are *already* checking for joystick-right+RESET and this isn't that much different in complexity.

 

 

 

 

Yes using "Select" + "Reset" would even reduce some of this complexity, and even injecting fake code isn't adding much of complexity to the functions, because it would be done in an extra internal loop like it is done in the DPC+ emulation case here:

https://gitlab.com/firmaplus/atari-2600-pluscart/-/blob/dev/source/STM32firmware/PlusCart/Src/cartridge_emulation_dpcp.c#L329

 

But I am not sure that we always and in every ROM have the extra 6502 CPU Cycle needed to add our code?

 

Also I am not sure that the debounce used in most ROMs will block or only delay our detection of "Select" + "Reset"?

Link to comment
Share on other sites

42 minutes ago, Al_Nafuur said:

Yes using "Select" + "Reset" would even reduce some of this complexity, and even injecting fake code isn't adding much of complexity to the functions, because it would be done in an extra internal loop like it is done in the DPC+ emulation case here:

https://gitlab.com/firmaplus/atari-2600-pluscart/-/blob/dev/source/STM32firmware/PlusCart/Src/cartridge_emulation_dpcp.c#L329

 

But I am not sure that we always and in every ROM have the extra 6502 CPU Cycle needed to add our code?

 

Also I am not sure that the debounce used in most ROMs will block or only delay our detection of "Select" + "Reset"?

 

I'm most of the way through implementing for 3E+, just to test it out.

I'm hijacking the VSYNC (=0 only), so it should/will not affect with extra cycles, because that VSYNC set is nearly always bracketed by WSYNC - in other words, there's usually a lot of time there. I'll get it working (on 3E+ anyway) to confirm the principle, and then we can look at if it is plausible for other bankswitch schemes.  In any case, it's just 7 cycles, so in areas where VSYNC/WSYNC are used, it's likely there's time.

 

 

Link to comment
Share on other sites

7 hours ago, Omegamatrix said:

What about simply holding RESET for a couple of seconds, with nothing else used?

 

6 hours ago, Andrew Davie said:

Seems like a good solution. Relatively easy to implement and easy for user.

 

 

As I stated before, the emulation routines don't know what a frame or a second is.

We might setup a counter and check if "Reset" is pressed for a certain amount of checking loops continuously. Lets say 100 times, this might be 2 seconds for an NTSC ROM (1,66 for PAL), if the ROM is checking the Reset-switch every frame! But I think most debouncing routines will prevent this.

 

On 11/7/2020 at 11:43 PM, Andrew Davie said:

 

ALL games will be triggering VSYNC, though.  A less perfect place to do it, but maybe that's the best place to hijack exit-detection.

 

All games and demos that want to display something "useful" at the screen will use it. But e.g. BitChess, when it is "thinking" it's not using VSYNC?

 

 

Link to comment
Share on other sites

1 minute ago, Al_Nafuur said:

 

 

As I stated before, the emulation routines don't know what a frame or a second is.

We might setup a counter and check if "Reset" is pressed for a certain amount of checking loops continuously. Lets say 100 times, this might be 2 seconds for an NTSC ROM (1,66 for PAL), if the ROM is checking the Reset-switch every frame! But I think most debouncing routines will prevent this.

 

All games and demos that want to display something "useful" at the screen will use it. But e.g. BitChess, when it is "thinking" it's not using VSYNC?

 

 

 

Yes I understand the timing and need to check reset.

But my comment was in the context of the "code injection" implementation I am working on.

It knows how many frames have gone by, because it injects on VSYNC.  I'll put it up after the next release.

I'm not sure about your concerns regarding debouncing. Let's cross that bridge later.

 

You are correct that bitChess is not using VSYNC during thinking. But I can fix that with a dummy access. At worst, BitChess will not exit emulation when thinking.

 

 

 

Link to comment
Share on other sites

37 minutes ago, Andrew Davie said:

You are correct that bitChess is not using VSYNC during thinking. But I can fix that with a dummy access. At worst, BitChess will not exit emulation when thinking.

Just tested, and the TV/'2600/BitCHess is quite happy with a VSYNC access in the thinking period.

 

Link to comment
Share on other sites

On 11/10/2020 at 3:06 AM, Al_Nafuur said:

 

As I stated before, the emulation routines don't know what a frame or a second is.

We might setup a counter and check if "Reset" is pressed for a certain amount of checking loops continuously. Lets say 100 times, this might be 2 seconds for an NTSC ROM (1,66 for PAL), if the ROM is checking the Reset-switch every frame! But I think most debouncing routines will prevent this.

Most games do check reset and select every frame. Typically the older games just perform the same reset routine, clearing ram, etc... while reset is pressed. The better programmed games still check reset every frame but don't do anything until the switch is released from being held.

 

BTW I did trigger an exit while testing Berzerk. I had my hand off of the joystick and was just pressing reset and poof! Back to the menu screen. 

 

While emulation exit is a great idea I can see it being a nuisance if triggering occurs unexpectedly.

 

Link to comment
Share on other sites

16 hours ago, Omegamatrix said:

Most games do check reset and select every frame. Typically the older games just perform the same reset routine, clearing ram, etc... while reset is pressed. The better programmed games still check reset every frame but don't do anything until the switch is released from being held.

 

BTW I did trigger an exit while testing Berzerk. I had my hand off of the joystick and was just pressing reset and poof! Back to the menu screen. 

 

While emulation exit is a great idea I can see it being a nuisance if triggering occurs unexpectedly.

 

The proposed solution is to inject code that reads the switches every frame.

So it won't matter if a game does it, or does not. The injected code will do it.

Debounce/delay would be easy on top of that.  For example, requiring reset to be pressed for 2 seconds.

 

 

Link to comment
Share on other sites

  • 1 month later...
On 11/10/2020 at 3:06 AM, Al_Nafuur said:

We might setup a counter and check if "Reset" is pressed for a certain amount of checking loops continuously. Lets say 100 times, this might be 2 seconds for an NTSC ROM (1,66 for PAL), if the ROM is checking the Reset-switch every frame! But I think most debouncing routines will prevent this.

Back to this again, I still believe holding reset would be the simpliest solution. Two seconds seems about right. If a counter could be set up then why not?

Link to comment
Share on other sites

  • 4 weeks later...
1 hour ago, Wizzard said:

I just wonder why it writes "emulation stopped" or something like that when I exit the game. It is running natively, not using some emulation, or am I missing something? :)

It is running natively on the 2600, but the PlusCart (Harmony, UnoCart, ..) are somehow emulating a cartridge.

Link to comment
Share on other sites

3 hours ago, Wizzard said:

I just wonder why it writes "emulation stopped" or something like that when I exit the game. It is running natively, not using some emulation, or am I missing something? :)

The emulation is of the cartridge hardware.

From the '2600 point of view, a genuine cartridge of type "X" is plugged in to it.

But the cartridge isn't really type "X". It is a Pluscart pretending to be "X" -- emulating it, in other words.

So, on exit, it is "emulation exited", so to speak.

I don't like the wording either - but that is the reason for it.

 

Link to comment
Share on other sites

I just wonder what is this. Thunderground, running from PlusCart, look at those score numbers, looks like 0 00 00 0. When I run it from emulator or from my another machine without pluscart, it looks like 0 0 0 0 0 0. The spaces between numbers are different. Why is it like that? I am just curious, cause it looks like the game is running differently from PlusCart than from ROM itself. The ROM is the same.

 

PlusCart:

IMG_20210128_122333.thumb.jpg.41156a6bca9d65f9b86fe9f51f52180e.jpg

 

Stella:

1418802081_obrzok.thumb.png.d15424aec8b2e3fbbffa67ee186ee730.png

 

Edited by Wizzard
Link to comment
Share on other sites

3 hours ago, Wizzard said:

I just wonder what is this. Thunderground, running from PlusCart, look at those score numbers, looks like 0 00 00 0. When I run it from emulator or from my another machine without pluscart, it looks like 0 0 0 0 0 0. The spaces between numbers are different. Why is it like that? I am just curious, cause it looks like the game is running differently from PlusCart than from ROM itself. The ROM is the same.

 

PlusCart:

IMG_20210128_122333.thumb.jpg.41156a6bca9d65f9b86fe9f51f52180e.jpg

 

Stella:

1418802081_obrzok.thumb.png.d15424aec8b2e3fbbffa67ee186ee730.png

 

Looks like the same issue we have with the menu text spacing.
@Wizzard, can you open a new thread in the "Bug reports" section:
https://atariage.com/forums/forum/290-bug-reports/

Link to comment
Share on other sites

The problem is a timing issue, which is caused by the console. Thunderground uses an "early HMOVE" for positioning the score digits. And several TIA chips differ slightly here.

Please try with a real cart (or a different flash cart) on the same console. I am pretty sure the behavior is identical.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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