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:
- Lines 1–140: REM header and machine code address map comments
- Lines 300–530: Initialization, mode selection menu, and input validation
- Lines 540–580: Option 4 (user-specific column) parameter input
- Lines 600–900: Screen load, dispatch, save, and exit
- Lines 1000–5010: Five conversion subroutines (one per mode)
- Lines 9000–9150: Machine code DATA and loader/verifier subroutine
- 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:
| Address | DATA line | Purpose |
|---|---|---|
| 57000 | 9000 | Video mode switcher (OUT 244, port handling) |
| 57040 | 9010 | Locate routine — converts (x,y) pixel coords to screen address |
| 57075 | 9020 | Clear display frame 2 (LDIR from 24576) |
| 57090 | 9030 | Copy DF3 to DF1 (LDIR) |
| 57105 | 9040 | Clear 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 65414–65415 and y into 65416–65417.
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
vto bothr+0andr+16/q+0andq+16— doubling into both halves. - Mode 2 (2000): Writes only to
randq(left half only). - Mode 3 (3000): Writes only to
r+16andq+16(right half only). - Mode 4 (4000): Writes to
r+bandq+b, wherebis 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 PIevaluates 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 9000before each DATA read ensures the DATA pointer is reset to the correct block for each checksum pass.- Line 4010 uses
STEP -SGN mwheremis uninitialized (defaults to 0), makingSGN m = 0and thereforeSTEP 0— this is effectively an infinite loop that processes onlyy=175. This appears to be a latent bug; the variablemwas 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.
Content
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.
