|
Subject: [stella] Euchre: squeezing in more From: Erik Eid <eeid@xxxxxxxxx> Date: Sun, 28 Jul 2002 20:50:05 -0400 |
Good evening, everyone! I have added a few more features to Euchre in the last couple of weeks. In my previous update, I mentioned that I wanted to add sound, a game-over indicator, and if there was space, include a "stick the dealer" option. I am pleased to report that I was able to do all three of these. With only 130 bytes left at the time, I took a decidedly minimalist approach to sound. I initially wanted to play tunes a few notes in length, but abandoned that due to the amount of overhead and sound data it would have needed. I instead looked again to the Atari board and card games for my inspiration. In those, sounds normally occurred only when making a move (such as picking up or placing a piece) and sometimes when scoring (such as with Othello). I decided to use only a single sound per event and settled on four events: player's team scores, other team scores, player makes a valid selection, and player makes an invalid selection. To make Euchres and marches (taking all five tricks) more impressive, I changed the scoring routines so it would add to scores in one-point increments, sounding a beep for each point. If you or your opponents succeed in taking all five tricks while alone, you'll hear four tones. I dug up an old message by Nick Bensema on color-cycling (http://www.biglist.com/lists/stella/archives/199707/msg00023.html). Basically, to accomplish color-cycling, just exclusive-or some set of bits before setting a color register. When not in attract mode, the mask should be zero, so that no bits are changed. During the game over period, I just let a counter decrement each frame and change the mask when it rolls over. (I apologize for some of the hideous color combinations that result from the random masks that are generated. I kept the background black just so it wouldn't be truly offensive to sensitive eyes. <g>) I decided to go for gold and add the "stick the dealer" variant. If the Left Difficulty switch is set to A, then the dealer cannot pass after all the other players have passed twice. The dealer must call a trump suit. This is a commonly used variation that is intended to keep the game moving; otherwise several hands can go by in which no one calls trump. After all that was done, I found myself with zero bytes left. Since there are still some tweaks to make (such as the "west" player putting its card down immediately after the human "south", with no pause), I wanted to free up some space. I started up another round of optimizations and managed to clear out 78 bytes. Two changes were very profitable. In the first, the joystick read routine, I noticed I was continuously reloading the joystick value so I could apply a different bit mask. Noticing that bit shifts affect the carry flag, and that branches can be made based on the carry flag, I changed this: lda SWCHA sta T1 and #J0_Up beq JoyMinus lda T1 and #J0_Left beq JoyMinus lda T1 and #J0_Down beq JoyPlus lda T1 and #J0_Right beq JoyPlus lda #$00 sta JoyPause beq JoySetDir to this: lda SWCHA asl ; Move bit 7 (right) to carry bcc JoyPlus ; If carry is clear, stick was moved right asl ; Move bit 6 (left) to carry bcc JoyMinus ; If carry is clear, stick was moved left asl ; Move bit 5 (down) to carry bcc JoyPlus ; If carry is clear, stick was moved down asl ; Move bit 4 (up) to carry bcc JoyMinus ; If carry is clear, stick was moved up lda #$00 sta JoyPause beq JoySetDir (This only works if you do not need diagonal movement.) I also replaced the jsr-jmp pairs in StageJumpTable with jsr-rts pairs. I put the indirect jump in its own subroutine, which allowed me to use rts to get out of the jump table after each stage routine finished. (A side effect was that I only had to multiply the stage number by four instead of six, saving a few bytes more.) Since the jump table is fairly long, please see the source if you are interested in looking at it. I finally replaced the "RB" and "LB" indicators that I was using to easily spot the bowers. However, I left them in the source; if you compile with the -DBOWER option, they will replace the "J" in the right and left bowers. I am still not finished with the game. As I mentioned, there's a few rough edges to clean up; the remaining 78 bytes will help make that happen. If I have enough space afterward, I can look into implementing the suggestions given by the list readers. I also still have to create a PAL version. (Then there's also a manual to consider...) If you haven't already, please give the game a try. Let me know if it behaves oddly and if the sound is a bit too obnoxious. :) (Also let me know if a computer player ever takes the bid alone. I almost never see it. Then again, I almost never get a hand I can use alone myself.) I have attached the source, binary, and Stella profile entry as usual. ?The version of vcs.h that I am using until the list decides on a standard is at http://www.io.com/~nickb/atari/doc/vcs.h.
"Cartridge.MD5" "80e52315919bd8a8b82a407ccd9bb13f" "Cartridge.Name" "Euchre (July 28, 2002 pre-release)" "Cartridge.Manufacturer" "Erik Eid" "Cartridge.Rarity" "New Release" "Cartridge.Type" "4K" "Controller.Left" "Joystick" "Display.Format" "NTSC" ""
Attachment:
Euchre.bin
Description: Euchre ROM
;
; Euchre game program for the Atari 2600 video computer system
;
; Copyright 2001 by Erik Eid (eeid@xxxxxxxxx)
;
; Last update: July 28, 2002
;
; Compiled with the dasm assembler (version 2.12.04) using the -f3 option
;
processor 6502
include vcs.h
; Constants
seg.u defines
CardsInDeck = $18 ; 24 cards in a Euchre deck
CardsInHand = $05
TricksInRound = $05
SuitsInDeck = $04
Team1Color = $88
Team2Color = $38
TableRegionColor = $d4
CardTableColor = $0f
RedSuitColor = $36
BlackSuitColor = $00
MessageRegionDisplayColor = $22
MessageRegionChoiceColor = $84
MessageTextColor = $1c
CursorColor = $82
TrumpDisplayColor = %00101000
RankMask = %00011100 ; Bit mask for rank of a card
DispSuitMask = %01100000 ; Bit mask for suit displayed on a card
RealSuitMask = %00000011 ; Bit mask for suit used when following (the left
; bower becomes the suit of the right bower)
FullSuitMask = %01100011 ; Bit mask for both display and real suit
CardHiddenMask = %10000000 ; Bit mask for determining if a card is hidden
ShowCardMask = %01111111 ; Bit mask to force a card to be shown
HideCardValue = %10000000 ; Bit mask to force a card to be hidden
CardPlayedMask = %10000000
CardPlayedValue = %10000000
; This mask is used only when calculating the strength of a hand
; because they rely on the original rank and suit of a card
RankSuitMask = %01111100 ; Bit mask for rank and suit combined
RightRankValue = %00011100
LeftRankValue = %00011000
AceRankValue = %00010100
KingRankValue = %00010000
QueenRankValue = %00001100
JackRankValue = %00001000
TenRankValue = %00000100
NineRankValue = %00000000
HeartSuitValue = %00000000
DiamondSuitValue = %00100001
ClubSuitValue = %01000010
SpadeSuitValue = %01100011
BlackSuitMask = %00000010
FlipColorSuitMask = %00000001 ; EOR with this to change suit to other suit
; of the same color
PlayersMask = %00000011
NoPlayer = %11111111 ; Since players are numbered 0-3, a 255 indicates
; that no player meets the condition
NoSuit = %11111111
NoCard = %11111111
NoChoice = %11111111
SWACNT = $281 ; Strangely not part of the original vcs.h
VS_Disable = 0 ; Ditto
CenterRankPos = $75 ; Player positions
CenterSuitPos = $66
LeftRankPos = $a2
RightRankPos = $57
LeftScorePos = $31
RightScorePos = $97
LeftTrickPos = $f1
RightTrickPos = $97
BidArrowPos = $35
BidDecisionPos = $b5
InstructionPos = $f5
MessageP0Pos = $c4
MessageP1Pos = $35
NoCursorPos = $ff
MsgNumBlank = $00
MsgNumSelectAction = $01
MsgNumSelectTrump = $02
MsgNumDeal = $03
StageNewGame = $00
StageNewHand = $01
StageGameOver = $02
StageShuffle = $03
StageDeal = $04
StageBidding1 = $05
StageDiscarding = $06
StageBidding2 = $07
StagePlaying = $08
StageAddToTricks = $09
StageAddToScore = $0a
StageBetweenHands = $0b
FramesWait = 90 ; Number of frames to wait for 1 1/2 seconds
FramesShortWait = 45 ; Number of frames to wait for 3/4 second
FramesLongWait = 180 ; Number of frames to wait for 3 seconds
FramesJoyWait = 15
FramesScoreIncrement = 20
FramesValidSelect = 2
FramesInvalidSelect = 10
SoundTeam1Scores = $00 ; Do not change this value!
SoundTeam2Scores = $01 ; Do not change this value!
SoundValidSelect = $02
SoundInvalidSelect = $03
ChoicePass = $00
ChoiceCall = $01
ChoiceAlone = $02
TriggerOff = $00
TriggerOn = $01
TriggerHeld = $02
CursorModeNone = $00
CursorModeCard = $01
CursorModeAction = $02
CursorModeTrump = $03
CursorModeSelectMask = %00000010
TrumpScoreValue = %01000000 ; Value to add to rank for trump cards when
; considering the card to play
LeadSuitScoreValue = %00100000 ; Value to add to rank for cards of the
; led suit when considering the card to play
UnbrokenModifier = %00100010 ; Value to add to rank for cards of an unbroken
; suit when considering the card to lead
BrokenModifier = %00100000 ; Value to add to rank for cards of a broken
; suit when considering the card to lead
; Variables
seg.u vars
org $80
Team1Score ds 1
Team2Score ds 1
Team1Tricks ds 1
Team2Tricks ds 1
SouthHand ds 5 ; Cards in a player's hand
WestHand ds 5
NorthHand ds 5
EastHand ds 5
SouthCard ds 1 ; Cards down on the table
WestCard ds 1
NorthCard ds 1
EastCard ds 1
ImgPtr1 ds 2 ; Pointers to playfield and player images
ImgPtr2 ds 2
ImgPtr3 ds 2
ImgPtr4 ds 2
HandCard ds 1 ; Pointer to a card in a hand
T1 ds 1 ; Temporary variables used in subroutines
T2 ds 1
T3 ds 1
T4 ds 1
rand1 ds 1 ; Locations to hold bits of random number
rand2 ds 1
rand3 ds 1
rand4 ds 1
NeedShuffle ds 1 ; Flag indicating if a shuffle is needed
MessageNum ds 1 ; Number of message to display
MessagePtr ds 2
CursorPos ds 1 ; Card selection cursor position
Stage ds 1
Turn ds 1
BiddingTeam ds 1
Dealer ds 1
FrameCounter ds 1
RightBowerMask ds 1 ; Calculated masks for bower when figuring hand strength
LeftBowerMask ds 1
TriggerTrack ds 1 ; Low four bits are number of frames held, high bit
; indicates release after a full hold
MessageRegionColor ds 1 ; Color to use as background of message area
JoyDir ds 1
HighCardScore ds 1
HighCardNum ds 1
TrumpSuitMask ds 1
HandStartOffset ds 1
HandEndOffset ds 1
Choice ds 1
JoyPause ds 1
CursorMode ds 1
NumTrumps ds 1
HighestRemainingTrump ds 1
NumTrumpsLeft ds 1 ; Amount of trumps left in the round
TrumpsLeft ds 1 ; Array of bits representing which trumps are left
SuitBroken ds 4
DeclinedSuit ds 1
DeclinedBower ds 1
AttractMask ds 1
AttractFrameCounter ds 1
SoundTableOffset ds 1
SoundFrameCounter ds 1
Overlay ds 30
DeckStart = SouthHand
Upcards = SouthCard
seg.u vars
org Overlay
PotentialTrump ds 1
Upcard ds 1
HighStrength ds 1
BestSuit ds 1
HandStrength ds 1
CardScore ds 1
NumInSuit ds 1
NumOffLoneAces ds 1
NumVoids ds 1
HasRight ds 1
HasLeft ds 1
HasTrumpAce ds 1
HasAce ds 1
LowTrumpFactor ds 1
UpcardFactor ds 1
OtherFactor ds 1
Bidder ds 1
seg.u vars
org Overlay
TrickNum ds 1
Leader ds 1
LeadSuitMask ds 1
HasLeadSuit ds 1
PlayerToSkip ds 1
TrickWinner ds 1
CardInTrickNum ds 1
CardsInFullTrick ds 1
CardToPlay ds 1
CardToPlayOffset ds 1
LeadSuitCount ds 1
HighCard ds 1
LegalPlays ds 5
GatheredInfo ds 1
TrickWinningScore ds 1
CurrentCardScore ds 1
OpponentsWinning ds 1
TrueIdealScore ds 1
HighWinnerNum ds 1
HighWinnerScore ds 1
LowWinnerNum ds 1
LowWinnerScore ds 1
LowCardNum ds 1
LowCardScore ds 1
IdealTrumpNum ds 1
IdealTrumpScore ds 1
BestLeadNum = HighWinnerNum
BestLeadScore = HighWinnerScore
BestTrumpNum = LowWinnerNum
BestTrumpScore = LowWinnerScore
ProcessedCounters = GatheredInfo
ScoringTeam = HighWinnerNum
ScoreIncrement = HighWinnerScore
; Program
seg code
org $f000 ; 4K cartridge
;
; Initialization
;
CartStart
sei ; Disable all interrupts
cld ; Clear decimal mode (so carry is at 256, not 100)
ldx #$ff
txs ; Reset the stack pointer to the highest point possible
; Clear out registers and variables
lda #$00
ClearRAM
sta $00,x
dex
bne ClearRAM ; Loop does not zero WSYNC, but it's not needed
sta SWACNT ; Tell port A to accept input
lda #$6d ; seed random number generator
sta rand1
sta rand2
sta rand3
sta rand4
lda #$80
ldx #CardsInDeck
HideLoop
dex
sta DeckStart,x
bne HideLoop
jsr StartGameOver
jmp Main
ProgStart ; These initializations are done when Reset is pressed
lda #NoCursorPos
sta CursorPos
lda #TriggerOff
sta TriggerTrack
;
; Main loop
;
Main
; Start of display kernel
; Provide three lines of vertical sync
lda #VB_Enable
sta VBLANK
lda #VS_Enable
sta WSYNC
sta WSYNC
sta WSYNC
sta VSYNC
sta WSYNC
sta WSYNC
lda #VS_Disable
sta VSYNC
; Provide 37 scanlines of vertical blank
lda #43 ; 37 lines * 76 cycles/line = 2812 cycles / 64 cycles/interval = 43.96 intervals
sta TIM64T
jsr RandomBit ; keep the randomness flowing
lda INPT4
bpl TriggerPressed
lda #TriggerOff
beq TriggerEnd ; We know TriggerOff = 0 so we can beq instead of jmp
TriggerPressed
lda TriggerTrack
cmp #TriggerOff
bne TriggerWasHeld
lda #TriggerOn
bne TriggerEnd ; We know TriggerOn <> 0 so we can bne instead of jmp
TriggerWasHeld
lda #TriggerHeld
TriggerEnd
sta TriggerTrack
lda JoyPause
beq JoyRead
dec JoyPause
lda #$00
beq JoySetDir
JoyRead
lda SWCHA
asl ; Move bit 7 (right) to carry
bcc JoyPlus ; If carry is clear, stick was moved right
asl ; Move bit 6 (left) to carry
bcc JoyMinus ; If carry is clear, stick was moved left
asl ; Move bit 5 (down) to carry
bcc JoyPlus ; If carry is clear, stick was moved down
asl ; Move bit 4 (up) to carry
bcc JoyMinus ; If carry is clear, stick was moved up
lda #$00
sta JoyPause
beq JoySetDir
JoyPlus
lda #FramesJoyWait
sta JoyPause
lda #$01
bne JoySetDir
JoyMinus
lda #FramesJoyWait
sta JoyPause
lda #$FF
JoySetDir
sta JoyDir
JoyEnd
lda #CursorModeNone
ldx Turn
bne DCM4
DetermineCursorMode
ldx CursorPos
bmi DCM4
ldx Stage
cpx #StageDiscarding
beq DCM0
cpx #StagePlaying
bne DCM1
DCM0
lda #CursorModeCard
bne DCM4
DCM1
cpx #StageBidding1
bne DCM2
lda #CursorModeAction
bne DCM4
DCM2
cpx #StageBidding2
bne DCM4
ldx Choice
bmi DCM3
lda #CursorModeTrump
bne DCM4
DCM3
lda #CursorModeAction
DCM4
sta CursorMode
lda SoundFrameCounter
bmi SoundOff
dec SoundFrameCounter
ldx SoundTableOffset
lda FreqTable,x
sta AUDF0
lda ToneTable,x
sta AUDC0
lda #$08
sta AUDV0
bne SoundOut
SoundOff
lda #$00
sta AUDV0
SoundOut
WaitVBlank
lda INTIM
bne WaitVBlank
sta WSYNC ; Finish up last line
sta VBLANK ; Stop vertical blank (accumulator holds zero)
; Now we start on the visible portion of the screen
lda Stage
cmp #StageShuffle
bne NormalKernel
ShuffleKernel
lda #228 ; 192 lines * 76 cycles/line = 14592 cycles / 64 cycles/interval = 228 intervals
sta TIM64T
; Shuffling takes an awfully long time. It costs about 5.6 lines to get one random byte and
; we need 24 random numbers for one shuffle, or 134.4 lines. Since there's not enough time
; to do this during overscan (30 lines), we'll just draw a blank screen whenever we're
; shuffling. This will last for only eight frames, or 0.133 seconds (0.16 seconds in PAL),
; which should not be terribly disruptive.
ShuffleDeck
ldy #CardsInDeck-1
SDOnce
jsr RandomByte ; Assume this is a number R between 0 and 1
lsr
lsr
lsr
lsr ; Four high bits are now four low, let this be R * 16
sta T2
lsr ; Now the three high bits are low, let this be R * 8
clc
adc T2 ; A = R * 8 + R * 16 = R * 24, a number between 0 and 23
; (The shift rights are more efficient than rotating
; left into a separate variable.)
tax
lda DeckStart,y
pha
lda DeckStart,x
sta DeckStart,y
pla
sta DeckStart,x
dey
bpl SDOnce
sta WSYNC
jmp WaitForEnd
NormalKernel
; First eight lines are blank...
lda #9 ; 8 lines * 76 cycles/line = 608 cycles / 64 cycles/interval = 9.5 intervals
sta TIM64T
; Since we have some time, prepare the playfield for displaying the scores
; and get pointers to playfield images for them.
lda #Team1Color
eor AttractMask
sta COLUP0
lda #Team2Color
eor AttractMask
sta COLUP1
lda #LeftScorePos
ldx #0
jsr PositionPlayer
lda #RightScorePos
inx
jsr PositionPlayer
sta WSYNC
sta HMOVE
lda #P_Quad
sta NUSIZ0
sta NUSIZ1
lda Team1Score
ldx #ImgPtr1
jsr GetScoreImage
lda Team2Score
ldx #ImgPtr2
jsr GetScoreImage
WaitEndScore
lda INTIM
bne WaitEndScore
; Now we spend ten lines drawing the scores on the playfield
ldx #$01
jsr DrawBookkeeping
; Pause for four lines and prepare to show tricks
sta WSYNC
lda #$00
sta GRP0
sta GRP1
lda #P_Reflect
sta REFP0
lda #LeftTrickPos
ldx #0
jsr PositionPlayer
lda #RightTrickPos ; Despite the fact that the position for the second
ldx #1 ; score and tricks are identical, the HMOVE will
jsr PositionPlayer ; cause another move, so we need to reset the
; position.
sta WSYNC
sta HMOVE
lda Team1Tricks
ldx #ImgPtr1
jsr GetTrickImage
lda Team2Tricks
ldx #ImgPtr2
jsr GetTrickImage
ldx #$00
jsr DrawBookkeeping
sta WSYNC
lda #$00
sta GRP0
sta GRP1
sta NUSIZ0
sta NUSIZ1
sta REFP0
; Draw an informational region. Depending on the stage, it can be the current
; trump suit, an arrow indicating the current bidder and bid, or an instruction
; to the human player (D = deal, S = swap).
lda Stage
cmp #StageBidding1
beq PrepareShowBid
cmp #StageBidding2
beq PrepareShowBid
cmp #StageBetweenHands
beq PrepareShowD
cmp #StageDiscarding
beq PrepareShowS
cmp #StagePlaying
bcc PrepareShowNothing
PrepareShowTrump
sta WSYNC
lda TrumpSuitMask
ldx #ImgPtr1
jsr GetSuitImage
lda #<LetterImageSpace
ldx #ImgPtr2
jsr GetLetterImage
ldx BiddingTeam
lda BidTeamToTrumpPos,x
ldx #0
jsr PositionPlayer
jmp ShowInformation
PrepareShowNothing
sta WSYNC ; Compensate for this being a short routine
sta WSYNC
sta WSYNC
lda #<LetterImageSpace
ldx #ImgPtr1
jsr GetLetterImage
lda #<LetterImageSpace
ldx #ImgPtr2
jsr GetLetterImage
jmp ShowInformation
PrepareShowD
lda #<LetterImageD
PSD2
ldx #ImgPtr1
jsr GetLetterImage
lda #<LetterImageSpace
ldx #ImgPtr2
jsr GetLetterImage
sta WSYNC ; Compensate for this being a short routine
lda #InstructionPos
ldx #0
jsr PositionPlayer
jmp ShowInformation
PrepareShowS
lda Turn
bne PrepareShowNothing
lda #<LetterImageS
bne PSD2
PrepareShowBid
lda Turn
ldx #ImgPtr1
jsr GetArrowImage
lda Choice
and #$03 ; Change NoChoice ($FF) to $03
tax
lda ChoiceToLetterTable,x
ldx #ImgPtr2
jsr GetLetterImage
lda #BidArrowPos
ldx #0
jsr PositionPlayer
lda #BidDecisionPos
ldx #1
jsr PositionPlayer
ShowInformation
sta WSYNC
sta HMOVE
lda #TrumpDisplayColor
eor AttractMask
sta COLUP0
sta COLUP1
jsr DrawSingleCard
lda #$00
sta GRP0
sta GRP1
sta WSYNC
lda #7
sta TIM64T
; Position the players for display of a card. This is well in advance but
; we have time now.
lda #CenterRankPos
ldx #0
jsr PositionPlayer
lda #CenterSuitPos
ldx #1
jsr PositionPlayer
sta WSYNC
sta HMOVE
WaitBeginTable
lda INTIM
bne WaitBeginTable
; Now switch to the "card table" display
sta WSYNC
lda #TableRegionColor
eor AttractMask
sta COLUBK
lda #CardTableColor
eor AttractMask
sta COLUPF
lda #PF_Reflect
sta CTRLPF
lda #$0f
sta PF1
lda #$ff
sta PF2
lda NorthCard
ldx #ImgPtr1
ldy #ImgPtr2
jsr GetCardGraphics
sta COLUP0
sta COLUP1
jsr DrawSingleCard
lda #$00
sta GRP0
sta GRP1
; Now we come to the hard one... both West and East
lda #P_TwoClose ; Two copies close
sta NUSIZ0
sta NUSIZ1
lda WestCard
ldx #ImgPtr1
ldy #ImgPtr3
jsr GetCardGraphics
sta COLUP0
lda EastCard
ldx #ImgPtr2
ldy #ImgPtr4
jsr GetCardGraphics
sta COLUP1
lda #LeftRankPos
ldx #0
jsr PositionPlayer
lda #RightRankPos
ldx #1
jsr PositionPlayer
; sta WSYNC
; sta HMOVE
jsr DrawTwoCards
lda #$00
sta GRP0
sta GRP1
sta NUSIZ0
sta NUSIZ1
lda #CenterRankPos
ldx #0
jsr PositionPlayer
lda #CenterSuitPos
ldx #1
jsr PositionPlayer
sta WSYNC
sta HMOVE
lda SouthCard
ldx #ImgPtr1
ldy #ImgPtr2
jsr GetCardGraphics
sta COLUP0
sta COLUP1
jsr DrawSingleCard
lda #4
sta TIM64T
lda #$00
sta GRP0
sta GRP1
WaitEndSouth
lda INTIM
bne WaitEndSouth
sta WSYNC ; Stabilizer
lda #9 ; burn 8 lines
sta TIM64T
lda #$00
sta PF1
sta PF2
sta COLUBK
WaitBeforeHand
lda INTIM
bne WaitBeforeHand
; Draw the five cards in the player's hand. For each of the cards, draw four
; black lines then twelve card lines. The middle eight lines of the card have
; the images. During the four black lines, get the image pointers and player
; colors.
lda #$00
sta HandCard
ShowHandLoop
lda #4
sta TIM64T
lda #$00
sta PF2
sta COLUBK
ldx HandCard
lda SouthHand,x
ldx #ImgPtr1
ldy #ImgPtr2
jsr GetCardGraphics
sta COLUP0
sta COLUP1
WaitToDrawHandCard
lda INTIM
bne WaitToDrawHandCard
lda #$f0
sta PF2
sta WSYNC
lda #$00
ldx CursorMode
cpx #CursorModeCard
bne SC
ldx HandCard
cpx CursorPos
bne SC
ShowCursor
lda #CursorColor
eor AttractMask
SC
sta WSYNC
sta COLUBK
jsr DrawSingleCard
lda #$00
sta COLUBK
sta GRP0
sta GRP1
sta WSYNC
sta WSYNC
sta WSYNC
inc HandCard
lda HandCard
cmp #CardsInHand
bne ShowHandLoop
; Now the gap between the last card and the message region
lda #13
sta TIM64T
lda #$00
sta PF2
sta COLUBK
eor AttractMask
sta COLUP0
sta COLUP1
; Prepare for the message section
lda #MessageP0Pos
ldx #0
jsr PositionPlayer
lda #MessageP1Pos
ldx #1
jsr PositionPlayer
sta WSYNC
sta HMOVE
ldx #MessagePtr
lda CursorMode
cmp #CursorModeAction
beq DetermineMessageAction
cmp #CursorModeTrump
beq DetermineMessageTrump
ldy MessageNum
jmp DM0
DetermineMessageAction
ldy #MsgNumSelectAction
bne DM0
DetermineMessageTrump
ldy #MsgNumSelectTrump
DM0
jsr GetMessagePointer
jsr GetMessageImages
WaitForGap
lda INTIM
bne WaitForGap
sta WSYNC
lda MessageRegionColor
; No need to use attract mask - messages do not appear when game is over.
sta COLUBK
lda #19 ; 16 lines of message
sta TIM64T
lda #P_TwoClose
sta NUSIZ0
sta NUSIZ1
lda #MessageTextColor
; No need to use attract mask - messages do not appear when game is over.
sta COLUP0
sta COLUP1
sta WSYNC
sta WSYNC
sta WSYNC
jsr DrawMessageText
lda #$00
sta GRP0
sta GRP1
sta VDELP0
sta VDELP1
sta NUSIZ0
sta NUSIZ1
lda CursorMode
and #CursorModeSelectMask
bne DrawSelector
lda #$00
beq DS2
DrawSelector
lda #%11000000
DS2
pha
lda #MessageTextColor
; No need to use attract mask - messages do not appear when game is over.
sta COLUP0
ldx CursorPos
lda CursorPosToSelectorPos,x
ldx #$00
jsr PositionPlayer
sta WSYNC
sta HMOVE
pla
sta GRP0
sta WSYNC
sta WSYNC
lda #$00
sta GRP0
WaitForMessage
lda INTIM
bne WaitForMessage
; lda #1 ; 8 lines
; sta INTIM
lda #$00
sta WSYNC
; This is shutting off all images and player data, so there is again
; no need to use the attract mask.
sta COLUP0
sta COLUP1
sta GRP0
sta GRP1
sta NUSIZ0
sta NUSIZ1
WaitForEnd
; lda INTIM
; bne WaitForEnd
sta WSYNC
sta WSYNC
lda #35 ; 30 lines of overscan
sta TIM64T
lda #$02
sta VBLANK
CheckReset
lda SWCHB
and #Con_Start
bne CheckStages
jsr StartNewGame
jmp ProgStart
CheckStages
lda Stage
asl
asl
; No clear carry needed - stage numbers are too low to affect the
; carry after only two shifts.
adc #<StageJumpTable
sta T2
lda #>StageJumpTable
adc #$00
sta T3
jsr StageJump
jmp EndCase
StageJump
jmp (T2)
rts
StageJumpTable
jsr PerformNewGame
rts
jsr PerformNewHand
rts
jsr PerformGameOver
rts
jsr PerformShuffle
rts
jsr PerformDeal
rts
jsr PerformBidding1
rts
jsr PerformDiscarding
rts
jsr PerformBidding2
rts
jsr PerformPlaying
rts
jsr PerformAddToTricks
rts
jsr PerformAddToScore
rts
jsr PerformBetweenHands
rts
EndCase
WaitOverscan
lda INTIM
bne WaitOverscan
sta WSYNC
jmp Main
StartBidding1
lda #StageBidding1
sta Stage
lda #NoSuit
sta DeclinedSuit
sta DeclinedBower
jsr StartBidding
rts
StartBidding
lda Dealer
clc
adc #$01
and #PlayersMask
sta Turn
lda #NoChoice
sta Choice
lda #$00
sta CursorPos
rts
PerformBidding1
ldx Turn
beq PB1South
PB1NotSouth
lda Choice ; Has the computer player made a decision?
bmi PB1TimeToAct ; No - get the player's bid.
dec FrameCounter ; Yes - pause for a bit, then go to next player
beq PB1Advance
rts
PB1TimeToAct
jsr GetHandOffsets ; x still has the turn number, so this is fine
lda Upcard
and #RealSuitMask
sta PotentialTrump
jsr AnalyzeHand
jsr DecideBid
PB1SetDelay
lda #FramesWait
sta FrameCounter
rts
PB1Advance
lda Choice
cmp #ChoicePass
beq PB1Pass
lda Turn
sta Bidder
and #$01
sta BiddingTeam
lda Upcard
and #RealSuitMask
sta TrumpSuitMask
jsr CreateBowers
jsr StartDiscarding ; StartDiscarding also starts play
rts
PB1Pass
lda Turn
cmp Dealer ; Did the dealer just pass?
beq PB1DealerPass ; Yes!
clc ; No, advance to the next player
adc #$01
and #PlayersMask
sta Turn
lda #NoChoice
sta Choice
rts
PB1DealerPass
jsr StartBidding2
rts
PB1South
lda TriggerTrack
cmp #TriggerOn
bne PB1SouthMove
jsr InitializeSelectSound
lda CursorPos
sta Choice
jmp PB1Advance
PB1SouthMove
ldx #$03
jsr MoveCursor
rts
; x = highest possible choice number plus one (lowest is zero)
MoveCursor
stx T1
lda JoyDir
clc
adc CursorPos
bmi MCUnder
cmp T1
bne MCSave
lda #$00 ; "Wrap" from highest to lowest by loading lowest
beq MCSave
MCUnder
dex
txa ; "Wrap" from lowest to highest by loading highest
MCSave
sta CursorPos
rts
StartBidding2
lda #StageBidding2
sta Stage
ldx Dealer
lda Upcards,x
ora #HideCardValue
sta Upcards,x
and #RealSuitMask
sta DeclinedSuit
lda Upcards,x
and #RankMask
cmp #JackRankValue
bne SB2A
lda DeclinedSuit
sta DeclinedBower
SB2A
jsr StartBidding
rts
PerformBidding2
ldx Turn
beq PB2South
PB2NotSouth
lda Choice ; Has the computer player made a decision?
bmi PB2TimeToAct ; No - get the player's bid.
dec FrameCounter ; Yes - pause for a bit, then go to next player
beq PB2Advance
rts
PB2TimeToAct
jsr GetHandOffsets ; x still has the turn number, so this is fine
lda #$00
sta HighStrength
lda #$03
sta T4
PB2FindBestSuit
lda T4 ; Real suits are 0-3; usable as loop control
cmp DeclinedSuit
beq PB2Declined
sta PotentialTrump
jsr AnalyzeHand
lda HandStrength
jmp PB2CheckIfHigh
PB2Declined
lda #$00
PB2CheckIfHigh
cmp HighStrength ; Is the strength of this hand with the suit
; being checked as trump better than the best
; suit so far?
bcc PB2NextSuit ; No, carry on to the next suit
sta HighStrength ; Yes, store this number and set the best suit
lda T4
sta BestSuit
PB2NextSuit
dec T4
bpl PB2FindBestSuit
lda HighStrength
sta HandStrength
jsr DecideBid ; Submit the highest possible tricks to be taken
; with this hand to get a choice
PB2SetDelay
lda #FramesWait
sta FrameCounter
rts
PB2Advance
lda Choice
cmp #ChoicePass
beq PB2Pass
PB2Accept
lda Turn
sta Bidder
and #$01
sta BiddingTeam
lda BestSuit
and #RealSuitMask
sta TrumpSuitMask
jsr CreateBowers
jsr StartPlaying
rts
PB2Pass
lda Turn
cmp Dealer ; Did the dealer just pass?
beq PB2DealerPass ; Yes!
clc ; No, advance to the next player
adc #$01
and #PlayersMask
sta Turn
lda #NoChoice
sta Choice
rts
PB2DealerPass
lda SWCHB
and #%01000000 ; Is the left difficulty switch on A or B?
beq PB2AllowPass ; It's on B, so the computer player can pass.
lda #ChoiceCall ; It's on A, so the computer must call a suit.
sta Choice
bne PB2Accept
PB2AllowPass
jsr StartBetweenHands ; Everybody passed! Throw in the hand.
rts
PB2South
lda Choice ; Has south made a bid yet?
bpl PB2SouthSuit ; Yes, so go set a trump suit
lda TriggerTrack ; No, so see if south is setting the bid now
cmp #TriggerOn
bne PB2SouthMoveAction ; South is still in the process of selecting
jsr InitializeSelectSound
lda CursorPos
sta Choice
cmp #ChoicePass ; Did the human player pass?
bne PB2SouthReadySuit ; No, in next frame start suit selection
lda SWCHB
and #%01000000 ; Is the left difficulty switch on A or B?
beq PB2Advance ; It's on B, so the pass will be allowed.
; Go on to the next player.
lda Dealer ; It's on A. Check if south is the dealer.
bne PB2Advance ; South did not deal, so it is fine to pass.
jsr InitializeBuzzSound ; South did deal, so a pass is not allowed.
lda #NoChoice
sta Choice
rts
PB2SouthReadySuit
lda #$00
sta CursorPos
rts
PB2SouthMoveAction
ldx #$03
jsr MoveCursor
rts
PB2SouthSuit
lda TriggerTrack ; Did south pick a suit yet?
cmp #TriggerOn
bne PB2SouthMoveSuit ; No, south is still selecting a trump suit
lda CursorPos
cmp DeclinedSuit ; Did south set trump to the turned-down suit?
beq PB2ChoseDeclined
sta BestSuit
jsr InitializeSelectSound
jmp PB2Advance
PB2ChoseDeclined
jsr InitializeBuzzSound
rts
PB2SouthMoveSuit
ldx #$04
jsr MoveCursor
rts
StartDiscarding
lda #StageDiscarding
sta Stage
ldx Dealer
stx Turn ; Need to show cursor position if south dealt
jsr GetHandOffsets
lda Upcards,x
and #RealSuitMask
sta PotentialTrump
lda #$00
sta CursorPos
rts
PerformDiscarding
lda Dealer
beq PDSouth
; bne PDNotSouth ; Handle case of human player separately
; jmp PDSouth ; Can't beq PDSouth - too far away
PDNotSouth
lda #$00
sta HighCardScore ; Assume highest rating of a card is zero
ldx HandEndOffset
stx HighCardNum ; Assume the best card is the last one
PDCardLoop1
lda DeckStart,x ; Get a card from the hand
and #RankMask ; Extract its rank
eor #RankMask ; Effectively inverts rank, turning high to low
sta CardScore ; Starts the card's score as seven minus its rank
; (offset by two bits - no reason to move them)
lda DeckStart,x
and #RealSuitMask
cmp PotentialTrump ; Is this card a trump?
beq PDCardTrump ; Yes, do not add to the score
lda CardScore
clc
adc #$20 ; Card is not trump; increase its score
sta CardScore
lda DeckStart,x
and #RankMask
cmp #AceRankValue ; Is this card an off-suit ace?
beq PDCardAce ; Yes, do not check if it's alone
lda DeckStart,x
and #RealSuitMask
sta T1 ; T1 will hold the suit of this card so we can use
; it to count how many of the same suit we have
stx T2 ; Stow the current loop index for recovery later
lda #$00
sta NumInSuit
ldx HandEndOffset
PDCardLoop2
lda DeckStart,x
and #RealSuitMask
cmp T1 ; Is this card of the same suit?
bne PDC1 ; No, move on
inc NumInSuit ; Yes, increment count of the suit
PDC1
dex
cpx HandStartOffset
bpl PDCardLoop2
ldx T2 ; Restore the original loop index
lda NumInSuit
cmp #$01 ; Is this card the only one of its suit?
bne PDC2 ; No, there are others
lda CardScore ; Yes, it's the only one of the suit
clc
adc #$20 ; Increase its card score to encourage voiding
sta CardScore
PDCardAce
PDC2
PDCardTrump
lda CardScore
cmp HighCardScore ; Is the score of the current card the highest?
bcc PDC3 ; No, go on to the next card
lda CardScore ; Yes, save the score and the index of the card
sta HighCardScore
stx HighCardNum
PDC3
dex
cpx HandStartOffset
bpl PDCardLoop1
lda HighCardNum
bpl PDSwap ; HighCardNum should always be positive
PDSouth
lda TriggerTrack
cmp #TriggerOn
bne PDSouthSelecting
jsr InitializeSelectSound
lda CursorPos
clc
adc HandStartOffset
bpl PDSwap ; Result of addition should always be positive
PDSouthSelecting
ldx #$05
jsr MoveCursor
rts
PDSwap ; Assumes index of card to swap is in accumulator
tax
lda DeckStart,x
pha ; Push card to swap onto stack
txa
tay ; Save its index in the y register
ldx Dealer
lda Upcards,x ; Get the upcard (need original because dealer might
; not be the bidder)
pha ; Save the upcard to the stack
tya ; Retrieve index of card to swap
tax
pla ; Retrieve the upcard from the stack
sta DeckStart,x ; Place upcard in hand
ldx Dealer
pla ; Retrieve card to be swapped from stack
ora #HideCardValue
sta Upcards,x ; Discard it!
jsr StartPlaying
rts
StartShuffle
lda #StageShuffle
sta Stage
lda #$08
sta Turn
RefreshDeck
ldx #CardsInDeck-1
RD1
lda NewDeck,x
sta DeckStart,x
dex
bpl RD1
rts
PerformShuffle
; Note that the shuffling is done from within the specialized
; shuffle kernel, since one shuffle takes too long to do in overscan.
; However, the counter of shuffles (Turn) is still decremented here.
dec Turn
bpl PSOut
jsr StartDeal
PSOut
rts
StartDeal
lda #StageDeal
sta Stage
rts
PerformDeal
; Flag all the cards in all the hands as available.
ldx #CardsInHand*4-1
DealLoop
lda DeckStart,x
and #ShowCardMask
sta DeckStart,x
dex
bpl DealLoop
; Reveal the turned-up card.
ldx Dealer
lda Upcards,x
and #ShowCardMask
sta Upcards,x
sta Upcard ; Save upcard for use in AnalyzeHand
jsr StartBidding1
rts
StartNewGame
; StageNewGame, MsgNumBlank, and the value to clear the attract-mode
; color adjustment are all zero.
lda #$00
sta Stage
sta MessageNum
sta AttractMask
rts
PerformNewGame
lda #$00
sta Team1Score
sta Team2Score
jsr RandomByte
and #PlayersMask
sta Dealer
jsr StartNewHand
rts
StartNewHand
lda #StageNewHand
sta Stage
rts
PerformNewHand
lda #$00
sta Team1Tricks
sta Team2Tricks
lda Dealer
clc
adc #$01
and #PlayersMask
sta Dealer
jsr StartShuffle
rts
StartGameOver
lda #StageGameOver
sta Stage
lda #MsgNumBlank
sta MessageNum
; lda #FramesLongWait
; sta FrameCounter
lda #$00
sta AttractFrameCounter
lda #$ff
sta SoundFrameCounter
rts
PerformGameOver
lda TriggerTrack
cmp #TriggerOn
bne PGOHold
jsr StartNewGame
jmp ProgStart
PGOHold
dec AttractFrameCounter ; Is it time to change the color scheme?
bne PGOOut ; No, just exit.
; (Color change occurs every 256 frames,
; about 4.266 seconds NTSC and 5.12
; seconds PAL.)
PGOColorChange
jsr RandomByte ; Get a random color adjustment factor.
sta AttractMask
PGOOut
rts
StartPlaying
lda TrumpSuitMask ; Has both display and "real" suit during bidding
and #RealSuitMask
sta TrumpSuitMask ; Now has only "real" suit
lda #$00
sta TrickNum
lda Choice
cmp #ChoiceAlone
beq SPCalcSkip
lda #$04
sta CardsInFullTrick
lda #NoPlayer
bmi SPDoSkip
SPCalcSkip
lda #$03
sta CardsInFullTrick
ldx Bidder
lda PlayerToPartnerTable,x
SPDoSkip
sta PlayerToSkip
lda Dealer
SPAdvanceLeader
clc
adc #$01
and #PlayersMask
cmp PlayerToSkip
beq SPAdvanceLeader
sta Leader
sta Turn
lda #$00
ldx #SuitsInDeck-1
SPClearBroken
sta SuitBroken,x
dex
bpl SPClearBroken
ldx DeclinedSuit
bmi SPSetTrumpsLeft
lda #$01 ; Mark the turned-down suit as broken since one
sta SuitBroken,x ; less card is available.
SPSetTrumpsLeft
lda DeclinedBower
eor #FlipColorSuitMask
cmp TrumpSuitMask
bne SPSetTrumpsLeft20
lda #%10111011 ; Left bower was turned down, so there are only
ldx #$06 ; six trump available.
bne SPSetTrumpsLeft30
SPSetTrumpsLeft20
lda #%11111011 ; Left bower was not turned down, so there are
ldx #$07 ; seven trump available.
SPSetTrumpsLeft30
sta TrumpsLeft
stx NumTrumpsLeft
lda #RightRankValue
sta HighestRemainingTrump
ContinuePlaying
lda #StagePlaying
sta Stage
lda Turn
beq CP1
sta CursorPos ; Value for south and cursor are both $00
CP1
lda #$00
sta CardInTrickNum
sta GatheredInfo
lda #FramesWait
sta FrameCounter
rts
PerformPlaying
ldx Turn
beq PPSouth
; bne PPNotSouth
; jmp PPSouth
PPNotSouth
lda Upcards,x ; Has a card been laid down by the computer player?
bmi PPTimeToAct ; No, go pick a card
dec FrameCounter
beq PPAdvance
rts
PPAdvance
inc CardInTrickNum
lda CardInTrickNum
cmp CardsInFullTrick
beq PPTrickComplete
lda #$00
sta GatheredInfo
sta CursorPos
lda Turn
PPAdvanceTurn
clc
adc #$01
and #PlayersMask
cmp PlayerToSkip
beq PPAdvanceTurn
sta Turn
PPSetDelay
lda #FramesWait
sta FrameCounter
rts
PPTrickComplete
jsr StartAddToTricks
rts
PPTimeToAct
ldx Turn
jsr GetHandOffsets
lda GatheredInfo
beq PPComputerGatherInfo
ldx CardInTrickNum
beq PPComputerLead
jsr PickCardToPlay
bpl PPPlayCard ; Result of addition at end of PickCardToPlay is
; always positive. Use bpl instead of jmp.
PPComputerLead
jsr PickCardToLead ; Result of addition at end of PickCardToLead is
; always positive. Use bpl instead of jmp.
bpl PPPlayCard
PPComputerGatherInfo
jsr FindLegalPlays
ldx CardInTrickNum
beq PPComputerGatherInfo2
jsr GatherInfoNonLeader
bmi PPComputerGatherInfoDone ; When GatherInfoNonLeader finishes, it is
; due to a loop index becoming negative.
; Use bmi instead of jmp.
PPComputerGatherInfo2
jsr GatherInfoLeader
PPComputerGatherInfoDone
lda #$01
sta GatheredInfo
rts
PPSouth
lda TriggerTrack
cmp #TriggerOn
bne PPSouthSelecting
ldx Turn
jsr GetHandOffsets
lda CursorPos
clc
adc HandStartOffset
sta CardToPlayOffset
jsr IsCardValidPlay
beq PPInvalidSelection
jsr InitializeSelectSound
lda #NoCursorPos
sta CursorPos
jmp PPPlayCard
PPInvalidSelection
jsr InitializeBuzzSound
rts
PPSouthSelecting
ldx #$05
jsr MoveCursor
rts
PPPlayCard
ldx CardToPlayOffset
lda DeckStart,x
sta CardToPlay ; Tuck away the card to be played for safe keeping
lda CardInTrickNum ; Check if this is the first play of the trick
bne PPNotFirst ; It's not the first play of the trick
lda CardToPlay
sta HighCard ; It is the first card of the trick, so it's the
and #RealSuitMask ; winner and its suit is recorded as the led suit
sta LeadSuitMask
lda Turn
sta TrickWinner
bpl PPShowPlay ; Turn is always a positive value. Use bpl instead
; of jmp.
PPNotFirst
lda CardToPlay
and #RealSuitMask
cmp TrumpSuitMask ; Is the card that was played a trump?
beq PPTrumped ; Yes, go handle it
cmp LeadSuitMask ; The card wasn't trump - is it in the led suit?
bne PPShowPlay ; No, so just show the card, since it won't have any
; impact on the trick.
PPFollowedSuit
lda HighCard
and #RealSuitMask
cmp TrumpSuitMask ; Is the current high card a trump?
beq PPShowPlay ; Yes, and the card just played wasn't, so it can't win
lda HighCard ; No, so see if this card beat the high card
and #RankMask
sta T1
lda CardToPlay
and #RankMask
cmp T1 ; Is the card that was played higher than the current
; winning card in the trick?
bmi PPShowPlay ; No, so just show the card
lda CardToPlay ; Yes, save its information
sta HighCard
lda Turn
sta TrickWinner
bpl PPShowPlay ; Turn is always a positive value. Use bpl instead
; of jmp.
PPTrumped
lda HighCard
and #RealSuitMask
cmp TrumpSuitMask ; Is the current high card a trump?
bne PPTrumpSucceeds ; No, so this card will become high
lda HighCard ; Yes, so we have to see if this card is a higher trump
and #RankMask
sta T1
lda CardToPlay
and #RankMask
cmp T1 ; Is the card that was played higher than the current
; winning card in the trick?
bmi PPShowPlay ; No, so just show the card
PPTrumpSucceeds
lda CardToPlay
sta HighCard
lda Turn
sta TrickWinner
PPShowPlay
ldx Turn
lda CardToPlay
sta Upcards,x ; Put the card on the "table"
ldx CardToPlayOffset
ora #CardPlayedValue
sta DeckStart,x ; Mark the card played as "used"
lda Turn
bne PPOut
jmp PPAdvance ; Force an advance if it's south's turn. (jmp needed)
PPOut
rts
StartAddToTricks
lda #StageAddToTricks
sta Stage
lda TrickWinner
and #$01
tax
inc Team1Tricks,x
lda #$00
sta ProcessedCounters
lda #FramesShortWait
sta FrameCounter
rts
PerformAddToTricks
dec FrameCounter
beq PAT010
rts
PAT010
lda ProcessedCounters
bne PAT200
inc ProcessedCounters
inc FrameCounter ; Force one more process frame
ldx #$03
PAT025
lda Upcards,x
bmi PAT050 ; The card wasn't played since the partner went alone.
and #RealSuitMask
tay
lda #$01
sta SuitBroken,y ; Register that the suit of this card is now broken.
cpy TrumpSuitMask ; Is this card trump?
bne PAT050 ; No, so perform no more checks on it.
dec NumTrumpsLeft
lda Upcards,x
and #RankMask
lsr
lsr ; Convert rank to an index
tay
lda TrumpsLeft
and RankToTrumpsLeftTable,y
sta TrumpsLeft ; Clear the bit corresponding to this trump in the
; array of remaining trumps.
PAT050
dex
bpl PAT025
ldx #$07 ; Scan for highest remaining trump, starting with
; the right bower.
PAT100
lda TrumpsLeft
and TrumpsLeftIndexTable,x
beq PAT180 ; This rank is not among those remaining.
txa
asl
asl ; Convert index into the proper bits for a card rank.
sta HighestRemainingTrump
rts
PAT180
dex
bpl PAT100
; If there are no trumps left in the game, the
; HighestRemainingTrump variable will still hold the
; last trump that was high. However, since the
; leader will be out of trump, the variable will not
; be examined.
rts
PAT200
ldx #$03
PATClear
lda Upcards,x
ora #NoCard
sta Upcards,x
dex
bpl PATClear
lda #$00
sta CardInTrickNum
inc TrickNum
lda TrickNum
cmp #TricksInRound
beq PATHandComplete
lda TrickWinner
sta Leader
sta Turn
jsr ContinuePlaying
rts
PATHandComplete
jsr StartAddToScore
rts
StartAddToScore
lda #StageAddToScore
sta Stage
ldx BiddingTeam
lda Team1Tricks,x
cmp #$03
bmi SASEuchre
cmp #$05
beq SASMarch
lda #$01
bne SASSet
SASEuchre
txa
eor #$01
tax
SASMarch2
lda #$02
bne SASSet
SASMarch
lda PlayerToSkip
cmp #NoPlayer
beq SASMarch2
lda #$04
SASSet
sta ScoreIncrement
stx ScoringTeam
stx SoundTableOffset
lda #FramesScoreIncrement
sta FrameCounter
rts
PerformAddToScore
lda ScoreIncrement
beq PASAdvance
dec FrameCounter
lda FrameCounter
cmp #FramesScoreIncrement/4
beq PASIncrement
cmp #$00
beq PASNext
rts
PASIncrement
ldx ScoringTeam
inc Team1Score,x
lda FrameCounter
sta SoundFrameCounter
rts
PASNext
dec ScoreIncrement
lda #FramesScoreIncrement
sta FrameCounter
rts
PASAdvance
lda Team1Score
cmp #$0a
bpl PASOver
lda Team2Score
cmp #$0a
bpl PASOver
jsr StartBetweenHands
rts
PASOver
jsr StartGameOver
rts
StartBetweenHands
lda #StageBetweenHands
sta Stage
rts
PerformBetweenHands
lda TriggerTrack
cmp #TriggerOn
bne PBH1
jsr StartNewHand
PBH1
rts
; determine if a card can be played on a trick
;
; Requires values in CardToPlayOffset, HandStartOffset, HandEndOffset,
; Turn, Leader
;
; returns: a = 00 if card cannot be played, 01 if it can
IsCardValidPlay
ldx CardToPlayOffset
lda DeckStart,x
bpl VPAvailable ; No, continue checking
lda #$00 ; Yes, so it cannot be played again
rts
VPAvailable
lda Turn
cmp Leader ; Is this card leading the trick?
beq VPIsLeader ; Yes, so any card can be played
lda DeckStart,x ; Card is not leading the trick
and #RealSuitMask
cmp LeadSuitMask ; Does this card follow the led suit?
beq VPFollowingSuit ; Yes, so it can be played
lda #$00 ; No, check if hand has the led suit
sta LeadSuitCount
ldx HandEndOffset
VPCountLeadSuit
lda DeckStart,x
and #CardPlayedMask ; Was this card already played?
cmp #CardPlayedValue
beq VPAlreadyPlayed ; Yes, so don't bother checking its suit
lda DeckStart,x
and #RealSuitMask
cmp LeadSuitMask ; Is this card in the lead suit?
bne VPNotLeadSuit ; No, carry on
inc LeadSuitCount ; Yes!
VPNotLeadSuit
VPAlreadyPlayed
dex
cpx HandStartOffset
bpl VPCountLeadSuit
lda LeadSuitCount ; Does this hand have a card in the lead suit?
beq VPVoidInLeadSuit ; No, so an off-suit or trump can be played
lda #$00 ; Yes, so this card cannot be played
rts
VPVoidInLeadSuit
VPFollowingSuit
VPIsLeader
lda #$01
rts
; analyze a hand for its ability to take tricks
;
; Requires values in HandStartOffset, HandEndOffset, and PotentialTrump
AnalyzeHand
lda #$00
sta NumTrumps
sta NumOffLoneAces
sta HandStrength
sta UpcardFactor
lda PotentialTrump
ora #JackRankValue
and #RankMask+#RealSuitMask
sta RightBowerMask
eor #FlipColorSuitMask
sta LeftBowerMask
; Check on the trumps in the hand
ldx HandEndOffset
AHCountLoop1
lda DeckStart,x
and #RealSuitMask
cmp PotentialTrump ; Is this card a trump?
bne AHNotTrump ; No, so do nothing more with it, unless the left bower
AHIsTrump
inc NumTrumps ; Yes! Add one more to the count
lda DeckStart,x
jsr GetTrumpCardStrength
AHIT2
clc
adc HandStrength
sta HandStrength
bne AHCL1
AHNotTrump
lda DeckStart,x
and #RankMask+#RealSuitMask
cmp LeftBowerMask ; It's not of the trump suit, but is it the left bower?
bne AHCL1 ; No, go on to next card
lda #$06 ; Strength of the left bower
bne AHIT2 ; Add left bower to the hand strength
AHCL1
dex
cpx HandStartOffset
bpl AHCountLoop1
; Check on voids and off-suit lone aces, looping once through
; the hand for each non-trump suit
ldy #SuitsInDeck-1
AHCountLoop2
lda #$00
sta NumInSuit
ldx HandEndOffset
AHCountLoop3
cpy PotentialTrump ; Is this loop for trump?
beq AHCL2 ; Yes, advance out of the inner loop
sty T1 ; Loop control variable equivalent to suit
lda DeckStart,x
and #RealSuitMask
cmp T1 ; Is this card in the suit we're examining?
bne AHCL3 ; no, so ignore it
inc NumInSuit ; Yes, increase our count of the suit
lda DeckStart,x
and #RankMask
cmp #AceRankValue ; Is this card an ace?
bne AHCL3 ; No, carry on
inc HasAce ; Yes, indicate we have the ace of this suit
AHCL3
dex
cpx HandStartOffset
bpl AHCountLoop3
lda NumInSuit
cmp #$01 ; Do we have only one card in this suit?
bne AHCL2
lda HasAce ; Yes - is it the ace?
beq AHCL2
inc NumOffLoneAces ; It's a lone off-suit ace.
AHCL2
dey
bpl AHCountLoop2
ldx NumTrumps
lda NumTrumpsToStrength,x
clc
ldx NumOffLoneAces
adc NumOffLoneAcesToStrength,x
clc
adc HandStrength
sta HandStrength
lda Stage
cmp #StageBidding1 ; Is this the first bidding stage?
bne AHOut ; No, so the upcard isn't relevant
lda Dealer
eor Turn ; But 0 will be 1 if the upcard goes to an opponent
and #$01
bne AHUpOpp ; Upcard goes to opponent
lda Dealer ; Upcard goes to my team
cmp Turn ; Is it to me?
bne AHOut ; No, it's to my partner, and it's unclear what
; effect it will have
lda #$01
sta UpcardFactor
bne AHUpCalc
AHUpOpp
lda #$02
sta UpcardFactor
AHUpCalc
lda Upcard
jsr GetTrumpCardStrength
sta T1
lda UpcardFactor
cmp #$01 ; Does the upcard go to me?
beq AHUpMe
lda HandStrength ; No, subtract the strength of the upcard plus 3
; from the strength of my hand
sec
sbc T1
sec
sbc #$03
bmi AHBelowZero ; If strength is negative, set it to zero
AHUCSet
sta HandStrength
AHOut
rts
AHBelowZero
lda #$00
beq AHUCSet
AHUpMe
lda HandStrength ; Upcard goes to me; add its strength to my hand
clc
adc T1
sta HandStrength
lda NumTrumps
cmp #$03
bmi AHOut ; If I have less than three trumps, the upcard
; won't add the many-trump advantage
beq AHUpMe3 ; Do I have three trump exactly?
cmp #$05 ; No... how about five?
beq AHUpMeX ; If I have five trump, the upcard won't help in
; terms of number of trump
lda #$03
AHUpMeW
clc ; I have four trump and will go to five
adc HandStrength
sta HandStrength
bne AHUpMeX
AHUpMe3
lda #$04 ; I have three trump and will go to four
bne AHUpMeW
AHUpMeX
lda NumTrumps
clc
adc NumOffLoneAces
cmp #$05 ; If I currently have only trump and off-trump
; lone aces, the upcard will eliminate an advantage.
; Subtract a fudge factor of 2.
bne AHOut
lda HandStrength
sec
sbc #$02
jmp AHUCSet
; Assumptions: card is in accumulator, is trump, and the LeftBowerMask is set
GetTrumpCardStrength
and #RankMask+#RealSuitMask
cmp LeftBowerMask
beq GTCSLeft
and #RankMask
lsr
lsr
tay
lda PotentialTrumpRankToStrength,y ; Must be y since calling routines
; normally use x for loop control
rts
GTCSLeft
lda #$06
rts
DecideBid
lda HandStrength
cmp #24
bpl DBAlone
cmp #23
beq DBUorA
cmp #14
bpl DBUp
cmp #13
beq DBPorU
lda #ChoicePass
beq DBEnd
DBAlone
lda #ChoiceAlone
beq DBEnd
DBUorA
lda Dealer
clc
adc #$01
and #$03
cmp Turn ; Will I lead first?
beq DBAlone ; If so, go alone, otherwise just call.
DBUp
lda #ChoiceCall
bne DBEnd
DBPorU
lda Turn ; Borderline hand... depending on the score, choose
; to pass or call.
and #$01 ; If last bit is set, bidder is west or east
beq DBNS ; Bidder is north or south
ldx Team2Score
ldy Team1Score
jmp DB3a
DBNS
ldx Team1Score
ldy Team2Score
; The end result is that x has "our" score
; and y has "their" score
DB3a
cpx #$09 ; Do we have nine points?
bne DB3b ; No, move on...
cpy #$08 ; Does the other team have eight or more points?
bpl DB3c ; Yes, go to next check
cpy #$06 ; Does the other team have six or seven points?
bmi DB3c ; No, go to next check
lda #ChoiceCall ; With score 9-6 or 9-7, it's better to risk a euchre
; than a march
bne DBEnd
DB3b
cpx #$06 ; Do we have six points?
bne DB3c ; No, move on...
cpy #$04 ; Does the other team have four or more points?
bpl DB3c ; Yes, go to next check
cpy #$02 ; Does the other team have two or three points?
bmi DB3c ; No, go to next check
lda #ChoiceCall ; Call to protect the lead (a march would let the
; other team catch up and another would win the
; game for them)
bne DBEnd
DB3c
lda #ChoicePass
beq DBEnd
DBEnd
sta Choice
rts ; Whew!
; determine which cards in a hand are legal to play in a trick
;
; Requires values in CardToPlayOffset, HandStartOffset, HandEndOffset,
; Turn, Leader (all due to use of IsCardValidPlay)
;
; returns: five-byte array in LegalPlays; each byte = 00 if card cannot be
; played, 01 if it can
FindLegalPlays
ldx #CardsInHand-1
FLP2
stx T3
txa
clc
adc HandStartOffset
sta CardToPlayOffset
jsr IsCardValidPlay
ldx T3
sta LegalPlays,x
dex
bpl FLP2
rts
GatherInfoLeader
lda #$00
sta NumTrumps
lda #NoCard
sta BestLeadNum
sta BestTrumpNum
lda HandEndOffset
sta T1
ldx #CardsInHand-1
GILLoop
lda LegalPlays,x
beq GILOut ; This card was already played, so don't examine it.
stx T2 ; Save x, the offset into the hand, for later use.
ldx T1 ; Load the offset into the whole deck into x.
lda DeckStart,x
and #RankMask
sta CurrentCardScore
lda DeckStart,x
ldx T2 ; Restore original value of x.
and #RealSuitMask
cmp TrumpSuitMask ; Is the card being examined trump?
bne GIL100 ; No.
inc NumTrumps ; Yes, so increase the count of trumps in this hand.
lda CurrentCardScore ; Reload since it is expected for the upcoming
; comparison and the store in GIL010.
ldy BestTrumpNum ; Have we designated a best trump?
bmi GIL010 ; No, so do so!
cmp BestTrumpScore ; Is this card the best trump so far?
bmi GILOut ; No, so skip the other checks.
GIL010
sta BestTrumpScore
stx BestTrumpNum
jmp GILOut
GIL100
tay ; Copy suit (from prior to cmp TrumpSuitMask) to y
lda SuitBroken,y ; 0 if not broken, 1 if broken
bne GIL150
lda #BrokenModifier
bne GIL180
GIL150
lda #UnbrokenModifier
GIL180
clc
adc CurrentCardScore
sta CurrentCardScore
ldy BestLeadNum
bmi GIL190
cmp BestLeadScore
bmi GILOut
GIL190
sta BestLeadScore
stx BestLeadNum
GILOut
dec T1
dex
bpl GILLoop
rts
PickCardToLead
lda BestTrumpNum ; Was any trump found in the hand?
bmi PCTL250 ; No!
lda BestTrumpScore
cmp HighestRemainingTrump ; Yes - were any of them the highest still out?
bne PCTL250 ; No!
lda NumTrumpsLeft ; We have the highest trump still in the game.
lsr
sta T4 ; Number of remaining trump, divided by 2, rounded down.
lda NumTrumps
cmp T4 ; Do we have half of the trump left?
bmi PCTL250 ; No.
PCTL200
lda BestTrumpNum ; Yes, so play our highest trump.
bpl PCTLCardSelected
PCTL250
lda BestLeadNum ; Play the card designated as "best" lead.
bmi PCTL200 ; If no best non-trump lead exists, play the best trump.
PCTLCardSelected
clc
adc HandStartOffset
sta CardToPlayOffset
rts
GatherInfoNonLeader
lda #NoCard
sta HighWinnerNum
sta LowWinnerNum
sta LowCardNum
sta IdealTrumpNum
lda HighCard
and #RankMask
sta TrickWinningScore
lda HighCard
and #RealSuitMask
cmp TrumpSuitMask ; Is the winning card trump?
beq GINL10 ; Yes!
lda #LeadSuitScoreValue ; No, so it must be of the led suit
bne GINL15
GINL10
lda #TrumpScoreValue
GINL15
clc
adc TrickWinningScore
sta TrickWinningScore
ldy Turn
lda PlayerToPartnerTable,y
cmp TrickWinner ; Is my partner winning the trick?
bne GINLOpponentsWinning
lda #$00
beq GINL20
GINLOpponentsWinning
lda #$01
GINL20
sta OpponentsWinning ; 1 if the opponents are winning
; the trick or 0 if my partner is winning
ldy TrickNum
lda TrickToIdealTrump,y
sta TrueIdealScore
lda HandEndOffset
sta T1
ldx #CardsInHand-1
GINLLoop
lda LegalPlays,x
beq GINLOut ; This card is not a valid play or was already played,
; so don't examine it
stx T2
ldx T1
lda DeckStart,x
and #RankMask
sta CurrentCardScore
lda DeckStart,x
ldx T2
and #RealSuitMask
cmp TrumpSuitMask ; Is the current card trump?
beq GINL40 ; Yes!
cmp LeadSuitMask ; No. Is it of the led suit?
bne GINL70 ; No, so there's nothing to add.
lda #LeadSuitScoreValue ; Yes, this suit was led, so it is of more worth.
bne GINL60
GINL40
lda #TrumpScoreValue
GINL60
clc
adc CurrentCardScore
sta CurrentCardScore
bne GINL80
GINL70
lda CurrentCardScore ; There was nothing to add to the current card's
; relative strength, but it needs to be in the
; accumulator. The last data loaded there was
; the card's suit!
GINL80
ldy LowCardNum ; Has a card been designated as lowest in the hand?
bmi GINL90 ; No, so designate this one. This is particularly
; important as it is the default card to play.
cmp LowCardScore ; Is it the lowest so far?
bpl GINLHigher ; No, but continue comparing it...
GINL90
sta LowCardScore
stx LowCardNum
GINLHigher
cmp TrickWinningScore ; Can the current card beat the trick winner?
bmi GINLOut ; No, so stop processing it
ldy HighWinnerNum ; Has a card been designated as the highest winner?
bmi GINLHigherSet ; No, so designate this one.
cmp HighWinnerScore ; Does this card beat the current highest winner?
bmi GINLLowWinner ; No, but check if it is the lowest winning card.
GINLHigherSet
sta HighWinnerScore ; Yes, it's the highest winner encountered so far.
stx HighWinnerNum
GINLLowWinner
ldy LowWinnerNum ; Has a card been designated as the lowest winner?
bmi GINLLowWinSet ; No, so designate this one.
cmp LowWinnerScore ; Is this card lower than the current lowest winner?
bpl GINLIdeal ; No, but check if it's an ideal trump.
GINLLowWinSet
sta LowWinnerScore ; Yes, it's the lowest winner encountered so far.
stx LowWinnerNum
GINLIdeal
cmp #NineRankValue+#TrumpScoreValue ; Is the current card a trump?
bmi GINLOut ; No, so there's nothing else to consider.
ldy LeadSuitMask
cpy TrumpSuitMask ; Was trump led?
beq GINLOut ; Yes; treat it as a normal suit, ignoring
; considerations of "ideal".
ldy IdealTrumpNum ; Has any trump been encountered yet?
bmi GINLSetNewIdeal ; No, so this one is our "ideal" so far.
cmp TrueIdealScore ; How does this trump compare to the true ideal?
bmi GINLLessThanTrueIdeal ; It's lower...
cmp IdealTrumpScore ; It's >= true ideal, but is it less than closest?
bpl GINLOut ; No, so we've got the closest so far
GINLSetNewIdeal
sta IdealTrumpScore
stx IdealTrumpNum
jmp GINLOut
GINLLessThanTrueIdeal
cmp IdealTrumpScore ; This trump is less than true ideal; can we get closer?
bpl GINLSetNewIdeal ; Yes!
GINLOut
dec T1
dex
bpl GINLLoop
rts
PickCardToPlay
; Now we've collected all the information about the hand we need to make
; a decision. "Player" refers to the player in the trick; i.e. player #2
; is the second person to play in the trick, the one immediately after
; the leader.
lda CardInTrickNum
cmp #$01 ; Is this for player #2?
PCTPPlayer2
bne PCTPPlayer3 ; No, keep checking
; Player 2: If it's possible to win the trick, do so with the highest
; possible non-trump or the trump closest to ideal, otherwise play the
; lowest valid card
PCTPP2a
lda HighWinnerNum
bmi PCTPPlayLow
lda IdealTrumpNum
bpl PCTPCardSelected
lda HighWinnerNum
bpl PCTPCardSelected
PCTPPlayLow
lda LowCardNum
bpl PCTPCardSelected
PCTPPlayer3
cmp #$02 ; Is this for player #3?
bne PCTPPlayer4 ; No, so it must be for player #4
; Player 3: If partner is winning with a queen or lower, or if an
; opponent is winning the trick, beat the trick just as player 2
; does, otherwise play the lowest valid card
lda OpponentsWinning ; Is opponent winning?
bne PCTPP2a ; Yes, try to beat it
lda TrickWinningScore ; Partner is winning, but high enough?
cmp #KingRankValue ; Is the winning card a king or better?
bmi PCTPP2a ; No, try to beat it
bpl PCTPPlayLow ; Yes, go under if possible
PCTPPlayer4
; Player 4: If partner is winning, play low, otherwise win with the lowest
; available winning card
lda OpponentsWinning ; Is opponent winning?
beq PCTPPlayLow ; No, so play low
lda LowWinnerNum ; Yes - can this hand win?
bmi PCTPPlayLow ; No, so play low
;bpl PCTPCardSelected ; Yes, play this card
PCTPCardSelected
clc
adc HandStartOffset
sta CardToPlayOffset
rts
; Change jack of trumps and jack of suit of same color as trumps to the right
; bower and left bower
;
; Requires that TrumpSuitMask be set prior to call
CreateBowers
lda TrumpSuitMask
ora #JackRankValue
sta RightBowerMask
eor #FlipColorSuitMask
sta LeftBowerMask
ldx #CardsInDeck-1
CBLoop
lda DeckStart,x
and #RankMask+#RealSuitMask ; compare against card information only
cmp RightBowerMask ; Is this card the jack of trump?
bne CBLB ; No, check if it is the other jack of the same color
lda TrumpSuitMask
ora #RightRankValue ; Grant the card the right bower rank
bne CBSet ; Will always be > 0, bne instead of jmp
CBLB
cmp LeftBowerMask ; Is this card the jack of the same color as trump?
bne CBLoopEnd ; No, it is not either bower
lda TrumpSuitMask ; By doing this we make the "real" suit trump while the
; displayed suit remains the original.
ora #LeftRankValue ; Grant the card the left bower rank
CBSet
sta T4
lda DeckStart,x
and #HideCardValue+#DispSuitMask
ora T4 ; Do not change whether card is hidden or shown
sta DeckStart,x
CBLoopEnd
dex
bpl CBLoop
rts
; find the zero page address of the first and last cards of a player's hand
; x = hand (0=south, 1=west, 2=north, 3=east)
;
; returns: HandStartOffset = zero page address of first card of desired hand
; HandEndOffset = zero page address of last card of desired hand
GetHandOffsets
lda HandToStartOffset,x
sta HandStartOffset
clc
adc #CardsInHand-1
sta HandEndOffset
rts
; routine for getting images and colors of a card
; a = card
; x = image pointer for rank
; y = image pointer for suit
; returns: a = color of card
GetCardGraphics
sta T1
sty T2
and #RankMask
lsr
lsr
jsr GetRankImage
lda T1
and #DispSuitMask
lsr
lsr
lsr
lsr
lsr
ldx T2
jsr GetSuitImage
lda T1
bmi HideCard
and #RealSuitMask
tax
lda SuitToColorTable,x
eor AttractMask
rts
HideCard
lda #CardTableColor
eor AttractMask
rts
; routine to draw a single card
; assumes ImgPtr1 and ImgPtr2 point to proper images, sprites are
; positioned, and so on
DrawSingleCard
ldy #$05
DrawCardLoop
sta WSYNC
lda (ImgPtr1),y
sta GRP0
lda (ImgPtr2),y
sta GRP1
dey
bpl DrawCardLoop
sta WSYNC
rts
; routine to draw two cards
; assumes ImgPtr1-4 point to proper images, sprites are positioned,
; and so on
DrawTwoCards
sta WSYNC
sta HMOVE
ldy #$05
DrawCardLoop2
nop
nop
ror T1
ror T1
lda (ImgPtr3),y
tax
lda (ImgPtr1),y
sta GRP0
stx GRP0
pha
lda (ImgPtr4),y
tax
lda (ImgPtr2),y
sta GRP1
nop
stx GRP1
pla
sta WSYNC
dey
bpl DrawCardLoop2
rts
; input: x = $00 for single-line, $01 for double
DrawBookkeeping
ldy #$05
stx T4
DrB1
ldx T4
DrB2
sta WSYNC
lda (ImgPtr1),y
sta GRP0
lda (ImgPtr2),y
sta GRP1
dex
bpl DrB2
dey
bpl DrB1
rts
; Six-digit score display
; original version by Robert Colbert in the Stella mailing list message
; "Re: [stella] Displaying scores - how?" from March 11, 1997
; (http://www.biglist.com/lists/stella/archives/199703/msg00219.html)
; Adapted to four-digit display
DrawMessageText
ldy #$05
loop2
sta WSYNC
sty T2
lda (ImgPtr1),y
sta GRP0
lda (ImgPtr2),y
sta GRP1
lda (ImgPtr3),y
tax
lda (ImgPtr4),y
tay
ror T1
lda $00
lda $00
stx GRP0
sty GRP1
ldy T2
dey
bpl loop2
rts
END_UTIL = *
org $fe00
echo *-END_UTIL, "bytes available in utility area 1: ", END_UTIL, "-", *-1
ImageBanks
; All images are reversed since they are read by decrementing loops.
LetterImageStart
LetterImageSpace
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
LetterImageD
.byte %01111100
.byte %01100110
.byte %01100110
.byte %01100110
.byte %01100110
.byte %01111100
LetterImageP
.byte %01100000
.byte %01100000
.byte %01111100
.byte %01100110
.byte %01100110
.byte %01111100
LetterImageS
.byte %00111100
.byte %00000110
.byte %00000110
.byte %00111100
.byte %01100000
.byte %00111100
LetterImageU
.byte %00111100
.byte %01100110
.byte %01100110
.byte %01100110
.byte %01100110
.byte %01100110
RankImageStart
RankImage9
.byte %00111100
.byte %01000110
.byte %00000110
.byte %00111110
.byte %01100110
.byte %00111100
RankImage10
.byte %11101110
.byte %01011011
.byte %01011011
.byte %01011011
.byte %11011011
.byte %01001110
RankImageJ
LetterImageJ
.byte %00111100
.byte %01100110
.byte %00000110
.byte %00000110
.byte %00000110
.byte %00001110
RankImageQ
LetterImageQ
.byte %00111010
.byte %01100100
.byte %01101010
.byte %01100110
.byte %01100110
.byte %00111100
RankImageK
LetterImageK
.byte %01100110
.byte %01100110
.byte %01101100
.byte %01111000
.byte %01101100
.byte %01100110
RankImageA
LetterImageA
.byte %01100110
.byte %01100110
.byte %01111110
.byte %01100110
.byte %00111100
.byte %00011000
; The two bower images are normally copies of the "J" used for the jack.
; It's simpler to spend twelve bytes duplicating the letter and allow
; the rank (multiplied by six) to be used to access the image address
; directly than to always adjust for bowers. If this is compiled with
; the BOWER constant, "LB" and "RB" are used instead of "J".
IFCONST BOWER
RankImageLeft
.byte %11101100
.byte %10001010
.byte %10001010
.byte %10001100
.byte %10001010
.byte %10001100
RankImageRight
.byte %10101100
.byte %10101010
.byte %10101010
.byte %11001100
.byte %10101010
.byte %11001100
ELSE
RankImageLeft
.byte %00111100
.byte %01100110
.byte %00000110
.byte %00000110
.byte %00000110
.byte %00001110
RankImageRight
.byte %00111100
.byte %01100110
.byte %00000110
.byte %00000110
.byte %00000110
.byte %00001110
ENDIF
SuitImageStart
SuitImageHeart
.byte %00010000
.byte %00111000
.byte %01111100
.byte %11111110
.byte %11101110
.byte %01000100
SuitImageDiamond
.byte %00011000
.byte %00111100
.byte %01111110
.byte %01111110
.byte %00111100
.byte %00011000
SuitImageClub
.byte %00011000
.byte %01111110
.byte %11111111
.byte %00011000
.byte %00111100
.byte %00011000
SuitImageSpade
.byte %00111000
.byte %10111010
.byte %11111110
.byte %01111100
.byte %00111000
.byte %00010000
ArrowImageStart
ArrowImageSouth
.byte %00011000
.byte %00111100
.byte %01111110
.byte %11011011
.byte %00011000
.byte %00011000
ArrowImageWest
.byte %00110000
.byte %01100000
.byte %11111111
.byte %11111111
.byte %01100000
.byte %00110000
ArrowImageNorth
.byte %00011000
.byte %00011000
.byte %11011011
.byte %01111110
.byte %00111100
.byte %00011000
ArrowImageEast
.byte %00001100
.byte %00000110
.byte %11111111
.byte %11111111
.byte %00000110
.byte %00001100
ScoreImageStart
.byte %00000000
.byte %01110111
.byte %01010101
.byte %01010101
.byte %01010101
.byte %01110111
.byte %00000000
.byte %01110111
.byte %01010010
.byte %01010010
.byte %01010110
.byte %01110010
.byte %00000000
.byte %01110111
.byte %01010100
.byte %01010111
.byte %01010001
.byte %01110111
.byte %00000000
.byte %01110111
.byte %01010001
.byte %01010011
.byte %01010001
.byte %01110111
.byte %00000000
.byte %01110001
.byte %01010001
.byte %01010111
.byte %01010101
.byte %01110001
.byte %00000000
.byte %01110111
.byte %01010001
.byte %01010111
.byte %01010100
.byte %01110111
.byte %00000000
.byte %01110111
.byte %01010101
.byte %01010111
.byte %01010100
.byte %01110111
.byte %00000000
.byte %01110100
.byte %01010100
.byte %01010010
.byte %01010001
.byte %01110111
.byte %00000000
.byte %01110111
.byte %01010101
.byte %01010010
.byte %01010101
.byte %01110111
.byte %00000000
.byte %01110111
.byte %01010001
.byte %01010111
.byte %01010101
.byte %01110111
.byte %00000000
.byte %01110111
.byte %00100101
.byte %00100101
.byte %01100101
.byte %00100111
.byte %00000000
.byte %01110111
.byte %00100010
.byte %00100010
.byte %01100110
.byte %00100010
.byte %00000000
.byte %01110111
.byte %00100100
.byte %00100111
.byte %01100001
.byte %00100111
.byte %00000000
.byte %01110111
.byte %00100001
.byte %00100011
.byte %01100001
.byte %00100111
TrickImageStart
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000001
.byte %00000001
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000101
.byte %00000101
.byte %00000001
.byte %00000001
.byte %00000000
.byte %00000000
.byte %00000101
.byte %00000101
.byte %00000101
.byte %00000101
.byte %00000000
.byte %00000000
.byte %00000101
.byte %00000101
.byte %00000101
.byte %00000101
.byte %00000010
.byte %00000010
.byte %00000101
.byte %00000101
NewDeck
.byte #HideCardValue + #NineRankValue + #HeartSuitValue
.byte #HideCardValue + #TenRankValue + #HeartSuitValue
.byte #HideCardValue + #JackRankValue + #HeartSuitValue
.byte #HideCardValue + #QueenRankValue + #HeartSuitValue
.byte #HideCardValue + #KingRankValue + #HeartSuitValue
.byte #HideCardValue + #AceRankValue + #HeartSuitValue
.byte #HideCardValue + #NineRankValue + #DiamondSuitValue
.byte #HideCardValue + #TenRankValue + #DiamondSuitValue
.byte #HideCardValue + #JackRankValue + #DiamondSuitValue
.byte #HideCardValue + #QueenRankValue + #DiamondSuitValue
.byte #HideCardValue + #KingRankValue + #DiamondSuitValue
.byte #HideCardValue + #AceRankValue + #DiamondSuitValue
.byte #HideCardValue + #NineRankValue + #ClubSuitValue
.byte #HideCardValue + #TenRankValue + #ClubSuitValue
.byte #HideCardValue + #JackRankValue + #ClubSuitValue
.byte #HideCardValue + #QueenRankValue + #ClubSuitValue
.byte #HideCardValue + #KingRankValue + #ClubSuitValue
.byte #HideCardValue + #AceRankValue + #ClubSuitValue
.byte #HideCardValue + #NineRankValue + #SpadeSuitValue
.byte #HideCardValue + #TenRankValue + #SpadeSuitValue
.byte #HideCardValue + #JackRankValue + #SpadeSuitValue
.byte #HideCardValue + #QueenRankValue + #SpadeSuitValue
.byte #HideCardValue + #KingRankValue + #SpadeSuitValue
.byte #HideCardValue + #AceRankValue + #SpadeSuitValue
MessageTableStart
MessageBlank
.byte #<LetterImageSpace, #<LetterImageSpace
.byte #<LetterImageSpace, #<LetterImageSpace
MessageSelectAction
ChoiceToLetterTable
.byte #<LetterImageP, #<LetterImageU
.byte #<LetterImageA, #<LetterImageSpace
MessageSelectTrump
.byte #<SuitImageHeart, #<SuitImageDiamond
.byte #<SuitImageClub, #<SuitImageSpade
CursorPosToSelectorPos
.byte $94, $05, $76, $f6
BidTeamToTrumpPos
.byte $a2,$48
TrickToIdealTrump
.byte #TenRankValue + #TrumpScoreValue
.byte #QueenRankValue + #TrumpScoreValue
.byte #AceRankValue + #TrumpScoreValue
.byte #RightRankValue + #TrumpScoreValue
.byte #RightRankValue + #TrumpScoreValue
PlayerToPartnerTable
.byte $02, $03, $00, $01
HandToStartOffset
.byte $00, $05, $0a, $0f
ImageBankOffsets
.byte $00
.byte (#SuitImageStart-#RankImageStart)/6
.byte (#ArrowImageStart-#RankImageStart)/6
.byte (#ScoreImageStart-#RankImageStart)/6
.byte (#TrickImageStart-#RankImageStart)/6
SuitToColorTable
.byte #RedSuitColor, #RedSuitColor, #BlackSuitColor, #BlackSuitColor
NumTrumpsToStrength
.byte $00, $00, $00, $00, $04, $07
NumOffLoneAcesToStrength
.byte $00, $03, $05, $06
PotentialTrumpRankToStrength
; Since the bower masks haven't been set yet, the rank of the jack is
; still third-lowest. The GetTrumpCardStrength routine adjusts for the
; left bower, and since the input card is definitely trump, its jack
; rank gets the highest strength value.
.byte $01, $01, $08, $02, $02, $04
RankToTrumpsLeftTable
; These are masks used to remove a particular trump from the list of
; trumps left in a round. Notice that the third-from-last bit is always
; zero since the jack is promoted to a bower.
.byte %11111010, %11111001, %11111011, %11110011
.byte %11101011, %11011011, %10111011, %01111011
TrumpsLeftIndexTable
; These are used to translate a loop index into a bit of a byte. In
; this game, they are only for examining the TrumpsLeft bit array.
; The third value is intentionally zero since there is no jack rank
; in the trump suit.
.byte %00000001, %00000010, %00000000, %00001000
.byte %00010000, %00100000, %01000000, %10000000
; routine to generate a random number
; original version by Erik Mooney in the Stella mailing list message
; "Re: [stella] demo update: PCMSD20.BIN" from April 14, 1997
; (http://www.biglist.com/lists/stella/archives/199704/msg00136.html)
; requires four memory locations to be reserved for generation
;
; returns: a = random number
RandomBit
lda rand4 ; +3 = 3
asl ; +2 = 5
asl ; +2 = 7
asl ; +2 = 9
eor rand4 ; +3 = 12 ;new bit is now in bit 6 of A
asl ; +2 = 14
asl ; +2 = 16 ;new bit is now in carry
rol rand1 ; +5 = 21 ;shift new bit into bit 0 of register
;bit 7 goes into carry
rol rand2 ; +5 = 26 ;shift old bit 7 into bit 8, etc.
rol rand3 ; +5 = 31
rol rand4 ; +5 = 36
rts ; +5 = 41
RandomByte
ldx #8 ; +2
RandomByte1
jsr RandomBit ; +6+41 = +47
dex ; +2
bne RandomByte1 ; +3 when taken (7 times), +2 when not (1 time)
; Total after all loops is 2+(47+2+3)*7+(47+2+2)=417
lda rand1 ; +3 = 420
rts ; +5 = 425, or roughly 5.6 scan lines
; routine to position a player
; original version by Erik Mooney in the Stella mailing list message
; "Re: [stella] sexp8.bin Multi-Japanese Sprites" from April 18, 1998
; (http://www.biglist.com/lists/stella/archives/199804/msg00170.html)
; modified to work on both player 0 and 1 and to take a hard-coded
; position value rather than look at a table (there is no motion in
; this game, so the table is not necessary)
; This must not cross a page boundary!
;
; a = position value - high nybble = fine position, low nybble =
; course position
; x = player number
PositionPlayer
sta WSYNC
ror T1 ; waste 5 cycles
sta HMP0,x
and #$0f
tay
P0
dey
bpl P0
sta RESP0,x
; Rather than WSYNC and HMOVE now, let the calling routine do it. If both
; players are positioned in succession, this saves a scanline.
rts
; Routines that get an image from a table
;
; a = image number out of the set
; x = offset to the addresses to receive the pointer
GetTrickImage
ldy #$04
bne GRI2
GetScoreImage
ldy #$03
bne GRI2
GetArrowImage
ldy #$02
bne GRI2
GetSuitImage
ldy #$01
bne GRI2
GetRankImage
ldy #$00
GRI2
clc
adc ImageBankOffsets,y
asl
sta T4
asl
clc
adc T4
adc #<RankImageStart
sta $00,x
lda #>RankImageStart
adc #$00
sta $01,x
rts
GetLetterImage
clc
adc #<LetterImageStart
sta $00,x
lda #>LetterImageStart
adc #$00
sta $01,x
rts
GetMessagePointer
tya
asl
asl
clc
adc #<MessageTableStart
sta $00,x
lda #>MessageTableStart
adc #$00
sta $01,x
rts
GetMessageImages
ldy #$04
MessageImageLoop
dey
sty T4
lda (MessagePtr),y
sta T3
tya
asl
clc
adc #ImgPtr1
tax
lda T3
jsr GetLetterImage
ldy T4
bne MessageImageLoop
rts
InitializeSelectSound
lda #SoundValidSelect
sta SoundTableOffset
lda #FramesValidSelect
sta SoundFrameCounter
rts
InitializeBuzzSound
lda #SoundInvalidSelect
sta SoundTableOffset
lda #FramesInvalidSelect
sta SoundFrameCounter
rts
FreqTable
.byte $17, $14, $13, $04
ToneTable
.byte $05, $1c, $05, $0f
; This and other free space calculations were taken from Brian Watson's
; Poker Squares. See "[stella] Poker solitaire source, versions 012 & 014"
; from November 26, 2001.
; (http://www.biglist.com/lists/stella/archives/200111/msg00421.html)
END_DATA = *
org $fff8
echo *-END_DATA, "bytes available in image and table area: ", END_DATA, "-", *-1
.byte $00, $00, $00, $00 ; Locations $fff8 and $fff9 are reserved for
; use in a Supercharger. Accessing them can
; crash a regular 4K game. Since the processor
; reads two bytes for one-byte instructions, a
; one-byte instruction should not be at $fff7.
.word CartStart
.word CartStart
| Current Thread |
|---|
|
| <- Previous | Index | Next -> |
|---|---|---|
| [stella] Space Treat release, Fabrizio Zavagli | Thread | [stella] Ideal settings for makewav, Erik Eid |
| [stella] Space Treat release, Fabrizio Zavagli | Date | Re: [stella] Star Fire - Return of , Manuel Polik |
| Month |