--- title: "Horserace" id: 71075 type: "computer_media" slug: "horserace-2" url: "http://localhost/computer_media/horserace-2/" markdown_url: "http://localhost/computer_media/horserace-2.md" published_at: "2026-08-27T19:42:33+00:00" modified_at: "2026-08-27T19:47:55+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2022/04/20230809-041332.jpg" excerpt: "A horse racing game for up to eight bettors, featuring UDG jockeys, a bugle fanfare, and randomized race movement driven by a computed GO TO dispatch table." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Richard Cunningham" slug: "richard-cunningham" taxonomy: "indiv" url: "http://localhost/indiv/richard-cunningham/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_type: "Program" programmers: - name: "Richard Cunningham" slug: "richard-cunningham" taxonomy: "indiv" url: "http://localhost/indiv/richard-cunningham/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Horserace%20%28198x%29%28Cunningham%2C%20Richard%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" media_type_tags: "Game" --- # Horserace Horserace is a multi-bettor horse racing simulation for up to eight players, where each bettor picks a horse before the race begins. The program uses two UDG characters — a horse-and-jockey figure (UDG-M) and a track marker (UDG-A) — defined via READ/DATA loops at startup. Race movement is driven by a random number (1–8) that selects one of eight horses to advance each iteration, using a computed GO TO of the form `(r*30)+360` to dispatch to the correct horse’s print routine. The track is drawn using PLOT/DRAW commands to create a bordered rectangle, with horizontal lane lines rendered via PRINT AT. An extended bugle call fanfare is played from DATA statements before the race begins, and a winning jingle loop plays when any horse reaches column 32. *** ### Program Structure The program is organized into several distinct phases, controlled by GO SUB and GO TO branching: 1. **Lines 10–180:** Initialization — UDG definition, screen setup, title display, and border drawing. 2. **Lines 190–320:** Track layout rendering and bugle fanfare playback from DATA. 3. **Lines 330–620:** Main race loop — random horse selection and position advancement. 4. **Lines 630–680:** Border-draw subroutine, winner announcement, and restart. 5. **Lines 690–870:** Bet entry screen — collects bettor count and each bettor’s horse selection. 6. **Line 880:** SAVE with auto-run. ### Machine Code Usage Lines 20–40 and 60 invoke machine code via `RANDOMIZE USR FN a()`. The function `FN a()` (line 20) computes the address of the machine code by reading the system variables at addresses 23635–23636 (the PROG pointer, giving the start of BASIC) and adding 5, which points into the embedded binary data stored in the REM statement at line 10. Different POKE values into system variables 23728, 23729, and 23723 are set before each call, presumably to parameterize the routine for different tasks (title rendering on first call, a secondary display action on the second call). ### UDG Definitions Two UDGs are defined at lines 140–170: - **UDG-M:** A horse-and-jockey sprite, defined by DATA `0,0,134,127,62,60,86,169`. Used in the horse string `h$` and in the title display. - **UDG-A:** A track post/marker, defined by DATA `0,24,24,154,127,60,60,60`. Used in lane separator lines printed by the track layout loop. The horse display string `h$` is assembled at line 180 as `CHR$ 17 + CHR$ 5 + "_" + UDG-M`, combining an INK color attribute byte (INK 5) with an underscore and the horse UDG for a colored jockey-on-track appearance. ### Race Dispatch via Computed GO TO The core race mechanic (lines 360–620) uses a computed GO TO to select which horse advances each tick: - Line 360: `LET r = INT(RND*8+1)` picks a horse number 1–8. - Line 380: `GO TO (r*30)+360` jumps to lines 390, 420, 450, 480, 510, 540, 570, or 600 for horses 1–8 respectively. - Each horse block prints `h$` at the appropriate row and increments the horse’s position variable, then loops back to line 360. This is an efficient dispatch table implemented entirely in BASIC, avoiding a chain of IF/THEN comparisons. ### Track Layout and Graphics The track is drawn at lines 200–250. A STEP 2 loop (lines 200–220) prints lane numbers and the UDG-A track marker string across rows 2–16. A vertical finish line is plotted pixel-by-pixel at x=248 (line 240). The border rectangle subroutine at line 630 uses four chained DRAW commands to form a closed rectangle. ### Bugle Fanfare The racing call fanfare (lines 260–320) reads pairs of duration and pitch values from DATA, scales duration by dividing by 18, and plays each note with BEEP. The sentinel value `999,999` triggers a short PAUSE before exiting to the race start. The DATA spans two lines (300–310) representing two phrases of the bugle call. ### Betting System Lines 700–870 implement the pre-race betting phase. The program accepts 1–8 bettors via INPUT into `b$`, validated by string comparison (`b$<"1" OR b$>"8"`) and a length check (`LEN b$>1`). Each bettor then picks a horse number, stored in `w$` with the same validation pattern. Bet selections are displayed with colored PRINT attributes but are not stored beyond the screen — there is no array or variable tracking each bettor’s pick for post-race comparison. ### Notable Techniques and Idioms - String comparison for numeric range validation (`b$<"1" OR b$>"8"`) is a common Spectrum BASIC idiom that works correctly for single-digit inputs. - `RESTORE 150` and `RESTORE 300` reset the DATA pointer to specific lines, allowing the UDG data and fanfare data to be re-read on restart. - The OVER 1 PRINT technique at lines 90, 100, and 250 XORs color attributes onto the track background without disturbing underlying characters. - BORDER changes synchronize with BEEP during the race (line 370–371), giving a light-show effect tied to horse movement. ### Bugs and Anomalies - **Variable `i` reused as loop variable and horse:** Variable `i` is used as a FOR loop counter in lines 90–100 and also as a horse position variable at line 350 (`LET i=a`), but horse `i` is never subsequently advanced or printed — the race only handles horses `a` through `h` (eight horses), making variable `i` at line 350 a vestigial assignment with no effect. - **Winner jingle uses variable `x` for pitch:** Line 660 references `x` in BEEP calls, but `x` at this point holds the last value from the UDG definition loop (0–7), making the jingle pitches semi-arbitrary rather than intentional musical notes. - **Bettor picks not evaluated:** The program announces which horse number wins (variable `r`) but never compares `r` to the stored bettor inputs (`w$`), so no winner among the bettors is declared. - **Line 680 RESTORE 150 then GO TO 10:** Restarting at line 10 re-runs UDG setup and machine code calls, then falls through to the bet entry at line 700 again — this is intentional as a full restart, though the RESTORE at line 680 is redundant since GO SUB 140 at line 30 will read the same DATA regardless (the RESTORE at line 860 covers the fanfare data reset). ## Source Code ``` 10 REM !VAL \~!CODE \RETURN (RETURN ( THEN ▞ THEN AND *U\>"PEEK #(IF RESTORE !LEN \N!@RESTORE !VAL \~NEW o>& THEN % THEN IF gSTR$ RESTORE !VAL \~LLIST RETURN 8RETURN 8"TAB \LLIST RESTORE ~= OR RETURN [UDG-A]8p[UDG-A]STR$ XCOPY OR [UDG-A]o> THEN % THEN IF gSTR$ RESTORE !ATTR \~LLIST RETURN (rCAT >2AT \RESTORE ~W!ATTR \~RETURN z({*TAB \ OR STR$ r#s+:AT \RETURN (%RETURN xRETURN RESTORE !CODE \ THEN FLLIST OR LPRINT PIGO SUB BSTR$ LPRINT PIGO SUB BSTR$ LINE "TAB \LLIST #:AT \=2AT \ POINT !CODE \ THEN F*TAB \(PIGO SUB BA▄PIGO SUB BATTR \RETURN #"TAB \LLIST #>"PEEK >=DATA ▌! THEN ? THEN THEN -SAVE THEN ? THEN THEN ,SAVE T]!ATTR \~RETURN (FLASH OR RESTORE ~VERIFY *TAB \r#s+"TAB \LLIST #STR$ CONTINUE *TAB \RETURN PIGO SUB B"TAB \LLIST #>"PEEK >=[UDG-B]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 20 DEF FN a()= PEEK 23635+256* PEEK 23636+5 30 POKE 23660,30:CLS :GO SUB 140 40 POKE 23728,8:POKE 23729,3:POKE 23723,3:RANDOMIZE USR FN a() 50 BORDER 7:BEEP 2,15:REM "[UDG-M] HORSERACE [UDG-M]" 60 POKE 23728,20:POKE 23729,4:POKE 23723,2:RANDOMIZE USR FN a() 70 REM PAPER 2;"PRESS ANY KEY TO BEGIN!" 80 GO SUB 630:PLOT 0,88:DRAW 255,0:PLOT 0,119:DRAW 255,0:PRINT AT 0,7;"© R.J. Cunningham" 90 FOR i=1 TO 6:PRINT AT i,0; OVER 1; PAPER 2; BRIGHT 1,,:NEXT i 100 FOR i=11 TO 21:PRINT AT i,0; OVER 1; PAPER 4; BRIGHT 1,,:NEXT i 110 REM l/n 1-6 large print; l/n7-14 opening graphics 120 REM l/n 15-40 udg graphics 130 PAUSE 0:GO TO 700 140 FOR x=0 TO 7:READ n:POKE USR "[UDG-M]"+x,n:NEXT x 150 DATA 0,0,134,127,62,60,86,169 160 FOR x=0 TO 7:READ n:POKE USR "[UDG-A]"+x,n:NEXT x 170 DATA 0,24,24,154,127,60,60,60 180 LET H$= CHR$ 17+ CHR$ 5+"_[UDG-M]":RETURN 190 REM track layout 200 FOR x=2 TO 16 STEP 2 210 PRINT AT x,0;x/2;"_[UDG-A]_____________________________" 220 NEXT x 230 GO SUB 630 240 FOR y=31 TO 159:PLOT 248,y:NEXT y 250 FOR i=0 TO 21:PRINT AT i,0; OVER 1; PAPER 5,,:NEXT i 260 REM racing bugle sound 270 READ d,p:LET d=d/18 280 IF P=999 THEN PAUSE 30:GO TO 330 290 BEEP d,p:GO TO 270 300 DATA 3,1,2,6,2,10,3,13,1,13,1,13,2,13,3,10,1,10,1,10,3,10,3,6,2,10,2,6,5,1,4,69,4,69 310 DATA 3,1,2,6,2,10,3,13,1,13,1,13,2,13,3,10,1,10,1,10,1,10,2,1,2,1,2,1,3,6 320 DATA 999,999 330 PRINT AT 21,9; PAPER 2; INK 9;"THEY'RE OFF!" 340 REM horserace program 350 RANDOMIZE :LET a=3:LET b=a:LET c=a:LET d=a:LET e=a:LET f=a:LET g=a:LET h=a:LET i=a 360 LET r= INT (RND*8+1) 370 BEEP .05,r:BORDER r-1 380 GO TO (r*30)+360 390 PRINT AT 2,a-1;h$ 400 LET a=a+1:IF a=32 THEN GO TO 640 410 GO TO 360 420 PRINT AT 4,b-1;h$ 430 LET b=b+1:IF b=32 THEN GO TO 640 440 GO TO 360 450 PRINT AT 6,c-1;h$ 460 LET c=c+1:IF c=32 THEN GO TO 640 470 GO TO 360 480 PRINT AT 8,d-1;h$ 490 LET d=d+1:IF d=32 THEN GO TO 640 500 GO TO 360 510 PRINT AT 10,e-1;h$ 520 LET e=e+1:IF e=32 THEN GO TO 640 530 GO TO 360 540 PRINT AT 12,f-1;h$ 550 LET f=f+1:IF f=32 THEN GO TO 640 560 GO TO 360 570 PRINT AT 14,g-1;h$ 580 LET g=g+1:IF g=32 THEN GO TO 640 590 GO TO 360 600 PRINT AT 16,h-1;h$ 610 LET h=h+1:IF h=32 THEN GO TO 640 620 GO TO 360 630 DRAW 255,0:DRAW 0,175:DRAW -255,0:DRAW 0,-175:RETURN 640 PRINT AT 18,4;".....The winner is ";r;"!!!" 650 PRINT AT r*2,0; OVER 1; BRIGHT 1,, 660 FOR a=1 TO 10:BEEP .5,x-1:BEEP .5,x-5:BEEP .5,x-3:BEEP .5,x-10:PAUSE 10:BEEP .5,x-10:BEEP .5,x-3:BEEP .5,x-1:BEEP .8,x-5 670 PRINT AT 21,2;"PRESS ENTER TO RACE AGAIN!!!" 680 PAUSE 1800:CLS :RESTORE 150:GO TO 10 690 REM bet entries 700 BEEP 1,10:CLS :BORDER 6 710 GO SUB 630 720 PRINT AT 1,7; PAPER 6;"TREVI RACE TRACK" 730 PRINT " There are eight horses in this"'" race." 740 PRINT :PRINT " Pick the"; PAPER 6;" TREVI "; PAPER 7;"entry and you"'" HAVE YOURSELF A WINNER!" 750 INPUT "OK! HOW MANY BETTORS? NO MORE THAN EIGHT PLEASE!";b$:BEEP .1,25 760 IF b$<"1" OR b$>"8" THEN GO TO 750 770 IF LEN b$>1 THEN GO TO 750 780 PRINT 790 FOR a=1 TO VAL b$ 800 PRINT PAPER 5;a; " Bets horse#:"; 810 INPUT w$:IF LEN w$>1 THEN GO TO 810 820 IF W$<"1" OR W$>"8" THEN GO TO 810 830 BEEP .2,15:PRINT INK 7; PAPER 2; BRIGHT 1;A;" BETS HORSE # ";W$ 840 NEXT a 850 PRINT :PRINT AT 21,8; INK 7; PAPER 2;"ALL BETS DOWN!" 860 PAUSE 100:RESTORE 300:CLS 870 GO TO 190 880 SAVE "horserace" LINE 10 ```