diff --git a/presets/c64/badspacerobots-c64.multi.bin b/presets/c64/badspacerobots-c64.multi.bin new file mode 100644 index 00000000..203e9fc4 Binary files /dev/null and b/presets/c64/badspacerobots-c64.multi.bin differ diff --git a/presets/c64/c64-sid.cfg b/presets/c64/c64-sid.cfg new file mode 100644 index 00000000..30b69ac2 --- /dev/null +++ b/presets/c64/c64-sid.cfg @@ -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__; +} diff --git a/presets/c64/climber.c b/presets/c64/climber.c index 5f072a35..1cf0c3e2 100644 --- a/presets/c64/climber.c +++ b/presets/c64/climber.c @@ -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; iyvel = 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); diff --git a/presets/c64/scroll5.c b/presets/c64/scroll5.c index 1d84bfb4..8adfd32b 100644 --- a/presets/c64/scroll5.c +++ b/presets/c64/scroll5.c @@ -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 diff --git a/presets/c64/siddemo.c b/presets/c64/siddemo.c new file mode 100644 index 00000000..31d0b238 --- /dev/null +++ b/presets/c64/siddemo.c @@ -0,0 +1,32 @@ + +#include +#include +#include +#include +#include + +//#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(); + } +} diff --git a/presets/c64/sidmusic1.bin b/presets/c64/sidmusic1.bin new file mode 100644 index 00000000..3d1d6bfc Binary files /dev/null and b/presets/c64/sidmusic1.bin differ diff --git a/presets/c64/sidplaysfx.ca65 b/presets/c64/sidplaysfx.ca65 new file mode 100644 index 00000000..bc114310 --- /dev/null +++ b/presets/c64/sidplaysfx.ca65 @@ -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 + 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 + +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 diff --git a/presets/c64/sidplaysfx.h b/presets/c64/sidplaysfx.h new file mode 100644 index 00000000..83329d0a --- /dev/null +++ b/presets/c64/sidplaysfx.h @@ -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); diff --git a/presets/c64/sprite_collision.c b/presets/c64/sprite_collision.c new file mode 100644 index 00000000..866ac4f2 --- /dev/null +++ b/presets/c64/sprite_collision.c @@ -0,0 +1,152 @@ + +#include +#include +#include +#include +#include +#include +#include + +#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(); + } +} + diff --git a/presets/c64/sprites.c b/presets/c64/sprites.c index 541bd14b..07eaed11 100644 --- a/presets/c64/sprites.c +++ b/presets/c64/sprites.c @@ -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; + } } - diff --git a/presets/c64/sprites.h b/presets/c64/sprites.h index c75c007e..90308c88 100644 --- a/presets/c64/sprites.h +++ b/presets/c64/sprites.h @@ -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 diff --git a/presets/c64/viewer-badspacerobots-c64.multi.asm b/presets/c64/viewer-badspacerobots-c64.multi.asm new file mode 100644 index 00000000..28fea7ca --- /dev/null +++ b/presets/c64/viewer-badspacerobots-c64.multi.asm @@ -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+1 + lda #$40 + sta Dest+1 + ldx #$20 + jsr CopyMem +; copy screen memory + lda #ScreenData + sta Src+1 + lda #$60 + sta Dest+1 + ldx #$04 + jsr CopyMem +; copy color RAM + 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" diff --git a/src/machine/c64.ts b/src/machine/c64.ts index cfc8ae6c..84adb4fc 100644 --- a/src/machine/c64.ts +++ b/src/machine/c64.ts @@ -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 diff --git a/src/platform/c64.ts b/src/platform/c64.ts index c3bcada3..9518d9d2 100644 --- a/src/platform/c64.ts +++ b/src/platform/c64.ts @@ -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'},