From e852fd27a677bdf2701a2a66b8b0841876c086c9 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 19 Jul 2019 21:50:53 -0400 Subject: [PATCH] vcs: added chap 8, 11, 12, 13 examples --- doc/notes.txt | 5 + presets/vcs/examples/colorsprites.a | 103 ++++++++++++++++ presets/vcs/examples/controls.a | 176 ++++++++++++++++++++++++++++ presets/vcs/examples/piatimer.a | 51 ++++++++ presets/vcs/examples/sethorizpos.a | 140 ++++++++++++++++++++++ src/platform/vcs.ts | 4 + 6 files changed, 479 insertions(+) create mode 100644 presets/vcs/examples/colorsprites.a create mode 100644 presets/vcs/examples/controls.a create mode 100644 presets/vcs/examples/piatimer.a create mode 100644 presets/vcs/examples/sethorizpos.a diff --git a/doc/notes.txt b/doc/notes.txt index 249c7679..c6bc119d 100644 --- a/doc/notes.txt +++ b/doc/notes.txt @@ -130,6 +130,11 @@ TODO: - support NES_HEADER_16K? - PPU/TIA register write visualization - NES crt should mark raster pos when debugging +- JSNES + - doesn't support hiding >8 sprites + - doesn't do sprite zero test right + - doesn't do clip right + - doesn't do b/w tint - vcs - need Chapter 8 example? - vcs sound continues when paused diff --git a/presets/vcs/examples/colorsprites.a b/presets/vcs/examples/colorsprites.a new file mode 100644 index 00000000..1fa85afe --- /dev/null +++ b/presets/vcs/examples/colorsprites.a @@ -0,0 +1,103 @@ + + processor 6502 + include "vcs.h" + include "macro.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; We can draw a color sprite by setting two registers +; on every scanline: +; GRP0 (the bitmap) and COLUP0 (the player color). +; There's a separate lookup table for each. +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +SpriteHeight equ 8 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Variables segment + + seg.u Variables + org $80 + +YPos .byte + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Code segment + + seg Code + org $f000 + +Start + CLEAN_START + + lda #5 + sta YPos + +NextFrame + lsr SWCHB ; test Game Reset switch + bcc Start ; reset? +; 1 + 3 lines of VSYNC + VERTICAL_SYNC +; 37 lines of underscan + ldx #37 +LVBlank sta WSYNC + dex + bne LVBlank +; 192 lines of frame + ldx #192 ; X = 192 scanlines +LVScan + txa ; X -> A + sec ; set carry for subtract + sbc YPos ; local coordinate + cmp #SpriteHeight ; in sprite? + bcc InSprite ; yes, skip over next + lda #0 ; not in sprite, load 0 +InSprite + tay ; local coord -> Y + lda Frame0,y ; lookup color + sta WSYNC ; sync w/ scanline + sta GRP0 ; store bitmap + lda ColorFrame0,y ; lookup color + sta COLUP0 ; store color + dex ; decrement X + bne LVScan ; repeat until 192 lines + +; 29 lines of overscan + ldx #29 +LVOver sta WSYNC + dex + bne LVOver +; total = 262 lines, go to next frame + jmp NextFrame + +; Cat-head graphics data +Frame0 + .byte #0 ; zero padding, also clears register + .byte #%00111100 + .byte #%01000010 + .byte #%11100111 + .byte #%11111111 + .byte #%10011001 + .byte #%01111110 + .byte #%11000011 + .byte #%10000001 + +; Cat-head color data +ColorFrame0 + .byte #0 ; unused (for now) + .byte #$AE + .byte #$AC + .byte #$A8 + .byte #$AC + .byte #$8E + .byte #$8E + .byte #$98 + .byte #$94 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Epilogue + + org $fffc + .word Start ; reset vector + .word Start ; BRK vector diff --git a/presets/vcs/examples/controls.a b/presets/vcs/examples/controls.a new file mode 100644 index 00000000..4350868f --- /dev/null +++ b/presets/vcs/examples/controls.a @@ -0,0 +1,176 @@ + + processor 6502 + include "vcs.h" + include "macro.h" + include "xmacro.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; We're going to set the player's coarse and fine position +; at the same time using a clever method. +; We divide the X coordinate by 15, in a loop that itself +; is 15 cycles long. When the loop exits, we are at +; the correct coarse position, and we set RESP0. +; The accumulator holds the remainder, which we convert +; into the fine position for the HMP0 register. +; This logic is in a subroutine called SetHorizPos. +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +SpriteHeight equ 8 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Variables segment + + seg.u Variables + org $80 + +XPos .byte +YPos .byte + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Code segment + + seg Code + org $f000 + +Start + CLEAN_START + + lda #80 + sta YPos + sta XPos + +NextFrame + lsr SWCHB ; test Game Reset switch + bcc Start ; reset? +; 1 + 3 lines of VSYNC + VERTICAL_SYNC +; 37 lines of underscan + TIMER_SETUP 37 +; move X and Y coordinates w/ joystick + jsr MoveJoystick +; the next two scanlines +; position the player horizontally + lda XPos ; get X coordinate + ldx #0 ; player 0 + jsr SetHorizPos ; set coarse offset + sta WSYNC ; sync w/ scanline + sta HMOVE ; apply fine offsets +; it's ok if we took an extra scanline because +; the PIA timer will always count 37 lines +; wait for end of underscan + TIMER_WAIT +; 192 lines of frame + ldx #192 ; X = 192 scanlines +LVScan + txa ; X -> A + sec ; set carry for subtract + sbc YPos ; local coordinate + cmp #SpriteHeight ; in sprite? + bcc InSprite ; yes, skip over next + lda #0 ; not in sprite, load 0 +InSprite + tay ; local coord -> Y + lda Frame0,y ; lookup color + sta WSYNC ; sync w/ scanline + sta GRP0 ; store bitmap + lda ColorFrame0,y ; lookup color + sta COLUP0 ; store color + dex ; decrement X + bne LVScan ; repeat until 192 lines + +; 29 lines of overscan + TIMER_SETUP 29 + TIMER_WAIT +; total = 262 lines, go to next frame + jmp NextFrame + +; SetHorizPos routine +; A = X coordinate +; X = player number (0 or 1) +SetHorizPos + sta WSYNC ; start a new line + sec ; set carry flag +DivideLoop + sbc #15 ; subtract 15 + bcs DivideLoop ; branch until negative + eor #7 ; calculate fine offset + asl + asl + asl + asl + sta RESP0,x ; fix coarse position + sta HMP0,x ; set fine offset + rts ; return to caller + +; Read joystick movement and apply to object 0 +MoveJoystick +; Move vertically +; (up and down are actually reversed since ypos starts at bottom) + ldx YPos + lda #%00100000 ;Up? + bit SWCHA + bne SkipMoveUp + cpx #2 + bcc SkipMoveUp + dex +SkipMoveUp + lda #%00010000 ;Down? + bit SWCHA + bne SkipMoveDown + cpx #183 + bcs SkipMoveDown + inx +SkipMoveDown + stx YPos +; Move horizontally + ldx XPos + lda #%01000000 ;Left? + bit SWCHA + bne SkipMoveLeft + cpx #16 + bcc SkipMoveLeft + dex +SkipMoveLeft + lda #%10000000 ;Right? + bit SWCHA + bne SkipMoveRight + cpx #153 + bcs SkipMoveRight + inx +SkipMoveRight + stx XPos + rts + + +; Cat-head graphics data +Frame0 + .byte #0 ; zero padding, also clears register + .byte #%00111100 + .byte #%01000010 + .byte #%11100111 + .byte #%11111111 + .byte #%10011001 + .byte #%01111110 + .byte #%11000011 + .byte #%10000001 + +; Cat-head color data +ColorFrame0 + .byte #0 ; unused (for now) + .byte #$AE + .byte #$AC + .byte #$A8 + .byte #$AC + .byte #$8E + .byte #$8E + .byte #$98 + .byte #$94 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Epilogue + + org $fffc + .word Start ; reset vector + .word Start ; BRK vector diff --git a/presets/vcs/examples/piatimer.a b/presets/vcs/examples/piatimer.a new file mode 100644 index 00000000..34162df3 --- /dev/null +++ b/presets/vcs/examples/piatimer.a @@ -0,0 +1,51 @@ + + processor 6502 + include "vcs.h" + include "macro.h" + include "xmacro.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Variables segment + + seg.u Variables + org $80 + +Temp .byte + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Code segment + + seg Code + org $f000 + +Start + CLEAN_START + +NextFrame + lsr SWCHB ; test Game Reset switch + bcc Start ; reset? +; 1 + 3 lines of VSYNC + VERTICAL_SYNC +; 37 lines of underscan + TIMER_SETUP 37 + TIMER_WAIT +; 192 lines of frame + TIMER_SETUP 192 +; draw colors based on timer value +LRainbow + lda INTIM ; load timer + sta COLUBK ; store background + bne LRainbow ; loop until timer == 0 + TIMER_WAIT ; timer is already 0, no-op +; 29 lines of overscan + TIMER_SETUP 29 + TIMER_WAIT +; total = 262 lines, go to next frame + jmp NextFrame + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Epilogue + + org $fffc + .word Start ; reset vector + .word Start ; BRK vector diff --git a/presets/vcs/examples/sethorizpos.a b/presets/vcs/examples/sethorizpos.a new file mode 100644 index 00000000..0552ef68 --- /dev/null +++ b/presets/vcs/examples/sethorizpos.a @@ -0,0 +1,140 @@ + + processor 6502 + include "vcs.h" + include "macro.h" + include "xmacro.h" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; We're going to set the player's coarse and fine position +; at the same time using a clever method. +; We divide the X coordinate by 15, in a loop that itself +; is 15 cycles long. When the loop exits, we are at +; the correct coarse position, and we set RESP0. +; The accumulator holds the remainder, which we convert +; into the fine position for the HMP0 register. +; This logic is in a subroutine called SetHorizPos. +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +SpriteHeight equ 8 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Variables segment + + seg.u Variables + org $80 + +XPos .byte +YPos .byte + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Code segment + + seg Code + org $f000 + +Start + CLEAN_START + + lda #5 + sta YPos + sta XPos + +NextFrame + lsr SWCHB ; test Game Reset switch + bcc Start ; reset? +; 1 + 3 lines of VSYNC + VERTICAL_SYNC +; 35 lines of underscan + ldx #35 +LVBlank sta WSYNC + dex + bne LVBlank +; animate X and Y coordinates +; NOTE: when X is too close to the right side, +; we risk using an extra scanline + inc XPos + inc YPos +; the next two scanlines +; position the player horizontally + lda XPos ; get X coordinate + ldx #0 ; player 0 + jsr SetHorizPos ; set coarse offset + sta WSYNC ; sync w/ scanline + sta HMOVE ; apply fine offsets +; 192 lines of frame + ldx #192 ; X = 192 scanlines +LVScan + txa ; X -> A + sec ; set carry for subtract + sbc YPos ; local coordinate + cmp #SpriteHeight ; in sprite? + bcc InSprite ; yes, skip over next + lda #0 ; not in sprite, load 0 +InSprite + tay ; local coord -> Y + lda Frame0,y ; lookup color + sta WSYNC ; sync w/ scanline + sta GRP0 ; store bitmap + lda ColorFrame0,y ; lookup color + sta COLUP0 ; store color + dex ; decrement X + bne LVScan ; repeat until 192 lines + +; 29 lines of overscan + ldx #29 +LVOver sta WSYNC + dex + bne LVOver +; total = 262 lines, go to next frame + jmp NextFrame + +; SetHorizPos routine +; A = X coordinate +; X = player number (0 or 1) +SetHorizPos + sta WSYNC ; start a new line + sec ; set carry flag +DivideLoop + sbc #15 ; subtract 15 + bcs DivideLoop ; branch until negative + eor #7 ; calculate fine offset + asl + asl + asl + asl + sta RESP0,x ; fix coarse position + sta HMP0,x ; set fine offset + rts ; return to caller + +; Cat-head graphics data +Frame0 + .byte #0 ; zero padding, also clears register + .byte #%00111100 + .byte #%01000010 + .byte #%11100111 + .byte #%11111111 + .byte #%10011001 + .byte #%01111110 + .byte #%11000011 + .byte #%10000001 + +; Cat-head color data +ColorFrame0 + .byte #0 ; unused (for now) + .byte #$AE + .byte #$AC + .byte #$A8 + .byte #$AC + .byte #$8E + .byte #$8E + .byte #$98 + .byte #$94 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Epilogue + + org $fffc + .word Start ; reset vector + .word Start ; BRK vector diff --git a/src/platform/vcs.ts b/src/platform/vcs.ts index c385310b..bd25ef62 100644 --- a/src/platform/vcs.ts +++ b/src/platform/vcs.ts @@ -14,8 +14,12 @@ const VCS_PRESETS = [ {id:'examples/vsync.a', chapter:5, name:'Painting on the CRT', title:'Color Bars'}, {id:'examples/playfield.a', chapter:6, name:'Playfield Graphics'}, {id:'examples/sprite.a', chapter:7, name:'Players and Sprites'}, + {id:'examples/colorsprites.a', chapter:8, name:'Color Sprites'}, {id:'examples/timing2.a', chapter:9, name:'Fine Positioning', title:'Fine Position'}, {id:'examples/missiles.a', chapter:10, name:'Player/Missile Graphics', title:'Player/Missile'}, + {id:'examples/sethorizpos.a', chapter:11, name:'SetHorizPos Routine'}, + {id:'examples/piatimer.a', chapter:12, name:'PIA Timer'}, + {id:'examples/controls.a', chapter:13, name:'Joysticks'}, {id:'examples/complexscene.a', chapter:15, name:'Complex Scene I'}, {id:'examples/complexscene2.a', chapter:16, name:'Complex Scene II'}, {id:'examples/scoreboard.a', chapter:18, name:'Scoreboard'},