git @ Cat's Eye Technologies SixtyPical / 0.19 eg / atari2600 / atari-2600-example.oph
0.19

Tree @0.19 (Download .tar.gz)

atari-2600-example.oph @0.19raw · history · blame

;
; atari-2600-example.oph
; Skeleton code for an Atari 2600 ROM,
;   plus an example of reading the joystick.
; By Chris Pressey, November 2, 2012.
;
; This work is in the public domain.  See the file UNLICENSE for more info.
;
; Based on Chris Cracknell's Atari 2600 clock (also in the public domain):
;   http://everything2.com/title/An+example+of+Atari+2600+source+code
;
; to build and run in Stella:
;   ophis atari-2600-example.oph -o example.bin
;   stella example.bin
;
; More useful information can be found in the Stella Programmer's Guide:
;   http://alienbill.com/2600/101/docs/stella.html
;

;
; Useful system addresses (TODO: briefly describe each of these.)
;

.alias VSYNC          $00
.alias VBLANK         $01
.alias WSYNC          $02
.alias NUSIZ0         $04
.alias NUSIZ1         $05
.alias COLUPF         $08
.alias COLUBK         $09
.alias PF0            $0D
.alias PF1            $0E
.alias PF2            $0F
.alias SWCHA          $280
.alias INTIM          $284
.alias TIM64T         $296
.alias CTRLPF         $0A
.alias COLUP0         $06
.alias COLUP1         $07
.alias GP0            $1B
.alias GP1            $1C
.alias HMOVE          $2a
.alias RESP0          $10
.alias RESP1          $11

;
; Cartridge ROM occupies the top 4K of memory ($F000-$FFFF).
; Thus, typically, the program will occupy all that space too.
;
; Zero-page RAM we can use with impunity starts at $80 and goes
; upward (at least until $99, but probably further.)
;

.alias colour         $80
.alias luminosity     $81
.alias joystick_delay $82

.org  $F000

;
; Standard prelude for Atari 2600 cartridge code.
;
; Get various parts of the machine into a known state:
;
; - Disable interrupts
; - Clear the Decimal flag
; - Initialize the Stack Pointer
; - Zero all bytes in Zero Page memory
;

start:
        sei
        cld
        ldx #$FF
        txs
        lda #$00

zero_loop:
        sta $00, x
        dex
        bne zero_loop

        ; and fall through to...

;
; Initialization.
;
; - Clear the Playfield Control register.
; - Set the player (sprite) colour to light green (write to COLUP0.)
; - Set the player (sprite) size/repetion to normal (write to NUSIZ0.)
;

        lda #$00
        sta CTRLPF
        lda #$0c
        sta colour
        lda #$0a
        sta luminosity
        lda #$00
        sta NUSIZ0

        ; and fall through to...

;
; Main loop.
;
; A typical main loop consists of:
; - Waiting for the frame to start (vertical blank period)
; - Displaying stuff on the screen (the _display kernel_)
; - Doing any processing you like (reading joysticks, updating program state,
;   etc.), as long as you get it all done before the next frame starts!
;

main:
        jsr vertical_blank
        jsr display_frame
        jsr read_joystick
        jmp main

;
; Vertical blank routine.
;
; In brief: wait until it is time for the next frame of video.
; TODO: describe this in more detail.
;

vertical_blank:
        ldx #$00
        lda #$02
        sta WSYNC
        sta WSYNC
        sta WSYNC
        sta VSYNC
        sta WSYNC
        sta WSYNC
        lda #$2C
        sta TIM64T
        lda #$00
        sta WSYNC
        sta VSYNC
        rts

;
; Display kernal.
;
; First, wait until it's time to display the frame.
;

.scope
display_frame:
        lda INTIM
        bne display_frame

;
; (After that loop finishes, we know the accumulator must contain 0.)
; Wait for the next scanline, zero HMOVE (for some reason; TODO discover
; this), then turn on the screen.
;

        sta WSYNC
        sta HMOVE
        sta VBLANK

;
; Actual work in the display kernal is done here.
;
; This is a pathological approach to writing a display kernal.
; This wouldn't be how you'd do things in a game.  So be it.
; One day I may improve it.  For now, be happy that it displays
; anything at all!
;

;
; Wait for $3f (plus one?) scan lines to pass, by waiting for
; WSYNC that many times.
;

        ldx #$3F
_wsync_loop:
        sta WSYNC
        dex
        bpl _wsync_loop
        sta WSYNC

;
; Delay while the raster scans across the screen.  The more
; we delay here, the more to the right the player will be when
; we draw it.
;

        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop

;
; OK, *now* display the player.
;
        
        sta RESP0

;
; Loop over the rows of the sprite data, drawing each to the screen
; over four scan lines.
;
; TODO understand this better and describe it!
;

        ldy #$07
_image_loop:
        lda image_data, y
        sta GP0

        sta WSYNC
        sta WSYNC
        sta WSYNC
        sta WSYNC
        dey
        bpl _image_loop

        lda #$00
        sta GP0

;
; Turn off screen display and clear display registers.
;

        lda #$02
        sta WSYNC
        sta VBLANK
        lda #$00
        sta PF0
        sta PF1
        sta PF2
        sta COLUPF
        sta COLUBK

        rts
.scend


;
; Read the joystick and use it to modify the colour and luminosity
; of the player.
;

.scope
read_joystick:
        lda joystick_delay
        beq _continue

        dec joystick_delay
        rts

_continue:
        lda SWCHA
        and #$f0
        cmp #$e0
        beq _up
        cmp #$d0
        beq _down
        cmp #$b0
        beq _left
        cmp #$70
        beq _right
        jmp _tail

_up:     
        inc luminosity
        jmp _tail
_down:
        dec luminosity
        jmp _tail
_left:
        dec colour
        jmp _tail
_right:
        inc colour
        ;jmp _tail

_tail:
        lda colour
        and #$0f
        sta colour

        lda luminosity
        and #$0f
        sta luminosity

        lda colour
        clc
        rol
        rol
        rol
        rol
        ora luminosity
        sta COLUP0

        lda #$06
        sta joystick_delay

        rts
.scend

;
; Player (sprite) data.
;
; Because we loop over these bytes with the Y register counting *down*,
; this image is stored "upside-down".
;

image_data:
        .byte %01111110 
        .byte %10000001
        .byte %10011001
        .byte %10100101
        .byte %10000001
        .byte %10100101
        .byte %10000001
        .byte %01111110

;
; Standard postlude for Atari 2600 cartridge code.
; Give BRK and boot vectors that point to the start of the code.
;

.advance $FFFC
        .word start
        .word start