2015 Retrochallenge Winter Warmup, Retrochallenge

Retrochallenge 2015/01 – Post 04

Pieces Finally Coming Together

(This is part four of the chronicle of my Retrochallenge 2015/01 submission, which is to port the modern-day Apple II game, Structris, to the Atari 8-bit home computer using an obscure language called PL65. The mediocrity starts here.)

This week I added a lot of code that couldn’t easily be tested individually. It’s the code that derives which Tetris piece will be generated based on the profile underneath the player’s current position. For a long while, just part of a single Tetris piece would appear at the top of the playfield and smear down the screen.

A late Friday/early Saturday debugging session delivered magical results. One correction to a variable assignment made things go from “nothing works” to “everything works” (well, everything I’ve put in up to now).

From watching the video, several annoyances are immediately apparent:

  • The scroll routine is still unoptimized and the pieces shear as the pixels are moved down the screen
  • The player’s blip is barely visible. The scroll routine is taking too long and the other routines seem to be running very quickly. I’m following Martin’s original algorithm which has the player’s blip invisible during the scroll routine and visible during the “derive next piece” routines.

Mind If I Crash Here

Earlier in the week, it was looking very dark when my project was causing the Atari to immediately crash. I started commenting out blocks of code in an attempt to narrow down the culprit. Nothing I changed in the code would alleviate the error. At one point my wife was reminding me that I didn’t *have* to finish the project. And I was beginning to listen.

The whole time I was assuming something was wrong in my code, but it turned out something on the floppy disk image or with BeweDOS had gotten corrupted. I’m sure I’m to blame. I’d gotten into the habit of rebooting the emulator whenever I saw a compilation error appear. I realize now, this is probably a bad thing to do when the Atari is writing to the disk.

Code Dump

Here’s the current state of my source code. With the intent of keeping the feel of the original Apple II game and (selfishly) to help finish this in a short timeframe, many of the critical algorithms were lifted straight from Martin Haye’s Applesoft BASIC source code.

INCLUDE D:GRAPHICS.LIB
INCLUDE D:PEEKPOKE.LIB

! MEMORY MAP
BYTE DL[490]          = $8DA0
BYTE FRAME_TEXT[120]  = $9F86
BYTE FRAME_GR[5120]   = $A000

! GLOBALS
INT SCORE, LV, OX
INT ORIGIN
BYTE WELL, FLOOR
BYTE W, HEIGHT, MH, CX, CY, M, X, Y
BYTE H0, HL, HR, HB
BYTE RG, RC, RD
BYTE CI, BL, B0, BR, SL, SX, SR
BYTE H[22]

POINTER P1, P2, P3, P4
BYTE V1 BASED P1
BYTE V2 BASED P2
BYTE V3 BASED P3
BYTE V4 BASED P4
BYTE KEYCH  = $02FC

! SYMBOLS
CONST GRWIDTH = $20

CONST LMARGN = $52
CONST ROWCRS = $54
CONST COLCRS = $55
CONST SAVMSC = $58
CONST CRSINH = $02F0
CONST SDMCTL = $022F
CONST SDLIST = $0230
CONST GPRIOR = $026F
CONST PCOLR0 = $02C0
CONST PCOLR1 = $02C1
CONST PCOLR2 = $02C2
CONST PCOLR3 = $02C3
CONST COLOR0 = $02C4
CONST COLOR1 = $02C5
CONST COLOR2 = $02C6
CONST COLOR3 = $02C7
CONST COLOR4 = $02C8
!CONST KEYCH  = $02FC

! HARDWARE REGISTERS
CONST COLPM0 = $D012
CONST COLPM1 = $D013
CONST COLPM2 = $D014
CONST COLPM3 = $D015
CONST COLPF0 = $D016
CONST COLPF1 = $D017
CONST COLPF2 = $D018
CONST COLPF3 = $D019
CONST COLBK  = $D01A
CONST PRIOR  = $D01B
CONST WSYNC  = $D40A

! COLORS
CONST BLACK  = $00
CONST GRAY   = $08
CONST WHITE  = $0F
CONST YELLOW = $2C
CONST ORANGE = $36
CONST PURPLE = $54
CONST PINK   = $5C
CONST DKBLUE = $64
CONST LTBLUE = $8C
CONST BLUE   = $84
CONST GREEN  = $C6

!---------------------------------------
! DISPLAY LIST INTERRUPTS
!---------------------------------------
CONST DUMMY=@
@=$8B00
INTERRUPT DLINT1()
INT VDSLST = $0200
BEGIN
  STA WSYNC
  LDA $84     STA PRIOR
  LDA #ORANGE STA COLPF1
  LDA #YELLOW STA COLPF2
  LDA #GRAY   STA COLBK
  VDSLST = $8C00
END

@=$8C00
INTERRUPT DLINT2()
INT VDSLST = $0200
BEGIN
  STA WSYNC
  LDA #$00   STA PRIOR
  LDA #WHITE STA COLPF1
  LDA #BLACK STA COLPF2 STA COLBK
  VDSLST = $8B00
END
@=DUMMY

!---------------------------------------
! DISPLAY INTERRUPT ENABLE
!---------------------------------------
PROC DLI_ENABLE()
INT  VDSLST = $0200
BYTE NMIEN  = $D40E
BEGIN
  VDSLST = $8B00
  NMIEN  = $C0
END

!---------------------------------------
! DISPLAY INTERRUPT DISABLE
!---------------------------------------
PROC DLI_DISABLE()
BYTE NMIEN  = $D40E
BEGIN
  NMIEN  = $00
END

!---------------------------------------
! RND returns a random 16 bit number
!---------------------------------------
FUNC RND*()
BYTE RANDOM=$D20A
BEGIN
  LDA RANDOM
  LDY RANDOM
  JSR PUSH
END

!---------------------------------------
! RAND returns a random number
!      in the range 0 to range-1
!---------------------------------------
FUNC RAND(BYTE range)
BEGIN
END RND()/(65535/range)

!---------------------------------------
! SET COLORS
!---------------------------------------
PROC SET_COLORS()
BEGIN
  POKE(PCOLR0, BLACK)
  POKE(PCOLR1, PURPLE)
  POKE(PCOLR2, BLUE)
  POKE(PCOLR3, DKBLUE)
  POKE(COLOR0, PINK)
  POKE(COLOR1, ORANGE)
  POKE(COLOR2, YELLOW)
  POKE(COLOR3, GREEN)
  POKE(COLOR4, GRAY)
END

!---------------------------------------
! SET DISPLAY LIST
!---------------------------------------
PROC SET_DL()
INT  I, J, K, GR_ADR
BEGIN
  POKE(SDMCTL,$00)
  DLI_DISABLE()
  GR_ADR = .FRAME_GR

  ! 2x 8 BLANK SCANLINES 
  ! 1x 8 BLANK SCANLINES & DLI
  DL[0] = $70
  DL[1] = $70
  DL[2] = $F0

  ! 160 LINES OF MODE 10
  ! EACH SET OF 4 SCANLINES 
  ! POINT TO SAME MEMORY RANGE
  K = 3
  FOR I = 0 TO 39 DO
    FOR J = 0 TO 3 DO
      DL[K] = $4F
      DOKE(.DL+K+1, GR_ADR)
      K = K + 3
    NEXT
    GR_ADR  = GR_ADR + $20
  NEXT 

  ! OVERWRITE LAST MODE F ADD DLI
  ! TO ENABLE NORMAL TEXT AT BOTTOM
  DL[K-3] = $CF

  ! TEXT FRAME
  DL[K+0] = $42
  DOKE(.DL+K+1, .FRAME_TEXT)
  DL[K+3] = $02
  DL[K+4] = $02
  DL[K+5] = $02

  ! JVB
  DL[K+6] = $41
  DOKE(.DL+K+7, .DL)

  ! INSTALL NEW DISPLAY LIST
  ! 1. TURN OFF ANTIC
  ! 2. POINT TO NEW DISPLAY_LIST
  ! 3. TURN ON ANTIC
  POKE(SDMCTL,$00)
  DOKE(SDLIST,.DL)
  POKE(SDMCTL,$21)

  ! ENABLE GTIA MODE 10
  POKE(GPRIOR,$84)
  POKE(PRIOR,$84)

  DLI_ENABLE()
END

!---------------------------------------
! GET KEY
!---------------------------------------
FUNC GET_KEY()
BYTE KEY
BEGIN
  CLOSE(1)
  OPEN(1,4,0,"K:")
  KEY = GET(1)
  CLOSE(1)
END KEY

!---------------------------------------
! MYSTR
!---------------------------------------
FUNC MYSTR$(INT NUM)
STRING SBUFF$[16]
BYTE A,B
BYTE BASE
BEGIN
  BASE = 10
  SBUFF$[15] = "0"; A = 16
  REPEAT
    DEC A
    B = NUM MOD BASE + 48
    IF B >= 58 THEN
      B = B + 7
    ENDIF
    NUM = NUM/BASE
    P1 = .SBUFF+A
    V1 = B
  UNTIL NUM = 0
END SBUFF$[A]

!---------------------------------------
! SET LEVEL
!---------------------------------------
PROC SET_LEVEL(INT L)
BEGIN
  LV = L
  WELL = LV MOD 10
  IF WELL = 0 THEN  WELL = 10 ENDIF
  FLOOR = 39 - 2 * WELL
END

!---------------------------------------
! HOME
!---------------------------------------
PROC HOME()
BEGIN
  WRTLN(CHR$(125))
END

!---------------------------------------
! HELP SCREEN
!---------------------------------------
PROC HELP_SCREEN()
BYTE KEY
BEGIN
  SETCOLOR(2,0,0)
  POKE(CRSINH,$FF)
  POKE(LMARGN,0) CR()
  HOME()
  WRTLN("WELCOME TO STRUCTRIS!")
  WRTLN("BY MARTIN HAYE, INTRO'D KFEST 2010")
  CR() CR()
  WRTLN("(FANCY OPENING SCREEN GOES HERE)")
  CR() CR()
  WRTLN("KEYS:")
  WRTLN("  I: UP")
  WRTLN("  J: LEFT")
  WRTLN("  K: RIGHT (ALTERNATE: L)")
  WRTLN("  M: DOWN") CR()
  WRTLN("BUILD UP THE TETRIS BLOCKS. YOU CANNOT")
  WRTLN("ROTATE THEM. EVIL PROGRAMMER LAUNCHES")
  WRTLN("BLOCKS WHERE YOU ARE TO TRAP YOU. DON'T")
  WRTLN("GET TRAPPED. FINISHED ROWS FALL AWAY.")
  CR()
  WRTLN("CLEAR ENOUGH ROWS: NEXT LEVEL! HARDER!")
  CR()
  WRTLN("HIT A KEY TO BEGIN THE TORTURE.")
  KEY = GET_KEY()
  POKE(CRSINH,0)
END

!---------------------------------------
! INIT
!---------------------------------------
PROC INIT()
INT I
BEGIN
  SET_LEVEL(1)
  DLI_ENABLE()
  SET_COLORS()
  SET_DL()
END

!---------------------------------------
! CLEAR WELL
!---------------------------------------
PROC CLEAR_WELL()
INT I, J
BEGIN
  P1 = .FRAME_GR
  FOR I = 0 TO 31 DO
    FOR J = 0 TO FLOOR DO
      V1 = $00
      P1 = P1 + 1
    NEXT
  NEXT
END

!---------------------------------------
! LPLOT
!---------------------------------------
PROC LPLOT (BYTE X, Y, C)
BEGIN
  P1 = .FRAME_GR + $20 * Y + X
  V1 = C
END

!---------------------------------------
! SCRN
!---------------------------------------
FUNC SCRN (BYTE X, Y)
BEGIN
  P1 = .FRAME_GR + $20 * Y + X
END V1

!---------------------------------------
! HLIN - PLOT HORIZ LINE AT Y=0
!---------------------------------------
PROC HLIN (BYTE X1, X2, C)
BEGIN
  WHILE X1 <= X2 DO
    LPLOT(X1,1,C)
    INC X1
  ENDWHILE 
END

!---------------------------------------
! LPRINT
!---------------------------------------
PROC LPRINT(BYTE X, Y INT ADDR BYTE LEN)
BEGIN
  P1 = .FRAME_TEXT + $20 * Y + X
  P2 = ADDR
  WHILE LEN > 0 DO
    V1 = V2 - $20
    P1 = P1 + 1
    P2 = P2 + 1
    DEC LEN
  ENDWHILE
END

!---------------------------------------
! PRINT LEVEL
!---------------------------------------
PROC PRINT_LEVEL()
BYTE D, L
BEGIN
  LPRINT(12,0,"LEVEL:     ")

  L = 1
  IF LV >= 10 THEN L = 2 ENDIF
  IF LV >= 99 THEN L = 3 ENDIF

  D = LV MOD 10 + $10
  POKE(.FRAME_TEXT + 17 + L, D)

  IF LV >= 10 THEN
    D = LV /  10 + $10  
    POKE(.FRAME_TEXT + 17 + L - 1, D)
  ENDIF

  IF LV >= 100 THEN
    D = (LV MOD 100) / 10 + $10
    POKE(.FRAME_TEXT + 17 + L - 1, D)

    D = LV / 100 + $10
    POKE(.FRAME_TEXT + 17 + L - 2, D)
  ENDIF
END

!---------------------------------------
! DRAW WELL
!---------------------------------------
PROC DRAW_WELL()
BYTE X1, X2, Y1, Y2
INT I
BEGIN
  X1 = WELL
  X2 = 31 - WELL
  Y1 = 41 - 2 * WELL - 1
  Y2 = Y1 + 1

  CLEAR_WELL()

  ! DRAW WALLS
  FOR I = 0 TO Y2 + 1 DO
    LPLOT(X1, I, $88)
    LPLOT(X2, I, $88)
  NEXT

  ! DRAW BLACK LINES OVER PREV WALLS
  FOR I = 0 TO Y2 DO
    LPLOT(X1-1, I, $00)
    LPLOT(X2+1, I, $00)
  NEXT

  ! DRAW CHECKBOARD1
  FOR I = X1 + 1 TO X2 - 1 STEP 2 DO
    LPLOT(I, Y1, $88)
    LPLOT(I, Y2, $22)
  NEXT

  ! DRAW CHECKBOARD1
  FOR I = X1 + 2 TO X2 - 1 STEP 2 DO
    LPLOT(I, Y1, $22)
    LPLOT(I, Y2, $88)
  NEXT

  PRINT_LEVEL()
END

!---------------------------------------
! PRINT NUM CLEARED
!---------------------------------------
PROC PRINT_NUM_CLEARED()
BEGIN
  LPRINT (11, 2, "CLEARED ")
END

!---------------------------------------
! LEVEL INIT
!---------------------------------------
PROC LEVEL_INIT()
INT I
BEGIN
  ORIGIN = .FRAME_GR + 1249 - 63 * WELL
  W = 15 - LV
  HEIGHT = 20 - LV
  MH = HEIGHT - 1
  OX = (32 - W * 2) / 2
  M = 0; HB = 0
  H[0] = -9 H[W + 1] = -9
  FOR I = 1 TO W DO H[I] = 0 NEXT
  X = -99
  CX = OX + W; CY = 37 - LV + 1
  RG = 5 + LV * 2; RD = 0; RC = 0 
  DRAW_WELL()
  PRINT_NUM_CLEARED()
END

!---------------------------------------
! SCROLL
!---------------------------------------
PROC SCROLL()
INT A1, A2
BYTE H, F
BEGIN
  F = 0; H = 37 - LV
  A1 = ORIGIN
  A2 = A1 - 32
  P1 = A1; P2 = A2; P3 = A1 + 1; P4 = A2 + 1
  WHILE V1 <> $88 DO
    WHILE H > 0 DO
      IF F = 0 AND V1 = $00 THEN F = 1 ENDIF
      IF F = 1 THEN 
        V1 = V2 V3 = V4
        IF H = 1 THEN V2 = $00 V4 = $00 ENDIF
      ENDIF 
      P1 = A2 P3 = A2 + 1 A2 = A2 - 32 
      P2 = A2 P4 = A2 + 1
      DEC H
    ENDWHILE
    A1 = A1 + 2 A2 = A1 - 32
    P1 = A1 P2 = A2 P3 = A1 + 1 P4 = A2 + 1
    F = 0; H = 37 - LV
  ENDWHILE
END

!---------------------------------------
! KB PROC
!---------------------------------------
FUNC KB_PROC()
BYTE RC
BEGIN
  RC = 1
  LPLOT(CX,CY,$00)
  SCROLL()
  IF SCRN(CX,CY) = $00 THEN 
    CASE KEYCH
      OF $0D DO IF SCRN(CX, CY-1) = $00 AND CY > 1 THEN DEC CY ENDIF ENDOF
      OF $01 DO IF SCRN(CX-1, CY) = $00            THEN DEC CX ENDIF ENDOF
      OF $05 DO IF SCRN(CX, CY+1) = $00            THEN INC CY ENDIF ENDOF
      OF $44 DO IF SCRN(CX, CY+1) = $00            THEN INC CY ENDIF ENDOF
      OF $00 DO IF SCRN(CX+1, CY) = $00            THEN INC CX ENDIF ENDOF
    ENDCASE
    KEYCH = $FF
  ELSE
    IF SCRN(CX,CY+1) <> $00 THEN 
      RC = 0
    ENDIF 
    INC CY
  ENDIF
  LPLOT(CX,CY,$66)
END RC

!---------------------------------------
! PICK_X_COORD
!---------------------------------------
PROC PICK_X_COORD()
BYTE I
BEGIN
  IF X > 128 THEN X = (CX - OX) / 2 + 1 ENDIF
  IF X > 128 OR X < 1 THEN X = -9 RETURN ENDIF
  IF X > W THEN X = -9 RETURN ENDIF
  IF X > 1 AND H[X-1] < H[X] THEN DEC X
  ELSE
    IF X < W AND H[X+1] < H[X] THEN INC X ENDIF
  ENDIF
  IF H[X] - HB < MH THEN INC M ENDIF
END

!---------------------------------------
! CALC_SHAPE_CONSTRAINTS
!---------------------------------------
PROC CALC_SHAPE_CONSTRAINTS()
BEGIN
  H0 = H[X]
  HL = H[X-1] - H0
  HR = H[X+1] - H0
  IF HR > 3   THEN HR = 3 ENDIF
  IF HL > 3   THEN HL = 3 ENDIF
  INC M
END

!---------------------------------------
! APPLY CHOSEN COORD
!---------------------------------------
PROC APPLY_CHOSEN_COORD()
BEGIN
  H[X-1] = H[X-1] + BL
  H[X]   = H[X]   + B0
  H[X+1] = H[X+1] + BR
  SX = (X - 1) * 2 + OX
  IF BL = 0 THEN SL = 0 GOTO L745 ENDIF
  BL = BL * 3; SL = HL * 3
:L745
  IF BR = 0 THEN GOTO L755 ENDIF
  BR = BR * 3; SR = HR * 3
:L755
  B0 = B0 * 3 
  INC M
END

!---------------------------------------
! CHOOSE_SHAPE
!---------------------------------------
PROC CHOOSE_SHAPE()
BYTE I
BEGIN
  INC M
  I =  HL * 4 + HR
  CASE I
    OF 0 DO 
      CASE RAND(4) 
        OF 0 DO CI = $11 BL = 1 B0 = 1 BR = 2 ENDOF
        OF 1 DO CI = $22 BL = 2 B0 = 1 BR = 1 ENDOF
        OF 2 DO CI = $66 BL = 1 B0 = 2 BR = 1 ENDOF
        OF 3 DO CI = $77 BL = 1 B0 = 1 BR = 1 ENDOF
      ENDCASE
    ENDOF
    OF 1 DO     CI = $44 BL = 1 B0 = 2 BR = 1 ENDOF
    OF 2 DO     CI = $22 BL = 0 B0 = 3 BR = 1 ENDOF
    OF 3 DO
      CASE RAND(3)
        OF 0 DO CI = $11 BL = 3 B0 = 1 BR = 0 ENDOF
        OF 1 DO CI = $22 BL = 1 B0 = 3 BR = 0 ENDOF
        OF 2 DO CI = $33 BL = 2 B0 = 2 BR = 0 ENDOF
      ENDCASE
    ENDOF
    OF 4 DO     CI = $55 BL = 1 B0 = 2 BR = 1 ENDOF
    OF 5 DO     CI = $66 BL = 1 B0 = 2 BR = 1 ENDOF
    OF 6 DO
      CASE RAND(2)
        OF 0 DO CI = $44 BL = 2 B0 = 2 BR = 0 ENDOF
        OF 0 DO CI = $66 BL = 1 B0 = 3 BR = 0 ENDOF
      ENDCASE
    ENDOF
    OF 7 DO
      CASE RAND(2)
        OF 0 DO CI = $44 BL = 2 B0 = 2 BR = 0 ENDOF
        OF 0 DO CI = $66 BL = 1 B0 = 3 BR = 0 ENDOF
      ENDCASE
    ENDOF
    OF 8 DO     CI = $11 BL = 1 B0 = 3 BR = 0 ENDOF
    OF 9 DO     CI = $11 BL = 1 B0 = 3 BR = 0 ENDOF
    OF 10 DO
      CASE RAND(2)
        OF 0 DO CI = $11 BL = 1 B0 = 3 BR = 0 ENDOF
        OF 1 DO CI = $22 BL = 0 B0 = 3 BR = 1 ENDOF
      ENDCASE
    ENDOF
    OF 11 DO    CI = $11 BL = 1 B0 = 3 BR = 0 ENDOF
    OF 12 DO
      CASE RAND(3)
        OF 0 DO CI = $11 BL = 0 B0 = 3 BR = 1 ENDOF
        OF 1 DO CI = $22 BL = 0 B0 = 1 BR = 3 ENDOF 
        OF 2 DO CI = $33 BL = 0 B0 = 2 BR = 2 ENDOF
      ENDCASE
    ENDOF
    OF 13 DO
      CASE RAND(2)
        OF 0 DO CI = $55 BL = 0 B0 = 2 BR = 2 ENDOF
        OF 1 DO CI = $66 BL = 0 B0 = 3 BR = 1 ENDOF
      ENDCASE
    ENDOF
    OF 14 DO    CI = $22 BL = 0 B0 = 3 BR = 1 ENDOF
    OF 15 DO    CI = $77 BL = 0 B0 = 3 BR = 0 ENDOF
  ENDCASE
END

!---------------------------------------
! PLOT_A_LINE
!---------------------------------------
PROC PLOT_A_LINE()
BEGIN
  IF BL = 0 THEN GOTO L835 ENDIF
  IF SL > 0 AND SL < 128 THEN 
      DEC SL
      GOTO L835
  ENDIF
  DEC BL
  HLIN(SX-2,SX-1,CI)
  IF SL <> 0 THEN LPRINT (0,0,"WHOA!") ENDIF
:L835
  IF B0 = 0 THEN GOTO L850 ENDIF
  DEC B0
  HLIN(SX,SX+1,CI)
:L850
  IF BR = 0 THEN GOTO L870 ENDIF
  IF SR > 0 AND SR < 128 THEN
    DEC SR
    GOTO L870
  ENDIF
  DEC BR
  HLIN(SX+2,SX+3,CI)
:L870
  IF BL + B0 + BR = 0 THEN
    X = -99
    M = 0
  ENDIF
END

!---------------------------------------
! MYDEBUG
!---------------------------------------
PROC MYDEBUG()
BEGIN
  LPRINT(0,0,"CX:  ") LPRINT(3,0,MYSTR$(CX))
  LPRINT(0,1,"CY:  ") LPRINT(3,1,MYSTR$(CY))
  LPRINT(0,2,"OX:  ") LPRINT(3,2,MYSTR$(OX))
  LPRINT(0,3,"W:   ") LPRINT(3,3,MYSTR$(W))

  LPRINT(6,0,"X:  ") LPRINT(9,0,MYSTR$(X))
  LPRINT(6,1,"HL: ") LPRINT(9,1,MYSTR$(HL))
  LPRINT(6,2,"H0: ") LPRINT(9,2,MYSTR$(H0))
  LPRINT(6,3,"HR: ") LPRINT(9,3,MYSTR$(HR))

  LPRINT(12,0,"M:  ") LPRINT(15,0,MYSTR$(M))
  LPRINT(12,1,"BL:  ") LPRINT(15,1,MYSTR$(BL))
  LPRINT(12,2,"B0:  ") LPRINT(15,2,MYSTR$(B0))
  LPRINT(12,3,"BR:  ") LPRINT(15,3,MYSTR$(BR))

  LPRINT(17,0,"SL:  ") LPRINT(20,0,MYSTR$(SL))
  LPRINT(17,1,"SR:  ") LPRINT(20,1,MYSTR$(SR))
  LPRINT(17,2,"SX:  ") LPRINT(20,2,MYSTR$(SX))
END

!---------------------------------------
! GOTCHA
!---------------------------------------
FUNC GOTCHA()
BYTE KEY
BYTE R,N,Q
BEGIN
  KEY = 0
  KEYCH = $FF
  R = ASC("R")
  N = ASC("N")
  Q = ASC("Q")
  LPRINT(9,0,"    OOPS!     ")
  LPRINT(0,2,"R)ESTART, N)EW, OR Q)UIT?") 
  OPEN(1,4,0,"K:")
  WHILE KEY <> R AND KEY <> N AND KEY <> Q DO
    KEY = GET(1)
    LPRINT(26,2,.KEY,1)
  ENDWHILE
  CLOSE(1)
END KEY

!---------------------------------------
! BYE_NOW
!---------------------------------------
PROC BYE_NOW()
INT I
BEGIN
  I = 3000 
  WHILE I > 0 DO
    I = I - 1
  ENDWHILE
  GRAPHICS(0) HOME() WRTLN("BYE NOW.")
END

!---------------------------------------
! MAIN
!---------------------------------------
MAIN()
BYTE LOOP
BEGIN
  HELP_SCREEN() HOME()
:_init
  INIT() 
:_level_init
  LEVEL_INIT()
  LOOP = 1
  WHILE LOOP = 1 DO
    CASE M
      OF 0 DO PICK_X_COORD()            ENDOF
      OF 1 DO CALC_SHAPE_CONSTRAINTS()  ENDOF
      OF 2 DO CHOOSE_SHAPE()            ENDOF
      OF 3 DO APPLY_CHOSEN_COORD()      ENDOF
      OF 4 DO PLOT_A_LINE()             ENDOF
    ENDCASE
    LOOP = KB_PROC()
  ENDWHILE
  LOOP = GOTCHA()
  CASE LOOP
    OF ASC("R") DO GOTO _level_init ENDOF
    OF ASC("N") DO GOTO _init       ENDOF
    OF ASC("Q") DO BYE_NOW()        ENDOF
  ENDCASE
END

Up Next (Yikes! Less than a week left)

  1. Add row clearing
  2. Implement level advancement
  3. Re-write scrolling subroutine in 6502