SGDKを使ってMEGA DRIVEで、TMS9918を表示するプログラムを開発して行きます。今回も"Hello World"の高速化を行いました。
ポイントは、Z80は00C0HにあるサブルーチンでHALTせずに、リングバッファに書き込むことです。68000はZ80をHALTしてリングバッファを調べて、Z80を再開して68000の描画ルーチンと同時に実行します。
#include <genesis.h> #define RGB24_TO_COLOR(color) (((color >> 12) & 0x0E00) | ((color >> 8) & 0x00E0) | ((color >> 4) & 0x000E)) #define TMS9918A_VRAM_SIZE 0x4000 #define ADDR_MASK (TMS9918A_VRAM_SIZE - 1) const u8 hello[0x00E0]={ 0xC3,0x03,0x00,0xf3,0x31,0xFF,0x1F,0x21,0xC0,0x00,0x22,0x01,0x00,0x0E,0xBF,0xAF, // 0000H 0xED,0x79,0x3E,0x40,0xED,0x79,0x21,0xFF,0x3F,0x0E,0xBE,0x3E,0x00,0xED,0x79,0x2B, // 0010H 0x7C,0xB5,0x00,0x00,0x20,0xF5,0x0E,0xBF,0xED,0x79,0x3E,0x80,0xED,0x79,0x3E,0x50, // 0020H 0xED,0x79,0x3E,0x81,0xED,0x79,0xAF,0xED,0x79,0x3E,0x82,0xED,0x79,0x3E,0x01,0xED, // 0030H 0x79,0x3E,0x84,0xED,0x79,0x3E,0xF0,0xED,0x79,0x3E,0x87,0xED,0x79,0x3E,0x08,0xED, // 0040H 0x79,0x3E,0x48,0xED,0x79,0x21,0x87,0x00,0x06,0x38,0x7E,0x0E,0xBE,0xED,0x79,0x23, // 0050H 0x00,0x00,0x10,0xF6,0x0E,0xBF,0xAF,0xED,0x79,0x3E,0x40,0xED,0x79,0x21,0x7B,0x00, // 0060H 0x06,0x0C,0x7E,0x0E,0xBE,0xED,0x79,0x23,0x10,0xF8,0x76,0x01,0x02,0x03,0x03,0x04, // 0070H 0x00,0x05,0x04,0x06,0x03,0x07,0x00,0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00,0x00, // 0080H 0x00,0x70,0x88,0xF8,0x80,0x70,0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // 0090H 0x00,0x70,0x88,0x88,0x88,0x70,0x00,0x88,0x88,0x88,0xA8,0xA8,0xD8,0x88,0x00,0x00, // 00A0H 0x00,0xB0,0xC8,0x80,0x80,0x80,0x00,0x08,0x08,0x68,0x98,0x88,0x98,0x68,0x00,0x00, // 00B0H 0xDD,0x21,0x00,0x01,0xFD,0x21,0x00,0x02,0xFD,0x77,0x00,0xDD,0x71,0x00,0x3A,0xCA, // 00C0H 0x00,0x3C,0xCB,0x7F,0x28,0x01,0xAF,0x32,0xCA,0x00,0x32,0xCD,0x00,0xC9,0x00,0x00};// 00D0H u8 regs[8], status_reg, read_ahead, first_byte; u16 vram_addr; u8 latch, intstat; u16 color_table, pattern_table, name_table; u16 sprite_pattern, sprite_attrib; u16 color_mask, pattern_mask; u32 screen[768][8]; u8 vram[TMS9918A_VRAM_SIZE]; void initZ80(u8 *source, u32 length) { u8 *haltZ80; u8 *resetZ80; u8 *ramZ80; u32 cnt; haltZ80 = (u8 *)0xA11100; resetZ80 = (u8 *)0xA11200; *resetZ80 = 0x01; *haltZ80 = 0x01; ramZ80 = (u8 *)0xA00000; for(cnt = length; cnt != 0; cnt--) { if (*source==0xED && *(source+1)==0x79) { *ramZ80++ = 0xC7; *ramZ80++ = 0x00; source+=2; cnt--; } else { *ramZ80++ = *source++; } } *resetZ80 = 0x00; *haltZ80 = 0x00; *resetZ80 = 0x01; } void reset() { memset(vram, 0, sizeof(vram)); memset(regs, 0, sizeof(regs)); status_reg = read_ahead = first_byte = 0; vram_addr = 0; intstat = latch = 0; color_table = pattern_table = name_table = 0; sprite_pattern = sprite_attrib = 0; color_mask = pattern_mask = 0; } ///void set_intstat(u8 val) ///{ /// if(val != intstat) { /// write_signals(&outputs_irq, val ? 0xffffffff : 0); /// intstat = val; /// } ///} u32 read_io8(u8 addr) { if(addr & 1) { // register u8 val = status_reg; status_reg = 0x1f; /// set_intstat(0); latch = 0; return val; } else { // vram u8 val = read_ahead; read_ahead = vram[vram_addr]; vram_addr = (vram_addr + 1) & ADDR_MASK; latch = 0; return val; } } void write_io8(u8 addr, u8 data) { if(addr & 1) { // register if(latch) { if(data & 0x80) { switch(data & 7) { case 0: regs[0] = first_byte & 3; if(regs[0] & 2) { color_table = ((regs[3] & 0x80) * 64) & ADDR_MASK; color_mask = ((regs[3] & 0x7f) * 8) | 7; pattern_table = ((regs[4] & 4) * 2048) & ADDR_MASK; pattern_mask = ((regs[4] & 3) * 256) | (color_mask & 0xff); } else { color_table = (regs[3] * 64) & ADDR_MASK; pattern_table = (regs[4] * 2048) & ADDR_MASK; } break; case 1: regs[1] = first_byte & 0xfb; /// set_intstat((regs[1] & 0x20) && (status_reg & 0x80)); break; case 2: regs[2] = first_byte & 0x0f; name_table = (regs[2] * 1024) & ADDR_MASK; break; case 3: regs[3] = first_byte; if(regs[0] & 2) { color_table = ((regs[3] & 0x80) * 64) & ADDR_MASK; color_mask = ((regs[3] & 0x7f) * 8) | 7; } else { color_table = (regs[3] * 64) & ADDR_MASK; } pattern_mask = ((regs[4] & 3) * 256) | (color_mask & 0xff); break; case 4: regs[4] = first_byte & 7; if(regs[0] & 2) { pattern_table = ((regs[4] & 4) * 2048) & ADDR_MASK; pattern_mask = ((regs[4] & 3) * 256) | 255; } else { pattern_table = (regs[4] * 2048) & ADDR_MASK; } break; case 5: regs[5] = first_byte & 0x7f; sprite_attrib = (regs[5] * 128) & ADDR_MASK; break; case 6: regs[6] = first_byte & 7; sprite_pattern = (regs[6] * 2048) & ADDR_MASK; break; case 7: regs[7] = first_byte; break; } } else { vram_addr = ((data * 256) | first_byte) & ADDR_MASK; if(!(data & 0x40)) { read_io8(0); // read ahead } } latch = 0; } else { first_byte = data; latch = 1; } } else { // vram vram[vram_addr] = data; vram_addr = (vram_addr + 1) & ADDR_MASK; read_ahead = data; latch = 0; } } void draw_mode0() { int x, y, yy, name; for(y = 0, name = 0; y < 24; y++) { for(x = 0; x < 32; x++) { u16 code = vram[name_table + (name++)]; u8* pattern_ptr = vram + pattern_table + code * 8; u8 color = vram[color_table + (code >> 3)]; u8 fg = (color & 0xf0) ? (color >> 4) : (regs[7] & 0x0f); u8 bg = (color & 0x0f) ? (color & 0x0f) : (regs[7] & 0x0f); for(yy=0; yy < 8; yy++) { u8 pattern = *pattern_ptr++; u8* buffer = screen[y * 8 + yy] + x * 8; buffer[0] = (pattern & 0x80) ? fg : bg; buffer[1] = (pattern & 0x40) ? fg : bg; buffer[2] = (pattern & 0x20) ? fg : bg; buffer[3] = (pattern & 0x10) ? fg : bg; buffer[4] = (pattern & 0x08) ? fg : bg; buffer[5] = (pattern & 0x04) ? fg : bg; buffer[6] = (pattern & 0x02) ? fg : bg; buffer[7] = (pattern & 0x01) ? fg : bg; } } } } void draw_mode1() { u8* buffer; int x, x1, x2, y, yy, name; u8 fg = regs[7] >> 4; u8 bg = regs[7] & 0x0f; memset(screen, ((bg << 4) | bg), sizeof(screen)); for(y = 0, name = 0; y < 24; y++) { for(x = 0, x1=0, x2=0; x < 40; x++, x1+=2) { if (x1 > 6) {x1=0; x2++;} u16 code = vram[name_table + (name++)]; u8* pattern_ptr = vram + pattern_table + code * 8; for(yy = 0; yy < 8; yy++) { u8 pattern = *pattern_ptr++; if (x1==0) { buffer = (u8 *)(screen[y * 32 + x - x2 + 1]) + yy*4; buffer[0] = (pattern & 0x80) ? (fg << 4) : (bg << 4); buffer[0] |= (pattern & 0x40) ? fg : bg; buffer[1] = (pattern & 0x20) ? (fg << 4) : (bg << 4); buffer[1] |= (pattern & 0x10) ? fg : bg; buffer[2] = (pattern & 0x08) ? (fg << 4) : (bg << 4); buffer[2] |= (pattern & 0x04) ? fg : bg; } if (x1==2) { buffer = (u8 *)(screen[y * 32 + x - x2]) + yy*4; buffer[3] = (pattern & 0x80) ? (fg << 4) : (bg << 4); buffer[3] |= (pattern & 0x40) ? fg : bg; buffer = (u8 *)(screen[y * 32 + x - x2 + 1]) + yy*4; buffer[0] = (pattern & 0x20) ? (fg << 4) : (bg << 4); buffer[0] |= (pattern & 0x10) ? fg : bg; buffer[1] = (pattern & 0x08) ? (fg << 4) : (bg << 4); buffer[1] |= (pattern & 0x04) ? fg : bg; } if (x1==4) { buffer = (u8 *)(screen[y * 32 + x - x2]) + yy*4; buffer[2] = (pattern & 0x80) ? (fg << 4) : (bg << 4); buffer[2] |= (pattern & 0x40) ? fg : bg; buffer[3] = (pattern & 0x20) ? (fg << 4) : (bg << 4); buffer[3] |= (pattern & 0x10) ? fg : bg; buffer = (u8 *)(screen[y * 32 + x - x2 + 1]) + yy*4; buffer[0] = (pattern & 0x08) ? (fg << 4) : (bg << 4); buffer[0] |= (pattern & 0x04) ? fg : bg; } if (x1==6) { buffer = (u8 *)(screen[y * 32 + x - x2]) + yy*4; buffer[1] = (pattern & 0x80) ? (fg << 4) : (bg << 4); buffer[1] |= (pattern & 0x40) ? fg : bg; buffer[2] = (pattern & 0x20) ? (fg << 4) : (bg << 4); buffer[2] |= (pattern & 0x10) ? fg : bg; buffer[3] = (pattern & 0x08) ? (fg << 4) : (bg << 4); buffer[3] |= (pattern & 0x04) ? fg : bg; } } } } } void draw_mode2() { int x, y, yy, name; for(y = 0, name = 0; y < 24; y++) { for(x = 0; x < 32; x++) { u16 code = vram[name_table + (name++)] + (y & 0xf8) * 32; u8* pattern_ptr = vram + pattern_table + (code & pattern_mask) * 8; u8* color_ptr = vram + color_table + (code & color_mask) * 8; for(yy = 0; yy < 8; yy++) { u8 pattern = *pattern_ptr++; u8 color = *color_ptr++; u8 fg = (color & 0xf0) ? (color >> 4) : (regs[7] & 0x0f); u8 bg = (color & 0x0f) ? (color & 0x0f) : (regs[7] & 0x0f); u8* buffer = screen[y * 8 + yy] + x * 8; buffer[0] = (pattern & 0x80) ? fg : bg; buffer[1] = (pattern & 0x40) ? fg : bg; buffer[2] = (pattern & 0x20) ? fg : bg; buffer[3] = (pattern & 0x10) ? fg : bg; buffer[4] = (pattern & 0x08) ? fg : bg; buffer[5] = (pattern & 0x04) ? fg : bg; buffer[6] = (pattern & 0x02) ? fg : bg; buffer[7] = (pattern & 0x01) ? fg : bg; } } } } void draw_mode12() { int x, y, yy, name; u8 fg = regs[7] >> 4; u8 bg = regs[7] & 0x0f; memset(screen, bg, sizeof(screen)); for(y = 0, name = 0; y < 24; y++) { for(x = 0; x < 40; x++) { u16 code = vram[name_table + (name++)] + (y & 0xf8) * 32; u8* pattern_ptr = vram + pattern_table + (code & pattern_mask) * 8; for(yy = 0; yy < 8; yy++) { u8 pattern = *pattern_ptr++; u8* buffer = screen[y * 8 + yy] + x * 6 + 8; buffer[0] = (pattern & 0x80) ? fg : bg; buffer[1] = (pattern & 0x40) ? fg : bg; buffer[2] = (pattern & 0x20) ? fg : bg; buffer[3] = (pattern & 0x10) ? fg : bg; buffer[4] = (pattern & 0x08) ? fg : bg; buffer[5] = (pattern & 0x04) ? fg : bg; } } } } void draw_mode3() { int x, y, yy, yyy, name; for(y = 0, name = 0; y < 24; y++) { for(x = 0; x < 32; x++) { u16 code = vram[name_table + (name++)]; u8* pattern_ptr = vram + pattern_table + code * 8 + (y & 3) * 2; for(yy = 0; yy < 2; yy++) { u8 color = *pattern_ptr++; u8 fg = (color & 0xf0) ? (color >> 4) : (regs[7] & 0x0f); u8 bg = (color & 0x0f) ? (color & 0x0f) : (regs[7] & 0x0f); for(yyy = 0; yyy < 4; yyy++) { u8* buffer = screen[y * 8 + yy * 4 + yyy] + x * 8; buffer[0] = buffer[1] = buffer[2] = buffer[3] = fg; buffer[4] = buffer[5] = buffer[6] = buffer[7] = bg; } } } } } void draw_mode23() { int x, y, yy, yyy, name; for(y = 0, name = 0; y < 24;y++) { for(x = 0; x < 32; x++) { u16 code = vram[name_table + (name++)]; u8* pattern_ptr = vram + pattern_table + ((code + (y & 3) * 2 + (y & 0xf8) * 32) & pattern_mask) * 8; for(yy = 0; yy < 2; yy++) { u8 color = *pattern_ptr++; u8 fg = (color & 0xf0) ? (color >> 4) : (regs[7] & 0x0f); u8 bg = (color & 0x0f) ? (color & 0x0f) : (regs[7] & 0x0f); for(yyy = 0; yyy < 4; yyy++) { u8* buffer = screen[y * 8 + yy * 4 + yyy] + x * 8; buffer[0] = buffer[1] = buffer[2] = buffer[3] = fg; buffer[4] = buffer[5] = buffer[6] = buffer[7] = bg; } } } } } void draw_modebogus() { int y, i, j; u8 fg = regs[7] >> 4; u8 bg = regs[7] & 0x0f; for(y = 0; y < 192; y++) { u8* buffer = screen[y]; int x = 0; for(i = 0; i < 8; i++) { buffer[x++] = bg; } for(i = 0; i < 40; i++) { for(j = 0; j < 4; j++) { buffer[x++] = fg; } for(j = 0; j < 2; j++) { buffer[x++] = bg; } } for(i = 0; i < 8; i++) { buffer[x++] = bg; } } } void draw_sprites() { int x, y, xx, yy, i, j; u8* attrib_ptr = vram + sprite_attrib; int size = (regs[1] & 2) ? 16 : 8; u8 large = ((regs[1] & 1) != 0); u8 limit[192], collision[192][256]; int illegal_sprite = 0, illegal_sprite_line = 255, p; memset(limit, 4, sizeof(limit)); memset(collision, 0, sizeof(collision)); status_reg = 0x80; for(p = 0; p < 32; p++) { y = *attrib_ptr++; if(y == 208) { break; } if(y > 208) { y = -(~y & 0xff); } else { y++; } x = *attrib_ptr++; u8* pattern_ptr = vram + sprite_pattern + ((size == 16) ? (*attrib_ptr & 0xfc) : *attrib_ptr) * 8; attrib_ptr++; u8 c = *attrib_ptr & 0x0f; if(*attrib_ptr & 0x80) { x -= 32; } attrib_ptr++; if(!large) { // draw sprite (not enlarged) for(yy = y; yy < y + size; yy++) { if(yy < 0 || 191 < yy) { continue; } if(limit[yy] == 0) { // illegal sprite line if(yy < illegal_sprite_line) { illegal_sprite_line = yy; illegal_sprite = p; } else if(illegal_sprite_line == yy) { if(illegal_sprite > p) { illegal_sprite = p; } } continue; } else { limit[yy]--; } u16 line = pattern_ptr[yy - y] * 256 + pattern_ptr[yy - y + 16]; for(xx = x; xx < x + size; xx++) { if(line & 0x8000) { if(0 <= xx && xx < 256) { if(collision[yy][xx]) { status_reg |= 0x20; } else { collision[yy][xx] = 1; } if(c && !(collision[yy][xx] & 2)) { collision[yy][xx] |= 2; screen[yy][xx] = c; } } } line *= 2; } } } else { // draw enlarged sprite for(i = 0; i < size; i++) { yy = y + i * 2; u16 line2 = pattern_ptr[i] * 256 + pattern_ptr[i + 16]; for(j = 0; j < 2; j++) { if(0 <= yy && yy <= 191) { if(limit[yy] == 0) { // illegal sprite line if(yy < illegal_sprite_line) { illegal_sprite_line = yy; illegal_sprite = p; } else if(illegal_sprite_line == yy) { if(illegal_sprite > p) { illegal_sprite = p; } } continue; } else { limit[yy]--; } u16 line = line2; for(xx = x; xx < x + size * 2; xx += 2) { if(line & 0x8000) { if(0 <= xx && xx < 256) { if(collision[yy][xx]) { status_reg |= 0x20; } else { collision[yy][xx] = 1; } if(c && !(collision[yy][xx] & 2)) { collision[yy][xx] |= 2; screen[yy][xx] = c; } } if(0 <= xx + 1 && xx + 1 < 256) { if(collision[yy][xx + 1]) { status_reg |= 0x20; } else { collision[yy][xx + 1] = 1; } if(c && !(collision[yy][xx + 1] & 2)) { collision[yy][xx + 1] |= 2; screen[yy][xx + 1] = c; } } } line *= 2; } } yy++; } } } } if(illegal_sprite_line == 255) { status_reg |= (p > 31) ? 31 : p; } else { status_reg |= 0x40 + illegal_sprite; } } void event_vline(int v, int clock) { if(v == 192) { // create virtual screen if(regs[1] & 0x40) { // draw character plane int mode = (regs[0] & 2) | ((regs[1] & 0x10) >> 4) | ((regs[1] & 8) >> 1); switch(mode) { case 0: draw_mode0(); break; case 1: draw_mode1(); break; case 2: draw_mode2(); break; case 3: draw_mode12(); break; case 4: draw_mode3(); break; case 6: draw_mode23(); break; case 5: case 7: draw_modebogus(); break; } // draw sprite plane if((regs[1] & 0x50) == 0x40) { draw_sprites(); } } else { memset(screen, 0, sizeof(screen)); } // do interrupt status_reg |= 0x80; /// set_intstat((regs[1] & 0x20) != 0); } } int main( ) { int i, j, k; u8 *p, *q, *haltZ80; u8 r; VDP_setPaletteColor(16, RGB24_TO_COLOR(0x000000)); /// SWAP v---------v VDP_setPaletteColor(17, RGB24_TO_COLOR(0x000000)); /// 0x21 0xC8 0x42 VDP_setPaletteColor(18, RGB24_TO_COLOR(0x42C821)); /// RGB_COLOR( 33, 200, 66) VDP_setPaletteColor(19, RGB24_TO_COLOR(0x78DC5E)); /// RGB_COLOR( 94, 220, 120) VDP_setPaletteColor(20, RGB24_TO_COLOR(0xED5554)); /// RGB_COLOR( 84, 85, 237) VDP_setPaletteColor(21, RGB24_TO_COLOR(0xFC767D)); /// RGB_COLOR(125, 118, 252) VDP_setPaletteColor(22, RGB24_TO_COLOR(0x4D52D4)); /// RGB_COLOR(212, 82, 77) VDP_setPaletteColor(23, RGB24_TO_COLOR(0xF5EB42)); /// RGB_COLOR( 66, 235, 245) VDP_setPaletteColor(24, RGB24_TO_COLOR(0x5455FC)); /// RGB_COLOR(252, 85, 84) VDP_setPaletteColor(25, RGB24_TO_COLOR(0x7879FF)); /// RGB_COLOR(255, 121, 120) VDP_setPaletteColor(26, RGB24_TO_COLOR(0x54C1D4)); /// RGB_COLOR(212, 193, 84) VDP_setPaletteColor(27, RGB24_TO_COLOR(0x80CEE6)); /// RGB_COLOR(230, 206, 128) VDP_setPaletteColor(28, RGB24_TO_COLOR(0x3BB021)); /// RGB_COLOR( 33, 176, 59) VDP_setPaletteColor(29, RGB24_TO_COLOR(0xBA5BC9)); /// RGB_COLOR(201, 91, 186) VDP_setPaletteColor(30, RGB24_TO_COLOR(0xCCCCCC)); /// RGB_COLOR(204, 204, 204) VDP_setPaletteColor(31, RGB24_TO_COLOR(0xFFFFFF)); /// RGB_COLOR(255, 255, 255) k=0; for (j=0; j<24; j++) for (i=0; i<32; i++) VDP_setTileMapXY(APLAN, TILE_ATTR_FULL(PAL1, 0, 0, 0, k++), i+4, j+2); haltZ80 = (u8 *)0xA11100; i = 0; j = 0; k = 0; p = (u8 *)0xA00100; q = (u8 *)0xA00200; r = 0; reset(); initZ80(hello, 0x00E0); while(1) { *haltZ80 = 0x01; if (*(p+r) != 0x00) { j = *(p+r); k = *(q+r); *(p+r) = 0; r++; if (r>127) r=0; } if (i++ > 261) { event_vline(192, 0); i=0; for (k=0; k<768; k++) VDP_loadTileData(screen[k] , k, 1, 0); } *haltZ80 = 0x00; if (j) { write_io8(j, k); j = 0; k = 0; } } return 0; }