Cards
The cards use a simple system of splitting the value into two halves, the first four bits signify the card value - which, for the ease of evaluating the hands, starts from 0 representing "Two".
Then, in order (as you'd expect), the cards are:
Diamonds Hearts Spades Clubs
Byte Card Byte Card Byte Card Byte Card
0 Two 16 Two 32 Two 48 Two
1 Three 17 Three 33 Three 49 Three
2 Four 18 Four 34 Four 50 Four
3 Five 19 Five 35 Five 51 Five
4 Six 20 Six 36 Six 52 Six
5 Seven 21 Seven 37 Seven 53 Seven
6 Eight 22 Eight 38 Eight 54 Eight
7 Nine 23 Nine 39 Nine 55 Nine
8 Ten 24 Ten 40 Ten 56 Ten
9 Jack 25 Jack 41 Jack 57 Jack
10 Queen 26 Queen 42 Queen 58 Queen
11 King 27 King 43 King 59 King
12 Ace 28 Ace 44 Ace 60 Ace
The upper four bits, when shifted four positions to the right, represent the suits. Obviously, they don't have to be shifted, just this is how the game evaluates them, so that's what we'll do here.
The suits are: 0: Diamonds, 1: Hearts, 2: Spades and 3: Clubs.
Card Graphics
Here are some examples of cards and their values:
Byte Bits Breakdown Result
Lower Upper Card Suit
Byte Bits Byte Bits
6 00000110 6 0110 0 0000 Eight Diamonds
12 00001100 12 1100 0 0000 Ace Diamonds
25 00011001 9 1001 1 0001 Jack Hearts
26 00011010 10 1010 1 0001 Queen Hearts
39 00100111 7 0111 2 0010 Nine Spades
43 00101011 11 1011 2 0010 King Spades
52 00110100 4 0100 3 0011 Six Clubs
To follow the mechanism in the game, start from PrintHand which cycles through all five cards in a hand and prints them to the screen via CheckCardType.
CheckCardType 58170 LD B,A Store the card value in B to process the suit later.
58171 AND %00001111 Keep only bits 0-3.
58173 CP 9 Jump to PrintNumberCard if A is 9 or lower.
58175 JR C,PrintNumberCard
58177 CP 12 Jump to PrintAceCard if A is equal to 12.
58179 JR Z,PrintAceCard
Anything else is a picture card, so work out what we're printing.
* The picture card routine is not shown here as it complicates things, check the disassembly for further details.
Let's take an example and follow it all the way through: 37 The AND turns it into 5, so this card is a "Seven".
Moving onto PrintNumberCard:
PrintNumberCard 58301 LD C,B Copy the original card value into C as we need B for the loop below.
58302 LD B,A Set a counter in B of the card number (this is the lower four bits).
58303 INC B Increment B by one, due to the way the loop below works.
58304 LD DE,$001E The card UDG data blocks are 0030 in length, so store this in DE for the calculation.
58307 LD HL,58422 The card data begins from 58422 so store this in HL.
58310 XOR A Reset the flags.
58311 SBC HL,DE Subtract DE from HL as the loop will add it back and then it will be pointing to the beginning again.
FindCardUDGData_Loop 58313 ADD HL,DE Add 0030 to HL to move to the next block of card UDG data.
58314 DJNZ FindCardUDGData_Loop Decrease the card value counter by one and loop back to FindCardUDGData_Loop until the appropriate card UDG data block is found.
58316 LD (58723),HL Write the card UDG data block pointer to *PointerCardUDGData.
So, our card 37 will end up writing Graphics_CardSeven to *PointerCardUDGData (which will be used later). This references the number part of the card (without any suit applied yet).
Work out the suit.
58319 LD B,4 Using the original card value, shift the upper four bits to be the lower four bits.
SuitShift_Loop 58321 SRL C
58323 DJNZ SuitShift_Loop
58325 LD A,C Write the calculated suit to *CurrentCardSuit.
58326 LD (58722),A
Now calculate the suit UDG data address.
58329 LD B,C Set a counter in B of the calculated suit (this is the upper four bits).
58330 INC B Increment B by one, due to the way the loop below works.
58331 LD DE,0088 The suit UDG data blocks are 0088 in length, so store this in DE for the calculation.
58334 LD HL,59071 The suit UDG data begins from 59071 so store this in HL.
58337 XOR A Reset the flags.
58338 SBC HL,DE Subtract DE from HL as the loop will add it back and then it will be pointing to the beginning again.
FindSuitUDGData_Loop 58340 ADD HL,DE Add 0088 to HL to move to the next block of suit UDG data.
58341 DJNZ FindSuitUDGData_Loop Decrease the suit counter by one and loop back to FindSuitUDGData_Loop until the appropriate suit UDG data block is found.
58343 PUSH DE Set a counter in BC to the length of the suit UDG data block: 0088.
58344 POP BC
58345 LD DE,58983 Set the target in DE to Buffer_CardData.
58348 LDIR Copy the suit UDG data block to the card data buffer.
With 37 after shifting the bits, we end up with 2 - so the suit is Spades. And after the "Find" loop, HL will point to Graphics_SpadesSuitData which is then copied into Buffer_CardData so it becomes the "upper part" of the font data and can be referenced by the UDG data.
Next, we set the UDG font pointer and discover the colour of the card to be printed.
Set the card character-set to be the in-use font.
         58350 LD HL,58207 Write 58207 to *CHARS.
58353 LD (23606),HL
Handle setting the colour of the card.
58356 LD A,19 BRIGHT: ON.
58358 RST 16
58359 LD A,1
58361 RST 16
58362 LD A,16 Set INK: BLACK.
58364 RST 16
58365 LD A,0
58367 RST 16
58368 LD A,(58722) Jump to PrintCard if *CurrentCardSuit is 2 or higher (so Spades or Clubs).
58371 CP 2
58373 JR NC,PrintCard
Else this card is a Diamonds or Hearts so set the INK appropriately.
58375 LD A,16 Set INK: RED.
58377 RST 16
58378 LD A,2
58380 RST 16
Our suit is 2 so the colour will be BLACK.
Now we have all the components, we know the card UDG data and the suit UDG data has been copied into the upper range of it. We also know the colour too, so all that's left to do is to print it on the screen using PrintCard.
The cards are printed using the standard Spectrum printing routine, and so the data is stored as a series of references:
66 67 67 67 67 68
80 107 95 95 107 81
105 95 97 98 95 106
65 95 99 100 95 69
65 101 95 95 101 69
For ease of explaining this, if the data just referenced ASCII - then the print would output the following letter graphics:
B C C C C D
P k _ _ k Q
i _ a b _ j
A _ c d _ E
A e _ _ e E
How the code works, is that we write 58207 to CHARS and this is where the "magic" happens! Rather than reference the graphics for each letter in ROM - the print routine references the game UDGs instead.
So the data we have links through to these address (click to see):
58735 58743 58743 58743 58743 58751
58847 59063 58967 58967 59063 58855
59047 58967 58983 58991 58967 59055
58727 58967 58999 59007 58967 58759
58727 59015 58967 58967 59015 58759
And produces the following output:
udg58735_120x4 udg58743_120x4 udg58743_120x4 udg58743_120x4 udg58743_120x4 udg58751_120x4
udg58847_120x4 udg59063_120x4 udg58967_120x4 udg58967_120x4 udg59063_120x4 udg58855_120x4
udg59047_120x4 udg58967_120x4 udg58983_120x4 udg58991_120x4 udg58967_120x4 udg59055_120x4
udg58727_120x4 udg58967_120x4 udg58999_120x4 udg59007_120x4 udg58967_120x4 udg58759_120x4
udg58727_120x4 udg59015_120x4 udg58967_120x4 udg58967_120x4 udg59015_120x4 udg58759_120x4
The blank spaces are due to the cards being stored "suit-less" (at least on game load anyway). The print routine copies in the suit UDGs when printing every card (regardless of the previous card suit). So the final result looks like this:
demo-card-0-0 demo-card-1-0 demo-card-2-0 demo-card-3-0 demo-card-4-0 demo-card-5-0
demo-card-0-1 demo-card-1-1 demo-card-2-1 demo-card-3-1 demo-card-4-1 demo-card-5-1
demo-card-0-2 demo-card-1-2 demo-card-2-2 demo-card-3-2 demo-card-4-2 demo-card-5-2
demo-card-0-3 demo-card-1-3 demo-card-2-3 demo-card-3-3 demo-card-4-3 demo-card-5-3
demo-card-0-4 demo-card-1-4 demo-card-2-4 demo-card-3-4 demo-card-4-4 demo-card-5-4
Girls
To save on space, the girls are created with one initial image, and then the other "frames" are single sections which are overlaid on top of them.
This means that often, an image is an amalgamation of several images all printed on top of each other.
Girl 1: Shelia
girl-1-frame-1
girl-1-frame-2
girl-1-frame-3
girl-1-frame-4
girl-1-frame-5
girl-1-frame-6
girl-1-frame-7
Girl 2: Ireen
girl-2-frame-1
girl-2-frame-2
girl-2-frame-3
girl-2-frame-4
girl-2-frame-5
Girl 3: Diane
girl-3-frame-1
girl-3-frame-2
girl-3-frame-3
girl-3-frame-4
girl-3-frame-5
Two Pairs
Of interest, the game will (intentionally) randomly gamble redrawing when the girl has two pairs. It's not a particularly strong hand, only higher than a single pair but obviously it's still a pretty good hand!
The girls hand has no cards which form one of the "special" outcomes (special, as in, which aren't able to be found by counting duplicate card face values).
         38622 CALL GetRandomNumber Call GetRandomNumber.
38625 CP 10 Jump to FindCardsToMark if the random number is lower than 10.
38627 JR C,FindCardsToMark
Two pairs is also somewhat of a "special" outcome, so handle it separately.
38629 LD A,(38043) Jump to FindLowestSingleCard if *TableHandEvaluation_Type is equal to "Two Pairs".
38632 CP 3
38634 JR Z,FindLowestSingleCard