Index: .cproject
===================================================================
--- .cproject	(revision 43ea41794eee143de5cc153ae8562d075b78fafe)
+++ .cproject	(revision 5475ecffc7e57c236384c2d970e1fea0538c9460)
@@ -68,3 +68,4 @@
 		</scannerConfigBuildInfo>
 	</storageModule>
+	<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
 </cproject>
Index: .settings/language.settings.xml
===================================================================
--- .settings/language.settings.xml	(revision 43ea41794eee143de5cc153ae8562d075b78fafe)
+++ .settings/language.settings.xml	(revision 5475ecffc7e57c236384c2d970e1fea0538c9460)
@@ -6,5 +6,5 @@
 			<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
 			<provider copy-of="extension" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"/>
-			<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-1750853831677083752" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
+			<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="617322979982056114" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
 				<language-scope id="org.eclipse.cdt.core.gcc"/>
 				<language-scope id="org.eclipse.cdt.core.g++"/>
Index: emu/ser.c
===================================================================
--- emu/ser.c	(revision 43ea41794eee143de5cc153ae8562d075b78fafe)
+++ emu/ser.c	(revision 5475ecffc7e57c236384c2d970e1fea0538c9460)
@@ -60,5 +60,5 @@
 };
 
-static uint8_t mem[CON_H][CON_W + 1];
+static uint8_t mem[CON_H][CON_W];
 
 static SDL_Window *win;
@@ -81,5 +81,5 @@
 static void scroll(void)
 {
-	memmove(mem, mem + 1, (CON_H - 1) * (CON_W + 1));
+	memmove(mem, mem + 1, (CON_H - 1) * CON_W);
 	memset(mem + (CON_H - 1), ' ', CON_W);
 }
@@ -256,4 +256,5 @@
 	for (int32_t y = 0; y < CON_H; ++y) {
 		char line[CON_W + 1];
+		line[CON_W] = 0;
 
 		if (SDL_LockMutex(cpu_mutex) < 0) {
@@ -261,5 +262,5 @@
 		}
 
-		memcpy(line, mem[y], CON_W + 1);
+		memcpy(line, mem[y], CON_W);
 
 		if (SDL_UnlockMutex(cpu_mutex) < 0) {
@@ -515,6 +516,4 @@
 			mem[y][x] = ' ';
 		}
-
-		mem[y][CON_W] = 0;
 	}
 }
Index: emu/vid.c
===================================================================
--- emu/vid.c	(revision 43ea41794eee143de5cc153ae8562d075b78fafe)
+++ emu/vid.c	(revision 5475ecffc7e57c236384c2d970e1fea0538c9460)
@@ -34,4 +34,5 @@
 #define REG_ODTBA 7
 #define REG_ATBA 8
+#define REG_CGBA 10
 #define REG_ATBAC 11
 
@@ -48,4 +49,6 @@
 #define OD0_BLA 0x0010
 #define OD0_CB 0x0800
+
+#define AT_CG 0x8000
 
 #define WIN_SZ_W 0x10000
@@ -63,5 +66,6 @@
 	uint16_t *mem;
 	bool tran;
-} bm_obj_t;
+	int32_t fon_h;
+} obj_t;
 
 static int32_t reg_off = REG_OFF;
@@ -72,6 +76,6 @@
 static uint32_t pal[16];
 
-static bm_obj_t bm_objs[16];
-static int32_t n_bm_objs = 0;
+static obj_t objs[16];
+static int32_t n_objs = 0;
 
 static SDL_Window *win;
@@ -80,4 +84,65 @@
 static SDL_atomic_t frame;
 
+static void rend_bm(uint16_t *vid, int32_t w, int32_t h, uint32_t *pix, int32_t pitch)
+{
+	for (int32_t y = 0; y < h; ++y) {
+		for (int32_t x = 0; x < w / 4; ++x) {
+			uint16_t v4 = *vid++;
+
+			*pix++ = pal[(v4 & 0x000f) >>  0];
+			*pix++ = pal[(v4 & 0x00f0) >>  4];
+			*pix++ = pal[(v4 & 0x0f00) >>  8];
+			*pix++ = pal[(v4 & 0xf000) >> 12];
+		}
+
+		pix += pitch / 4 - w;
+	}
+}
+
+static void rend_tx(uint16_t *vid, int32_t w, int32_t h, int32_t fon_h,
+		uint32_t *pix, int32_t pitch)
+{
+	int32_t cgba = mem[REG_CGBA];
+
+	uint16_t *cg0 = mem + ((cgba & 0x00f0) <<  8);
+	uint16_t *cg1 = mem + ((cgba & 0x000f) << 12);
+
+	int32_t line = fon_h - 1;
+
+	for (int32_t y = 0; y < h; ++y) {
+		uint16_t *walk = vid;
+
+		for (int32_t col2 = 0; col2 < w / 16; ++col2) {
+			uint16_t ch2 = *walk++;
+
+			for (int32_t i = 0; i < 2; ++i) {
+				int32_t ch = (uint8_t)ch2;
+				int32_t at = *walk++;
+
+				int32_t bg = at & 0x000f;
+				int32_t fg = (at & 0x00f0) >> 4;
+
+				uint16_t *cg = (at & AT_CG) != 0 ? cg0 : cg1;
+				int32_t bits = cg[256 * line + ch];
+				int32_t mask = 0x01;
+
+				for (int32_t k = 0; k < 8; ++k) {
+					*pix++ = (bits & mask) != 0 ? pal[fg] : pal[bg];
+					mask <<= 1;
+				}
+
+				ch2 >>= 8;
+			}
+		}
+
+		if (--line < 0) {
+			line = fon_h - 1;
+			vid = walk;
+		}
+
+		pix += pitch / 4 - w;
+	}
+}
+
 void vid_sdl(void)
 {
@@ -98,19 +163,19 @@
 	}
 
-	void *buf;
-	int32_t pitch;
-
 	if (SDL_LockMutex(cpu_mutex) < 0) {
 		fail("SDL_LockMutex() failed: %s", SDL_GetError());
 	}
 
-	for (int32_t i = 0; i < n_bm_objs; ++i) {
-		bm_obj_t *bm_obj = bm_objs + i;
-
-		pal[0] = bm_obj->tran ? pal[0] & 0xffffff00 : pal[0] | 0x000000ff;
+	for (int32_t i = 0; i < n_objs; ++i) {
+		obj_t *obj = objs + i;
+
+		pal[0] = obj->tran ? pal[0] & 0xffffff00 : pal[0] | 0x000000ff;
 
 		SDL_Rect src = {
-			.x = 0, .y = 0, .w = bm_obj->w, .h = bm_obj->h
+			.x = 0, .y = 0, .w = obj->w, .h = obj->h
 		};
+
+		void *buf;
+		int32_t pitch;
 
 		if (SDL_LockTexture(tex, &src, &buf, &pitch) < 0) {
@@ -118,18 +183,9 @@
 		}
 
-		uint32_t *pix = buf;
-		uint16_t *vid = bm_obj->mem;
-
-		for (int32_t y = 0; y < src.h; ++y) {
-			for (int32_t x = 0; x < src.w / 4; ++x) {
-				uint16_t v4 = *vid++;
-
-				*pix++ = pal[(v4 & 0x000f) >>  0];
-				*pix++ = pal[(v4 & 0x00f0) >>  4];
-				*pix++ = pal[(v4 & 0x0f00) >>  8];
-				*pix++ = pal[(v4 & 0xf000) >> 12];
-			}
-
-			pix += pitch / 4 - src.w;
+		if (obj->fon_h < 0) {
+			rend_bm(obj->mem, obj->w, obj->h, buf, pitch);
+		}
+		else {
+			rend_tx(obj->mem, obj->w, obj->h, obj->fon_h, buf, pitch);
 		}
 
@@ -137,14 +193,14 @@
 
 		SDL_Rect dst = {
-			.x = SCALE(bm_obj->x), .y = SCALE(bm_obj->y),
-			.w = SCALE(bm_obj->w), .h = SCALE(bm_obj->h)
+			.x = SCALE(obj->x), .y = SCALE(obj->y),
+			.w = SCALE(obj->w), .h = SCALE(obj->h)
 		};
 
 		ver2("vid rend %d %dx%d -> (%d, %d) (%d, %d) %dx%d",
 				i,
-				bm_obj->w, bm_obj->h,
-				bm_obj->x, bm_obj->y,
-				SCALE(bm_obj->x), SCALE(bm_obj->y),
-				SCALE(bm_obj->w), SCALE(bm_obj->h));
+				obj->w, obj->h,
+				obj->x, obj->y,
+				SCALE(obj->x), SCALE(obj->y),
+				SCALE(obj->w), SCALE(obj->h));
 
 		if (SDL_RenderCopy(ren, tex, &src, &dst) < 0) {
@@ -250,5 +306,5 @@
 	int32_t odtba = mem[REG_ODTBA] & 0xffc0;
 
-	n_bm_objs = 0;
+	n_objs = 0;
 
 	for (int32_t i = 0; i < 16; ++i) {
@@ -263,7 +319,7 @@
 		}
 
-		int32_t w = (od[1] & 0xfc00) >> 6;
-
-		if (w == 0) {
+		int32_t w64 = (od[1] & 0xfc00) >> 10;
+
+		if (w64 == 0) {
 			ver3("vid obj %d empty", i);
 			continue;
@@ -293,25 +349,32 @@
 
 		int32_t x = (od[1] & 0x03ff) * 2;
-		int32_t off = ((od[0] & 0x00c0) << 10) | od[2];
+		int32_t off, fon_h, w;
+
+		if (cb) {
+			off = od[2];
+			int32_t vcr1 = mem[REG_VCR1];
+			fon_h = (vcr1 & 0xf000) >> 12;
+			w = w64 * 4 / 3 * 2 * 8;
+		}
+		else {
+			off = ((od[0] & 0x00c0) << 10) | od[2];
+			fon_h = -1;
+			w = w64 * 16;
+		}
+
+		obj_t *obj = objs + n_objs;
+
+		obj->x = x;
+		obj->y = flips[0];
+		obj->w = w;
+		obj->h = flips[1] - flips[0];
+		obj->mem = mem + off;
+		obj->tran = tde;
+		obj->fon_h = fon_h;
 
 		ver2("vid obj %d %c %c %d:%d %d+%d 0x%05x",
 				i, cb ? 'c' : 'b', tde ? 't' : '-', flips[0], flips[1], x, w, off);
 
-		if (!cb) {
-			bm_obj_t *bm_obj = bm_objs + n_bm_objs;
-
-			bm_obj->x = x;
-			bm_obj->y = flips[0];
-			bm_obj->w = w;
-			bm_obj->h = flips[1] - flips[0];
-			bm_obj->mem = mem + off;
-			bm_obj->tran = tde;
-
-			++n_bm_objs;
-		}
-		else {
-			int32_t vcr1 = mem[REG_VCR1];
-			int32_t fon_h = (vcr1 & 0xf000) >> 12;
-		}
+		++n_objs;
 	}
 
