c64: added sid music/sfx, updated memmap, examples

This commit is contained in:
Steven Hugg 2022-07-19 14:53:26 -05:00
parent 3beef5626c
commit b0d4f8f1f5
14 changed files with 461 additions and 17 deletions

Binary file not shown.

47
presets/c64/c64-sid.cfg Normal file
View File

@ -0,0 +1,47 @@
FEATURES {
STARTADDRESS: default = $0801;
}
SYMBOLS {
__LOADADDR__: type = import;
__EXEHDR__: type = import;
__STACKSIZE__: type = weak, value = $0800; # 2k stack
__HIMEM__: type = weak, value = $D000;
}
MEMORY {
ZP: file = "", define = yes, start = $0002, size = $001A;
LOADADDR: file = %O, start = %S - 2, size = $0002;
HEADER: file = %O, define = yes, start = %S, size = $000D;
LOWMAIN: file = %O, define = yes, start = __HEADER_LAST__, size = $1000 - __HEADER_LAST__, fill = yes;
SIDFILE: file = %O, define = yes, start = $1000, size = $1000, fill = yes;
MAIN: file = %O, define = yes, start = $2000, size = __HIMEM__ - $2000;
BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
LOADADDR: load = LOADADDR, type = ro;
EXEHDR: load = HEADER, type = ro;
STARTUP: load = LOWMAIN, type = ro;
LOWCODE: load = LOWMAIN, type = ro, optional = yes;
SIDFILE: load = SIDFILE, type = ro, optional = yes;
CODE: load = MAIN, type = ro;
RODATA: load = MAIN, type = ro;
DATA: load = MAIN, type = rw;
INIT: load = MAIN, type = rw;
ONCE: load = MAIN, type = ro, define = yes;
BSS: load = BSS, type = bss, define = yes;
}
FEATURES {
CONDES: type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__,
segment = ONCE;
CONDES: type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__,
segment = RODATA;
CONDES: type = interruptor,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__,
segment = RODATA,
import = __CALLIRQ__;
}

View File

@ -17,8 +17,18 @@
#include "sprites.h"
//#link "sprites.c"
// indices of sound effects (0..3)
typedef enum { SND_START, SND_HIT, SND_COIN, SND_JUMP } SFXIndex;
//#resource "c64-sid.cfg"
#define CFGFILE c64-sid.cfg
//#resource "sidmusic1.bin"
//#link "sidplaysfx.ca65"
#include "sidplaysfx.h"
// indices of sound effects
#define SND_JUMP 0
#define SND_HIT 2
#define SND_COIN 1
#define SND_FALL 3
///// DEFINES
@ -469,7 +479,7 @@ void draw_actor(byte i) {
void refresh_actors() {
byte i;
yscroll = BOTTOM_Y + scroll_fine_y + (START_ORIGIN_Y - origin_y)*8;
sprite_clear();
sprshad.spr_ena = 0; // make all sprites invisible
for (i=0; i<MAX_ACTORS; i++)
draw_actor(i);
animate_explosion();
@ -541,6 +551,7 @@ void move_actor(struct Actor* actor, byte joystick, bool scroll) {
actor->yvel = 15;
if (joystick & JOY_LEFT_MASK) actor->xvel = -1;
if (joystick & JOY_RIGHT_MASK) actor->xvel = 1;
if (scroll) sid_sfx(SND_JUMP);
} else if (joystick & JOY_LEFT_MASK) {
actor->x--;
actor->dir = 1;
@ -605,6 +616,7 @@ void move_actor(struct Actor* actor, byte joystick, bool scroll) {
if (actor->state == WALKING &&
is_in_gap(actor->x, floors[actor->level].gap)) {
fall_down(actor);
if (scroll) sid_sfx(SND_FALL);
}
}
@ -626,11 +638,11 @@ void pickup_object(Actor* actor) {
if (objtype == ITEM_MINE) {
// we hit a mine, fall down
fall_down(actor);
//sfx_play(SND_HIT,0);
sid_sfx(SND_HIT);
} else {
// we picked up an object, add to score
//score = bcd_add(score, 1);
//sfx_play(SND_COIN,0);
sid_sfx(SND_COIN);
}
}
}
@ -717,6 +729,9 @@ void play_scene() {
create_actors_on_floor(2);
refresh_screen();
sid_init(1);
sid_start();
while (actors[0].level != MAX_FLOORS-1) {
refresh_actors();
move_player();
@ -728,6 +743,7 @@ void play_scene() {
if (VIC.spr_coll & 0x01) {
if (actors[0].level > 0 && check_collision(&actors[0])) {
fall_down(&actors[0]);
sid_sfx(SND_HIT);
}
}
if (swap_needed) sprite_update(hidbuf);

View File

@ -73,13 +73,16 @@ void main(void) {
// infinite loop
while (1) {
static char speed = 1;
// get joystick bits
char joy = joy_read(0);
// speed up scrolling while button pressed
speed = JOY_BTN_1(joy) ? 2 : 1;
// move sprite based on arrow keys
if (JOY_LEFT(joy)) scroll_horiz(-1);
if (JOY_UP(joy)) scroll_vert(-1);
if (JOY_RIGHT(joy)) scroll_horiz(1);
if (JOY_DOWN(joy)) scroll_vert(1);
if (JOY_LEFT(joy)) scroll_horiz(-speed);
if (JOY_UP(joy)) scroll_vert(-speed);
if (JOY_RIGHT(joy)) scroll_horiz(speed);
if (JOY_DOWN(joy)) scroll_vert(speed);
// animate sprite in shadow sprite ram
sprite_draw(0, n++, 70, 32);
// wait for vblank

32
presets/c64/siddemo.c Normal file
View File

@ -0,0 +1,32 @@
#include <stdio.h>
#include <conio.h>
#include <c64.h>
#include <cbm_petscii_charmap.h>
#include <joystick.h>
//#resource "c64-sid.cfg"
#define CFGFILE c64-sid.cfg
//#resource "sidmusic1.bin"
//#link "sidplaysfx.ca65"
#include "sidplaysfx.h"
void main(void) {
clrscr();
cursor(0);
joy_install(joy_static_stddrv);
sid_init(0);
sid_start();
printf("\r\nSID file loaded at $1000\r\n");
printf("\r\nMove joystick for SFX\r\n");
while (1) {
char joy = joy_read(0);
if (joy) {
sid_sfx(joy & 3);
}
waitvsync();
//sid_update();
}
}

BIN
presets/c64/sidmusic1.bin Normal file

Binary file not shown.

View File

@ -0,0 +1,85 @@
; music and SFX from GoatTracker 2 sample files
; http://sourceforge.net/projects/goattracker2
.segment "SIDFILE"
.incbin "sidmusic1.bin"
.segment "LOWCODE"
.global _sid_init, _sid_update, _sid_sfx
.global _sid_start
_sid_init:
jmp $1000
_sid_update:
jmp $1003
_sid_sfx:
tax
lda sfxtbllo,x ;Address in A,Y
ldy sfxtblhi,x
ldx #$0e ;Channel index in X
jmp $1006 ;(0, 7 or 14)
SID_IRQ:
jsr $1003
ASL $D019 ; acknowledge the interrupt by clearing the VIC's interrupt flag
JMP $EA31 ; jump into KERNAL's standard interrupt service routine to handle keyboard scan, cursor display etc.
_sid_start:
SEI ; set interrupt bit, make the CPU ignore interrupt requests
LDA #%01111111 ; switch off interrupt signals from CIA-1
STA $DC0D
AND $D011 ; clear most significant bit of VIC's raster register
STA $D011
LDA $DC0D ; acknowledge pending interrupts from CIA-1
LDA $DD0D ; acknowledge pending interrupts from CIA-2
LDA #210 ; set rasterline where interrupt shall occur
STA $D012
LDA #<SID_IRQ ; set interrupt vectors, pointing to interrupt service routine below
STA $0314
LDA #>SID_IRQ
STA $0315
LDA #%00000001 ; enable raster interrupt signals from VIC
STA $D01A
CLI ; clear interrupt flag, allowing the CPU to respond to interrupt requests
RTS
sfxtbllo: .byte <arpeggio2
.byte <arpeggio1
.byte <gunshot
.byte <explosion
sfxtblhi: .byte >arpeggio2
.byte >arpeggio1
.byte >gunshot
.byte >explosion
arpeggio2:
.byte $00,$89,$04,$A2,$41,$A2,$A2,$A6,$A6,$A6,$40,$A9,$A9,$A9,$A2,$A2
.byte $A2,$A6,$A6,$A6,$A9,$A9,$A9,$A2,$A2,$A2,$A6,$A6,$A6,$A9,$A9,$A9
.byte $A2,$A2,$A2,$A6,$A6,$A6,$A9,$A9,$A9,$00
arpeggio1:
.byte $0A,$00,$02,$A0,$41,$A0,$A0,$A4,$A4,$A4,$A7,$A7,$A7,$A0,$A0,$A0
.byte $A4,$A4,$A4,$A7,$A7,$A7,$A0,$A0,$A0,$A4,$A4,$A4,$A7,$A7,$A7,$A0
.byte $A0,$A0,$A4,$A4,$A4,$A7,$A7,$A7,$00
gunshot:
.byte $00,$F9,$08,$C4,$81,$A8,$41,$C0,$81,$BE,$BC,$80,$BA,$B8,$B6,$B4
.byte $B2,$B0,$AE,$AC,$AA,$A8,$A6,$A4,$A2,$A0,$9E,$9C,$9A,$98,$96,$94
.byte $92,$90,$00
explosion:
.byte $00,$FA,$08,$B8,$81,$A4,$41,$A0,$B4,$81,$98,$92,$9C,$90,$95,$9E
.byte $92,$80,$94,$8F,$8E,$8D,$8C,$8B,$8A,$89,$88,$87,$86,$84,$00

7
presets/c64/sidplaysfx.h Normal file
View File

@ -0,0 +1,7 @@
extern void sid_init(char musicindex);
extern void sid_update(void);
extern void sid_sfx(char sfxindex);
extern void sid_start(void);
extern void sid_stop(void);

View File

@ -0,0 +1,152 @@
#include <stdio.h>
#include <conio.h>
#include <c64.h>
#include <cbm_petscii_charmap.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include "common.h"
//#link "common.c"
#include "sprites.h"
//#link "sprites.c"
/*{w:12,h:21,bpp:2,brev:1}*/
const char SPRITEMC[3*21] = {
0x00,0xAA,0x80,0x02,0xAA,0xA0,0x0A,0xAA,0xA8,
0x0A,0xAE,0xA8,0x0A,0xBB,0xA8,0x0A,0xBA,0xA8,
0x0A,0xBB,0xA8,0x0A,0xAE,0xA8,0x0A,0xAA,0xA8,
0x09,0xAA,0x98,0x08,0x6A,0x48,0x08,0x1D,0x08,
0x02,0x0C,0x20,0x02,0x0C,0x20,0x02,0x0C,0x20,
0x00,0x8C,0x80,0x00,0x8C,0x80,0x00,0x55,0x40,
0x00,0x77,0x40,0x00,0x5D,0x40,0x00,0x15,0x00
};
int xpos[8];
int ypos[8];
int xvel[8];
int yvel[8];
void init_sprites(void) {
byte i;
// setup sprite positions
for (i=0; i<8; i++) {
xpos[i] = ((i & 3) * 0x2000) - 0x1000;
ypos[i] = (i * 0x1000) - 0x3000;
sprshad.spr_color[i] = i | 8;
}
}
void move_sprites(void) {
byte i;
for (i=0; i<8; i++) {
//VIC.bordercolor = i;
sprite_draw(i, (xpos[i]>>7)+0x80, (ypos[i]>>8)+0x80, 32);
// update position
xpos[i] += xvel[i];
ypos[i] += yvel[i];
}
}
void update_sprites(void) {
byte i;
for (i=0; i<8; i++) {
// update velocity
xvel[i] -= xpos[i] >> 12;
yvel[i] -= ypos[i] >> 12;
}
}
void collide_sprites(byte spr_coll) {
byte i;
byte mask = 1;
// exit if no collisions
if (!spr_coll) return;
// iterate all sprites that have their flag set
for (i=0; i<8; i++, mask<<=1) {
//VIC.bordercolor = i;
if (spr_coll & mask) {
// find the first sprite that intersects
byte j = sprite_get_closest_collision(i, spr_coll);
// returns 0..7 if a sprite was found
if (j < 8) {
xvel[i] = (xpos[i] - xpos[j]) >> 4;
yvel[i] = (ypos[i] - ypos[j]) >> 4;
}
}
}
}
void iterategame1(void) {
byte spr_coll;
// wait for vblank
wait_vblank();
// grab and reset sprite-sprite collision flags
spr_coll = VIC.spr_coll;
// then update sprite registers from shadow RAM
sprite_update(DEFAULT_SCREEN);
// draw sprites into shadow ram
move_sprites();
// and update velocity and position
update_sprites();
// if any flags are set in the collision register,
// process sprite collisions
collide_sprites(spr_coll);
}
void iterategame2(void) {
byte spr_coll;
// FIRST FRAME: move and update velocity
wait_vblank();
// grab and reset sprite-sprite collision flags
spr_coll = VIC.spr_coll;
// then update sprite registers from shadow RAM
sprite_update(DEFAULT_SCREEN);
// draw sprites into shadow ram
// and update posiitons
move_sprites();
// and update velocities
update_sprites();
// SECOND FRAME: move and process collisions
wait_vblank();
// grab and reset sprite-sprite collision flags
// combine with previous frame flags
spr_coll |= VIC.spr_coll;
// then update sprite registers from shadow RAM
sprite_update(DEFAULT_SCREEN);
// draw sprites into shadow ram
// and update posiitons
move_sprites();
// if any flags are set in the collision register,
// process sprite collisions
collide_sprites(spr_coll);
}
void main(void) {
clrscr();
VIC.bordercolor = 0;
// setup sprite library and copy sprite to VIC bank
sprite_clear();
sprite_shape((void*)0x400, 32/2, SPRITEMC);
// set colors
sprshad.spr_mcolor = 0xff;
sprshad.spr_mcolor0 = 4;
sprshad.spr_mcolor1 = 7;
// set sprite initial positions
init_sprites();
// game loop
while (1) {
iterategame2();
}
}

View File

@ -4,6 +4,10 @@
SpriteShadow sprshad;
void sprite_clear(void) {
memset(&sprshad, 0, sizeof(sprshad));
}
void sprite_update(char* vicbank) {
memcpy(vicbank + 0x3f8, sprshad.spr_shapes, 8);
VIC.spr_ena = sprshad.spr_ena;
@ -38,7 +42,25 @@ void sprite_draw(byte i, word x, byte y, byte shape) {
sprshad.spr_shapes[i] = shape;
}
void sprite_clear(void) {
sprshad.spr_ena = 0;
byte sprite_get_closest_collision(byte i, byte spr_coll) {
byte j;
byte jmask = 1;
byte dx,dy;
if (spr_coll & BITS[i]) {
spr_coll ^= BITS[i];
for (j=0; j<8; j++, jmask<<=1) {
if (spr_coll & jmask) {
// TODO?
dx = sprshad.spr_pos[i].x - sprshad.spr_pos[j].x + 24;
if (dx < 48) {
dy = sprshad.spr_pos[i].y - sprshad.spr_pos[j].y + 21;
if (dy < 42) {
return j;
}
}
}
}
} else {
return 0xff;
}
}

View File

@ -3,6 +3,8 @@
#include "common.h"
#define DEFAULT_SCREEN ((void*)0x400)
typedef struct {
byte spr_ena; /* Enable sprites */
byte spr_hi_x; /* High bits of X coordinate */
@ -22,9 +24,10 @@ typedef struct {
extern SpriteShadow sprshad;
void sprite_clear(void);
void sprite_update(char* screenram);
void sprite_shape(char* vicbank, byte index, const char* sprite_data);
void sprite_draw(byte i, word x, byte y, byte shape);
void sprite_clear(void);
byte sprite_get_closest_collision(byte i, byte spr_coll);
#endif

View File

@ -0,0 +1,72 @@
processor 6502
include "basicheader.dasm"
Src equ $02
Dest equ $04
Start:
lda #$38 ; 25 rows, on, bitmap
sta $d011 ; VIC control #1
lda #$18 ; 40 column, multicolor
sta $d016 ; VIC control #2
lda #$02
sta $dd00 ; set VIC bank ($4000-$7FFF)
lda #$80
sta $d018 ; set VIC screen to $6000
lda XtraData+0
sta $d020 ; border
sta $d021 ; background
lda #0
sta Dest
; copy char memory
lda #<CharData
sta Src
lda #>CharData
sta Src+1
lda #$40
sta Dest+1
ldx #$20
jsr CopyMem
; copy screen memory
lda #<ScreenData
sta Src
lda #>ScreenData
sta Src+1
lda #$60
sta Dest+1
ldx #$04
jsr CopyMem
; copy color RAM
lda #<ColorData
sta Src
lda #>ColorData
sta Src+1
lda #$d8
sta Dest+1
ldx #4
jsr CopyMem
; infinite loop
jmp .
; copy data from Src to Dest
; X = number of bytes * 256
CopyMem
ldy #0
.Loop
lda (Src),y
sta (Dest),y
iny
bne .Loop
inc Src+1
inc Dest+1
dex
bne .Loop
rts
; bitmap data
CharData equ .
ScreenData equ CharData+8000
ColorData equ ScreenData+1000
XtraData equ ColorData+1000
incbin "badspacerobots-c64.multi.bin"

View File

@ -122,7 +122,7 @@ export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeab
this.exports.machine_load_state(this.sys, this.stateptr);
}
getVideoParams() {
return {width:392, height:272, overscan:true, videoFrequency:50};
return {width:392, height:272, overscan:true, videoFrequency:50, aspect:392/272*0.9365};
}
setKeyInput(key: number, code: number, flags: number): void {
// TODO: handle shifted keys

View File

@ -16,18 +16,23 @@ const C64_PRESETS = [
{id:'scroll3.c', name:'Scrolling 3 (C)'},
{id:'scroll4.c', name:'Scrolling 4 (C)'},
{id:'scroll5.c', name:'Scrolling 5 (C)'},
{id:'climber.c', name:'Climber Game (C)'},
{id:'sprite_collision.c', name:'Sprite Collision (C)'},
{id:'multilines.c', name:'Multicolor Lines+Flood Fill (C)'},
{id:'sidtune.dasm', name:'SID Tune (ASM)'},
{id:'musicplayer.c', name:'Music Player (C)'},
{id:'sidtune.dasm', name:'Tiny SID Tune (ASM)'},
{id:'siddemo.c', name:'SID Demo (C)'},
{id:'climber.c', name:'Climber Game (C)'},
];
const C64_MEMORY_MAP = { main:[
{name:'6510 Registers',start:0x0, size:0x2,type:'io'},
{name:'BIOS Reserved', start:0x200, size:0xa7},
{name:'Default Screen RAM', start:0x400, size:1024,type:'ram'},
//{name:'RAM', start:0x2, size:0x7ffe,type:'ram'},
{name:'Cartridge ROM',start:0x8000,size:0x2000,type:'rom'},
{name:'BASIC ROM', start:0xa000,size:0x2000,type:'rom'},
{name:'RAM', start:0xc000,size:0x1000,type:'ram'},
{name:'Upper RAM', start:0xc000,size:0x1000,type:'ram'},
{name:'Character ROM',start:0xd000,size:0x1000,type:'rom'},
{name:'VIC-II I/O', start:0xd000,size:0x0400,type:'io'},
{name:'SID', start:0xd400,size:0x0400,type:'io'},
{name:'Color RAM', start:0xd800,size:0x0400,type:'io'},