mirror of
https://github.com/michaelcmartin/Ophis.git
synced 2024-12-21 12:29:46 +00:00
Improved Atari 2600 example programs
Add the color test program as a sample program. Also update the hi_stella example so that it runs properly when run in a Harmony cartridge.
This commit is contained in:
parent
c3d48da59d
commit
778fbf7e2c
@ -1,7 +0,0 @@
|
||||
"Hi Stella" is a simple "Hello World" program for the "Stella" chip,
|
||||
more famously known as the Atari 2600. Simply running
|
||||
|
||||
ophis hi_stella.oph
|
||||
|
||||
should produce hi_stella.bin, a 256-byte file that prints "HI" on
|
||||
the screen with some rolling color bars.
|
13
examples/stella/README.txt
Normal file
13
examples/stella/README.txt
Normal file
@ -0,0 +1,13 @@
|
||||
"Hi Stella" is a simple "Hello World" program for the "Stella" chip,
|
||||
more famously known as the Atari 2600. Simply running
|
||||
|
||||
ophis hi_stella.oph
|
||||
|
||||
should produce hi_stella.bin, a 256-byte file that prints "HI" on
|
||||
the screen with some rolling color bars.
|
||||
|
||||
A more sophisticated program is colortest, which lets the user
|
||||
explore the 128 colors provided by the system. Use up and down
|
||||
to move the color value by 2, and left and right to move it
|
||||
by 16. (The lowest bit in the color value byte is ignored, for
|
||||
a total of 128 colors available.)
|
391
examples/stella/colortest.oph
Normal file
391
examples/stella/colortest.oph
Normal file
@ -0,0 +1,391 @@
|
||||
;;; ---------- COLOR TEST ----------
|
||||
;;; Michael Martin, 2014
|
||||
;;;
|
||||
;;; This is a sample program for the Atari 2600 VCS that lets you
|
||||
;;; explore the 128-color palette the system provides. This is
|
||||
;;; presented mainly as a more sophisticated example program to
|
||||
;;; supplement "hi_stella".
|
||||
;;;
|
||||
;;; It makes use of every graphical element but the Ball, and also
|
||||
;;; makes use of multicolor asymmetric playfields.
|
||||
|
||||
.require "../../platform/stella.oph"
|
||||
.outfile "colortest.bin"
|
||||
|
||||
.data
|
||||
.org $0080
|
||||
.space startcol 1 ; Starting color for the striped playfield
|
||||
.space subrow 1 ; Counting lines per "tall pixel"
|
||||
.space curcol 1 ; The color number we are focusing on at the moment
|
||||
.space high_nybble 2 ; Pointer to graphic data for 16s hexit
|
||||
.space low_nybble 2 ; Pointer to graphic data for ones hexit
|
||||
.space input_allowed 1 ; Flag for whether or not to ignore input.
|
||||
|
||||
.text
|
||||
;; Start at $f800 - 2KB ROMs are the smallest size available.
|
||||
.org $f800
|
||||
|
||||
reset: `clean'start
|
||||
|
||||
;; We offer 1c as the initial color. It's a nice yellow shade.
|
||||
lda #$1c
|
||||
sta curcol
|
||||
|
||||
|
||||
frame: `vertical'sync ; Beginning of the frame. Set up the timer
|
||||
lda #43 ; to count out the length of VBLANK while
|
||||
sta TIM64T ; we do the processing for the display.
|
||||
|
||||
;; Place the player and missile graphics appropriately. We
|
||||
;; count cycles and write the missile and player reset registers
|
||||
;; at the closest times we can manage. Due to the way the TIA
|
||||
;; timing works, the formula for the pixel they will show up at
|
||||
;; is N*3-55+P, where N is the number of cycles from the end of
|
||||
;; the latest STA WSYNC and the end of the STA RES* instruction,
|
||||
;; and P is 1 for player sprites and 0 for missiles and the ball.
|
||||
;;
|
||||
;; The line after that we can strobe HMOVE to adjust them the
|
||||
;; rest of the way into place.
|
||||
;;
|
||||
;; We will be using the missiles to draw the left and right
|
||||
;; sides of a largish square and the player sprites to display a
|
||||
;; byte value (the current color) as two hex digits (one per
|
||||
;; player). All the rest of our graphics will be done via the
|
||||
;; playfield registers.
|
||||
;;
|
||||
;; The missile graphics are being targeted to pixels 40 and 116
|
||||
;; and will be 4 pixels wide each. The player graphics will be
|
||||
;; 8 pixels wide and are targeting pixels 72 and 80.
|
||||
sta WSYNC
|
||||
sta WSYNC ; = 0
|
||||
ldy #$06 ; +2 = 2
|
||||
* dey ; +2 = 4- 9-14-19-24-29
|
||||
bne - ; +3 = 7-12-17-22-27-31
|
||||
sta RESM0 ; +3 = 34 (31*3-55 = 38. Needs to move 2 pixels right.)
|
||||
lda #$E0 ; +2 = 36
|
||||
sta HMCLR ; +3 = 39 - reset the fine-move registers
|
||||
sta HMM0 ; +3 = 42 - set M0 to move 2 right
|
||||
sta RESP0 ; +3 = 45 (42*3-54 = 72. Placed perfectly.)
|
||||
sta RESP1 ; +3 = 48 (45*3-54 = 81. Needs to move 1 pixel left.)
|
||||
lda #$10 ; +2 = 50
|
||||
sta HMP1 ; +3 = 53 - and set P1 to move 1 left.
|
||||
nop ; +2 = 55
|
||||
nop ; +2 = 57
|
||||
sta RESM1 ; +3 = 60 (57*3-55 = 116. Placed perfectly.)
|
||||
sta WSYNC
|
||||
sta HMOVE ; Next scanline, execute the fine moves.
|
||||
|
||||
lda #$20
|
||||
sta NUSIZ0 ; Quad-size missiles, single copy of single player
|
||||
sta NUSIZ1 ; M1 and P1 are the same
|
||||
|
||||
;; Read the input
|
||||
lda #$00
|
||||
sta SWACNT
|
||||
lda SWCHA
|
||||
bit input_allowed
|
||||
bmi true_input_read
|
||||
;; Wait for neutral stick so we can re-enable input.
|
||||
and #$f0
|
||||
cmp #$f0
|
||||
bne input_done
|
||||
;; Bits are set if the direction isn't active, so we only get
|
||||
;; here if the stick was neutral. this also means the accumulator
|
||||
;; has #$f0 in it now, which means we can store it directly and
|
||||
;; the BIT/BMI above will start succeeding next frame.
|
||||
sta input_allowed
|
||||
beq input_done
|
||||
true_input_read:
|
||||
;; Now we rotate it through the carry bit to see what
|
||||
;; direction was pushed. We advance the color 2 or 16 at a time,
|
||||
;; depending. (The least significant bit in the color register is
|
||||
;; the one ignored, so we are not missing anything here.)
|
||||
ror ; Skip P2 input
|
||||
ror
|
||||
ror
|
||||
ror
|
||||
ror ; Carry clear if up
|
||||
bcs +
|
||||
inc curcol ; If up, increase color by 2
|
||||
inc curcol
|
||||
jmp input_found
|
||||
* ror ; Carry clear if down
|
||||
bcs +
|
||||
dec curcol ; If down, decrease color by 2
|
||||
dec curcol
|
||||
jmp input_found
|
||||
* ror ; Carry clear if left
|
||||
bcs +
|
||||
lda curcol
|
||||
sec
|
||||
sbc #$10 ; Left decreases color by 16
|
||||
sta curcol
|
||||
jmp input_found
|
||||
* ror ; Carry clear if right
|
||||
bcs input_done
|
||||
lda curcol
|
||||
adc #$10 ; Right increases color by 16
|
||||
sta curcol
|
||||
input_found:
|
||||
lda #$00
|
||||
sta input_allowed
|
||||
|
||||
input_done:
|
||||
;; Clear the playfield while we wait, and make it asymmetric
|
||||
lda #$00
|
||||
sta PF0
|
||||
sta PF1
|
||||
sta PF2
|
||||
sta CTRLPF
|
||||
|
||||
;; alter playfield color so we get a rotating effect
|
||||
dec startcol
|
||||
|
||||
;; prepare numeric sprite values
|
||||
lda curcol
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
tay
|
||||
lda digits_low, y
|
||||
sta high_nybble
|
||||
lda curcol
|
||||
and #$0f
|
||||
tay
|
||||
lda digits_low, y
|
||||
sta low_nybble
|
||||
lda #$ff
|
||||
sta low_nybble+1
|
||||
sta high_nybble+1
|
||||
|
||||
;; Wait for VBLANK to finish, then turn off the VBLANK signal.
|
||||
* lda INTIM
|
||||
bne -
|
||||
sta WSYNC
|
||||
sta VBLANK
|
||||
|
||||
;; Display kernel.
|
||||
;; Top blank: 4 lines
|
||||
ldx #4
|
||||
stx subrow
|
||||
* sta WSYNC
|
||||
dex
|
||||
bne -
|
||||
|
||||
;; Header graphics: 20 lines
|
||||
ldy #5
|
||||
ldx startcol
|
||||
header_loop:
|
||||
sta WSYNC
|
||||
stx COLUPF ; +3 = 3
|
||||
lda pf0_left-1,y ; +4 = 7
|
||||
sta PF0 ; +3 = 10
|
||||
lda pf1_left-1,y ; +4 = 14
|
||||
sta PF1 ; +3 = 17
|
||||
lda pf2_left-1,y ; +4 = 21
|
||||
sta PF2 ; +3 = 24
|
||||
cmp $80 ; +3 = 27 (3-cycle no-op)
|
||||
lda pf0_right-1,y ; +4 = 31
|
||||
sta PF0 ; +3 = 34
|
||||
lda pf1_right-1,y ; +4 = 38
|
||||
sta PF1 ; +3 = 41
|
||||
lda pf2_right-1,y ; +4 = 45
|
||||
sta PF2 ; +3 = 48 ** MUST STORE PF2 2ND TIME ON EXACTLY CYCLE 48 **
|
||||
inx ; +2 = 50
|
||||
inx ; +2 = 52
|
||||
dec subrow ; +5 = 57
|
||||
bne header_loop ; +2 = 59
|
||||
dey ; +2 = 61
|
||||
beq header_done ; +2 = 63
|
||||
lda #4 ; +2 = 65
|
||||
sta subrow ; +3 = 68
|
||||
bne header_loop ; +3 = 71
|
||||
;; We've cut it very fine here! We only have 76 cycles per
|
||||
;; scanline and we use nearly all of them.
|
||||
header_done:
|
||||
;; Ruled split between title and data (8 lines)
|
||||
ldy #$00 ; Clear playfield now that we're done (+2 = 72)
|
||||
ldx #$0c ; Default status color is light grey (+2 = 74)
|
||||
sta WSYNC ; Rest of previous line
|
||||
sty PF0
|
||||
sty PF1
|
||||
sty PF2
|
||||
stx COLUPF
|
||||
stx COLUP0
|
||||
stx COLUP1
|
||||
dey
|
||||
ldx #$f0
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
stx PF0 ; Fill playfield completely
|
||||
sty PF1
|
||||
sty PF2
|
||||
|
||||
ldy #$01
|
||||
sty CTRLPF ; Symmetric PF
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
dey
|
||||
sty PF0 ; Clear playfield again
|
||||
sty PF1
|
||||
sty PF2
|
||||
|
||||
ldy #$08 ; 32 lines (for letters; 8, 16, 8)
|
||||
* sta WSYNC
|
||||
dey
|
||||
bne -
|
||||
ldy #$06
|
||||
* lda (high_nybble), y
|
||||
sta GRP0
|
||||
lda (low_nybble), y
|
||||
sta GRP1
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
dey
|
||||
bpl -
|
||||
iny
|
||||
sty GRP0
|
||||
sty GRP1
|
||||
ldy #$0A
|
||||
* sta WSYNC
|
||||
dey
|
||||
bne -
|
||||
|
||||
;; Top border (12 lines)
|
||||
lda #$03
|
||||
sta PF1
|
||||
lda #$ff
|
||||
sta PF2
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
ldx #$00
|
||||
stx PF1
|
||||
stx PF2
|
||||
ldx #$02 ; Turn on walls (the missiles)
|
||||
stx ENAM0
|
||||
stx ENAM1
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta PF2
|
||||
lda curcol
|
||||
sta COLUPF
|
||||
|
||||
;; Color blob (96 lines)
|
||||
ldx #96
|
||||
* sta WSYNC
|
||||
dex
|
||||
bne -
|
||||
|
||||
;; Bottom border (12 lines)
|
||||
stx PF2
|
||||
lda #$0c
|
||||
sta COLUPF
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
stx ENAM0 ; Turn off walls (the missles)
|
||||
stx ENAM1
|
||||
lda #$03
|
||||
sta PF1
|
||||
lda #$ff
|
||||
sta PF2
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
sta WSYNC
|
||||
stx PF1
|
||||
stx PF2
|
||||
ldx #$08
|
||||
* sta WSYNC
|
||||
dex
|
||||
bne -
|
||||
|
||||
; Turn on VBLANK, do 30 lines of Overscan
|
||||
lda #$02
|
||||
sta VBLANK
|
||||
ldy #30
|
||||
* sta WSYNC
|
||||
dey
|
||||
bne -
|
||||
jmp frame ; And the frame is done, back to VSYNC.
|
||||
|
||||
;;; Graphical data. Notice that we have to start not on a page
|
||||
;;; boundary, but with all graphics in each group on one page.
|
||||
.advance $FF01
|
||||
pf0_left:
|
||||
.byte $e0,$20,$20,$20,$e0
|
||||
|
||||
pf1_left:
|
||||
.byte $77,$54,$54,$54,$74
|
||||
|
||||
pf2_left:
|
||||
.byte $ae,$6a,$ea,$aa,$ee
|
||||
|
||||
pf0_right:
|
||||
.byte $00,$00,$00,$00,$00
|
||||
|
||||
pf1_right:
|
||||
.byte $4e,$48,$4c,$48,$ee
|
||||
|
||||
pf2_right:
|
||||
.byte $27,$24,$27,$21,$77
|
||||
|
||||
;; We don't need a digits_high. It's always $FF!
|
||||
digits_low:
|
||||
.byte <digit_0, <digit_1, <digit_2, <digit_3
|
||||
.byte <digit_4, <digit_5, <digit_6, <digit_7
|
||||
.byte <digit_8, <digit_9, <digit_a, <digit_b
|
||||
.byte <digit_c, <digit_d, <digit_e, <digit_f
|
||||
|
||||
digit_0:
|
||||
.byte $3c,$66,$66,$76,$6e,$66,$3c
|
||||
digit_1:
|
||||
.byte $7e,$18,$18,$18,$38,$18,$18
|
||||
digit_2:
|
||||
.byte $7e,$60,$30,$0c,$06,$66,$3c
|
||||
digit_3:
|
||||
.byte $3c,$66,$06,$1c,$06,$66,$3c
|
||||
digit_4:
|
||||
.byte $06,$06,$7f,$66,$1e,$0e,$06
|
||||
digit_5:
|
||||
.byte $3c,$66,$06,$06,$7c,$60,$7e
|
||||
digit_6:
|
||||
.byte $3c,$66,$66,$7c,$60,$66,$3c
|
||||
digit_7:
|
||||
.byte $18,$18,$18,$18,$0c,$66,$7e
|
||||
digit_8:
|
||||
.byte $3c,$66,$66,$3c,$66,$66,$3c
|
||||
digit_9:
|
||||
.byte $3c,$66,$06,$3e,$66,$66,$3c
|
||||
digit_a:
|
||||
.byte $66,$66,$66,$7e,$66,$3c,$18
|
||||
digit_b:
|
||||
.byte $7c,$66,$66,$7c,$66,$66,$7c
|
||||
digit_c:
|
||||
.byte $3c,$66,$60,$60,$60,$66,$3c
|
||||
digit_d:
|
||||
.byte $78,$6c,$66,$66,$66,$6c,$78
|
||||
digit_e:
|
||||
.byte $7e,$60,$60,$78,$60,$60,$7e
|
||||
digit_f:
|
||||
.byte $60,$60,$60,$78,$60,$60,$7e
|
||||
;;; Interrupt vectors.
|
||||
.advance $FFFA
|
||||
.word reset, reset, reset
|
@ -9,37 +9,42 @@
|
||||
.space counter 1
|
||||
|
||||
.text
|
||||
.org $FF00
|
||||
.org $F800
|
||||
|
||||
reset: `clean'start
|
||||
|
||||
; Initialize the player sprites.
|
||||
|
||||
; We're going to use quad-sized players for our
|
||||
; letters. With 160 color clocks, and letters
|
||||
; twenty clocks wide, and a 68 color clock HBLANK,
|
||||
; we want to place the two letters at color clock
|
||||
; 68+80-20-2 = 126 and 68+80+2 = 150. Those
|
||||
; translate to cycle counts 42 and 50.
|
||||
; letters, and each one is 5 notional pixels wide.
|
||||
; When we write RESP*, the start cycle after WSYNC
|
||||
; determines the pixel it appears at with the
|
||||
; formula 3N - 54 (minimum 1). Cycles 36 and 44
|
||||
; get us close. We end up 4 pixels too far left.
|
||||
|
||||
; While we wait, we set up our sprites to be
|
||||
; quad-sized and initialize the independent
|
||||
; color-counters for each sprite.
|
||||
|
||||
sta WSYNC ; 3
|
||||
lda #$07 ; 5
|
||||
sta NUSIZ0 ; 7
|
||||
sta NUSIZ1 ; 10
|
||||
lda #$20 ; 13
|
||||
ldy #5 ; 15
|
||||
sta WSYNC
|
||||
lda #$07 ; +2= 2
|
||||
sta NUSIZ0 ; +3= 5
|
||||
sta NUSIZ1 ; +3= 8
|
||||
lda #$20 ; +2=10
|
||||
ldy #5 ; +2=12
|
||||
* dey
|
||||
bne - ; 20-25-30-35-39
|
||||
sta RESP0 ; 42 - color clock 126
|
||||
sta col'0 ; 45
|
||||
eor #$80 ; 47
|
||||
sta RESP1 ; 50 - color clock 150
|
||||
bne - ; 17-22-27-32-36
|
||||
sta RESP0 ; +3=39 (P0 at pixel 54)
|
||||
sta col'0 ; +3=42
|
||||
eor #$80 ; +2=44
|
||||
sta RESP1 ; (P1 at pixel 78)
|
||||
sta col'1
|
||||
sta temp
|
||||
lda #$C0 ; HMOVE us 4 right
|
||||
sta HMP0
|
||||
sta HMP1
|
||||
sta WSYNC
|
||||
sta HMOVE
|
||||
|
||||
frame: `vertical'sync
|
||||
lda #43
|
||||
@ -117,7 +122,7 @@ frame: `vertical'sync
|
||||
dey ; loop.
|
||||
bne -
|
||||
|
||||
; Clear out the player graphics,,,
|
||||
; Clear out the player graphics...
|
||||
lda #$00
|
||||
sta GRP0
|
||||
sta GRP1
|
@ -86,9 +86,14 @@ Section "Ophis" SEC01
|
||||
File "..\..\examples\structuredemo.oph"
|
||||
File "..\..\examples\fibonacci.oph"
|
||||
File "..\..\examples\kinematics.oph"
|
||||
SetOutPath "$INSTDIR\examples\hi_stella"
|
||||
File "..\..\examples\hi_stella\hi_stella.oph"
|
||||
File "..\..\examples\hi_stella\README.txt"
|
||||
# Remove the older copies of hi_stella if needed
|
||||
Delete "$INSTDIR\examples\hi_stella\hi_stella.oph"
|
||||
Delete "$INSTDIR\examples\hi_stella\README.txt"
|
||||
RMDir "$INSTDIR\examples\hi_stella"
|
||||
SetOutPath "$INSTDIR\examples\stella"
|
||||
File "..\..\examples\stella\hi_stella.oph"
|
||||
File "..\..\examples\stella\colortest.oph"
|
||||
File "..\..\examples\stella\README.txt"
|
||||
SetOutPath "$INSTDIR\examples\hello_nes"
|
||||
File "..\..\examples\hello_nes\hello_prg.oph"
|
||||
File "..\..\examples\hello_nes\hello_chr.oph"
|
||||
@ -159,8 +164,9 @@ Section Uninstall
|
||||
Delete "$INSTDIR\ophis.exe"
|
||||
Delete "$INSTDIR\ophismanual.pdf"
|
||||
Delete "$INSTDIR\README.txt"
|
||||
Delete "$INSTDIR\examples\hi_stella\hi_stella.oph"
|
||||
Delete "$INSTDIR\examples\hi_stella\README.txt"
|
||||
Delete "$INSTDIR\examples\stella\hi_stella.oph"
|
||||
Delete "$INSTDIR\examples\stella\colortest.oph"
|
||||
Delete "$INSTDIR\examples\stella\README.txt"
|
||||
Delete "$INSTDIR\examples\hello_nes\hello_prg.oph"
|
||||
Delete "$INSTDIR\examples\hello_nes\hello_chr.oph"
|
||||
Delete "$INSTDIR\examples\hello_nes\hello_ines.oph"
|
||||
@ -180,7 +186,7 @@ Section Uninstall
|
||||
Delete "$STARTMENU\Manual.lnk"
|
||||
|
||||
RMDir "$SMPROGRAMS\$ICONS_GROUP"
|
||||
RMDir "$INSTDIR\examples\hi_stella"
|
||||
RMDir "$INSTDIR\examples\stella"
|
||||
RMDir "$INSTDIR\examples\hello_nes"
|
||||
RMDir "$INSTDIR\examples"
|
||||
RMDir "$INSTDIR\platform"
|
||||
|
Loading…
Reference in New Issue
Block a user