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;
192 loadFrontEndBinaryText: function(binStr){
193 //引数はフロントエンドコードのHex文字列表現でスペースなどの他の文字の混入は認められない。
194 this.message("****LoadFrontEndBinaryText****\n", 40);
195 this.mainMemory.initMemory();
196 this.memoryPageCounter = 0;
197 this.memoryInstructionCounter = 0;
198 //フロントエンドコードデコーダーの読み込み
199 this.loadBinaryText(decoderBinaryString);
201 var backendMaxSize = 65535;
202 var temp0MaxSize = 2 * 1024 * 1024;
203 var temp1MaxSize = 16 * 1024;
204 var temp2MaxSize = 64;
205 var temp3MaxSize = 4 * 1024 + 1;
206 var temp4MaxSize = 256;
208 //P02 = T_UINT8: &backend[2] (出力バッファ先頭)
209 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT8, backendMaxSize);
210 this.registers.Pointer[0x02] = new WebCPU_Pointer(m);
211 //P03 = T_UINT8: &backend[backend-maxsize] (出力バッファの限界)
212 this.registers.Pointer[0x03] = this.registers.Pointer[0x02].getCopy();
213 this.registers.Pointer[0x03].addressOffset = backendMaxSize;
214 //P04 = T_UINT8: &frontend[2] (入力データ先頭)
215 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT8);
216 this.registers.Pointer[0x04] = new WebCPU_Pointer(m);
217 this.registers.Pointer[0x04].addressOffset = 0;
218 this.registers.Pointer[0x04].memoryPage.data[1].data = new Array();
219 var a = this.registers.Pointer[0x04].memoryPage.data[1].data;
221 for(i = 0; i * 2 < binStr.length; i++){
222 a.push(parseInt(binStr.substr(i * 2, 2), 16));
224 //P05 = T_UINT8: &frontend[frontend-size] (入力データの終端:最終バイトの次のアドレス)
225 this.registers.Pointer[0x05] = this.registers.Pointer[0x04].getCopy();
226 this.registers.Pointer[0x05].addressOffset = i;
227 //P06 = T_UINT8: &temp0[0] (要素数が2M以上のテンポラリバッファ)
228 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT8, temp0MaxSize);
229 this.registers.Pointer[0x06] = new WebCPU_Pointer(m);
230 //P07 = T_UINT8: &temp0[temp-maxsize]
231 this.registers.Pointer[0x07] = this.registers.Pointer[0x06].getCopy();
232 this.registers.Pointer[0x07].addressOffset = temp0MaxSize;
233 //P0A = T_UINT32: &temp1[0] (要素数が16Kくらいあれば十分なバッファ)
234 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT32, temp1MaxSize);
235 this.registers.Pointer[0x0A] = new WebCPU_Pointer(m);
236 //P0B = T_SINT32: &temp2[0] (要素数が64のバッファ:Pxxレジスタの個数)
237 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.SINT32, temp2MaxSize);
238 this.registers.Pointer[0x0B] = new WebCPU_Pointer(m);
239 //P0C = T_SINT32: &temp3[0] (要素数が4Kのバッファ:登録可能ラベル数)
240 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.SINT32, temp3MaxSize);
241 this.registers.Pointer[0x0C] = new WebCPU_Pointer(m);
242 //P0D = T_UINT8: &temp4[0] (要素数が256のバッファ)
243 m = this.mainMemory.allocateMemoryPage(WebCPU.pType.UINT8, temp4MaxSize);
244 this.registers.Pointer[0x0D] = new WebCPU_Pointer(m);
248 P02 == バックエンドコードの終端, つまり P02 - &backend[0] = バックエンドコードのサイズ
250 if(this.registers.Integer[0x00] == 0){
252 var binData = this.registers.Pointer[0x02].memoryPage.data[1];
254 for(var i = 0; i < this.registers.Pointer[0x02].addressOffset; i++){
255 binStr += ("00" + binData.data[i].toString(16)).slice(-2);
257 this.message("[" + binStr + "]\n");
258 this.loadBackEndBinaryText(binStr);
262 this.message("loadFrontEndBinaryText: Translate failed.\n");
265 this.message("****LoadFrontEndBinaryText End****\n", 40);
267 executeStepIn: function(){
270 var retv = this.executeStepIn_Internal(true);
271 this.API.API_flushWin(this, this.API.mainWindowCanvas.width, this.API.mainWindowCanvas.height, 0, 0);
274 executeStepIn_Internal: function(isManualStepIn){
276 //終端到達時は1を、まだ後続命令がある場合は0を返す。
279 this.message(">stepIn:Break.\n", 2);
280 this.stopFlag = false;
281 this.API.API_flushWin(this, this.API.mainWindowCanvas.width, this.API.mainWindowCanvas.height, 0, 0);
284 var instr = this.fetchMemoryNext();
285 if(instr === undefined){
286 this.message(">stepIn:control reached end of binary.\n", 2);
287 this.API.API_flushWin(this, this.API.mainWindowCanvas.width, this.API.mainWindowCanvas.height, 0, 0);
291 this.message(">stepIn:" + this.memoryPageCounter + "-" + (this.memoryInstructionCounter - 1) + ":" + instr.toString() + "\n", 20);
294 //これ以降this.memoryInstructionCounterが今実行した命令を指すとは限らない。(JMP系命令のため)
297 if(this.memoryPageCounter == 233 && this.memoryInstructionCounter == 0){
298 this.message(">stepIn:Breakpoint.\n", 2);
307 if(this.executeStepIn_Internal(false) != 0){
313 //実行環境をリセットする。メモリ内容は保持される。
314 for(var i = 0; i < this.registers.Integer.length; i++){
315 this.registers.Integer[i] = 0;
317 this.registers.Pointer = new Array(64);
318 for(var i = 0; i < this.registers.Pointer.length; i++){
319 this.registers.Pointer[i] = new WebCPU_Pointer(null);
321 //Set P28 for API calling.
322 this.registers.Pointer[0x28].memoryType = 0xC0FFEE;
324 this.memoryPageCounter = 0;
325 this.memoryInstructionCounter = 0;
326 this.stopFlag = false;
328 this.message("<<< Reset >>>\n");
330 fetchMemoryNext: function(){
331 if(this.mainMemory.root[this.memoryPageCounter].data.length <= this.memoryInstructionCounter){
332 this.memoryPageCounter++;
333 if(this.mainMemory.root.length <= this.memoryPageCounter){
334 this.memoryPageCounter--;
337 this.memoryInstructionCounter -= this.mainMemory.root[this.memoryPageCounter - 1].data.length;
339 var retv = this.mainMemory.root[this.memoryPageCounter].data[this.memoryInstructionCounter];
340 this.memoryInstructionCounter++;
343 setMainWindowCanvasDOMObject: function(id){
344 this.API.setMainWindowCanvasDOMObject(document.getElementById(id));
346 setDebugMessageDOMObject: function(name){
347 this.debugMessageText = document.getElementsByName(name)[0];
348 this.setDebugTimer();
350 setDebugIntegerRegisterDOMObject: function(name){
351 this.debugIntegerRegisterText = document.getElementsByName(name)[0];
352 this.setDebugTimer();
354 setDebugPointerRegisterDOMObject: function(name){
355 this.debugPointerRegisterText = document.getElementsByName(name)[0];
356 this.setDebugTimer();
358 setDebugTimer: function(){
359 if(!this.debugMessageText && !this.debugIntegerRegisterText && !this.debugPointerRegisterText){
360 //すべて無効だったらタイマーの動作自体を止める
361 window.clearTimeout(this.messageTimer);
362 this.messageTimer = null;
363 WebCPU_Instruction.prototype.isEnabledPrintSourceRegister = false;
364 WebCPU_Instruction.prototype.isEnabledPrintDestinationRegister = false;
365 } else if(!this.messageTimer){
366 //どれかが有効でかつタイマーが止まっていたらスタートさせる
368 this.messageTimer = window.setInterval(function(){that.debugShowTick();}, 50);
369 WebCPU_Instruction.prototype.isEnabledPrintSourceRegister = true;
370 WebCPU_Instruction.prototype.isEnabledPrintDestinationRegister = true;
373 debugShowTick: function(){
374 if(this.debugMessageText && this.debugMessageBuffer != ""){
375 var str = this.debugMessageText.innerHTML + this.debugMessageBuffer;
376 this.debugMessageBuffer = "";
377 if(str.length > WebCPU.maxDebugStringLength){
378 str = str.slice(str.length - (WebCPU.maxDebugStringLength >> 1));
380 this.debugMessageText.innerHTML = str;
381 this.debugMessageText.scrollTop = this.debugMessageText.scrollHeight;
383 this.refreshDebugIntegerRegisterText();
384 this.refreshDebugPointerRegisterText();
386 message: function(str, id){
393 //30:バックエンドバイナリ読み込みデバッグ
394 //40:フロントエンドバイナリ読み込みデバッグ
395 if(this.debugMessageText != null){
402 this.debugMessageBuffer += str;
405 //エラーのときmessageが無効であればalertで表示する。
409 showDisassembledCode: function(){
410 for(var i = 0; i < this.mainMemory.root.length; i++){
411 for(var j = 0; j < this.mainMemory.root[i].data.length; j++){
412 var instr = this.mainMemory.root[i].data[j];
413 this.message("+0x" + instr.binOffset.toString(16).toUpperCase() + ":" + i + "-" + j + ":" + instr.toString() + "\n", 30);
415 this.message(" - - - - \n", 30);
418 refreshDebugIntegerRegisterText: function(){
419 if(this.debugIntegerRegisterText != null){
420 this.debugIntegerRegisterText.innerHTML = "";
421 for(var i = 0; i < this.registers.Integer.length; i++){
422 this.debugIntegerRegisterText.innerHTML += "R" + ("00" + i.toString(16)).slice(-2).toUpperCase() + ":0x" + this.registers.Integer[i].toString(16).toUpperCase() + "\n";
426 refreshDebugPointerRegisterText: function(){
427 if(this.debugPointerRegisterText != null){
428 this.debugPointerRegisterText.innerHTML = "";
429 for(var i = 0; i < this.registers.Pointer.length; i++){
430 if(this.registers.Pointer[i]){
431 this.debugPointerRegisterText.innerHTML += "P" + ("00" + i.toString(16)).slice(-2).toUpperCase() + ":" + this.registers.Pointer[i].toString() + "\n";
433 this.debugPointerRegisterText.innerHTML += "P" + ("00" + i.toString(16)).slice(-2).toUpperCase() + ":(null)\n";
438 goToPointerRegister: function(reg0P){
441 var p = this.registers.Pointer[reg0P];
442 if(p.memoryType == 1){
444 this.memoryPageCounter = this.mainMemory.getMemoryPageCountFromMemoryPageInstance(p.memoryPage);
445 if(this.memoryPageCounter !== undefined){
446 this.memoryInstructionCounter = 0;
447 this.message("JMP:page " + this.memoryPageCounter + "\n", 20);
449 throw new WebCPU_Exception(2, ["memoryPage not found in mainMemory."]);
452 throw new WebCPU_Exception(2, ["Can't goto Memory page type is not VPtr."]);
455 createBackendBinaryString: function(){
461 instr = this.fetchMemoryNext();
462 if(instr === undefined){
465 s += instr.createBinaryString(this);
469 staticOptimize: function(){
472 //一つのメモリページにラベル命令のみがある場合は、それを次のメモリページのラベル名に置換する
474 var removePageIndexStack = new Array();
475 var labelIDStack = new Array();
478 for(var i = 0; i < this.mainMemory.root.length; i++){
479 mpage = this.mainMemory.root[i];
480 if(mpage instanceof WebCPU_MemoryPage && mpage.data[0] instanceof WebCPU_Instruction_LB){
482 if(mpage.data.length == 1){
484 labelIDStack.push(mpage.data[0].imm32);
485 removePageIndexStack.push(i);
488 if(labelIDStack.length != 0){
490 labelID = labelIDStack.pop();
491 if(labelID == undefined){
494 this.mainMemory.root.splice(removePageIndexStack[0], 1);
496 this.staticOptimize_ReplaceLabelNumber(labelID, mpage.data[0].imm32);
498 removePageIndexStack = new Array();
503 if(labelIDStack.length != 0){
504 //プログラム末尾のラベル命令は削除しないようにする
505 newLabelID = labelIDStack.pop();
507 labelID = labelIDStack.pop();
508 if(labelID == undefined){
511 this.mainMemory.root.splice(removePageIndexStack[0], 1);
513 this.staticOptimize_ReplaceLabelNumber(labelID, newLabelID);
517 staticOptimize_ReplaceLabelNumber: function(from, to){
520 for(var i = 0, iLen = this.mainMemory.root.length; i < iLen; i++){
521 mpage = this.mainMemory.root[i];
522 for(var j = 0, jLen = mpage.data.length; j < jLen; j++){
523 instr = mpage.data[j];
524 if(instr instanceof WebCPU_Instruction_PLIMM){
525 if(instr.imm32 == from){
535 function parseSignedInt32(hexStr){
536 //文字列を16進32bit符号あり整数として変換する。
537 var i = parseInt(hexStr, 16);
546 function parseSignedInt(hexStr, bits){
547 //文字列を16進符号あり整数として変換する。
549 var i = parseInt(hexStr, 16);
550 if(i >= (1 << (bits - 1))){
552 return -(((0xffffffff >>> (32 - bits)) - i) + 1);
558 function toHexString8(v){
559 return ("00" + v.toString(16).toUpperCase()).slice(-2);
562 function toHexString32(v){
566 return ("00000000" + v.toString(16).toUpperCase()).slice(-8);
569 function replaceAll(str, org, dest){
570 //文字列str中にある文字列orgを文字列destにすべて置換する。
571 //http://www.syboos.jp/webjs/doc/string-replace-and-replaceall.html
572 return str.split(org).join(dest);