importScripts('software.js');

var video = new GameBoyAdvanceSoftwareRenderer();
var proxyBacking = null;
var currentFrame = 0;

self.finishDraw = function(pixelData) {
	self.postMessage({ type: 'finish', backing: pixelData, frame: currentFrame });
}

function receiveDirty(dirty) {
	for (var type in dirty) {
		switch (type) {
		case 'DISPCNT':
			video.writeDisplayControl(dirty[type]);
			break;
		case 'BGCNT':
			for (var i in dirty[type]) {
				if (typeof(dirty[type][i]) === 'number') {
					video.writeBackgroundControl(i, dirty[type][i]);
				}
			}
			break;
		case 'BGHOFS':
			for (var i in dirty[type]) {
				if (typeof(dirty[type][i]) === 'number') {
					video.writeBackgroundHOffset(i, dirty[type][i]);
				}
			}
			break;
		case 'BGVOFS':
			for (var i in dirty[type]) {
				if (typeof(dirty[type][i]) === 'number') {
					video.writeBackgroundVOffset(i, dirty[type][i]);
				}
			}
			break;
		case 'BGX':
			for (var i in dirty[type]) {
				if (typeof(dirty[type][i]) === 'number') {
					video.writeBackgroundRefX(i, dirty[type][i]);
				}
			}
			break;
		case 'BGY':
			for (var i in dirty[type]) {
				if (typeof(dirty[type][i]) === 'number') {
					video.writeBackgroundRefY(i, dirty[type][i]);
				}
			}
			break;
		case 'BGPA':
			for (var i in dirty[type]) {
				if (typeof(dirty[type][i]) === 'number') {
					video.writeBackgroundParamA(i, dirty[type][i]);
				}
			}
			break;
		case 'BGPB':
			for (var i in dirty[type]) {
				if (typeof(dirty[type][i]) === 'number') {
					video.writeBackgroundParamB(i, dirty[type][i]);
				}
			}
			break;
		case 'BGPC':
			for (var i in dirty[type]) {
				if (typeof(dirty[type][i]) === 'number') {
					video.writeBackgroundParamC(i, dirty[type][i]);
				}
			}
			break;
		case 'BGPD':
			for (var i in dirty[type]) {
				if (typeof(dirty[type][i]) === 'number') {
					video.writeBackgroundParamD(i, dirty[type][i]);
				}
			}
			break;
		case 'WIN0H':
			video.writeWin0H(dirty[type]);
			break;
		case 'WIN1H':
			video.writeWin1H(dirty[type]);
			break;
		case 'WIN0V':
			video.writeWin0V(dirty[type]);
			break;
		case 'WIN1V':
			video.writeWin1V(dirty[type]);
			break;
		case 'WININ':
			video.writeWinIn(dirty[type]);
			break;
		case 'WINOUT':
			video.writeWinOut(dirty[type]);
			break;
		case 'BLDCNT':
			video.writeBlendControl(dirty[type]);
			break;
		case 'BLDALPHA':
			video.writeBlendAlpha(dirty[type]);
			break;
		case 'BLDY':
			video.writeBlendY(dirty[type]);
			break;
		case 'MOSAIC':
			video.writeMosaic(dirty[type]);
			break;
		case 'memory':
			receiveMemory(dirty.memory);
			break;
		}
	}
}

function receiveMemory(memory) {
	if (memory.palette) {
		video.palette.overwrite(new Uint16Array(memory.palette));
	}
	if (memory.oam) {
		video.oam.overwrite(new Uint16Array(memory.oam));
	}
	if (memory.vram) {
		for (var i = 0; i < 12; ++i) {
			if (memory.vram[i]) {
				video.vram.insert(i << 12, new Uint16Array(memory.vram[i]));
			}
		}
	}
}

var handlers = {
	clear: function(data) {
		video.clear(data);
	},

	scanline: function(data) {
		receiveDirty(data.dirty);
		video.drawScanline(data.y, proxyBacking);
	},

	start: function(data) {
		proxyBacking = data.backing;
		video.setBacking(data.backing);
	},

	finish: function(data) {
		currentFrame = data.frame;
		var scanline = 0;
		for (var i = 0; i < data.scanlines.length; ++i) {
			for (var y = scanline; y < data.scanlines[i].y; ++y) {
				video.drawScanline(y, proxyBacking);
			}
			scanline = data.scanlines[i].y;
			receiveDirty(data.scanlines[i].dirty);
			video.drawScanline(scanline, proxyBacking);
		}
		for (var y = scanline; y < 160; ++y) {
			video.drawScanline(y, proxyBacking);
		}
		video.finishDraw(self);
	},
};

self.onmessage = function(message) {
	handlers[message.data['type']](message.data);
};

@@ -3478,16 +3478,20 @@
 nes%5Bi%5D.y
+ + 1
 ;%0A%09%09%09rec
@@ -3548,24 +3548,29 @@
 canline(
+data.
 scanline
 , proxyB
@@ -3561,16 +3561,22 @@
 scanline
+s%5Bi%5D.y
 , proxyB

Fix off-by-one in scanline batching