Jump to content

Recommended Posts

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!

  • Like 3
Link to comment
https://forums.atariage.com/topic/194119-26007800-pause-routine/
Share on other sites

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 by Omegamatrix
  • Like 1
  • 8 years later...

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 by Dionoid
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.

 

 

  • Like 1
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?

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
  • Like 1
  • 2 years later...

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):

 

Edited by Dionoid
  • Like 1
  • 1 year later...

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 by Dionoid
  • Like 2

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...