Skip to content

mwenge/psychedelia.large

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

psychedelia.large

A subset of Psychedelia ported to Javascript and adapted for the big screen.

See also Psychedelia.js

Specifically this is an adaptation in Javascript of the version of Psychedelia that appeared as a type-in listing in 'Popular Computing Magazine' in December 1984:

Like all type-in listings of its day this consisted of a small BASIC program that loads the raw machine code into memory. The juice of the program therefore is not in the short human-readable preamble at the start of the listing:

10 REM *** A JEFF MINTER PRODUCTION ***
20 REM *** BASIC PROG. BY KEVIN BERGIN ***
30 REM *** ENTER THE FOLLOWING IN DIREC T MODE BEFORE YOU START!! ***
40 REM *** POKE 43,1:POKE 44,44:POKE 16 384,0:CLR:NEW ***
50 PRINT"HANG ON A SEC."
60 POKE53281 ,0:POKE53280,0:FORA=2049TO3457
70 READX: J=J+X:POKEA,X
80 NEXT: IFJ<>98035THEN180
90 PRINT" WONDERFUL COLOURS COMING..."
100 PRINT"&& BUT FIRST HAVE YOU SAVED THIS ?"
110 GETA$: IFA$<>"V"ANDA$<>"N"THEN110
120 IFA$="N"THENPRINT"SAVE IT NOW THEN": STOP
130 PRINT "TO SAVE THE CODE": PRINT"PRESS RUN/STOP & RESTORE"
140 PRINT"THEN SAVE AS NORMAL (THAT IS AFTER YOU EXIT THIS PROG"
150 POKE198,0:PRINT"PRESS A KEY TO START. ":WAIT198,1
160 PRINT"CLR: NEW": PRINT" S2PP43, 11P (744,8":PRINT"ERUN" 3 CHRS (19)
170 POKE631,13: POKE632,13: POKE633,13:POKE198,3:END
180 PRINT"DATA ERROR TRY AGAIN!"

This just performs the mundane task of reading in all of the numbers in the rest of the listing and, once done, executing those numbers as a machine code program. Rather what we're interested in is the assembly instructions that underlie that long list of numbers. For example:

snippet

To give you an idea of how we get from raw numbers to an assembly program in practice I'll show you how a little routine I call PaintPixel appears in the listing. Here it is:

410 DATA                165,2
420 DATA 41,128,208,249,165,2,201
430 DATA 40,16,243,165,3,41,128
440 DATA 208,237,165,3,201,24,16
450 DATA 231,32,92,64,177,5,41
460 DATA 7,162,0,221,84,64,240
470 DATA 5,232,224,8,208,246,138
480 DATA 133,253,166,4,232,228,253
490 DATA 240,3,16,1,96,166,4
500 DATA 189,84,64,145,5,96   

If you read left to right below, you can see the decimal values given in the listing translated to hexadecimal, then to the meaning of those hexadecimal values in 6502 assembly language.

Decimal                                    Pretty  Assembly
Data          Hex         Assembly         with labels
-------       --------    ------------     ------------------------------------------------
                                           PaintPixel                                       
165 2         a5 02       lda $02                  LDA pixelXPosition                               
41 128        29 80       and #$80                 AND #$80 ; Detect if has moved off left of screen
208 249       d0 f9       bne $089f                BNE ReturnEarly                                  
165 2         a5 02       lda $02                  LDA pixelXPosition                               
201 40        c9 28       cmp #$28                 CMP #NUM_COLS                                    
16 243        10 f3       bpl $089f                BPL ReturnEarly                                  
165 3         a5 03       lda $03                  LDA pixelYPosition                               
41 128        29 80       and #$80                 AND #$80 ; Detect if has moved off top of screen.
208 237       d0 ed       bne $089f                BNE ReturnEarly                                  
165 3         a5 03       lda $03                  LDA pixelYPosition                               
201 24        c9 18       cmp #$18                 CMP #NUM_ROWS                                    
16 231        10 e7       bpl $089f                BPL ReturnEarly                                  
32 145 8      20 91 08    jsr $0891                JSR LoadXAndYPosition                            
177 5         b1 05       lda ($05),y              LDA (currentLineForPixelInColorRamLoPtr),Y       
41 7          29 07       and #$07                 AND #COLOR_MAX                                   
162 0         a2 00       ldx #$00                 LDX #$00                                         
221 137 8     dd 89 08    cmp $0889,x      b408C   CMP presetColorValuesArray,X             
240 5         f0 05       beq $08cb                BEQ b4096                                        
232           e8          inx                      INX                                              
224 8         e0 08       cpx #$08                 CPX #COLOR_MAX + 1                               
208 246       d0 f6       bne $08c1                BNE b408C                                        
138           8a          txa              b4096   TXA                                      
133 253       85 fd       sta $fd                  STA indexOfCurrentColor                          
166 4         a6 04       ldx $04                  LDX colorIndexForCurrentPixel                    
232           e8          inx                      INX                                              
228 253       e4 fd       cpx $fd                  CPX indexOfCurrentColor                          
240 3         f0 03       beq $08d8                BEQ ActuallyPaintPixel                           
16 1          10 01       bpl $08d8                BPL ActuallyPaintPixel                           
96            60          rts                      RTS                                              
                                           ActuallyPaintPixel                               
166 4         a6 04       ldx $04                  LDX colorIndexForCurrentPixel                    
189 137 8     bd 89 08    lda $0889,x              LDA presetColorValuesArray,X                     
145 5         91 05       sta ($05),y              STA (currentLineForPixelInColorRamLoPtr),Y       
96            60          rts                      RTS                               

And this is what PaintPixel looks like when we port it from assembly to Javascript:

6502 Assembly Language                                 Javascript
;-----------------------                               -------------
PaintPixel                                             function paintPixel(pixelXPos, pixelYPos, colorIndexForCurrentPixel) {                              
  LDA pixelXPosition                                     if (pixelXPos < 0) {
  AND #$80 ; Detect if has moved off left of screen        return;
  BNE ReturnEarly                                        }

  LDA pixelXPosition                                     if (pixelXPos >= NUM_COLS) {
  CMP #NUM_COLS                                            return;
  BPL ReturnEarly                                        }

  LDA pixelYPosition                                     if (pixelYPos < 0) {
  AND #$80 ; Detect if has moved off top of screen.        return;
  BNE ReturnEarly                                        }

  LDA pixelYPosition                                     if (pixelYPos >= NUM_ROWS) {
  CMP #NUM_ROWS                                            return;
  BPL ReturnEarly                                        }
                                                                                                                                                          
  JSR LoadXAndYPosition                                  const x = (pixelYPos * NUM_COLS) + pixelXPos;

  ; Y now contains the pixelXPosition                    const currentColorForPixel = pixel_matrix[x] & COLOR_MAX;
  LDA (currentLineForPixelInColorRamLoPtr),Y              
  ; Make sure the color we get is addressable by         
  ; presetColorValuesArray.                               
  AND #COLOR_MAX                                         
                                                                                                                                                          
  LDX #$00                                               const indexOfCurrentColor = presetColorValuesArray.indexOf(currentColorForPixel);
b408C
  CMP presetColorValuesArray,X                                                                                                                           
  BEQ b4096                                              
  INX                                                    
  CPX #COLOR_MAX + 1                                     
  BNE b408C                                              
                                                         
b4096
  TXA                                                    let cx = colorIndexForCurrentPixel + 1;
  STA indexOfCurrentColor                                                                                                                                 
  LDX colorIndexForCurrentPixel                          if (cx < indexOfCurrentColor) {
  INX                                                      return;
  CPX indexOfCurrentColor                                }                                                                                                
  BEQ ActuallyPaintPixel                                                                                                                                 
  BPL ActuallyPaintPixel                                 
  RTS                                                    
                                                         
ActuallyPaintPixel                                             
  LDX colorIndexForCurrentPixel                          const newColor = presetColorValuesArray[colorIndexForCurrentPixel];
  LDA presetColorValuesArray,X                           
  STA (currentLineForPixelInColorRamLoPtr),Y             pixel_matrix[x] = newColor;
  RTS                                                    return;
                                                     }

The core of Psychedelia is contained in this routine and just two others: LoopThroughPatternAndPaint and PaintPixelForCurrentSymmetry. They are called in the following order:

-> `LoopThroughPatternAndPaint`
  -> `PaintPixelForCurrentSymmetry`
    -> `PaintPixel`

PaintPixelForCurrentSymmetry is the simplest of the three, it figures out how many copies of the current pattern to paint. For example, if the current symmetry setting is 'Quad' it will paint 4 copies of the pattern perpendicular to each other. Despite its simplicity it's verbose compared to the Javascript implementation. It gives you a real sense of the detail management required when writing assembly.

PaintPixelForCurrentSymmetry                        function PaintPixelForCurrentSymmetry(pixelXPosition, pixelYPosition, colorIndexForCurrentPixel) {
  ; First paint the normal pattern without any                                                                                                       
  ; symmetry.                                                                                                                                        
  LDA pixelXPosition                                                                                                                                 
  PHA                                                                                                                                                
  LDA pixelYPosition                                                                                                                                 
  PHA                                                                                                                                                
  JSR PaintPixel                                     paintPixel(pixelXPosition, pixelYPosition, colorIndexForCurrentPixel);                          
                                                                                                                                                     
  LDA currentSymmetrySettingForStep                                                                                                                  
  BNE HasSymmetry                                                                                                                                    
                                                                                                                                                     
CleanUpAndReturnFromSymmetry                         if (!currentSymmetrySettingForStep) {                                                           
  PLA                                                  return;                                                                                       
  STA pixelYPosition                                 }                                                                                               
  PLA                                                                                                                                                
  STA pixelXPosition                                                                                                                                 
  RTS                                                                                                                                                
                                                                                                                                                     
HasSymmetry                                                                                                                                                
  CMP #X_AXIS_SYMMETRY                                                                                                                               
  BEQ XAxisSymmetry                                                                                                                                  
                                                                                                                                                     
  ; Has a pattern to paint on the Y axis                                                                                                             
  ; symmetry so prepare for that.                                                                                                                    
  LDA #$27                                                                                                                                           
  SEC                                                                                                                                                
  SBC pixelXPosition                                                                                                                                 
  STA pixelXPosition                                                                                                                                 
                                                                                                                                                     
  ; If it has X_Y_SYMMETRY then we just                                                                                                              
  ; need to paint that, and we're done.                                                                                                              
  LDY currentSymmetrySettingForStep                                                                                                                  
  CPY #X_Y_SYMMETRY                                                                                                                                  
  BEQ XYSymmetry                                                                                                                                     
                                                                                                                                                     
  ; If we're here it's either Y_AXIS_SYMMETRY                                                                                                        
  ; or QUAD_SYMMETRY so we can paint a pattern        const symmPixelXPosition = NUM_COLS - pixelXPosition;                                          
  ; on the Y axis.                                    paintPixel(symmPixelXPosition, pixelYPosition, colorIndexForCurrentPixel);                     
  JSR PaintPixel                                                                                                                                     
                                                                                                                                                     
  ; If it's Y_AXIS_SYMMETRY we're done and can                                                                                                       
  ; return.                                                                                                                                          
  LDA currentSymmetrySettingForStep                   if (currentSymmetrySettingForStep == 0x01) {                                                   
  CMP #Y_AXIS_SYMMETRY                                  return;                                                                                      
  BEQ CleanUpAndReturnFromSymmetry                    }                                                                                              
                                                                                                                                                     
  ; Has QUAD_SYMMETRY so the remaining steps are                                                                                                     
  ; to paint two more: one on our X axis and one                                                                                                     
  ; on our Y axis.                                                                                                                                   
                                                                                                                                                     
  ; First we do the Y axis.                                                                                                                          
  LDA #$17                                                                                                                                           
  SEC                                                                                                                                                
  SBC pixelYPosition                                                                                                                                 
  STA pixelYPosition                                  const symmPixelYPosition = NUM_ROWS - pixelYPosition;                                          
  JSR PaintPixel                                      paintPixel(pixelXPosition, symmPixelYPosition, colorIndexForCurrentPixel);                     
                                                                                                                                                     
  ; Paint one on the X axis.                                                                                                                         
PaintXAxisPixelForSymmetry                                                                                                                                 
  PLA                                                                                                                                                
  TAY                                                                                                                                                
  PLA                                                                                                                                                
  STA pixelXPosition                                                                                                                                 
  TYA                                                                                                                                                
  PHA                                                                                                                                                
  JSR PaintPixel                                                                                                                                     
  PLA                                                                                                                                                
  STA pixelYPosition                                                                                                                                 
  RTS                                                                                                                                                
                                                                                                                                                     
XAxisSymmetry                                                                                                                                              
  LDA #$17                                                                                                                                           
  SEC                                                                                                                                                
  SBC pixelYPosition                                                                                                                                 
  STA pixelYPosition                                                                                                                                 
  JMP PaintXAxisPixelForSymmetry                                                                                                                     
                                                                                                                                                     
XYSymmetry                                          if (currentSymmetrySettingForStep == 0x02) {                                                  
  LDA #$17                                            return;                                                                                     
  SEC                                               }                                                                                             
  SBC pixelYPosition                                                                                                                                 
  STA pixelYPosition                                paintPixel(symmPixelXPosition, symmPixelYPosition, colorIndexForCurrentPixel);                
  JSR PaintPixel                                 }                                                                                               
  PLA                                                                                                                                                      
  STA pixelYPosition                                                                                                                                       
  PLA                                                                                                                                                      
  STA pixelXPosition                                                                                                                                       
  RTS                                                                                                                                                      

Take a look at the rest of the code.