+Andrew Davie Posted February 14, 2012 Share Posted February 14, 2012 It's been a while since I've posted some code, so here's a handy little routine. The code uses the COLOR/B&W switch for pausing a game. The issue here is that the 2600 and 7800 have different switch-action for that switch. On the 2600 it's a two-position switch so you choose COLOR or you choose B&W and it stays there. On the 7800 it's a momentary switch -- it's only 'pressed' while you're actually pressing it; it doesn't have a state... just pressed or not pressed. So for pause on 7800 you have to press once to pause, and press again to unpause. One other issue, on the 2600 you really don't want the game to pause when it starts up, so the code should detect a change in state, not an absolute position. The following code handles all of the above, in just a few cycles. Enjoy. ;---------------------------------------------------------------------------------------------- ; handle pause button for 2600 and 7800 BW_SWITCH = 8 ; SWCHB COLOUR/B&W SWITCH GAMEMODE_PAUSED = 128 ; gameMode BIT7 SET=paused GAMEMODE_CONSOLE = 64 ; gameMODE BIT6 SET=2600, CLEAR=7800 bit gameMode bvc .pause7800 ; 7800 platform ; 2600 pause logic... lda SWCHB eor gameMode and #BW_SWITCH beq .setPauseCol ; no different to original state = no pause change bne .buttonDown ; unconditional ; 7800 pause logic... ; When the button is pressed, we check if it is the FIRST time it is pressed. ; This FIRST time is indicated by the gameMode BW_SWITCH flag being clear. If it is the first time, we toggle the pause ; flag (BIT7) AND we toggle the BW_SWITCH flag so continued button-down does nothing. When the button is ; released, then we again toggle the BW_SWITCH flag, allowing a FIRST time check once again, when the button ; is next pressed. .pause7800 lda #BW_SWITCH bit SWCHB beq .pausePress ora gameMode ; not pressed, so enable first time press bne .fixPause ; unconditional .pausePress bit gameMode beq .setPauseCol2 ; NOT the first time in pause - so do nothing new ; Button is down, and we have detected it as a FIRST-TIME button press. .buttonDown eor gameMode ; toggle first time flag(7800) or current switch state(2600) eor #GAMEMODE_PAUSED ; toggle pause flag .fixPause sta gameMode ; Following code a usage-example to set background colour IFF paused. ; This code can be discarded for most users... .setPauseCol lda gameMode ; are we paused? .setPauseCol2 bpl .exitPause ; only show pause colour when actually paused ldx Platform ; NTSC=0, PAL=1 lda pscol,x ; table with colours for RED for each platform. sta BGColour ; set main screen background colour variable .exitPause gameMode -- one byte variable conatins... BIT7 current pause state (set = game paused) BIT6 platform (1 = atari 2600, 0 = atari 7800) BIT3 internal use for pause state (initialise as shown below) To detect the platform (2600/7600), do the following on machine startup... ; Detect 2600/7800 consoles. ; Call first-ting on machine init... ldy #GAMEMODE_CONSOLE ldx $d0 cpx #$2c bne .is2600 ldx $d1 cpx #$a9 bne .is2600 ldy #0 ; 7800: bit 6 = 0 .is2600 ; 2600: bit 6 = 1 sty gameMode ; store console type To use the pause code successfully, you must also initialise gameMode to the current state of the pause switch but DON'T destroy the platform bit (BIT6). This initialisation works regardless of the machine type... lda gameMode and #~(BW_SWITCH|GAMEMODE_PAUSED) sta gameMode lda SWCHB and #BW_SWITCH ora gameMode ; COLOR/B&W gameMode sta gameMode ; also, BIT7=0 -- system is NOT paused Usage... call the pause code once per game loop, and to check if paused or not... lda gameMode bmi .paused ; yes, paused.... pause flag set! 3 Quote Link to comment https://forums.atariage.com/topic/194119-26007800-pause-routine/ Share on other sites More sharing options...
Omegamatrix Posted February 14, 2012 Share Posted February 14, 2012 (edited) I wrote this little code some time ago based off of Nukey's 7800/2600 detection. - it will fail on a Harmony cart or Krok cart if they are in multigame mode. It works if a single image is burned. - it looks at the initial position of the 2600 color/BW switch, and uses the opposite position as the pause. The reason for this is people leave that switch in either position, and I don't want the game to appear frozen upon start. - at reset, the 7800/2600 detection bit is preserved (desirable). ;saveSwitches XXXx xxxx ; ||| ; ||pause bit ; || ; |Initial position of 2600 color/BW switch ; | ; 7800/2600 detection START: ldx $D0 ; thanks to Nukey Shay for code on how to detect cpx #$2C ; if a 7800 or 2600 is being used, so that a pause bne .not7800 ; switch can be implemented for both systems... ldx $D1 cpx #$A9 bne .not7800 sec ; if 7800... .byte $24 ; BIT opcode, skip 1 byte .not7800: clc ; if 2600... .byte $2C ; BIT opcode ; skip 2 bytes, preserve the carry Reset: asl saveSwitches ; preserve 7800 detection during a reset cld ldx #0 txa .loopClear: dex txs pha bne .loopClear lda SWCHB and #08 ; initial state of color/BW switch for the 2600 ror ; bit 7 = 7800/2600 detection, bit 2 = color/BW switch (will eventually be bit 6) ora #$3B ; fill lower bits... adc #1 ; carry is clear, now move color/BW setting to bit 6, and #$C0 ; BIT opcode will be used later to test bits 6 & 7... sta saveSwitches ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; during game ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;check reset, pause switch lda SWCHB cmp debouncer beq .noChange sta debouncer lsr bcc Reset ; reset takes priority over pause ;reset not pressed ldx saveSwitches bit saveSwitches ; test for 7800/2600 console, and initial state of color/bw switch (for 2600) bpl .detected2600 ;7800 console, check pause switch and #$04 bne .noChange txa eor #$20 ; flip pause flag, as 7800 has a double grounded switch, only high in transition bne .updateSwitch ; always branch, as bit 7 always set for 7800 .detected2600 bvs .bwIs2600Pause eor #$04 ; invert color/bw switch... if 2600 was powered on with switch at B&W position .bwIs2600Pause: and #$04 bne .noPause .pause: txa ora #$20 ; set pause bne .updateSwitch ; always branch .noPause: txa and #$DF ; clear pause .updateSwitch: sta saveSwitches Cheers, Jeff Edit: Wow did the forum software screw up the formatting in that code box. It used to keep it nice and straight. What is going on?? Anyhow those first three lines were supposed to indicate the lines like this: ;saveSwitches ; ;XXXx xxxx ;||| ;||pause bit ;|| ;|Initial position of 2600 color/BW switch ;| ;7800/2600 detection Edited February 14, 2012 by Omegamatrix 1 Quote Link to comment https://forums.atariage.com/topic/194119-26007800-pause-routine/#findComment-2466050 Share on other sites More sharing options...
Dionoid Posted March 28, 2020 Share Posted March 28, 2020 (edited) Thanks for sharing this pause routine, @Andrew Davie I could test it in the Stella emulator, which supports the 7800 pause button. But I also like to test it on a real '7800 PAL and a Harmony cartridge, but no luck so far: If I run the Harmony cart in multi-game mode, the specific 7800-detection zeropage addresses (e.g. $d0 and $d1) are changed (cleared?) before my rom starts. So the routine will detect a 2600 instead of a 7800 machine. When I run the Harmony in 'single program' mode, my 7800 won't start the game at all. Note that it does work on my 2600. Did anyone find a solution for this? (other than actually burning an EPROM) As a side-note: Out of the 10 homebrew '2600 carts I own, 3 fail to play on my 7800 (which are Aardvark, Star Castle Arcade and Thrust+ Platinum). So I guess my 7800 is kind of picky ? Edited March 28, 2020 by Dionoid Quote Link to comment https://forums.atariage.com/topic/194119-26007800-pause-routine/#findComment-4493204 Share on other sites More sharing options...
+Andrew Davie Posted March 28, 2020 Author Share Posted March 28, 2020 I don't remember this code. I think maybe @Thomas Jentzsch wrote it and I rudely forgot to credit him? 1 Quote Link to comment https://forums.atariage.com/topic/194119-26007800-pause-routine/#findComment-4493287 Share on other sites More sharing options...
+SpiceWare Posted March 28, 2020 Share Posted March 28, 2020 4 hours ago, Dionoid said: If I run the Harmony cart in multi-game mode, the specific 7800-detection zeropage addresses (e.g. $d0 and $d1) are changed (cleared?) before my rom starts. So the routine will detect a 2600 instead of a 7800 machine. My understanding is the Harmony Menu will restore $D0 and $D1 when it launches a game so the game can detect the console. SF2 uses this to detect 2600 vs 7800: InitSystem: ; DPC+ always boots in bank 5 so we don't need to select it ; detect 2600 or 7800 for pause routines ldy #1 ldx $d0 cpx #$2c bne .is2600 ldx $d1 cpx #$a9 ; <-- evil7800 note - this A9 is OK as FastFetch is not active bne .is2600 dey ; 7800: y=0 .is2600 ; 2600: y=1 I just confirmed it works correctly on both my 2600 and 7800 when the game is launched via the Harmony menu, as well as when run on the stand alone cartridge. 1 Quote Link to comment https://forums.atariage.com/topic/194119-26007800-pause-routine/#findComment-4493334 Share on other sites More sharing options...
Dionoid Posted March 28, 2020 Share Posted March 28, 2020 31 minutes ago, SpiceWare said: My understanding is the Harmony Menu will restore $D0 and $D1 when it launches a game so the game can detect the console. SF2 uses this to detect 2600 vs 7800 ... I just tested this on my Harmony Encore, and you are right!!! Thanks Darrell. But I swear it didn't work on my regular Harmony cart before I flashed it to 'single program' mode. Maybe it had an old bios? Or maybe I accidentally tested an older version of my rom? Quote Link to comment https://forums.atariage.com/topic/194119-26007800-pause-routine/#findComment-4493364 Share on other sites More sharing options...
+SpiceWare Posted March 28, 2020 Share Posted March 28, 2020 21 minutes ago, Dionoid said: But I swear it didn't work on my regular Harmony cart before I flashed it to 'single program' mode. Maybe it had an old bios? Hmm <search> BIOS 1.05 was released May 23, 2010, so predates this reply by batari about 7800 detection being on the todo list. On 12/25/2010 at 3:29 AM, batari said: There have been a few small changes to the code but not enough to justify a release. There are a few things on the todo list (such as the 7800 detection code) that when finished will justify a release. However, the team just hasn't had time to work on it. Maybe early next year. Just dug out my original Harmony: Original has 1.05, SF2 detects 7800 as 2600 Encore has 1.06, SF2 correctly detects 7800 1 Quote Link to comment https://forums.atariage.com/topic/194119-26007800-pause-routine/#findComment-4493371 Share on other sites More sharing options...
Dionoid Posted December 19, 2022 Share Posted December 19, 2022 (edited) I came up with an alternative solution for a 2600/7800 pause routine, without the need to detect if we're running on a 2600 or 7800. The trick here is to immediately toggle the pause/resume state when the pause button (7800) or COLOR/B&W (2600) was pushed/switched, but delay storing its state (i.e., BIT3 of SWCHB) by 32 frames, which is around 0.5 second on NTSC. This allows for the 7800 pause button to return to its original state as the user releases the button again. Of course that means that pushing the pause button twice within half a second isn't handled. But that seems like an edge case. ; ... BW_MASK = %00001000 PAUSED = %10000000 STATE_READY = %01000000 PauseState = $80 ; variable holding the pause state. BIT7 SET=paused ; pause/resume logic: ; when the COLOR/B&W is switched (2600) or PAUSE button pressed (7800), we immediately ; toggle the pause/resume mode, but we delay storing the state of the button ; (i.e., BIT3 of SWCHB) by 32 frames (= about 0.5 second). ; this way we support handling both the 7800 Pause button (momentary switch) ; and the 2600 two-position switch (COLOR/B&W) ; note that the delayed storing of the button state means that pushing/switching the ; button twice within the same second isn't handled. but who needs that anyway? :) Update_PauseState: subroutine ; note: this subroutine should be called every frame bit PauseState bvc .delayStateStore ; if STATE_READY (BIT6) isn't set, branch to .delayStateStore lda SWCHB ; now check if the pause button was pushed (7800) or COLOR/B&W switched (2600) eor PauseState and #BW_MASK beq .return ; no different to original state = no pause change lda PauseState ; now toggle the pause/unpause state (BIT7) and #PAUSED eor #(PAUSED|32) ; alternatively use #(PAUSED) to delay by 64 frames (approx 1 second) .storePauseState: sta PauseState .return: rts .delayStateStore: lda SWCHB and #BW_MASK ora #STATE_READY inc PauseState ; increment PauseState until BIT6 is set... bit PauseState bvc .return bpl .storePauseState ; ...and store the current switch state (BIT3 of SWCHB) in PauseState ora #PAUSED bmi .storePauseState ; ... Here is the source code and rom file, showing a scrolling rainbow which you can pause/resume by pushing the PAUSE button (on Atari 7800) or changing the COLOR/B&W switch (on Atari 2600): pause.asm source file pause.bin binary rom file (NTSC) Edited December 19, 2022 by Dionoid 1 Quote Link to comment https://forums.atariage.com/topic/194119-26007800-pause-routine/#findComment-5174338 Share on other sites More sharing options...
Dionoid Posted September 6 Share Posted September 6 (edited) For Lode Runner 2600, I ended up using an even simpler routine to implement pause functionality, which works across both the Atari 2600 and 7800. The basic idea is that whenever the B/W switch changes state (or the 7800's pause button is down), the game pauses. To resume the game, the player can either move the joystick in any direction or press the fire button. I also added some logic to ensure that you can still pause the game while there's simultaneous joystick movement. In pseudo code: // init byte which holds pause state byte pauseState = CURR_BW_SW_STATE; // Define constants for checking pause states BW_MASK = %00001000 // bit for B/W switch status in SWCHB CURR_BW_SW_STATE = (SWCHB & BW_MASK) // Actual B/W switch state LAST_BW_SW_STATE = (pauseState & BW_MASK) // Last B/W switch state IS_PAUSED = (pauseState & %10000000) // Check if game is paused yes/no WAITING_FOR_JOYSTICK = (pauseState & %01000000) // Indicates if we're waiting for joystick to unpause/resume // Function to check if the game is paused Function isGamePaused() // If the game is paused or the color switch state has changed If IS_PAUSED is true OR CURR_BW_SW_STATE != LAST_BW_SW_STATE // Set the pause flag in the pause state (IS_PAUSED = true) pauseState = pauseState OR %10000000 // If no joystick input is detected If JOYSTICK_ANY_INPUT is false // Allow the game to unpause when joystick input is found (set WAITING_FOR_JOYSTICK bit) pauseState = pauseState OR %01000000 // Else if joystick input found Else If WAITING_FOR_JOYSTICK is true // Unpause and reset the pause state to the current B/W switch state pauseState = CURR_BW_SW_STATE // Return whether the game is paused return IS_PAUSED Edited September 6 by Dionoid 2 Quote Link to comment https://forums.atariage.com/topic/194119-26007800-pause-routine/#findComment-5529130 Share on other sites More sharing options...
Recommended Posts
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.