PSGE 32/64

Developer(s): Stan Lemke
Date: 198x
Type: Program
Platform(s): TS 2068

PSGE 32/64 is a screen conversion utility that transforms a standard 32-column display into a 64-column high-resolution screen layout using machine code routines loaded from DATA statements. Five conversion modes are available: doubling the image across both halves, placing content on the left half only, the right half only, a user-specified column offset, or merging two separate 32-column screens side by side into a single 64-column result. Machine code is POKEd into memory starting at address 57000 and provides four subroutines: a video mode switcher, a pixel-coordinate locate function, and two display-frame copy routines. Each DATA block is checksum-verified after loading to detect transcription errors before any conversion is attempted.


Program Structure

The program is organized into clearly separated functional blocks:

  1. Lines 1–140: REM header and machine code address map comments
  2. Lines 300–530: Initialization, mode selection menu, and input validation
  3. Lines 540–580: Option 4 (user-specific column) parameter input
  4. Lines 600–900: Screen load, dispatch, save, and exit
  5. Lines 1000–5010: Five conversion subroutines (one per mode)
  6. Lines 9000–9150: Machine code DATA and loader/verifier subroutine
  7. Lines 9800–9998: Error handler and SAVE line

Machine Code Routines

Four machine code routines are loaded via GO SUB 9100 into addresses 57000–57116:

AddressDATA linePurpose
570009000Video mode switcher (OUT 244, port handling)
570409010Locate routine — converts (x,y) pixel coords to screen address
570759020Clear display frame 2 (LDIR from 24576)
570909030Copy DF3 to DF1 (LDIR)
571059040Clear display frame 3 (LDIR)

The locate routine stores results at fixed scratchpad addresses: the pixel screen address is read back from PEEK 65399/PEEK 65398 (low/high bytes) and the attribute address from PEEK 65375/PEEK 65374. Parameters are passed by POKEing x into 6541465415 and y into 6541665417.

Checksum Verification

Each DATA block is verified by summing all byte values and comparing to a hardcoded expected total. Subroutine 9100 performs five such checks (lines 9100–9140). On mismatch, the offending DATA line number is stored in l and execution branches to line 9800, which prints a diagnostic message and halts. This guards against transcription errors when entering the program by hand.

Conversion Logic

All five conversion modes share the same inner-loop structure. For each pixel row y (175 down to 0), the locate routine is called to find the screen and attribute addresses for the leftmost byte of that row. The loop then walks across 32-column positions (incrementing by 16 pixel units, stepping through 15 bytes with the loop guard IF x<238). Each source byte v is read, the source cell is blanked to 255 (all pixels white), and v is written to one or both destination offsets in the alternate display frames.

  • Mode 1 (1000): Writes v to both r+0 and r+16 / q+0 and q+16 — doubling into both halves.
  • Mode 2 (2000): Writes only to r and q (left half only).
  • Mode 3 (3000): Writes only to r+16 and q+16 (right half only).
  • Mode 4 (4000): Writes to r+b and q+b, where b is the user-specified column divided by 2.
  • Mode 5 (5000): Calls mode 2, pauses to allow a second tape load, then calls mode 3 — merging two distinct screens side by side.

The offsets +8192 (stored in q) and +33536 (stored in r) represent the two alternate display frame bases relative to the standard screen at 16384.

Key BASIC Idioms

  • NOT PI evaluates to 0 (since PI is nonzero, NOT PI = 0), used throughout as a compact zero literal in POKE arguments and loop bounds.
  • VAL "256* PEEK 65399+ PEEK 65398" forces expression evaluation in a string to reconstruct a 16-bit address from two POKEd bytes — a workaround for expression-length or precedence concerns.
  • GO TO (1000* VAL a$) at line 700 dispatches to the correct conversion subroutine based on the menu digit, avoiding a chain of IF tests.
  • RESTORE 9000 before each DATA read ensures the DATA pointer is reset to the correct block for each checksum pass.
  • Line 4010 uses STEP -SGN m where m is uninitialized (defaults to 0), making SGN m = 0 and therefore STEP 0 — this is effectively an infinite loop that processes only y=175. This appears to be a latent bug; the variable m was probably intended to hold a row count but was never assigned.

Save Strategy

Line 800 performs a two-part SAVE: first SAVE n$ SCREEN$ saves the standard screen area (6912 bytes from 16384), then SAVE n$ CODE 24576,6912 saves the converted 64-column screen data from address 24576. The user is warned via the lower display frame to prepare the tape before either SAVE executes.

Image Gallery

Source Code

    1 REM \{6}\{6}           \{20}\{1}PS/GE  APPEND\{20}\{0}\{6}\{6}\{6}     \{18}\{1}\*\{18}\{0}    by   S D Lemke\{6}\{6}\{6}     Lemke Software Development\{6}     2144 White Oak\{6}     Wichita, Ks    67207\{6}\{6}\{6}
   10 PRINT TAB 10;"\{18}\{1}WORKING\{18}\{0}"
  100 REM  57000 -- video mode
  110 REM  57040 -- locate
  120 REM  57075 -- clear DF 2
  130 REM  57090 -- DF3 -> DF1
  140 REM  57105 -- clear DF 3    
  300 GO SUB 9100:RANDOMIZE USR 57000:OUT 255,0:PAPER 1:BORDER 1:INK 7:CLS :RANDOMIZE USR 57075:RANDOMIZE USR 57105
  400 CLS 
  500 PRINT "        \{20}\{1}PS/GE   32->64\{20}\{0}"''"Use this program to  convert a  standard 32 column screen to a  64 (hi res) column screen."''" Press 1-5 to select an option."
  510 PRINT ''" 1) DOUBLE IMAGE (32 => 32 + 32) 2) LEFT ONLY"'" 3) RIGHT ONLY"'" 4) USER SPECIFIC"'" 5) LEFT + RIGHT (32 + 32 => 64)"
  520 PAUSE 0:LET a$= INKEY$:IF a$<"1" OR a$>"5" THEN BEEP 1,0:GO TO 520
  530 PRINT AT 8+ VAL a$,1; OVER 1; FLASH 1;" ":IF a$ <>"4" THEN GO TO 600
  540 INPUT "Specify Beginning Column ";b$:LET lb= LEN b$:IF lb=0 THEN BEEP 1,0:GO TO 540
  550 FOR i=1 TO lb:IF b$(i)<"0" OR b$(i)>"9" THEN BEEP 1,0:GO TO 540
  560 NEXT i
  570 LET b= VAL b$:IF b>32 THEN BEEP 1,0:GO TO 540
  580 LET b= INT (b/2)
  600 INPUT "Screen Name? ";n$:PRINT AT 14,10;"\{18}\{1}LOAD SCREEN\{18}\{0}":LOAD n$ SCREEN$ 
  700 GO SUB (1000* VAL a$)
  800 PRINT #0; NOT PI, NOT PI;"Prepare tape to SAVE your new 64Column SCREEN, Note: \{18}\{1}2 part SAVE\{18}\{0}\{20}\{0}":PAUSE 300:PRINT #0; AT NOT PI, NOT PI,,,,:PRINT #0; AT NOT PI, NOT PI;"   Press any KEY to CONTINUE":PAUSE NOT PI:INPUT "Screen Name? ";n$:OUT 255, NOT PI:SAVE n$ SCREEN$ :SAVE n$ CODE 24576,6912
  900 OUT 255,54:STOP 
 1000 REM    \{20}\{1}32 ->   32 + 32\{20}\{0}
 1010 FOR y=175 TO 0 STEP -1:LET x=0:POKE 65414,x:POKE 65415,0:POKE 65416,y:POKE 65417,0:RANDOMIZE USR 57040:LET p= VAL "256* PEEK 65399+ PEEK 65398":LET q=p+8192:LET r=p+33536:LET s= VAL "256* PEEK 65375+ PEEK 65374"
 1020 LET v= PEEK p:POKE p,255:POKE s,15:POKE r,v:POKE r+16,v:LET p=p+1:LET s=s+1:LET v= PEEK p:POKE p,255:POKE s,15:POKE q,v:POKE q+16,v:IF x<238 THEN LET x=x+16:LET p=p+1:LET s=s+1:LET r=r+1:LET q=q+1:GO TO 1020
 1030 NEXT y
 1040 OUT 255,54:RANDOMIZE USR 57090
 1050 RETURN 
 2000 REM    \{20}\{1}32 ->   32 + 00\{20}\{0}
 2010 FOR y=175 TO 0 STEP -1:LET x=0:POKE 65414,x:POKE 65415, NOT PI:POKE 65416,y:POKE 65417, NOT PI:RANDOMIZE USR 57040:LET p= VAL "256* PEEK 65399+ PEEK 65398":LET q=p+8192:LET r=p+33536:LET s= VAL "256* PEEK 65375+ PEEK 65374"
 2020 LET v= PEEK p:POKE p,255:POKE s,15:POKE r,v:LET p=p+1:LET s=s+1:LET v= PEEK p:POKE p,255:POKE s,15:POKE q,v:IF x<238 THEN LET x=x+16:LET p=p+1:LET s=s+1:LET r=r+1:LET q=q+1:GO TO 2020
 2030 NEXT y
 2040 OUT 255,54:RANDOMIZE USR 57090
 2050 RETURN 
 3000 REM    \{20}\{1}32 ->   00 + 32\{20}\{0}
 3010 FOR y=175 TO 0 STEP -1:LET x= NOT PI:POKE 65414,x:POKE 65415, NOT PI:POKE 65416,y:POKE 65417, NOT PI:RANDOMIZE USR 57040:LET p= VAL "256* PEEK 65399+ PEEK 65398":LET q=p+8192:LET r=p+33536:LET s= VAL "256* PEEK 65375+ PEEK 65374"
 3020 LET v= PEEK p:POKE p,255:POKE s,15:POKE r+16,v:LET p=p+1:LET s=s+1:LET v= PEEK p:POKE p,255:POKE s,15:POKE q+16,v:IF x<238 THEN LET x=x+16:LET p=p+1:LET s=s+1:LET r=r+1:LET q=q+1:GO TO 3020
 3030 NEXT y
 3040 OUT 255,54:RANDOMIZE USR 57090
 3050 RETURN 
 4000 REM    \{20}\{1}32 -> SPECIFIC\{20}\{0}
 4010 FOR y=175 TO NOT PI STEP - SGN m:LET x= NOT PI:POKE 65414,x:POKE 65415, NOT PI:POKE 65416,y:POKE 65417, NOT PI:RANDOMIZE USR 57040:LET p= VAL "256* PEEK 65399+ PEEK 65398":LET q=p+8192:LET r=p+33536:LET s= VAL "256* PEEK 65375+ PEEK 65374"
 4020 LET v= PEEK p:POKE p,255:POKE s,15:POKE r+b,v:LET p=p+1:LET s=s+1:LET v= PEEK p:POKE p,255:POKE s,15:POKE q+b,v:IF x<238 THEN LET x=x+16:LET p=p+1:LET s=s+1:LET r=r+1:LET q=q+1:GO TO 4020
 4030 NEXT y
 4040 OUT 255,54:RANDOMIZE USR 57090
 4050 RETURN 
 5000 REM    \{20}\{1}32 + 32 => 64\{20}\{0}
 5010 GO SUB 2000:PAUSE 60:OUT 255,0:CLS :PRINT AT 14,10;"\{18}\{1}LOAD SCREEN 2\{18}\{0}":INPUT "Screen Name? ";n$:LOAD n$ SCREEN$ :GO SUB 3000:RETURN 
 8999 STOP 
 9000 DATA 46,0,62,1,211,244,219,255,203,255,211,255,62,6,245,251,205,142,14,0,219,255,203,191,211,255,175,211,244,241,254,128,32,4,50,91,104,251,201
 9010 DATA 33,134,255,78,33,136,255,70,205,3,38,34,118,255,124,198,32,103,34,96,255,124,15,15,15,230,3,246,88,103,34,94,255,201
 9020 DATA 17,0,96,33,0,64,1,0,27,237,176,201
 9030 DATA 17,0,64,33,0,195,1,0,27,237,176,201
 9040 DATA 17,0,195,33,0,64,1,0,27,237,176,201
 9100 LET sum=0:RESTORE 9000:FOR i=57000 TO 57038:READ a:POKE i,a:LET sum=sum+a:NEXT i:IF sum <>6207 THEN LET l=9000:GO TO 9800
 9110 LET sum=0:RESTORE 9010:FOR i=57040 TO 57073:READ a:POKE i,a:LET sum=sum+a:NEXT i:IF sum <>3912 THEN LET l=9010:GO TO 9800
 9120 LET sum=0:RESTORE 9020:FOR i=57075 TO 57086:READ a:POKE i,a:LET sum=sum+a:NEXT i:IF sum <>852 THEN LET l=9020:GO TO 9800
 9130 LET sum=0:RESTORE 9030:FOR i=57090 TO 57101:READ a:POKE i,a:LET sum=sum+a:NEXT i:IF sum <>951 THEN LET l=9030:GO TO 9800
 9140 LET sum=0:RESTORE 9040:FOR i=57105 TO 57116:READ a:POKE i,a:LET sum=sum+a:NEXT i:IF sum <>951 THEN LET l=9040:GO TO 9800
 9150 RETURN 
 9800 CLS :PRINT "You have an error in line ";l:BEEP 1,1:STOP 
 9997 STOP 
 9998 SAVE "PSGE 32/64" LINE 1

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