Perimipics is a drawing program that lets the user move a cursor around a bordered canvas using keyboard keys 5–8 or a joystick, placing or removing filled block-graphic tiles to compose a picture. When the drawing is complete, the program converts the 22×24-cell canvas into nine user-defined graphics (UDGs A through I), each storing a portion of the image as POKEd character data at addresses 65368–65416. A companion subroutine at line 9987 reassembles and displays the nine UDGs as a tiled image on screen, and the instructions explain how to preserve or merge the UDG data with other programs using NEW, DELETE, or MERGE commands.
Entry for the SyncWare News programming contest.
Program Analysis
Program Structure
The program divides into four logical phases:
- Initialization (lines 1–30): Builds a 528-character string
A$representing the canvas, displays instructions, and waits for a keypress. - Drawing loop (lines 40–190): Draws a green border, then enters a real-time loop reading input (joystick via machine code or keyboard), updating cursor position, and placing or removing tiles in
A$. - UDG generation (lines 191–313): Iterates over the canvas string and POKEs computed byte values into the nine UDG slots, then prints post-processing instructions and stops.
- Display subroutine (lines 9986–9999): Reassembles the nine UDGs into a full-screen picture using tiled PRINT AT statements.
Canvas Representation
The canvas is encoded as a 528-character string A$ (lines 10–17). Line 10 creates a 32-character space string; line 15 multiplies it ninefold to 288 characters, and line 17 doubles it again. The result is a flat array of 528 characters indexed by a single position counter SC, where each character is either a space (empty) or the block graphic \:: (filled). The canvas is 24 columns wide and 22 rows tall (24×22 = 528).
Cursor Movement and Input Handling
The main loop uses a dual input path. If no key is pressed (line 60), it calls a machine code routine at address 26720 via RANDOMIZE USR 26720 (line 61), which reads joystick state and returns delta values via PEEKs at addresses starting at X (26715). If a key is pressed, line 70 reads the keyboard scan code from system variable address 23560 into P, and line 80 updates the scalar cursor position SC using Boolean arithmetic: (P=56) moves right, (P=54) moves up by a row (adds 24), (P=53) moves left, and (P=55) moves down.
The joystick path (lines 61–62) extracts directional and fire information from four consecutive PEEKed bytes relative to address X, accumulating movement into SC and storing the fire state in W. Lines 90–100 clamp SC to the range 0–527 to keep the cursor within bounds.
Tile Placement
The cursor is displayed by printing "*" at the computed row and column (line 140), then immediately overprinted with the current canvas cell content from A$ (line 150), giving a flicker effect. Line 160 sets the canvas cell to a filled block graphic \:: if joystick fire (W=1) or the "1" key is pressed. Line 170 clears the cell on "0". The brief PAUSE 3 delays on lines 140 and 150 control the cursor blink rate.
UDG Generation
Lines 205–300 convert the canvas into nine UDGs (A through I). The outer loop steps through UDG memory addresses 65368 to 65416 in steps of 24 (8 bytes × 3 columns per UDG). For each UDG, nested loops iterate over 8 rows (C) and 3 byte-columns (R). The innermost loop (lines 240–280) reads eight consecutive canvas cells and assembles a byte by treating each filled cell as a 1-bit, summing M * 2^H where M is 1 if the cell equals \::. The assembled byte is POKEd into the appropriate UDG address at line 290.
| UDG | Canvas columns | Canvas rows |
|---|---|---|
| A | 0–7 | 0–7 |
| B | 8–15 | 0–7 |
| C | 16–23 | 0–7 |
| D | 0–7 | 8–15 |
| E | 8–15 | 8–15 |
| F | 16–23 | 8–15 |
| G | 0–7 | 16–21 (+pad) |
| H | 8–15 | 16–21 (+pad) |
| I | 16–23 | 16–21 (+pad) |
Display Subroutine (lines 9987–9999)
The subroutine at line 9987 tiles the nine UDGs across the screen. Lines 9988–9991 print the left and right border columns (C=0 and C=29) for each set of three rows. Lines 9992–9998 handle the top and bottom rows: the first pass covers columns 3–12, the second (triggered when M≠17) covers columns 17–26, filling the interior of the screen with the assembled UDG picture. The inner subroutine at line 9999 prints a 3×3 block of UDGs A–I using a single PRINT AT statement with three lines.
Notable Techniques
- String
A$used as a flat 2D canvas array, indexed by a single scalarSCdecomposed into row/column with integer division. - Boolean expressions cast to integers (0 or 1) for arithmetic updates to
SC, avoiding IF branches in the movement logic. - Machine code joystick driver called via
RANDOMIZE USR, with results communicated back through PEEKed memory locations. - Bit-packing loop using powers of two (
2^H) to serialize canvas cells into UDG bytes without bitwise operators. - The program is designed to be partially preserved after completion: the UDG data and display subroutine survive a
NEWcommand and can be merged into user programs.
Potential Anomalies
The INK 4: PLOT 0,0: DRAW 192,0: DRAW 0,175: INK 0 sequence at line 40 draws only two sides of the border (bottom and right), relying on the screen edge for the other two sides rather than completing a full rectangle. Also, PAUSE 4E4 at line 30 uses floating-point scientific notation for the pause duration, which is an unusual but valid idiom to express a large frame count without typing all digits.
Content
Source Code
1 REM 
