diff --git a/presets/williams-z80/game1.c b/presets/williams-z80/game1.c
index 6e722255..9f90f9de 100644
--- a/presets/williams-z80/game1.c
+++ b/presets/williams-z80/game1.c
@@ -4,6 +4,7 @@
typedef unsigned char byte;
typedef signed char sbyte;
typedef unsigned short word;
+typedef enum { false, true } bool;
byte __at (0x0) vidmem[152][256]; // 304x256x4bpp video memory
byte __at (0xc000) palette[16];
@@ -15,6 +16,8 @@ volatile byte __at (0xcb00) video_counter;
byte __at (0xcbff) watchdog0x39;
byte __at (0xcc00) nvram[0x400];
+__sfr __at (0) debug;
+
// blitter flags
#define SRCSCREEN 0x1
#define DSTSCREEN 0x2
@@ -108,6 +111,94 @@ void fill_char_table() {
}
}
+inline word swapw(word j) {
+ return ((j << 8) | (j >> 8));
+}
+
+// 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 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;
+}
+
+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 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 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;
+}
+
+// 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 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 draw_char(char ch, byte x, byte y, byte color) {
+ if (ch < LOCHAR || ch > 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) {
+ draw_char(*str++, x, y, color);
+ x += 4;
+ }
+}
+
+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);
+}
+
+// GRAPHIC DATA
+
const byte palette_data[16] = {
0x00, 0x03, 0x19, 0x50, 0x52, 0x07, 0x1f, 0x37, 0xe0, 0xa4, 0xfd, 0xff, 0x38, 0x70, 0x7f, 0xf8, };
@@ -335,112 +426,60 @@ const byte* const all_sprites[9] = {
sprite9,
};
-inline word swapw(word j) {
- return ((j << 8) | (j >> 8));
-}
+// GAME CODE
-// 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;
-}
+typedef struct Actor;
+typedef struct Task;
-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 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 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+y*256; // swapped
- blitter.flags = DSTSCREEN|FGONLY;
-}
-
-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+y*256; // swapped
- blitter.solid = color;
- blitter.flags = DSTSCREEN|FGONLY|SOLID;
-}
-
-inline void erase_sprite_rect(const byte* data, byte x, byte y) {
- blitter.width = data[0]^4;
- blitter.height = data[1]^4;
- blitter.dstart = x+y*256; // swapped
- blitter.solid = 0;
- blitter.flags = DSTSCREEN|SOLID;
-}
-
-inline void draw_sprite_strided(const byte* data, byte x, byte y, byte stride) {
- const byte* src = data+2;
- byte height = data[1]^4;
- byte width = data[0]^4;
- while (height--) {
- blit_copy(x, y, width, 1, src);
- y += stride;
- src += width;
- }
-}
-
-inline void draw_char(char ch, byte x, byte y, byte color) {
- if (ch < LOCHAR || ch > 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) {
- draw_char(*str++, x, y, color);
- x += 4;
- }
-}
-
-typedef struct Actor* a;
-
-typedef void ActorUpdateFn(struct Actor* a);
-typedef void ActorDrawFn(struct Actor* a);
-typedef void ActorEnumerateFn(struct Actor* a);
+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 struct Actor {
byte grid_index;
byte next_actor;
+ byte last_update_frame;
byte x,y;
byte* shape;
- ActorUpdateFn* update;
- ActorDrawFn* draw;
+ byte flags;
+ byte updatefreq;
+ byte updatetimer;
+ ActorUpdateFn update;
+ ActorDrawFn draw;
union {
struct { sbyte dx,dy; } laser;
+ struct { byte exploding; } enemy;
} u;
} Actor;
+typedef struct Task {
+ Actor* actor;
+ struct Task* next;
+ TaskFn func;
+} Task;
+
#define GBITS 3
#define GDIM (1<> (8-GBITS)) | (y & ((GDIM-1) << GBITS));
+#define PLAYER 1
+#define LASER 2
+#define F_PLAYER 0x1
+#define F_KILLABLE 0x2
+
+byte xy2grid(byte x, byte y) {
+ return (x >> (8-GBITS)) | ((y >> (8-GBITS)) << GBITS);
}
void insert_into_grid(byte gi, byte actor_index) {
@@ -476,16 +515,22 @@ void add_actor(byte actor_index) {
insert_into_grid(xy2grid(a->x, a->y), actor_index);
}
-void enumerate_actors_at_grid(ActorEnumerateFn* enumfn, byte gi) {
- byte ai = grid[gi];
+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 enumerate_actors_at_grid(ActorEnumerateFn enumfn, byte gi) {
+ byte ai = grid[gi & GMASK];
while (ai) {
- Actor* a = &actors[ai];
- enumfn(a);
+ const Actor* a = &actors[ai];
ai = a->next_actor;
+ enumfn(a);
}
}
-void enumerate_actors_in_rect(ActorEnumerateFn* enumfn,
+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;
@@ -506,12 +551,17 @@ void enumerate_actors_in_rect(ActorEnumerateFn* enumfn,
} while (1);
}
-void draw_actor_normal(struct Actor* a) {
+void draw_actor_normal(Actor* a) {
draw_sprite(a->shape, a->x, a->y);
}
-void draw_actor_debug(struct Actor* a) {
- draw_sprite_solid(a->shape, a->x, a->y, a->next_actor?0xff:0x33);
+void draw_actor_debug(Actor* a) {
+ draw_sprite_solid(a->shape, a->x, a->y, a->grid_index);
+}
+
+byte time_to_update(Actor* a) {
+ byte t0 = a->updatetimer;
+ return (a->updatetimer += a->updatefreq) < t0;
}
byte update_actor(byte actor_index) {
@@ -519,20 +569,26 @@ byte update_actor(byte actor_index) {
byte next_actor = a->next_actor;
byte gi0,gi1;
// if NULL shape, we don't have anything
- if (a->shape) {
+ if (a->shape && time_to_update(a)) {
gi0 = a->grid_index;
+ // 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;
// did we delete it?
if (a->shape) {
+ // draw the sprite
if (a->draw) a->draw(a);
- gi1 = xy2grid(a->x, a->y);
// grid bucket changed?
+ gi1 = xy2grid(a->x, a->y);
if (gi0 != gi1) {
delete_from_grid(gi0, actor_index);
insert_into_grid(gi1, actor_index);
}
} else {
+ // shape NULL, delete from grid
delete_from_grid(gi0, actor_index);
}
}
@@ -560,6 +616,9 @@ signed char random_dir() {
void random_walk(Actor* a) {
a->x += random_dir();
a->y += random_dir();
+ if (a->u.enemy.exploding) {
+ a->shape = NULL;
+ }
}
void update_grid_cell(byte grid_index) {
@@ -569,12 +628,16 @@ void update_grid_cell(byte grid_index) {
}
}
-void update_grid_rows(byte row_start, byte row_end) {
- byte i0 = row_start * GDIM;
- byte i1 = row_end * GDIM;
- byte i;
- for (i=i0; i!=i1; i++) {
- update_grid_cell(i);
+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);
}
}
@@ -586,72 +649,110 @@ byte did_overflow() {
__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) {
+ // 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;
+ return;
+ }
+ // move laser
+ // check for wall collisions
a->x += a->u.laser.dx;
- if (a->x > 152) a->shape = NULL;
+ if (a->x > 255-8) a->shape = NULL;
a->y += a->u.laser.dy;
if (a->y > 255-8) a->shape = NULL;
}
void shoot_laser(sbyte dx, sbyte dy, const byte* shape) {
- actors[2].shape = (void*) shape;
- actors[2].x = actors[1].x;
- actors[2].y = actors[1].y;
- actors[2].u.laser.dx = dx;
- actors[2].u.laser.dy = dy;
- add_actor(2);
+ 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);
}
-void player_move(Actor* a) {
- byte x = a->x;
- byte y = a->y;
- if (UP1) y-=2;
- if (DOWN1) y+=2;
- if (LEFT1) x-=1;
- if (RIGHT1) x+=1;
- a->x = x;
- a->y = y;
+void player_laser() {
// shoot laser
- if (actors[2].shape == NULL) {
- if (UP2) shoot_laser(0,-8,laser_vert);
- else if (DOWN2) shoot_laser(0,8,laser_vert);
+ 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);
}
}
+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();
+}
+
void main() {
byte i;
- byte num_actors = 32;
+ 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) * 16 + 16;
- a->y = (i / 4) * 16 + 32;
+ a->x = (i & 7) * 24 + 16;
+ a->y = (i / 4) * 24 + 16;
a->shape = (void*) all_sprites[i%9];
- //a->update = random_walk;
+ a->update = random_walk;
a->draw = draw_actor_normal;
- add_actor(i);
+ a->updatefreq = i*8;
+ a->updatetimer = i*8;
+ if (i > LASER) a->flags = F_KILLABLE;
+ if (i != LASER) add_actor(i);
}
- actors[1].shape = (void*) playersprite1;
- actors[1].update = player_move;
- actors[2].update = laser_move;
- actors[2].shape = NULL;
+ 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) {
- while (video_counter == 0) ;
- update_grid_rows(GDIM*3/4, GDIM*4/4);
- while (video_counter == 0x3c) ;
- update_grid_rows(GDIM*0/4, GDIM*1/4);
- while (video_counter == 0xbc) ;
- update_grid_rows(GDIM*1/4, GDIM*2/4);
- while (video_counter == 0xfc) ;
- update_grid_rows(GDIM*2/4, GDIM*3/4);
+ 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++;
WATCHDOG;
}
}
diff --git a/spinner.gif b/spinner.gif
new file mode 100644
index 00000000..a3ade8a3
Binary files /dev/null and b/spinner.gif differ
diff --git a/src/emu.js b/src/emu.js
index 909828d0..9d95f134 100644
--- a/src/emu.js
+++ b/src/emu.js
@@ -929,3 +929,13 @@ function AddressDecoder(table, options) {
}
return makeFunction(0x0, 0xffff).bind(self);
}
+
+var BusProbe = function(bus) {
+ this.read = function(a) {
+ var val = bus.read(a);
+ return val;
+ }
+ this.write = function(a,v) {
+ return bus.write(a,v);
+ }
+}
diff --git a/src/platform/sound_williams.js b/src/platform/sound_williams.js
index 98e13a2b..e8fe2f70 100644
--- a/src/platform/sound_williams.js
+++ b/src/platform/sound_williams.js
@@ -1,7 +1,7 @@
"use strict";
var WILLIAMS_SOUND_PRESETS = [
- {id:'minimal.c', name:'Minimal Test'},
+ {id:'wavetable.c', name:'Wavetable Synth'},
];
/****************************************************************************
diff --git a/src/platform/williams.js b/src/platform/williams.js
index 0a17117e..9a65c9e9 100644
--- a/src/platform/williams.js
+++ b/src/platform/williams.js
@@ -23,7 +23,7 @@ var WilliamsPlatform = function(mainElement, proto) {
var video, timer, pixels, displayPCs;
var screenNeedsRefresh = false;
- var membus, iobus;
+ var membus;
var video_counter;
var audio, worker, workerchannel;
@@ -169,7 +169,7 @@ var WilliamsPlatform = function(mainElement, proto) {
function write_display_byte(a,v) {
ram.mem[a] = v;
drawDisplayByte(a, v);
- displayPCs[a] = cpu.getPC(); // save program counter
+ if (displayPCs) displayPCs[a] = cpu.getPC(); // save program counter
}
function drawDisplayByte(a,v) {
@@ -272,15 +272,18 @@ var WilliamsPlatform = function(mainElement, proto) {
ram = new RAM(0xc000);
nvram = new RAM(0x400);
// TODO: save in browser storage?
- // defender nvram.mem.set([240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,242,241,242,247,240,244,244,245,242,244,250,240,241,248,243,241,245,245,243,244,241,244,253,240,241,245,249,242,240,244,252,244,245,244,244,240,241,244,242,248,245,245,240,244,247,244,244,240,241,242,245,242,240,244,243,245,242,244,242,240,241,241,240,243,245,244,253,245,242,245,243,240,240,248,242,246,245,245,243,245,243,245,242,240,240,246,240,241,240,245,244,244,253,244,248,240,240,245,250,240,241,240,240,240,243,240,243,240,241,240,244,240,241,240,241,240,240,240,240,240,240,240,245,241,245,240,241,240,245,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240]);
-
- displayPCs = new Uint16Array(new ArrayBuffer(0x9800*2));
+ //displayPCs = new Uint16Array(new ArrayBuffer(0x9800*2));
rom = padBytes(new lzgmini().decode(ROBOTRON_ROM).slice(0), 0xc001);
membus = {
read: memread_williams,
write: memwrite_williams,
};
- cpu = self.newCPU(membus);
+ //var probebus = new BusProbe(membus);
+ var iobus = {
+ read: function(a) {return 0;},
+ write: function(a,v) {console.log(hex(a),hex(v));}
+ }
+ cpu = self.newCPU(membus, iobus);
audio = new MasterAudio();
worker = new Worker("./src/audio/z80worker.js");
@@ -293,7 +296,7 @@ var WilliamsPlatform = function(mainElement, proto) {
var x = Math.floor(e.offsetX * video.canvas.width / $(video.canvas).width());
var y = Math.floor(e.offsetY * video.canvas.height / $(video.canvas).height());
var addr = (x>>3) + (y*32) + 0x400;
- console.log(x, y, hex(addr,4), "PC", hex(displayPCs[addr],4));
+ if (displayPCs) console.log(x, y, hex(addr,4), "PC", hex(displayPCs[addr],4));
});
var idata = video.getFrameData();
setKeyboardFromMap(video, pia6821, ROBOTRON_KEYCODE_MAP);
@@ -419,3 +422,5 @@ var WilliamsZ80Platform = function(mainElement) {
PLATFORMS['williams'] = WilliamsPlatform;
PLATFORMS['williams-z80'] = WilliamsZ80Platform;
+
+// http://seanriddle.com/willhard.html
diff --git a/src/ui.js b/src/ui.js
index c66c38c3..aa5e5621 100644
--- a/src/ui.js
+++ b/src/ui.js
@@ -36,6 +36,8 @@ var PRESETS; // presets array
var platform_id;
var platform; // platform object
+var toolbar = $("#controls_top");
+
var FileStore = function(storage, prefix) {
var self = this;
this.saveFile = function(name, text) {
@@ -344,6 +346,8 @@ function setCode(text) {
return;
worker.postMessage({code:text, platform:platform_id,
tool:platform.getToolForFilename(current_preset_id)});
+ toolbar.addClass("is-busy");
+ $('#compile_spinner').css('visibility', 'visible');
}
function arrayCompare(a,b) {
@@ -363,7 +367,6 @@ function setCompileOutput(data) {
assemblyfile = new SourceFile(data.asmlines, data.intermediate.listing);
}
// errors?
- var toolbar = $("#controls_top");
function addErrorMarker(line, msg) {
var div = document.createElement("div");
div.setAttribute("class", "tooltipbox tooltiperror");
@@ -442,6 +445,8 @@ function setCompileOutput(data) {
}
worker.onmessage = function(e) {
+ toolbar.removeClass("is-busy");
+ $('#compile_spinner').css('visibility', 'hidden');
// TODO: this doesn't completely work yet
if (pendingWorkerMessages > 1) {
pendingWorkerMessages = 0;
diff --git a/tools/sintbl.py b/tools/sintbl.py
new file mode 100644
index 00000000..d21e75b4
--- /dev/null
+++ b/tools/sintbl.py
@@ -0,0 +1,13 @@
+
+import math
+
+#n = 8
+#m = 127
+
+n = 64
+m = 127
+
+for i in range(0,n*4):
+ print '%d,' % int(round(math.sin(i*math.pi/2/n)*m)),
+ if i % 16 == 15:
+ print