3D Character Set

Developer(s): Algis Gedris
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Font, Graphics

This program renders text in a three-dimensional block-letter style on screen, using pixel-level POINT detection and sequences of PLOT/DRAW commands to construct each character’s 3D face, top, and side. It loads a 1,300-byte machine code routine from tape into address 64000, which handles screen printing (COPY) via a specific printer interface (AERCO with Star SG-10). The BASIC loop scans each character’s pixel map at line 150 using POINT, then at lines 160–180 draws a three-face 3D box for every set pixel using relative DRAW commands, including diagonal strokes (DRAW 5,5 and DRAW -5,-5) to form the depth effect. The program includes an introductory sequence that renders “3D”, “-“, and “LETTERS” as a demo before prompting the user to enter up to seven characters of their own. POKEs to addresses 23609 and 23658 adjust system parameters for border color timing and caps lock behavior.


Program Analysis

Program Structure

The program is organized into three distinct phases: an introduction/demo sequence, a user input loop, and a shared rendering subroutine. Control flow is summarized below:

  1. Initialization (line 10): Loads machine code from tape, sets system POKEs, configures display, then jumps to line 310.
  2. Introduction (lines 310–350): Displays a title screen explaining printer interface requirements, then jumps to line 20.
  3. Demo sequence (lines 20–50): Calls the rendering subroutine (line 130) three times with preset strings " 3D", " -", and "LETTERS" at staggered vertical offsets, then displays instructional text.
  4. User input loop (lines 100–300): Prompts for vertical offset p and a string a$ (1–7 characters), renders it, optionally prints a hard copy, and loops.
  5. Rendering subroutine (lines 130–200): Shared by both the demo and the user input loop; returns via RETURN only when called from the demo (lines 210–230 check for the demo strings), otherwise continues to the copy/loop prompts.
  6. Save/Verify (lines 360–370): Saves the BASIC program and machine code block to tape; these lines are not reached during normal execution.

The Rendering Subroutine (Lines 130–200)

The core technique uses the built-in POINT function to read individual pixels from the character set as printed in the lower portion of the screen. At line 130, a$ is printed invisibly at row 21 using PRINT AT 21,0;a$. The double loop at line 140 iterates over each pixel column (f, 0 to 8*a-1) and each pixel row (n, 0 to 7), sampling the character bitmap one pixel at a time.

When POINT (f,n) returns non-zero (a set pixel), three groups of DRAW commands construct the 3D appearance:

  • Line 160: Draws the front face of the pixel block — a filled square outline — and removes internal lines, using PI (≈3.14159, truncated to 3) as a shorthand integer constant in DRAW 0,-PI, DRAW PI,0, etc.
  • Line 170: Draws the top face using diagonal strokes (DRAW 5,5 and DRAW -5,-5) to simulate depth, plus vertical lines connecting front-face top edge to top-face top edge.
  • Line 180: Draws the right-side face using a diagonal start, a horizontal line, and a return diagonal.

Each pixel block is scaled 4× (f*4, n*4), and the vertical position is controlled by the parameter p (pixels from top, default 32 for the demo calls).

Use of PI as an Integer Constant

Several DRAW arguments use PI rather than the literal integer 3. Since DRAW truncates its arguments to integers, PI (3.14159…) evaluates to 3. This is a well-known Spectrum BASIC space-saving idiom: PI is a single token, whereas 3 is stored as a token plus a 5-byte floating-point number inline. This saves bytes in each occurrence where the value 3 is needed.

Machine Code Usage

A 1,300-byte machine code block is loaded from a separate tape file ("3dcharsetC") into address 64000. The entry point used at line 250 is RANDOMIZE USR 64009 (offset +9 into the block), which triggers a screen dump to the printer. The program’s own comments (lines 310–340) instruct users to replace this with COPY or their own printer interface command if not using the AERCO/Star SG-10 combination for which it was originally configured.

System POKEs

AddressSystem VariableValueEffect
23609BORDCR50Sets border/lower-screen color attributes
23658FLAGS28Sets caps lock on

Control Flow Anomalies

The subroutine starting at line 130 is used both as a proper GO SUB target (lines 20–40) and as a fall-through target from GO TO 120 (line 120 → 130). The RETURN statements at lines 210–230 only execute when the string matches one of the three demo strings; for user-entered strings, execution falls through to the input/copy prompts at lines 240–300. This dual-use pattern means the “subroutine” is not a clean subroutine for user input — it acts as a subroutine only during the demo phase.

Line 90 uses PAUSE NOT PI: NOT PI evaluates to 0 (since PI is non-zero, NOT returns 0), so PAUSE 0 waits indefinitely for a keypress — a standard idiom for halting until input.

Input Validation

At line 120, the entered string is validated: if its length exceeds 7 or is less than 1, a low beep (BEEP 1,-30) is sounded and input is requested again. The 7-character limit corresponds to the maximum width that can be rendered across the 256-pixel-wide screen at 4× scaling with 3D depth offsets without overflow.

Notable Techniques

  • Using PRINT AT 21,0;a$ to render the system character set into screen memory, then reading it back pixel-by-pixel with POINT — an elegant way to access font bitmaps without storing them separately.
  • VAL "280" and VAL "20" in PAUSE and GO TO at line 350 as a token-saving technique (the string "280" is shorter in tokenized form than the inline floating-point representation of 280).
  • Diagonal DRAW commands (DRAW 5,5) used to create isometric-style depth faces on each pixel block.

Source Code

   10 CLS : PRINT AT 10,2;"LOADING - 3D-LETTERS - CODE": LOAD "3dcharsetC"CODE 64000,1300: POKE 23609,50: POKE 23658,8: BORDER 2: INK 0: PAPER 6: CLS : GO TO 310
   20 LET a$="  3D": LET p=0: GO SUB 130
   30 LET a$="  -": LET p=30: GO SUB 130
   40 LET a$="LETTERS": LET p=60: GO SUB 130
   50 PRINT PAPER 1; INK 7;AT 13,0;"   NOW YOU CAN WRITE YOUR OWN             3D - LETTERS",
   60 PRINT '' PAPER 0; INK 7;"    FOLLOW THE INSTRUCTIONS",
   70 PRINT PAPER 4; INK 7;AT 19,17;"ALGIS E. GEDRIS"
   80 PRINT INK 7; PAPER 2;AT 21,9;"PRESS ANY KEY"
   90 PAUSE NOT PI
  100 PAPER 6: BORDER 2: INK NOT PI: CLS 
  110 INPUT "PIXELS FROM TOP I=32 PIXELS:";p
  120 INPUT "LETTERS (7 MAX) ";a$: IF LEN a$>7 OR LEN a$<1 THEN BEEP 1,-30: GO TO 120
  130 LET a=LEN a$: PRINT AT 21,0;a$
  140 FOR f=0 TO 8*a-1: FOR n=0 TO 7
  150 IF POINT (f,n)=0 THEN GO TO 190
  160 PLOT f*4,n*4+135-p: DRAW 4,0: DRAW 0,4: DRAW -4,0: DRAW 0,-PI: DRAW PI,0: DRAW 0,2: DRAW -2,0: DRAW 0,-1: DRAW 2,0: DRAW -2,-2
  170 DRAW 5,5: DRAW 0,4: DRAW 0,-4: DRAW 4,0: DRAW 0,4: DRAW 0,-4: DRAW -5,-5
  180 DRAW 0,4: DRAW 5,5: DRAW -4,0: DRAW -5,-5
  190 NEXT n: NEXT f
  200 PRINT AT 21,0;"        "
  210 IF a$="  3D" THEN RETURN 
  220 IF a$="  -" THEN RETURN 
  230 IF a$="LETTERS" THEN RETURN 
  240 INPUT "COPY OF THE SCREEN (Y/N) :";Z$
  250 IF Z$="Y" OR Z$="y" THEN RANDOMIZE USR 64009
  260 INPUT "WRITE SOME MORE ? (Y/N) ";w$
  270 IF w$="n" OR w$="N" THEN STOP 
  280 INPUT "CLEAR THE SCREEN ? (Y/N) ";c$
  290 IF c$="y" OR c$="Y" THEN CLS 
  300 GO TO 100
  310 PRINT AT 4,PI;"THIS ""3D-LETTERS"" PROGRAM"'TAB 5;"IS SPECIALLY MADE FOR"'TAB 8;"AERCO INTERFACE"'TAB 14;" AND "'TAB 7;"STAR SG-10 PRINTER"
  320 PRINT '"   REMOVE SECOND PART OF LINE        9998 SAVE ""3D-LETTERS""       CODE 64000,1300 AND REPLACE     WITH YOUR PRINTER INTERFACE"
  330 PRINT '" REPLACE ** RANDOMIZE USR 64009   WITH "" COPY "" OR EQUIVALENT"
  340 PRINT AT 17,15;"IN ";AT 18,12;" LINE 250"
  350 PAUSE VAL "280": CLS : GO TO VAL "20"
  360 SAVE "3Dcharset" LINE 10: SAVE "3dcharsetC"CODE 64000,1300
  370 PRINT #1,AT 0,8;"REWIND THE TAPE                 ****AND PLAY***": VERIFY "": VERIFY "3D-LETTERS"CODE 64000,1300

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.