[stella] Euchre: long winding road

Subject: [stella] Euchre: long winding road
From: "Erik J. Eid" <eeid@xxxxxxxxx>
Date: Sun, 14 Apr 2002 17:23:45 -0400
It has been almost five months since I posted a Euchre update to the list. Never fear, Euchre is not fated to be vaporware!

Naturally I experienced the typical slowdown during the holidays. I've also been taking a beginning Linux/UNIX class, which has taken up some of the free time I would be otherwise devoting to Euchre.

I actually was steadily working on the game for a while. I began in earnest to devise the computer's intelligence routine for actual gameplay. Unfortunately, when I was done, I found myself several hundred (!) bytes over the 4K limit, so it was clear that not only was Euchre due for more optimization, but some sacrifices were necessary.

As much as I hoped not to, I cut out nearly all of the text messages. No longer will you see the words "pass", "call", or "alone", nor the direction names ("north" etc.), nor the messages indicating what trump suit the computer player selected. I found numerous places in the program with a lot of code devoted just to determining which message to show. The table of messages itself took up quite a bit of room. (What few messages I have left - game over, the Euchre name, and my byline, could come out in a later version.)

I also dropped the paddle support in favor of a joystick. I found that with the type of display I have - not very repetitive - and only reading the paddle in a few locations, that the screen was never stable enough for my liking.

I discovered (thanks to the scanline count capability of Z26) that my deck shuffling routine was taking many hundred scanlines to finish. This is because with all the random numbers being generated, one shuffle needed about 134 lines! To clean that up, I changed it so only one shuffle is performed per frame, and the screen is blanked during those frames. (It's only eight frames, which is far fewer than the "thinking" pauses in 3-D Tic Tac Toe and Video Chess.)

Since many of the loops are based on suit rather than rank, I rearranged the bits in each card so that the suit is now in the least significant two bits. This made it easy to use dec/bpl loop structures throughout the program when processing a hand.

I consolidated the routines for getting a pointer to an image into one routine with multiple entry points. I also switched from drawing the scores and tricks as playfield over to sprites. The code to draw them as playfield, especially once I tried doing each digit separately (much like the early games), was too expensive.

Now that there are no significant messages, I had to change the user interface for selecting an action (pass/call up/go alone) and a trump suit. Inspired by Raiders of the Lost Ark and the Swordquest series, I show letters for each action (P=pass, U=call Up, A=go Alone) or the possible suits, and a cursor underneath one of the symbols. The player moves the joystick to select one of these and presses the button to make the choice.

To indicate what the computer elects to do, above the "card table" are an arrow and a letter. The arrow indicates which player is bidding - if it points left, West is bidding, etc. An action letter next to the arrow indicates what the player chose to do. Once play begins, a suit appears underneath the score and tricks of the bidding team, indicating the trump suit and which team called trump. At other times, a single letter appears to give instructions to the human player - if it is a D, the player should press the joystick button to start a new deal, and if it is an S, the player should swap one of his or her cards for the turned-up card.

There have also been many here-and-there optimizations and changes. (Some of the newer code may not be all that optimal, though.) I also reworked the bidding routines so that the computer (hopefully) is not too cautious nor too ambitious. I'll be able to tell better once I finally enable the computer to play by the rules; it may be that what appears to be a crazy decision ("you called it up on what?!?") is actually very successful.

So, in you define progress in terms of what more this version has than my previous one did, I haven't really made progress. However, what I've learned over the last five months, in going well beyond memory limits, finding features to scale back or eliminate, optimizing, and revisiting some logic, is much progress to me. Since I am a novice assembly language programmer coming from a structured programming world, all of this is a learning experience, and even long tangents like the one I took are very valuable.

Anyhow, enough rambling from me... the attached files show where things stand now. I have almost 500 bytes (and I know this thanks to some techniques I lifted from Brian Watson's Poker Solitaire/Squares <g>).

P.S. For reference, here is a link to the VCS.H I am using: http://www.io.com/~nickb/atari/doc/vcs.h.
;
; Euchre game program for the Atari 2600 video computer system
;
; Copyright 2001 by Erik Eid (eeid@xxxxxxxxx)
;
; Last update: April 14, 2002
;
; Compiled with the dasm assembler 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

Team1Color = $88
Team2Color = $38
TableRegionColor = $d4
CardTableColor = $0f
RedSuitColor = $36
BlackSuitColor = $00
MessageRegionDisplayColor = $22
MessageRegionChoiceColor = $84
MessageTextColor = $1c
CursorColor = $82
TrumpDisplayColor = %00101000

RankMask = %01110000      ; Bit mask for rank of a card
DispSuitMask = %00001100  ; 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 = %00001111  ; 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 = %01110000
LeftRankValue  = %01100000
AceRankValue   = %01010000
KingRankValue  = %01000000
QueenRankValue = %00110000
JackRankValue  = %00100000
TenRankValue   = %00010000
NineRankValue  = %00000000

HeartSuitValue   = %00000000
DiamondSuitValue = %00000101
ClubSuitValue    = %00001010
SpadeSuitValue   = %00001111

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

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 = $44
MessageP1Pos = $c4

NoCursorPos = $FF

MsgNumBlank = $00
MsgNumDeal = $01
MsgNumGameOver = $02
MsgNumEuchre = $03
MsgNumCopyright = $04
MsgNumByline = $05
MsgNumSelectAction = $06
MsgNumSelectTrump = $07

StageHold = $00
StageNewGame = $01
StageNewHand = $02
StageGameOver = $03
StageShuffle = $04
StageDeal = $05
StageBidding1 = $06
StageDiscarding = $07
StageBidding2 = $08
StagePlaying = $09
StageAddToTricks = $0a
StageAddToScore = $0b
StageBetweenHands = $0c

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

ChoicePass = $00
ChoiceCall = $01
ChoiceAlone = $02

TriggerOff = $00
TriggerOn = $01
TriggerHeld = $02

CursorModeNone = $00
CursorModeCard = $01
CursorModeAction = $02
CursorModeTrump = $03
CursorModeSelectMask = %00000010

; 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
ImgPtr5     ds 2
ImgPtr6     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
Overlay         ds 19

DeckStart = SouthHand
Upcards = SouthCard

    seg.u vars
    org Overlay

PotentialTrump  ds 1
Upcard          ds 1
DeclinedSuit    ds 1
HighPsblTrks    ds 1
BestSuit        ds 1
PossibleTricks  ds 1
CardScore       ds 1
NumInSuit       ds 1
NumTrumps       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

; 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 #NoCursorPos
    sta CursorPos

    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_DumpPots + #VB_Enable
    sta VBLANK
    lda #VS_Enable
    sta WSYNC
    sta WSYNC
    sta WSYNC
    sta VSYNC
    sta WSYNC
    sta WSYNC
;    sta WSYNC
    lda #VS_Disable
    sta VSYNC
    lda #VB_Enable
    sta VBLANK  ; Start recharge of paddle

; 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 TriggerTrack
;    cmp #TriggerOff
;    beq TriggerEnd
    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
    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
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

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.
    jsr ShuffleDeck
    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
    sta COLUP0
    lda #Team2Color
    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
    ldx Choice
    inx                 ; Change NoChoice (255) to 0
    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
    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
    sta COLUBK
    lda #CardTableColor
    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 COLUBK
    sta PF1
    sta PF2

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 COLUBK
    sta PF2
    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
    sta WSYNC
    lda #$00
    ldx CursorMode
    cpx #CursorModeCard
    bne SC
    ldx HandCard
    cpx CursorPos
    bne SC
ShowCursor
    lda #CursorColor
SC
    sta COLUBK
    jsr DrawSingleCard
    lda #$00
    sta GRP0
    sta GRP1
    sta WSYNC
    sta COLUBK
    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 COLUBK
    sta PF2
    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
    sta COLUBK

    lda #19  ; 16 lines of message
    sta TIM64T

    lda #P_ThreeClose
    sta NUSIZ0
    sta NUSIZ1
    lda #MessageTextColor
    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
    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
    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 #$01
    bne CheckStages
    jsr StartNewGame
    jmp ProgStart

CheckStages
    lda Stage
    asl
    asl
    sta T1
    lda Stage
    asl
    clc
    adc T1
    clc
    adc #<StageJumpTable
    sta T2
    lda #>StageJumpTable
    adc #$00
    sta T3
    jmp (T2)

StageJumpTable
    jsr PerformHold
    jmp EndCase
    jsr PerformNewGame
    jmp EndCase
    jsr PerformNewHand
    jmp EndCase
    jsr PerformGameOver
    jmp EndCase
    jsr PerformShuffle
    jmp EndCase
    jsr PerformDeal
    jmp EndCase
    jsr PerformBidding1
    jmp EndCase
    jsr PerformDiscarding
    jmp EndCase
    jsr PerformBidding2
    jmp EndCase
    jsr PerformPlaying
    jmp EndCase
    jsr PerformAddToTricks
    jmp EndCase
    jsr PerformAddToScore
    jmp EndCase
    jsr PerformBetweenHands

EndCase

WaitOverscan
    lda INTIM
    bne WaitOverscan
    sta WSYNC

    jmp Main

StartHolding        ; Stop the action - only used during development
    lda #StageHold
    sta Stage
PerformHold
    rts

StartBidding1
    lda #StageBidding1
    sta Stage
    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
;    bne PB1NotSouth        ; Handle case of human player separately
;    jmp PB1South           ; Can't beq PB1South - too far away
    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
    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
    beq MCOver
MCSave
    sta CursorPos
    rts
MCUnder
    dex
    txa                      ; "Wrap" from lowest to highest by loading highest
    bne MCSave
MCOver
    lda #$00                 ; "Wrap" from highest to lowest by loading lowest
    beq MCSave

StartBidding2
    lda #StageBidding2
    sta Stage
    ldx Dealer
    lda Upcards,x
    ora #HideCardValue
    sta Upcards,x
    and #RealSuitMask
    sta DeclinedSuit
    jsr StartBidding
    rts

PerformBidding2
    ldx Turn
;    bne PB2NotSouth        ; Handle case of human player separately
;    jmp PB2South           ; Can't beq PB2South - too far away
    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 HighPsblTrks
    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 PossibleTricks
    jmp PB2CheckIfHigh
PB2Declined
    lda #$00
PB2CheckIfHigh
    cmp HighPsblTrks       ; Is the number of possible tricks with the suit
                           ; being checked as trump better than the best
                           ; suit so far?
    bcc PB2NextSuit        ; No, carry on to the next suit
    sta HighPsblTrks       ; Yes, store this number and set the best suit
    lda T4
    sta BestSuit
PB2NextSuit
    dec T4
    bpl PB2FindBestSuit

    lda HighPsblTrks
    sta PossibleTricks
    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
    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.
    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
    lda CursorPos
    sta Choice
    cmp #ChoicePass          ; Did the human player pass?
    beq PB2Advance           ; Yes, go to the next player
    lda #$00                 ; No, in next frame start suit selection
    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
    jmp PB2Advance
PB2ChoseDeclined
    ; buzz?
    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
    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
    lsr                    ; Move rank to lowest three bits, making a number
    lsr                    ; between zero and seven
    eor #RankMask          ; Effectively inverts rank, turning high to low
    sta CardScore          ; Starts the card's score as seven minus its rank
    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 #$08               ; Card is not trump; add 8 to 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 #$08               ; Add 8 to 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
    jmp PDSwap
PDSouth
    lda TriggerTrack
    cmp #TriggerOn
    bne PDSouthSelecting
    lda CursorPos
    clc
    adc HandStartOffset
    jmp PDSwap             ; a could be 0; must jump instead of branch
PDSouthSelecting
    jsr GetSelection
    rts
PDSwap                     ; Assumes index of card to swap is in accumulator
    tax
    lda DeckStart,x
    sta T1
    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)
    sta T2                 ; Save the upcard
    tya                    ; Retrieve index of card to swap
    tax
    lda T2                 ; Retrieve upcard
    sta DeckStart,x        ; Place upcard in hand
    ldx Dealer
    lda T1                 ; Retrieve card to be swapped
    ora #HideCardValue
    sta Upcards,x          ; Discard it!
    jsr StartPlaying
    rts

StartShuffle
    lda #StageShuffle
    sta Stage
    lda #$08
    sta Turn
    jsr RefreshDeck
    rts

PerformShuffle
    ; Note that the call to ShuffleDeck 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
;    lda #MsgNumDeal
;    sta MessageNum
    rts

PerformDeal
    ldx #$04
DealLoop
    lda SouthHand,x
    and #ShowCardMask
    sta SouthHand,x
    dex
    bpl DealLoop
    ldx Dealer
    lda Upcards,x
    and #ShowCardMask
    sta Upcards,x
    sta Upcard          ; Save upcard for use in AnalyzeHand
    jsr StartBidding1
    rts

StartNewGame
    lda #StageNewGame
    sta Stage
    lda #MsgNumBlank
    sta MessageNum
    rts

PerformNewGame
    lda #$00
    sta Team1Score
    sta Team2Score
    lda rand1
    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 #MsgNumGameOver
    sta MessageNum
    lda #FramesLongWait
    sta FrameCounter
    rts

PerformGameOver
    lda TriggerTrack
    cmp #TriggerOn
    bne PGOAttract
    jsr StartNewGame
    jmp ProgStart
PGOAttract
    lda FrameCounter
    beq PGOChange
    dec FrameCounter
    rts
PGOChange
    lda #FramesLongWait
    sta FrameCounter
    inc MessageNum
    lda MessageNum
    cmp #MsgNumByline+1
    bcc PGOEnd
    lda #MsgNumEuchre
    sta MessageNum
PGOEnd
    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
    jmp SPDoSkip
SPCalcSkip
    lda #$03
    sta CardsInFullTrick
    lda Bidder
    clc
    adc #$02
    and #PlayersMask
SPDoSkip
    sta PlayerToSkip
    lda Dealer
SPAdvanceLeader
    clc
    adc #$01
    and #PlayersMask
    cmp PlayerToSkip
    beq SPAdvanceLeader
    sta Leader
    sta Turn
ContinuePlaying
    lda #StagePlaying
    sta Stage
    lda Turn
    beq CP1
    sta CursorPos            ; Value for south and cursor are both $00
CP1
    lda #$00
    sta CardInTrickNum
    lda #FramesWait
    sta FrameCounter
    rts

PerformPlaying
    ldx Turn
    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 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             ; This is a stub that simply picks the nth card from
                         ; a computer player's hand, where n is the number of
                         ; the current trick (0-4)
    jsr GetHandOffsets
    lda TrickNum
    clc
    adc HandStartOffset
    sta CardToPlayOffset
    jmp PPPlayCard
PPSouth
    lda TriggerTrack
    cmp #TriggerOn
    bne PPSouthSelecting
    ldx Turn
    jsr GetHandOffsets
    lda CursorPos
    clc
    adc HandStartOffset
    sta CardToPlayOffset
    jsr IsCardValidPlay
    beq PPInvalidSelection
    lda #NoCursorPos
    sta CursorPos
    jmp PPPlayCard
PPInvalidSelection
    ; buzz?
    rts
PPSouthSelecting
    jsr GetSelection
    rts
PPPlayCard
    ldx CardToPlayOffset
    lda DeckStart,x
    and #ShowCardMask
    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
    jmp PPShowPlay
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?
    beq PPFollowedSuit   ; Yes, go handle it
    jmp PPShowPlay       ; If card isn't trump or in led suit, it can't win,
                         ; so just go show the card
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
    jmp PPShowPlay
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
    ; sound?
    lda #FramesShortWait
    sta FrameCounter
    rts

PerformAddToTricks
    dec FrameCounter
    beq PAT1
    rts
PAT1
    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
    inc Team1Score,x
    jmp SAS1
SASEuchre
    txa
    eor #$01
    tax
    lda Team1Score,x
    clc
    adc #$02
    sta Team1Score,x
    jmp SAS1
SASMarch
    lda PlayerToSkip
    cmp #NoPlayer
    beq SASMarch2
    lda #$04
    sta T1
    jmp SASFive
SASMarch2
    lda #$02
    sta T1
SASFive
    lda Team1Score,x
    clc
    adc T1
    sta Team1Score,x
SAS1
    lda #FramesWait*2
    sta FrameCounter
    rts

PerformAddToScore
    dec FrameCounter
    beq PAS1
    rts
PAS1
    lda Team1Score
    cmp #$0a
    bpl PASOver
    lda Team2Score
    cmp #$0a
    bpl PASOver
    jsr StartBetweenHands
    rts
PASOver
    ; sound?
    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
    and #CardPlayedMask      ; Has the card already been played?
    cmp #CardPlayedValue
    bne 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 NumVoids
    sta LowTrumpFactor
    sta OtherFactor
    sta HasRight
    sta HasLeft
    sta HasTrumpAce

    lda PotentialTrump
    ora #JackRankValue
;    and #RankSuitMask
    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
    and #RankMask+#RealSuitMask
    cmp RightBowerMask
    beq AHIsRight
    and #RankMask
    cmp #AceRankValue
    beq AHIsAce
    cmp #QueenRankValue
    bmi AHLowTrump
    inc LowTrumpFactor  ; The omission of a branch is intentional.  If the
                        ; trump is a king or queen, LowTrumpFactor will be
                        ; incremented twice.
AHLowTrump
    inc LowTrumpFactor
    bne AHCL1
AHIsRight
    inc HasRight
    bne AHCL1
AHIsAce
    inc HasTrumpAce
    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
    inc NumTrumps
    inc HasLeft
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 #$03
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
    bne AHNotVoid       ; If a <> 0, we had a card in the suit
    inc NumVoids        ; If a = 0, we were void in the suit
    bne AHCL2
AHNotVoid
    lda HasAce
    beq AHCL2           ; If a = 0, we do not have any aces in the suit
    lda NumInSuit
    cmp #$02            ; Do we have the ace plus just one other card?
    beq AHAcePlus       ; Yes, so the ace might still take one.
    cmp #$01            ; Did we have just one card in the suit?
    bne AHCL2           ; No, so we didn't get an off-suit lone ace
    lda PotentialTrump  ; We have an off-suit lone ace...
    eor #FlipColorSuitMask
    cmp T1              ; ...but is it the same color as trump?
    bne AHOffColorAce   ; No, so there's a good chance it will take a trick.
AHAcePlus
    inc OtherFactor     ; Yes, but it may be possible given how the hand plays out.
    bne AHCL2
AHOffColorAce
    inc NumOffLoneAces
AHCL2
    dey
    bpl AHCountLoop2

; Now calculate the number of tricks this hand will probably take.

GetPossibleTricks
    lda #$00
    sta PossibleTricks
    lda Stage
    cmp #StageBidding1
    beq PTExamineUpcard
    lda #$00            ; If this is the second round of bidding, there is no
    sta UpcardFactor    ; upcard to consider.
    beq PTRight
PTExamineUpcard
    lda Dealer
    eor Turn            ; Bit 0 will be 1 if the upcard goes to an opponent
    and #$01            ; Does upcard belong to opponent?
    bne PTEUOpp
    lda Dealer          ; No, the upcard goes to my team...
    cmp Turn            ; ... but does it go to my partner?
    bne PTRight         ; Yes, so the upcard's effect is uncertain.
    lda NumTrumps       ; The upcard goes to the bidder.  Unless the hand is
                        ; full of trumps, or there are four and an off-trump-
                        ; color lone ace, the upcard potentially adds a
                        ; likely trick.
    cmp #$05
    bpl PTRight
    cmp #$04
    bmi PTEU1
    lda NumOffLoneAces
    bne PTRight
PTEU1
    lda #$01
    sta UpcardFactor
    bne PTRight
PTEUOpp
    lda NumTrumps       ; The upcard going to the opponent will be a likely
                        ; trick for their team unless the bidder already has
                        ; four or five trumps and the upcard isn't rather low.
    cmp #$04
    bpl PTRight
    lda Upcard
    cmp #JackRankValue
    bmi PTRight
PTEUOpp2
    lda #$ff            ; Save a -1 to "add" to the count of likely tricks.
    sta UpcardFactor
PTRight
    lda HasRight        ; Do we have the right bower?
    beq PTLeft          ; No, move on to next check
    inc PossibleTricks  ; Yes, this is a guaranteed trick
PTLeft
    lda HasLeft         ; Do we have the left bower?
    beq PTAce           ; No, move on to next check
    lda NumTrumps
    clc
    adc UpcardFactor
    cmp #$02            ; Is left bower the only trump?  (If the upcard goes to
                        ; the opponent, it may cancel one of ours.)
    bmi PTAce           ; Yes, so we can't count it.
    inc PossibleTricks  ; No, so the left bower should take a trick.
PTAce
    lda HasTrumpAce     ; Do we have the ace of trump?
    beq PTLowTrumps     ; No, move on to next check
    lda NumTrumps
    clc
    adc UpcardFactor
    cmp #$03            ; Is the ace one of only two trumps?  (If the upcard
                        ; goes to the opponent, it may cancel one of ours.)
    bmi PTLowTrumps
    inc PossibleTricks  ; No, so the ace should take a trick.
PTLowTrumps
    lda LowTrumpFactor  ; We counted king and queen of trump as "two" and 10
    lsr                 ; and 9 of trump as "one".  Cut the total in half and
    clc                 ; add this to our likely tricks.
    adc PossibleTricks
    sta PossibleTricks
PTOther
    lda OtherFactor     ; Much like with low trumps, count half of our "other"
    lsr                 ; good cards as potential tricks.
    clc
    adc PossibleTricks
    sta PossibleTricks
PTUpcard
    lda PossibleTricks
    clc
    adc UpcardFactor    ; Finally count the upcard as one of ours... or theirs
    sta PossibleTricks
PTOffColorAces
    lda PossibleTricks  ; Each ace that is alone in a suit that is not trump or
                        ; the same color as trump will probably take a trick.
    clc
    adc NumOffLoneAces
    sta PossibleTricks
;PTTwoSuited
;    lda NumVoids        ; Do we have only two suits in this hand?  (Since there
;    cmp #$02            ; are four suits, two voids implies two remaining suits.)
;    bne PTEnd           ; No, so move on...
;    lda NumTrumps       ; Yes!
;    cmp #$02            ; Do we have two or more trump?
;    bmi PTEnd           ; No, so there'll be no help.
;    lda NumOffLoneAces  ; Do we only have an ace in the non-trump suit?
;    bne PTEnd           ; Yes, so the ace will likely take one anyway.
;    inc PossibleTricks  ; Being void in two suits, with two or three trumps,
;                        ; gives us a good opportunity to use a trump that might
;                        ; otherwise be taken.
PTEnd
    rts

DecideBid
    lda PossibleTricks
    bmi DB2a            ; If we got a negative count (zero with upcard going
                        ; to opponent), make sure to pass!  Don't allow a low
                        ; negative number to be treated as greather than five!
    cmp #$05            ; Will this hand take five tricks?
    bmi DB1             ; No, so keep considering.
    lda #ChoiceAlone    ; Yes!  Go alone!
    bne DBEnd           ; Knowing a <> 0, a bne is a savings over a jmp
DB1
    cmp #$03            ; Will this hand take exactly three tricks?
    bne DB2             ; No, keep checking.
    lda #ChoiceCall     ; With three tricks, call, but don't go alone.
    bne DBEnd
DB2
    cmp #$02            ; Will this hand take zero or one tricks?
    bpl DB3             ; No, keep checking.
DB2a
    lda #ChoicePass     ; This is a weak hand, so pass.
    beq DBEnd
DB3
    bne DB4             ; It's not two tricks, so it's four...
    lda Turn            ; This hand will take two tricks.
    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
DB4
    lda Dealer          ; If we got here, then our hand is likely to take four
    clc                 ; tricks.  Go alone if we lead (because then we can
    adc #$01            ; pull out trump easily), otherwise leave our partner
    and #PlayersMask    ; in so they can provide the fifth tricks for a two-
    cmp Turn            ; point march.
    beq DB4a
    lda #ChoiceCall
    bne DBEnd
DB4a
    lda #ChoiceAlone
DBEnd
    sta Choice
    rts                 ; Whew!

; 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     ; No need to clear carry; previous addition cannot
                           ; be > 255
    sta HandEndOffset
    rts
HandToStartOffset
    .byte #$00, #$05, #$0a, #$0f

; translate a paddle position into a selection of one of five cards
;
; returns: CursorPos = which card is selected (0-4)

GetSelection
    ldx #$05
    jsr MoveCursor
    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

; 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

ImageBankOffsets
    .byte $00
    .byte (#SuitImageStart-#RankImageStart)/6
    .byte (#ArrowImageStart-#RankImageStart)/6
    .byte (#ScoreImageStart-#RankImageStart)/6
    .byte (#TrickImageStart-#RankImageStart)/6

GetLetterImage
    clc
    adc #<LetterImageStart
    sta $00,x
    lda #>LetterImageStart
    adc #$00
    sta $01,x
    rts

GetMessagePointer
    tya
    asl
    sta T1
    asl
    clc
    adc T1
    clc
    adc #<MessageTableStart
    sta $00,x
    lda #>MessageTableStart
    adc #$00
    sta $01,x
    rts

GetMessageImages
    ldy #$06
MessageImageLoop
    dey
    sty T4
    lda (MessagePtr),y
    sta T3
    tya
    asl
    clc
    adc #ImgPtr1
    tax
    lda T3
    jsr GetLetterImage
    ldy T4
    bne MessageImageLoop
    rts

; All images are reversed since they are read by decrementing loops.

; routine to shuffle the deck

RefreshDeck
    ldx #CardsInDeck-1
RD1
    lda NewDeck,x
    sta DeckStart,x
    dex
    bpl RD1
    rts

ShuffleDeck
    ldx #CardsInDeck
OneShuffle
    dex
    stx T1
SD1
    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.)
SD3
    ldx T1
    tay
    lda DeckStart,x
    sta T2
    lda DeckStart,y
    sta DeckStart,x
    lda T2
    sta DeckStart,y
    ldx T1
    bne OneShuffle
    rts

;    org $fc00

; 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 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
    stx T2
    sty T3
    and #RankMask
    lsr
    lsr
    lsr
    lsr
    tay
    ldx T2
    jsr GetRankImage
    lda T1
    and #DispSuitMask
    lsr
    lsr
    tay
    ldx T3
    jsr GetSuitImage

    lda T1
    bmi HideCard
    and #RealSuitMask
    tax
    lda SuitToColorTable,x
    rts
HideCard
    lda #CardTableColor
    rts

SuitToColorTable
    .byte #RedSuitColor, #RedSuitColor, #BlackSuitColor, #BlackSuitColor

END_UTIL1 = *

    org $fd00
    echo *-END_UTIL1, "bytes available in utility area 1: ", END_UTIL1, "-", *-1

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

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

DrawMessageText
    lda #$01
    sta VDELP0
    sta VDELP1
    ldy #$05
    sty T2
loop2
    ldy T2
    lda (ImgPtr1),y
    sta GRP0
    sta WSYNC
    lda (ImgPtr2),y
    sta GRP1
    lda (ImgPtr3),y
    sta GRP0
    lda (ImgPtr4),y
    sta T1
    lda (ImgPtr5),y
    tax
    lda (ImgPtr6),y
    tay
    lda T1
    sta GRP1
    stx GRP0
    sty GRP1
    sta GRP0
    dec T2
    bpl loop2
    rts

END_UTIL2 = *

    org $fe00
    echo *-END_UTIL2, "bytes available in utility area 2: ", END_UTIL2, "-", *-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
LetterImageC
    .byte %00111100
    .byte %01100110
    .byte %01100000
    .byte %01100000
    .byte %01100110
    .byte %00111100
LetterImageD
    .byte %01111100
    .byte %01100110
    .byte %01100110
    .byte %01100110
    .byte %01100110
    .byte %01111100
LetterImageE
    .byte %01111110
    .byte %01100000
    .byte %01100000
    .byte %01111000
    .byte %01100000
    .byte %01111110
LetterImageH
    .byte %01100110
    .byte %01100110
    .byte %01100110
    .byte %01111110
    .byte %01100110
    .byte %01100110
LetterImageL
    .byte %01111110
    .byte %01100000
    .byte %01100000
    .byte %01100000
    .byte %01100000
    .byte %01100000
LetterImageO
    .byte %00111100
    .byte %01100110
    .byte %01100110
    .byte %01100110
    .byte %01100110
    .byte %00111100
LetterImageP
    .byte %01100000
    .byte %01100000
    .byte %01111100
    .byte %01100110
    .byte %01100110
    .byte %01111100
LetterImageR
    .byte %01100110
    .byte %01100110
    .byte %01100110
    .byte %01111100
    .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
LetterImageV
    .byte %00010000
    .byte %00111000
    .byte %01101100
    .byte %11000110
    .byte %11000110
    .byte %11000110
CopyImage01
    .byte %00000000
    .byte %11101110
    .byte %10001010
    .byte %10001110
    .byte %10000000
    .byte %11100000
CopyImage02
    .byte %10001110
    .byte %10000010
    .byte %11101110
    .byte %10101010
    .byte %11101010
    .byte %00000000
CopyImage03
    .byte %00000111
    .byte %10010001
    .byte %10010111
    .byte %11010101
    .byte %00000111
    .byte %00010000
CopyImage04
    .byte %00000000
    .byte %01010011
    .byte %01010010
    .byte %01110010
    .byte %01000111
    .byte %01000010
CopyImage05
    .byte %00000000
    .byte %01110111
    .byte %01000101
    .byte %01110101
    .byte %00010101
    .byte %01110111
CopyImage06
    .byte %00000000
    .byte %01110111
    .byte %01010010
    .byte %01010010
    .byte %01010110
    .byte %01110010
BylineImage01
    .byte %00001110
    .byte %11100010
    .byte %10101110
    .byte %11101010
    .byte %10001010
    .byte %10000000
BylineImage02
    .byte %00000000
    .byte %01110100
    .byte %01000100
    .byte %01100110
    .byte %01000000
    .byte %01110000
BylineImage03
    .byte %00000000
    .byte %10101001
    .byte %10101001
    .byte %10110001
    .byte %00101001
    .byte %10100001
BylineImage04
    .byte %00000000
    .byte %11010111
    .byte %00010101
    .byte %10010111
    .byte %00000001
    .byte %11010001
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
RankImageLeft
    .byte %11101100
    .byte %10001010
    .byte %10001010
    .byte %10001100
    .byte %10001010
    .byte %10001100  ; "LB" used for debugging
RankImageRight
    .byte %10101100
    .byte %10101010
    .byte %10101010
    .byte %11001100
    .byte %10101010
    .byte %11001100  ; "RB" used for debugging

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, #<LetterImageSpace
    .byte #<LetterImageSpace, #<LetterImageSpace, #<LetterImageSpace
MessageDeal
    .byte #<LetterImageSpace, #<LetterImageD, #<LetterImageE
    .byte #<LetterImageA, #<LetterImageL, #<LetterImageSpace
MessageGameOver
    .byte #<LetterImageSpace, #<LetterImageO, #<LetterImageV
    .byte #<LetterImageE, #<LetterImageR, #<LetterImageSpace
MessageEuchre
    .byte #<LetterImageE, #<LetterImageU, #<LetterImageC
    .byte #<LetterImageH, #<LetterImageR, #<LetterImageE
MessageCopyright
    .byte #<CopyImage01, #<CopyImage02, #<CopyImage03
    .byte #<CopyImage04, #<CopyImage05, #<CopyImage06
MessageByline
    .byte #<LetterImageSpace, #<BylineImage01, #<BylineImage02
    .byte #<BylineImage03, #<BylineImage04, #<LetterImageSpace
MessageSelectAction
ChoiceToLetterTable
    .byte #<LetterImageSpace, #<LetterImageP, #<LetterImageU
    .byte #<LetterImageA, #<LetterImageSpace, #<LetterImageSpace
MessageSelectTrump
    .byte #<LetterImageSpace, #<SuitImageHeart, #<SuitImageDiamond
    .byte #<SuitImageClub, #<SuitImageSpade, #<LetterImageSpace

CursorPosToSelectorPos
    .byte $94, $05, $76, $f6

BidTeamToTrumpPos
    .byte $a2,$48

; 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 $fffc
    echo *-END_DATA, "bytes available in image and table area: ", END_DATA, "-", *-1
    .word CartStart
    .word CartStart

Attachment: Euchre.bin
Description: Binary data

"Cartridge.MD5" "01264c091efc27ce23e43a6aa5cfd89a"
"Cartridge.Name" "Euchre (April 14, 2002 pre-release)"
"Cartridge.Manufacturer" "Erik Eid"
"Cartridge.Rarity" "New Release"
"Cartridge.Type" "4K"
"Controller.Left" "Joystick"
"Display.Format" "NTSC"
""

. . \_ +\_ + o_-/ . O
. \-_o -=/- . .
. . \=// .
---------\_______ ||O ___.______
/* Erik Eid */ \____||L/\_____/
/* eeid@xxxxxxxxx */_______________________
Current Thread