Showdown is a two-player gun-fighting game in which each player controls a gunfighter on screen, moving up and down and firing at the opponent. The BASIC loader reads a long hexadecimal string from DATA statements, converts each two-character pair into a byte value using the expression `16*VAL a$(1)+VAL a$(2)`, and POKEs the resulting machine code into memory starting at address 31000. A checksum of 72781 is verified against the accumulated byte values before the machine code is executed, guarding against corrupted DATA. Two custom UDG characters (defined in subroutine 9000) form the sprite of each gunfighter, drawn as a 2×2 block using characters `\a\b` and `\c\d`. Two separate machine code entry points are called via `USR 31000` and `USR 31506`, with the result of a PEEK at address 31631 determining which player won.
Program Analysis
Program Structure
The program is divided into four logical phases:
- Initialization (lines 1–120): Sets border, clears memory to address 30999, calls the UDG setup subroutine, then loads and checksums the machine code from DATA.
- Machine code loading (lines 40–100): Reads hex strings from DATA, converts them to bytes, and POKEs them into RAM from address 31000 upward.
- Game loop (lines 1000–1120): Draws the arena, places random cactus-like obstacles using UDG sprites, runs the machine code game engine, displays the winner, and offers replay.
- Setup subroutine (lines 9000–9170): Defines UDG bitmaps and displays the title/instructions screen.
Machine Code Loading and Checksum
Lines 40–100 implement a hex-string loader. Each DATA record is a string of hexadecimal digit pairs. The loop at lines 50–90 reads one string at a time into a$, then processes two characters at a time:
z = 16*VAL a$(1) + VAL a$(2)converts a two-character hex pair to a byte. This relies onVALtreating a single digit character as its numeric value, which works for digits 0–9 but not for A–F. All hex digits in the DATA are therefore restricted to 0–9 and A–F represented as decimal digit pairs — on inspection the DATA strings contain only hex digits 0–9 and A–F encoded as the characters themselves, soVALis applied to single characters. This is a subtle limitation:VAL "A"would cause an error, so the hex-string decoder here actually relies on the DATA bytes never containing letter hex digits in positions that reachVAL. Looking at the DATA more carefully, digits A–F appear, meaning this loader would fail on those bytes. This is a likely bug: the standard approach on this platform for hex loading usesCODE a$(1)-48or a lookup, notVAL.- Each byte value is accumulated into
tfor checksum purposes. - Bytes are POKEd starting at address
n=31000, incrementingneach iteration.
After loading, line 100 compares t to 72781. If the sum does not match, the program halts with an error message and a low beep. The sentinel value # in the last DATA record signals end-of-data.
UDG Sprite Definition
Subroutine 9000 defines four UDG characters (a, b, c, d) using 32 bytes of bitmap data (8 bytes each), POKEd into USR "a" and onward. The gunfighter sprite is rendered as a 2×2 tile using PRINT INK 4;AT y,x;"\a\b";AT y+1,x;"\c\d", producing a 16×16 pixel figure in yellow ink.
Arena Setup
Lines 1000–1040 draw the game arena. The border columns are created by POKEing attribute memory directly: for each row n from 1 to 23, address 22528+32*n and adjacent cells are set to attribute values 67 and 69 (INK/PAPER combinations for left and right border columns). Four obstacles are then placed at random positions within the play area using the UDG sprites.
Game Engine Execution
The machine code is invoked twice per game via USR 31000 and USR 31506. These two entry points likely handle player 1 and player 2 logic respectively. After execution, PEEK 31631 is tested: a value of 0 indicates Player 1 wins; any other value indicates Player 2 wins. This flag byte is embedded within the machine code block at a fixed offset.
Key Variables
| Variable | Purpose |
|---|---|
a$ | Current hex DATA string being parsed |
n | Current POKE address (starts at 31000) |
t | Running checksum of loaded bytes |
z | Current decoded byte value |
a–f | Pre-set to values 10–15 (unused after initialization — likely intended for hex digit conversion but superseded) |
Notable Techniques
POKE 23693,71sets the border color to black (attribute value 71 encodes the border in the system variableBORDCR).CLEAR 30999both resets the BASIC variable area and sets the stack pointer, protecting machine code loaded above address 31000.- The title screen uses
PAPER 1(blue) withTAB 31to create a full-width colored banner without looping. - Variables
athroughfare initialized to 10–15 at lines 20–30, suggesting an originally planned hex-to-decimal lookup that was abandoned in favor of theVALapproach — though theVALapproach is itself buggy for A–F hex digits. - The replay loop at lines 1100–1110 polls
INKEY$continuously without aPAUSE, which is a busy-wait but functional for keyboard response.
Bugs and Anomalies
- The hex loader uses
VAL a$(1)andVAL a$(2)to parse hex digits.VALon a single alphabetic character (A–F) would cause a BASIC error, yet the DATA strings contain letters A–F. The program likely relies on the machine code bytes being loaded correctly despite this apparent contradiction — possibly the DATA was pre-tested and the checksumming masks the issue, or the DATA strings were crafted to avoid letter-only single-characterVALcalls in a specific way not immediately apparent from inspection. - Variables
athroughf(lines 20–30) are set to 10–15 but never used in the loader logic, suggesting leftover code from an earlier design.
Content
Source Code
1 REM SHOWDOWN
5 POKE 23693,71: BORDER 0
10 CLEAR 30999
12 GO SUB 9000
14 RESTORE
15 PRINT AT 21,0; INK 6; PAPER 1; FLASH 1;" Please wait - Setting up game "
20 LET a=10: LET b=11: LET c=12
30 LET d=13: LET e=14: LET f=15
40 LET a$="": LET n=31000: LET t=0
50 IF a$="" THEN READ a$: IF a$="#" THEN GO TO 100
60 LET z=16*VAL a$(1)+VAL a$(2)
70 LET t=t+z: POKE n,z
80 LET a$=a$(3 TO )
90 LET n=n+1: GO TO 50
100 IF t<>72781 THEN PRINT "ERROR IN DATA": BEEP 2,-10: STOP
120 GO TO 1000
200 DATA "F33E02CD011621100022827B22847BAF32867B32877B3E05328E7B328F7B2A827BCD277B11907BCD"
210 DATA "5F7B01FEFBED78E61FFE1F280C2A827B2D3E08BD201D2C181A01FEFDED78E61FFE1F280C2A827B2C"
220 DATA "3EABBD20062D18032A827B22827BCD277B11BA7BCD5F7B2A847BCD277B111E001911907BCD5F7B01"
230 DATA "FEDFED78E61FFE1F280C2A847B2D3E08BD201D2C181A01FEBFED78E61FFE1F280C2A847B2C3EABBD"
240 DATA "20062D18032A847B22847BCD277B111E001911E47BCD5F7B3A867BB7201D01FEFEED78E61FFE1F28"
250 DATA "492A827B3E0A856F22887B3E02328C7B32867B2A887BCD277B3A8C7B5F16001936003A8C7B3CFE20"
260 DATA "2006AF32867B181A328C7B237EB72810AF32867B3A8C7BFE1ED2CC7A3600180236FF3A877BB7201D"
270 DATA "01FE7FED78E61FFE1F28482A847B3E0A856F228A7B3E1D328D7B32877B2A8A7BCD277B3A8D7B5F16"
280 DATA "001936003A8D7B3DFEFF2006AF32877B1819328D7B2B7EB7280FAF32877B3A8D7BFE023813360018"
290 DATA "0236FF060FC5060010FEC110F8C336792A827BCD277B110E7CCD5F7B3A8E7B3D328E7B286B3E16D7"
300 DATA "3E00D73E06D73A8E7BC630D7CD127B2A827BCD277B11907BCD5F7B21100022827BC336792A847BCD"
310 DATA "277B111E0019110E7CCD5F7B3A8F7B3D328F7B282B3E16D73E00D73E1FD73A8F7BC630D7CD127B2A84"
320 DATA "7BCD277B111E001911907BCD5F7B21100022847BC33679FBC921000211050006C8E5D5C5CDB503C1"
330 DATA "D1E12310F4C9C5D5444D21004079E6071E00571979E63F16005F79E6F8E526006F19160059ED5229"
340 DATA "29EBE11911000079E6C05FEB2929292929EB19D1C1C906151A772C131A772D24137CE60720107CD6"
350 DATA "08677DC6206FE6E020047CC6086710E0C90000000000000000000000000000000000000000000000"
360 DATA "000000000000000000000000000000000000000000000000000000000000000000078007801FE006"
370 DATA "8007C00780030007800FC01FC03FEF37F83FB01F800FC007C006C006C00EC01CE018F001E001E007"
380 DATA "F8016003E001E000C001E003F003F8F7FC1FEC0DFC01F803F003E003600360037007380F18000000"
390 DATA "00000003C007E00FF00FF03FFC3E7C3E7C3E7C300C300C3E7C3E7C3E7C3E7C3E7C3E7C3FFC3FFC00"
400 DATA "#"
1000 CLS : FOR n=1 TO 23: LET a=22528+32*n: POKE a,67: POKE a+1,67: POKE a+30,69: POKE a+31,69: NEXT n
1020 FOR n=1 TO 4
1030 LET y=INT (RND*20)+1: LET x=INT (RND*20)+6
1040 PRINT INK 4;AT y,x;"\a\b";AT y+1,x;"\c\d": NEXT n
1050 PRINT AT 0,0;"LIVES 5";TAB 25;"LIVES 5"
1060 LET z=USR 31000: LET z=USR 31506
1070 IF PEEK 31631=0 THEN PRINT AT 0,10;"PLAYER 1 WINS": GO TO 1090
1080 PRINT AT 0,10;"PLAYER 2 WINS"
1090 PRINT AT 21,0;"Do you want to play again? (Y/N)"
1100 IF INKEY$="Y" OR INKEY$="y" THEN GO TO 1000
1110 IF INKEY$<>"N" AND INKEY$<>"n" THEN GO TO 1100
1120 CLS : STOP
9000 RESTORE 9010: FOR n=0 TO 31: READ a: POKE USR "a"+n,a: NEXT n
9010 DATA 0,0,1,1,1,9,15,13,64,224,240,176,240,240,240,176
9020 DATA 7,7,7,2,3,1,1,31,240,240,240,240,224,192,192,248
9100 CLS : PRINT PAPER 1;TAB 31;TAB 8;"S H O W D O W N";TAB 31;" ";TAB 31;" "
9110 PRINT AT 5,0; INK 3;"LEFT PLAYER";AT 5,20;"RIGHT PLAYER"
9120 PRINT AT 7,1;"Q - UP";AT 7,24;"P - UP"
9130 PRINT AT 9,1;"A - DOWN";AT 9,24;"L - DOWN"
9140 PRINT AT 11,1;"Z - FIRE";AT 11,15;"SYM. SHIFT - FIRE"
9170 RETURN
9999 SAVE "showdown" LINE 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

