MEGA DRIVEでTMS9918を表示してみる その6

SGDKを使ってMEGA DRIVEで、TMS9918を表示するプログラムを開発して行きます。今回も"Hello World"の高速化を行いました。

f:id:tanam:20140829235707p:image:w360

ポイントは、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;
}