--- title: "Ocean Defense" id: 70812 type: "computer_media" slug: "ocean-defense" url: "http://localhost/computer_media/ocean-defense/" markdown_url: "http://localhost/computer_media/ocean-defense.md" published_at: "2026-08-23T15:08:02+00:00" modified_at: "2026-08-23T22:52:58+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/Ocean-Defense.png" alt: "Ocean Defense screen" excerpt: "Drop depth charges on enemy subs while intercepting their missiles in this scrolling naval action game featuring custom graphics and two difficulty levels." 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/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Ocean%20Defense%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" tsrun_member: "Ocean Defense (198x)(-)(TS2068)(US)(Program).tap" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/Ocean-Defense.png" alt: "Ocean Defense screen" media_type_tags: "Game" --- # Ocean Defense Ocean Defense is a submarine warfare game in which the player maneuvers a ship across the top of the screen, dropping depth charges to destroy enemy subs while intercepting missiles they launch at the home base. The program uses 17 custom UDG (User Defined Graphics) characters, stored from USR “A” onward, which are saved and loaded separately as a CODE block. Scrolling sea-lane rows are implemented by rotating string arrays — A$(1) and A$(3) shift left by one character per frame, while A$(2) shifts right — creating a parallax effect across three rows. Difficulty selection sets the variable SK, which modifies missile evasion probability in the subroutine at line 1615 and affects scoring. The end-of-mission statistics screen at line 3000 renders icons for destroyed subs, missed missiles, and depth charge counts in a tabular layout, and computes a percentage rating using a formula combining raw score, accuracy, and mission number. *** ### Program Structure The program is organized into a set of functional blocks separated by line number ranges: - **Lines 10–200:** Initialization, setup, and main intro/title screen. - **Lines 690–790:** Main game loop — scrolls sea rows, draws sprites, checks input, dispatches to subroutines. - **Lines 800–830 (GO SUB 800):** Draws the player’s ship moving across the screen. - **Lines 900–930 (GO SUB 900):** Status bar update; checks home base destruction condition. - **Lines 1000–1040 (GO SUB 1000):** Fires a depth charge (if charges remain). - **Lines 1100–1160 (GO SUB 1100):** Moves the depth charge downward; detects collisions via `SCREEN$` and `ATTR`. - **Lines 1400–1470:** Submarine destruction sequence with animated explosion using INK cycling. - **Lines 1500–1570:** Handles depth charge hitting sea-row graphics (near-miss/sea damage logic). - **Lines 1600–1650 (GO SUB 1600):** Randomly generates a missile launch from a submarine row. - **Lines 1700–1840 (GO SUB 1700):** Moves missile upward toward the home base ship. - **Lines 1900–1920:** Missile misses — increments `MM` (missiles missed). - **Lines 2000–2030:** Wave completion — flash effect via `RANDOMIZE USR 23769`, advance to next stage. - **Lines 2100–2140:** Missile hits ship; ship loss logic, game-over if ships depleted. - **Lines 3040–3300:** End-of-mission debrief screen with statistics, rating calculation, and graphical tallying. - **Lines 4010–4150 (GO SUB 4000):** Title/intro screen with difficulty selection loop. - **Lines 7005–7070:** UDG definition utility (not called during normal gameplay; used to create the custom character set). - **Lines 8010–8040:** SAVE/LOAD bootstrap for game code and UDG CODE block. ### Scrolling Sea Rows Three rows of sea graphics are stored as 32-character strings in the array `A$`. Each game loop iteration, the strings are rotated in-place: - `A$(1)` and `A$(3)` scroll left: `LET A$(1)=A$(1,2 TO )+A$(1,1)` - `A$(2)` scrolls right: `LET A$(2)=A$(2,32)+A$(2, TO 31)` This gives a simple but effective parallax impression across the three submarine lanes, printed at rows 8, 13, and `L` (a randomly chosen row between 15 and 19). The three lanes are printed in different INK colors based on the stage number (`ST`), so later waves appear in different colors. ### Custom UDG Characters The game relies on 17 custom UDGs (characters A through Q, codes 144–160), loaded at startup from a separately saved CODE block starting at `USR "A"`. The utility at lines 7005–7070 was used to define each glyph by POKEing 8 bytes per character. In-game, these UDGs represent ship parts, sub graphics, missile sprites, explosion tiles, and sea-wave block graphics. The ship display at line 180 uses a 32-character string of UDG references to render the home base across the bottom of the play area. ### Collision Detection The game uses two Spectrum BASIC collision methods: - `SCREEN$ (X,Y)<>" "` — detects whether the depth charge has hit any visible character at its current position (line 1130). - `ATTR (X,Y)=78` — checks the attribute byte at the collision cell; value 78 corresponds to BRIGHT 0, PAPER 1, INK 6, which identifies submarine cells specifically, routing to the destroy sequence at line 1400 rather than the near-miss handler (line 1510). ### Machine Code Usage Two ROM addresses are called via `RANDOMIZE USR`: - `RANDOMIZE USR 23769` — calls the Spectrum’s TV interrupt handler / frame flash routine, used to create rapid screen flash effects during wave completion (line 2010) and missile hits (line 2120, 2140). - `RANDOMIZE USR 23792` — called in the home-base destruction sequence (line 2140), producing a different visual effect. `POKE 23779,N` writes to the BORDCR system variable, changing the border and lower screen paper color directly, used for flash effects at line 2010. `POKE 23658,8` at line 10 sets FLAGS2 to disable the lower-case shift lock, ensuring consistent keyboard input. ### Difficulty System The variable `SK` (1 = Admiral, 2 = Sea Cadet) is set at line 4120 via `INKEY$` key detection using the Boolean arithmetic idiom. `SK` affects gameplay in several places: - Line 40: `DD=44+(11 AND SK=2)` — Sea Cadets start with 55 depth charges; Admirals get 44. - Line 1615: `IF R<0+(.66 AND SK=1)+(.83 AND SK=2) THEN RETURN` — At Admiral difficulty, missiles fire 34% of the time; at Cadet, only 17%. - Line 920: Home base is destroyed after `(SK+1)*3` missiles reach it — 6 for Admiral, 9 for Cadet. - Lines 3050, 3290: Difficulty label displayed on debrief screen. ### Boolean Arithmetic Idiom The program makes extensive use of the Sinclair BASIC convention that logical expressions evaluate to 1 (true) or 0 (false), enabling compact conditional assignment without `IF` statements. Examples: - `LET DD=44+(11 AND SK=2)` — adds 11 depth charges for Cadet mode. - `LET S=S+(1 AND X=8)+(2 AND X=13)+(3 AND X=L)` — awards different scores depending on which sub row was hit. - `LET XX=1+(1 AND X=13)+(2 AND X=L)` — identifies which sea row array to damage. - `(" \q" AND DD<>0)` — prints a depth charge icon only if charges remain. ### Rating Calculation The end-of-mission percentage rating at lines 3240–3250 uses a multi-factor formula: - `K1 = S * 0.267` — score contribution. - `O = (DC-(SU+MD))/(SU+MD)^0.5` — accuracy penalty based on depth charges used versus kills. - `K2 = 20 - (O*3)` — accuracy component. - `K3 = 20 - MM` — penalty for missiles that reached home base. - `K4 = ST*6` — bonus for completing more waves. The final rating `K = INT(K1+K2+K3+K4)` is displayed as a percentage. Note that negative values of O or MM exceeding 20 can push K2 or K3 negative, naturally penalizing poor play. ### Bugs and Anomalies - Line 2020 contains the typo `"MISSION COMPLEATED"` (should be “COMPLETED”). This appears in the win condition message. - Lines 50 and 130 are referenced by `GO TO 50` (line 2030) and `GO TO 130` (line 2130) but are not present in the listing. Line 30 and line 140 are the closest existing lines; this likely causes a “Line does not exist” error on wave advance or ship loss, unless these lines exist in the actual loaded program but were omitted from this listing. - Line 1110 contains `IF P=1 THEN LET X=X+1`, but `P` is also used as the loop variable in the `FOR P=0 TO 20` at line 2010, which will overwrite the depth charge direction variable. This could cause incorrect depth charge behavior if lines 2010 and 1110 interact. - The missile movement at line 1705 uses `LET X1=X1-.5` and line 1710 checks `IF X1=3`. Since X1 decrements by 0.5, it will pass through 3.0 correctly if it started at an odd integer plus 0.5, but may skip the value 3 exactly depending on initial conditions — a potential off-by-one in missile arrival detection. - Line 3050 has a trailing `'` character after the closing quote, which in Spectrum BASIC is a newline token within a PRINT statement. This is likely a spurious character but is harmless. ### Save/Load Architecture The program is designed to be saved as two separate blocks: the BASIC program saved with `LINE 8040` to auto-run, and the UDG data saved as a 136-byte CODE block (`17*8` bytes from `USR "A"`). Line 8040 loads the CODE block before running, ensuring the custom characters are in place before the title screen displays. ## Source Code ``` 1 REM Px RETURN IF <>&> COPY OPEN # RETURN STEP FORMAT \\> OPEN # RETURN STEP FORMAT \\%STICK RETURN GO TO <>> OPEN # RETURN = CLS <> 10 POKE 23658,8: GO SUB 4000 30 LET ST=0: LET SU=0: LET MM=0: LET DC=0: LET MD=0 40 LET DD=44+(11 AND SK=2): LET SS=3: LET S1=0: LET S=0 60 DIM A$(3,32): LET ST=ST+1: LET PP=0: IF RND>.5 THEN LET PP=5 70 LET A$(1)=" \h\i\j \h▄\i\j \h\b\i\j " 80 LET a$(2)=" \k▄\i▄\m \k▄\i▄\m " 90 LET a$(3)=" \h▄\i▄\j \h▄\b\i\j " 140 BRIGHT 1: PAPER 1: INK 7: BORDER 0: CLS 150 FOR n=0 TO 3: PRINT AT N,0; PAPER PP;" ": NEXT N 160 IF PP=0 THEN FOR N=0 TO 40: LET Y=INT (RND*24)+151: LET X=INT (RND*255): PLOT X,Y: NEXT N 170 LET X=5: LET Y=0: LET M=0: LET P=-1: LET D=0: LET B=1 180 PRINT AT 21,0; INK 6;"\a\e\d\d\e\q\e\e\p\e\e\a\d\d\d\e\e\q\e\d\d\d\e\d\a\e\e\a\e\d\d\e" 190 GO SUB 900: LET L=INT (RND*5)+15 200 PRINT #1;AT 1,0; PAPER 2;D$ 690 LET A$(1)=A$(1,2 TO )+A$(1,1) 700 LET A$(2)=A$(2,32)+A$(2, TO 31) 710 LET A$(3)=A$(3,2 TO )+A$(3,1) 720 PRINT AT 8,0; INK 7;A$(1);AT 13,0; INK (ST+1);A$(2);AT L,0; INK (ST+2);A$(3) 730 GO SUB 800 740 IF INKEY$="1" THEN GO SUB 1000: REM CHANGE 255 TO 191 IF YOU HAVE AN ISSUE 3 SPECTRUM 750 IF M=0 THEN GO SUB 1600 760 IF M=1 THEN GO SUB 1700 770 IF D=1 THEN GO SUB 1100 780 IF INKEY$="0" THEN GO SUB 800 790 GO TO 300 810 PRINT AT 3,B; PAPER PP; INK 3;(" \q" AND DD<>0);(" " AND DD=0); INK 0+(7 AND PP=0);"\a\b\c\d\e";AT 4,B; INK 2; PAPER 1;" \f▀▀▀▀\g": LET B=B+1 820 IF B=26 THEN PRINT AT 3,B; PAPER PP;" ";AT 4,B; PAPER 1;" ": LET B=0 830 RETURN 910 PRINT #1;AT 0,0; INK 6; PAPER 0;"LOST:";MM;" D/C:";DD;" SHIPS :";SS;" SCORE:";S;" " 920 IF MM=(SK+1)*3 THEN LET M$=" HOME BASE DESTROYED ": GO TO 2140 930 RETURN 1010 IF DD=0 THEN RETURN 1020 LET D=1: LET DD=DD-1: LET DC=DC+1: BEEP .01,20: IF D=1 THEN PRINT AT X,Y;" " 1030 PRINT AT 3,B; PAPER PP;" ";AT 2,B-1; INK 3;"\q": BEEP .01,25: PRINT AT 2,B-1; PAPER PP;" ";AT 3,B-2; INK 3;"\p";AT 3,B-2;" " 1040 GO SUB 900: LET X=4: LET Y=B-1: RETURN 1110 PRINT AT X,Y;" ": BEEP .009,-X: IF P=1 THEN LET X=X+1 1120 IF X=20 THEN BEEP .1,-40: LET D=0: RETURN 1130 IF SCREEN$ (X,Y)<>" " THEN GO TO 1500 1150 PRINT AT X,Y; INK 3;("\q" AND p=-1);("\p" AND P=1) 1160 LET p=-P: RETURN 1420 LET MD=MD+1: LET S=S+3 1450 FOR N=7 TO 1 STEP -1: PRINT AT X1,Y1; PAPER 1; INK N;"\l";AT X1+1,Y1;"\l";AT X1+2,Y1;"\l": BEEP .02,0: NEXT N 1460 PRINT AT X1,Y1;" ";AT X1+1,Y1;" ";AT X1+2,Y1;" ": LET D=0: LET M=0 1470 GO SUB 900: RETURN 1500 IF ATTR (X,Y)=78 THEN GO TO 1400 1510 LET OV=0: LET D=0: LET SU=SU+1: BEEP .1,40: 1520 LET H=Y-5: LET J=Y+5: IF H<1 THEN LET OV=1: LET H=1 1530 LET XX=1+(1 AND X=13)+(2 AND X=L): LET A$(XX,H TO J)=" ": IF OV=1 THEN LET A$(XX,30 TO 32)=" " 1540 PRINT AT X,Y-1; BRIGHT 1; FLASH 1; INK 2; PAPER 6;"\l\l\l" 1550 LET S=S+(1 AND X=8)+(2 AND X=13)+(3 AND X=L) 1560 LET S1=S1+1: IF S1=ST*7 THEN GO TO 2000 1570 GO SUB 900: RETURN 1610 LET T=INT (RND*2): LET X1=0+(13 AND T=0)+(L AND T=1): LET Y1=INT (RND*24)+1 1615 LET R=RND: IF R<0+(.66 AND SK=1)+(.83 AND SK=2) THEN RETURN 1620 IF SCREEN$ (X1,Y1)=" " OR X1=0 THEN RETURN 1630 LET C=1: LET X1=X1-2 1640 PRINT AT X1+1,Y1; INK 6;"\n": BEEP .08,-20: PRINT AT X1,Y1; INK 6;"\n";AT X1+1,Y1;"\o" 1650 LET M=1: LET X1=X1-1 1705 PRINT AT X1+2,Y1; PAPER C;" ": LET X1=X1-.5 1710 IF X1=3 THEN GO TO 1800 1720 IF X1<=-1 THEN GO TO 1900 1730 PRINT AT X1,Y1; INK 6; PAPER C;"\n";AT X1+1,Y1;"\o";AT X1+2,Y1; INK 2;"\l" 1740 RETURN 1810 IF Y1>=B-1 AND Y10 THEN RETURN 4140 FOR N=35 TO 34.45 STEP -.02: BEEP .02,N: NEXT N 4150 PAUSE 35: GO TO 4110 7005 LET B=65 7010 LET N=(B*8)+64848 7020 PRINT INVERSE 1;"GRAPHIC ";CHR$ B: PRINT 7030 FOR M=0 TO 7 7040 INPUT A: PRINT A 7050 POKE N+M,A 7060 NEXT M: BEEP .1,10: PRINT : LET B=B+1 7070 LET N=N+8: GO TO 7020 8010 SAVE "ocean def" LINE 8040 8020 SAVE "graphics"CODE USR "A",17*8 8030 VERIFY "": VERIFY ""CODE 8035 STOP 8040 LOAD ""CODE : RUN ```