;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Penny Arcade, by Bill Budge ;
; Copyright 1979 Apple Computer, Inc. ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The user interface is written in Applesoft BASIC. The machine code is ;
; embedded in the BASIC program. It appears to have been designed to work on ;
; a machine with as little as 16KB of RAM. ;
; ;
; Collision detection is performed on hi-res pixels. The data structures that ;
; hold the game fields are only used to draw it before the game starts. ;
; Collisions with the paddles are handled specially, because the ball's bounce ;
; angle depends on where on the paddle the ball hit. ;
; ;
; The game allows you to set a difficulty from 1 to 10. This affects how ;
; often the paddle position is updated. ;
; ;
; Paddles are 3 pixels wide and 24 pixels high. ;
; ;
; Hitting ESC has the same effect as the ball going out of bounds. This can ;
; be used if the ball gets stuck. ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Disassembly by Andy McFadden, using 6502bench SourceGen v1.9.1. ;
; Last updated 2025/01/25 ;
; ;
; The binary was extracted with CiderPress II from a recording of an audio ;
; cassette. ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
line_ptr .eq $00 {addr/2} ;pointer to hi-res line
byte_offset .eq $02 ;offset into the hi-res line (0-39)
pixel_mask .eq $03 ;pixel bit mask
draw_flag .eq $04 ;0=erase, nonzero=draw
coll_flag .eq $05
coll_ctr .eq $07 ;collision counter
hptr .eq $1a {addr/2}
delta_x .eq $3e
delta_y .eq $3f
pdl_drw_ctr .eq $40 ;paddle draw counter
left_pdl_new_y .eq $41 ;target position of left paddle
right_pdl_new_y .eq $42 ;target position of right paddle
left_pdl_y .eq $43 ;current position of left paddle (top)
right_pdl_y .eq $44 ;current position of right paddle (top)
gravity_ctr .eq $d0 ;gravity counter
line_st_x .eq $d1
line_st_y .eq $d2
line_len .eq $d3
line_xc .eq $d4
line_yc .eq $d5
ball_xc .eq $e0 ;X coordinate of ball (~0-255)
ball_yc .eq $e1 ;Y coordinate of ball (~0-159)
new_ball_xc .eq $e2 ;new X coordinate of ball
new_ball_yc .eq $e3 ;new Y coordinate of ball
ball_xvel .eq $e4 ;ball X velocity
ball_yvel .eq $e5 ;ball Y velocity
ball_x_frac .eq $e6 ;ball fractional X position
ball_y_frac .eq $e7 ;ball fractional Y position
snd_pitch .eq $e8
snd_dur .eq $e9
pdl_mv_ctr .eq $ea ;paddle movement counter
KBD .eq $c000 ;R last key pressed + 128
KBDSTRB .eq $c010 ;RW keyboard strobe
SPKR .eq $c030 ;RW toggle speaker
TXTCLR .eq $c050 ;RW display graphics
MIXSET .eq $c053 ;RW display split screen
HIRES .eq $c057 ;RW display hi-res graphics
MON_PREAD .eq $fb1e ;read paddle specifed by X-reg, return in Y-reg
;
; Applesoft BASIC program. It starts with this:
;
; 1020 POKE 175,32: POKE 176,19: POKE 105,32: POKE 106,19
; 1030 POKE 107,32: POKE 108,19: POKE 109,32: POKE 110,19
;
; This sets PRGEND ($af-b0), VARTAB ($69-6a), ARYTAB ($6b-6c), and STREND ($6d-
; 6e) to $1320.
;
; The BASIC code POKEs and CALLs various locations in the assembly code. This
; is documented separately.
;
.addrs $0801
0801: 07 08 64 00+ .bulk $07,$08,$64,$00,$b2,$00,$1d,$08,$6e,$00,$b2,$20,$20,$20,$20,$50
+ $45,$4e,$4e,$59,$20,$41,$52,$43,$41,$44,$45,$00,$23,$08,$78,$00
+ $b2,$00,$40,$08,$82,$00,$b2,$20,$20,$20,$20,$20,$20,$20,$20,$20
+ $20,$42,$59,$20,$42,$49,$4c,$4c,$20,$42,$55,$44,$47,$45,$00,$46
+ $08,$8c,$00,$b2,$00,$6a,$08,$fc,$03,$b9,$31,$37,$35,$2c,$33,$32
+ $3a,$b9,$31,$37,$36,$2c,$31,$39,$3a,$b9,$31,$30,$35,$2c,$33,$32
+ $3a,$b9,$31,$30,$36,$2c,$31,$39,$00,$8e,$08,$06,$04,$b9,$31,$30
+ $37,$2c,$33,$32,$3a,$b9,$31,$30,$38,$2c,$31,$39,$3a,$b9,$31,$30
+ $39,$2c,$33,$32,$3a,$b9,$31,$31,$30,$2c,$31,$39,$00,$9e,$08,$10
+ $04,$b9,$c9,$31,$36,$32,$39,$38,$2c,$30,$3a,$97,$00,$bb,$08,$1a
+ $04,$b9,$32,$31,$36,$2c,$30,$3a,$b2,$20,$20,$4f,$46,$46,$20,$54
+ $48,$45,$20,$4f,$4e,$20,$45,$52,$52,$00,$f2,$08,$4c,$04,$89,$3a
+ $8c,$c9,$39,$33,$36,$3a,$ba,$3a,$ba,$3a,$ba,$3a,$ba,$3a,$ba,$3a
+ $ba,$3a,$ba,$22,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
+ $20,$50,$45,$4e,$4e,$59,$20,$41,$52,$43,$41,$44,$45,$22,$3a,$ba
+ $00,$32,$09,$56,$04,$ba,$22,$20,$20,$20,$20,$20,$20,$20,$20,$20
+ $20,$20,$20,$20,$20,$20,$20,$20,$20,$42,$59,$22,$3a,$ba,$3a,$ba
+ $22,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$42
+ $49,$4c,$4c,$20,$42,$55,$44,$47,$45,$22,$3a,$ba,$3a,$ba,$3a,$ba
+ $00,$5f,$09,$60,$04,$ba,$22,$20,$20,$43,$4f,$50,$59,$52,$49,$47
+ $48,$54,$20,$31,$39,$37,$39,$20,$20,$41,$50,$50,$4c,$45,$20,$43
+ $4f,$4d,$50,$55,$54,$45,$52,$20,$49,$4e,$43,$2e,$22,$00,$a4,$09
+ $6a,$04,$ba,$3a,$a2,$32,$33,$3a,$ba,$22,$20,$20,$20,$20,$20,$50
+ $52,$45,$53,$53,$20,$27,$52,$45,$54,$55,$52,$4e,$27,$20,$54,$4f
+ $20,$43,$4f,$4e,$54,$49,$4e,$55,$45,$2e,$2e,$2e,$22,$3b,$3a,$be
+ $41,$24,$3a,$ad,$e6,$28,$41,$24,$29,$d1,$cf,$31,$33,$c4,$31,$31
+ $33,$30,$00,$aa,$09,$74,$04,$97,$00,$cd,$09,$7e,$04,$a2,$33,$3a
+ $96,$31,$31,$3a,$ba,$22,$2a,$2a,$2a,$20,$49,$4e,$53,$54,$52,$55
+ $43,$54,$49,$4f,$4e,$53,$20,$2a,$2a,$2a,$22,$00,$d3,$09,$88,$04
+ $ba,$00,$04,$0a,$d0,$07,$ba,$3a,$ba,$22,$20,$54,$48,$45,$20,$4f
+ $42,$4a,$45,$43,$54,$20,$4f,$46,$20,$54,$48,$45,$20,$47,$41,$4d
+ $45,$20,$49,$53,$20,$54,$4f,$20,$47,$45,$54,$20,$54,$48,$45,$22
+ $3a,$ba,$00,$60,$0a,$da,$07,$ba,$22,$20,$42,$41,$4c,$4c,$20,$49
+ $4e,$54,$4f,$20,$54,$48,$45,$20,$4f,$50,$50,$4f,$4e,$45,$4e,$54
+ $53,$20,$47,$4f,$41,$4c,$2e,$20,$44,$4f,$20,$54,$48,$49,$53,$22
+ $3a,$ba,$3a,$ba,$22,$20,$46,$49,$56,$45,$20,$54,$49,$4d,$45,$53
+ $20,$41,$4e,$44,$20,$59,$4f,$55,$20,$57,$49,$4e,$21,$20,$20,$55
+ $53,$45,$20,$54,$48,$45,$20,$47,$41,$4d,$45,$22,$3a,$ba,$00,$b2
+ $0a,$e4,$07,$ba,$22,$20,$50,$41,$44,$44,$4c,$45,$53,$20,$54,$4f
+ $20,$44,$45,$46,$45,$4e,$44,$20,$59,$4f,$55,$52,$20,$47,$4f,$41
+ $4c,$2e,$22,$3a,$ba,$3a,$ba,$3a,$ba,$22,$20,$4e,$45,$58,$54,$20
+ $59,$4f,$55,$20,$57,$49,$4c,$4c,$20,$42,$45,$20,$53,$48,$4f,$57
+ $4e,$20,$41,$20,$53,$45,$52,$49,$45,$53,$20,$4f,$46,$22,$3a,$ba
+ $00,$0e,$0b,$ee,$07,$ba,$22,$20,$50,$4c,$41,$59,$49,$4e,$47,$20
+ $46,$49,$45,$4c,$44,$53,$20,$2d,$2d,$20,$54,$4f,$20,$53,$45,$4c
+ $45,$43,$54,$20,$4f,$4e,$45,$2c,$20,$50,$52,$45,$53,$53,$22,$3a
+ $ba,$3a,$ba,$22,$20,$41,$4e,$59,$20,$4b,$45,$59,$20,$57,$48,$45
+ $4e,$20,$54,$48,$45,$20,$46,$49,$45,$4c,$44,$20,$54,$48,$41,$54
+ $20,$59,$4f,$55,$20,$57,$41,$4e,$54,$22,$3a,$ba,$00,$24,$0b,$f8
+ $07,$ba,$22,$20,$49,$53,$20,$44,$49,$53,$50,$4c,$41,$59,$45,$44
+ $2e,$22,$00,$6a,$0b,$02,$08,$ba,$3a,$a2,$32,$33,$3a,$ba,$22,$20
+ $20,$20,$20,$20,$20,$50,$52,$45,$53,$53,$20,$27,$52,$45,$54,$55
+ $52,$4e,$27,$20,$54,$4f,$20,$43,$4f,$4e,$54,$49,$4e,$55,$45,$2e
+ $2e,$2e,$22,$3b,$3a,$be,$41,$24,$3a,$ad,$e6,$28,$41,$24,$29,$d1
+ $cf,$31,$33,$c4,$32,$30,$35,$30,$00,$74,$0b,$fa,$0f,$8c,$c9,$39
+ $33,$36,$00,$97,$0b,$88,$13,$8c,$37,$30,$36,$32,$3a,$8c,$37,$34
+ $36,$32,$3a,$81,$49,$d0,$31,$c1,$32,$30,$30,$30,$3a,$82,$49,$3a
+ $8c,$37,$30,$36,$32,$00,$a1,$0b,$92,$13,$b0,$35,$39,$30,$30,$00
+ $ac,$0b,$9c,$13,$47,$41,$4d,$45,$d0,$30,$00,$c1,$0b,$a6,$13,$b9
+ $32,$32,$34,$2c,$31,$38,$32,$3a,$b9,$32,$32,$35,$2c,$32,$37,$00
+ $cb,$0b,$b0,$13,$8c,$36,$39,$39,$34,$00,$e7,$0b,$ba,$13,$a2,$32
+ $33,$3a,$96,$31,$33,$3a,$ba,$22,$42,$55,$4d,$50,$45,$52,$20,$50
+ $4f,$4f,$4c,$20,$22,$00,$f1,$0b,$c4,$13,$b0,$35,$32,$30,$30,$00
+ $05,$0c,$ce,$13,$b9,$32,$32,$34,$2c,$39,$38,$3a,$b9,$32,$32,$35
+ $2c,$32,$38,$00,$0f,$0c,$d8,$13,$8c,$36,$39,$39,$34,$00,$2b,$0c
+ $e2,$13,$a2,$32,$33,$3a,$96,$31,$33,$3a,$ba,$22,$20,$20,$20,$48
+ $4f,$43,$4b,$45,$59,$20,$20,$20,$22,$00,$35,$0c,$ec,$13,$b0,$35
+ $32,$30,$30,$00,$4a,$0c,$f6,$13,$b9,$32,$32,$34,$2c,$31,$35,$38
+ $3a,$b9,$32,$32,$35,$2c,$32,$38,$00,$54,$0c,$00,$14,$8c,$36,$39
+ $39,$34,$00,$71,$0c,$0a,$14,$a2,$32,$33,$3a,$96,$31,$33,$3a,$ba
+ $22,$20,$20,$53,$43,$52,$41,$4d,$42,$4c,$45,$20,$20,$20,$22,$00
+ $7b,$0c,$14,$14,$b0,$35,$32,$30,$30,$00,$90,$0c,$1e,$14,$b9,$32
+ $32,$34,$2c,$32,$35,$30,$3a,$b9,$32,$32,$35,$2c,$32,$38,$00,$9a
+ $0c,$28,$14,$8c,$36,$39,$39,$34,$00,$b6,$0c,$32,$14,$a2,$32,$33
+ $3a,$96,$31,$33,$3a,$ba,$22,$20,$20,$20,$54,$45,$4e,$4e,$49,$53
+ $20,$20,$20,$22,$00,$c0,$0c,$3c,$14,$b0,$35,$32,$30,$30,$00,$ca
+ $0c,$46,$14,$ab,$35,$30,$32,$30,$00,$da,$0c,$50,$14,$47,$41,$4d
+ $45,$d0,$47,$41,$4d,$45,$c8,$31,$00,$e7,$0c,$5a,$14,$81,$49,$d0
+ $31,$c1,$31,$30,$30,$00,$f7,$0c,$64,$14,$58,$d0,$e2,$28,$c9,$31
+ $36,$33,$38,$34,$29,$00,$05,$0d,$6e,$14,$b9,$c9,$31,$36,$33,$36
+ $38,$2c,$30,$00,$16,$0d,$78,$14,$ad,$58,$d1,$d0,$31,$32,$37,$c4
+ $35,$32,$37,$30,$00,$1c,$0d,$82,$14,$a1,$00,$26,$0d,$8c,$14,$ab
+ $35,$33,$30,$30,$00,$2d,$0d,$96,$14,$82,$49,$00,$37,$0d,$a0,$14
+ $8c,$37,$30,$36,$32,$00,$3d,$0d,$aa,$14,$b1,$00,$5a,$0d,$b4,$14
+ $ba,$22,$57,$48,$41,$54,$20,$44,$49,$46,$46,$49,$43,$55,$4c,$54
+ $59,$20,$4c,$45,$56,$45,$4c,$22,$00,$7d,$0d,$be,$14,$84,$22,$28
+ $31,$2d,$45,$41,$53,$49,$45,$53,$54,$2c,$20,$31,$30,$2d,$48,$41
+ $52,$44,$45,$53,$54,$29,$22,$3b,$44,$46,$46,$00,$94,$0d,$c8,$14
+ $ad,$44,$46,$46,$cf,$31,$30,$ce,$44,$46,$46,$d1,$31,$c4,$35,$33
+ $30,$30,$00,$a2,$0d,$d2,$14,$53,$31,$d0,$30,$3a,$53,$32,$d0,$30
+ $00,$b6,$0d,$dc,$14,$b9,$32,$32,$38,$2c,$31,$32,$37,$3a,$b9,$32
+ $32,$39,$2c,$30,$00,$c5,$0d,$e6,$14,$55,$50,$44,$4f,$57,$4e,$d0
+ $31,$32,$30,$00,$d6,$0d,$f0,$14,$44,$46,$46,$d0,$31,$30,$c8,$44
+ $46,$46,$ca,$35,$00,$ee,$0d,$fa,$14,$b9,$36,$37,$36,$32,$2c,$44
+ $46,$46,$3a,$b9,$36,$38,$30,$30,$2c,$44,$46,$46,$00,$0c,$0e,$04
+ $15,$b9,$36,$37,$32,$33,$2c,$55,$50,$44,$4f,$57,$4e,$3a,$b9,$36
+ $37,$32,$37,$2c,$55,$50,$44,$4f,$57,$4e,$00,$32,$0e,$0e,$15,$b9
+ $36,$37,$33,$32,$2c,$32,$35,$36,$c9,$55,$50,$44,$4f,$57,$4e,$3a
+ $b9,$36,$37,$33,$36,$2c,$32,$35,$36,$c9,$55,$50,$44,$4f,$57,$4e
+ $00,$43,$0e,$18,$15,$ad,$47,$41,$4d,$45,$d0,$32,$c4,$35,$34,$34
+ $30,$00,$53,$0e,$22,$15,$4c,$45,$d0,$37,$3a,$52,$45,$d0,$32,$34
+ $37,$00,$6b,$0e,$2c,$15,$b9,$36,$37,$37,$31,$2c,$31,$38,$39,$3a
+ $b9,$36,$38,$33,$30,$2c,$31,$38,$39,$00,$75,$0e,$36,$15,$ab,$35
+ $34,$36,$30,$00,$86,$0e,$40,$15,$4c,$45,$d0,$39,$30,$3a,$52,$45
+ $d0,$31,$36,$36,$00,$9e,$0e,$4a,$15,$b9,$36,$37,$37,$31,$2c,$32
+ $30,$33,$3a,$b9,$36,$38,$33,$30,$2c,$32,$30,$33,$00,$b4,$0e,$54
+ $15,$b9,$36,$35,$38,$39,$2c,$4c,$45,$3a,$b9,$36,$36,$35,$32,$2c
+ $52,$45,$00,$e4,$0e,$5e,$15,$b9,$36,$36,$38,$30,$2c,$4c,$45,$c9
+ $33,$3a,$b9,$36,$36,$38,$34,$2c,$4c,$45,$c8,$33,$3a,$b9,$36,$36
+ $38,$38,$2c,$52,$45,$c9,$33,$3a,$b9,$36,$36,$39,$32,$2c,$52,$45
+ $c8,$33,$00,$07,$0f,$68,$15,$84,$22,$53,$48,$4f,$55,$4c,$44,$20
+ $54,$48,$45,$52,$45,$20,$42,$45,$20,$47,$52,$41,$56,$49,$54,$59
+ $3f,$22,$3b,$41,$24,$00,$0d,$0f,$6d,$15,$97,$00,$1f,$0f,$72,$15
+ $b9,$36,$35,$2c,$32,$37,$3a,$b9,$36,$37,$2c,$32,$37,$00,$25,$0f
+ $7c,$15,$ba,$00,$35,$0f,$86,$15,$81,$49,$d0,$32,$37,$c1,$31,$31
+ $c7,$c9,$38,$00,$45,$0f,$90,$15,$b9,$36,$36,$2c,$49,$3a,$b9,$36
+ $38,$2c,$49,$00,$4f,$0f,$9a,$15,$8c,$36,$35,$32,$36,$00,$56,$0f
+ $a4,$15,$82,$49,$00,$5e,$0f,$a9,$15,$a2,$32,$33,$00,$81,$0f,$ae
+ $15,$ba,$22,$50,$4c,$41,$59,$45,$52,$20,$23,$31,$22,$3b,$3a,$96
+ $32,$37,$3a,$ba,$22,$50,$4c,$41,$59,$45,$52,$20,$23,$32,$22,$00
+ $a7,$0f,$b8,$15,$b9,$32,$32,$34,$2c,$31,$32,$38,$3a,$b9,$32,$32
+ $35,$2c,$33,$38,$3a,$b9,$32,$32,$36,$2c,$31,$32,$38,$3a,$b9,$32
+ $32,$37,$2c,$33,$38,$00,$c0,$0f,$c2,$15,$a2,$32,$34,$3a,$96,$34
+ $3a,$ba,$53,$31,$3b,$3a,$96,$33,$31,$3a,$ba,$53,$32,$3b,$00,$d4
+ $0f,$cc,$15,$ad,$53,$31,$d0,$35,$ce,$53,$32,$d0,$35,$c4,$35,$38
+ $34,$30,$00,$ea,$0f,$d6,$15,$ad,$e8,$28,$41,$24,$2c,$31,$29,$d0
+ $22,$59,$22,$c4,$35,$37,$30,$30,$00,$f7,$0f,$e0,$15,$81,$49,$d0
+ $31,$c1,$32,$30,$30,$00,$01,$10,$ea,$15,$8c,$36,$35,$32,$36,$00
+ $08,$10,$f4,$15,$82,$49,$00,$12,$10,$fe,$15,$8c,$36,$37,$34,$38
+ $00,$1c,$10,$08,$16,$b0,$35,$37,$35,$30,$00,$27,$10,$12,$16,$b9
+ $32,$32,$39,$2c,$30,$00,$3f,$10,$1c,$16,$ad,$e2,$28,$32,$32,$34
+ $29,$d1,$31,$32,$37,$c4,$53,$32,$d0,$53,$32,$c8,$31,$00,$57,$10
+ $26,$16,$ad,$e2,$28,$32,$32,$34,$29,$cf,$31,$32,$37,$c4,$53,$31
+ $d0,$53,$31,$c8,$31,$00,$61,$10,$30,$16,$8c,$36,$38,$38,$32,$00
+ $6b,$10,$3a,$16,$ab,$35,$35,$36,$30,$00,$78,$10,$44,$16,$81,$49
+ $d0,$31,$c1,$32,$30,$30,$00,$82,$10,$4e,$16,$8c,$36,$35,$32,$36
+ $00,$89,$10,$58,$16,$82,$49,$00,$93,$10,$62,$16,$8c,$36,$37,$38
+ $36,$00,$9d,$10,$6c,$16,$ab,$35,$36,$34,$30,$00,$a8,$10,$76,$16
+ $81,$49,$d0,$31,$c1,$32,$00,$b4,$10,$80,$16,$81,$4a,$d0,$31,$c1
+ $31,$30,$00,$ce,$10,$8a,$16,$5a,$d0,$e2,$28,$c9,$31,$36,$33,$33
+ $36,$29,$c9,$e2,$28,$c9,$31,$36,$33,$33,$36,$29,$00,$d5,$10,$94
+ $16,$82,$4a,$00,$e0,$10,$9e,$16,$81,$4a,$d0,$31,$c1,$35,$00,$0e
+ $11,$a8,$16,$5a,$d0,$e2,$28,$c9,$31,$36,$33,$33,$36,$29,$c9,$e2
+ $28,$c9,$31,$36,$33,$33,$36,$29,$c8,$e2,$28,$c9,$31,$36,$33,$33
+ $36,$29,$c9,$e2,$28,$c9,$31,$36,$33,$33,$36,$29,$00,$15,$11,$b2
+ $16,$82,$4a,$00,$1c,$11,$bc,$16,$82,$49,$00,$22,$11,$c6,$16,$b1
+ $00,$39,$11,$d0,$16,$ba,$3a,$84,$22,$4d,$4f,$52,$45,$20,$50,$4f
+ $4e,$47,$3f,$22,$3b,$41,$24,$00,$3f,$11,$d5,$16,$97,$00,$4d,$11
+ $da,$16,$ba,$3a,$ba,$3a,$8c,$37,$30,$36,$32,$00,$63,$11,$e4,$16
+ $ad,$e8,$28,$41,$24,$2c,$31,$29,$d0,$22,$59,$22,$c4,$35,$30,$32
+ $30,$00,$7b,$11,$ee,$16,$b9,$c9,$31,$36,$33,$30,$33,$2c,$30,$3a
+ $b9,$c9,$31,$36,$32,$39,$38,$2c,$30,$00,$8d,$11,$f8,$16,$8c,$c9
+ $39,$33,$36,$3a,$ba,$22,$42,$59,$45,$21,$22,$00,$93,$11,$02,$17
+ $80,$00,$99,$11,$0c,$17,$b2,$00,$bb,$11,$84,$17,$b9,$c9,$31,$36
+ $32,$39,$37,$2c,$30,$3a,$b9,$c9,$31,$36,$33,$30,$31,$2c,$30,$3a
+ $b9,$c9,$31,$36,$33,$30,$34,$2c,$30,$00,$c1,$11,$8e,$17,$b1,$00
+ $00,$00
;
; Unused space. This leaves some room for the BASIC program to expand before it
; runs into the area used for variables.
;
11c3: 08 d3 a8 68+ .junk 349
;
; Applesoft variable storage starts here.
;
; Without garbage collection, strings will eventually fill the space and
; overwrite the code.
;
1320: 41 80 01 fd+ .junk 752
;
; Computes the address of a line on the hi-res screen.
;
; On entry:
; A-reg: line number (0-191)
;
; On exit:
; $00/01: pointer to start of line
;
1610: 48 GetLineAddr pha ;put two copies of the line number on the stack
1611: 48 pha
1612: 29 3f and #%00111111 ;get the low 5 bits (0-63)
1614: a8 tay
1615: b9 35 16 lda hires_line_hi,y ;get the base value from the table
1618: 85 01 sta line_ptr+1 ;set the high byte
161a: 68 pla ;get full number
161b: 29 c0 and #%11000000 ;look at the two high bits; should be 00/01/10
161d: f0 08 beq @SetLow ;it's 00, branch
161f: 10 04 bpl @SetLow28 ;high is clear, branch (low byte is $00 or $80)
1621: a9 50 lda #$50 ;line range 128-191, low byte is $50 or $d0
1623: d0 02 bne @SetLow
1625: a9 28 @SetLow28 lda #$28 ;line range 64-127, low byte is $28 or $a8
1627: 85 00 @SetLow sta line_ptr ;set the low byte
1629: 68 pla ;check the line number again
162a: 29 08 and #%00001000 ;is it 0-7, 16-23, etc?
162c: f0 06 beq @Return ;yes, bail
162e: a5 00 lda line_ptr ;no, it's 8-15, 24-31, etc.
1630: 09 80 ora #$80 ;add $80 to the low byte
1632: 85 00 sta line_ptr
1634: 60 @Return rts
1635: 20 24 28 2c+ hires_line_hi .bulk $20,$24,$28,$2c,$30,$34,$38,$3c,$20,$24,$28,$2c,$30,$34,$38,$3c
+ $21,$25,$29,$2d,$31,$35,$39,$3d,$21,$25,$29,$2d,$31,$35,$39,$3d
+ $22,$26,$2a,$2e,$32,$36,$3a,$3e,$22,$26,$2a,$2e,$32,$36,$3a,$3e
+ $23,$27,$2b,$2f,$33,$37,$3b,$3f,$23,$27,$2b,$2f,$33,$37,$3b,$3f
;
; Calculates the line offset and bit mask for the pixel at the specified
; coordinate.
;
; On entry:
; A-reg: X coordinate (0-255)
;
; On exit:
; $02: byte offset (0-39)
; $03: bit mask
;
1675: a8 CalcOffAndMask tay
1676: be 90 16 ldx div7,y ;divide X coordinate by 7 -> floor(XC/7)
1679: 86 02 stx byte_offset ;use that as hi-res byte offset
167b: 86 03 stx pixel_mask ;store it temporarily so we can shift it
167d: 06 03 asl pixel_mask ;multiply -> floor(XC/7) * 8
167f: 06 03 asl pixel_mask
1681: 06 03 asl pixel_mask
1683: 18 clc
1684: 65 02 adc byte_offset ;add X coord to byte offset -> XC + floor(XC/7)
1686: 38 sec
1687: e5 03 sbc pixel_mask ;subtract -> (XC + floor(XC/7)) - (floor(XC/7) * 8)
1689: a8 tay ; which would be zero without the floor() op
168a: b9 90 17 lda pixel_bits,y ; but instead gives us the remainder (0-6)
168d: 85 03 sta pixel_mask ;set the bit mask for the pixel
168f: 60 rts
;
; Table lookup to compute N/7, for N=[0,255].
;
1690: 00 00 00 00+ div7 .bulk $00,$00,$00,$00,$00,$00,$00
1697: 01 01 01 01+ .bulk $01,$01,$01,$01,$01,$01,$01
169e: 02 02 02 02+ .bulk $02,$02,$02,$02,$02,$02,$02
16a5: 03 03 03 03+ .bulk $03,$03,$03,$03,$03,$03,$03
16ac: 04 04 04 04+ .bulk $04,$04,$04,$04,$04,$04,$04
16b3: 05 05 05 05+ .bulk $05,$05,$05,$05,$05,$05,$05
16ba: 06 06 06 06+ .bulk $06,$06,$06,$06,$06,$06,$06
16c1: 07 07 07 07+ .bulk $07,$07,$07,$07,$07,$07,$07
16c8: 08 08 08 08+ .bulk $08,$08,$08,$08,$08,$08,$08
16cf: 09 09 09 09+ .bulk $09,$09,$09,$09,$09,$09,$09
16d6: 0a 0a 0a 0a+ .bulk $0a,$0a,$0a,$0a,$0a,$0a,$0a
16dd: 0b 0b 0b 0b+ .bulk $0b,$0b,$0b,$0b,$0b,$0b,$0b
16e4: 0c 0c 0c 0c+ .bulk $0c,$0c,$0c,$0c,$0c,$0c,$0c
16eb: 0d 0d 0d 0d+ .bulk $0d,$0d,$0d,$0d,$0d,$0d,$0d
16f2: 0e 0e 0e 0e+ .bulk $0e,$0e,$0e,$0e,$0e,$0e,$0e
16f9: 0f 0f 0f 0f+ .bulk $0f,$0f,$0f,$0f,$0f,$0f,$0f
1700: 10 10 10 10+ .bulk $10,$10,$10,$10,$10,$10,$10
1707: 11 11 11 11+ .bulk $11,$11,$11,$11,$11,$11,$11
170e: 12 12 12 12+ .bulk $12,$12,$12,$12,$12,$12,$12
1715: 13 13 13 13+ .bulk $13,$13,$13,$13,$13,$13,$13
171c: 14 14 14 14+ .bulk $14,$14,$14,$14,$14,$14,$14
1723: 15 15 15 15+ .bulk $15,$15,$15,$15,$15,$15,$15
172a: 16 16 16 16+ .bulk $16,$16,$16,$16,$16,$16,$16
1731: 17 17 17 17+ .bulk $17,$17,$17,$17,$17,$17,$17
1738: 18 18 18 18+ .bulk $18,$18,$18,$18,$18,$18,$18
173f: 19 19 19 19+ .bulk $19,$19,$19,$19,$19,$19,$19
1746: 1a 1a 1a 1a+ .bulk $1a,$1a,$1a,$1a,$1a,$1a,$1a
174d: 1b 1b 1b 1b+ .bulk $1b,$1b,$1b,$1b,$1b,$1b,$1b
1754: 1c 1c 1c 1c+ .bulk $1c,$1c,$1c,$1c,$1c,$1c,$1c
175b: 1d 1d 1d 1d+ .bulk $1d,$1d,$1d,$1d,$1d,$1d,$1d
1762: 1e 1e 1e 1e+ .bulk $1e,$1e,$1e,$1e,$1e,$1e,$1e
1769: 1f 1f 1f 1f+ .bulk $1f,$1f,$1f,$1f,$1f,$1f,$1f
1770: 20 20 20 20+ .bulk $20,$20,$20,$20,$20,$20,$20
1777: 21 21 21 21+ .bulk $21,$21,$21,$21,$21,$21,$21
177e: 22 22 22 22+ .bulk $22,$22,$22,$22,$22,$22,$22
1785: 23 23 23 23+ .bulk $23,$23,$23,$23,$23,$23,$23
178c: 24 24 24 24 .bulk $24,$24,$24,$24
;
; Hi-res single-pixel masks.
;
1790: 01 02 04 08+ pixel_bits .bulk $01,$02,$04,$08,$10,$20,$40
;
; Draws or erases one column of the ball (3 pixels). This takes a horizontal
; offset as an argument, so we can just the position by one column.
;
; On entry:
; $e0: X coordinate
; $e1: center Y coordinate
; A-reg: X coordinate delta (usually 1 or -1)
;
DrawEraseBallCol
1797: 18 clc
1798: 65 e0 adc ball_xc ;add offset to X coordinate
179a: 20 75 16 jsr CalcOffAndMask ;calculate the horizontal offset and pixel mask
179d: a5 e1 lda ball_yc ;get Y coordinate
179f: 20 10 16 jsr GetLineAddr ;get row address
17a2: 20 f0 17 jsr DrawErasePixel ;draw/erase that pixel
17a5: a5 e1 lda ball_yc
17a7: 18 clc
17a8: 69 01 adc #$01 ;repeat for pixel below
17aa: 20 10 16 jsr GetLineAddr
17ad: 20 f0 17 jsr DrawErasePixel
17b0: a5 e1 lda ball_yc
17b2: 38 sec
17b3: e9 01 sbc #$01 ;repeat for pixel above
17b5: 20 10 16 jsr GetLineAddr
17b8: 20 f0 17 jsr DrawErasePixel
17bb: 60 rts
;
; Draws or erases one row of the ball (3 pixels). This takes an offset as an
; argument, so we can adjust the position by one row.
;
; On entry:
; $e0: center X coordinate
; $e1: Y coordinate
; A-reg: Y coordinate delta (usually 1 or -1)
;
DrawEraseBallRow
17bc: 18 clc
17bd: 65 e1 adc ball_yc ;add offset to Y coordinate
17bf: 20 10 16 jsr GetLineAddr ;calculate the row address
17c2: a5 e0 lda ball_xc ;get X coordinate
17c4: 20 75 16 jsr CalcOffAndMask ;calculate byte offset and pixel mask
17c7: 20 f0 17 jsr DrawErasePixel ;draw/erase that pixel
17ca: a5 e0 lda ball_xc
17cc: 18 clc
17cd: 69 01 adc #$01 ;repeat for pixel to the right
17cf: 20 75 16 jsr CalcOffAndMask
17d2: 20 f0 17 jsr DrawErasePixel
17d5: a5 e0 lda ball_xc
17d7: 38 sec
17d8: e9 01 sbc #$01 ;repeat for pixel to the left
17da: 20 75 16 jsr CalcOffAndMask
17dd: 20 f0 17 jsr DrawErasePixel
17e0: 60 rts
;
; Draws or erases the ball.
;
; Instead of drawing the entire 3x3 ball, we only draw the rows or columns that
; change. The movement delta is applied to the ball's position.
;
17e1: a5 3e DrawEraseBall lda delta_x ;did the X position change?
17e3: f0 03 beq @NotCol ;no, branch
17e5: 20 97 17 jsr DrawEraseBallCol ;yes, draw or erase one column
17e8: a5 3f @NotCol lda delta_y ;did the Y position change?
17ea: f0 03 beq @Return ;no, branch
17ec: 20 bc 17 jsr DrawEraseBallRow ;yes, draw or erase one row
17ef: 60 @Return rts
;
; Draws or erases one pixel.
;
; On entry:
; $00-01: hi-res line offset
; $02: byte offset in line
; $03: pixel bit mask
; $04: draw flag (0=erase)
;
17f0: a4 02 DrawErasePixel ldy byte_offset ;get offset into hi-res line (0-39)
17f2: a5 03 lda pixel_mask ;get pixel bits
17f4: a6 04 ldx draw_flag ;are we drawing or erasing?
17f6: d0 07 bne @DoDraw ;drawing, branch
17f8: 49 ff eor #$ff ;invert pixel mask
17fa: 31 00 and (line_ptr),y ;clear bits
17fc: 91 00 sta (line_ptr),y ;update screen
17fe: 60 rts
17ff: 11 00 @DoDraw ora (line_ptr),y ;merge bits
1801: 91 00 sta (line_ptr),y ;update screen
1803: 60 rts
;
; Erases part of the ball from its current position, then draws it in its new
; position. Because the ball moves at most one pixel per frame, we only need to
; redraw one row and/or one column.
;
; Replaces the "current" coordinates with "new" values.
;
; This is also used to draw the paddles, which can be treated as very tall
; balls.
;
; On entry:
; $e0: ball X position (current)
; $e1: ball Y position (current)
; $e2: ball X position (new)
; $e3: ball Y position (new)
;
; On exit:
; $3e: X delta
; $3f: Y delta
;
1804: a5 e0 RedrawBall lda ball_xc ;compute inverse change in X position
1806: 38 sec
1807: e5 e2 sbc new_ball_xc
1809: 85 3e sta delta_x
180b: a5 e1 lda ball_yc ;compute inverse change in Y position
180d: 38 sec
180e: e5 e3 sbc new_ball_yc
1810: 85 3f sta delta_y
;
1812: a9 00 lda #$00
1814: 85 04 sta draw_flag ;configure to erase
1816: 20 e1 17 jsr DrawEraseBall ;erase row and/or column, using backward delta
; Invert delta_x/delta_y, so it now represents the delta from the old position
; to the new.
1819: a5 3e lda delta_x ;set delta_x = -delta_x
181b: 49 ff eor #$ff
181d: 85 3e sta delta_x
181f: e6 3e inc delta_x
1821: a5 3f lda delta_y ;set delta_y = -delta_y
1823: 49 ff eor #$ff
1825: 85 3f sta delta_y
1827: e6 3f inc delta_y
;
1829: a5 e2 lda new_ball_xc ;copy "new" X/Y to "current" X/Y
182b: 85 e0 sta ball_xc
182d: a5 e3 lda new_ball_yc
182f: 85 e1 sta ball_yc
1831: a9 ff lda #$ff
1833: 85 04 sta draw_flag ;configure to draw
1835: 20 e1 17 jsr DrawEraseBall ;draw row and/or column, using forward delta
1838: 60 rts
;
; Tests to see if a screen pixel is set.
;
; On entry:
; $00: hi-res line address
; $02: byte offset in line
; $03: pixel mask
;
; On exit:
; $07: incremented if pixel is set
;
1839: a4 02 TestPixel ldy byte_offset
183b: a5 03 lda pixel_mask ;get the pixel bit mask
183d: 31 00 and (line_ptr),y ;merge it with the screen contents
183f: f0 02 beq @Return ;if zero, no collision; branch
1841: e6 07 inc coll_ctr ;increment collision counter
1843: 60 @Return rts
;
; Tests for a collision between the ball and another object due to horizontal
; movement.
;
; On entry:
; A-reg: movement delta (-2 or +2)
;
; On exit:
; $07: incremented 0-3 times
;
1844: 18 CheckCollX clc
1845: 65 e0 adc ball_xc ;add offset to X coord
1847: 20 75 16 jsr CalcOffAndMask ;calculate byte offset and pixel mask
184a: a5 e1 lda ball_yc ;get ball Y coordinate
184c: 20 10 16 jsr GetLineAddr ;calculate line address
184f: 20 39 18 jsr TestPixel ;test if pixel is set
1852: a5 e1 lda ball_yc
1854: 18 clc
1855: 69 01 adc #$01 ;repeat for Y + 1
1857: 20 10 16 jsr GetLineAddr
185a: 20 39 18 jsr TestPixel
185d: a5 e1 lda ball_yc
185f: 38 sec
1860: e9 01 sbc #$01 ;repeat for Y - 1
1862: 20 10 16 jsr GetLineAddr
1865: 20 39 18 jsr TestPixel
1868: 60 rts
;
; Tests for a collision between the ball and another object due to vertical
; movement.
;
; On entry:
; A-reg: movement delta (-2 or +2)
;
; On exit:
; $07: incremented 0-3 times
;
1869: 18 CheckCollY clc
186a: 65 e1 adc ball_yc ;add offset to Y coord
186c: 20 10 16 jsr GetLineAddr ;calculate line address
186f: a5 e0 lda ball_xc ;get ball X coordinate
1871: 20 75 16 jsr CalcOffAndMask ;calculate byte offset and pixel mask
1874: 20 39 18 jsr TestPixel ;test if pixel is set
1877: a5 e0 lda ball_xc
1879: 18 clc
187a: 69 01 adc #$01 ;repeat for X + 1
187c: 20 75 16 jsr CalcOffAndMask
187f: 20 39 18 jsr TestPixel
1882: a5 e0 lda ball_xc
1884: 38 sec
1885: e9 01 sbc #$01 ;repeat for X - 1
1887: 20 75 16 jsr CalcOffAndMask
188a: 20 39 18 jsr TestPixel
188d: 60 rts
;
; Checks for a collision between the ball and an on-screen object.
;
; In the event of a collision, the ball's velocity and position is updated and a
; sound is played.
;
188e: a9 00 CheckCollision lda #$00
1890: 85 07 sta coll_ctr ;clear collision counters
1892: 85 05 sta coll_flag
1894: a5 e5 lda ball_yvel ;check Y velocity
1896: 10 04 bpl @YPos ;positive or zero, branch
1898: a9 fe lda #$fe ;use an offset of -2
189a: d0 02 bne @YCommon ;(always)
189c: a9 02 @YPos lda #$02 ;use an offset of +2
189e: 85 3f @YCommon sta delta_y ;save for later
18a0: 20 69 18 jsr CheckCollY ;check for collisions from Y movement
18a3: a5 07 lda coll_ctr ;did we hit anything?
18a5: f0 0d beq @NoYColl ;no, branch
18a7: e6 05 inc coll_flag ;yes, set flag
18a9: a5 e5 lda ball_yvel ;set yvel = -yvel
18ab: 49 ff eor #$ff
18ad: 85 e5 sta ball_yvel
18af: e6 e5 inc ball_yvel
18b1: 20 19 19 jsr BounceY ;back us off one pixel, play sound
;
18b4: a9 00 @NoYColl lda #$00
18b6: 85 07 sta coll_ctr ;reset counter
18b8: a5 e4 lda ball_xvel ;check X velocity
18ba: 10 04 bpl @XPos ;positive or zero, branch
18bc: a9 fe lda #$fe ;use an offset of -2
18be: d0 02 bne @XCommon ;(always)
18c0: a9 02 @XPos lda #$02
18c2: 85 3e @XCommon sta delta_x ;save for later
18c4: 20 44 18 jsr CheckCollX ;check for collisions from X movement
18c7: a5 07 lda coll_ctr ;did we hit anything?
18c9: f0 10 beq @NoXColl ;no, branch
18cb: e6 05 inc coll_flag ;yes, set flag
18cd: a5 e4 lda ball_xvel ;set xvel = -xvel
18cf: 49 ff eor #$ff
18d1: 85 e4 sta ball_xvel
18d3: e6 e4 inc ball_xvel
18d5: 20 15 1a jsr TestPdlColl ;modify Y velocity if we hit a paddle
18d8: 20 06 19 jsr BounceX ;back us off one pixel, play sound
;
18db: a5 05 @NoXColl lda coll_flag ;did we hit anything at all?
18dd: d0 26 bne @Return ;yes, bail
; The previous tests were for the leading edge. We now test the future position
; of the center pixel. (Not sure what this is for... maybe an edge case where
; the paddle moves on top of the ball?)
18df: a9 00 lda #$00
18e1: 85 07 sta coll_ctr ;reset collision counter
18e3: a5 3e lda delta_x ;get X movement (+2/-2)
18e5: 18 clc
18e6: 65 e0 adc ball_xc ;add current position
18e8: 20 75 16 jsr CalcOffAndMask ;calculate byte offset and pixel mask
18eb: a5 3f lda delta_y ;get Y movement (+2/-2)
18ed: 18 clc
18ee: 65 e1 adc ball_yc ;add current position
18f0: 20 10 16 jsr GetLineAddr ;calculate line address
18f3: 20 39 18 jsr TestPixel ;see if the center pixel is set
18f6: a5 07 lda coll_ctr
18f8: f0 0b beq @Return ;it isn't, return
18fa: a5 e4 lda ball_xvel ;must be a paddle hit, invert the X coordinate
18fc: 49 ff eor #$ff
18fe: 85 e4 sta ball_xvel
1900: e6 e4 inc ball_xvel
1902: 20 06 19 jsr BounceX ;do the bounce
1905: 60 @Return rts
;
; Adjusts the ball's X position and plays a sound.
;
1906: a5 e4 BounceX lda ball_xvel ;check new velocity
1908: 30 04 bmi @MovingLf
190a: a9 01 lda #$01 ;moving right
190c: 10 02 bpl @Common
190e: a9 ff @MovingLf lda #$ff
1910: 18 @Common clc
1911: 65 e0 adc ball_xc ;add +1 or -1 to current position
1913: 85 e2 sta new_ball_xc ; so we're no longer colliding
1915: 20 3f 19 jsr BounceSoundX ;play sound
1918: 60 rts
;
; Adjusts the ball's Y position and plays a sound.
;
1919: a5 e5 BounceY lda ball_yvel ;check new velocity
191b: 30 04 bmi @MovingUp
191d: a9 01 lda #$01 ;moving down
191f: 10 02 bpl @Common
1921: a9 ff @MovingUp lda #$ff
1923: 18 @Common clc
1924: 65 e1 adc ball_yc ;add +1 or -1 to current position
1926: 85 e3 sta new_ball_yc ; so we're no longer colliding
1928: 20 4d 19 jsr BounceSoundY ;play sound
192b: 60 rts
;
; Plays a sound.
;
; On entry:
; $e8: pitch
; $e9: duration
;
192c: ad 30 c0 BounceSound lda SPKR ;click
192f: 88 @NoClick dey
1930: d0 04 bne @NoDec
1932: c6 e9 dec snd_dur
1934: f0 08 beq @Return
1936: ca @NoDec dex
1937: d0 f6 bne @NoClick
1939: a6 e8 ldx snd_pitch
193b: 4c 2c 19 jmp BounceSound
193e: 60 @Return rts
;
; Sets up and plays the sound for bouncing off of a vertical surface.
;
; A-reg is preserved.
;
193f: 48 BounceSoundX pha
1940: a9 d0 lda #$d0
1942: 85 e8 sta snd_pitch
1944: a9 06 lda #$06
1946: 85 e9 sta snd_dur
1948: 20 2c 19 jsr BounceSound
194b: 68 pla
194c: 60 rts
;
; Sets up and plays the sound for bouncing off of a horizontal surface.
;
; A-reg is preserved.
;
194d: 48 BounceSoundY pha
194e: a9 f0 lda #$f0
1950: 85 e8 sta snd_pitch
1952: a9 06 lda #$06
1954: 85 e9 sta snd_dur
1956: 20 2c 19 jsr BounceSound
1959: 68 pla
195a: 60 rts
;
; Updates the ball's X/Y coordinates.
;
; If we're, say, moving to the right, then X velocity will be positive. If the
; fractional portion is negative, the ADC won't overflow, and we do nothing. If
; the fractional portion is positive, the ADC might overflow; if it does, the
; carry will be clear. Thus we can use the overflow flag as a movement trigger,
; and use the carry flag to see which way we were moving.
;
195b: a5 e6 MoveBall lda ball_x_frac ;get fractional part of X position
195d: 18 clc
195e: 65 e4 adc ball_xvel ;add velocity
1960: 50 08 bvc @NoXOv ;if no overflow, branch
1962: 90 04 bcc @PosX ;if went pos to neg, branch
1964: c6 e2 dec new_ball_xc ;move left
1966: b0 02 bcs @NoXOv ;(always)
1968: e6 e2 @PosX inc new_ball_xc ;move right
196a: 85 e6 @NoXOv sta ball_x_frac
;
196c: a5 e7 lda ball_y_frac ;get fractional part of Y position
196e: 38 sec
196f: e5 e5 sbc ball_yvel ;subtract velocity
1971: 50 08 bvc @NoYOv ;if no overflow, branch
1973: 90 04 bcc @NegY ;if went pos to neg, branch
1975: e6 e3 inc new_ball_yc ;move down
1977: b0 02 bcs @NoYOv
1979: c6 e3 @NegY dec new_ball_yc ;move up
197b: 85 e7 @NoYOv sta ball_y_frac
197d: 60 rts
;
; CALL 6526 - redraws the paddles on screen, based on the current paddle
; position.
;
; Paddles are 24 pixels high and 3 pixels wide. The position is set between 3
; and 130, but they're drawn one line below that.
;
; Paddles don't move instantaneously. A call here moves each paddle 8x, one
; pixel at a time.
;
; On entry:
; $43: current left paddle position
; $44: current right paddle position
;
197e: a9 08 UpdatePaddles lda #8 ;move paddle 8 pixels per frame
1980: 85 40 sta pdl_drw_ctr
1982: a5 e0 lda ball_xc ;preserve $e0-e3 (ball current/new position)
1984: 48 pha ; so we can use the ball-drawing code for the
1985: a5 e1 lda ball_yc ; paddles too
1987: 48 pha
1988: a5 e2 lda new_ball_xc
198a: 48 pha
198b: a5 e3 lda new_ball_yc
198d: 48 pha
; Handle left paddle (player #1).
198e: a2 00 ldx #$00
1990: 20 1e fb jsr MON_PREAD ;read paddle 0 into Y-reg (0-255)
1993: 98 tya
1994: 4a lsr A ;halve it (0-127)
1995: 18 clc
1996: 69 03 adc #$03 ;add 3 (3-130)
1998: 85 41 sta left_pdl_new_y ;save it
; Set up coordinates.
;
; We set the "current" Y coordinate to position+8, and "new" to position+16 (or
; vice-versa), yielding a deltaY of 8. The ball drawing routine will erase the
; row at (current - deltaY), and draw the row at (new + deltaY). Since
; "current" and "new" are 8 pixels apart, this updates rows 24 pixels apart.
199a: a5 41 @Loop0 lda left_pdl_new_y ;get desired position
199c: 38 sec
199d: e5 43 sbc left_pdl_y ;subtract old position
199f: 18 clc
19a0: 30 0e bmi @LeftUp
19a2: a5 43 lda left_pdl_y ;moving paddle down; get old position
19a4: 69 08 adc #8
19a6: 85 e1 sta ball_yc ;set old paddle + 8 as current position
19a8: 69 08 adc #8
19aa: 85 e3 sta new_ball_yc ;set old paddle + 16 as new position
19ac: e6 43 inc left_pdl_y ;move paddle down one pixel
19ae: d0 0c bne @Common0 ;(always)
19b0: a5 43 @LeftUp lda left_pdl_y ;moving paddle up; get old position
19b2: 69 08 adc #8
19b4: 85 e3 sta new_ball_yc ;set old paddle + 8 as new position
19b6: 69 08 adc #8
19b8: 85 e1 sta ball_yc ;set old paddle + 16 as old position
19ba: c6 43 dec left_pdl_y
; Draw a row. The row will be erased from the current position and drawn at the
; new position. If we're not actually moving, the draw operation will do
; nothing (but we still do it 8x). The delay may be unavoidable because
; paddle0's capacitor might still be charging.
19bc: a9 07 @Common0 lda #$07 ;POKE 6589,LE
19be: 85 e0 sta ball_xc ;set X position for left paddle
19c0: 85 e2 sta new_ball_xc ;set "new" position so it draws rows
19c2: 20 04 18 jsr RedrawBall ;erase one row, draw one row
19c5: c6 40 dec pdl_drw_ctr ;have we done this 8x?
19c7: d0 d1 bne @Loop0 ;no, loop
; Repeat for right paddle (player #2).
19c9: a9 08 lda #8
19cb: 85 40 sta pdl_drw_ctr ;reset draw counter
19cd: a2 01 ldx #$01
19cf: 20 1e fb jsr MON_PREAD ;read paddle 1 into Y-reg (0-255)
19d2: 98 tya
19d3: 4a lsr A ;halve it (0-127)
19d4: 18 clc
19d5: 69 03 adc #$03 ;add 3 (3-130)
19d7: 85 42 sta right_pdl_new_y ;save it
;
19d9: a5 42 @Loop1 lda right_pdl_new_y
19db: 38 sec
19dc: e5 44 sbc right_pdl_y
19de: 18 clc
19df: 30 0e bmi @RightUp
19e1: a5 44 lda right_pdl_y
19e3: 69 08 adc #8
19e5: 85 e1 sta ball_yc
19e7: 69 08 adc #8
19e9: 85 e3 sta new_ball_yc
19eb: e6 44 inc right_pdl_y
19ed: d0 0c bne @Common1 ;(always)
19ef: a5 44 @RightUp lda right_pdl_y
19f1: 69 08 adc #8
19f3: 85 e3 sta new_ball_yc
19f5: 69 08 adc #8
19f7: 85 e1 sta ball_yc
19f9: c6 44 dec right_pdl_y
19fb: a9 f7 @Common1 lda #$f7 ;POKE 6652,RE
19fd: 85 e0 sta ball_xc ;set X position for right paddle
19ff: 85 e2 sta new_ball_xc ;set "new" position so it draws rows
1a01: 20 04 18 jsr RedrawBall
1a04: c6 40 dec pdl_drw_ctr
1a06: d0 d1 bne @Loop1
;
1a08: 68 pla ;restore $e0-e3
1a09: 85 e3 sta new_ball_yc
1a0b: 68 pla
1a0c: 85 e2 sta new_ball_xc
1a0e: 68 pla
1a0f: 85 e1 sta ball_yc
1a11: 68 pla
1a12: 85 e0 sta ball_xc
1a14: 60 rts
;
; Tests for a collision between the ball and a paddle. Updates the ball's Y
; velocity based on where on the paddle the ball hit.
;
; This is only called after we detected a collision as a result of horizontal
; movement. There are no horizontal field lines in the columns where the
; paddles are, so if the X coordinate matches we assume it's a paddle hit.
;
; It's possible to hit the top or bottom of a paddle, but that just reacts like
; striking any other horizontal surface.
;
1a15: a5 e0 TestPdlColl lda ball_xc ;get ball X coordinate
1a17: c9 04 cmp #$04 ;POKE 6680,LE - 3
1a19: f0 0d beq @HitLeft
1a1b: c9 0a cmp #$0a ;POKE 6684,LE + 3
1a1d: f0 09 beq @HitLeft
1a1f: c9 f4 cmp #$f4 ;POKE 6688,RE - 3
1a21: f0 31 beq @HitRight
1a23: c9 fa cmp #$fa ;POKE 6692,RE + 3
1a25: f0 2d beq @HitRight
1a27: 60 rts
1a28: a5 e1 @HitLeft lda ball_yc ;get ball Y coordinate (0-159)
1a2a: 38 sec
1a2b: e5 41 sbc left_pdl_new_y ;subtract left paddle Y position (3-130)
1a2d: 38 @HitCommon sec
1a2e: e9 0c sbc #12 ;subtract 12 to get signed delta from center
1a30: 0a asl A ; (should be roughly [-12,+12] now)
1a31: 0a asl A ;multiply by 4 (roughly [-48,+48])
1a32: 65 e5 adc ball_yvel ;add to ball Y velocity (carry matches sign)
1a34: 50 08 bvc @GoodVel ;sign didn't overflow, branch
1a36: b0 04 bcs @ClampToNeg ;sign changed; if went from neg to pos, branch
1a38: a9 70 lda #$70 ;clamp to +112
1a3a: d0 02 bne @GoodVel
1a3c: a9 8f @ClampToNeg lda #$8f ;clamp to -113
1a3e: 85 e5 @GoodVel sta ball_yvel
1a40: 30 09 bmi @MovingUp ;if moving upward, branch
1a42: c9 78 cmp #$78 ;is velocity > 120?
1a44: 30 0d bmi @Return ;no, bail
1a46: a9 78 lda #$78 ;yes, clamp to 120
1a48: 85 e5 sta ball_yvel
1a4a: 60 rts
1a4b: c9 88 @MovingUp cmp #$88 ;is velocity < -120?
1a4d: 10 04 bpl @Return ;no, bail
1a4f: a9 88 lda #$88 ;yes, clamp to -120
1a51: 85 e5 sta ball_yvel
1a53: 60 @Return rts
1a54: a5 e1 @HitRight lda ball_yc ;get ball Y coordinate (0-159)
1a56: 38 sec
1a57: e5 42 sbc right_pdl_new_y ;subtract right paddle Y position (3-130)
1a59: 4c 2d 1a jmp @HitCommon
;
; CALLL 6748 - plays game, without gravity.
;
; Continues until a point is scored or ESC is hit.
;
1a5c: 20 04 18 PlayGameNG jsr RedrawBall ;erase and redraw the ball
1a5f: 20 5b 19 jsr MoveBall ;update the ball's position
1a62: 20 8e 18 jsr CheckCollision ;check for and handle collisions
1a65: a5 ea lda pdl_mv_ctr ;time to update the paddles?
1a67: d0 07 bne @SkipUpdate ;not yet, branch
1a69: a9 0f @_diff1 lda #$0f ;POKE 6762,DFF
1a6b: 85 ea sta pdl_mv_ctr ;reset counter
1a6d: 20 7e 19 jsr UpdatePaddles ;update player paddle positions
1a70: c6 ea @SkipUpdate dec pdl_mv_ctr
; Check to see if a point has been scored.
1a72: 20 bd 1a @_call1 jsr TestVictoryNH ;POKE 6771,189 or 203 ($1abd or $1acb)
1a75: ad 00 c0 lda KBD ;was a key hit?
1a78: 10 e2 bpl PlayGameNG ;no, keep going
1a7a: 2c 10 c0 bit KBDSTRB ;clear it
1a7d: c9 9b cmp #$9b ;was it ESC?
1a7f: d0 db bne PlayGameNG ;no, keep going
1a81: 60 rts
;
; CALL 6786 - plays game, with gravity.
;
; Continues until a point is scored or ESC is hit.
;
1a82: 20 04 18 PlayGameG jsr RedrawBall ;erase and redraw the ball
1a85: 20 5b 19 jsr MoveBall ;update the ball's position
1a88: 20 8e 18 jsr CheckCollision ;check for and handle collisions
1a8b: a5 ea lda pdl_mv_ctr ;time to update the paddles?
1a8d: d0 07 bne @SkipUpdate ;not yet, branch
1a8f: a9 0f @_diff2 lda #$0f ;POKE 6800,DFF
1a91: 85 ea sta pdl_mv_ctr ;reset counter
1a93: 20 7e 19 jsr UpdatePaddles ;update player paddle positions
1a96: c6 ea @SkipUpdate dec pdl_mv_ctr ;update counter
; Apply gravity.
1a98: a5 d0 lda gravity_ctr ;time to apply gravity?
1a9a: d0 0f bne @NotYet ;no, branch
1a9c: a9 05 lda #$05
1a9e: 85 d0 sta gravity_ctr ;reset counter; we adjust Y vel every 5 frames
1aa0: a5 e5 lda ball_yvel
1aa2: 18 clc
1aa3: 69 01 adc #$01 ;increment Y velocity
1aa5: 85 e5 sta ball_yvel
1aa7: 50 02 bvc @NotYet ;math didn't overflow, branch
1aa9: c6 e5 dec ball_yvel ;decrement back to max vel
1aab: c6 d0 @NotYet dec gravity_ctr ;update counter
; Check to see if a point has been scored.
1aad: 20 bd 1a @_call2 jsr TestVictoryNH ;POKE 6830,189 or 203 ($1abd or $1acb)
1ab0: ad 00 c0 lda KBD ;was a key hit?
1ab3: 10 cd bpl PlayGameG ;no, keep going
1ab5: 2c 10 c0 bit KBDSTRB ;clear it
1ab8: c9 9b cmp #$9b ;was it ESC?
1aba: d0 c6 bne PlayGameG ;no, keep going
1abc: 60 rts
;
; Called from self-modifying code.
;
; Used for non-HOCKEY fields. Score a point if the ball reaches the edge of the
; screen.
;
1abd: a5 e0 TestVictoryNH lda ball_xc ;get X coord
1abf: c9 01 cmp #1 ;reached left edge of screen?
1ac1: d0 03 bne @NotLeft ;no, branch
1ac3: 68 @Scored pla ;yes, pop so we return to BASIC
1ac4: 68 pla
1ac5: 60 rts
1ac6: c9 fe @NotLeft cmp #254 ;reached right edge of screen?
1ac8: f0 f9 beq @Scored ;yes, branch
1aca: 60 rts
;
; Called from self-modifying code.
;
; Used for HOCKEY field. Score a point if the ball reaches the back of the goal
; area.
;
1acb: a5 e0 TestVictoryH lda ball_xc ;get X coord
1acd: c9 23 cmp #35 ;reached leftmost goal?
1acf: f0 05 beq @CheckY ;yes, check Y
1ad1: c9 dd cmp #221 ;reached rightmost goal?
1ad3: f0 01 beq @CheckY ;yes, check Y
1ad5: 60 rts
1ad6: a5 e1 @CheckY lda ball_yc ;get Y coord
1ad8: c9 36 cmp #54 ;below top of goal area?
1ada: 10 01 bpl @CheckBot ;yes, branch
1adc: 60 rts
1add: c9 68 @CheckBot cmp #104 ;above bottom of goal area?
1adf: 30 e2 bmi @Scored ;yes, branch
1ae1: 60 rts
;
; CALL 6882 - erases the ball. Called after point scored.
;
1ae2: c6 e1 EraseBall dec ball_yc ;erase at Y-1
1ae4: a9 00 lda #$00 ;set flag to erase mode
1ae6: 85 04 sta draw_flag
1ae8: 20 bc 17 jsr DrawEraseBallRow ;erase row
1aeb: e6 e1 inc ball_yc ;erase at Y
1aed: a9 00 lda #$00 ;no additional Y offset
1aef: 20 bc 17 jsr DrawEraseBallRow
1af2: e6 e1 inc ball_yc ;erase at Y+1
1af4: a9 00 lda #$00 ;no additional Y offset
1af6: 20 bc 17 jsr DrawEraseBallRow
1af9: 60 rts
;
; Draws a single dot. Used by the line-drawing code.
;
; On entry:
; $00-01: hi-res line pointer
; $02: hi-res byte offset (0-39)
; $04: high-bit set flag ($00/$80)
; $05: color index (0-3)
;
]hi_color_flag .var $04 {addr/1}
]color .var $05 {addr/1}
1afa: a5 02 DrawLineDot lda byte_offset ;get offset into hi-res line
1afc: 29 01 and #$01 ;odd or even?
1afe: 18 clc
1aff: 06 05 asl ]color ;shift the color value left to make room
1b01: 65 05 adc ]color ;merge in the odd/even bit
1b03: 46 05 lsr ]color ;undo the change to ZP value
1b05: a8 tay ;use combined value as index (0-7)
1b06: b9 14 1b lda color_bytes,y ;get color pattern
1b09: 25 03 and pixel_mask ;mask to get just the bits we want
1b0b: 05 04 ora ]hi_color_flag ;set the high bit if requested
1b0d: a4 02 ldy byte_offset ;get offset into hi-res line
1b0f: 11 00 ora (line_ptr),y ;merge the pixels
1b11: 91 00 sta (line_ptr),y
1b13: 60 rts
;
; Hi-res color patterns. Take a color value 0-3, double it, and add one if
; we're drawing in an odd column. Black, purple, green, white (high bit clear)
; or black, blue, orange, white (high bit set).
;
1b14: 00 00 55 2a+ color_bytes .bulk $00,$00,$55,$2a,$2a,$55,$ff,$ff
;
; Draw horizontal line. Used by game field drawing code.
;
; On entry:
; $d1: initial X coordinate
; $d2: initial Y coordinate
; $d3: line length
;
1b1c: a5 d2 DrawHorizLine lda line_st_y ;get row
1b1e: 20 10 16 jsr GetLineAddr ;compute address
1b21: a5 d1 lda line_st_x ;get initial X coordinate
1b23: 85 d4 @Loop sta line_xc
1b25: 20 75 16 jsr CalcOffAndMask ;calculate line offset and pixel mask
1b28: 20 fa 1a jsr DrawLineDot ;draw it
1b2b: c6 d3 dec line_len ;count down
1b2d: f0 07 beq @Return ;branch if done
1b2f: a5 d4 lda line_xc ;advance to next pixel position
1b31: 18 clc
1b32: 69 01 adc #$01
1b34: d0 ed bne @Loop ;(always... unless we hit right edge of screen)
1b36: 60 @Return rts
;
; Draw vertical line. Used by game field drawing code.
;
; On entry:
; $d1: initial X coordinate
; $d2: initial Y coordinate
; $d3: line length
;
1b37: a5 d1 DrawVertLine lda line_st_x ;get column
1b39: 20 75 16 jsr CalcOffAndMask ;calculate line offset and pixel mask
1b3c: a5 d2 lda line_st_y ;get initial Y coordinate
1b3e: 85 d5 @Loop sta line_yc
1b40: 20 10 16 jsr GetLineAddr ;compute address
1b43: 20 fa 1a jsr DrawLineDot ;draw a dot
1b46: c6 d3 dec line_len ;count down
1b48: f0 07 beq @Return ;branch if done
1b4a: a5 d5 lda line_yc ;advance to next line
1b4c: 18 clc
1b4d: 69 01 adc #$01
1b4f: d0 ed bne @Loop ;(always)
1b51: 60 @Return rts
;
; CALL 6994 - draw game field.
;
; The BASIC program sets a pointer to field data in 224/225 ($e0/e1).
;
]board_ptr .var $e0 {addr/2}
1b52: a0 00 DrawField ldy #$00
1b54: b1 e0 @Loop lda (]board_ptr),y ;get initial X coord from data
1b56: 85 d1 sta line_st_x
1b58: c8 iny
1b59: b1 e0 lda (]board_ptr),y ;get initial Y coord
1b5b: 85 d2 sta line_st_y
1b5d: c8 iny
1b5e: b1 e0 lda (]board_ptr),y ;get line length
1b60: 85 d3 sta line_len
1b62: c8 iny
1b63: b1 e0 lda (]board_ptr),y ;get direction / color flag
1b65: f0 2e beq @Return ;zero means end of list
1b67: c8 iny
1b68: 0a asl A ;shift left to check hi bit (bit 7)
1b69: 90 06 bcc @ClearFlag ;(the value set is not actually used)
1b6b: a2 80 ldx #$80
1b6d: 86 04 stx ]hi_color_flag
1b6f: d0 04 bne @Cont ;(always)
1b71: a2 00 @ClearFlag ldx #$00
1b73: 86 04 stx ]hi_color_flag
1b75: 0a @Cont asl A ;shift left to check bit 6
1b76: 90 0f bcc @DoHoriz ;0=horizontal, 1=vertical
1b78: 18 clc
1b79: 4a lsr A ;shift the bits back, leaving only the color value
1b7a: 4a lsr A
1b7b: 85 05 sta ]color
1b7d: 98 tya ;preserve array index
1b7e: 48 pha
1b7f: 20 37 1b jsr DrawVertLine ;draw line
1b82: 68 pla ;restore index
1b83: a8 tay
1b84: 4c 54 1b jmp @Loop
1b87: 4a @DoHoriz lsr A ;shift the bits back, leaving only the color value
1b88: 4a lsr A
1b89: 85 05 sta ]color
1b8b: 98 tya ;preserve array index
1b8c: 48 pha
1b8d: 20 1c 1b jsr DrawHorizLine ;draw line
1b90: 68 pla ;restore index
1b91: a8 tay
1b92: 4c 54 1b jmp @Loop
1b95: 60 @Return rts
;
; CALL 7062 - erases the hi-res screen and displays it, in mixed mode.
;
• Clear variables
1b96: a9 20 ClearHiRes lda #$20 ;set pointer to $2000
1b98: 85 1b sta hptr+1
1b9a: a0 00 ldy #$00
1b9c: 84 1a sty hptr
1b9e: 98 @Loop1 tya
1b9f: 91 1a @Loop sta (hptr),y
1ba1: c8 iny
1ba2: d0 fb bne @Loop
1ba4: e6 1b inc hptr+1 ;advance to next page
1ba6: a5 1b lda hptr+1
1ba8: 29 1f and #$1f ;done full screen?
1baa: d0 f2 bne @Loop1 ;not yet, branch
1bac: 8d 57 c0 sta HIRES ;select hi-res screen
1baf: 8d 53 c0 sta MIXSET ;mix 4 lines of text
1bb2: 8d 50 c0 sta TXTCLR ;enable graphics
1bb5: 60 rts
;
; Game field data. These are a series of four-byte entries that describe lines:
;
; - initial X coordinate
; - initial Y coordinate
; - length of line; $00=256
; - line color and direction: bits HD0000CC
; H: color high-bit flag; if set, pixel mask has $80 added
; D: direction flag, 0=horiz, 1=vert
; CC: color (0-3)
;
; A value of zero in the fourth byte indicates the end of the list.
;
;
; Field #1, "BUMPER POOL".
;
1bb6: 00 00 00 01 field1 .bulk $00,$00,$00,$01
1bba: 00 9f 00 01 .bulk $00,$9f,$00,$01
1bbe: 00 00 38 41 .bulk $00,$00,$38,$41
1bc2: 00 68 38 41 .bulk $00,$68,$38,$41
1bc6: fe 00 38 41 .bulk $fe,$00,$38,$41
1bca: fe 68 38 41 .bulk $fe,$68,$38,$41
1bce: 21 36 0b 42 .bulk $21,$36,$0b,$42
1bd2: 21 5e 0b 42 .bulk $21,$5e,$0b,$42
1bd6: 2b 36 0b 42 .bulk $2b,$36,$0b,$42
1bda: 2b 5e 0b 42 .bulk $2b,$5e,$0b,$42
1bde: 21 36 0b 02 .bulk $21,$36,$0b,$02
1be2: 21 5e 0b 02 .bulk $21,$5e,$0b,$02
1be6: 21 40 0b 02 .bulk $21,$40,$0b,$02
1bea: 21 68 0b 02 .bulk $21,$68,$0b,$02
1bee: 65 36 0b 42 .bulk $65,$36,$0b,$42
1bf2: 65 5e 0b 42 .bulk $65,$5e,$0b,$42
1bf6: 71 36 0b 42 .bulk $71,$36,$0b,$42
1bfa: 71 5e 0b 42 .bulk $71,$5e,$0b,$42
1bfe: 79 4a 0b 42 .bulk $79,$4a,$0b,$42
1c02: 85 4a 0b 42 .bulk $85,$4a,$0b,$42
1c06: 8f 36 0b 42 .bulk $8f,$36,$0b,$42
1c0a: 8f 5e 0b 42 .bulk $8f,$5e,$0b,$42
1c0e: 9b 36 0b 42 .bulk $9b,$36,$0b,$42
1c12: 9b 5e 0b 42 .bulk $9b,$5e,$0b,$42
1c16: 65 36 0c 02 .bulk $65,$36,$0c,$02
1c1a: 65 40 0c 02 .bulk $65,$40,$0c,$02
1c1e: 65 5e 0c 02 .bulk $65,$5e,$0c,$02
1c22: 65 68 0c 02 .bulk $65,$68,$0c,$02
1c26: 79 4a 0c 02 .bulk $79,$4a,$0c,$02
1c2a: 79 54 0c 02 .bulk $79,$54,$0c,$02
1c2e: 8f 36 0c 02 .bulk $8f,$36,$0c,$02
1c32: 8f 40 0c 02 .bulk $8f,$40,$0c,$02
1c36: 8f 5e 0c 02 .bulk $8f,$5e,$0c,$02
1c3a: 8f 68 0c 02 .bulk $8f,$68,$0c,$02
1c3e: d1 36 0b 42 .bulk $d1,$36,$0b,$42
1c42: d1 5e 0b 42 .bulk $d1,$5e,$0b,$42
1c46: db 36 0b 42 .bulk $db,$36,$0b,$42
1c4a: db 5e 0b 42 .bulk $db,$5e,$0b,$42
1c4e: d1 36 0b 02 .bulk $d1,$36,$0b,$02
1c52: d1 5e 0b 02 .bulk $d1,$5e,$0b,$02
1c56: d1 40 0b 02 .bulk $d1,$40,$0b,$02
1c5a: d1 68 0b 02 .bulk $d1,$68,$0b,$02
1c5e: 00 00 00 00 .bulk $00,$00,$00,$00
;
; Field #2, "HOCKEY". Points are scored when the ball hits the back of the net,
; not the edge of the screen, so a separate victory condition routine is needed.
;
1c62: 00 00 00 81 field2 .bulk $00,$00,$00,$81
1c66: 00 9f 00 81 .bulk $00,$9f,$00,$81
1c6a: 00 00 a0 c1 .bulk $00,$00,$a0,$c1
1c6e: fe 00 a0 c1 .bulk $fe,$00,$a0,$c1
1c72: 21 36 33 42 .bulk $21,$36,$33,$42
1c76: df 36 33 42 .bulk $df,$36,$33,$42
1c7a: 21 36 19 02 .bulk $21,$36,$19,$02
1c7e: c7 36 19 02 .bulk $c7,$36,$19,$02
1c82: 21 68 19 02 .bulk $21,$68,$19,$02
1c86: c7 68 19 02 .bulk $c7,$68,$19,$02
1c8a: 40 10 19 02 .bulk $40,$10,$19,$02
1c8e: a7 10 19 02 .bulk $a7,$10,$19,$02
1c92: 40 8f 19 02 .bulk $40,$8f,$19,$02
1c96: a7 8f 19 02 .bulk $a7,$8f,$19,$02
1c9a: 00 00 00 00 .bulk $00,$00,$00,$00
;
; Field #3, "SCRAMBLE".
;
1c9e: 00 00 00 82 field3 .bulk $00,$00,$00,$82
1ca2: 00 9f 00 82 .bulk $00,$9f,$00,$82
1ca6: 01 00 38 c2 .bulk $01,$00,$38,$c2
1caa: 01 68 38 c2 .bulk $01,$68,$38,$c2
1cae: ff 00 38 c2 .bulk $ff,$00,$38,$c2
1cb2: ff 68 38 c2 .bulk $ff,$68,$38,$c2
1cb6: 10 10 29 01 .bulk $10,$10,$29,$01
1cba: 10 20 29 01 .bulk $10,$20,$29,$01
1cbe: 10 7f 29 01 .bulk $10,$7f,$29,$01
1cc2: 10 8f 29 01 .bulk $10,$8f,$29,$01
1cc6: c8 10 29 01 .bulk $c8,$10,$29,$01
1cca: c8 20 29 01 .bulk $c8,$20,$29,$01
1cce: c8 7f 29 01 .bulk $c8,$7f,$29,$01
1cd2: c8 8f 29 01 .bulk $c8,$8f,$29,$01
1cd6: 48 40 10 02 .bulk $48,$40,$10,$02
1cda: a8 40 10 02 .bulk $a8,$40,$10,$02
1cde: 48 60 10 02 .bulk $48,$60,$10,$02
1ce2: a8 60 10 02 .bulk $a8,$60,$10,$02
1ce6: 59 40 21 42 .bulk $59,$40,$21,$42
1cea: 79 40 21 42 .bulk $79,$40,$21,$42
1cee: 89 40 21 42 .bulk $89,$40,$21,$42
1cf2: a9 40 21 42 .bulk $a9,$40,$21,$42
1cf6: 00 00 00 00 .bulk $00,$00,$00,$00
;
; Field #4, TENNIS.
;
1cfa: 00 00 00 01 field4 .bulk $00,$00,$00,$01
1cfe: 00 9f 00 01 .bulk $00,$9f,$00,$01
1d02: 7d 50 50 42 .bulk $7d,$50,$50,$42
1d06: 81 50 50 42 .bulk $81,$50,$50,$42
1d0a: 7d 50 05 02 .bulk $7d,$50,$05,$02
1d0e: 00 00 00 00 .bulk $00,$00,$00,$00
;
; Clear one pixel. Used for title animation.
;
; On entry:
; $00: address of hi-res line
; $02: byte offset in line
; $03: pixel bit mask
;
1d12: a4 02 ClearPixel ldy byte_offset
1d14: a5 03 lda pixel_mask
1d16: 49 ff eor #$ff ;invert, to make it an AND mask
1d18: 31 00 and (line_ptr),y
1d1a: 91 00 sta (line_ptr),y
1d1c: 60 rts
;
; Set one pixel. Used for title animation.
;
; On entry:
; $00: address of hi-res line
; $02: byte offset in line
; $03: pixel bit mask
;
1d1d: a4 02 SetPixel ldy byte_offset
1d1f: a5 03 lda pixel_mask
1d21: 11 00 ora (line_ptr),y
1d23: 91 00 sta (line_ptr),y
1d25: 60 rts
;
; CALL 7462 - show the hi-res animated title.
;
; We animate 128 individual particles, bouncing them off of an invisible
; bounding box. The movement was designed so that the pixels will return to
; their original positions all at once.
;
; This has a bug that causes a few pixels to be missing from the final frame.
; It walks through the list, erasing each pixel and then drawing it in its new
; position. If a new pixel overlaps with the old position of a different pixel,
; the other pixel's erase can clear the new pixel. This could have been avoided
; by using XOR rendering.
;
1d26: a0 00 AnimTitle ldy #$00 ;init particle index
1d28: 98 @AnimLoop tya ;save it on stack
1d29: 48 pha
1d2a: b9 b4 1d lda title_xy,y ;get particle X coordinate
1d2d: 85 e0 sta ball_xc ;save copy
1d2f: 20 75 16 jsr CalcOffAndMask ;calculate current byte offset and bit mask
1d32: 68 pla
1d33: a8 tay ;restore particle index
1d34: a5 e0 lda ball_xc ;get X coordinate
1d36: 18 clc
1d37: 79 b4 1e adc vectors,y ;add vector
1d3a: 99 b4 1d sta title_xy,y ;update particle X position
1d3d: c9 50 cmp #80 ;have we reached left edge of box?
1d3f: d0 0d bne @NotLeft ;no, branch
; Invert movement vector X component.
1d41: b9 b4 1e @InvertVX lda vectors,y ;get vector X
1d44: 49 ff eor #$ff ;do 2's complement negation
1d46: 18 clc
1d47: 69 01 adc #$01
1d49: 99 b4 1e sta vectors,y ;update value
1d4c: d0 04 bne @MoveY ;(always)
1d4e: c9 a0 @NotLeft cmp #160 ;have we reached right edge of box?
1d50: f0 ef beq @InvertVX ;yes, bounce
1d52: c8 @MoveY iny ;advance particle index to Y component
1d53: 98 tya ;save it on stack
1d54: 48 pha
1d55: b9 b4 1d lda title_xy,y ;get particle Y component
1d58: 85 e1 sta ball_yc ;save copy
1d5a: 20 10 16 jsr GetLineAddr ;calculate current line address
1d5d: 20 12 1d jsr ClearPixel ;erase current position
1d60: 68 pla ;restore particle index
1d61: a8 tay
1d62: a5 e1 lda ball_yc ;get Y coordinate
1d64: 18 clc
1d65: 79 b4 1e adc vectors,y ;add vector
1d68: 99 b4 1d sta title_xy,y ;update particle Y position
1d6b: c9 30 cmp #48 ;have we reached top edge of box?
1d6d: d0 0d bne @NotTop ;no, branch
; Invert movement vector Y component.
1d6f: b9 b4 1e @InvertVY lda vectors,y ;get vector Y
1d72: 49 ff eor #$ff ;do 2's complement negation
1d74: 18 clc
1d75: 69 01 adc #$01
1d77: 99 b4 1e sta vectors,y ;update value
1d7a: d0 04 bne @Next ;(always)
1d7c: c9 80 @NotTop cmp #128 ;have we reached bottom edge of box?
1d7e: f0 ef beq @InvertVY ;yes, bounce
1d80: 88 @Next dey ;decrement particle index (back to X coord)
1d81: 98 tya ;save it on stack
1d82: 48 pha
1d83: b9 b4 1d lda title_xy,y ;get new X coordinate
1d86: 20 75 16 jsr CalcOffAndMask ;calculate byte offset and bit mask
1d89: 68 pla ;restore particle index
1d8a: a8 tay
1d8b: c8 iny ;advance to Y coordinate
1d8c: 98 tya ;save it on stack
1d8d: 48 pha
1d8e: b9 b4 1d lda title_xy,y ;get new Y coordinate
1d91: 20 10 16 jsr GetLineAddr ;calculate line address
1d94: 20 1d 1d jsr SetPixel ;draw the pixel
1d97: 68 pla ;restore particle index
1d98: a8 tay
1d99: c8 iny ;advance to next particle
1d9a: d0 8c bne @AnimLoop ;branch if we have more to do
; See if we've returned to the initial state by comparing the first four pixels
; to their initial values.
1d9c: a2 00 ldx #$00
1d9e: bd b4 1d @CmpLoop lda title_xy,x
1da1: dd ac 1d cmp pos_check,x
1da4: d0 82 bne @AnimLoop ;found a mismatch, keep going
1da6: e8 inx
1da7: e0 08 cpx #8
1da9: 30 f3 bmi @CmpLoop
1dab: 60 rts ;done
;
; Initial position of the first four pixels. When these match, we're done.
;
1dac: 67 40 67 41+ pos_check .bulk $67,$40,$67,$41,$67,$42,$67,$43
;
; X/Y coordinates of the individual pixels that make up the "Penny Arcade" title
; text. There are 128 individual pixels, stored with the X coordinate followed
; by the Y coordinate.
;
; These values are updated during the animation. If the animation runs to
; completion, the values will be returned to their initial state.
;
1db4: 67 40 67 41+ title_xy .bulk $67,$40,$67,$41,$67,$42,$67,$43
1dbc: 67 44 67 45+ .bulk $67,$44,$67,$45,$67,$46,$68,$40
1dc4: 68 43 69 40+ .bulk $68,$43,$69,$40,$69,$43,$6a,$41
1dcc: 6a 42 6d 43+ .bulk $6a,$42,$6d,$43,$6d,$44,$6d,$45
1dd4: 6e 42 6e 44+ .bulk $6e,$42,$6e,$44,$6e,$46,$6f,$42
1ddc: 6f 44 6f 46+ .bulk $6f,$44,$6f,$46,$70,$43,$70,$44
1de4: 73 42 73 43+ .bulk $73,$42,$73,$43,$73,$44,$73,$45
1dec: 73 46 74 42+ .bulk $73,$46,$74,$42,$75,$42,$76,$43
1df4: 76 44 76 45+ .bulk $76,$44,$76,$45,$76,$46,$79,$42
1dfc: 79 43 79 44+ .bulk $79,$43,$79,$44,$79,$45,$79,$46
1e04: 7a 42 7b 42+ .bulk $7a,$42,$7b,$42,$7c,$43,$7c,$44
1e0c: 7c 45 7c 46+ .bulk $7c,$45,$7c,$46,$7f,$42,$7f,$43
1e14: 7f 44 7f 45+ .bulk $7f,$44,$7f,$45,$7f,$49,$80,$46
1e1c: 80 48 81 47+ .bulk $80,$48,$81,$47,$82,$42,$82,$43
1e24: 82 44 82 45+ .bulk $82,$44,$82,$45,$82,$46,$6d,$4c
1e2c: 6d 4d 6d 4e+ .bulk $6d,$4d,$6d,$4e,$6d,$4f,$6d,$50
1e34: 6d 51 6e 4b+ .bulk $6d,$51,$6e,$4b,$6e,$4e,$6f,$4b
1e3c: 6f 4e 70 4c+ .bulk $6f,$4e,$70,$4c,$70,$4d,$70,$4e
1e44: 70 4f 70 50+ .bulk $70,$4f,$70,$50,$70,$51,$73,$4d
1e4c: 73 4e 73 4f+ .bulk $73,$4e,$73,$4f,$73,$50,$73,$51
1e54: 74 4d 75 4d+ .bulk $74,$4d,$75,$4d,$76,$4e,$79,$4e
1e5c: 79 4f 79 50+ .bulk $79,$4f,$79,$50,$7a,$4d,$7a,$51
1e64: 7b 4d 7b 51+ .bulk $7b,$4d,$7b,$51,$7c,$4e,$7f,$4e
1e6c: 7f 4f 7f 50+ .bulk $7f,$4f,$7f,$50,$80,$4d,$80,$51
1e74: 81 4d 81 51+ .bulk $81,$4d,$81,$51,$82,$4d,$82,$4e
1e7c: 82 4f 82 50+ .bulk $82,$4f,$82,$50,$82,$51,$85,$4e
1e84: 85 4f 85 50+ .bulk $85,$4f,$85,$50,$86,$4d,$86,$51
1e8c: 87 4d 87 51+ .bulk $87,$4d,$87,$51,$88,$4b,$88,$4c
1e94: 88 4d 88 4e+ .bulk $88,$4d,$88,$4e,$88,$4f,$88,$50
1e9c: 88 51 8b 4e+ .bulk $88,$51,$8b,$4e,$8b,$4f,$8b,$50
1ea4: 8c 4d 8c 4f+ .bulk $8c,$4d,$8c,$4f,$8c,$51,$8d,$4d
1eac: 8d 4f 8d 51+ .bulk $8d,$4f,$8d,$51,$8e,$4e,$8e,$4f
;
; Movement vectors. Each entry is two bytes, one for X and one for Y. Each
; value is either +1 or -1. These are modified during the animation, but will
; have returned to their initial configuration when the animation completes.
;
; The vectors repeat every 4 pixels, which is why the title appears to split
; into 4 pieces.
;
1eb4: 01 01 01 ff+ vectors .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1ebc: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1ec4: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1ecc: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1ed4: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1edc: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1ee4: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1eec: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1ef4: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1efc: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f04: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f0c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f14: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f1c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f24: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f2c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f34: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f3c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f44: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f4c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f54: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f5c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f64: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f6c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f74: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f7c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f84: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f8c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f94: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1f9c: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1fa4: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
1fac: 01 01 01 ff+ .bulk $01,$01,$01,$ff,$ff,$01,$ff,$ff
;
; The rest of the file is junk. The last few bytes sit on the hi-res screen.
;
1fb4: 8e fd 20 ce+ .junk 76
2000: 55 2a 55 2a+ .junk 17
.adrend ↑ $0801