--- title: "PSG Register" id: 56311 type: "computer_media" slug: "psg-register" url: "http://localhost/computer_media/psg-register/" markdown_url: "http://localhost/computer_media/psg-register.md" published_at: "2024-08-04T10:49:54+00:00" modified_at: "2026-03-30T21:44:50+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/08/SCR-20240804-kasg.png" excerpt: "An interactive PSG register editor that reads, displays, and writes all 15 sound chip registers live — showing binary and decimal values simultaneously." 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: "Sound" slug: "sound" taxonomy: "genre" url: "http://localhost/type/sound/" media_contents: - id: 56277 title: "Timex Sinclair Public Domain Library Tape 2001" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-2001/" - id: 60550 title: "Fort Worth TS2068 Club Library Tape" type: "computer_media" url: "http://localhost/computer_media/fort-worth-ts2068-club-library-tape/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/PSG%20Register%20%28198x%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/08/SCR-20240804-kasg.png" media_type_tags: "Sound" --- # PSG Register This program provides an interactive display and editor for the AY-3-8912 Programmable Sound Generator (PSG) chip registers. It uses OUT 245 and IN 246 to write to and read from the PSG’s address and data ports respectively, showing all 15 registers (0–14) with their human-readable function names, current binary representations, and decimal values on a color-formatted screen. The binary display routine works by successively subtracting powers of two from the register value, implementing a manual bit-extraction loop without relying on bitwise operators. Users can select any register by number and enter a new value, with range validation (0–14 for registers, 0–255 for values) enforced by BEEP error tones and re-prompting loops. *** ## Program Analysis ### Program Structure The program is divided into clearly labelled sections separated by REM blocks. Execution flows through an initialisation subroutine, then enters a main input loop: 1. **Lines 30:** Calls the screen setup subroutine at line 500. 2. **Lines 100–150:** Main interactive loop — prompts for a register number and a value, writes to the PSG, refreshes the display, and repeats. 3. **Lines 300–350:** Subroutine that reads a register’s current value via `IN 246` and prints its 8-bit binary representation followed by its decimal value. 4. **Lines 500–540:** Screen initialisation — sets colours, prints column headers, iterates through all 15 registers reading their names from DATA and displaying their initial values. 5. **Lines 610–620:** DATA statements providing human-readable names for each PSG register. ### PSG Hardware Interface The AY-3-8912 PSG in the TS2068 is accessed via two I/O ports. Port 245 selects the register address, and port 246 reads or writes the register data. The program uses `OUT 245,r` to latch a register number, then `OUT 246,v` to write a value or `IN 246` to read the current contents. This is the standard two-step address/data protocol required by the AY chip. ### Register Display All 15 registers (0–14) are displayed in a fixed-position table on screen. The `AT r+4,12` addressing places each register’s information on a row offset by 4 from the top, corresponding to row positions 4 through 18. Three columns are maintained: the register number at column 12, the binary value at column 17, and the decimal value following it. ### Binary Conversion Routine (Lines 310–350) The binary display does not use bitwise operations (unavailable in Sinclair BASIC). Instead it implements a manual base-2 decomposition using successive subtraction. `power` starts at 128 and is halved each iteration. For each bit position, the current value of `mod` is tested by subtracting `power`; a negative result means the bit is 0 (restore `mod`), otherwise the bit is 1. This is a classic BASIC technique for extracting individual bits: - `power` = 128, 64, 32, 16, 8, 4, 2, 1 across 8 loop iterations (`x` = 0 to 7) - A “0” or “1” character is PRINTed directly for each bit, producing the binary string left-to-right (MSB first) - After the loop, `IN 246` is called again to print the decimal value — note the register address is still latched from line 310 ### Input Validation Both input prompts include range checks with audible error feedback. Register numbers outside 0–14 trigger `BEEP 1,10` and loop back to line 100. Values outside 0–255 trigger `BEEP 1,5` and return to line 110 (re-prompting for the value only, not the register). The differing BEEP pitches give the user an audible distinction between the two error types. ### Screen Layout The screen uses `PAPER 1` (blue), `BORDER 1`, and `INK 6` (yellow) for a coloured display. The header row at line 2 uses `INVERSE 1` for the column title bar. The currently-selected register is highlighted using `FLASH 1` during value entry (line 110) and reverted to normal after (line 140). ### Register Names (DATA) | Register | Name | | --- | --- | | 0 | Ch.A Fine | | 1 | Coarse | | 2 | Ch.B Fine | | 3 | Coarse | | 4 | Ch.C Fine | | 5 | Coarse | | 6 | Noise | | 7 | Enable | | 8 | Ch.A Ampl | | 9 | Ch.B Ampl | | 10 | Ch.C Ampl | | 11 | Env.Fine | | 12 | Coarse | | 13 | Env.Shape | | 14 | Port | ### Anomalies and Notes - Line 201 contains a stray `REM Print contents` that is never reached; execution jumps directly to line 300 via `GO SUB 300`. It appears to be a leftover label comment. - In the initialisation loop (line 530), `LET r=y` is used before calling `GO SUB 300` so that the subroutine has the correct register number in `r`. This reuses the global variable `r` which is also the main loop’s register variable — a minor coupling that works correctly here since setup runs before any user input. - The decimal value is read with a second `IN 246` call at line 350 rather than storing the value already read into `mod` at line 310. This is functionally correct since the register address remains latched, but reads the hardware twice unnecessarily. - The title “PSG REGISTRS” on line 520 is truncated (missing a second ‘E’), apparently due to column width constraints. ## Source Code ``` 10 REM PSG REGISTER PROGRAM 11 REM 20 REM Set up 21 REM 30 GO SUB 500 40 REM 41 REM Loop 42 REM 100 INPUT "Register?";r: IF r<0 OR r>14 THEN BEEP 1,10: GO TO 100 110 PRINT AT r+4,12; FLASH 1;r 120 INPUT "Value?";v: IF v<0 OR v>255 THEN BEEP 1,5: GO TO 110 130 OUT 245,r: OUT 246,v: GO SUB 300 140 PRINT AT r+4,12;r 150 GO TO 100 201 REM Print contents 300 REM 302 REM 310 OUT 245,r: LET mod=IN 246: LET power=128: PRINT AT r+4,17; 320 FOR x=0 TO 7: LET mod=mod-power: IF mod<0 THEN PRINT "0";: LET mod=mod+power: GO TO 340 330 PRINT "1"; 340 LET power=power/2: NEXT x 350 PRINT " ";IN 246;" ": RETURN 500 REM 501 REM Print screen 502 REM 510 PAPER 1: BORDER 1: INK 6: CLS 520 PRINT TAB 2;"PSG REGISTRS";TAB 20;"CONTENTS": PRINT AT 2,0; INVERSE 1;"Function No.";TAB 18;"Binary Dec. ": PRINT 530 FOR y=0 TO 14: READ a$: PRINT a$;AT y+4,12;y: LET r=y: GO SUB 300: NEXT y 540 RETURN 600 REM 601 REM String data 602 REM 610 DATA "Ch.A Fine"," Coarse","Ch.B Fine"," Coarse","Ch.C Fine"," Coarse","Noise" 620 DATA "Enable","Ch.A Ampl","Ch.B Ampl","Ch.C Ampl","Env.Fine"," Coarse","Env.Shape","Port" ```