mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-02-17 08:30:51 +00:00
c64: update presets, refactor analysis
This commit is contained in:
parent
de6250b0cd
commit
f6452a719f
@ -24,6 +24,37 @@ typedef enum { false, true } bool; // boolean
|
|||||||
|
|
||||||
///// MACROS /////
|
///// MACROS /////
|
||||||
|
|
||||||
|
// VIC Control Register 1 Flags
|
||||||
|
#define VIC_CTRL1_RST8 0x80 // Bit 8 of RASTER (read) or raster line interrupt set (write)
|
||||||
|
#define VIC_CTRL1_ECM 0x40 // Extended Color Mode
|
||||||
|
#define VIC_CTRL1_BMM 0x20 // Bitmap Mode
|
||||||
|
#define VIC_CTRL1_DEN 0x10 // Display Enable
|
||||||
|
#define VIC_CTRL1_RSEL 0x08 // Row Select (25 or 24 rows)
|
||||||
|
#define VIC_CTRL1_YSCROLL_MASK 0x07 // Vertical Fine Scrolling
|
||||||
|
|
||||||
|
// VIC Control Register 2 Flags
|
||||||
|
#define VIC_CTRL2_RES 0x20 // Chip reset
|
||||||
|
#define VIC_CTRL2_MCM 0x10 // Multicolor Mode Enable
|
||||||
|
#define VIC_CTRL2_CSEL 0x08 // Column Select (40 or 38 columns)
|
||||||
|
#define VIC_CTRL2_XSCROLL_MASK 0x07 // Horizontal Fine Scrolling
|
||||||
|
|
||||||
|
// VIC Memory Control Register Flags
|
||||||
|
#define VIC_ADDR_VM_MASK 0xf0 // Video Matrix Base Address Mask (character data)
|
||||||
|
#define VIC_ADDR_CB_MASK 0x0e // Character Bank Base Address Mask (screen memory)
|
||||||
|
|
||||||
|
// VIC Interrupt Register Flags
|
||||||
|
#define VIC_IRR_IRQ 0x80 // Interrupt Request
|
||||||
|
#define VIC_IRR_ILP 0x08 // Light Pen Interrupt
|
||||||
|
#define VIC_IRR_IMMC 0x04 // Sprite-Sprite Collision Interrupt
|
||||||
|
#define VIC_IRR_IMBC 0x02 // Sprite-Background Collision Interrupt
|
||||||
|
#define VIC_IRR_IRST 0x01 // Raster Line Interrupt
|
||||||
|
|
||||||
|
// VIC Interrupt Mask Register Flags
|
||||||
|
#define VIC_IMR_ELP 0x08 // Enable Light Pen Interrupt
|
||||||
|
#define VIC_IMR_EMMC 0x04 // Enable Sprite-Sprite Collision Interrupt
|
||||||
|
#define VIC_IMR_EMBC 0x02 // Enable Sprite-Background Collision Interrupt
|
||||||
|
#define VIC_IMR_ERST 0x01 // Enable Raster Interrupt
|
||||||
|
|
||||||
// lookup screen address macro
|
// lookup screen address macro
|
||||||
#define SCRNADR(base,col,row) ((base)+(col)+(row)*40)
|
#define SCRNADR(base,col,row) ((base)+(col)+(row)*40)
|
||||||
|
|
||||||
@ -77,6 +108,16 @@ char* get_vic_bank_start();
|
|||||||
// get current screen memory address
|
// get current screen memory address
|
||||||
char* get_screen_memory();
|
char* get_screen_memory();
|
||||||
|
|
||||||
|
// read joystick fast
|
||||||
|
#define READ_STICK(index) ~PEEK(0xdc01-(index))
|
||||||
|
|
||||||
|
#define STICK_UP(joy) ((joy & 0x1) != 0)
|
||||||
|
#define STICK_DOWN(joy) ((joy & 0x2) != 0)
|
||||||
|
#define STICK_LEFT(joy) ((joy & 0x4) != 0)
|
||||||
|
#define STICK_RIGHT(joy) ((joy & 0x8) != 0)
|
||||||
|
#define STICK_BUTTON(joy) ((joy & 0x10) != 0)
|
||||||
|
#define STICK_MOVED(joy) ((joy & 0x1f) != 0)
|
||||||
|
|
||||||
#ifdef __CC65__
|
#ifdef __CC65__
|
||||||
// return key in buffer, or 0 if none (BIOS call)
|
// return key in buffer, or 0 if none (BIOS call)
|
||||||
char __fastcall__ poll_keyboard();
|
char __fastcall__ poll_keyboard();
|
||||||
|
300
presets/c64/plasma.c
Normal file
300
presets/c64/plasma.c
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
** plasma test program for cc65. **
|
||||||
|
** **
|
||||||
|
** (w)2001 by groepaz **
|
||||||
|
** **
|
||||||
|
** Cleanup and porting by Ullrich von Bassewitz. **
|
||||||
|
** **
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <conio.h>
|
||||||
|
#include <cc65.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__C64__) || defined(__C128__)
|
||||||
|
# define SCREEN1 0xE000
|
||||||
|
# define SCREEN2 0xE400
|
||||||
|
# define CHARSET 0xE800
|
||||||
|
# define outb(addr,val) (*(addr)) = (val)
|
||||||
|
# define inb(addr) (*(addr))
|
||||||
|
#elif defined(__CBM510__)
|
||||||
|
# define SCREEN1 0xF000
|
||||||
|
# define SCREEN2 0xF400
|
||||||
|
# define CHARSET 0xE000
|
||||||
|
# define outb(addr,val) pokebsys ((unsigned)(addr), val)
|
||||||
|
# define inb(addr) peekbsys ((unsigned)(addr))
|
||||||
|
#elif defined(__PLUS4__)
|
||||||
|
# define SCREEN1 0x6400
|
||||||
|
# define SCREEN2 0x6C00
|
||||||
|
# define CHARSET 0x7000
|
||||||
|
# define outb(addr,val) (*(addr)) = (val)
|
||||||
|
# define inb(addr) (*(addr))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Values for the VIC address register to switch between the two pages */
|
||||||
|
#if defined(__PLUS4__)
|
||||||
|
#define PAGE1 ((SCREEN1 >> 8) & 0xF8)
|
||||||
|
#define PAGE2 ((SCREEN2 >> 8) & 0xF8)
|
||||||
|
#define CHARADR ((CHARSET >> 8) & 0xFC)
|
||||||
|
#else
|
||||||
|
#define PAGE1 ((SCREEN1 >> 6) & 0xF0) | ((CHARSET >> 10) & 0x0E)
|
||||||
|
#define PAGE2 ((SCREEN2 >> 6) & 0xF0) | ((CHARSET >> 10) & 0x0E)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Use static local variables for speed */
|
||||||
|
#pragma static-locals (1);
|
||||||
|
|
||||||
|
|
||||||
|
static const unsigned char sinustable[0x100] = {
|
||||||
|
0x80, 0x7d, 0x7a, 0x77, 0x74, 0x70, 0x6d, 0x6a,
|
||||||
|
0x67, 0x64, 0x61, 0x5e, 0x5b, 0x58, 0x55, 0x52,
|
||||||
|
0x4f, 0x4d, 0x4a, 0x47, 0x44, 0x41, 0x3f, 0x3c,
|
||||||
|
0x39, 0x37, 0x34, 0x32, 0x2f, 0x2d, 0x2b, 0x28,
|
||||||
|
0x26, 0x24, 0x22, 0x20, 0x1e, 0x1c, 0x1a, 0x18,
|
||||||
|
0x16, 0x15, 0x13, 0x11, 0x10, 0x0f, 0x0d, 0x0c,
|
||||||
|
0x0b, 0x0a, 0x08, 0x07, 0x06, 0x06, 0x05, 0x04,
|
||||||
|
0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03,
|
||||||
|
0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x0a,
|
||||||
|
0x0b, 0x0c, 0x0d, 0x0f, 0x10, 0x11, 0x13, 0x15,
|
||||||
|
0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24,
|
||||||
|
0x26, 0x28, 0x2b, 0x2d, 0x2f, 0x32, 0x34, 0x37,
|
||||||
|
0x39, 0x3c, 0x3f, 0x41, 0x44, 0x47, 0x4a, 0x4d,
|
||||||
|
0x4f, 0x52, 0x55, 0x58, 0x5b, 0x5e, 0x61, 0x64,
|
||||||
|
0x67, 0x6a, 0x6d, 0x70, 0x74, 0x77, 0x7a, 0x7d,
|
||||||
|
0x80, 0x83, 0x86, 0x89, 0x8c, 0x90, 0x93, 0x96,
|
||||||
|
0x99, 0x9c, 0x9f, 0xa2, 0xa5, 0xa8, 0xab, 0xae,
|
||||||
|
0xb1, 0xb3, 0xb6, 0xb9, 0xbc, 0xbf, 0xc1, 0xc4,
|
||||||
|
0xc7, 0xc9, 0xcc, 0xce, 0xd1, 0xd3, 0xd5, 0xd8,
|
||||||
|
0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8,
|
||||||
|
0xea, 0xeb, 0xed, 0xef, 0xf0, 0xf1, 0xf3, 0xf4,
|
||||||
|
0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfa, 0xfb, 0xfc,
|
||||||
|
0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd,
|
||||||
|
0xfd, 0xfc, 0xfb, 0xfa, 0xfa, 0xf9, 0xf8, 0xf6,
|
||||||
|
0xf5, 0xf4, 0xf3, 0xf1, 0xf0, 0xef, 0xed, 0xeb,
|
||||||
|
0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0, 0xde, 0xdc,
|
||||||
|
0xda, 0xd8, 0xd5, 0xd3, 0xd1, 0xce, 0xcc, 0xc9,
|
||||||
|
0xc7, 0xc4, 0xc1, 0xbf, 0xbc, 0xb9, 0xb6, 0xb3,
|
||||||
|
0xb1, 0xae, 0xab, 0xa8, 0xa5, 0xa2, 0x9f, 0x9c,
|
||||||
|
0x99, 0x96, 0x93, 0x90, 0x8c, 0x89, 0x86, 0x83
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void doplasma (register unsigned char* scrn)
|
||||||
|
{
|
||||||
|
unsigned char xbuf[40];
|
||||||
|
unsigned char ybuf[25];
|
||||||
|
unsigned char c1a,c1b;
|
||||||
|
unsigned char c2a,c2b;
|
||||||
|
unsigned char c1A,c1B;
|
||||||
|
unsigned char c2A,c2B;
|
||||||
|
register unsigned char i, ii;
|
||||||
|
|
||||||
|
c1a = c1A;
|
||||||
|
c1b = c1B;
|
||||||
|
for (ii = 0; ii < 25; ++ii) {
|
||||||
|
ybuf[ii] = (sinustable[c1a] + sinustable[c1b]);
|
||||||
|
c1a += 4;
|
||||||
|
c1b += 9;
|
||||||
|
}
|
||||||
|
c1A += 3;
|
||||||
|
c1B -= 5;
|
||||||
|
c2a = c2A;
|
||||||
|
c2b = c2B;
|
||||||
|
for (i = 0; i < 40; ++i) {
|
||||||
|
xbuf[i] = (sinustable[c2a] + sinustable[c2b]);
|
||||||
|
c2a += 3;
|
||||||
|
c2b += 7;
|
||||||
|
}
|
||||||
|
c2A += 2;
|
||||||
|
c2B -= 3;
|
||||||
|
for (ii = 0; ii < 25; ++ii) {
|
||||||
|
/* Unrolling the following loop will give a speed increase of
|
||||||
|
** nearly 100% (~24fps), but it will also increase the code
|
||||||
|
** size a lot.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < 40; ++i, ++scrn) {
|
||||||
|
*scrn = (xbuf[i] + ybuf[ii]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void makechar (void)
|
||||||
|
{
|
||||||
|
static const unsigned char bittab[8] = {
|
||||||
|
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
|
||||||
|
};
|
||||||
|
unsigned char i, ii, b, s;
|
||||||
|
unsigned c;
|
||||||
|
|
||||||
|
gotoxy (0, 1);
|
||||||
|
for (c = 0; c < 0x100; ++c) {
|
||||||
|
s = sinustable[c];
|
||||||
|
for (i = 0; i < 8; ++i){
|
||||||
|
b = 0;
|
||||||
|
for (ii = 0; ii < 8; ++ii) {
|
||||||
|
if ((rand() & 0xFFu) > s) {
|
||||||
|
b |= bittab[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
((unsigned char*)CHARSET) [(c*8) + i] = b;
|
||||||
|
}
|
||||||
|
if ((c & 0x07) == 0) {
|
||||||
|
cputc ('.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
unsigned char border;
|
||||||
|
unsigned char background;
|
||||||
|
unsigned char text;
|
||||||
|
unsigned char v;
|
||||||
|
clock_t t;
|
||||||
|
unsigned long f = 0;
|
||||||
|
unsigned long sec;
|
||||||
|
unsigned sec10;
|
||||||
|
unsigned long fps;
|
||||||
|
unsigned fps10;
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__C64__)
|
||||||
|
unsigned char block;
|
||||||
|
#endif
|
||||||
|
#if defined(__C128__)
|
||||||
|
unsigned char block;
|
||||||
|
unsigned char initflag;
|
||||||
|
unsigned char graphflag;
|
||||||
|
#endif
|
||||||
|
#if defined(__PLUS4__)
|
||||||
|
unsigned int i;
|
||||||
|
unsigned char v2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
clrscr ();
|
||||||
|
cprintf ("Making charset, mompls");
|
||||||
|
makechar();
|
||||||
|
|
||||||
|
/* Set the border and background colors */
|
||||||
|
border = bordercolor (COLOR_BLUE);
|
||||||
|
background = bgcolor (COLOR_BLUE);
|
||||||
|
text = textcolor (COLOR_BLACK);
|
||||||
|
clrscr ();
|
||||||
|
|
||||||
|
#if defined(__C64__) || defined(__C128__)
|
||||||
|
/* Move the VIC 16K block */
|
||||||
|
block = inb (&CIA2.pra);
|
||||||
|
outb (&CIA2.pra, (block & 0xFC) | ((SCREEN1 >> 14) ^ 0x03));
|
||||||
|
#endif
|
||||||
|
#if defined(__C128__)
|
||||||
|
/* Save and change some flags, so that kernal/basic interrupt handler will
|
||||||
|
** not interfere with our routine.
|
||||||
|
*/
|
||||||
|
initflag = *(unsigned char*) 0xA04;
|
||||||
|
*(unsigned char*) 0xA04 &= 0xFE;
|
||||||
|
graphflag = *(unsigned char*) 0xD8;
|
||||||
|
*(unsigned char*) 0xD8 = 0xFF;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Remember the VIC address register */
|
||||||
|
#if defined(__PLUS4__)
|
||||||
|
v = inb (&TED.char_addr);
|
||||||
|
v2 = inb (&TED.video_addr);
|
||||||
|
#else
|
||||||
|
v = inb (&VIC.addr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__PLUS4__)
|
||||||
|
for (i=0;i<1000;i++) {
|
||||||
|
((unsigned char *) (SCREEN1-0x0400))[i] = 0;
|
||||||
|
((unsigned char *) (SCREEN2-0x0400))[i] = 0;
|
||||||
|
}
|
||||||
|
outb (&TED.char_addr, CHARADR);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Run the demo until a key was hit */
|
||||||
|
t = clock ();
|
||||||
|
while (!kbhit()) {
|
||||||
|
/* Build page 1, then make it visible */
|
||||||
|
doplasma ((unsigned char*)SCREEN1);
|
||||||
|
#if defined(__PLUS4__)
|
||||||
|
outb (&TED.video_addr, PAGE1);
|
||||||
|
#else
|
||||||
|
outb (&VIC.addr, PAGE1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Build page 2, then make it visible */
|
||||||
|
doplasma ((unsigned char*)SCREEN2);
|
||||||
|
#if defined(__PLUS4__)
|
||||||
|
outb (&TED.video_addr, PAGE2);
|
||||||
|
#else
|
||||||
|
outb (&VIC.addr, PAGE2);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Count frames */
|
||||||
|
f += 2;
|
||||||
|
}
|
||||||
|
t = clock() - t;
|
||||||
|
|
||||||
|
/* Switch back the VIC screen */
|
||||||
|
#if defined(__PLUS4__)
|
||||||
|
outb (&TED.video_addr, v2);
|
||||||
|
outb (&TED.char_addr, v);
|
||||||
|
#else
|
||||||
|
outb (&VIC.addr, v);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__C64__) || defined(__C128__)
|
||||||
|
/* Move back the VIC 16K block */
|
||||||
|
outb (&CIA2.pra, block);
|
||||||
|
#endif
|
||||||
|
#if defined(__C128__)
|
||||||
|
/* Restore the flags */
|
||||||
|
*(unsigned char*) 0xA04 = initflag;
|
||||||
|
*(unsigned char*) 0xD8 = graphflag;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Fetch the character from the keyboard buffer and discard it */
|
||||||
|
(void) cgetc();
|
||||||
|
|
||||||
|
/* Reset screen colors */
|
||||||
|
bordercolor (border);
|
||||||
|
bgcolor (background);
|
||||||
|
textcolor (text);
|
||||||
|
clrscr ();
|
||||||
|
|
||||||
|
/* Calculate stats */
|
||||||
|
sec = (t * 10) / CLK_TCK;
|
||||||
|
sec10 = sec % 10;
|
||||||
|
sec /= 10;
|
||||||
|
fps = (f * (CLK_TCK * 10)) / t;
|
||||||
|
fps10 = fps % 10;
|
||||||
|
fps /= 10;
|
||||||
|
|
||||||
|
/* Output stats */
|
||||||
|
gotoxy (0, 0); cprintf ("time : %lu.%us", sec, sec10);
|
||||||
|
gotoxy (0, 1); cprintf ("frames: %lu", f);
|
||||||
|
gotoxy (0, 2); cprintf ("fps : %lu.%u", fps, fps10);
|
||||||
|
|
||||||
|
if (doesclrscrafterexit ()) {
|
||||||
|
cputsxy (0, 4, "Press any key when done...");
|
||||||
|
(void) cgetc ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Done */
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
; use CC65's interrupter (slower)
|
||||||
USE_INTERRUPTOR = 0
|
USE_INTERRUPTOR = 0
|
||||||
|
|
||||||
.segment "DATA"
|
.segment "DATA"
|
||||||
@ -9,39 +10,41 @@ NextDlist: .word NullDlist-1
|
|||||||
.segment "CODE"
|
.segment "CODE"
|
||||||
|
|
||||||
.global ___dlist_setup
|
.global ___dlist_setup
|
||||||
|
.global ___dlist_done
|
||||||
.global DLIST_IRQ_NEXT
|
.global DLIST_IRQ_NEXT
|
||||||
.global DLIST_IRQ_RESTART
|
.global DLIST_IRQ_RESTART
|
||||||
|
|
||||||
.if USE_INTERRUPTOR
|
.if USE_INTERRUPTOR
|
||||||
.interruptor DLIST_IRQ
|
.interruptor DLIST_IRQ
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
___dlist_setup:
|
___dlist_setup:
|
||||||
SEI ; set interrupt bit, make the CPU ignore interrupt requests
|
sei ; set interrupt bit, make the CPU ignore interrupt requests
|
||||||
|
|
||||||
sta StartDlist+0 ; save XA as pointer to start of dlist
|
sta StartDlist+0 ; save XA as pointer to start of dlist
|
||||||
stx StartDlist+1
|
stx StartDlist+1
|
||||||
|
|
||||||
LDA #%01111111 ; switch off interrupt signals from CIA-1
|
lda #%01111111 ; switch off interrupt signals from CIA-1
|
||||||
STA $DC0D
|
sta $DC0D
|
||||||
|
|
||||||
AND $D011 ; clear most significant bit of VIC's raster register
|
and $D011 ; clear most significant bit of VIC's raster register
|
||||||
STA $D011
|
sta $D011
|
||||||
|
|
||||||
LDA $DC0D ; acknowledge pending interrupts from CIA-1
|
lda $DC0D ; acknowledge pending interrupts from CIA-1
|
||||||
LDA $DD0D ; acknowledge pending interrupts from CIA-2
|
lda $DD0D ; acknowledge pending interrupts from CIA-2
|
||||||
|
|
||||||
LDA #252 ; set rasterline where interrupt shall occur
|
lda #252 ; set rasterline where interrupt shall occur
|
||||||
STA $D012
|
sta $D012
|
||||||
|
|
||||||
.if !USE_INTERRUPTOR
|
.if !USE_INTERRUPTOR
|
||||||
LDA #<DLIST_IRQ ; set interrupt vectors, pointing to interrupt service routine below
|
lda #<DLIST_IRQ ; set interrupt vectors, pointing to interrupt service routine below
|
||||||
STA $0314
|
sta $0314
|
||||||
LDA #>DLIST_IRQ
|
lda #>DLIST_IRQ
|
||||||
STA $0315
|
sta $0315
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
LDA #%00000001 ; enable raster interrupt signals from VIC
|
lda #%00000001 ; enable raster interrupt signals from VIC
|
||||||
STA $D01A
|
sta $D01A
|
||||||
cli
|
cli
|
||||||
rts
|
rts
|
||||||
|
|
||||||
@ -54,26 +57,21 @@ DLIST_CALL:
|
|||||||
rts
|
rts
|
||||||
|
|
||||||
DLIST_IRQ_RESTART:
|
DLIST_IRQ_RESTART:
|
||||||
sta $d012
|
sta $D012 ; set IRQ raster line
|
||||||
lda StartDlist+0
|
lda StartDlist+0
|
||||||
sta NextDlist+0
|
sta NextDlist+0
|
||||||
lda StartDlist+1
|
lda StartDlist+1
|
||||||
sta NextDlist+1
|
sta NextDlist+1
|
||||||
bne DLIST_ACK
|
bne DLIST_ACK
|
||||||
|
|
||||||
DLIST_IRQ_STOP:
|
|
||||||
lda #0 ; disable raster interrupt signals from VIC
|
|
||||||
sta $D01A
|
|
||||||
bne DLIST_ACK
|
|
||||||
|
|
||||||
DLIST_IRQ_NEXT:
|
DLIST_IRQ_NEXT:
|
||||||
sta $d012
|
sta $D012
|
||||||
pla
|
pla
|
||||||
sta NextDlist+0
|
sta NextDlist+0
|
||||||
pla
|
pla
|
||||||
sta NextDlist+1
|
sta NextDlist+1
|
||||||
DLIST_ACK:
|
DLIST_ACK:
|
||||||
ASL $D019 ; acknowledge the interrupt by clearing the VIC's interrupt flag
|
asl $D019 ; acknowledge the interrupt by clearing the VIC's interrupt flag
|
||||||
.if USE_INTERRUPTOR
|
.if USE_INTERRUPTOR
|
||||||
clc
|
clc
|
||||||
rts
|
rts
|
||||||
@ -84,9 +82,30 @@ DLIST_ACK:
|
|||||||
tax
|
tax
|
||||||
pla
|
pla
|
||||||
rti ; return from interrupt
|
rti ; return from interrupt
|
||||||
; JMP $EA31 ; jump into KERNAL's standard interrupt service routine to handle keyboard scan, cursor display etc.
|
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
|
___dlist_done:
|
||||||
|
php
|
||||||
|
sei ; disable interrupts
|
||||||
|
lda #$0 ; disable raster interrupt signals from VIC
|
||||||
|
sta $D01A
|
||||||
|
lda #$ff
|
||||||
|
sta $DC0D
|
||||||
|
.if !USE_INTERRUPTOR
|
||||||
|
lda #$31 ; set interrupt vectors back to KERNAL
|
||||||
|
sta $0314
|
||||||
|
lda #$ea
|
||||||
|
sta $0315
|
||||||
|
.else
|
||||||
|
lda #<(NullDlist-1)
|
||||||
|
sta StartDlist
|
||||||
|
lda #>(NullDlist-1)
|
||||||
|
sta StartDlist+1
|
||||||
|
.endif
|
||||||
|
plp
|
||||||
|
rts
|
||||||
|
|
||||||
|
|
||||||
NullDlist:
|
NullDlist:
|
||||||
lda #252
|
lda #252
|
||||||
jmp DLIST_IRQ_RESTART
|
jmp DLIST_IRQ_RESTART
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
// internal function, use macro instead
|
// internal function, use macro instead
|
||||||
void __dlist_setup(void* ptr);
|
void __dlist_setup(void* ptr);
|
||||||
|
void __dlist_done();
|
||||||
|
|
||||||
// initialize display list with function 'func'
|
// initialize display list with function 'func'
|
||||||
#define DLIST_SETUP(func) \
|
#define DLIST_SETUP(func) \
|
||||||
@ -19,5 +20,7 @@ void __dlist_setup(void* ptr);
|
|||||||
__A__ = line; \
|
__A__ = line; \
|
||||||
asm ("jmp DLIST_IRQ_RESTART");
|
asm ("jmp DLIST_IRQ_RESTART");
|
||||||
|
|
||||||
|
// stop display list
|
||||||
|
#define DLIST_DONE() __dlist_done();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
179
presets/c64/scrollingmap1.c
Normal file
179
presets/c64/scrollingmap1.c
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <conio.h>
|
||||||
|
#include <c64.h>
|
||||||
|
#include <cbm_petscii_charmap.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <joystick.h>
|
||||||
|
|
||||||
|
//#resource "c64-sid.cfg"
|
||||||
|
#define CFGFILE c64-sid.cfg
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
//#link "common.c"
|
||||||
|
|
||||||
|
#include "scrolling.h"
|
||||||
|
//#link "scrolling.c"
|
||||||
|
|
||||||
|
#include "sprites.h"
|
||||||
|
//#link "sprites.c"
|
||||||
|
|
||||||
|
//#link "level1.ca65"
|
||||||
|
|
||||||
|
extern const byte charset_data[];
|
||||||
|
extern const byte charset_attrib_data[];
|
||||||
|
extern const byte chartileset_data[];
|
||||||
|
extern const byte chartileset_tag_data[];
|
||||||
|
extern const byte* map_row_pointers[];
|
||||||
|
|
||||||
|
#define MAP_COLS 28
|
||||||
|
#define MAP_ROWS 11
|
||||||
|
|
||||||
|
static void draw_cell(word ofs, byte x, byte y) {
|
||||||
|
sbyte xx = x + origin_x;
|
||||||
|
sbyte yy = y + origin_y;
|
||||||
|
sbyte col = xx >> 2;
|
||||||
|
sbyte row = yy >> 2;
|
||||||
|
byte xofs = xx & 3;
|
||||||
|
byte yofs = yy & 3;
|
||||||
|
char ch;
|
||||||
|
char color;
|
||||||
|
if (col < 0 || col >= MAP_COLS || row < 0 || row >= MAP_ROWS) {
|
||||||
|
ch = 0;
|
||||||
|
color = 0;
|
||||||
|
} else {
|
||||||
|
byte tileindex = map_row_pointers[row][col];
|
||||||
|
ch = chartileset_data[xofs + yofs*4 + tileindex*16];
|
||||||
|
color = charset_attrib_data[ch];
|
||||||
|
}
|
||||||
|
hidbuf[ofs] = ch;
|
||||||
|
colorbuf[ofs] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll_draw_column(byte col) {
|
||||||
|
byte y;
|
||||||
|
word ofs = col;
|
||||||
|
for (y=0; y<ROWS; y++) {
|
||||||
|
draw_cell(ofs, col, y);
|
||||||
|
ofs += COLS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll_draw_row(byte row) {
|
||||||
|
byte x;
|
||||||
|
word ofs = row * COLS;
|
||||||
|
for (x=0; x<COLS; x++) {
|
||||||
|
draw_cell(ofs, x, row);
|
||||||
|
++ofs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*{w:24,h:21,bpp:1,brev:1}*/
|
||||||
|
const char SPRITE1[3*21] = {
|
||||||
|
0x00,0x7F,0x00,0x01,0xFF,0xC0,0x03,0xFF,0xE0,
|
||||||
|
0x03,0xE7,0xE0,0x07,0xD9,0xF0,0x07,0xDF,0xF0,
|
||||||
|
0x07,0xD9,0xF0,0x03,0xE7,0xE0,0x03,0xFF,0xE0,
|
||||||
|
0x03,0xFF,0xE0,0x02,0xFF,0xA0,0x01,0x7F,0x40,
|
||||||
|
0x01,0x3E,0x40,0x00,0x9C,0x80,0x00,0x9C,0x80,
|
||||||
|
0x00,0x49,0x00,0x00,0x49,0x00,0x00,0x3E,0x00,
|
||||||
|
0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x1C,0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
int playerx = 0;
|
||||||
|
int playery = 0;
|
||||||
|
int camerax = 0;
|
||||||
|
int cameray = 0;
|
||||||
|
|
||||||
|
void update_player() {
|
||||||
|
sprite_draw(0, playerx-camerax+172, playery-cameray+140, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
void camera_follow(byte moving) {
|
||||||
|
int dx, dy;
|
||||||
|
dx = camerax - playerx;
|
||||||
|
dy = cameray - playery;
|
||||||
|
if (moving && abs(dx) < 32 && abs(dy) < 32) return;
|
||||||
|
dx >>= 4;
|
||||||
|
dy >>= 4;
|
||||||
|
if (dx) {
|
||||||
|
if (dx > 8) dx = 8;
|
||||||
|
else if (dx < -8) dx = -8;
|
||||||
|
camerax -= dx;
|
||||||
|
scroll_horiz(dx);
|
||||||
|
}
|
||||||
|
if (dy) {
|
||||||
|
if (dy > 8) dy = 8;
|
||||||
|
else if (dy < -8) dy = -8;
|
||||||
|
cameray -= dy;
|
||||||
|
scroll_vert(dy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh_world(void) {
|
||||||
|
byte i;
|
||||||
|
for (i=0; i<25; i++) {
|
||||||
|
scroll_draw_row(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
|
||||||
|
clrscr();
|
||||||
|
|
||||||
|
// setup scrolling library
|
||||||
|
scroll_setup();
|
||||||
|
|
||||||
|
// multicolor character mode
|
||||||
|
VIC.ctrl2 |= 0x10;
|
||||||
|
VIC.bgcolor0 = 6;
|
||||||
|
VIC.bgcolor1 = 0;
|
||||||
|
VIC.bgcolor2 = 1;
|
||||||
|
|
||||||
|
// select character set @ 0x8800
|
||||||
|
VIC.addr = 0x12;
|
||||||
|
memcpy((char*)0x8800, charset_data, 520);
|
||||||
|
|
||||||
|
// setup sprite library and copy sprite to VIC bank
|
||||||
|
sprite_clear();
|
||||||
|
sprite_set_shapes(SPRITE1, 255, 1);
|
||||||
|
sprshad.spr_color[0] = 13;
|
||||||
|
|
||||||
|
// install the joystick driver
|
||||||
|
joy_install (joy_static_stddrv);
|
||||||
|
|
||||||
|
// repaint screen memory w/ the map
|
||||||
|
refresh_world();
|
||||||
|
|
||||||
|
// infinite loop
|
||||||
|
while (1) {
|
||||||
|
static char speed;
|
||||||
|
static char joy;
|
||||||
|
static bool slowframe = false;
|
||||||
|
// get joystick bits
|
||||||
|
joy = joy_read(0);
|
||||||
|
// speed up scrolling while button pressed
|
||||||
|
speed = JOY_BTN_1(joy) ? 3 : 1;
|
||||||
|
// if we copied screen memory last frame,
|
||||||
|
// double speed of player for this frame
|
||||||
|
if (slowframe) speed *= 2;
|
||||||
|
// move sprite based on arrow keys
|
||||||
|
if (JOY_LEFT(joy)) playerx -= speed;
|
||||||
|
if (JOY_RIGHT(joy)) playerx += speed;
|
||||||
|
if (JOY_UP(joy)) playery -= speed;
|
||||||
|
if (JOY_DOWN(joy)) playery += speed;
|
||||||
|
// move the camera?
|
||||||
|
camera_follow(joy);
|
||||||
|
slowframe = swap_needed;
|
||||||
|
// animate sprite in shadow sprite ram
|
||||||
|
update_player();
|
||||||
|
// wait for vblank
|
||||||
|
wait_vblank();
|
||||||
|
// then update sprite registers
|
||||||
|
sprite_update(visbuf);
|
||||||
|
// update scroll registers
|
||||||
|
// and swap screens if we must
|
||||||
|
scroll_update();
|
||||||
|
}
|
||||||
|
}
|
@ -124,6 +124,7 @@ void update_scoreboard() {
|
|||||||
|
|
||||||
void add_score(int delta) {
|
void add_score(int delta) {
|
||||||
score = bcd_add(score, delta);
|
score = bcd_add(score, delta);
|
||||||
|
update_scoreboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear scoreboard and draw initial strings
|
// clear scoreboard and draw initial strings
|
||||||
@ -242,13 +243,13 @@ void scroll_one_pixel_left() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void detect_player_collision(byte bgcoll, byte sprcoll) {
|
void detect_player_collision(byte bg_coll, byte spr_coll) {
|
||||||
// did we hit a powerup? (#0 and #1)
|
// did we hit a powerup? (#0 and #1)
|
||||||
bool hit_powerup = (sprcoll & 0b011) == 0b011;
|
bool hit_powerup = (spr_coll & 0b011) == 0b011;
|
||||||
// did player and obstacle sprite (#0 and #2) collide?
|
// did player and obstacle sprite (#0 and #2) collide?
|
||||||
bool hit_obstacle = (sprcoll & 0b101) == 0b101;
|
bool hit_obstacle = (spr_coll & 0b101) == 0b101;
|
||||||
// did player (#0) collide with background?
|
// did player (#0) collide with background?
|
||||||
hit_obstacle |= (bgcoll & 0b001) != 0;
|
hit_obstacle |= (bg_coll & 0b001) != 0;
|
||||||
// did we hit anything bad?
|
// did we hit anything bad?
|
||||||
if (hit_obstacle) {
|
if (hit_obstacle) {
|
||||||
// make player fall downward and backward
|
// make player fall downward and backward
|
||||||
@ -257,7 +258,6 @@ void detect_player_collision(byte bgcoll, byte sprcoll) {
|
|||||||
sprshad.spr_color[PLAYER_INDEX] = COLOR_LIGHTRED;
|
sprshad.spr_color[PLAYER_INDEX] = COLOR_LIGHTRED;
|
||||||
SID_PLAY_TONE(500);
|
SID_PLAY_TONE(500);
|
||||||
if (score != 0) { add_score(0x9999); } // BCD -1
|
if (score != 0) { add_score(0x9999); } // BCD -1
|
||||||
update_scoreboard();
|
|
||||||
} else {
|
} else {
|
||||||
sprshad.spr_color[PLAYER_INDEX] = COLOR_GREEN;
|
sprshad.spr_color[PLAYER_INDEX] = COLOR_GREEN;
|
||||||
}
|
}
|
||||||
@ -266,7 +266,6 @@ void detect_player_collision(byte bgcoll, byte sprcoll) {
|
|||||||
sprshad.spr_color[POWERUP_INDEX] += 1; // cycle colors
|
sprshad.spr_color[POWERUP_INDEX] += 1; // cycle colors
|
||||||
SID_PLAY_TONE(8000);
|
SID_PLAY_TONE(8000);
|
||||||
add_score(1);
|
add_score(1);
|
||||||
update_scoreboard();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,15 +311,15 @@ void main() {
|
|||||||
// game loop, repeat forever
|
// game loop, repeat forever
|
||||||
while (1) {
|
while (1) {
|
||||||
// saved collision flags
|
// saved collision flags
|
||||||
byte sprcoll, bgcoll;
|
byte spr_coll, bg_coll;
|
||||||
|
|
||||||
// wait for end of frame
|
// wait for end of frame
|
||||||
waitvsync();
|
waitvsync();
|
||||||
|
|
||||||
//--- START TIME CRITICAL SECTION
|
//--- START TIME CRITICAL SECTION
|
||||||
// grab and reset collision flags
|
// grab and reset collision flags
|
||||||
sprcoll = VIC.spr_coll;
|
spr_coll = VIC.spr_coll;
|
||||||
bgcoll = VIC.spr_bg_coll;
|
bg_coll = VIC.spr_bg_coll;
|
||||||
|
|
||||||
// update sprite registers from sprite shadow buffer
|
// update sprite registers from sprite shadow buffer
|
||||||
sprite_update(DEFAULT_SCREEN);
|
sprite_update(DEFAULT_SCREEN);
|
||||||
@ -330,7 +329,7 @@ void main() {
|
|||||||
//--- END TIME CRITICAL SECTION
|
//--- END TIME CRITICAL SECTION
|
||||||
|
|
||||||
// use collision flags to see if player collided
|
// use collision flags to see if player collided
|
||||||
detect_player_collision(bgcoll, sprcoll);
|
detect_player_collision(bg_coll, spr_coll);
|
||||||
|
|
||||||
// get joystick bits and move player
|
// get joystick bits and move player
|
||||||
move_player(joy_read(0));
|
move_player(joy_read(0));
|
||||||
|
76
presets/c64/sprite_stretch.c
Normal file
76
presets/c64/sprite_stretch.c
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
//#link "common.c"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
//#link "rasterirq.ca65"
|
||||||
|
#include "rasterirq.h"
|
||||||
|
|
||||||
|
//#link "sprites.c"
|
||||||
|
#include "sprites.h"
|
||||||
|
|
||||||
|
#include <cbm_petscii_charmap.h>
|
||||||
|
#include <cc65.h>
|
||||||
|
|
||||||
|
/*{w:24,h:21,bpp:1,brev:1}*/
|
||||||
|
const char spriteshape[3*21] = {
|
||||||
|
0x00,0x7F,0x00,0x01,0xFF,0xC0,0x03,0xFF,0xE0,
|
||||||
|
0x03,0xE7,0xE0,0x07,0xD9,0xF0,0x07,0xDF,0xF0,
|
||||||
|
0x07,0xD9,0xF0,0x03,0xE7,0xE0,0x03,0xFF,0xE0,
|
||||||
|
0x03,0xFF,0xE0,0x02,0xFF,0xA0,0x01,0x7F,0x40,
|
||||||
|
0x01,0x3E,0x40,0x00,0x9C,0x80,0x00,0x9C,0x80,
|
||||||
|
0x00,0x49,0x00,0x00,0x49,0x00,0x00,0x3E,0x00,
|
||||||
|
0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x1C,0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
void sprite_stretch() {
|
||||||
|
// get current raster line
|
||||||
|
asm("lda $d012");
|
||||||
|
// sprite Y expand bits = 255
|
||||||
|
asm("ldx #$ff");
|
||||||
|
asm("stx $d017");
|
||||||
|
// wait for next raster line
|
||||||
|
asm("@loop:");
|
||||||
|
asm("cmp $d012");
|
||||||
|
asm("beq @loop");
|
||||||
|
// sprite Y expand bits = 0
|
||||||
|
asm("inx");
|
||||||
|
asm("stx $d017");
|
||||||
|
}
|
||||||
|
|
||||||
|
void dlist_example(void) {
|
||||||
|
|
||||||
|
// stretch for the next 40 lines
|
||||||
|
while (VIC.rasterline != 160) {
|
||||||
|
sprite_stretch();
|
||||||
|
}
|
||||||
|
|
||||||
|
VIC.spr0_y+=3;
|
||||||
|
VIC.spr7_y-=2;
|
||||||
|
|
||||||
|
DLIST_RESTART(8*15);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
byte i;
|
||||||
|
|
||||||
|
clrscr();
|
||||||
|
VIC.bordercolor = 0;
|
||||||
|
|
||||||
|
sprite_clear();
|
||||||
|
sprite_set_shapes(spriteshape, 192, 1);
|
||||||
|
|
||||||
|
sprshad.spr_exp_x = 0xff;
|
||||||
|
for (i=0; i<8; i++) {
|
||||||
|
sprshad.spr_color[i] = i|8;
|
||||||
|
sprite_draw(i, i*38+24, 120-i, 192);
|
||||||
|
}
|
||||||
|
sprite_update(DEFAULT_SCREEN);
|
||||||
|
|
||||||
|
DLIST_SETUP(dlist_example);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (STICK_MOVED(READ_STICK(0))) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLIST_DONE();
|
||||||
|
}
|
92
presets/c64/test_border_sprites.c
Normal file
92
presets/c64/test_border_sprites.c
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
//#link "common.c"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
//#link "rasterirq.ca65"
|
||||||
|
#include "rasterirq.h"
|
||||||
|
|
||||||
|
//#link "sprites.c"
|
||||||
|
#include "sprites.h"
|
||||||
|
|
||||||
|
#include <cbm_petscii_charmap.h>
|
||||||
|
#include <cc65.h>
|
||||||
|
|
||||||
|
/*{w:24,h:21,bpp:1,brev:1}*/
|
||||||
|
const char spriteshape[3*21] = {
|
||||||
|
0x00,0x7F,0x00,0x01,0xFF,0xC0,0x03,0xFF,0xE0,
|
||||||
|
0x03,0xE7,0xE0,0x07,0xD9,0xF0,0x07,0xDF,0xF0,
|
||||||
|
0x07,0xD9,0xF0,0x03,0xE7,0xE0,0x03,0xFF,0xE0,
|
||||||
|
0x03,0xFF,0xE0,0x02,0xFF,0xA0,0x01,0x7F,0x40,
|
||||||
|
0x01,0x3E,0x40,0x00,0x9C,0x80,0x00,0x9C,0x80,
|
||||||
|
0x00,0x49,0x00,0x00,0x49,0x00,0x00,0x3E,0x00,
|
||||||
|
0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x1C,0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
byte scroll_x = 0;
|
||||||
|
byte scroll_y = 0;
|
||||||
|
|
||||||
|
void dlist_example(void) {
|
||||||
|
VIC.ctrl1 = VIC_CTRL1_DEN | VIC_CTRL1_RSEL;
|
||||||
|
VIC.bordercolor = 5;
|
||||||
|
|
||||||
|
// Flexible line distance (FLD)
|
||||||
|
// this adds a gap of 1-6 scanlines
|
||||||
|
DLIST_NEXT(150);
|
||||||
|
VIC.ctrl1 = (scroll_y & 7) | 0x18;
|
||||||
|
VIC.bordercolor = 2;
|
||||||
|
|
||||||
|
// this opens up the vertical borders
|
||||||
|
// it must be done on the last row (247-249)
|
||||||
|
DLIST_NEXT(249);
|
||||||
|
VIC.ctrl1 = VIC_CTRL1_DEN;
|
||||||
|
|
||||||
|
// move sprites and restart the display list
|
||||||
|
scroll_x++;
|
||||||
|
scroll_y++;
|
||||||
|
VIC.spr0_y++;
|
||||||
|
VIC.spr7_y--;
|
||||||
|
VIC.bordercolor = 4;
|
||||||
|
DLIST_RESTART(30);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SieveOfEratosthenes() {
|
||||||
|
const int n = 1023;
|
||||||
|
int primes[1024];
|
||||||
|
int i,p;
|
||||||
|
memset(primes, 1, sizeof(primes));
|
||||||
|
|
||||||
|
for (p = 2; p*p <= n; p++) {
|
||||||
|
if (primes[p]) {
|
||||||
|
for (i = p*p; i <= n; i += p)
|
||||||
|
primes[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (p = 2; p <= n; p++)
|
||||||
|
if (primes[p])
|
||||||
|
printf("%d ", p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
byte i;
|
||||||
|
|
||||||
|
clrscr();
|
||||||
|
|
||||||
|
sprite_clear();
|
||||||
|
sprite_set_shapes(spriteshape, 192, 1);
|
||||||
|
|
||||||
|
sprshad.spr_exp_x = 0xff;
|
||||||
|
for (i=0; i<8; i++) {
|
||||||
|
sprshad.spr_color[i] = i+3;
|
||||||
|
sprite_draw(i, i*38+24, 248, 192);
|
||||||
|
}
|
||||||
|
sprite_update(DEFAULT_SCREEN);
|
||||||
|
|
||||||
|
DLIST_SETUP(dlist_example);
|
||||||
|
|
||||||
|
// do something complicated while IRQ runs...
|
||||||
|
while (1) {
|
||||||
|
SieveOfEratosthenes();
|
||||||
|
}
|
||||||
|
}
|
143
presets/c64/test_display_list.c
Normal file
143
presets/c64/test_display_list.c
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
//#link "common.c"
|
||||||
|
|
||||||
|
#include "rasterirq.h"
|
||||||
|
//#link "rasterirq.ca65"
|
||||||
|
|
||||||
|
#include "bcd.h"
|
||||||
|
//#link "bcd.c"
|
||||||
|
|
||||||
|
///// DEFINES
|
||||||
|
|
||||||
|
#define GAME_BASE 0x400 // scrolling screen ram
|
||||||
|
#define SCORE_BASE 0x2c00 // scoreboard screen ram
|
||||||
|
|
||||||
|
#define SCROLL_TOP 8 // scroll top row
|
||||||
|
#define SCROLL_ROWS 14 // scroll # of rows
|
||||||
|
#define GROUND_ROW 7 // ground row (+ top row)
|
||||||
|
|
||||||
|
///// VARIABLES
|
||||||
|
|
||||||
|
word scroll_x = 0; // current scroll X position
|
||||||
|
word score = 0; // current player score
|
||||||
|
|
||||||
|
///// FUNCTIONS
|
||||||
|
|
||||||
|
// display list used by rasterirq.h
|
||||||
|
// draws scoreboard and sets scroll register
|
||||||
|
void display_list() {
|
||||||
|
// set x scroll register to scroll value
|
||||||
|
SET_SCROLL_X(scroll_x);
|
||||||
|
// set background color
|
||||||
|
VIC.bgcolor[0] = COLOR_CYAN;
|
||||||
|
// next interrupt is two rows from bottom
|
||||||
|
DLIST_NEXT(250-16);
|
||||||
|
|
||||||
|
// set background color
|
||||||
|
VIC.bgcolor[0] = COLOR_BLUE;
|
||||||
|
// screen memory = 0x2800
|
||||||
|
SET_VIC_SCREEN(SCORE_BASE);
|
||||||
|
// clear x scroll register
|
||||||
|
SET_SCROLL_X(0);
|
||||||
|
// next interrupt is bottom of frame
|
||||||
|
DLIST_NEXT(250);
|
||||||
|
|
||||||
|
// reset screen to 0x400
|
||||||
|
SET_VIC_SCREEN(0x400);
|
||||||
|
// next interrupt is above top of next frame
|
||||||
|
DLIST_RESTART(40);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_scoreboard() {
|
||||||
|
draw_bcd_word(SCRNADR(SCORE_BASE,7,24), score);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_score(int delta) {
|
||||||
|
score = bcd_add(score, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear scoreboard and draw initial strings
|
||||||
|
void init_scoreboard() {
|
||||||
|
memset((void*)SCORE_BASE, ' ', 1024);
|
||||||
|
memcpy((void*)SCRNADR(SCORE_BASE,1,24), "SCORE:", 6);
|
||||||
|
update_scoreboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte get_char_for_row(byte row) {
|
||||||
|
// ground?
|
||||||
|
if (row >= GROUND_ROW) { return 253; }
|
||||||
|
// obstacle?
|
||||||
|
if (row >= GROUND_ROW-3) {
|
||||||
|
// only show obstacle for certain values of scroll_x
|
||||||
|
if ((scroll_x & 0b1110000) == 0) { return 247; }
|
||||||
|
}
|
||||||
|
// default is the sky (empty space)
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_right_column() {
|
||||||
|
// get the top-right corner address of scroll area
|
||||||
|
word addr = SCRNADR(GAME_BASE, 39, SCROLL_TOP);
|
||||||
|
byte row;
|
||||||
|
// draw one character per row
|
||||||
|
for (row=0; row<SCROLL_ROWS; row++) {
|
||||||
|
POKE(addr, get_char_for_row(row));
|
||||||
|
addr += 40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll_one_column_left() {
|
||||||
|
// copy several rows of screen memory
|
||||||
|
// backwards one byte
|
||||||
|
const word start = SCRNADR(GAME_BASE, 0, SCROLL_TOP);
|
||||||
|
const word nbytes = SCROLL_ROWS*40-1;
|
||||||
|
memcpy((byte*)start, (byte*)start+1, nbytes);
|
||||||
|
// draw the right column of characters
|
||||||
|
draw_right_column();
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll_one_pixel_left() {
|
||||||
|
// scroll left one pixel
|
||||||
|
scroll_x -= 1;
|
||||||
|
// set scroll register with lower three bits
|
||||||
|
VIC.ctrl2 = (VIC.ctrl2 & ~7) | (scroll_x & 7);
|
||||||
|
// move screen memory if the scroll register
|
||||||
|
// has just gone past 0 and wrapped to 7
|
||||||
|
if ((scroll_x & 7) == 7) {
|
||||||
|
scroll_one_column_left();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// clear screen, set background color
|
||||||
|
clrscr();
|
||||||
|
VIC.bgcolor[0] = COLOR_CYAN;
|
||||||
|
VIC.bordercolor = COLOR_BLUE;
|
||||||
|
|
||||||
|
// set vertical scroll = 3, 25 rows
|
||||||
|
VIC.ctrl1 = 0b00011011;
|
||||||
|
// set 38 column mode (for X scrolling)
|
||||||
|
VIC.ctrl2 = 0b00000000;
|
||||||
|
// set uniform color of characters
|
||||||
|
memset(COLOR_RAM, COLOR_WHITE, 1000);
|
||||||
|
|
||||||
|
// setup scoreboard
|
||||||
|
init_scoreboard();
|
||||||
|
|
||||||
|
// setup rasterirq library for scoreboard split
|
||||||
|
DLIST_SETUP(display_list);
|
||||||
|
|
||||||
|
// game loop, repeat forever
|
||||||
|
while (1) {
|
||||||
|
// wait for end of frame
|
||||||
|
waitvsync();
|
||||||
|
|
||||||
|
// scroll screen
|
||||||
|
scroll_one_pixel_left();
|
||||||
|
|
||||||
|
// add to score
|
||||||
|
add_score(0x0001);
|
||||||
|
update_scoreboard();
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,6 @@
|
|||||||
// ported from
|
// ported from
|
||||||
// https://odensskjegg.home.blog/2018/12/29/recreating-the-commodore-64-user-guide-code-samples-in-cc65-part-three-sprites/
|
// https://odensskjegg.home.blog/2018/12/29/recreating-the-commodore-64-user-guide-code-samples-in-cc65-part-three-sprites/
|
||||||
|
|
||||||
//#include "common.h"
|
|
||||||
#include <peekpoke.h>
|
#include <peekpoke.h>
|
||||||
#include <c64.h>
|
#include <c64.h>
|
||||||
|
|
||||||
@ -71,13 +70,13 @@ int main (void)
|
|||||||
rx -= 24;
|
rx -= 24;
|
||||||
if (rx >= 0 && rx < 366) {
|
if (rx >= 0 && rx < 366) {
|
||||||
// Set MSB of x coordinate for sprite if x position > 255
|
// Set MSB of x coordinate for sprite if x position > 255
|
||||||
if (x >= 256) {
|
if (rx >= 256) {
|
||||||
msb |= LUT[t]; // look up 1 << t
|
msb |= LUT[t]; // look up 1 << t
|
||||||
}
|
}
|
||||||
VIC.spr_pos[t].x = x;
|
VIC.spr_pos[t].x = rx;
|
||||||
// Y position is an indirect Sinus function of X, using array
|
// Y position is an indirect Sinus function of X, using array
|
||||||
// index for retrieving the Y value
|
// index for retrieving the Y value
|
||||||
VIC.spr_pos[t].y = yValues[x & 63] + 40;
|
VIC.spr_pos[t].y = yValues[rx & 63] + 40;
|
||||||
} else {
|
} else {
|
||||||
VIC.spr_pos[t].x = 0;
|
VIC.spr_pos[t].x = 0;
|
||||||
}
|
}
|
||||||
|
67
presets/vcs/skeleton.remote:llvm-mos
Normal file
67
presets/vcs/skeleton.remote:llvm-mos
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
#include <atari2600.h>
|
||||||
|
#include <vcslib.h>
|
||||||
|
#include <peekpoke.h>
|
||||||
|
#include <mapper.h>
|
||||||
|
|
||||||
|
#ifdef __ATARI2600_MAPPER_3E__
|
||||||
|
MAPPER_CART_ROM_KB(6); // 6K ROM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MAPPER_BANKED_ROM
|
||||||
|
#define ROM_BANK(index) __attribute__((noinline, section(".rom"#index)))
|
||||||
|
#else
|
||||||
|
#define ROM_BANK(index)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define KERNEL_BANK 1
|
||||||
|
|
||||||
|
ROM_BANK(KERNEL_BANK) void my_preframe(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ROM_BANK(KERNEL_BANK) void my_doframe(void) {
|
||||||
|
int i;
|
||||||
|
// Set player sprite color
|
||||||
|
TIA.colup0 = COLOR_CONV(0xfe);
|
||||||
|
// Draw each scanline
|
||||||
|
for (i=0; i<192; i++) {
|
||||||
|
TIA.wsync = 0; // sync to scanline
|
||||||
|
TIA.colubk = i; // set background color
|
||||||
|
TIA.pf1 = i; // set playfield
|
||||||
|
TIA.grp0 = i; // set sprite bitmap
|
||||||
|
}
|
||||||
|
TIA.grp0 = 0; // clear sprite
|
||||||
|
TIA.colubk = 0; // clear background
|
||||||
|
}
|
||||||
|
|
||||||
|
ROM_BANK(KERNEL_BANK) void my_postframe(void) {
|
||||||
|
// additional post-frame processing goes here
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display kernel loop
|
||||||
|
ROM_BANK(KERNEL_BANK) void do_kernel_loop() {
|
||||||
|
// loop until reset released
|
||||||
|
while (SW_RESET()) { }
|
||||||
|
// loop forever
|
||||||
|
while (1) {
|
||||||
|
kernel_1();
|
||||||
|
my_preframe();
|
||||||
|
kernel_2();
|
||||||
|
my_doframe();
|
||||||
|
kernel_3();
|
||||||
|
my_postframe();
|
||||||
|
kernel_4();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
// test banked rom call, if available
|
||||||
|
#ifdef MAPPER_BANKED_ROM
|
||||||
|
banked_call_rom(KERNEL_BANK, do_kernel_loop);
|
||||||
|
#else
|
||||||
|
do_kernel_loop();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,30 +1,30 @@
|
|||||||
|
|
||||||
import { hex, byte2signed } from "./util";
|
import { hex, byte2signed } from "./util";
|
||||||
import { Platform } from "./baseplatform";
|
import { OpcodeMetadata, Platform } from "./baseplatform";
|
||||||
|
|
||||||
const debug = false;
|
const debug = false;
|
||||||
|
|
||||||
export interface CodeAnalyzer {
|
export interface CodeAnalyzer {
|
||||||
showLoopTimingForPC(pc:number);
|
showLoopTimingForPC(pc: number);
|
||||||
pc2clockrange : {[key:number]:ClockRange};
|
pc2clockrange: { [key: number]: ClockRange };
|
||||||
MAX_CLOCKS : number;
|
MAX_CLOCKS: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// VCS TIMING ANALYSIS
|
/// VCS TIMING ANALYSIS
|
||||||
|
|
||||||
// [taken, not taken]
|
// [taken, not taken]
|
||||||
const BRANCH_CONSTRAINTS = [
|
const BRANCH_CONSTRAINTS = [
|
||||||
[{N:0},{N:1}],
|
[{ N: 0 }, { N: 1 }],
|
||||||
[{N:1},{N:0}],
|
[{ N: 1 }, { N: 0 }],
|
||||||
[{V:0},{V:1}],
|
[{ V: 0 }, { V: 1 }],
|
||||||
[{V:1},{V:0}],
|
[{ V: 1 }, { V: 0 }],
|
||||||
[{C:0},{C:1}],
|
[{ C: 0 }, { C: 1 }],
|
||||||
[{C:1},{C:0}],
|
[{ C: 1 }, { C: 0 }],
|
||||||
[{Z:0},{Z:1}],
|
[{ Z: 0 }, { Z: 1 }],
|
||||||
[{Z:1},{Z:0}]
|
[{ Z: 1 }, { Z: 0 }]
|
||||||
];
|
];
|
||||||
|
|
||||||
function constraintEquals(a,b) {
|
function constraintEquals(a, b) {
|
||||||
if (a == null || b == null)
|
if (a == null || b == null)
|
||||||
return null;
|
return null;
|
||||||
for (var n in a) {
|
for (var n in a) {
|
||||||
@ -44,15 +44,15 @@ interface ClockRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class CodeAnalyzer6502 implements CodeAnalyzer {
|
abstract class CodeAnalyzer6502 implements CodeAnalyzer {
|
||||||
pc2clockrange : {[key:number]:ClockRange} = {};
|
pc2clockrange: { [key: number]: ClockRange } = {};
|
||||||
jsrresult : {[key:number]:ClockRange} = {};
|
jsrresult: { [key: number]: ClockRange } = {};
|
||||||
START_CLOCKS : number;
|
START_CLOCKS: number;
|
||||||
MAX_CLOCKS : number;
|
MAX_CLOCKS: number;
|
||||||
WRAP_CLOCKS : boolean;
|
WRAP_CLOCKS: boolean;
|
||||||
platform : Platform;
|
platform: Platform;
|
||||||
MAX_CYCLES : number = 2000;
|
MAX_CYCLES: number = 2000;
|
||||||
|
|
||||||
constructor(platform : Platform) {
|
constructor(platform: Platform) {
|
||||||
this.platform = platform;
|
this.platform = platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,12 +62,12 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer {
|
|||||||
return meta; // minCycles, maxCycles
|
return meta; // minCycles, maxCycles
|
||||||
}
|
}
|
||||||
|
|
||||||
traceInstructions(pc:number, minclocks:number, maxclocks:number, subaddr:number, constraints) {
|
traceInstructions(pc: number, minclocks: number, maxclocks: number, subaddr: number, constraints) {
|
||||||
if (debug) console.log("trace", hex(pc), minclocks, maxclocks);
|
if (debug) console.log("trace", hex(pc), minclocks, maxclocks);
|
||||||
if (!constraints) constraints = {};
|
if (!constraints) constraints = {};
|
||||||
var modified = true;
|
var modified = true;
|
||||||
var abort = false;
|
var abort = false;
|
||||||
for (let i=0; modified && !abort; i++) {
|
for (let i = 0; modified && !abort; i++) {
|
||||||
if (i >= this.MAX_CYCLES) {
|
if (i >= this.MAX_CYCLES) {
|
||||||
console.log("too many cycles @", hex(pc), "routine", hex(subaddr));
|
console.log("too many cycles @", hex(pc), "routine", hex(subaddr));
|
||||||
break;
|
break;
|
||||||
@ -77,10 +77,10 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer {
|
|||||||
// wrap clocks
|
// wrap clocks
|
||||||
minclocks = minclocks % this.MAX_CLOCKS;
|
minclocks = minclocks % this.MAX_CLOCKS;
|
||||||
maxclocks = maxclocks % this.MAX_CLOCKS;
|
maxclocks = maxclocks % this.MAX_CLOCKS;
|
||||||
if (maxclocks == minclocks-1) {
|
if (maxclocks == minclocks - 1) {
|
||||||
if (debug) console.log("0-75", hex(pc), minclocks, maxclocks);
|
if (debug) console.log("0-75", hex(pc), minclocks, maxclocks);
|
||||||
minclocks = 0;
|
minclocks = 0;
|
||||||
maxclocks = this.MAX_CLOCKS-1;
|
maxclocks = this.MAX_CLOCKS - 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// truncate clocks
|
// truncate clocks
|
||||||
@ -88,13 +88,13 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer {
|
|||||||
maxclocks = Math.min(this.MAX_CLOCKS, maxclocks);
|
maxclocks = Math.min(this.MAX_CLOCKS, maxclocks);
|
||||||
}
|
}
|
||||||
let meta = this.getClockCountsAtPC(pc);
|
let meta = this.getClockCountsAtPC(pc);
|
||||||
let lob = this.platform.readAddress(pc+1);
|
let lob = this.platform.readAddress(pc + 1);
|
||||||
let hib = this.platform.readAddress(pc+2);
|
let hib = this.platform.readAddress(pc + 2);
|
||||||
let addr = lob + (hib << 8);
|
let addr = lob + (hib << 8);
|
||||||
let pc0 = pc;
|
let pc0 = pc;
|
||||||
let pcrange = this.pc2clockrange[pc0];
|
let pcrange = this.pc2clockrange[pc0];
|
||||||
if (pcrange == null) {
|
if (pcrange == null) {
|
||||||
this.pc2clockrange[pc0] = pcrange = {minclocks:minclocks, maxclocks:maxclocks};
|
this.pc2clockrange[pc0] = pcrange = { minclocks: minclocks, maxclocks: maxclocks };
|
||||||
if (debug) console.log("new", hex(pc), hex(pc0), hex(subaddr), minclocks, maxclocks);
|
if (debug) console.log("new", hex(pc), hex(pc0), hex(subaddr), minclocks, maxclocks);
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer {
|
|||||||
if (this.WRAP_CLOCKS && (minclocks <= maxclocks) != (pcrange.minclocks <= pcrange.maxclocks)) {
|
if (this.WRAP_CLOCKS && (minclocks <= maxclocks) != (pcrange.minclocks <= pcrange.maxclocks)) {
|
||||||
if (debug) console.log("wrap", hex(pc), hex(pc0), hex(subaddr), minclocks, maxclocks, pcrange);
|
if (debug) console.log("wrap", hex(pc), hex(pc0), hex(subaddr), minclocks, maxclocks, pcrange);
|
||||||
pcrange.minclocks = minclocks = 0;
|
pcrange.minclocks = minclocks = 0;
|
||||||
pcrange.maxclocks = maxclocks = this.MAX_CLOCKS-1;
|
pcrange.maxclocks = maxclocks = this.MAX_CLOCKS - 1;
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
if (minclocks < pcrange.minclocks) {
|
if (minclocks < pcrange.minclocks) {
|
||||||
@ -124,106 +124,88 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer {
|
|||||||
pc += meta.insnlength;
|
pc += meta.insnlength;
|
||||||
var oldconstraints = constraints;
|
var oldconstraints = constraints;
|
||||||
constraints = null;
|
constraints = null;
|
||||||
// TODO: if jump to zero-page, maybe assume RTS?
|
let syncMaxCycles = this.getMaxCyclesForSync(meta, lob, hib);
|
||||||
switch (meta.opcode) {
|
if (typeof syncMaxCycles === 'number') {
|
||||||
case 0x19: case 0x1d:
|
minclocks = 0;
|
||||||
case 0x39: case 0x3d:
|
maxclocks = syncMaxCycles;
|
||||||
case 0x59: case 0x5d:
|
meta.minCycles = meta.maxCycles = 0;
|
||||||
case 0x79: case 0x7d:
|
} else {
|
||||||
case 0xb9: case 0xbb:
|
// TODO: if jump to zero-page, maybe assume RTS?
|
||||||
case 0xbc: case 0xbd: case 0xbe: case 0xbf:
|
switch (meta.opcode) {
|
||||||
case 0xd9: case 0xdd:
|
case 0x19: case 0x1d:
|
||||||
case 0xf9: case 0xfd:
|
case 0x39: case 0x3d:
|
||||||
if (lob == 0) meta.maxCycles -= 1; // no page boundary crossed
|
case 0x59: case 0x5d:
|
||||||
break;
|
case 0x79: case 0x7d:
|
||||||
// TODO: only VCS
|
case 0xb9: case 0xbb:
|
||||||
case 0x85:
|
case 0xbc: case 0xbd: case 0xbe: case 0xbf:
|
||||||
if (lob == 0x2) { // STA WSYNC
|
case 0xd9: case 0xdd:
|
||||||
minclocks = maxclocks = 0;
|
case 0xf9: case 0xfd:
|
||||||
meta.minCycles = meta.maxCycles = 0;
|
if (lob == 0) meta.maxCycles -= 1; // no page boundary crossed
|
||||||
}
|
break;
|
||||||
break;
|
case 0x20: // JSR
|
||||||
// TODO: only NES (sprite 0 poll)
|
// TODO: handle bare RTS case
|
||||||
case 0x2c:
|
minclocks += meta.minCycles;
|
||||||
if (lob == 0x02 && hib == 0x20) { // BIT $2002
|
maxclocks += meta.maxCycles;
|
||||||
minclocks = 0;
|
this.traceInstructions(addr, minclocks, maxclocks, addr, constraints);
|
||||||
maxclocks = 4; // uncertainty b/c of assumed branch poll
|
var result = this.jsrresult[addr];
|
||||||
meta.minCycles = meta.maxCycles = 0;
|
if (result) {
|
||||||
}
|
minclocks = result.minclocks;
|
||||||
break;
|
maxclocks = result.maxclocks;
|
||||||
// TODO: only Apple2 (vapor lock)
|
|
||||||
/*
|
|
||||||
case 0xad:
|
|
||||||
if (lob == 0x61 && hib == 0xc0) { // LDA $C061
|
|
||||||
minclocks = 0;
|
|
||||||
maxclocks = 4; // uncertainty?
|
|
||||||
meta.minCycles = meta.maxCycles = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
*/
|
|
||||||
case 0x20: // JSR
|
|
||||||
// TODO: handle bare RTS case
|
|
||||||
minclocks += meta.minCycles;
|
|
||||||
maxclocks += meta.maxCycles;
|
|
||||||
this.traceInstructions(addr, minclocks, maxclocks, addr, constraints);
|
|
||||||
var result = this.jsrresult[addr];
|
|
||||||
if (result) {
|
|
||||||
minclocks = result.minclocks;
|
|
||||||
maxclocks = result.maxclocks;
|
|
||||||
} else {
|
|
||||||
console.log("No JSR result!", hex(pc), hex(addr));
|
|
||||||
minclocks = maxclocks;
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x4c: // JMP
|
|
||||||
pc = addr; // TODO: make sure in ROM space
|
|
||||||
break;
|
|
||||||
case 0x40: // RTI
|
|
||||||
abort = true;
|
|
||||||
break;
|
|
||||||
case 0x60: // RTS
|
|
||||||
if (subaddr) { // TODO: 0 doesn't work
|
|
||||||
// TODO: combine with previous result
|
|
||||||
var result = this.jsrresult[subaddr];
|
|
||||||
if (!result) {
|
|
||||||
result = {minclocks:minclocks, maxclocks:maxclocks};
|
|
||||||
} else {
|
} else {
|
||||||
result = {
|
console.log("No JSR result!", hex(pc), hex(addr));
|
||||||
minclocks:Math.min(minclocks,result.minclocks),
|
minclocks = maxclocks;
|
||||||
maxclocks:Math.max(maxclocks,result.maxclocks)
|
//return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.jsrresult[subaddr] = result;
|
break;
|
||||||
console.log("RTS", hex(pc), hex(subaddr), this.jsrresult[subaddr]);
|
case 0x4c: // JMP
|
||||||
}
|
pc = addr; // TODO: make sure in ROM space
|
||||||
return;
|
break;
|
||||||
case 0x10: case 0x30: // branch
|
case 0x40: // RTI
|
||||||
case 0x50: case 0x70:
|
|
||||||
case 0x90: case 0xB0:
|
|
||||||
case 0xD0: case 0xF0:
|
|
||||||
var newpc = pc + byte2signed(lob);
|
|
||||||
var crosspage = (pc>>8) != (newpc>>8);
|
|
||||||
if (!crosspage) meta.maxCycles--;
|
|
||||||
// TODO: other instructions might modify flags too
|
|
||||||
var cons = BRANCH_CONSTRAINTS[Math.floor((meta.opcode-0x10)/0x20)];
|
|
||||||
var cons0 = constraintEquals(oldconstraints, cons[0]);
|
|
||||||
var cons1 = constraintEquals(oldconstraints, cons[1]);
|
|
||||||
// recursively trace the taken branch
|
|
||||||
if (true || cons0 !== false) { // TODO?
|
|
||||||
this.traceInstructions(newpc, minclocks+meta.maxCycles, maxclocks+meta.maxCycles, subaddr, cons[0]);
|
|
||||||
}
|
|
||||||
// abort if we will always take the branch
|
|
||||||
if (cons1 === false) {
|
|
||||||
console.log("branch always taken", hex(pc), oldconstraints, cons[1]);
|
|
||||||
abort = true;
|
abort = true;
|
||||||
}
|
break;
|
||||||
constraints = cons[1]; // not taken
|
case 0x60: // RTS
|
||||||
meta.maxCycles = meta.minCycles; // branch not taken, no extra clock(s)
|
if (subaddr) { // TODO: 0 doesn't work
|
||||||
break;
|
// TODO: combine with previous result
|
||||||
case 0x6c:
|
var result = this.jsrresult[subaddr];
|
||||||
console.log("Instruction not supported!", hex(pc), hex(meta.opcode), meta); // TODO
|
if (!result) {
|
||||||
return;
|
result = { minclocks: minclocks, maxclocks: maxclocks };
|
||||||
|
} else {
|
||||||
|
result = {
|
||||||
|
minclocks: Math.min(minclocks, result.minclocks),
|
||||||
|
maxclocks: Math.max(maxclocks, result.maxclocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.jsrresult[subaddr] = result;
|
||||||
|
console.log("RTS", hex(pc), hex(subaddr), this.jsrresult[subaddr]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 0x10: case 0x30: // branch
|
||||||
|
case 0x50: case 0x70:
|
||||||
|
case 0x90: case 0xB0:
|
||||||
|
case 0xD0: case 0xF0:
|
||||||
|
var newpc = pc + byte2signed(lob);
|
||||||
|
var crosspage = (pc >> 8) != (newpc >> 8);
|
||||||
|
if (!crosspage) meta.maxCycles--;
|
||||||
|
// TODO: other instructions might modify flags too
|
||||||
|
var cons = BRANCH_CONSTRAINTS[Math.floor((meta.opcode - 0x10) / 0x20)];
|
||||||
|
var cons0 = constraintEquals(oldconstraints, cons[0]);
|
||||||
|
var cons1 = constraintEquals(oldconstraints, cons[1]);
|
||||||
|
// recursively trace the taken branch
|
||||||
|
if (true || cons0 !== false) { // TODO?
|
||||||
|
this.traceInstructions(newpc, minclocks + meta.maxCycles, maxclocks + meta.maxCycles, subaddr, cons[0]);
|
||||||
|
}
|
||||||
|
// abort if we will always take the branch
|
||||||
|
if (cons1 === false) {
|
||||||
|
console.log("branch always taken", hex(pc), oldconstraints, cons[1]);
|
||||||
|
abort = true;
|
||||||
|
}
|
||||||
|
constraints = cons[1]; // not taken
|
||||||
|
meta.maxCycles = meta.minCycles; // branch not taken, no extra clock(s)
|
||||||
|
break;
|
||||||
|
case 0x6c:
|
||||||
|
console.log("Instruction not supported!", hex(pc), hex(meta.opcode), meta); // TODO
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// add min/max instruction time to min/max clocks bound
|
// add min/max instruction time to min/max clocks bound
|
||||||
if (debug) console.log("add", hex(pc), meta.minCycles, meta.maxCycles);
|
if (debug) console.log("add", hex(pc), meta.minCycles, meta.maxCycles);
|
||||||
@ -232,41 +214,65 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showLoopTimingForPC(pc:number) {
|
showLoopTimingForPC(pc: number) {
|
||||||
this.pc2clockrange = {};
|
this.pc2clockrange = {};
|
||||||
this.jsrresult = {};
|
this.jsrresult = {};
|
||||||
// recurse through all traces
|
// recurse through all traces
|
||||||
this.traceInstructions(pc | this.platform.getOriginPC(), this.START_CLOCKS, this.MAX_CLOCKS, 0, {});
|
this.traceInstructions(pc | this.platform.getOriginPC(), this.START_CLOCKS, this.MAX_CLOCKS, 0, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMaxCyclesForSync(meta: OpcodeMetadata, lob: number, hib: number) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 76 cycles
|
// 76 cycles
|
||||||
export class CodeAnalyzer_vcs extends CodeAnalyzer6502 {
|
export class CodeAnalyzer_vcs extends CodeAnalyzer6502 {
|
||||||
constructor(platform : Platform) {
|
constructor(platform: Platform) {
|
||||||
super(platform);
|
super(platform);
|
||||||
this.MAX_CLOCKS = 76; // 1 scanline
|
this.MAX_CLOCKS = 76; // 1 scanline
|
||||||
this.START_CLOCKS = 0; // TODO?
|
this.START_CLOCKS = 0; // TODO?
|
||||||
this.WRAP_CLOCKS = true;
|
this.WRAP_CLOCKS = true;
|
||||||
}
|
}
|
||||||
|
getMaxCyclesForSync(meta: OpcodeMetadata, lob: number, hib: number) {
|
||||||
|
if (meta.opcode == 0x85) {
|
||||||
|
if (lob == 0x2) { // STA WSYNC
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://wiki.nesdev.com/w/index.php/PPU_rendering#Line-by-line_timing
|
// https://wiki.nesdev.com/w/index.php/PPU_rendering#Line-by-line_timing
|
||||||
// TODO: sprite 0 hit, CPU stalls
|
// TODO: sprite 0 hit, CPU stalls
|
||||||
export class CodeAnalyzer_nes extends CodeAnalyzer6502 {
|
export class CodeAnalyzer_nes extends CodeAnalyzer6502 {
|
||||||
constructor(platform : Platform) {
|
constructor(platform: Platform) {
|
||||||
super(platform);
|
super(platform);
|
||||||
this.MAX_CLOCKS = 114; // 341 clocks for 3 scanlines
|
this.MAX_CLOCKS = 114; // 341 clocks for 3 scanlines
|
||||||
this.START_CLOCKS = 0;
|
this.START_CLOCKS = 0;
|
||||||
this.WRAP_CLOCKS = true;
|
this.WRAP_CLOCKS = true;
|
||||||
}
|
}
|
||||||
|
getMaxCyclesForSync(meta: OpcodeMetadata, lob: number, hib: number) {
|
||||||
|
if (meta.opcode == 0x2c) {
|
||||||
|
if (lob == 0x02 && hib == 0x20) { // BIT $2002
|
||||||
|
return 4; // uncertainty b/c of assumed branch poll
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CodeAnalyzer_apple2 extends CodeAnalyzer6502 {
|
export class CodeAnalyzer_apple2 extends CodeAnalyzer6502 {
|
||||||
constructor(platform : Platform) {
|
constructor(platform: Platform) {
|
||||||
super(platform);
|
super(platform);
|
||||||
this.MAX_CLOCKS = 65;
|
this.MAX_CLOCKS = 65;
|
||||||
this.START_CLOCKS = 0;
|
this.START_CLOCKS = 0;
|
||||||
this.WRAP_CLOCKS = true;
|
this.WRAP_CLOCKS = true;
|
||||||
}
|
}
|
||||||
|
getMaxCyclesForSync(meta: OpcodeMetadata, lob: number, hib: number) {
|
||||||
|
if (meta.opcode == 0xad) {
|
||||||
|
if (lob == 0x61 && hib == 0xc0) { // LDA $C061
|
||||||
|
return 4; // uncertainty b/c of assumed branch poll
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,15 +13,17 @@ const C64_PRESETS = [
|
|||||||
{id:'joymove.c', name:'Sprite Movement'},
|
{id:'joymove.c', name:'Sprite Movement'},
|
||||||
{id:'sprite_collision.c', name:'Sprite Collision'},
|
{id:'sprite_collision.c', name:'Sprite Collision'},
|
||||||
{id:'scroll1.c', name:'Scrolling (Single Buffer)'},
|
{id:'scroll1.c', name:'Scrolling (Single Buffer)'},
|
||||||
|
{id:'test_display_list.c', name:'Display List / Raster IRQ'},
|
||||||
|
{id:'scrolling_text.c', name:'Big Scrolling Text'},
|
||||||
|
{id:'side_scroller.c', name:'Side-Scrolling Game'},
|
||||||
{id:'scroll2.c', name:'Scrolling (Double Buffer)'},
|
{id:'scroll2.c', name:'Scrolling (Double Buffer)'},
|
||||||
{id:'scroll3.c', name:'Scrolling (Multidirectional)'},
|
{id:'scroll3.c', name:'Scrolling (Multidirectional)'},
|
||||||
{id:'scroll4.c', name:'Scrolling (Color RAM Buffering)'},
|
{id:'scroll4.c', name:'Scrolling (Color RAM Buffering)'},
|
||||||
{id:'scroll5.c', name:'Scrolling (Camera Following)'},
|
{id:'scroll5.c', name:'Scrolling (Camera Following)'},
|
||||||
{id:'side_scroller.c', name:'Side-Scrolling Game'},
|
{id:'scrollingmap1.c', name:'Scrolling Tile Map'},
|
||||||
{id:'fullscrollgame.c', name:'Full-Scrolling Game'},
|
{id:'fullscrollgame.c', name:'Full-Scrolling Game'},
|
||||||
{id:'test_multiplex.c', name:'Sprite Retriggering'},
|
{id:'test_multiplex.c', name:'Sprite Retriggering'},
|
||||||
{id:'test_multispritelib.c', name:'Sprite Multiplexing Library'},
|
{id:'test_multispritelib.c', name:'Sprite Multiplexing Library'},
|
||||||
{id:'scrolling_text.c', name:'Big Scrolling Text'},
|
|
||||||
{id:'mcbitmap.c', name:'Multicolor Bitmap Mode'},
|
{id:'mcbitmap.c', name:'Multicolor Bitmap Mode'},
|
||||||
{id:'testlz4.c', name:'LZ4 Bitmap Compression'},
|
{id:'testlz4.c', name:'LZ4 Bitmap Compression'},
|
||||||
//{id:'mandel.c', name:'Mandelbrot Fractal'},
|
//{id:'mandel.c', name:'Mandelbrot Fractal'},
|
||||||
@ -29,6 +31,9 @@ const C64_PRESETS = [
|
|||||||
//{id:'sidtune.dasm', name:'Tiny SID Tune (ASM)'},
|
//{id:'sidtune.dasm', name:'Tiny SID Tune (ASM)'},
|
||||||
{id:'siddemo.c', name:'SID Player Demo'},
|
{id:'siddemo.c', name:'SID Player Demo'},
|
||||||
{id:'climber.c', name:'Climber Game'},
|
{id:'climber.c', name:'Climber Game'},
|
||||||
|
{id:'test_border_sprites.c', name:'Sprites in the Borders'},
|
||||||
|
{id:'sprite_stretch.c', name:'Sprite Stretching'},
|
||||||
|
{id:'plasma.c', name:'Plasma Demo'},
|
||||||
{id:'hello.wiz', name:'Hello Wiz (Wiz)'},
|
{id:'hello.wiz', name:'Hello Wiz (Wiz)'},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ const LLVM_MOS_TOOL: ServerBuildTool = {
|
|||||||
default: {
|
default: {
|
||||||
binpath: 'llvm-mos/bin',
|
binpath: 'llvm-mos/bin',
|
||||||
command: 'mos-clang',
|
command: 'mos-clang',
|
||||||
args: ['-Os', '-g', '-o', '$OUTFILE', '$INFILES'],
|
args: ['-Os', '-g', '-D', '__8BITWORKSHOP__', '-o', '$OUTFILE', '$INFILES'],
|
||||||
},
|
},
|
||||||
debug: { // TODO
|
debug: { // TODO
|
||||||
binpath: 'llvm-mos/bin',
|
binpath: 'llvm-mos/bin',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user