diff --git a/presets/vector-z80color/game.c b/presets/vector-z80color/game.c index 9cb0c8d1..809cc965 100644 --- a/presets/vector-z80color/game.c +++ b/presets/vector-z80color/game.c @@ -1,45 +1,74 @@ #include -#include -#include typedef unsigned char byte; -typedef unsigned short word; typedef signed char sbyte; +typedef unsigned short word; +typedef enum { false, true } bool; -word __at(0xa000) dvgram[0x1000]; -byte __at(0x8840) _dvgstart; +byte __at (0x0) vidmem[152][256]; // 304x256x4bpp video memory +byte __at (0xc000) palette[16]; +volatile byte __at (0xc804) input0; +volatile byte __at (0xc806) input1; +volatile byte __at (0xc80c) input2; +byte __at (0xc900) rom_select; +volatile byte __at (0xcb00) video_counter; +byte __at (0xcbff) watchdog0x39; +byte __at (0xcc00) nvram[0x400]; -volatile int __at(0x8100) mathbox_sum; -sbyte __at(0x8102) mathbox_arg1; -sbyte __at(0x8103) mathbox_arg2; -byte __at(0x810f) mathbox_go_mul; +__sfr __at (0) debug; -volatile byte __at (0x8000) input0; -volatile byte __at (0x8001) input1; -volatile byte __at (0x8002) input2; -volatile byte __at (0x800f) vidframe; -byte __at (0x8980) watchdog; +// blitter flags +#define SRCSCREEN 0x1 +#define DSTSCREEN 0x2 +#define ESYNC 0x4 +#define FGONLY 0x8 +#define SOLID 0x10 +#define RSHIFT 0x20 +#define EVENONLY 0x40 +#define ODDONLY 0x80 -#define LEFT1 !(input1 & 0x8) -#define RIGHT1 !(input1 & 0x4) -#define UP1 !(input1 & 0x10) -#define DOWN1 !(input1 & 0x20) -#define FIRE1 !(input1 & 0x2) -#define BOMB1 !(input1 & 0x1) -#define COIN1 (input0 & 0x2) -#define COIN2 (input0 & 0x1) -#define START1 (input2 & 0x20) -#define START2 (input2 & 0x40) +struct { + byte flags; + byte solid; + word sstart; + word dstart; + byte width; + byte height; +} __at (0xca00) blitter; + +// switch flags +#define UP1 (input0 & 0x1) +#define DOWN1 (input0 & 0x2) +#define LEFT1 (input0 & 0x4) +#define RIGHT1 (input0 & 0x8) +#define START1 (input0 & 0x10) +#define START2 (input0 & 0x20) +#define UP2 (input0 & 0x40) +#define DOWN2 (input0 & 0x80) +#define LEFT2 (input1 & 0x1) +#define RIGHT2 (input1 & 0x2) +#define AUTOUP (input2 & 0x1) +#define ADVANCE (input2 & 0x2) +#define COIN2 (input2 & 0x4) +#define HIGHSCORERESET (input2 & 0x8) +#define COIN1 (input2 & 0x10) +#define COIN3 (input2 & 0x20) +#define TILTSWITCH (input2 & 0x40) +#define SOUNDACK (input2 & 0x80) + +#define WATCHDOG watchdog0x39=0x39; // void main(); void _sdcc_heap_init(void); // for malloc() +// start routine @ 0x0 +// set stack pointer, enable interrupts void start() { __asm - LD SP,#0x0 + LD SP,#0xc000 DI ; copy initialized data LD BC, #l__INITIALIZER @@ -48,321 +77,486 @@ __asm LD HL, #s__INITIALIZER LDIR __endasm; - // init heap for malloc() and run main pgm. + _sdcc_heap_init(); main(); } -// VECTOR ROUTINES +#define LOCHAR 0x21 +#define HICHAR 0x5e -int dvgwrofs; // write offset for DVG buffer +const byte font8x8[HICHAR-LOCHAR+1][8] = { +{ 0x18,0x18,0x18,0x18,0x00,0x00,0x18,0x00 }, { 0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00 }, { 0x66,0x66,0xff,0x66,0xff,0x66,0x66,0x00 }, { 0x18,0x3e,0x60,0x3c,0x06,0x7c,0x18,0x00 }, { 0x62,0x66,0x0c,0x18,0x30,0x66,0x46,0x00 }, { 0x3c,0x66,0x3c,0x38,0x67,0x66,0x3f,0x00 }, { 0x06,0x0c,0x18,0x00,0x00,0x00,0x00,0x00 }, { 0x0c,0x18,0x30,0x30,0x30,0x18,0x0c,0x00 }, { 0x30,0x18,0x0c,0x0c,0x0c,0x18,0x30,0x00 }, { 0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00 }, { 0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30 }, { 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00 }, { 0x00,0x03,0x06,0x0c,0x18,0x30,0x60,0x00 }, { 0x3c,0x66,0x6e,0x76,0x66,0x66,0x3c,0x00 }, { 0x18,0x18,0x38,0x18,0x18,0x18,0x7e,0x00 }, { 0x3c,0x66,0x06,0x0c,0x30,0x60,0x7e,0x00 }, { 0x3c,0x66,0x06,0x1c,0x06,0x66,0x3c,0x00 }, { 0x06,0x0e,0x1e,0x66,0x7f,0x06,0x06,0x00 }, { 0x7e,0x60,0x7c,0x06,0x06,0x66,0x3c,0x00 }, { 0x3c,0x66,0x60,0x7c,0x66,0x66,0x3c,0x00 }, { 0x7e,0x66,0x0c,0x18,0x18,0x18,0x18,0x00 }, { 0x3c,0x66,0x66,0x3c,0x66,0x66,0x3c,0x00 }, { 0x3c,0x66,0x66,0x3e,0x06,0x66,0x3c,0x00 }, { 0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00 }, { 0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30 }, { 0x0e,0x18,0x30,0x60,0x30,0x18,0x0e,0x00 }, { 0x00,0x00,0x7e,0x00,0x7e,0x00,0x00,0x00 }, { 0x70,0x18,0x0c,0x06,0x0c,0x18,0x70,0x00 }, { 0x3c,0x66,0x06,0x0c,0x18,0x00,0x18,0x00 }, { 0x3c,0x66,0x6e,0x6e,0x60,0x62,0x3c,0x00 }, { 0x18,0x3c,0x66,0x7e,0x66,0x66,0x66,0x00 }, { 0x7c,0x66,0x66,0x7c,0x66,0x66,0x7c,0x00 }, { 0x3c,0x66,0x60,0x60,0x60,0x66,0x3c,0x00 }, { 0x78,0x6c,0x66,0x66,0x66,0x6c,0x78,0x00 }, { 0x7e,0x60,0x60,0x78,0x60,0x60,0x7e,0x00 }, { 0x7e,0x60,0x60,0x78,0x60,0x60,0x60,0x00 }, { 0x3c,0x66,0x60,0x6e,0x66,0x66,0x3c,0x00 }, { 0x66,0x66,0x66,0x7e,0x66,0x66,0x66,0x00 }, { 0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0x00 }, { 0x1e,0x0c,0x0c,0x0c,0x0c,0x6c,0x38,0x00 }, { 0x66,0x6c,0x78,0x70,0x78,0x6c,0x66,0x00 }, { 0x60,0x60,0x60,0x60,0x60,0x60,0x7e,0x00 }, { 0x63,0x77,0x7f,0x6b,0x63,0x63,0x63,0x00 }, { 0x66,0x76,0x7e,0x7e,0x6e,0x66,0x66,0x00 }, { 0x3c,0x66,0x66,0x66,0x66,0x66,0x3c,0x00 }, { 0x7c,0x66,0x66,0x7c,0x60,0x60,0x60,0x00 }, { 0x3c,0x66,0x66,0x66,0x66,0x3c,0x0e,0x00 }, { 0x7c,0x66,0x66,0x7c,0x78,0x6c,0x66,0x00 }, { 0x3c,0x66,0x60,0x3c,0x06,0x66,0x3c,0x00 }, { 0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x00 }, { 0x66,0x66,0x66,0x66,0x66,0x66,0x3c,0x00 }, { 0x66,0x66,0x66,0x66,0x66,0x3c,0x18,0x00 }, { 0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00 }, { 0x66,0x66,0x3c,0x18,0x3c,0x66,0x66,0x00 }, { 0x66,0x66,0x66,0x3c,0x18,0x18,0x18,0x00 }, { 0x7e,0x06,0x0c,0x18,0x30,0x60,0x7e,0x00 }, { 0x3c,0x30,0x30,0x30,0x30,0x30,0x3c,0x00 }, { 0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x00 }, { 0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00 }, { 0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18 } +}; -inline word ___swapw(word j) { +static byte font_table[HICHAR-LOCHAR+1][8*4]; + +void fill_char_table_entry(char ch) { + const byte* src = &font8x8[ch-LOCHAR][0]; + byte* data = &font_table[ch-LOCHAR][0]; + int i,j,pixels; + for (i=0; i<8; i++) { + byte b = *src++; + for (j=0; j<4; j++) { + pixels = 0; + if (b & 0x80) pixels |= 0xf0; + if (b & 0x40) pixels |= 0x0f; + *data++ = pixels; + b <<= 2; + } + } + WATCHDOG; +} + +void fill_char_table() { + char ch; + for (ch=LOCHAR; ch<=HICHAR; ch++) { + fill_char_table_entry(ch); + } +} + +inline word swapw(word j) { return ((j << 8) | (j >> 8)); } -inline void dvgreset() { - dvgwrofs = 0; +// x1: 0-151 +// y1: 0-255 +inline void blit_solid(byte x1, byte y1, byte w, byte h, byte color) { + blitter.width = w^4; + blitter.height = h^4; + blitter.dstart = x1+y1*256; // swapped + blitter.solid = color; + blitter.flags = DSTSCREEN|SOLID; } -inline void dvgstart() { - _dvgstart = 0; +inline void draw_solid(word x1, byte y1, byte w, byte h, byte color) { + blitter.width = w^4; + blitter.height = h^4; + blitter.dstart = (x1>>1)+y1*256; // swapped + blitter.solid = color; + blitter.flags = (x1&1) ? DSTSCREEN|SOLID|RSHIFT : DSTSCREEN|SOLID; } -void dvgwrite(word w) { - dvgram[dvgwrofs++] = w; +inline void draw_vline(word x1, byte y1, byte h, byte color) { + blitter.width = 1^4; + blitter.height = h^4; + blitter.dstart = (x1>>1)+y1*256; // swapped + blitter.solid = color; + blitter.flags = (x1&1) ? DSTSCREEN|SOLID|ODDONLY : DSTSCREEN|SOLID|EVENONLY; } -inline void VCTR(int dx, int dy, byte bright) { - dvgwrite((dy & 0x1fff)); - dvgwrite(((bright & 7) << 13) | (dx & 0x1fff)); +inline void blit_copy(byte x1, byte y1, byte w, byte h, const byte* data) { + blitter.width = w^4; + blitter.height = h^4; + blitter.sstart = swapw((word)data); + blitter.dstart = x1+y1*256; // swapped + blitter.flags = DSTSCREEN|FGONLY; } -inline void SVEC(signed char dx, signed char dy, byte bright) { - dvgwrite(0x4000 | (dx & 0x1f) | ((bright&7)<<5) | ((dy & 0x1f)<<8)); +inline void blit_copy_solid(byte x1, byte y1, byte w, byte h, const byte* data, byte solid) { + blitter.width = w^4; + blitter.height = h^4; + blitter.sstart = swapw((word)data); + blitter.dstart = x1+y1*256; // swapped + blitter.solid = solid; + blitter.flags = DSTSCREEN|FGONLY|SOLID; } -inline void JSRL(word offset) { - dvgwrite(0xa000 | offset); +// bias sprites by +12 pixels +#define XBIAS 6 + +inline void draw_sprite(const byte* data, byte x, byte y) { + blitter.width = data[0]^4; + blitter.height = data[1]^4; + blitter.sstart = swapw((word)(data+2)); + blitter.dstart = (x>>1)+y*256+XBIAS; // swapped + blitter.flags = (x&1) ? DSTSCREEN|FGONLY|RSHIFT : DSTSCREEN|FGONLY; } -inline void JMPL(word offset) { - dvgwrite(0xe000 | offset); +inline void draw_sprite_solid(const byte* data, byte x, byte y, byte color) { + blitter.width = data[0]^4; + blitter.height = data[1]^4; + blitter.sstart = swapw((word)(data+2)); + blitter.dstart = (x>>1)+y*256+XBIAS; // swapped + blitter.solid = color; + blitter.flags = (x&1) ? DSTSCREEN|FGONLY|RSHIFT|SOLID : DSTSCREEN|FGONLY|SOLID; } -inline void RTSL() { - dvgwrite(0xc000); -} - -inline void CNTR() { - dvgwrite(0x8000); -} - -inline void HALT() { - dvgwrite(0x2000); -} - -inline void STAT(byte rgb, byte intens) { - dvgwrite(0x6000 | ((intens & 0xf)<<4) | (rgb & 7)); -} - -inline void STAT_sparkle(byte intens) { - dvgwrite(0x6800 | ((intens & 0xf)<<4)); -} - -inline void SCAL(word scale) { - dvgwrite(0x7000 | scale); -} - -enum { - BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, YELLOW, WHITE -} Color; - - -/// https://trmm.net/Asteroids_font - -#define P(x,y) ((((x) & 0xF) << 4) | (((y) & 0xF) << 0)) -#define FONT_UP 0xFE -#define FONT_LAST 0xFF - -const byte vecfont[95][8] = { - ['0' - 0x20] = { P(0,0), P(8,0), P(8,12), P(0,12), P(0,0), P(8,12), FONT_LAST }, - ['1' - 0x20] = { P(4,0), P(4,12), P(3,10), FONT_LAST }, - ['2' - 0x20] = { P(0,12), P(8,12), P(8,7), P(0,5), P(0,0), P(8,0), FONT_LAST }, - ['3' - 0x20] = { P(0,12), P(8,12), P(8,0), P(0,0), FONT_UP, P(0,6), P(8,6), FONT_LAST }, - ['4' - 0x20] = { P(0,12), P(0,6), P(8,6), FONT_UP, P(8,12), P(8,0), FONT_LAST }, - ['5' - 0x20] = { P(0,0), P(8,0), P(8,6), P(0,7), P(0,12), P(8,12), FONT_LAST }, - ['6' - 0x20] = { P(0,12), P(0,0), P(8,0), P(8,5), P(0,7), FONT_LAST }, - ['7' - 0x20] = { P(0,12), P(8,12), P(8,6), P(4,0), FONT_LAST }, - ['8' - 0x20] = { P(0,0), P(8,0), P(8,12), P(0,12), P(0,0), FONT_UP, P(0,6), P(8,6), }, - ['9' - 0x20] = { P(8,0), P(8,12), P(0,12), P(0,7), P(8,5), FONT_LAST }, - [' ' - 0x20] = { FONT_LAST }, - ['.' - 0x20] = { P(3,0), P(4,0), FONT_LAST }, - [',' - 0x20] = { P(2,0), P(4,2), FONT_LAST }, - ['-' - 0x20] = { P(2,6), P(6,6), FONT_LAST }, - ['+' - 0x20] = { P(1,6), P(7,6), FONT_UP, P(4,9), P(4,3), FONT_LAST }, - ['!' - 0x20] = { P(4,0), P(3,2), P(5,2), P(4,0), FONT_UP, P(4,4), P(4,12), FONT_LAST }, - ['#' - 0x20] = { P(0,4), P(8,4), P(6,2), P(6,10), P(8,8), P(0,8), P(2,10), P(2,2) }, - ['^' - 0x20] = { P(2,6), P(4,12), P(6,6), FONT_LAST }, - ['=' - 0x20] = { P(1,4), P(7,4), FONT_UP, P(1,8), P(7,8), FONT_LAST }, - ['*' - 0x20] = { P(0,0), P(4,12), P(8,0), P(0,8), P(8,8), P(0,0), FONT_LAST }, - ['_' - 0x20] = { P(0,0), P(8,0), FONT_LAST }, - ['/' - 0x20] = { P(0,0), P(8,12), FONT_LAST }, - ['\\' - 0x20] = { P(0,12), P(8,0), FONT_LAST }, - ['@' - 0x20] = { P(8,4), P(4,0), P(0,4), P(0,8), P(4,12), P(8,8), P(4,4), P(3,6) }, - ['$' - 0x20] = { P(6,2), P(2,6), P(6,10), FONT_UP, P(4,12), P(4,0), FONT_LAST }, - ['&' - 0x20] = { P(8,0), P(4,12), P(8,8), P(0,4), P(4,0), P(8,4), FONT_LAST }, - ['[' - 0x20] = { P(6,0), P(2,0), P(2,12), P(6,12), FONT_LAST }, - [']' - 0x20] = { P(2,0), P(6,0), P(6,12), P(2,12), FONT_LAST }, - ['(' - 0x20] = { P(6,0), P(2,4), P(2,8), P(6,12), FONT_LAST }, - [')' - 0x20] = { P(2,0), P(6,4), P(6,8), P(2,12), FONT_LAST }, - ['{' - 0x20] = { P(6,0), P(4,2), P(4,10), P(6,12), FONT_UP, P(2,6), P(4,6), FONT_LAST }, - ['}' - 0x20] = { P(4,0), P(6,2), P(6,10), P(4,12), FONT_UP, P(6,6), P(8,6), FONT_LAST }, - ['%' - 0x20] = { P(0,0), P(8,12), FONT_UP, P(2,10), P(2,8), FONT_UP, P(6,4), P(6,2) }, - ['<' - 0x20] = { P(6,0), P(2,6), P(6,12), FONT_LAST }, - ['>' - 0x20] = { P(2,0), P(6,6), P(2,12), FONT_LAST }, - ['|' - 0x20] = { P(4,0), P(4,5), FONT_UP, P(4,6), P(4,12), FONT_LAST }, - [':' - 0x20] = { P(4,9), P(4,7), FONT_UP, P(4,5), P(4,3), FONT_LAST }, - [';' - 0x20] = { P(4,9), P(4,7), FONT_UP, P(4,5), P(1,2), FONT_LAST }, - ['"' - 0x20] = { P(2,10), P(2,6), FONT_UP, P(6,10), P(6,6), FONT_LAST }, - ['\'' - 0x20] = { P(2,6), P(6,10), FONT_LAST }, - ['`' - 0x20] = { P(2,10), P(6,6), FONT_LAST }, - ['~' - 0x20] = { P(0,4), P(2,8), P(6,4), P(8,8), FONT_LAST }, - ['?' - 0x20] = { P(0,8), P(4,12), P(8,8), P(4,4), FONT_UP, P(4,1), P(4,0), FONT_LAST }, - ['A' - 0x20] = { P(0,0), P(0,8), P(4,12), P(8,8), P(8,0), FONT_UP, P(0,4), P(8,4) }, - ['B' - 0x20] = { P(0,0), P(0,12), P(4,12), P(8,10), P(4,6), P(8,2), P(4,0), P(0,0) }, - ['C' - 0x20] = { P(8,0), P(0,0), P(0,12), P(8,12), FONT_LAST }, - ['D' - 0x20] = { P(0,0), P(0,12), P(4,12), P(8,8), P(8,4), P(4,0), P(0,0), FONT_LAST }, - ['E' - 0x20] = { P(8,0), P(0,0), P(0,12), P(8,12), FONT_UP, P(0,6), P(6,6), FONT_LAST }, - ['F' - 0x20] = { P(0,0), P(0,12), P(8,12), FONT_UP, P(0,6), P(6,6), FONT_LAST }, - ['G' - 0x20] = { P(6,6), P(8,4), P(8,0), P(0,0), P(0,12), P(8,12), FONT_LAST }, - ['H' - 0x20] = { P(0,0), P(0,12), FONT_UP, P(0,6), P(8,6), FONT_UP, P(8,12), P(8,0) }, - ['I' - 0x20] = { P(0,0), P(8,0), FONT_UP, P(4,0), P(4,12), FONT_UP, P(0,12), P(8,12) }, - ['J' - 0x20] = { P(0,4), P(4,0), P(8,0), P(8,12), FONT_LAST }, - ['K' - 0x20] = { P(0,0), P(0,12), FONT_UP, P(8,12), P(0,6), P(6,0), FONT_LAST }, - ['L' - 0x20] = { P(8,0), P(0,0), P(0,12), FONT_LAST }, - ['M' - 0x20] = { P(0,0), P(0,12), P(4,8), P(8,12), P(8,0), FONT_LAST }, - ['N' - 0x20] = { P(0,0), P(0,12), P(8,0), P(8,12), FONT_LAST }, - ['O' - 0x20] = { P(0,0), P(0,12), P(8,12), P(8,0), P(0,0), FONT_LAST }, - ['P' - 0x20] = { P(0,0), P(0,12), P(8,12), P(8,6), P(0,5), FONT_LAST }, - ['Q' - 0x20] = { P(0,0), P(0,12), P(8,12), P(8,4), P(0,0), FONT_UP, P(4,4), P(8,0) }, - ['R' - 0x20] = { P(0,0), P(0,12), P(8,12), P(8,6), P(0,5), FONT_UP, P(4,5), P(8,0) }, - ['S' - 0x20] = { P(0,2), P(2,0), P(8,0), P(8,5), P(0,7), P(0,12), P(6,12), P(8,10) }, - ['T' - 0x20] = { P(0,12), P(8,12), FONT_UP, P(4,12), P(4,0), FONT_LAST }, - ['U' - 0x20] = { P(0,12), P(0,2), P(4,0), P(8,2), P(8,12), FONT_LAST }, - ['V' - 0x20] = { P(0,12), P(4,0), P(8,12), FONT_LAST }, - ['W' - 0x20] = { P(0,12), P(2,0), P(4,4), P(6,0), P(8,12), FONT_LAST }, - ['X' - 0x20] = { P(0,0), P(8,12), FONT_UP, P(0,12), P(8,0), FONT_LAST }, - ['Y' - 0x20] = { P(0,12), P(4,6), P(8,12), FONT_UP, P(4,6), P(4,0), FONT_LAST }, - ['Z' - 0x20] = { P(0,12), P(8,12), P(0,0), P(8,0), FONT_UP, P(2,6), P(6,6), FONT_LAST }, -}; - -void draw_char(char ch) { - const byte* p = vecfont[ch-0x20]; - byte bright = 0; - byte x = 0; - byte y = 0; +void draw_sprite_strided(const byte* data, byte x, byte y, byte stride) { byte i; - for (i=0; i<8; i++) { - byte b = *p++; - if (b == FONT_LAST) break; // last move - else if (b == FONT_UP) bright = 0; // pen up - else { - byte x2 = b>>4; - byte y2 = b&15; - SVEC((char)(x2-x), (char)(y2-y), bright); - bright = 2; - x = x2; - y = y2; + byte width = data[0]; + byte height = data[1]; + byte yy = y-(height*(stride-1)/2); + word dest = (x>>1)+yy*256+XBIAS; + blitter.width = width^4; + blitter.height = 1^4; + for (i=0; i>1)+yy*256+XBIAS; + blitter.width = width^4; + blitter.height = 1^4; + blitter.solid = 0; + for (i=0; i HICHAR) return; + blit_copy_solid(x, y, 4, 8, font_table[ch - LOCHAR], color); +} + +void draw_string(const char* str, byte x, byte y, byte color) { while (*str) { - JSRL(font_shapes[*str++ - 0x20]); - if (spacing) SVEC(spacing, 0, 0); + draw_char(*str++, x, y, color); + x += 4; } } -// MATH/3D ROUTINES - -typedef struct { - sbyte m[3][3]; -} Matrix; - -typedef struct { - sbyte x,y,z; -} Vector8; - -typedef struct { - int x,y,z; -} Vector16; - -typedef struct { - byte numverts; - const Vector8* verts; // array of vertices - const sbyte* edges; // array of vertex indices (edges) -} Wireframe; - -void mat_identity(Matrix* m) { - memset(m, 0, sizeof(*m)); - m->m[0][0] = 127; - m->m[1][1] = 127; - m->m[2][2] = 127; +void draw_box(word x1, byte y1, word x2, byte y2, byte color) { + draw_solid(x1, y1, (x2-x1)>>1, 1, color); + draw_solid(x1, y2, (x2-x1)>>1, 1, color); + draw_vline(x1, y1, y2-y1, color); + draw_vline(x2, y1, y2-y1, color); } -inline void mul16(sbyte a, sbyte b) { - mathbox_arg1 = a; - mathbox_arg2 = b; - mathbox_go_mul=0; -} +// GRAPHIC DATA -void vec_mat_transform(Vector16* dest, const Vector8* v, const Matrix* m) { - byte i; - int* result = &dest->x; - const sbyte* mval = &m->m[0][0]; - for (i=0; i<3; i++) { - mathbox_sum = 0; - mul16(*mval++, v->x); - mul16(*mval++, v->y); - mul16(*mval++, v->z); - *result++ = mathbox_sum; - } -} +const byte palette_data[16] = { + 0x00, 0x03, 0x19, 0x50, 0x52, 0x07, 0x1f, 0x37, 0xe0, 0xa4, 0xfd, 0xff, 0x38, 0x70, 0x7f, 0xf8, }; -const sbyte sintbl[64] = { -0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, -49, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, -90, 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, 115, 116, -117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127, +const byte laser_vert[2+1*8] = { + 1,8, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, }; -sbyte isin(byte x0) { - byte x = x0; - if (x0 & 0x40) x = 127-x; - if (x0 & 0x80) { - return -sintbl[x+128]; +const byte laser_horiz[2+4*2] = { + 4,2, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, +}; + + +const byte playersprite1[] = { +7,16, +0x00,0x00,0xef,0xef,0xe0,0x00,0x00, +0x00,0xee,0xee,0xfe,0xee,0xe0,0x00, +0x0e,0xed,0xef,0xef,0xed,0xee,0x00, +0x0e,0xee,0xdd,0xdd,0xde,0xee,0x00, +0x0e,0xee,0xed,0xde,0xee,0xee,0x00, +0x00,0xee,0xee,0xde,0xee,0xe0,0x00, +0x00,0xee,0xee,0xde,0xee,0xe0,0x00, +0x00,0x00,0xed,0xdd,0xe0,0x00,0x0d, +0xdd,0xdd,0xee,0xee,0xed,0xdd,0xd0, +0x0d,0xee,0xee,0xee,0xee,0xee,0x00, +0x0e,0xe0,0xee,0xee,0xe0,0xee,0x00, +0x0e,0xe0,0xee,0xee,0xe0,0xee,0x00, +0x0e,0xe0,0xdd,0xdd,0xd0,0xde,0x00, +0x0d,0x00,0xee,0x0e,0xe0,0x0d,0x00, +0x00,0x00,0xed,0x0e,0xe0,0x00,0x00, +0x00,0x0d,0xdd,0x0d,0xdd,0x00,0x18, +}; + +const byte sprite1[2+16*16/2] = { +8,16, +0x00,0x09,0x99,0x00,0x00,0x99,0x90,0x00, +0x00,0x94,0x94,0x90,0x09,0x49,0x49,0x00, +0x04,0x49,0x49,0x90,0x09,0x94,0x94,0x90, +0x94,0x99,0x94,0x90,0x09,0x49,0x99,0x49, +0x99,0x99,0x49,0x93,0x39,0x94,0x99,0x99, +0x04,0x49,0x99,0x94,0x49,0x99,0x94,0x90, +0x00,0x94,0x94,0x43,0x34,0x49,0x49,0x00, +0x00,0x09,0x43,0x94,0x49,0x34,0x90,0x00, +0x00,0x90,0x00,0x39,0x93,0x00,0x09,0x00, +0x00,0x09,0x83,0x33,0x33,0x33,0x90,0x00, +0x00,0x09,0x32,0x23,0x32,0x23,0x90,0x00, +0x00,0x03,0x03,0x23,0x82,0x30,0x30,0x00, +0x03,0x30,0x00,0x33,0x33,0x00,0x03,0x30, +0x00,0x30,0x03,0x00,0x00,0x30,0x03,0x00, +0x00,0x00,0x00,0x30,0x03,0x00,0x00,0x00, +0x00,0x09,0x99,0x00,0x00,0x99,0x90,0x00, +}; + +const byte sprite2[2+16*16/2] = { +8,16, +0x00,0x94,0x94,0x90,0x09,0x49,0x49,0x00, +0x04,0x49,0x49,0x90,0x09,0x94,0x94,0x90, +0x94,0x99,0x94,0x90,0x09,0x49,0x99,0x49, +0x99,0x99,0x49,0x93,0x39,0x94,0x99,0x99, +0x04,0x49,0x99,0x94,0x49,0x99,0x94,0x90, +0x00,0x94,0x94,0x43,0x34,0x49,0x49,0x00, +0x00,0x09,0x43,0x94,0x49,0x34,0x90,0x00, +0x00,0x90,0x00,0x39,0x93,0x00,0x09,0x00, +0x00,0x09,0x83,0x33,0x33,0x33,0x90,0x00, +0x00,0x09,0x32,0x23,0x32,0x23,0x90,0x00, +0x00,0x03,0x03,0x23,0x82,0x30,0x30,0x00, +0x03,0x30,0x00,0x33,0x33,0x00,0x03,0x30, +0x00,0x30,0x03,0x00,0x00,0x30,0x03,0x00, +0x00,0x00,0x00,0x30,0x03,0x00,0x00,0x00, +0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00, +}; + +const byte sprite3[2+16*16/2] = { +8,16, +0x00,0x00,0x00,0x01,0x10,0x00,0x00,0x00, +0x00,0x00,0x00,0x11,0x11,0x00,0x00,0x00, +0x00,0x00,0x00,0x01,0x10,0x00,0x00,0x00, +0x00,0x00,0x00,0x01,0x10,0x00,0x00,0x00, +0x00,0x11,0x10,0x01,0x10,0x01,0x11,0x00, +0x11,0x00,0x10,0x11,0x11,0x01,0x00,0x11, +0x10,0x00,0x11,0x11,0x11,0x11,0x00,0x01, +0x10,0x00,0x00,0x11,0x11,0x00,0x00,0x01, +0x00,0x01,0x10,0x11,0x41,0x01,0x10,0x00, +0x00,0x10,0x11,0x11,0x11,0x11,0x01,0x00, +0x01,0x00,0x00,0x51,0x15,0x00,0x00,0x10, +0x01,0x00,0x01,0x10,0x01,0x10,0x00,0x10, +0x01,0x00,0x10,0x00,0x00,0x01,0x00,0x10, +0x00,0x10,0x01,0x00,0x00,0x10,0x01,0x00, +0x00,0x10,0x00,0x00,0x00,0x00,0x01,0x00, +0x00,0x01,0x10,0x00,0x00,0x01,0x10,0x00, +}; + +const byte sprite4[2+16*16/2] = { +8,16, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0xbb,0xbb,0x00,0x00,0x00, +0x00,0x00,0x00,0xbb,0xbb,0x00,0x00,0x00, +0x00,0x00,0xbb,0xbb,0xbb,0xbb,0x00,0x00, +0x00,0x0b,0x4b,0xbb,0xbb,0xb4,0xb0,0x00, +0x00,0x0b,0x44,0xbb,0xbb,0x49,0xb0,0x00, +0x00,0xbb,0x99,0x4b,0xb4,0x99,0xbb,0x00, +0x00,0x4b,0xb9,0x9b,0xb4,0x4b,0xb4,0x00, +0x00,0x04,0xbb,0x4b,0xb9,0xbb,0x40,0x00, +0x00,0x00,0x4b,0xbb,0xbb,0xb4,0x00,0x00, +0x11,0x00,0x0b,0xbb,0xbb,0xb0,0x00,0x11, +0x10,0x10,0x0b,0x41,0x14,0xb0,0x01,0x01, +0x10,0x00,0x05,0x11,0x11,0x50,0x00,0x01, +0x01,0x00,0x01,0x51,0x15,0x10,0x00,0x10, +0x00,0x10,0x01,0x10,0x01,0x10,0x01,0x00, +0x01,0x01,0x10,0x10,0x01,0x01,0x10,0x10, +}; + +const byte sprite5[2+16*16/2] = { +8,16, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x22,0x00,0x00,0x22,0x00,0x00, +0x00,0x20,0x20,0x00,0x00,0x02,0x02,0x00, +0x02,0x22,0x12,0x00,0x00,0x21,0x22,0x20, +0x00,0x02,0x11,0x00,0x00,0x11,0x20,0x00, +0x00,0x00,0x02,0x11,0x11,0x20,0x00,0x00, +0x00,0x00,0x02,0x22,0x22,0x20,0x00,0x00, +0x00,0x00,0x21,0x22,0x22,0x12,0x00,0x00, +0x00,0x00,0x22,0x12,0x21,0x22,0x00,0x00, +0x02,0x00,0x22,0x12,0x21,0x22,0x00,0x20, +0x20,0x02,0x21,0x11,0x11,0x12,0x20,0x02, +0x02,0x22,0x15,0x12,0x21,0x51,0x22,0x20, +0x02,0x11,0x15,0x52,0x25,0x51,0x11,0x20, +0x20,0x00,0x22,0x12,0x21,0x22,0x00,0x02, +0x20,0x02,0x20,0x20,0x02,0x02,0x20,0x02, +0x02,0x02,0x00,0x20,0x02,0x00,0x20,0x20, +}; + +const byte sprite6[2+12*16/2] = { +8,12, +0x00,0x00,0x00,0x00,0x04,0x04,0x04,0x00, +0x00,0x00,0x00,0x00,0x44,0x44,0x44,0x40, +0x00,0x00,0x04,0x04,0x49,0x49,0x99,0x44, +0x00,0x00,0x44,0x44,0x99,0x94,0x44,0x40, +0x00,0x04,0x49,0x99,0x94,0x49,0x99,0x40, +0x00,0x44,0x99,0x94,0x49,0x99,0x44,0x00, +0x04,0x49,0x44,0x99,0x99,0x44,0x00,0x00, +0x04,0x44,0x00,0x49,0x44,0x40,0x00,0x00, +0x04,0x40,0x00,0x04,0x00,0x00,0x00,0x00, +0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +const byte sprite7[2+16*16/2] = { +8,16, +0x00,0x60,0x00,0x00,0x00,0x00,0x06,0x00, +0x60,0x60,0x00,0x00,0x00,0x00,0x06,0x06, +0x60,0x60,0x50,0x00,0x00,0x05,0x06,0x06, +0x60,0x60,0x06,0x00,0x00,0x60,0x06,0x06, +0x66,0x60,0x06,0x06,0x60,0x60,0x06,0x66, +0x66,0x60,0x66,0x66,0x66,0x66,0x06,0x66, +0x66,0x60,0x6b,0x6b,0xb6,0xb6,0x06,0x66, +0x06,0x66,0x66,0xb6,0x6b,0x66,0x66,0x60, +0x00,0x67,0x66,0x66,0x66,0x66,0x66,0x00, +0x00,0x11,0x11,0x66,0x66,0x11,0x11,0x00, +0x01,0x11,0x21,0x11,0x11,0x12,0x11,0x10, +0x00,0x10,0x01,0x01,0x10,0x10,0x01,0x00, +0x00,0x10,0x11,0x00,0x00,0x11,0x01,0x00, +0x00,0x00,0x10,0x00,0x00,0x01,0x00,0x00, +}; + +const byte sprite8[2+16*16/2] = { +8,16, +0x00,0x00,0x11,0x11,0x11,0x11,0x00,0x00, +0x00,0x01,0x61,0x11,0x21,0x12,0x10,0x00, +0x00,0x11,0x12,0x11,0x61,0x11,0x11,0x00, +0x00,0x11,0x71,0x21,0x61,0x17,0x11,0x00, +0x00,0x11,0x77,0x11,0x12,0x77,0x11,0x00, +0x00,0x11,0x17,0x17,0x71,0x72,0x11,0x00, +0x00,0x16,0x21,0x78,0x87,0x11,0x11,0x00, +0x00,0x01,0x81,0x88,0x88,0x18,0x10,0x00, +0x00,0x08,0x18,0x58,0x85,0x81,0x80,0x00, +0x00,0x88,0x01,0x88,0x88,0x10,0x88,0x00, +0x00,0x08,0x07,0x78,0x87,0x70,0x80,0x00, +0x08,0x80,0x07,0x80,0x08,0x70,0x08,0x80, +0x88,0x00,0x88,0x00,0x00,0x88,0x00,0x88, +0x80,0x00,0x08,0x80,0x08,0x80,0x00,0x08, +0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x00, +}; + +const byte sprite9[2+13*16/2] = { +8,13, +0x00,0x00,0xaa,0x00,0x00,0xaa,0x00,0x00, +0x00,0x00,0xa0,0x0a,0xa0,0x0a,0x00,0x00, +0x0a,0xaa,0xa4,0xaa,0xaa,0x3a,0xaa,0xa0, +0x00,0x0a,0x3a,0x9a,0xa9,0xa3,0xa0,0x00, +0x00,0x0a,0xaa,0x99,0xa4,0xaa,0xa0,0x00, +0x00,0x0a,0xaa,0x99,0x94,0xaa,0xa0,0x00, +0x00,0xaa,0x33,0xaa,0xaa,0x43,0xaa,0x00, +0x0a,0x3a,0xaa,0x3a,0xa3,0xaa,0xa3,0xa0, +0x00,0xaa,0xaa,0x30,0x03,0xaa,0xaa,0x00, +0x00,0x0a,0xa0,0x30,0x03,0x0a,0xa0,0x00, +0x00,0xa3,0xa0,0x00,0x00,0x0a,0x4a,0x00, +0x00,0xaa,0xa0,0x00,0x00,0x0a,0xaa,0x00, +0x00,0x0a,0xa0,0x00,0x00,0x0a,0xa0,0x00, +}; + +const byte* const all_sprites[9] = { + sprite1, + sprite2, + sprite3, + sprite4, + sprite5, + sprite6, + sprite7, + sprite8, + sprite9, +}; + +// GAME CODE + +typedef struct Actor; + +typedef void (*ActorUpdateFn)(struct Actor* a); +typedef void (*ActorDrawFn)(struct Actor* a); + +typedef struct Actor { + struct Actor* next; + struct Actor** prevptr; + byte x,y; + byte* shape; + ActorUpdateFn update; + ActorDrawFn draw; + union { + struct { sbyte dx,dy; } laser; + struct { byte exploding; } enemy; + } u; +} Actor; + +#define MAX_ACTORS 128 + +static Actor actors[MAX_ACTORS]; +static Actor* player_list; +static Actor* fast_list; +static Actor* slow_lists[4]; +static Actor* obstacle_list; +static Actor* free_list; +static Actor* effects_list; +static byte frame; + +#define PLAYER 1 +#define LASER 2 + +void add_actor(Actor** list, Actor* a) { + if (*list) (*list)->prevptr = &a->next; + a->next = *list; + a->prevptr = list; + *list = a; +} + +void remove_actor(Actor* a) { + if (a->next) a->next->prevptr = a->prevptr; + *a->prevptr = a->next; +} + +char in_rect(const Actor* e, byte x, byte y, byte w, byte h) { + byte eh = e->shape[0]; + byte ew = e->shape[1]; + return (x >= e->x-w && x <= e->x+ew && y >= e->y-h && y <= e->y+eh); +} + +void draw_actor_normal(Actor* a) { + draw_sprite(a->shape, a->x, a->y); +} + +void draw_actor_exploding(Actor* a) { + erase_sprite_strided(a->shape, a->x, a->y, a->u.enemy.exploding); + if (a->u.enemy.exploding > 10) { + a->draw = NULL; } else { - return sintbl[x]; + draw_sprite_strided(a->shape, a->x, a->y, ++a->u.enemy.exploding); } } -sbyte icos(byte x) { - return isin(x+64); -} - -void mat_rotate(Matrix* m, byte axis, byte angle) { - sbyte sin = isin(angle); - sbyte cos = icos(angle); - mat_identity(m); - switch (axis) { - case 0: - m->m[1][1] = cos; - m->m[2][1] = sin; - m->m[1][2] = -sin; - m->m[2][2] = cos; - break; - case 1: - m->m[2][2] = cos; - m->m[0][2] = sin; - m->m[2][0] = -sin; - m->m[0][0] = cos; - break; - case 2: - m->m[0][0] = cos; - m->m[1][0] = -sin; - m->m[0][1] = sin; - m->m[1][1] = cos; - break; - } -} - -void xform_vertices(Vector16* dest, const Vector8* src, const Matrix* m, byte nv) { - byte i; - for (i=0; iedges; - byte bright = 0; - int x1 = 0; - int y1 = 0; - Vector16 scrnverts[16]; - xform_vertices(scrnverts, wf->verts, m, wf->numverts); - do { - sbyte i = *e++; - if (i == -1) - bright = 0; - else if (i == -2) - break; - else { - int x2 = scrnverts[i].x>>8; - int y2 = scrnverts[i].y>>8; - VCTR(x2-x1, y2-y1, bright); - x1 = x2; - y1 = y2; +void update_actor(Actor* a) { + // if NULL shape, we don't have anything + if (a->shape) { + // erase the sprite + draw_sprite_solid(a->shape, a->x, a->y, 0); + // call update callback + if (a->update) { + a->update(a); } - bright = 2; - } while (1); + // did we delete it? + if (a->shape) { + // draw the sprite + if (a->draw) { + a->draw(a); + } + } else { + // shape became null, remove from list + remove_actor(a); + } + } } -static word lfsr = 1; +// + +word lfsr = 1; word rand() { byte lsb = lfsr & 1; @@ -371,378 +565,211 @@ word rand() { return lfsr; } -// SHAPE CACHE +signed char random_dir() { + byte x = rand(); + if (x < 85) return 0; + else if (x < 85*2) return -1; + else return 1; +} -const Vector8 tetra_v[] = { {0,-86,86},{86,86,86},{-86,86,86},{0,0,-86} }; -const char tetra_e[] = { 0, 1, 2, 0, 3, 1, -1, 3, 2, -2 }; -const Wireframe tetra_wf = { 4, tetra_v, tetra_e }; +void random_walk(Actor* a) { + a->x += random_dir(); + a->y += random_dir(); +} -const Vector8 octa_v[] = { {86,0,0},{0,86,0},{-86,0,0},{0,-86,0},{0,0,86},{0,0,-86} }; -const char octa_e[] = { 0, 1, 2, 3, 0, 4, 1, 5, 0, -1, 2, 4, 3, 5, 2, -2 }; -const Wireframe octa_wf = { 6, octa_v, octa_e }; +static byte g_section; -const Vector8 ship_v[] = { {0,86,0},{-30,-30,0},{-50,0,0},{50,0,0},{30,-30,0} }; -const char ship_e[] = { 0, 1, 2, 3, 4, 0, -2 }; -const Wireframe ship_wf = { 5, ship_v, ship_e }; - -const Vector8 thrust_v[] = { {-20,-30,0},{-30,-50,0},{0,-86,0},{30,-50,0},{20,-30,0} }; -const char thrust_e[] = { 0, 1, 2, 3, 4, -2 }; -const Wireframe thrust_wf = { 5, thrust_v, thrust_e }; - -const Vector8 torpedo_v[] = { {-86,0,0},{86,0,0},{-40,-40,0},{40,40,0},{0,-20,0},{0,20,0} }; -const char torpedo_e[] = { 0, 1, -1, 2, 3, -1, 4, 5, -2 }; -const Wireframe torpedo_wf = { 6, torpedo_v, torpedo_e }; - -word ship_shapes[32]; -word thrust_shapes[32]; -word tetra_shapes[32]; -word torpedo_shapes[16]; -word explosion_shape[1]; - -void draw_explosion() { - byte i; - for (i=0; i<30; i++) { - byte angle = rand(); - sbyte xd = isin(angle) >> 4; - sbyte yd = icos(angle) >> 4; - SVEC(xd, yd, 2); - SVEC(-xd, -yd, 2); +void update_actor_list(Actor* a) { + while (a) { + update_actor(a); + a = a->next; } } -void make_cached_shapes() { - Matrix mat; - byte i; - for (i=0; i<32; i++) { - watchdog = 0; - ship_shapes[i] = dvgwrofs; - mat_rotate(&mat, 2, i<<3); - draw_wireframe_ortho(&ship_wf, &mat); - RTSL(); - thrust_shapes[i] = dvgwrofs; - draw_wireframe_ortho(&thrust_wf, &mat); - RTSL(); - tetra_shapes[i] = dvgwrofs; - mat_rotate(&mat, 0, i<<3); - draw_wireframe_ortho(&octa_wf, &mat); - RTSL(); +void update_actors_partial(Actor* a) { + while (a) { + if (g_section ^ (a->y < 0x80)) { + update_actor(a); + } + a = a->next; } - for (i=0; i<16; i++) { - torpedo_shapes[i] = dvgwrofs; - mat_rotate(&mat, 2, i<<4); - draw_wireframe_ortho(&torpedo_wf, &mat); - RTSL(); +} + +void update_screen_section(byte section) { + g_section = section; + update_actors_partial(player_list); + update_actors_partial(fast_list); + update_actors_partial(slow_lists[frame & 3]); +} + +byte did_overflow() { + __asm + ld l,#0 + ret nc + inc l + __endasm; +} + +static byte test_x, test_y; +static Actor* test_collided; + +Actor* test_collision(Actor* a) { + while (a) { + if (test_y >= a->y && test_x >= a->x && + test_y < a->y+16 && test_x < a->x+16) { + return a; + } + a = a->next; } - explosion_shape[0] = dvgwrofs; - STAT_sparkle(15); - draw_explosion(); - RTSL(); + return NULL; } -// MAIN PROGRAM - -struct Actor; - -typedef void ActorUpdateFn(struct Actor*); - -typedef struct Actor { - word* shapes; - ActorUpdateFn* update_fn; - byte angshift; - byte scale; - byte color; - byte intens; - byte collision_flags; - byte angle; - word xx; - word yy; - int velx; - int vely; - struct Actor* next; - byte removed:1; -} Actor; - -#define WORLD_SCALE 0x2c0 - -void draw_actor(const Actor* a) { - CNTR(); // center beam (0,0) - SCAL(WORLD_SCALE); // world scale - VCTR(a->xx>>3, a->yy>>3, 0); // go to object center - SCAL(a->scale); // object scale - STAT(a->color, a->intens); // set color/intensity - JSRL(a->shapes[a->angle >> a->angshift]); // draw +void laser_move(Actor* a) { + // did we hit something? + test_x = a->x; + test_y = a->y; + test_collided = test_collision(fast_list); + if (!test_collided) test_collided = test_collision(slow_lists[0]); + if (!test_collided) test_collided = test_collision(slow_lists[1]); + if (!test_collided) test_collided = test_collision(slow_lists[2]); + if (!test_collided) test_collided = test_collision(slow_lists[3]); + if (test_collided) { + // get rid of laser (we can do this in our 'update' fn) + a->shape = NULL; + // set exploding flag for enemy (we're not in its update) + test_collided->draw = draw_actor_exploding; + test_collided->update = NULL; + test_collided->u.enemy.exploding = 1; + remove_actor(test_collided); + add_actor(&effects_list, test_collided); + return; + } + // move laser + // check for wall collisions + a->x += a->u.laser.dx; + if (a->x > 255-8) a->shape = NULL; + a->y += a->u.laser.dy; + if (a->y > 255-8) a->shape = NULL; } -void move_actor(Actor* a) { - a->xx += a->velx; - a->yy += a->vely; +void shoot_laser(sbyte dx, sbyte dy, const byte* shape) { + Actor* a = &actors[LASER]; + a->shape = (void*) shape; + a->x = actors[PLAYER].x + 6; + a->y = actors[PLAYER].y + 8; + a->u.laser.dx = dx; + a->u.laser.dy = dy; + add_actor(&player_list, a); } -static Actor* first_actor = NULL; +void player_laser() { + // is the laser being used? + if (actors[LASER].shape == NULL) { // no, check controls + if (UP2) shoot_laser(0,-8,laser_vert); + else if (DOWN2) shoot_laser(0,8,laser_vert); + else if (LEFT2) shoot_laser(-8,0,laser_horiz); + else if (RIGHT2) shoot_laser(8,0,laser_horiz); + } +} -Actor* new_actor(const Actor* base) { - Actor* a = (Actor*) malloc(sizeof(Actor)); - memcpy(a, base, sizeof(Actor)); - a->next = first_actor; - first_actor = a; +void player_move(Actor* a) { + byte x = a->x; + byte y = a->y; + if (UP1) y-=1; + if (DOWN1) y+=1; + if (LEFT1) x-=1; + if (RIGHT1) x+=1; + a->x = x; + a->y = y; + player_laser(); +} + +static Actor* current_effect; + +void effects_new_frame() { + current_effect = effects_list; +} + +void effects_next() { + if (current_effect) { + if (current_effect->draw) { + current_effect->draw(current_effect); + } else { + remove_actor(current_effect); + } + current_effect = current_effect->next; + } +} + +Actor* new_actor() { + Actor* a = free_list; + remove_actor(a); return a; } -void draw_and_update_actors() { - Actor* a = first_actor; - while (a != NULL) { - draw_actor(a); - move_actor(a); - if (a->update_fn) a->update_fn(a); - a = a->next; - } +void redraw_playfield(Actor* a) { + a; + draw_box(0,0,303,255,0x11); } -void remove_expired_actors() { - Actor* a; - // get address of first pointer - Actor** prev = &first_actor; - while ((a = *prev) != NULL) { - // was actor removed? - if (a->removed) { - // set previous pointer to skip this actor - *prev = a->next; - // free memory - free(a); - } else { - // get address of next pointer - prev = &a->next; - } - } -} - -void draw_actor_rect(Actor* a) { - CNTR(); // center beam (0,0) - SCAL(WORLD_SCALE); // world scale - VCTR(a->xx>>3, a->yy>>3, 0); // go to object center - SCAL(a->scale); // object scale - STAT(RED, 7); // set color/intensity - VCTR(-86,-86,0); - VCTR(86*2,0,2); - VCTR(0,86*2,2); - VCTR(-86*2,0,2); - VCTR(0,-86*2,2); -} - -inline byte abs(sbyte x) { - return (x>=0) ? x : -x; -} - -inline word get_distance_squared(byte dx, byte dy) { - mathbox_sum = 0; - mul16(dx,dx); - mul16(dy,dy); - return mathbox_sum; -} - -typedef void ActorCollisionFn(struct Actor*, struct Actor*); - -byte test_actor_distance(ActorCollisionFn* fn, Actor* act1, byte mindist, byte flags) { - Actor* a = first_actor; - byte xx1 = act1->xx >> 8; - byte yy1 = act1->yy >> 8; - byte count = 0; - // mindist2 = mindist * mindist - word mindist2; - mathbox_sum = 0; - mul16(mindist,mindist); - mindist2 = mathbox_sum; - // go through list of actors - while (a) { - // only compare against actors with certain flags - // (that haven't been removed) - if ((a->collision_flags & flags) && !a->removed) { - byte dx = abs(xx1 - (a->xx >> 8)); - byte dy = abs(yy1 - (a->yy >> 8)); - if (dx+dy < mindist) { - word dist2 = get_distance_squared(dx, dy); - if (dist2 < mindist2) { - if (fn) fn(act1, a); - count++; - } - } - } - a = a->next; - } - return count; -} - -void explode_at(Actor* base); - -void explode_actor(Actor* a, Actor* b) { - a->removed = 1; - explode_at(b); - b->removed = 1; -} - -void obstacle_update_fn(struct Actor* a) { - a->angle += 1; -} - -void torpedo_update_fn(struct Actor* a) { - // expire? - if ((a->angle += 60) == 0) { - a->removed = 1; - } else { - // check for torpedo hits - test_actor_distance(explode_actor, a, 12, 0x2); - } -} - -void explosion_update_fn(struct Actor* a) { - a->scale -= 2; - if (a->scale < 8) { - a->removed = 1; - } -} - -const Actor ship_actor = { - ship_shapes, NULL, 3, 0xb0, WHITE, 7, 0x1, -}; -const Actor tetra_actor = { - tetra_shapes, obstacle_update_fn, 3, 0x80, CYAN, 7, 0x2, -}; -const Actor torpedo_actor = { - torpedo_shapes, torpedo_update_fn, 4, 0xe0, YELLOW, 15, 0x4, -}; -const Actor explosion_actor = { - explosion_shape, explosion_update_fn, 8, 0xa0, WHITE, 15, 0, -}; - -void create_obstacles(byte count) { - while (count--) { - Actor* a = new_actor(&tetra_actor); - a->xx = rand() | 0x8000; - a->yy = rand(); - a->velx = (int)rand()<<8>>8; - a->vely = (int)rand()<<8>>8; - } -} - -static int frame = 0; - -static Actor* curship = NULL; - -void draw_thrust() { - word rnd = rand(); - // save old values in actor - byte oldcolor = curship->color; - byte oldintens = curship->intens; - // temporarily give new thrust values - curship->shapes = thrust_shapes; - curship->scale ^= rnd; // random thrust scale - curship->intens = 15; - curship->color = (rnd&1) ? RED : YELLOW; - // draw thrust using player's ship actor - draw_actor(curship); - // restore previous values - curship->shapes = ship_shapes; - curship->scale ^= rnd; - curship->color = oldcolor; - curship->intens = oldintens; -} - -void thrust_ship() { - sbyte sin = isin(curship->angle); - sbyte cos = icos(curship->angle); - curship->velx += sin>>3; - curship->vely += cos>>3; -} - -int apply_friction(int vel) { - int delta = vel >> 8; - if (delta == 0 && vel > 0) delta++; - return vel - delta; -} - -void shoot_torpedo() { - sbyte sin = isin(curship->angle); - sbyte cos = icos(curship->angle); - Actor* torp = new_actor(&torpedo_actor); - torp->velx = sin << 2; - torp->vely = cos << 2; - torp->xx = curship->xx + torp->velx*4; - torp->yy = curship->yy + torp->vely*4; -} - -static byte can_fire; -static byte newship_timer; - -void new_player_ship() { - if (curship == NULL) { - curship = new_actor(&ship_actor); - } -} - -void explode_at(Actor* base) { - Actor* a = new_actor(&explosion_actor); - a->xx = base->xx; - a->yy = base->yy; -} - -void control_player() { - if (newship_timer && --newship_timer == 0) { - new_player_ship(); - } - if (!curship) return; - - if (LEFT1) curship->angle -= 2; - if (RIGHT1) curship->angle += 2; - if ((frame&1)==1) { - curship->velx = apply_friction(curship->velx); - curship->vely = apply_friction(curship->vely); - } - if (UP1) { - // draw flame - draw_thrust(); - // thrust every 4 frames, to avoid precision issues - if (!(frame&3)) thrust_ship(); - } - if (FIRE1) { - // must release fire button before firing again - if (can_fire) { - shoot_torpedo(); - can_fire = 0; - } - } else { - can_fire = 1; - } - // ship ran into something? - if (test_actor_distance(NULL, curship, 16, 0x2)) { - explode_at(curship); - curship->removed = 1; - curship = NULL; - newship_timer = 255; - } -} - -byte just_one_actor_left() { - return first_actor && first_actor->next == NULL; +Actor* new_effect(ActorDrawFn draw) { + Actor* a = new_actor(); + a->draw = draw; + add_actor(&effects_list, a); + return a; } void main() { - memset(dvgram, 0x20, sizeof(dvgram)); // HALTs - dvgwrofs = 0x800; - make_cached_font(); - make_cached_shapes(); - create_obstacles(5); - new_player_ship(); - while (!just_one_actor_left()) { - dvgreset(); - control_player(); - draw_and_update_actors(); - CNTR(); - HALT(); - dvgstart(); - remove_expired_actors(); - frame++; - watchdog=0; - while (vidframe == (frame & 0x3)) {} + byte i; + byte num_actors = 32; + blit_solid(0, 0, 255, 255, 0); + memset(actors, 0, sizeof(actors)); + memcpy(palette, palette_data, 16); + fill_char_table(); + player_list = fast_list = obstacle_list = free_list = NULL; + memset(slow_lists, 0, sizeof(slow_lists)); + // add all actors to free list + for (i=MAX_ACTORS-1; i>0; i--) { + add_actor(&free_list, &actors[i]); + } + // make actors + for (i=1; ix = rand() + rand(); + a->y = rand() + rand(); + } while ((byte)(a->x - 96) < 64 && (byte)(a->y - 96) < 64); + a->shape = (void*) all_sprites[i%9]; + a->update = random_walk; + a->draw = draw_actor_normal; + if (i == PLAYER) { + a->x = 128; + a->y = 120; + add_actor(&player_list, a); + } + else if (i == LASER) ; //add_actor(&player_list, a); + else if (i < 5) add_actor(&fast_list, a); + else add_actor(&slow_lists[i&3], a); + WATCHDOG; + } + actors[PLAYER].shape = (void*) playersprite1; + actors[PLAYER].update = player_move; + actors[LASER].update = laser_move; + actors[LASER].shape = NULL; + new_effect(redraw_playfield); + WATCHDOG; + while (1) { + effects_new_frame(); + while (video_counter >= 0x90) effects_next(); + update_screen_section(1); + while (video_counter < 0x90) effects_next(); + update_screen_section(0); + switch (frame & 7) { + case 0: + draw_box(0,0,303,255,0x11); + break; + } + frame++; + WATCHDOG; } - main(); } diff --git a/presets/williams-z80/game1.c b/presets/williams-z80/game1.c index 9f90f9de..58f0692d 100644 --- a/presets/williams-z80/game1.c +++ b/presets/williams-z80/game1.c @@ -62,6 +62,7 @@ struct { // void main(); +void _sdcc_heap_init(void); // for malloc() // start routine @ 0x0 // set stack pointer, enable interrupts @@ -76,7 +77,9 @@ __asm LD HL, #s__INITIALIZER LDIR __endasm; - main(); + + _sdcc_heap_init(); + main(); } #define LOCHAR 0x21 @@ -102,6 +105,7 @@ void fill_char_table_entry(char ch) { b <<= 2; } } + WATCHDOG; } void fill_char_table() { @@ -178,6 +182,43 @@ inline void draw_sprite_solid(const byte* data, byte x, byte y, byte color) { blitter.flags = (x&1) ? DSTSCREEN|FGONLY|RSHIFT|SOLID : DSTSCREEN|FGONLY|SOLID; } +void draw_sprite_strided(const byte* data, byte x, byte y, byte stride) { + byte i; + byte width = data[0]; + byte height = data[1]; + byte yy = y-(height*(stride-1)/2); + word dest = (x>>1)+yy*256+XBIAS; + blitter.width = width^4; + blitter.height = 1^4; + for (i=0; i>1)+yy*256+XBIAS; + blitter.width = width^4; + blitter.height = 1^4; + blitter.solid = 0; + for (i=0; i HICHAR) return; blit_copy_solid(x, y, 4, 8, font_table[ch - LOCHAR], color); @@ -429,22 +470,15 @@ const byte* const all_sprites[9] = { // GAME CODE typedef struct Actor; -typedef struct Task; typedef void (*ActorUpdateFn)(struct Actor* a); -typedef void (*ActorDrawFn)(const struct Actor* a); -typedef void (*ActorEnumerateFn)(const struct Actor* a); -typedef bool (*TaskFn)(struct Task* task); +typedef void (*ActorDrawFn)(struct Actor* a); typedef struct Actor { - byte grid_index; - byte next_actor; - byte last_update_frame; + struct Actor* next; + struct Actor** prevptr; byte x,y; byte* shape; - byte flags; - byte updatefreq; - byte updatetimer; ActorUpdateFn update; ActorDrawFn draw; union { @@ -453,66 +487,30 @@ typedef struct Actor { } u; } Actor; -typedef struct Task { - Actor* actor; - struct Task* next; - TaskFn func; -} Task; - -#define GBITS 3 -#define GDIM (1<> (8-GBITS)) | ((y >> (8-GBITS)) << GBITS); +void add_actor(Actor** list, Actor* a) { + if (*list) (*list)->prevptr = &a->next; + a->next = *list; + a->prevptr = list; + *list = a; } -void insert_into_grid(byte gi, byte actor_index) { - struct Actor* a = &actors[actor_index]; - a->grid_index = gi; - a->next_actor = grid[gi]; - grid[gi] = actor_index; -} - -void delete_from_grid(byte gi, byte actor_index) { - byte i = grid[gi]; - byte next = actors[actor_index].next_actor; - // is actor to delete at head of list? - if (i == actor_index) { - grid[gi] = next; - } else { - // iterate through the list - do { - byte j = actors[i].next_actor; - if (j == actor_index) { - actors[i].next_actor = next; - break; - } - i = j; - } while (1); // watchdog reset if actor not found to delete - } - actors[actor_index].next_actor = 0; - actors[actor_index].grid_index = 0; -} - -void add_actor(byte actor_index) { - struct Actor* a = &actors[actor_index]; - insert_into_grid(xy2grid(a->x, a->y), actor_index); +void remove_actor(Actor* a) { + if (a->next) a->next->prevptr = a->prevptr; + *a->prevptr = a->next; } char in_rect(const Actor* e, byte x, byte y, byte w, byte h) { @@ -521,78 +519,39 @@ char in_rect(const Actor* e, byte x, byte y, byte w, byte h) { return (x >= e->x-w && x <= e->x+ew && y >= e->y-h && y <= e->y+eh); } -void enumerate_actors_at_grid(ActorEnumerateFn enumfn, byte gi) { - byte ai = grid[gi & GMASK]; - while (ai) { - const Actor* a = &actors[ai]; - ai = a->next_actor; - enumfn(a); - } -} - -void enumerate_actors_in_rect(ActorEnumerateFn enumfn, - byte x1, byte y1, byte x2, byte y2) { - byte gi = xy2grid(x1, y1); - byte gi1 = xy2grid(x2, y2) + 1; - byte gwidth = 1 + ((gi1 - gi) & (GDIM-1)); - byte x; - do { - for (x=0; xnext_actor; - } - gi++; - } - if (gi == gi1) break; - gi += GDIM - gwidth; - } while (1); -} - void draw_actor_normal(Actor* a) { draw_sprite(a->shape, a->x, a->y); } -void draw_actor_debug(Actor* a) { - draw_sprite_solid(a->shape, a->x, a->y, a->grid_index); +void draw_actor_exploding(Actor* a) { + erase_sprite_strided(a->shape, a->x, a->y, a->u.enemy.exploding); + if (a->u.enemy.exploding > 10) { + a->draw = NULL; + } else { + draw_sprite_strided(a->shape, a->x, a->y, ++a->u.enemy.exploding); + } } -byte time_to_update(Actor* a) { - byte t0 = a->updatetimer; - return (a->updatetimer += a->updatefreq) < t0; -} - -byte update_actor(byte actor_index) { - struct Actor* a = &actors[actor_index]; - byte next_actor = a->next_actor; - byte gi0,gi1; +void update_actor(Actor* a) { // if NULL shape, we don't have anything - if (a->shape && time_to_update(a)) { - gi0 = a->grid_index; + if (a->shape) { // erase the sprite draw_sprite_solid(a->shape, a->x, a->y, 0); // call update callback - if (a->update) a->update(a); - // set last_update_frame - a->last_update_frame = frame; + if (a->update) { + a->update(a); + } // did we delete it? if (a->shape) { // draw the sprite - if (a->draw) a->draw(a); - // grid bucket changed? - gi1 = xy2grid(a->x, a->y); - if (gi0 != gi1) { - delete_from_grid(gi0, actor_index); - insert_into_grid(gi1, actor_index); + if (a->draw) { + a->draw(a); } } else { - // shape NULL, delete from grid - delete_from_grid(gi0, actor_index); + // shape became null, remove from list + remove_actor(a); } } - return next_actor; } // @@ -613,68 +572,133 @@ signed char random_dir() { else return 1; } +void update_actor_list(Actor* a) { + while (a) { + update_actor(a); + a = a->next; + } +} + +static byte g_section; + +void update_actors_partial(Actor* a) { + while (a) { + if (g_section ^ (a->y < 0x80)) { + update_actor(a); + } + a = a->next; + } +} + +void update_screen_section(byte section) { + g_section = section; + update_actors_partial(player_list); + update_actors_partial(fast_list); + update_actors_partial(slow_lists[frame & 3]); +} + +static Actor* test_actor; +static byte test_x, test_y; + +byte minbyte(byte a, byte b) { return ab?a:b; } + +bool test_actor_pixels(Actor* a) { + if (a->shape) { + byte x,y; + byte *p1, *p2; + byte x1 = maxbyte(test_actor->x, a->x); + byte y1 = maxbyte(test_actor->y, a->y); + byte x2 = minbyte(test_actor->x + test_actor->shape[0]*2, + a->x + a->shape[0]*2); + byte y2 = minbyte(test_actor->y + test_actor->shape[1], + a->y + a->shape[1]); + if (x2 <= x1 || y2 <= y1) return false; + //draw_box(x1,y1,x2,y2,0xff); + p1 = &test_actor->shape[2+(y1-test_actor->y)*test_actor->shape[0]]; + p2 = &a->shape[2+(y1-a->y)*a->shape[0]]; + p1 += (x1 - test_actor->x) >> 1; + p2 += (x1 - a->x) >> 1; + for (y=y1; yshape[0]; + p2 += a->shape[0]; + } + } + return false; +} + +inline bool test_collision_actor(Actor* a) { + return ((byte)(test_y - a->y + 16) < 32 && + (byte)(test_x - a->x + 16) < 32 && + test_actor_pixels(a)); +} + +Actor* test_collisions(Actor* a) { + while (a) { + if (test_collision_actor(a)) { + return a; + } + a = a->next; + } + return NULL; +} + +void setup_collision(Actor* a) { + test_actor = a; + test_x = a->x; + test_y = a->y; +} + +void destroy_player() { + Actor* a = &actors[PLAYER]; + byte i; + for (i=0; i<60; i++) { + WATCHDOG; + while (video_counter != 0xfc) ; + draw_sprite_solid(a->shape, a->x, a->y, i); + while (video_counter == 0xfc) ; + } + for (i=1; i<60; i++) { + WATCHDOG; + while (video_counter != 0xfc) ; + draw_sprite_strided(a->shape, a->x+i, a->y, i); + draw_sprite_strided(a->shape, a->x-i, a->y, i); + while (video_counter == 0xfc) ; + } +} + void random_walk(Actor* a) { a->x += random_dir(); a->y += random_dir(); - if (a->u.enemy.exploding) { - a->shape = NULL; + setup_collision(a); + if (actors[PLAYER].shape && test_collision_actor(&actors[PLAYER])) { + destroy_player(); + actors[PLAYER].shape = NULL; } } -void update_grid_cell(byte grid_index) { - byte actor_index = grid[grid_index]; - while (actor_index) { - actor_index = update_actor(actor_index); - } -} - -void update_screen_section(byte gi0, byte gi1, byte vc0, byte vc1) { - while (!(video_counter >= vc0 && video_counter <= vc1)) ; - while (gi0 < gi1) { - update_grid_cell(gi0++); - } -} - -inline void ensure_update(byte actor_index) { - if (actors[actor_index].last_update_frame != frame) { - update_actor(actor_index); - } -} - -byte did_overflow() { - __asm - ld l,#0 - ret nc - inc l - __endasm; -} - -static byte test_flags; -static byte test_x, test_y; -static Actor* test_collided; - -void enumerate_check_point(Actor* a) { - // check flags to see if we should test this - if (!(a->flags & test_flags)) return; - draw_actor_debug(a); - if (in_rect(a, test_x, test_y, 1, 1)) test_collided = a; -} - void laser_move(Actor* a) { // did we hit something? - test_x = a->x; - test_y = a->y; - test_flags = F_KILLABLE; - test_collided = NULL; - enumerate_actors_at_grid(enumerate_check_point, a->grid_index); - enumerate_actors_at_grid(enumerate_check_point, a->grid_index-1); - enumerate_actors_at_grid(enumerate_check_point, a->grid_index-GDIM); - enumerate_actors_at_grid(enumerate_check_point, a->grid_index-GDIM-1); - if (test_collided) { + static Actor* collided; + setup_collision(a); + collided = test_collisions(fast_list); + if (!collided) collided = test_collisions(slow_lists[0]); + if (!collided) collided = test_collisions(slow_lists[1]); + if (!collided) collided = test_collisions(slow_lists[2]); + if (!collided) collided = test_collisions(slow_lists[3]); + if (collided) { // get rid of laser (we can do this in our 'update' fn) a->shape = NULL; - // set exploding flag for enemy (we're not in its update) - test_collided->u.enemy.exploding = 1; + // set exploding counter for enemy, change fn pointers + collided->draw = draw_actor_exploding; + collided->update = NULL; + collided->u.enemy.exploding = 1; + // move enemy to effects list + remove_actor(collided); + add_actor(&effects_list, collided); return; } // move laser @@ -686,21 +710,22 @@ void laser_move(Actor* a) { } void shoot_laser(sbyte dx, sbyte dy, const byte* shape) { - actors[LASER].shape = (void*) shape; - actors[LASER].x = actors[1].x; - actors[LASER].y = actors[1].y; - actors[LASER].u.laser.dx = dx; - actors[LASER].u.laser.dy = dy; - add_actor(LASER); + Actor* a = &actors[LASER]; + a->shape = (void*) shape; + a->x = actors[PLAYER].x + 6; + a->y = actors[PLAYER].y + 8; + a->u.laser.dx = dx; + a->u.laser.dy = dy; + add_actor(&player_list, a); } void player_laser() { - // shoot laser - if (actors[LASER].shape == NULL) { - if (UP2) shoot_laser(0,-4,laser_vert); - else if (DOWN2) shoot_laser(0,4,laser_vert); - else if (LEFT2) shoot_laser(-4,0,laser_horiz); - else if (RIGHT2) shoot_laser(4,0,laser_horiz); + // is the laser being used? + if (actors[LASER].shape == NULL) { // no, check controls + if (UP2) shoot_laser(0,-8,laser_vert); + else if (DOWN2) shoot_laser(0,8,laser_vert); + else if (LEFT2) shoot_laser(-8,0,laser_horiz); + else if (RIGHT2) shoot_laser(8,0,laser_horiz); } } @@ -716,43 +741,107 @@ void player_move(Actor* a) { player_laser(); } -void main() { +static Actor* current_effect; + +void effects_new_frame() { + current_effect = effects_list; +} + +void effects_next() { + if (current_effect) { + if (current_effect->draw) { + current_effect->draw(current_effect); + } else { + remove_actor(current_effect); + } + current_effect = current_effect->next; + } +} + +Actor* new_actor() { + Actor* a = free_list; + remove_actor(a); + a->draw = draw_actor_normal; + return a; +} + +void redraw_playfield(Actor* a) { + a; + draw_box(0,0,303,255,0x11); +} + +Actor* new_effect(ActorDrawFn draw) { + Actor* a = new_actor(); + a->draw = draw; + add_actor(&effects_list, a); + return a; +} + +void init() { byte i; - byte num_actors = 16; blit_solid(0, 0, 255, 255, 0); - memset(grid, 0, sizeof(grid)); memset(actors, 0, sizeof(actors)); memcpy(palette, palette_data, 16); - draw_box(0,0,303,255,0x11); fill_char_table(); - WATCHDOG; - for (i=1; ix = (i & 7) * 24 + 16; - a->y = (i / 4) * 24 + 16; + player_list = fast_list = obstacle_list = free_list = NULL; + memset(slow_lists, 0, sizeof(slow_lists)); + // add all actors to free list + for (i=MAX_ACTORS-1; i>0; i--) { + add_actor(&free_list, &actors[i]); + } +} + +void make_player_actors() { + // make player + Actor* a = new_actor(); + a->x = 128; + a->y = 120; + a->shape = (void*) playersprite1; + a->update = player_move; + add_actor(&player_list, a); + // make laser + a = new_actor(); + a->update = laser_move; + a->shape = NULL; +} + +void make_enemy_actors() { + byte i; + const byte num_actors = 32; + for (i=3; ix = rand() + rand(); + a->y = rand() + rand(); + } while ((byte)(a->x - 96) < 64 && (byte)(a->y - 96) < 64); a->shape = (void*) all_sprites[i%9]; a->update = random_walk; a->draw = draw_actor_normal; - a->updatefreq = i*8; - a->updatetimer = i*8; - if (i > LASER) a->flags = F_KILLABLE; - if (i != LASER) add_actor(i); - } - actors[PLAYER].shape = (void*) playersprite1; - actors[PLAYER].update = player_move; - actors[PLAYER].updatefreq = 0xff; - actors[LASER].update = laser_move; - actors[LASER].shape = NULL; - actors[LASER].updatefreq = 0xff; - WATCHDOG; - while (1) { - update_screen_section(GDIM2*3/4, GDIM2*4/4, 0x00, 0x2f); - update_screen_section(GDIM2*0/4, GDIM2*1/4, 0x30, 0x7f); - update_screen_section(GDIM2*1/4, GDIM2*2/4, 0x80, 0xbf); - update_screen_section(GDIM2*2/4, GDIM2*3/4, 0xc0, 0xff); - ensure_update(PLAYER); - ensure_update(LASER); - frame++; + if (i < 5) add_actor(&fast_list, a); + else add_actor(&slow_lists[i&3], a); WATCHDOG; } } + +void main() { + init(); + make_player_actors(); + make_enemy_actors(); + new_effect(redraw_playfield); + WATCHDOG; + while (actors[PLAYER].shape) { + effects_new_frame(); + while (video_counter >= 0x90) effects_next(); + update_screen_section(1); + while (video_counter < 0x90) effects_next(); + update_screen_section(0); + switch (frame & 7) { + case 0: + draw_box(0,0,303,255,0x11); + break; + } + frame++; + WATCHDOG; + } + main(); +} diff --git a/src/platform/williams.js b/src/platform/williams.js index 9a65c9e9..e9fbf635 100644 --- a/src/platform/williams.js +++ b/src/platform/williams.js @@ -3,7 +3,7 @@ var WILLIAMS_PRESETS = [ {id:'gfxtest.c', name:'Graphics Test'}, {id:'sprites.c', name:'Sprite Test'}, - {id:'game1.c', name:'Game'}, + {id:'game1.c', name:'Raster Paranoia Game'}, {id:'bitmap_rle.c', name:'RLE Bitmap'}, ]; @@ -32,7 +32,7 @@ var WilliamsPlatform = function(mainElement, proto) { var cpuFrequency = xtal/3/4; var cpuCyclesPerFrame = cpuFrequency/60; // TODO var cpuScale = 1; - var INITIAL_WATCHDOG = 64; + var INITIAL_WATCHDOG = 8; var PIXEL_ON = 0xffeeeeee; var PIXEL_OFF = 0xff000000; @@ -304,19 +304,25 @@ var WilliamsPlatform = function(mainElement, proto) { timer = new AnimationTimer(60, function() { if (!self.isRunning()) return; - // interrupts happen every 1/4 of the screen - for (var quarter=0; quarter<4; quarter++) { - video_counter = [0x00, 0x3c, 0xbc, 0xfc][quarter]; - if (membus.read != memread_defender || pia6821[7] == 0x3c) { // TODO? - if (cpu.interrupt) - cpu.interrupt(); - if (cpu.requestInterrupt) - cpu.requestInterrupt(); + var cpuCyclesPerSection = Math.round(cpuCyclesPerFrame / 65); + for (var sl=0; sl<256; sl+=4) { + video_counter = sl; + // interrupts happen every 1/4 of the screen + if (sl == 0 || sl == 0x3c || sl == 0xbc || sl == 0xfc) { + if (membus.read != memread_defender || pia6821[7] == 0x3c) { // TODO? + if (cpu.interrupt) + cpu.interrupt(); + if (cpu.requestInterrupt) + cpu.requestInterrupt(); + } } if (!self.wasBreakpointHit()) - self.runCPU(cpu, cpuCyclesPerFrame/4); - video.updateFrame(0, 0, quarter*64, 0, 64, 304); + self.runCPU(cpu, cpuCyclesPerSection); + if (sl < 256) video.updateFrame(0, 0, 256-4-sl, 0, 4, 304); } + // last 6 lines + if (!self.wasBreakpointHit()) + self.runCPU(cpu, cpuCyclesPerSection*2); if (screenNeedsRefresh) { for (var i=0; i<0x9800; i++) drawDisplayByte(i, ram.mem[i]); diff --git a/src/ui.js b/src/ui.js index aa5e5621..0b167e2e 100644 --- a/src/ui.js +++ b/src/ui.js @@ -451,8 +451,9 @@ worker.onmessage = function(e) { if (pendingWorkerMessages > 1) { pendingWorkerMessages = 0; setCode(editor.getValue()); + } else { + pendingWorkerMessages = 0; } - pendingWorkerMessages = 0; setCompileOutput(e.data); }