/*

JatoBAS - A javascript MSX-BASIC translator
Copyright (C) 2006  Rafael Jannone

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Read the LGPL license at: 
http://www.gnu.org/copyleft/lesser.txt

Contact the author -
email: rafael AT jannone DOT org

*/

function toHex(v) {
	var htab = "0123456789ABCDEF";
	var r = htab.substr(v & 15, 1);
	v >>= 4;
	r = htab.substr(v & 15, 1) + r;
	return r;
}

function CanvasBase() {}

CanvasBase.prototype.charMap = [
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3c,
0x42,0xa5,0x81,0xa5,0x99,0x42,0x3c,0x3c,0x7e,0xdb,0xff,0xff,0xdb,0x66,0x3c,0x6c,
0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x10,
0x38,0x54,0xfe,0x54,0x10,0x38,0x00,0x10,0x38,0x7c,0xfe,0xfe,0x10,0x38,0x00,0x00,
0x00,0x00,0x30,0x30,0x00,0x00,0x00,0xff,0xff,0xff,0xe7,0xe7,0xff,0xff,0xff,0x38,
0x44,0x82,0x82,0x82,0x44,0x38,0x00,0xc7,0xbb,0x7d,0x7d,0x7d,0xbb,0xc7,0xff,0x0f,
0x03,0x05,0x79,0x88,0x88,0x88,0x70,0x38,0x44,0x44,0x44,0x38,0x10,0x7c,0x10,0x30,
0x28,0x24,0x24,0x28,0x20,0xe0,0xc0,0x3c,0x24,0x3c,0x24,0x24,0xe4,0xdc,0x18,0x10,
0x54,0x38,0xee,0x38,0x54,0x10,0x00,0x10,0x10,0x10,0x7c,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0xf0,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x1f,0x10,0x10,0x10,0x10,0x10,
0x10,0x10,0xff,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,
0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x10,0x10,0x10,0x10,0x00,
0x00,0x00,0xf0,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x1f,0x00,0x00,0x00,0x00,0x10,
0x10,0x10,0xf0,0x00,0x00,0x00,0x00,0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81,0x01,
0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,0x00,
0x10,0x10,0xff,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,
0x20,0x20,0x20,0x00,0x00,0x20,0x00,0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x50,
0x50,0xf8,0x50,0xf8,0x50,0x50,0x00,0x20,0x78,0xa0,0x70,0x28,0xf0,0x20,0x00,0xc0,
0xc8,0x10,0x20,0x40,0x98,0x18,0x00,0x40,0xa0,0x40,0xa8,0x90,0x98,0x60,0x00,0x10,
0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00,0x40,
0x20,0x10,0x10,0x10,0x20,0x40,0x00,0x20,0xa8,0x70,0x20,0x70,0xa8,0x20,0x00,0x00,
0x20,0x20,0xf8,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x40,0x00,
0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,
0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x70,0x88,0x98,0xa8,0xc8,0x88,0x70,0x00,0x20,
0x60,0xa0,0x20,0x20,0x20,0xf0,0x00,0x70,0x88,0x08,0x10,0x60,0x80,0xf8,0x00,0x70,
0x88,0x08,0x30,0x08,0x88,0x70,0x00,0x10,0x30,0x50,0x90,0xf8,0x10,0x10,0x00,0xf8,
0x80,0xe0,0x10,0x08,0x10,0xe0,0x00,0x30,0x40,0x80,0xf0,0x88,0x88,0x70,0x00,0xf8,
0x88,0x10,0x20,0x20,0x20,0x20,0x00,0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,
0x88,0x88,0x78,0x08,0x10,0x60,0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x00,
0x00,0x20,0x00,0x00,0x20,0x20,0x40,0x18,0x30,0x60,0xc0,0x60,0x30,0x18,0x00,0x00,
0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,0xc0,0x60,0x30,0x18,0x30,0x60,0xc0,0x00,0x70,
0x88,0x08,0x10,0x20,0x00,0x20,0x00,0x70,0x88,0x08,0x48,0xa8,0xa8,0x70,0x00,0x20,
0x50,0x88,0x88,0xf8,0x88,0x88,0x00,0xf0,0x48,0x48,0x70,0x48,0x48,0xf0,0x00,0x30,
0x48,0x80,0x80,0x80,0x48,0x30,0x00,0xe0,0x50,0x48,0x48,0x48,0x50,0xe0,0x00,0xf8,
0x80,0x80,0xf0,0x80,0x80,0xf8,0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0x80,0x00,0x70,
0x88,0x80,0xb8,0x88,0x88,0x70,0x00,0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x70,
0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x38,0x10,0x10,0x10,0x90,0x90,0x60,0x00,0x88,
0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00,0x88,
0xd8,0xa8,0xa8,0x88,0x88,0x88,0x00,0x88,0xc8,0xc8,0xa8,0x98,0x98,0x88,0x00,0x70,
0x88,0x88,0x88,0x88,0x88,0x70,0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00,0x70,
0x88,0x88,0x88,0xa8,0x90,0x68,0x00,0xf0,0x88,0x88,0xf0,0xa0,0x90,0x88,0x00,0x70,
0x88,0x80,0x70,0x08,0x88,0x70,0x00,0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,
0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x88,0x88,0x88,0x88,0x50,0x50,0x20,0x00,0x88,
0x88,0x88,0xa8,0xa8,0xd8,0x88,0x00,0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x88,
0x88,0x88,0x70,0x20,0x20,0x20,0x00,0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00,0x70,
0x40,0x40,0x40,0x40,0x40,0x70,0x00,0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x70,
0x10,0x10,0x10,0x10,0x10,0x70,0x00,0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0xf8,0x00,0x80,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x70,0x08,0x78,0x88,0x78,0x00,0x80,0x80,0xb0,0xc8,0x88,0xc8,0xb0,0x00,0x00,
0x00,0x70,0x88,0x80,0x88,0x70,0x00,0x08,0x08,0x68,0x98,0x88,0x98,0x68,0x00,0x00,
0x00,0x70,0x88,0xf8,0x80,0x70,0x00,0x10,0x28,0x20,0xf8,0x20,0x20,0x20,0x00,0x00,
0x00,0x68,0x98,0x98,0x68,0x08,0x70,0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x00,0x20,
0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x90,0x60,0x40,
0x40,0x48,0x50,0x60,0x50,0x48,0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,
0x00,0xd0,0xa8,0xa8,0xa8,0xa8,0x00,0x00,0x00,0xb0,0xc8,0x88,0x88,0x88,0x00,0x00,
0x00,0x70,0x88,0x88,0x88,0x70,0x00,0x00,0x00,0xb0,0xc8,0xc8,0xb0,0x80,0x80,0x00,
0x00,0x68,0x98,0x98,0x68,0x08,0x08,0x00,0x00,0xb0,0xc8,0x80,0x80,0x80,0x00,0x00,
0x00,0x78,0x80,0x70,0x08,0xf0,0x00,0x40,0x40,0xf0,0x40,0x40,0x48,0x30,0x00,0x00,
0x00,0x90,0x90,0x90,0x90,0x68,0x00,0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00,0x00,
0x00,0x88,0xa8,0xa8,0xa8,0x50,0x00,0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,
0x00,0x88,0x88,0x98,0x68,0x08,0x70,0x00,0x00,0xf8,0x10,0x20,0x40,0xf8,0x00,0x18,
0x20,0x20,0x40,0x20,0x20,0x18,0x00,0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x00,0xc0,
0x20,0x20,0x10,0x20,0x20,0xc0,0x00,0x40,0xa8,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x20,0x50,0xf8,0x00,0x00,0x00,0x30,0x48,0x80,0x80,0x80,0x48,0x30,0x60,0x50,
0x00,0x90,0x90,0x90,0x90,0x68,0x00,0x10,0x20,0x70,0x88,0xf8,0x80,0x70,0x00,0x20,
0x50,0x70,0x08,0x78,0x88,0x78,0x00,0x10,0x20,0x20,0x50,0x88,0xf8,0x88,0x00,0x40,
0x20,0x70,0x08,0x78,0x88,0x78,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x70,0x88,0x80,0x88,0x70,0x20,0x20,0x50,0x70,0x88,0xf8,0x80,0x70,0x00,0x10,
0x20,0x70,0x20,0x20,0x20,0x70,0x00,0x10,0x20,0x70,0x88,0x88,0x88,0x70,0x00,0x10,
0x20,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x50,0x20,0x50,0x88,0xf8,0x88,0x00,0x20,
0x50,0xf8,0x80,0xf0,0x80,0xf8,0x00,0x20,0x50,0x70,0x88,0x88,0x88,0x70,0x00,0x40,
0x20,0x20,0x50,0x88,0xf8,0x88,0x00,0x10,0x20,0xf8,0x80,0xf0,0x80,0xf8,0x00,0x00,
0x00,0x6c,0x12,0x7e,0x90,0x6e,0x00,0x3e,0x50,0x90,0x9c,0xf0,0x90,0x9e,0x00,0x20,
0x50,0x70,0x88,0x88,0x88,0x70,0x00,0x50,0x00,0x70,0x88,0x88,0x88,0x70,0x00,0x40,
0x20,0x70,0x88,0x88,0x88,0x70,0x00,0x20,0x50,0x00,0x90,0x90,0x90,0x68,0x00,0x40,
0x20,0x00,0x90,0x90,0x90,0x68,0x00,0x50,0x00,0x88,0x88,0x98,0x68,0x08,0x70,0x50,
0x00,0x70,0x88,0x88,0x88,0x70,0x00,0x50,0x00,0x88,0x88,0x88,0x88,0x70,0x00,0x20,
0x20,0x78,0x80,0x80,0x78,0x20,0x20,0x18,0x24,0x20,0xf8,0x20,0xe2,0x5c,0x00,0x88,
0x50,0x20,0xf8,0x20,0xf8,0x20,0x00,0x60,0x80,0x9c,0x84,0x88,0x90,0x7c,0x00,0x18,
0x20,0x20,0xf8,0x20,0x20,0x20,0x40,0x10,0x20,0x70,0x08,0x78,0x88,0x78,0x00,0x10,
0x20,0x00,0x60,0x20,0x20,0x70,0x00,0x10,0x20,0x70,0x88,0x88,0x88,0x70,0x00,0x10,
0x20,0x90,0x90,0x90,0x90,0x68,0x00,0x28,0x50,0xb0,0xc8,0x88,0x88,0x88,0x00,0x28,
0x50,0x88,0xc8,0xa8,0x98,0x88,0x00,0x60,0x90,0x90,0x68,0x00,0xf8,0x00,0x00,0x60,
0x90,0x90,0x60,0x00,0xf0,0x00,0x00,0x20,0x00,0x20,0x40,0x80,0x88,0x70,0x00,0x00,
0x00,0x00,0xf8,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0xf8,0x08,0x08,0x00,0x00,0x84,
0x88,0x90,0xa8,0x54,0x84,0x08,0x1c,0x84,0x88,0x90,0xa8,0x58,0xa8,0x3c,0x08,0x20,
0x00,0x00,0x20,0x20,0x20,0x20,0x00,0x00,0x12,0x24,0x48,0x90,0x48,0x24,0x12,0x00,
0x90,0x48,0x24,0x12,0x24,0x48,0x90,0x28,0x50,0x20,0x50,0x88,0xf8,0x88,0x00,0x28,
0x50,0x70,0x08,0x78,0x88,0x78,0x00,0x28,0x50,0x00,0x70,0x20,0x20,0x70,0x00,0x28,
0x50,0x00,0x20,0x20,0x20,0x70,0x00,0x28,0x50,0x70,0x88,0x88,0x88,0x70,0x00,0x28,
0x50,0x70,0x88,0x88,0x88,0x70,0x00,0x28,0x50,0x00,0x88,0x88,0x88,0x70,0x00,0x28,
0x50,0x00,0x90,0x90,0x90,0x68,0x00,0xfc,0x48,0x48,0x48,0xe8,0x08,0x50,0x20,0x00,
0xa0,0x00,0xa0,0xa0,0xa0,0x20,0x40,0xc0,0x44,0xc8,0x54,0xec,0x54,0x9e,0x04,0x10,
0xa8,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x50,0x88,0x50,0x20,0x00,0x00,0xc4,
0xc8,0x10,0x20,0x40,0xb6,0x36,0x00,0x7c,0xa8,0xa8,0x68,0x28,0x28,0x28,0x00,0x38,
0x40,0x30,0x48,0x48,0x30,0x08,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xf0,
0xf0,0xf0,0xf0,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x3c,0x00,0x00,0x00,0xff,
0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x0f,
0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0x03,
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x11,
0x22,0x44,0x88,0x11,0x22,0x44,0x88,0x88,0x44,0x22,0x11,0x88,0x44,0x22,0x11,0xfe,
0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x38,0x7c,0xfe,0x80,
0xc0,0xe0,0xf0,0xe0,0xc0,0x80,0x00,0x01,0x03,0x07,0x0f,0x07,0x03,0x01,0x00,0xff,
0x7e,0x3c,0x18,0x18,0x3c,0x7e,0xff,0x81,0xc3,0xe7,0xff,0xff,0xe7,0xc3,0x81,0xf0,
0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x0f,
0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x33,
0x33,0xcc,0xcc,0x33,0x33,0xcc,0xcc,0x00,0x20,0x20,0x50,0x50,0x88,0xf8,0x00,0x20,
0x20,0x70,0x20,0x70,0x20,0x20,0x00,0x00,0x00,0x00,0x50,0x88,0xa8,0x50,0x00,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xf0,
0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x68,0x90,0x90,0x90,0x68,0x00,0x30,
0x48,0x48,0x70,0x48,0x48,0x70,0xc0,0xf8,0x88,0x80,0x80,0x80,0x80,0x80,0x00,0xf8,
0x50,0x50,0x50,0x50,0x50,0x98,0x00,0xf8,0x88,0x40,0x20,0x40,0x88,0xf8,0x00,0x00,
0x00,0x78,0x90,0x90,0x90,0x60,0x00,0x00,0x50,0x50,0x50,0x50,0x68,0x80,0x80,0x00,
0x50,0xa0,0x20,0x20,0x20,0x20,0x00,0xf8,0x20,0x70,0xa8,0xa8,0x70,0x20,0xf8,0x20,
0x50,0x88,0xf8,0x88,0x50,0x20,0x00,0x70,0x88,0x88,0x88,0x50,0x50,0xd8,0x00,0x30,
0x40,0x40,0x20,0x50,0x50,0x50,0x20,0x00,0x00,0x00,0x50,0xa8,0xa8,0x50,0x00,0x08,
0x70,0xa8,0xa8,0xa8,0x70,0x80,0x00,0x38,0x40,0x80,0xf8,0x80,0x40,0x38,0x00,0x70,
0x88,0x88,0x88,0x88,0x88,0x88,0x00,0x00,0x00,0xf0,0x00,0xf0,0x00,0xf0,0x00,0x20,
0x20,0xf8,0x20,0x20,0x00,0xf8,0x00,0xc0,0x30,0x08,0x30,0xc0,0x00,0xf8,0x00,0x18,
0x60,0x80,0x60,0x18,0x00,0xf8,0x00,0x10,0x28,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0xa0,0x40,0x00,0x20,0x00,0xf8,0x00,0x20,0x00,0x00,0x00,
0x50,0xa0,0x00,0x50,0xa0,0x00,0x00,0x00,0x18,0x24,0x24,0x18,0x00,0x00,0x00,0x00,
0x30,0x78,0x78,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3e,
0x20,0x20,0x20,0xa0,0x60,0x20,0x00,0xa0,0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x40,
0xa0,0x20,0x40,0xe0,0x00,0x00,0x00,0x00,0x38,0x38,0x38,0x38,0x38,0x38,0x00,0x50,
0x50,0x50,0x00,0x00,0xff,0xff,0xff,0x00 ];

CanvasBase.prototype._init = function() {
	this.size = 256 * 24;

	this.mask = new Array(this.size);
	this.attrs = new Array(this.size);
	this.dirty = new Array(32 * 24);
	this.changed = false;

	this.color = 15;
	this.back = 1;
	this.border = 1;
	
	this.palette = new Array(16);	
	this.setPalette(0,0,0,0);
	this.setPalette(1,0,0,0);
	this.setPalette(2,33,200,66);
	this.setPalette(3,94,220,120);
	this.setPalette(4,84,85,237);
	this.setPalette(5,125,118,252);
	this.setPalette(6,212,82,77);
	this.setPalette(7,66,235,245);
	this.setPalette(8,252,85,84);
	this.setPalette(9,255,121,120);
	this.setPalette(10,212,193,84);
	this.setPalette(11,230,206,128);
	this.setPalette(12,33,176,59);
	this.setPalette(13,201,91,186);
	this.setPalette(14,204,204,204);
	this.setPalette(15,255,255,255);
}

CanvasBase.prototype.appendTo = function(parent) {
	parent = parent || document.body;
	parent.appendChild(this.el);
}

CanvasBase.prototype.setMode = function(mode) {
	this.mode = mode;
	this.fillAttribute((this.color << 4) | this.back);
	this.fillMask(0);
	this.invalidate();
}

CanvasBase.prototype.setAttribute = function(addr, value) {
	this.attrs[addr] = value;
	this.dirty[addr >> 3] = true;
}

CanvasBase.prototype.setMask = function(addr, value) {
	this.mask[addr] = value;
	this.dirty[addr >> 3] = true;	
}

CanvasBase.prototype.invalidate = function() {
	var dirty = this.dirty;
	var len = dirty.length;
	for (var i = 0; i < len; i++)
		dirty[i] = true;
	this.changed = true;
}

CanvasBase.prototype.fillAttribute = function(value) {
	var attrs = this.attrs;
	var len = attrs.length;
	for (var i = 0; i < len; i++)
		attrs[i] = value;
}

CanvasBase.prototype.fillMask = function(value) {
	var mask = this.mask;
	var len = mask.length;
	for (var i = 0; i < len; i++)
		mask[i] = value;
}

CanvasBase.prototype.setColors = function(fg, bg, border) {
	fg = fg || this.color;
	bg = bg || this.back;
	border = border || this.border;
	this.color = fg;
	this.back = bg;
	this.border = border;
}

/*
	var bdy = y >> 3;
	var bmy = y & 7;
	var bl = bdy * 256 + bmy;
	var bdx = x & 248;
	var bmx = x & 7;
	var bit = 128 >> bmx;
*/
CanvasBase.prototype.pset = function(x, y) {
	var addr = (y >> 3) * 256 + (y & 7) + (x & 248); //bl + bdx;
	var c = this.color;
	var bg = this.attrs[addr] & 15;
	if (c == bg) {
		this.mask[addr] &= ~(128 >> (x & 7)); // bit
	} else {
		this.mask[addr] |= 128 >> (x & 7); // bit
		this.attrs[addr] = (c << 4) | bg;
	}
	this.dirty[addr >> 3] = true;
	this.changed = true;
}

CanvasBase.prototype.line = function(x1, y1, x2, y2) {
	var swap, error;
	var x = x1;
	var y = y1;
	var dx = Math.abs(x2 - x1);
	var dy = Math.abs(y2 - y1);
	var s1 = (x2 > x1) ? 1 : ((x2 < x1) ? -1 : 0);
	var s2 = (y2 > y1) ? 1 : ((y2 < y1) ? -1 : 0);	
	if (dy > dx) {
		var temp = dx;
		dx = dy;
		dy = temp;
		swap = true;
	}
	var d2x = 2*dx;	
	var d2y = 2*dy;
	error = d2y - dx;
	if (swap) {
		for (var i = 1; i <= dx; i++) {
			this.pset(x, y);
			while (error >= 0) {
				x += s1;
				error -= d2x;
			}
			y += s2;
			error += d2y;
		}	
	} else {
		for (var i = 1; i <= dx; i++) {
			this.pset(x, y);
			while (error >= 0) {
				y += s2;
				error -= d2x;
			}
			x += s1;
			error += d2y;
		}	
	}
}

CanvasBase.prototype.circle = function(x, y, rad) {
	//@todo: implement arcs, elipses
	var d = 3 - (2 * rad);
	var px = 0;
	var py = rad;
	for (;;) {
		this.pset(x + px, y + py);
		this.pset(x + px, y - py);
		this.pset(x - px, y + py);
		this.pset(x - px, y - py);
		this.pset(x + py, y + px);
		this.pset(x + py, y - px);
		this.pset(x - py, y + px);
		this.pset(x - py, y - px);
		if (px >= py)
			break;
		if (d < 0) {
			d += (4 * px) + 6;
		} else {
			--py;
			d += 4 * (px - py) + 10;
			
		}
		++px;
	}
}

CanvasBase.prototype.blitChar = function(x, y, asc) {
	var base = asc * 8;
	for (var i = base; i < base + 8; i++, y++) {
		var mask = this.charMap[i];
		if (mask & 128) this.pset(x + 0, y);
		if (mask &  64) this.pset(x + 1, y);
		if (mask &  32) this.pset(x + 2, y);
		if (mask &  16) this.pset(x + 3, y);
		if (mask &   8) this.pset(x + 4, y);
		if (mask &   4) this.pset(x + 5, y);
		if (mask &   2) this.pset(x + 6, y);
		if (mask &   1) this.pset(x + 7, y);
	}
}

// CanvasTbl - a canvas that uses HTML tables (bad performance)

function CanvasTbl() {}

CanvasTbl.prototype = new CanvasBase();

CanvasTbl.prototype.init = function() {
	this._init();
	this.cells = new Array(this.size);

	this.imgsrc = [];
	this.preload = [];
	for (var color=0; color<16; color++) {
		this.preload[color] = new Image();
		this.preload[color].src = 'pix/' + color + '.gif';
		this.imgsrc[color] = 'url("pix/' + color + '.gif")';
	}
	
	var table = document.createElement('table');
	for (var y=0; y<192; y++) {
		var row = table.insertRow(y);
		var cell;
		var bdy = y >> 3;
		var bmy = y & 7;
		var bl = bdy * 256 + bmy;
		for (var x=0; x<32; x++) {
			cell = row.insertCell(x);
			cell.className = 'byt';
			this.cells[bl + (x << 3)] = cell;
		}
	}
	
	table.setAttribute('cellspacing', 0);
	table.setAttribute('cellpadding', 0);	
	table.setAttribute('border', 0);
	table.style.backgroundColor = 'black';
	this.el = this.table = table;	
}

CanvasTbl.prototype.setPalette = function(color, r, g, b) {
	this.palette[color] = '#' + toHex(r) + toHex(g) + toHex(b);
}

CanvasTbl.prototype.update = function(from, to) {
	to = to || from + 1;
	while (from < to) {
		var attr = this.attrs[from];
		var mask = this.mask[from];
		var fg = attr >> 4;
		var bg = attr & 15;
		var cell = this.cells[from];
		cell.style.background = this.imgsrc[fg];
		cell.style.backgroundPosition = -(mask << 4);
		cell.style.backgroundColor = (bg) ? this.palette[bg] : '';
		from++;
	};
}

CanvasTbl.prototype.updateDirty = function() {
	if (!this.changed)
		return;
	var addr = 0;
	var dirty = this.dirty;
	var len = dirty.length;
	for (var i = 0; i < len; i++, addr += 8) {
		if (dirty[i]) {
			this.update(addr, addr + 8);
			dirty[i] = false;
		}
	}
}

// CanvasCtx - a canvas that uses HTML canvas element (fast)

function CanvasCtx() {}

CanvasCtx.prototype = new CanvasBase();

CanvasCtx.prototype.init = function() {
	this._init();
	
	var canvas = document.createElement('canvas');
	canvas.width = 256 * 2;
	canvas.height = 192 * 2;
	canvas.style.backgroundColor = 'black';
	this.canvasCtx = canvas.getContext("2d");	
	this.el = this.canvas = canvas;
}

CanvasCtx.prototype.setPalette = function(color, r, g, b) {
	this.palette[color] = 'rgb(' + [r,g,b].join(',') + ')';
}

CanvasCtx.prototype.update = function(blk) {
	var i = blk << 3;
	var e = i + 8; // end
	var cv = this.canvasCtx;
	var pal = this.palette;
	var fg, bg, attr, mask;
	var x = ((blk & 31) << 4);
	var y = ((blk >> 5) << 4);
	while (i < e) {
		attr = this.attrs[i];
		mask = this.mask[i];
		fg = attr >> 4;
		bg = attr & 15;
		cv.fillStyle = pal[bg];
		cv.fillRect(x, y, 16, 2);
		cv.fillStyle = pal[fg];
		if (mask & 128) cv.fillRect( x+0, y, 2, 2);
		if (mask &  64) cv.fillRect( x+2, y, 2, 2);
		if (mask &  32) cv.fillRect( x+4, y, 2, 2);
		if (mask &  16) cv.fillRect( x+6, y, 2, 2);
		if (mask &   8) cv.fillRect( x+8, y, 2, 2);
		if (mask &   4) cv.fillRect(x+10, y, 2, 2);
		if (mask &   2) cv.fillRect(x+12, y, 2, 2);
		if (mask &   1) cv.fillRect(x+14, y, 2, 2);
		y += 2;		
		++i;
	}
}

CanvasCtx.prototype.updateDirty = function() {
	if (!this.changed)
		return;
	var dirty = this.dirty;
	var len = dirty.length;
	for (var i = 0; i < len; i++) {
		if (dirty[i]) {
			this.update(i);
			dirty[i] = false;
		}
	}
}

// Canvas - best canvas available depending on your browser
var Canvas = CanvasTbl;
try {
	Canvas = (CanvasRenderingContext2D && typeof(CanvasRenderingContext2D) ==  
"function") ? CanvasCtx : CanvasTbl;
} catch(e) {}

