From b0d4f8f1f5c9da75ff1e0f8efcccd4e02427abd2 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 19 Jul 2022 14:53:26 -0500 Subject: [PATCH] c64: added sid music/sfx, updated memmap, examples --- presets/c64/badspacerobots-c64.multi.bin | Bin 0 -> 10002 bytes presets/c64/c64-sid.cfg | 47 ++++++ presets/c64/climber.c | 26 ++- presets/c64/scroll5.c | 11 +- presets/c64/siddemo.c | 32 ++++ presets/c64/sidmusic1.bin | Bin 0 -> 2930 bytes presets/c64/sidplaysfx.ca65 | 85 ++++++++++ presets/c64/sidplaysfx.h | 7 + presets/c64/sprite_collision.c | 152 ++++++++++++++++++ presets/c64/sprites.c | 28 +++- presets/c64/sprites.h | 5 +- .../c64/viewer-badspacerobots-c64.multi.asm | 72 +++++++++ src/machine/c64.ts | 2 +- src/platform/c64.ts | 11 +- 14 files changed, 461 insertions(+), 17 deletions(-) create mode 100644 presets/c64/badspacerobots-c64.multi.bin create mode 100644 presets/c64/c64-sid.cfg create mode 100644 presets/c64/siddemo.c create mode 100644 presets/c64/sidmusic1.bin create mode 100644 presets/c64/sidplaysfx.ca65 create mode 100644 presets/c64/sidplaysfx.h create mode 100644 presets/c64/sprite_collision.c create mode 100644 presets/c64/viewer-badspacerobots-c64.multi.asm diff --git a/presets/c64/badspacerobots-c64.multi.bin b/presets/c64/badspacerobots-c64.multi.bin new file mode 100644 index 0000000000000000000000000000000000000000..203e9fc4e53c7cfc4410892cd1e9697ed7f515f6 GIT binary patch literal 10002 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 M0(g*{28V?I0z@IcRR910 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3d1d6bfc96d9862ef2834f5eab0836be1f36c526 GIT binary patch literal 2930 zcmb7D3rt(r89shozX)*c92=8BV+!G+HWAG_67uSG-ByG+!C~nm*|a)sP{XQ5qXdVf zonj~u9CMvJ2hTvbRtQ=I*^8)KS63V>+nOm%>gK7=q*NpANa#wYrPtdiB{9DHk0IhJ05@#(t zGKxeu?h`j+Qi{bY>=vuB`yA@oXXKwYy7^t0-;4RLVz={`rcu4i!b=;^_gqp~^3xI$t3c;a{vxC?{-ZY;X6^M@k4%IRAQ|F-{F8=dC#U zM>s5&3paYt4D*j(Hj5q{ebX$qU{Z!{9P8{}xwsjx#!_MQZ)Qmap`x?q%!Sp~=z0tF zTW5*q6S!ODcomN3v;6l=;~R1GBTHw>`7+Bg->SnECl()E^c02%5WS7XgBUy}l#*k7 z3zoJNmSCxhY{k&cLl#yaJ#7&iurqCfLP-O*9y@JmM8Mnti`;1A{eFYh77R6xJ8|>{ z+9f~jQVH!6Cs-%k!UGeRh4o}Djw>&vAi!EIIpVc2Z)o6zdkjywDsshAEYXIFWq7QV z@kwrmK1$r2_8R+;}4M*EHVk=!J;MswTjAws7k<_ z7EuBox~NEy&3M}$EUFW3(uzA%;}XW-beqRDAEe*phznh)&K6DS603A z`6H)>-bqL~>vlJNC-w<;92xyX!T!Oib+7-Xyz`U#pKbhc^_Bf|kvhX~huaSyYC84i zovjh``KQ#sk5A#3CXa2cc!JlxJ#?k~y?2tWUpcvoZF<~SclOAXbRp+UHd*lxQ7(## ziW-Dr7>!1w)#`M5y}^*RB0D=Lmo=JJVsoB#)#}1Ewg*efDjr(@$fFxKZ?`|WV|V?t zUwh%u;g;9m@Shp{?vJAH#wTxoF8}GS@!<`dwm<$R&FpI@6&u!UQ9?r&W7u-=#ajI3Ig)r+E3D?dO=MvasT zwc_IOv)`Y&*m%+T+G`hgDAf8DIc94?5nQlXp~*toxi}v^fQk{FN>cZ2t2k9VWjSy?H2%WXEg%4URUtE9pijPAFu$x{=&1w{b9Bl`PBJw#%pu>Q<;!OLx_w6l*JI z-Bu1EMyJck(Y+5#CnJSgqtDW>$X#hPnoQVi$)j3EN~P9tEjycK4F*^rF7K|(h>k@@ z`Kt>HWwff`fi*HJRmqAnSmm&6#WE@?kBa1y!ZmHqTI2at>m4G#^IA=n`o{iFhyfiQ{9hk^o${WTy2g&+YA z_K@vxCiZcP>3YQiQgh+i*q@P>>+0ybi8NgE0&{|Bnh5A-(9QpPZuYb;FdanWA)s49 zw=SPa2gxu5sp_rPPFj*@c3O=8zxe)gLk}<^%GxSQ2K_ zLRwmwHlk^vn--=;<7sLBPp-AuLp&a)9i~S&9>$|N3UmBtu08XWAY~oX=K4!VvOU?E zglE}3ADgGkx3rLw>rM*EU@}0z^1#yNzyDRo@u!7gZ>X=ocP1cwJ9G=|JG#1kexUgo z_%J{jY7y=~JJ>&?U8EFT)BYUS%~lF18yX&`^lL(^%*!JSGEAB zj_DKRBoRKQ|6K68bsS)dy82yy5+LC@bznYrH#-FH-bcDVQ_~8;wTtj1_;D{__cZ+* hrr-~_5F_B6hMVhyLY*O4mj&-|7Ru!Pyui<*zXQQkl6n9D literal 0 HcmV?d00001 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'},