1 //WebCPUは、このディレクトリにあるファイルで完結している。
3 function WebCPU_Exception(errno, infoArray){
5 this.infoArray = infoArray;
7 WebCPU_Exception.prototype = {
13 getMessageString: function(){
15 s += "Error" + this.errno.toString().toUpperCase();
16 if(this.errno < 0 || this.errorMessageList.length <= this.errno){
19 s += ":" + this.errorMessageList[this.errno] + "\n";
22 s += " >" + this.infoArray.toString() + "\n";
27 // throw new WebCPU_Exception(2, [""]);
31 this.API = new WebCPU_API();
34 this.debugMessageText = null;
35 this.debugIntegerRegisterText = null;
36 this.debugPointerRegisterText = null;
38 this.debugMessageBuffer = "";
40 this.messageTimer = null;
43 //メモリはラベルごとにページとして区切られ、それらの配列が一つのメモリとなる。
44 this.mainMemory = new WebCPU_Memory();
45 this.memoryPageCounter = 0;
46 this.memoryInstructionCounter = 0;
47 this.stopFlag = false;
50 this.registers = new Object;
51 this.registers.Integer = new Array(64);
57 this.instruction = new Array(0xFF + 1);
59 for(var i = 0; i < this.instruction.length; i++){
60 this.instruction[i] = WebCPU_Instruction_Undefined;
63 this.instruction[0x00] = WebCPU_Instruction_NOP;
64 this.instruction[0x01] = WebCPU_Instruction_LB;
65 this.instruction[0x02] = WebCPU_Instruction_LIMM;
66 this.instruction[0x03] = WebCPU_Instruction_PLIMM;
67 this.instruction[0x04] = WebCPU_Instruction_CND;
68 this.instruction[0x08] = WebCPU_Instruction_LMEM;
69 this.instruction[0x09] = WebCPU_Instruction_SMEM;
70 this.instruction[0x0E] = WebCPU_Instruction_PADD;
71 this.instruction[0x0F] = WebCPU_Instruction_PDIF;
73 this.instruction[0x10] = WebCPU_Instruction_TernaryOperation;
74 this.instruction[0x11] = WebCPU_Instruction_TernaryOperation;
75 this.instruction[0x12] = WebCPU_Instruction_TernaryOperation;
76 this.instruction[0x14] = WebCPU_Instruction_TernaryOperation;
77 this.instruction[0x15] = WebCPU_Instruction_TernaryOperation;
78 this.instruction[0x16] = WebCPU_Instruction_TernaryOperation;
79 this.instruction[0x18] = WebCPU_Instruction_TernaryOperation;
80 this.instruction[0x19] = WebCPU_Instruction_TernaryOperation;
81 this.instruction[0x1A] = WebCPU_Instruction_TernaryOperation;
82 this.instruction[0x1B] = WebCPU_Instruction_TernaryOperation;
84 this.instruction[0x1E] = WebCPU_Instruction_PCP;
86 this.instruction[0x20] = WebCPU_Instruction_TernaryOperation;
87 this.instruction[0x21] = WebCPU_Instruction_TernaryOperation;
88 this.instruction[0x22] = WebCPU_Instruction_TernaryOperation;
89 this.instruction[0x23] = WebCPU_Instruction_TernaryOperation;
90 this.instruction[0x24] = WebCPU_Instruction_TernaryOperation;
91 this.instruction[0x25] = WebCPU_Instruction_TernaryOperation;
92 this.instruction[0x26] = WebCPU_Instruction_TernaryOperation;
93 this.instruction[0x27] = WebCPU_Instruction_TernaryOperation;
95 this.instruction[0x28] = WebCPU_Instruction_TernaryOperation;
96 this.instruction[0x29] = WebCPU_Instruction_TernaryOperation;
97 this.instruction[0x2A] = WebCPU_Instruction_TernaryOperation;
98 this.instruction[0x2B] = WebCPU_Instruction_TernaryOperation;
99 this.instruction[0x2C] = WebCPU_Instruction_TernaryOperation;
100 this.instruction[0x2D] = WebCPU_Instruction_TernaryOperation;
102 this.instruction[0x32] = WebCPU_Instruction_MALLOC;
103 this.instruction[0x34] = WebCPU_Instruction_DATA;
105 this.instruction[0xFE] = WebCPU_Instruction_REMARK;
107 this.message("<<< Initialized >>>\n");
110 loadBinaryText: function(binStr){
111 //引数はフロントエンドコードのHex文字列も可能。(Not implemented.)
114 binStr = replaceAll(binStr, " ", "");
115 binStr = replaceAll(binStr, "\n", "");
116 binStr = replaceAll(binStr, "\r", "");
117 binStr = replaceAll(binStr, "\t", "");
118 binStr = binStr.toUpperCase();
119 if(binStr.substr(0, 4) == "05E1"){
121 this.message("LoadBinaryText:OSECPU signature found.\n", 10);
122 if(binStr.substr(4, 2) == "00"){
124 this.message("LoadBinaryText:This is BackEndCode.\n", 10);
125 binStr = binStr.substr(6);
128 this.message("LoadBinaryText:This is FrontEndCode.\n", 10);
129 binStr = binStr.substr(4);
130 this.loadFrontEndBinaryText(binStr);
134 this.message("LoadBinaryText:OSECPU signature NOT found.\n", 1);
136 this.loadBackEndBinaryText(binStr);
138 loadBackEndBinaryText: function(binStr){
139 //引数はバックエンドコードのHex文字列表現でスペースなどの他の文字の混入は認められない。
140 this.message("****LoadBackEndBinaryText****\n", 30);
141 this.mainMemory.initMemory();
142 this.memoryPageCounter = 0;
143 this.memoryInstructionCounter = 0;
146 for(var i = 0; i < binStr.length; ){
148 var id = parseInt(binStr.substr(i, 2), 16);
150 var instr = new this.instruction[id](id);
151 instr.binOffset = (i >> 1) - 1;
152 var instrarglen = instr.loadArguments(binStr, i);
153 if(isNaN(parseInt(instrarglen))){
154 throw new WebCPU_Exception(1, ["Invalid instrarglen."]);
156 //instrarglenをバイト単位から文字列単位に変換し、さらに引数部分のみのサイズに補正する。
157 instrarglen = (instrarglen - 1) * 2;
159 if(instrarglen > binStr.length - i){
160 //オペランドの長さ分だけのバイナリがなかった、つまり不完全な状態で途切れている
161 throw new WebCPU_Exception(1, ["Invalid instrarglen."]);
166 //ラベル命令だったのでメモリページを新たにする。
167 this.mainMemory.addMemoryPage(new WebCPU_MemoryPage());
168 this.memoryPageCounter++;
169 this.memoryInstructionCounter = 0;
172 this.mainMemory.root[this.memoryPageCounter].addMemoryData(instr);
175 this.message("****LoadBackEndBinaryText Abort:\n", 1);
176 if(e instanceof WebCPU_Exception){
177 this.message(e.getMessageString(), 1);
181 this.showDisassembledCode();
182 this.mainMemory.initMemory();
183 this.memoryPageCounter = 0;
184 this.memoryInstructionCounter = 0;
186 this.showDisassembledCode();
187 this.message("****LoadBackEndBinaryText End****\n", 30);
189 this.memoryPageCounter = 0;
190 this.memoryInstructionCounter = 0;
193 loadFrontEndBinaryText: function(binStr){
194 //引数はフロントエンドコードのHex文字列表現でスペースなどの他の文字の混入は認められない。
195 this.message("****LoadFrontEndBinaryText****\n", 40);
196 this.mainMemory.initMemory();
197 this.memoryPageCounter = 0;
198 this.memoryInstructionCounter = 0;
199 //フロントエンドコードデコーダーの読み込み
200 this.loadBinaryText(decoderBinaryString);
202 var backendMaxSize = 65535;
203 var temp0MaxSize = 2 * 1024 * 1024;
204 var temp1MaxSize = 16 * 1024;
205 var temp2MaxSize = 64;
206 var temp3MaxSize = 4 * 1024 + 1;
207 var temp4MaxSize = 256;
209 //P02 = T_UINT8: &backend[2] (出力バッファ先頭)
210 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT8, backendMaxSize);
211 this.registers.Pointer[0x02] = new WebCPU_Pointer(m);
212 //P03 = T_UINT8: &backend[backend-maxsize] (出力バッファの限界)
213 this.registers.Pointer[0x03] = this.registers.Pointer[0x02].getCopy();
214 this.registers.Pointer[0x03].addressOffset = backendMaxSize;
215 //P04 = T_UINT8: &frontend[2] (入力データ先頭)
216 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT8);
217 this.registers.Pointer[0x04] = new WebCPU_Pointer(m);
218 this.registers.Pointer[0x04].addressOffset = 0;
219 this.registers.Pointer[0x04].memoryPage.data[1].data = new Array();
220 var a = this.registers.Pointer[0x04].memoryPage.data[1].data;
222 for(i = 0; i * 2 < binStr.length; i++){
223 a.push(parseInt(binStr.substr(i * 2, 2), 16));
225 //P05 = T_UINT8: &frontend[frontend-size] (入力データの終端:最終バイトの次のアドレス)
226 this.registers.Pointer[0x05] = this.registers.Pointer[0x04].getCopy();
227 this.registers.Pointer[0x05].addressOffset = i;
228 //P06 = T_UINT8: &temp0[0] (要素数が2M以上のテンポラリバッファ)
229 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT8, temp0MaxSize);
230 this.registers.Pointer[0x06] = new WebCPU_Pointer(m);
231 //P07 = T_UINT8: &temp0[temp-maxsize]
232 this.registers.Pointer[0x07] = this.registers.Pointer[0x06].getCopy();
233 this.registers.Pointer[0x07].addressOffset = temp0MaxSize;
234 //P0A = T_UINT32: &temp1[0] (要素数が16Kくらいあれば十分なバッファ)
235 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT32, temp1MaxSize);
236 this.registers.Pointer[0x0A] = new WebCPU_Pointer(m);
237 //P0B = T_SINT32: &temp2[0] (要素数が64のバッファ:Pxxレジスタの個数)
238 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.SINT32, temp2MaxSize);
239 this.registers.Pointer[0x0B] = new WebCPU_Pointer(m);
240 //P0C = T_SINT32: &temp3[0] (要素数が4Kのバッファ:登録可能ラベル数)
241 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.SINT32, temp3MaxSize);
242 this.registers.Pointer[0x0C] = new WebCPU_Pointer(m);
243 //P0D = T_UINT8: &temp4[0] (要素数が256のバッファ)
244 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT8, temp4MaxSize);
245 this.registers.Pointer[0x0D] = new WebCPU_Pointer(m);
249 P02 == バックエンドコードの終端, つまり P02 - &backend[0] = バックエンドコードのサイズ
251 if(this.registers.Integer[0x00] == 0){
253 var binData = this.registers.Pointer[0x02].memoryPage.data[1];
255 for(var i = 0; i < this.registers.Pointer[0x02].addressOffset; i++){
256 binStr += ("00" + binData.data[i].toString(16)).slice(-2);
258 this.message("[" + binStr + "]\n");
259 this.loadBackEndBinaryText(binStr);
263 this.message("loadFrontEndBinaryText: Translate failed.\n");
266 this.message("****LoadFrontEndBinaryText End****\n", 40);
268 executeStepIn: function(){
271 var retv = this.executeStepIn_Internal(true);
272 this.API.API_flushWin(this, this.API.mainWindowCanvas.width, this.API.mainWindowCanvas.height, 0, 0);
275 executeStepIn_Internal: function(isManualStepIn){
277 //終端到達時は1を、まだ後続命令がある場合は0を返す。
280 this.message(">stepIn:Break.\n", 2);
281 this.stopFlag = false;
282 this.API.API_flushWin(this, this.API.mainWindowCanvas.width, this.API.mainWindowCanvas.height, 0, 0);
285 var instr = this.fetchMemoryNext();
286 if(instr === undefined){
287 this.message(">stepIn:control reached end of binary.\n", 2);
288 this.API.API_flushWin(this, this.API.mainWindowCanvas.width, this.API.mainWindowCanvas.height, 0, 0);
292 this.message(">stepIn:" + this.memoryPageCounter + "-" + (this.memoryInstructionCounter - 1) + ":" + instr.toString() + "\n", 20);
295 //これ以降this.memoryInstructionCounterが今実行した命令を指すとは限らない。(JMP系命令のため)
298 if(this.memoryPageCounter == 233 && this.memoryInstructionCounter == 0){
299 this.message(">stepIn:Breakpoint.\n", 2);
308 if(this.executeStepIn_Internal(false) != 0){
314 //実行環境をリセットする。メモリ内容は保持される。
315 for(var i = 0; i < this.registers.Integer.length; i++){
316 this.registers.Integer[i] = 0;
318 this.registers.Pointer = new Array(64);
319 for(var i = 0; i < this.registers.Pointer.length; i++){
320 this.registers.Pointer[i] = new WebCPU_Pointer(null);
322 //Set P28 for API calling.
323 this.registers.Pointer[0x28].memoryType = 0xC0FFEE;
325 this.memoryPageCounter = 0;
326 this.memoryInstructionCounter = 0;
327 this.stopFlag = false;
329 if(this.API.mainWindowCanvas){
330 this.API.setMainWindowCanvasDOMObject(this.API.mainWindowCanvas);
332 this.message("<<< Reset >>>\n");
334 fetchMemoryNext: function(){
335 if(this.mainMemory.root[this.memoryPageCounter].data.length <= this.memoryInstructionCounter){
336 this.memoryPageCounter++;
337 if(this.mainMemory.root.length <= this.memoryPageCounter){
338 this.memoryPageCounter--;
341 this.memoryInstructionCounter -= this.mainMemory.root[this.memoryPageCounter - 1].data.length;
343 var retv = this.mainMemory.root[this.memoryPageCounter].data[this.memoryInstructionCounter];
344 this.memoryInstructionCounter++;
347 setMainWindowCanvasDOMObject: function(id){
348 this.API.setMainWindowCanvasDOMObject(document.getElementById(id));
350 setDebugMessageDOMObject: function(name){
352 this.debugMessageText = document.getElementsByName(name)[0];
353 if(!this.debugMessageText){
354 this.debugMessageText = document.getElementById(name);
357 this.debugMessageText = null;
359 this.setDebugTimer();
361 setDebugIntegerRegisterDOMObject: function(name){
363 this.debugIntegerRegisterText = document.getElementsByName(name)[0];
364 if(!this.debugIntegerRegisterText){
365 this.debugIntegerRegisterText = document.getElementById(name);
368 this.debugIntegerRegisterText = null;
370 this.setDebugTimer();
372 setDebugPointerRegisterDOMObject: function(name){
373 this.debugPointerRegisterText = document.getElementsByName(name)[0];
375 this.debugPointerRegisterText = document.getElementsByName(name)[0];
376 if(!this.debugPointerRegisterText){
377 this.debugPointerRegisterText = document.getElementById(name);
380 this.debugPointerRegisterText = null;
382 this.setDebugTimer();
384 setDebugTimer: function(){
385 if(!this.debugMessageText && !this.debugIntegerRegisterText && !this.debugPointerRegisterText){
386 //すべて無効だったらタイマーの動作自体を止める
387 window.clearTimeout(this.messageTimer);
388 this.messageTimer = null;
389 WebCPU_Instruction.prototype.isEnabledPrintSourceRegister = false;
390 WebCPU_Instruction.prototype.isEnabledPrintDestinationRegister = false;
391 } else if(!this.messageTimer){
392 //どれかが有効でかつタイマーが止まっていたらスタートさせる
394 this.messageTimer = window.setInterval(function(){that.debugShowTick();}, 50);
395 WebCPU_Instruction.prototype.isEnabledPrintSourceRegister = true;
396 WebCPU_Instruction.prototype.isEnabledPrintDestinationRegister = true;
399 debugShowTick: function(){
400 if(this.debugMessageText && this.debugMessageBuffer != ""){
401 var str = this.debugMessageText.innerHTML + this.debugMessageBuffer;
402 this.debugMessageBuffer = "";
403 if(str.length > WebCPU.maxDebugStringLength){
404 str = str.slice(str.length - (WebCPU.maxDebugStringLength >> 1));
406 this.debugMessageText.innerHTML = str;
407 this.debugMessageText.scrollTop = this.debugMessageText.scrollHeight;
409 this.refreshDebugIntegerRegisterText();
410 this.refreshDebugPointerRegisterText();
412 message: function(str, id){
419 //30:バックエンドバイナリ読み込みデバッグ
420 //40:フロントエンドバイナリ読み込みデバッグ
421 if(this.debugMessageText != null){
428 this.debugMessageBuffer += str;
431 //エラーのときmessageが無効であればalertで表示する。
435 showDisassembledCode: function(){
436 for(var i = 0; i < this.mainMemory.root.length; i++){
437 for(var j = 0; j < this.mainMemory.root[i].data.length; j++){
438 var instr = this.mainMemory.root[i].data[j];
439 this.message("+0x" + instr.binOffset.toString(16).toUpperCase() + ":" + i + "-" + j + ":" + instr.toString() + "\n", 30);
441 this.message(" - - - - \n", 30);
444 refreshDebugIntegerRegisterText: function(){
445 if(this.debugIntegerRegisterText != null){
446 this.debugIntegerRegisterText.innerHTML = "";
447 for(var i = 0; i < this.registers.Integer.length; i++){
448 this.debugIntegerRegisterText.innerHTML += "R" + ("00" + i.toString(16)).slice(-2).toUpperCase() + ":0x" + this.registers.Integer[i].toString(16).toUpperCase() + "\n";
452 refreshDebugPointerRegisterText: function(){
453 if(this.debugPointerRegisterText != null){
454 this.debugPointerRegisterText.innerHTML = "";
455 for(var i = 0; i < this.registers.Pointer.length; i++){
456 if(this.registers.Pointer[i]){
457 this.debugPointerRegisterText.innerHTML += "P" + ("00" + i.toString(16)).slice(-2).toUpperCase() + ":" + this.registers.Pointer[i].toString() + "\n";
459 this.debugPointerRegisterText.innerHTML += "P" + ("00" + i.toString(16)).slice(-2).toUpperCase() + ":(null)\n";
464 goToPointerRegister: function(reg0P){
467 var p = this.registers.Pointer[reg0P];
468 if(p.memoryType == 1){
470 this.memoryPageCounter = this.mainMemory.getMemoryPageCountFromMemoryPageInstance(p.memoryPage);
471 if(this.memoryPageCounter !== undefined){
472 this.memoryInstructionCounter = 0;
473 this.message("JMP:page " + this.memoryPageCounter + "\n", 20);
475 throw new WebCPU_Exception(2, ["memoryPage not found in mainMemory."]);
478 throw new WebCPU_Exception(2, ["Can't goto Memory page type is not VPtr."]);
481 createBackendBinaryString: function(){
487 instr = this.fetchMemoryNext();
488 if(instr === undefined){
491 s += instr.createBinaryString(this);
495 staticOptimize: function(){
498 //一つのメモリページにラベル命令のみがある場合は、それを次のメモリページのラベル名に置換する
500 var removePageIndexStack = new Array();
501 var labelIDStack = new Array();
504 for(var i = 0; i < this.mainMemory.root.length; i++){
505 mpage = this.mainMemory.root[i];
506 if(mpage instanceof WebCPU_MemoryPage && mpage.data[0] instanceof WebCPU_Instruction_LB){
508 if(mpage.data.length == 1){
510 labelIDStack.push(mpage.data[0].imm32);
511 removePageIndexStack.push(i);
514 if(labelIDStack.length != 0){
516 labelID = labelIDStack.pop();
517 if(labelID == undefined){
520 this.mainMemory.root.splice(removePageIndexStack[0], 1);
522 this.staticOptimize_ReplaceLabelNumber(labelID, mpage.data[0].imm32);
524 removePageIndexStack = new Array();
529 if(labelIDStack.length != 0){
530 //プログラム末尾のラベル命令は削除しないようにする
531 newLabelID = labelIDStack.pop();
533 labelID = labelIDStack.pop();
534 if(labelID == undefined){
537 this.mainMemory.root.splice(removePageIndexStack[0], 1);
539 this.staticOptimize_ReplaceLabelNumber(labelID, newLabelID);
543 staticOptimize_ReplaceLabelNumber: function(from, to){
546 for(var i = 0, iLen = this.mainMemory.root.length; i < iLen; i++){
547 mpage = this.mainMemory.root[i];
548 for(var j = 0, jLen = mpage.data.length; j < jLen; j++){
549 instr = mpage.data[j];
550 if(instr instanceof WebCPU_Instruction_PLIMM){
551 if(instr.imm32 == from){
561 function parseSignedInt32(hexStr){
562 //文字列を16進32bit符号あり整数として変換する。
563 var i = parseInt(hexStr, 16);
572 function parseSignedInt(hexStr, bits){
573 //文字列を16進符号あり整数として変換する。
575 var i = parseInt(hexStr, 16);
576 if(i >= (1 << (bits - 1))){
578 return -(((0xffffffff >>> (32 - bits)) - i) + 1);
584 function toHexString8(v){
585 return ("00" + v.toString(16).toUpperCase()).slice(-2);
588 function toHexString32(v){
592 return ("00000000" + v.toString(16).toUpperCase()).slice(-8);
595 function replaceAll(str, org, dest){
596 //文字列str中にある文字列orgを文字列destにすべて置換する。
597 //http://www.syboos.jp/webjs/doc/string-replace-and-replaceall.html
598 return str.split(org).join(dest);