Subject: [stella] Euchre: Alpha|
From: Erik Eid <eeid@xxxxxxxxx>
Date: Sat, 31 Aug 2002 20:04:02 -0400
Greetings to the list! I am pleased to announce that I have posted the Alpha release candidate for Euchre both on its web page (http://www.wwnet.net/~eeid/station26/euchre.html) and attached to this message. I found a significant bug today: the game crashes if any computer player chooses to become a lone maker! This was very subtle, involving the difference between using a beq and a bne! (I was using a branch instead of a jump to save a byte, but incorrectly used beq to branch after loading a non-zero value. Execution continued into another section of the code and got caught in a loop.) I only managed to find this bug by adjusting upward the likelihood that a computer player will choose to go alone. I had felt for a while that the computer was being too timid with regards to making alone, so I reduced the strength of hand needed for it to make that choice. (This also comes from my own experience; I play a couple of times a week and over the last few months learned to take more risks.) I also added a green border above and below the "card table" region, per a suggestion by zu03776, in order to make it appear more like a table. As I mentioned, this version is an Alpha release candidate. I am not adding any more features to the game, but I will continue to fix bugs as I find them and they are reported by the list members. (Hint, hint. :) ) I haven't received any feedback with regards to whether or not the PAL version is stable and displays decent approximations of the NTSC color scheme. I hope that means that everything in the PAL version is ok. :) I have attached the source, binaries, 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.
Description: Euchre ROM (PAL)
"Cartridge.MD5" "40aa851e8d0f1c555176a5e209a5fabb" "Cartridge.Name" "Euchre (Alpha candidate)" "Cartridge.Manufacturer" "Erik Eid" "Cartridge.Rarity" "New Release" "Cartridge.Type" "4K" "Controller.Left" "Joystick" "Display.Format" "NTSC" "" "Cartridge.MD5" "c9d02d3cfeef8b48fb71cb4520a4aa84" "Cartridge.Name" "Euchre (Alpha candidate) (PAL) [!]" "Cartridge.Manufacturer" "Erik Eid" "Cartridge.Rarity" "New Release" "Cartridge.Type" "4K" "Controller.Left" "Joystick" "Display.Format" "PAL" ""
Description: Euchre ROM (NTSC)
; ; Euchre game program for the Atari 2600 video computer system ; ; Copyright 2001 by Erik Eid (eeid@xxxxxxxxx) ; ; Last update: August 31, 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 IFCONST PAL Team1Color = $cc Team2Color = $4a TableRegionColor = $32 CardTableColor = $0f RedSuitColor = $48 BlackSuitColor = $00 MessageTextColor = $2a CursorColor = $b4 TrumpDisplayColor = $28 ELSE Team1Color = $88 Team2Color = $38 TableRegionColor = $d4 CardTableColor = $0f RedSuitColor = $36 BlackSuitColor = $00 MessageTextColor = $1c CursorColor = $82 TrumpDisplayColor = $28 ENDIF 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 ScorePositions = $01 ; Offsets into PosTable, containing position pairs TrickPositions = $03 BidPositions = $05 CenterPositions = $07 LeftRightPositions = $09 MessagePositions = $0b InstrPositions = $0f NothingPositions = $0f 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 IFCONST PAL FramesWait = 75 FramesShortWait = 37 FramesLongWait = 150 FramesJoyWait = 12 FramesScoreIncrement = 16 FramesValidSelect = 2 FramesInvalidSelect = 8 ELSE 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 ENDIF 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 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 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 AuxFrameCounter = AttractFrameCounter 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 SouthDiscarded 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 IFCONST PAL lda #53 ; 45 lines * 76 cycles/line = 3420 cycles / 64 cycles/interval = ; 53.44 intervals sta TIM64T ELSE ; Provide 37 scanlines of vertical blank lda #43 ; 37 lines * 76 cycles/line = 2812 cycles / 64 cycles/interval = ; 43.96 intervals sta TIM64T ENDIF 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 ldx Choice bpl DCM4 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 IFCONST PAL lda #16 ; 228 lines * 76 cycles/line = 17328 cycles / 1024 cyc/interval = ; 16.75 intervals sta TIM1024T ELSE lda #228 ; 192 lines * 76 cycles/line = 14592 cycles / 64 cycles/interval = ; 228 intervals sta TIM64T ENDIF ; 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 lda #$00 sta T3 ; T3 will track how many random numbers have been ; thrown out during this frame. ldy #CardsInDeck-1 SDOnce jsr RandomByte and #%00011111 ; Reduce the random number to between 0 and 31 cmp #CardsInDeck ; Is it less than the number of cards in the deck? bmi SDSwap ; Yes, so switch the current card with the randomly ; chosen card. ldx T3 ; No, it is higher than how many are in the deck. cpx #$06 ; Have we hit the threshold for random numbers redone? bpl SDNext ; Yes, so do not swap cards this time. inc T3 ; No, so increase our count of thrown out numbers bpl SDOnce ; and select another. SDSwap tax lda DeckStart,y pha lda DeckStart,x sta DeckStart,y pla sta DeckStart,x SDNext dey bpl SDOnce sta WSYNC IFCONST PAL jmp WaitForEnd ELSE ShuffleEnd lda INTIM bne ShuffleEnd jmp KernelsEnd ENDIF NormalKernel ; 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 ldy #ScorePositions jsr PosBothPlayers sta WSYNC sta HMOVE lda #P_Quad sta NUSIZ0 sta NUSIZ1 lda Team1Score ldx #ImgPtr1 jsr GetScoreImage lda Team2Score ldx #ImgPtr2 jsr GetScoreImage ldx #$01 jsr DrawBookkeeping ; Now move on to the tricks sta WSYNC lda #$00 sta GRP0 sta GRP1 lda #P_Reflect sta REFP0 ldy #TrickPositions jsr PosBothPlayers 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/discard). sta WSYNC ; Stabilize so that all the branches can time similarly... lda Stage cmp #StageBetweenHands beq PrepareShowD cmp #StagePlaying bcs PrepareShowTrump cmp #StageBidding1 beq PrepareShowBid cmp #StageBidding2 beq PrepareShowBid cmp #StageDiscarding beq PrepareShowS PrepareShowNothing lda #<LetterImageSpace PSN2 ldx #ImgPtr1 jsr GetLetterImage lda #<LetterImageSpace ldy #InstrPositions bne ShowInformation PrepareShowTrump lda TrumpSuitMask ldx #ImgPtr1 jsr GetSuitImage lda #<LetterImageSpace ldx BiddingTeam ldy BidTeamToPTIdx,x bne ShowInformation PrepareShowD lda #<LetterImageD bne PSN2 PrepareShowS lda Turn bne PrepareShowNothing lda #<LetterImageS bne PSN2 PrepareShowBid sta WSYNC lda Turn ldx #ImgPtr1 jsr GetArrowImage lda Choice and #$03 ; Change NoChoice ($FF) to $03 tax lda ChoiceToLetterTable,x ldy #BidPositions ShowInformation ldx #ImgPtr2 jsr GetLetterImage tya jsr PosBothPlayers sta WSYNC sta HMOVE lda #TrumpDisplayColor eor AttractMask sta COLUP0 sta COLUP1 jsr DrawSingleCard lda #$00 sta GRP0 sta GRP1 ; Position the players for display of a card. This is well in advance but ; we have time now. ldy #CenterPositions jsr PosBothPlayers sta WSYNC sta HMOVE ; Now switch to the "card table" display sta WSYNC lda #TableRegionColor eor AttractMask sta COLUBK sta WSYNC ; Begin 4 lines of table background before showing the table sta WSYNC sta WSYNC sta WSYNC 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 ldy #LeftRightPositions jsr PosBothPlayers ; sta WSYNC ; sta HMOVE jsr DrawTwoCards lda #$00 sta GRP0 sta GRP1 sta NUSIZ0 sta NUSIZ1 ldy #CenterPositions jsr PosBothPlayers 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 WSYNC ; 4 lines of table background before shutting it off sta WSYNC sta WSYNC sta WSYNC 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 inc HandCard lda HandCard cmp #CardsInHand bne ShowHandLoop ; Now the gap between the last card and the message region lda #13 ; 11 lines * 76 cycles/line = 836 cycles / 64 cycles/interval = ; 13.0625 intervals sta TIM64T lda #$00 sta PF2 sta COLUBK eor AttractMask sta COLUP0 sta COLUP1 ; Prepare for the message section ldy #MessagePositions jsr PosBothPlayers sta WSYNC sta HMOVE ldx #MessagePtr lda CursorMode cmp #CursorModeAction beq DetermineMessageAction cmp #CursorModeTrump beq DetermineMessageTrump ldy #MsgNumBlank jmp DM0 DetermineMessageAction ldy #MsgNumSelectAction bne DM0 DetermineMessageTrump ldy #MsgNumSelectTrump DM0 jsr GetMessagePointer jsr GetMessageImages WaitForGap lda INTIM bne WaitForGap sta WSYNC lda #$00 ; No need to use attract mask - messages do not appear when game is over. sta COLUBK lda #19 ; 16 lines * 76 cycles/line = 1216 cycles / 64 cycles/interval = ; 19 intervals 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 stx GRP0 ; x still holds $00 after PositionPlayer WaitForMessage lda INTIM bne WaitForMessage IFCONST PAL ldy #43 ; 36 lines * 76 cycles/line = 2736 cycles / 64 cycles/interval = ; 42.75 intervals. The PAL screen has more lines than NTSC; ; this will burn through all the extras. sty TIM64T ENDIF ; The accumulator is already zero when the WaitForMessage loop ends. 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 IFCONST PAL WaitForEnd lda INTIM bne WaitForEnd ENDIF KernelsEnd sta WSYNC sta WSYNC IFCONST PAL lda #43 ; 36 lines of overscan sta TIM64T ELSE lda #35 ; 30 lines of overscan sta TIM64T ENDIF lda #$02 sta VBLANK CheckReset lda SWCHB and #Con_Start bne CheckStages jsr StartNewGame jmp ProgStart CheckStages ldx Stage lda StageJumpTableLow,x sta T2 lda StageJumpTableHigh,x sta T3 jsr StageJump 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 ldx #NoChoice stx Choice stx SouthDiscarded ; NoChoice is $ff; SouthDiscarded is set to zero ; only if South discards. inx ; NoChoice is $ff, so x becomes $00, which is ; the desired starting cursor position. stx 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 beq PB1Pass ; beq is fine since ChoicePass = 0 and Choice ; cannot be -1 if we are advancing. 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 Choice bmi PB1SouthActs dec AuxFrameCounter beq PB1Advance rts PB1SouthActs lda TriggerTrack cmp #TriggerOn bne PB1SouthMove jsr InitializeSelectSound lda CursorPos sta Choice beq PB1SetAuxDelay ; If south dealt and did not pass, immediately lda Dealer ; advance. Otherwise, set up a brief delay beq PB1Advance ; before the next bid. PB1SetAuxDelay jsr SetAuxCounter rts PB1SouthMove ldx #$03 jsr MoveCursor rts ; x = highest possible choice number plus one (lowest is zero) MoveCursor stx T1 MC00 lda JoyDir clc adc CursorPos bmi MCUnder cmp T1 bne MCSave lda #$00 ; "Wrap" from highest to lowest by loading lowest beq MCSave MCUnder lda T1 sec sbc #$01 ; "Wrap" from lowest to highest by setting to ; number of choices minus one MCSave sta CursorPos cpx #$05 ; If x = 5, we are selecting a card bne MCEnd tay lda SouthHand,y ; Check if this card has been played bmi MC00 ; If bit 7 is 1, card was played - move again in ; the same direction MCEnd rts SetCursorPosToFirstCard ldx #$ff SCP2 inx lda SouthHand,x bmi SCP2 SCP3 stx CursorPos rts StartBidding2 lda #StageBidding2 sta Stage lda #SuitsInDeck ; Not having a -1 is intentional. When ; PotentialTrump is equal to the number of suits, ; it indicates a need for initialization. sta PotentialTrump 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 bne PB2NotSouth jmp PB2South ; Must use jmp since PB2South is too far away for bne 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 lda PotentialTrump cmp #SuitsInDeck bne PB2TA2 ; SuitsInDeck = Computer has not scanned any, so it ; must initialize. ldy #NoSuit sty BestSuit iny ; This works because NoSuit = $ff, so we end up ; putting $00 in HighStrength. sty HighStrength PB2TA2 jsr GetHandOffsets ; x still has the turn number, so this is fine dec PotentialTrump lda PotentialTrump bmi PB2Decision ; -1 = Computer has scanned its hand four times, ; (technically three, skipping the declined suit), ; so now it must choose an action. 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 (in next frame) sta HighStrength ; Yes, store this number and set the best suit lda PotentialTrump sta BestSuit PB2NextSuit rts PB2Decision lda HighStrength sta HandStrength jsr DecideBid ; Submit the highest possible tricks to be taken ; with this hand to get a choice lda Choice ; Was the final decision a pass? bne PB2SetDelay ; No, delay until the next player's turn. (bne ; is fine because ChoicePass = 0 and Choice will ; not be -1 once we get here.) lda Turn cmp Dealer ; Was it the dealer who passed? bne PB2SetDelay ; No, delay until the next player's turn. lda SWCHB ; The dealer made a second pass. and #%01000000 ; Is the left difficulty switch on A or B? beq PB2SetDelay ; 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 PB2SetDelay lda #FramesWait sta FrameCounter rts PB2Advance lda Choice beq PB2Pass ; (beq is fine because ChoicePass = 0 and Choice ; cannot be -1 here.) 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 jsr StartBetweenHands ; Everybody passed! Throw in the hand. ; (If we got to this point, then stick-the-dealer ; is not on.) 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 bne PB2SouthReadySuit ; No, in next frame start suit selection. (bne ; is fine since ChoicePass = 0 and Choice cannot ; be -1 due to the bpl above.) 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 PB2SouthSuit lda BestSuit bmi PB2SouthSuitActs dec AuxFrameCounter beq PB2Advance rts PB2SouthSuitActs 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 lda Choice bne PB2Advance ; If south dealt and did not pass, immediately lda Dealer ; advance. Otherwise, set up a brief delay beq PB2Advance ; before the next bid. PB2SetAuxDelay jsr SetAuxCounter rts PB2ChoseDeclined jsr InitializeBuzzSound rts PB2SouthReadySuit ldx #NoSuit stx BestSuit inx ; This works because NoSuit = $ff, so we are ; initializing the cursor position to $00. stx CursorPos rts PB2SouthMoveAction ldx #$03 jsr MoveCursor rts PB2SouthMoveSuit ldx #$04 jsr MoveCursor rts SetAuxCounter lda #FramesWait sta AuxFrameCounter lda #NoCursorPos sta CursorPos 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 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 ora #$20 ; Card is not trump; increase its score (the ora ; can be used instead of adc only because this is ; the first possible addition and it is higher ; than the score can be at this point). 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! stx SouthDiscarded ; Just use x - any non-zero value will do. jsr StartPlaying PDOut 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 and the value to clear the attract-mode color adjustment ; are both zero. lda #$00 sta Stage 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 inc Dealer lda Dealer and #PlayersMask sta Dealer jsr StartShuffle rts StartGameOver lda #StageGameOver sta Stage ldx #$00 stx AttractFrameCounter dex ; result: $ff stx 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 beq SPAD10 ; If south leads, there's no consideration of delaying ; before the first card is led. (Since Turn will ; be zero, AuxFrameCounter will receive zero.) lda #$00 SPAuxDelay ldx Bidder ; If South bid, force a wait before the first player ; leads. beq SPAD05 ldx SouthDiscarded ; If South did not bid but had to discard, also ; force a wait. bne SPAD10 SPAD05 lda #FramesWait SPAD10 sta AuxFrameCounter 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 bne CP1 jsr SetCursorPosToFirstCard CP1 lda #$00 sta CardInTrickNum sta GatheredInfo lda #FramesWait sta FrameCounter rts PerformPlaying ldx Turn beq PPSouth PPNotSouth lda AuxFrameCounter beq PPCheckIfPlayed dec AuxFrameCounter rts PPCheckIfPlayed 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 AuxFrameCounter lda Turn bne PPAdvanceTurn ldy #FramesWait sty AuxFrameCounter PPAdvanceTurn clc adc #$01 and #PlayersMask cmp PlayerToSkip beq PPAdvanceTurn sta Turn lda Turn bne PPSetDelay jsr SetCursorPosToFirstCard 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 #$15 bpl DBAlone cmp #$14 beq DBUorA cmp #$0e bpl DBUp cmp #$0d beq DBPorU lda #ChoicePass beq DBEnd DBAlone lda #ChoiceAlone bne 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? beq GINLSetNewIdeal ; It's the real thing! bmi GINLLessThanTrueIdeal ; It's lower... cmp IdealTrumpScore ; It's > true ideal, but is it less than closest so far? bpl GINLOut ; No, so we've got the closest so far GINLSetNewIdeal sta IdealTrumpScore stx IdealTrumpNum jmp GINLOut ; Needs to be jmp since we could have gotten here by ; either a bmi or a fall-through. (Maybe bmi is ok?) 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+#LeadSuitScoreValue ; With ace or king of led suit? bmi PCTPP2a ; Lower, so try to beat it for "insurance" cmp #NineRankValue+#TrumpScoreValue bmi PCTPPlayLow ; Lower than 9 of trump, therefore it must be ace or ; king of led suit, so let it slide cmp #AceRankValue+#TrumpScoreValue bpl PCTPPlayLow ; Partner winning with an ace of trump or a bower, ; let it go since partner likely has it (particularly ; since partner led with it!) bmi PCTPP2a ; Partner winning with a low trump, beat it if possible ; and #RankMask ; 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 PosBothPlayers ldx #1 PBP2 lda PosTable,y jsr PositionPlayer dey dex bpl PBP2 rts PosTable .byte #LeftScorePos, #RightScorePos .byte #LeftTrickPos, #RightTrickPos .byte #BidArrowPos, #BidDecisionPos .byte #CenterRankPos, #CenterSuitPos .byte #LeftRankPos, #RightRankPos .byte #MessageP0Pos, #MessageP1Pos .byte $a2, $48 .byte #InstructionPos ; Since only the first sprite is used for instructions, it's not necessary ; to give a position for the second one. BidTeamToPTIdx .byte $0d, $0e StageJump jmp (T2) ; An rts is implied here - it is supplied by the routines in the ; jump table below and returns control to after the jsr StageJump ; statement. StageJumpTableLow .byte #<PerformNewGame, #<PerformNewHand, #<PerformGameOver .byte #<PerformShuffle, #<PerformDeal, #<PerformBidding1 .byte #<PerformDiscarding, #<PerformBidding2, #<PerformPlaying .byte #<PerformAddToTricks, #<PerformAddToScore, #<PerformBetweenHands StageJumpTableHigh .byte #>PerformNewGame, #>PerformNewHand, #>PerformGameOver .byte #>PerformShuffle, #>PerformDeal, #>PerformBidding1 .byte #>PerformDiscarding, #>PerformBidding2, #>PerformPlaying .byte #>PerformAddToTricks, #>PerformAddToScore, #>PerformBetweenHands EndCase 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 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, $01, $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. Do not change this in the middle of the routine! ; When two players are positions in succession, the calling section often ; uses "inx" to switch from player 0 to player 1. ; "sec" and "sbc #$01" replace "tay" and "dey", as suggested by Thomas ; Jentzsch to prevent the y register from being replaced. PositionPlayer sta WSYNC ror T1 ; waste 5 cycles sta HMP0,x and #$0f sec P0 sbc #$01 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 ; 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. BidTeamToTrumpPos .byte $a2,$48 .word CartStart .word CartStart