PSG Register

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Sound

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)

RegisterName
0Ch.A Fine
1 Coarse
2Ch.B Fine
3 Coarse
4Ch.C Fine
5 Coarse
6Noise
7Enable
8Ch.A Ampl
9Ch.B Ampl
10Ch.C Ampl
11Env.Fine
12 Coarse
13Env.Shape
14Port

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.

Content

Appears On

One of the largest single-tape collections anywhere, with over 40 programs spanning flight planning, satellite tracking, hydrology, Forth programming, a 17-game mega-pack, and a complete calligraphic font renderer. A snapshot of a thriving Texas user group at its peak.
The foundation of Tony Willing's numbered library series — play Breakout, write documents in a 64-column word processor, morph a triangle into a square with flicker-free animation, or run a complete fig-FORTH system. Fifty programs covering every category.

Related Products

Related Articles

Related Content

Image Gallery

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"

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

People

No people associated with this content.

Scroll to Top