Замыкания и расширение области видимости (движок эмулятора ПК)
Нe получается никак решить проблему с замыканием.
<script> String.prototype.x = function(n, j) {var s = this, buf = ""; while(n > 0) { if(n&1) buf+=s; s+=(j?j:"")+s; n >>= 1; } return buf; } Number.prototype.toHex = function(n) { return (n < 0 ? "0x" : "") + ((((this >> 16) & 0x0000FFFF) | 0x00010000).toString(16) + ((this & 0x0000FFFF) | 0x00010000).toString(16).substr(1)).substr(-Math.abs(n)).toUpperCase(); } Number.prototype.hi = function(n) { if(isFinite(n)) return (this & 255) | ((n & 255) << 8); return (this >> 8) & 255; } Number.prototype.lo = function(n) { if(isFinite(n)) return (this & 0xFF00) | (n & 255); return this & 255; } var CPU = function(pattern) { var matrix = { actions : [], // Instructions set commands : [], // Assembly commands }; var user = { instructions : null, logs : null, }; var Logging = []; // Отображение таблицы команд this.showCommands = function(e) { user.instructions = e; }; function command_set(ic, ib) { var large = false, white = "<b style=background-color:white><u style=color:black>", cols = 7, dump = [], Acts = [], opers = []; for(code = 0; code < 256; ++ code) { var Action = matrix.actions[code]; var Act = Action.command + " ".x(cols), Regs = Action.operand, len = Regs.length - cols; if(len > 0) Act = Act.substr(0, cols - len) + Regs, Regs = Regs.substr(-cols); Act += " ", Regs += " "; html = "<span class=CPU_Group_" + Action.color + ">#</span>"; Act = Act.substr(0, cols); Regs = Regs.substr(0, cols); Acts.push(html.replace("#", (ic == code ? white + Act + "</u></b>" : Act))); opers.push(html.replace("#", (ic == code ? white + Regs.replace("IB", ib.toHex(2)) + "</u></b>" : Regs))); if((code & 15) == 15) dump.push("║" + ("<span style=text-decoration:overline>" + (code >> 4).toHex(1) + "║" + Acts.join("│") + "</span>║<br />") + ("║ ║"+ opers.join("│") + "║").replace(/(IB|XX)/g, "<b style=color:cyan>$1</b>")), Acts = [], opers = []; } for(code = 0; code < 16; ++ code) opers.push("╝" + code.toHex(1) + "╚════"); user.instructions.innerHTML = (isFinite(ic) ? ic.toHex(2) + " " : "___") + "╔═╗ ".x(16) + "<br />" + "╔═╦" + opers.join("╤") + "╗<br />" + dump.join(large ? "<br />╟─╫" + "─".x(cols).x(16, "┼") + "╢<br />" : "<br />") + "<br />╚═╩" + "═".x(cols).x(16, "╧") + "╝"; } this.command_set = command_set; // Начало инициализации if(pattern) { var res, tmp, id; // Коллекция битов дешифратора var pins = [], pinex = /^([A-Z_]+)\?$/gm, pinexp = []; // Коллекция набора операндов var opers = [], operex = /^([^\s]{1,13})\t(.*)$/gm, operexp = [], operact = [], operem = []; // Коллекция командной модели var models = [], modelex = /^([0X1_]+)([A-Z_])\t([^:\t]+)\t*([^:\t]*?)\t*:(.*?)((?:\/\/)(.*))?$/gm; // Начинаем разбирать представленный шаблом if(!(res = pattern.match(pinex))) return alert("Where the pins?"); // Разбираем битовые поля инструкций res[0].replace(/[_?]/g, "").split("").reverse() .forEach (function(id, x, whole) { (((pins[id]) || (pins[id] = [])) && pins[id]).push("(((x >> " + x + ") & 1) << " + pins[id].length + ")"); }); // Объявляем функциональное считывание for(id in pins) pinexp.push("$" + id), pins["$" + id] = new Function("x", "return " + pins[id].join(" | ")), delete pins[id]; // Собираем имена полей в выражение pinexp = new RegExp("(" + pinexp.join("|").replace(/(\$)/g, "\\$1") + ")", "g"); if(!(res = pattern.match(operex))) return alert("Where the operands?"); // Разбираемся с операндами res.forEach (function(str) { var str = str.split(/\t+/); operexp.push(str[0]); opers[str[0]] = str[1]; operact[str[0]] = str[2]; operem[str[0]] = str[3] || str[1]; }); // Собираем имена операндов в выражение operexp = new RegExp("(" + operexp.join("|").replace(/(\$)/g, "\\$1") + ")", "g"); if(!(res = pattern.match(modelex))) return alert("Where the model???"); // Разбираемся и строим полную командную модель res.forEach (function(desc) { desc.replace(modelex, function(str, mask, group, command, operand, action, description) { mask = mask.replace(/_/g, ""); var $D, commands, operands, actions, descriptions; base = parseInt(mask.replace(/X/g, "0"), 2), // 0XX1X0X1XX -> 0001000100 over = parseInt(mask.replace(/X/g, "1"), 2), // 0XX1X0X1XX -> 0111101111 last = parseInt(mask.replace(/./g, "1"), 2), // 0XX1X0X1XX -> 1111111111 mix = base ^ over ^ last; // 1001010100 // Пробегаем по всем комбинациям неопределённых битов for($D = base; $D <= over; $D = ((($D | mix) + 1) & ~mix) | base) { if(matrix.actions[$D]) continue; // Корректируем объявления шаблона с подстановкой конкретных числовых величин commands = command .replace(pinexp ,function($id) {return pins[$id]($D); }); operands = operand .replace(pinexp ,function($id) {return pins[$id]($D); }); actions = action .replace(pinexp ,function($id) {return pins[$id]($D); }); descriptions = description .replace(pinexp ,function($id) {return pins[$id]($D); }); // Проходимся второй раз с подстановкой перечисленных имён шаблона конкретными ссылками commands = commands .replace(operexp,function($id) {return opers[$id]; }); operands = operands .replace(operexp,function($id) {return opers[$id]; }); actions = actions .replace(operexp,function($id) {return operact[$id]; }); descriptions = descriptions .replace(operexp,function($id) {return operem[$id]; }); Logging.push($D.toHex(2) + "#" + group + "|" + commands + "\t" + operands + "\t{" + actions + "}" + descriptions); // Строим функциональную матрицу системы команд для эмуляции и дизассемблера matrix.actions[$D] = { color : group, // Цвет группы, к которой относится операция command : commands, // Название команды operand : operands, // Операнды команды action : actions, // Функциональное описание действия команды remark : descriptions, // Краткое описание инструкции }; if(!matrix.commands[commands]) matrix.commands[commands] = []; // Строим обратную матрицу для ассемблера operands.split("/") .forEach(function(operands) { if(!(operand in matrix.commands[commands])) matrix.commands[commands][operands] = $D; }); }; }); }); /**/document.getElementById("Logging").innerText = Logging.join("\r\n"); } }; var cpu = null; function main() { cpu = new CPU(document.getElementById("Pattern").textContent); cpu.showCommands(document.getElementById("Commands")); cpu.command_set(0x55, 1); } </script> <style> pre#Commands { color : lightgreen; cursor : default; } span.CPU_Group__{ background-color: #012; cursor : pointer; } span.CPU_Group_A{ background-color: #772; <!--ALU--> } span.CPU_Group_B{ background-color: #764; <!--JMP--> } span.CPU_Group_C{ background-color: #888; <!--HLT--> } span.CPU_Group_D{ background-color: #663; <!--INX--> } span.CPU_Group_E{ background-color: #363; <!--POP--> } span.CPU_Group_F{ background-color: #367; <!--MOV--> } span.CPU_Group_X{ background-color: #437; <!--???--> } var { display :none } </style> </head> <body onload='main()'> <var id=Pattern >P0 BC reg.bc B and C P2 DE reg.de D and E P4 HL reg.hl H and L P6 PSW reg.acc Status Q0 B reg.bc B Q1 C reg.bc C Q2 D reg.de D Q3 E reg.de E Q4 H reg.hl H Q5 L reg.hl L Q6 M reg.m Memory Q7 A reg.acc Accumulator R0 B reg.bc.hi B R1 C reg.bc.lo C R2 D reg.de.hi D R3 E reg.de.lo E R4 H reg.hl.hi H R5 L reg.hl.lo L R6 M reg.mem Memory R7 A reg.acc.lo Accumulator ALU0 ADD 0 Addition ALU1 ADC 0 Addition with carry ALU2 SUB 0 Subtraction ALU3 SBB 0 Subtraction with borrow ALU4 ANA 0 Bitwise conjunction ALU5 XRA 0 Bitwise exclude disjunction ALU6 ORA 0 Bitwise disjunction ALU7 CMP 0 Comparation ZZ_YYY_XXX_? 00_000_000_C NOP : //No operation 00_110_001_B LXI SP,IW :reg.sp=$IW() // 00_XX0_001_B LXI P$Y,IW :P$Y=$IW() //Load address into P$Y 00_XXX_110_F MVI R$Y,IB :Q$X=$IB() //Flip-flop translation byte to R$Y 01_110_110_F HLT :reg.pc -- //Halting 01_XXX_XXX_F MOV R$Y,R$X :Q$X=R$Y(R$X()) //Flip-flop translation R$X to R$Y 10_XXX_XXX_A ALU$Y R$Y :ALU$Y(R$Y()) //ALU$Y 11_XX0_001_E POP P$Y :P$Y=STACK() //Pop P$Y from stack 11_XX0_101_E PUSH P$Y :STACK(P$Y) //Push P$Y to stack XX_XXX_XXX_X ??? : // </var> <pre id=Commands></pre> <pre id=Logging></pre> </body>Нe думал, что придётся просить помощи в элементарном. Мне нужно организовать всё так, чтобы анонимные функции видели локальные переменные. Спасибо! P.S.: Пример выше - очень абстрактный. И я здорово запутался в проблеме видимости локальных переменных. Пришлось явно всё передавать, да ещё и with(...) встроить... |
Удалoсь таки заставить "славный говнокод" работать:yes:
(доработал представленное выше, но не разобрался с замыканиями) Нa этих выходных решил попробовать написать эмулятор с нуля, руководствуясь накопленным опытом. Основной задачей было поставлено написать некий универсальный движок, с помощью которого можно описать нужный процессор простым шаблоном, который развернётся в работающую модель с комплектом 3 в 1: Эмулятор, Дизассемблер, Ассемблер. При этом, не нужно было бы сурово переделывать сам js-текст. В итоге, уложился в трое суток бессонных ночей отладки. Получился компактный - 1500 строк и 60кб. P.S.: Если браузерная совместимость не подведёт, эмулятор запустится сразу под рандомным мусором в его ОЗУ… Необходимо нажать кнопку Reset, а затем - Start. Клавиши тоже должны работать: F1-Шаг отладки, F4-Полный ход, F9-Брейк-пойнт. Также, F7-Выбор настраиваемого параметра(FPS, IPC, IPS), F6/F8-Декремент/Инкремент параметра. Пожалуйста, отпишитесь, у кого - какой результат. Кстати, шаблон можно редактировать прямо в процессе (кнопка Matrix) и изменить всю систему команд на ходу (двойной клик по той же Matrix)… (по идее, можно описать 6502. Для Z80 пока ещё не хватает поддержки префиксов, что довольно поправимо) |
Цитата:
var BUG = function(formula) { var expression = function() { return 0; }, adjust = 7, times = 3; function Bug(n) { var no = n; while(n > 0) { console.log("OUTSIDE: Adjust is " + adjust + ", N is " + n); *!*console.log("RESULT is " + expression.bind(self).call());*/!* if(no == n) { console.log("BUGGEST"); n --; } no = n; } if(-- times > 0) setTimeout(this.Bug.bind(self), 2500); } if(!!formula) expression = (new Function("", formula)); this.Bug = Bug; return this; }; var Bugs = new BUG( " console.log(\"INSIDER...\");\ console.log(\"INSIDE: Adjust is \" + this.adjust);\ console.log(\"INSIDE: N is \" + this.n);\ try {\ console.log(\"INSIDE: Adjust is \" + adjust);\ } catch(e) {\ console.log(\"INSIDE: Adjust is ???\");\ }\ try {\ console.log(\"INSIDE: N is \" + n);\ } catch(e) {\ console.log(\"INSIDE: N is ???\");\ }\ try {\ console.log(\"return n -- * adjust\");\ return n -- * adjust;\ } catch(e) {\ }\ try {\ console.log(\"return this.n -- * this.adjust\");\ return this.n -- * this.adjust;\ } catch(e) {\ console.log(\"Total Error...\");\ }\ return 0;" ); alert(Bugs.Bug(3)); P.S.: Проблема в том, что из-под eval всё работает:yes: и все переменные доступны и обновляемы. А вот красиво обвернув в функцию никак не получается… Выше я обновил пример - пишу эмулятор. И он пока весь держится на eval… Хотелось бы иначе:no: Спасибо! |
Цитата:
Вы с эмуляторами сталкивались? Пишу сейчас собственный под РАДИО-86РК, где нудная цепочка "switch(машинный_код) case инструкция№1 ... case инструкция№255" заменена на массив. Т.е. имеется структура регистров var Register = function(x) { return { id : "", fx : 0, hi : (x >> 8) & 255, lo : x & 255, start : x & 65535, get reset() { this.lo = this.start & 255, this.hi = this.start >> 8; return this.lo | (this.hi << 8); }, get h() { return this.hi; }, set h(x) { this.fx = (x >> 8) & 1; this.hi = (x &= 255); this.fx |= (x & 128) | (!x ? 64 : 0); x ^= x >> 4; x ^= x << 2; x ^= x >> 1; this.fx |= x & 4; //console.log(this.id + ":" + (this.lo | (this.hi << 8)).toHex(-4)); }, get l() { return this.lo; }, set l(x) { this.fx = (x >> 8) & 1; this.lo = (x &= 255); this.fx |= (x & 128) | (!x ? 64 : 0); x ^= x >> 4; x ^= x << 2; x ^= x >> 1; this.fx |= x & 4; //console.log(this.id + ":" + (this.lo | (this.hi << 8)).toHex(-4)); }, get x() { //console.log(this.id + ":" + (this.lo | (this.hi << 8)).toHex(-4)); return this.lo | (this.hi << 8); }, set x(x) { this.lo = x & 255, this.hi = (x >> 8) & 255; //console.log(this.id + ":" + (this.lo | (this.hi << 8)).toHex(-4)); }, get m() { //console.log(this.id + "[" + (this.lo | (this.hi << 8)).toHex(-4) + "]:" + (ram[this.lo | (this.hi << 8)] & 255).toHex(-2)); return ram[this.lo | (this.hi << 8)] & 255; }, set m(x) { ram[this.lo | (this.hi << 8)] = x & 255; //console.log(this.id + "[" + (this.lo | (this.hi << 8)).toHex(-4) + "]=" + (ram[this.lo | (this.hi << 8)] & 255).toHex(-2)); }, get n() { var x = this.lo | (this.hi << 8); this.lo = (this.lo + 1) & 255; this.hi = !this.lo ? (this.hi + 1) & 255 : this.hi; return ram[x]; }, set n(x) { this.hi = !this.lo ? (this.hi - 1) & 255 : this.hi; this.lo = (this.lo - 1) & 255; ram[this.lo | (this.hi << 8)] = x & 255; }, get w() { var x = this.lo | (this.hi << 8), d; this.lo = (this.lo + 2) & 255; this.hi = this.lo < 2 ? (this.hi + 1) & 255 : this.hi; d = ram[x] + 256 * ram[(x + 1) & 65535]; //console.log("[" + x.toHex(-4) + "]:" + d.toHex(-4)); return d; }, set w(x) { this.hi = this.lo < 2 ? (this.hi - 1) & 255 : this.hi; this.lo = (this.lo - 2) & 255; ram[this.lo | (this.hi << 8)] = x & 255; ram[(this.lo + (this.hi << 8) + 1) & 65535] = (x >> 8) & 255; //console.log("[" + (this.lo | (this.hi << 8)).toHex(-4) + "]=" + x.toHex(-4)); }, } };В основном цикле эмулятора, когда просто через eval пропускается нечто типа "r.a.h = r.b.l" - полный порядок. А если всё красиво обернуть в функции { r.a.h = r.b.l; }, то никак не получается заставить видеть r-структуру. Вы сами понимаете, что подготовить все 256 функций к вызовам и потом их вызывать - куда быстрее, чем каждый раз интерпретировать eval'ом тысячи команд за раз. P.S.: Сейчас нашёл более-менее работающий способ: Все 256 функций при инициализации прописываю прямо внутри операционного цикла, к которому насильно привязываю ещё и регистры с памятью. Некрасиво, обвёрнуто в with(this), но заработало…:dance: А передавать всё через аргументы - пробовал в прошлом году. Огороды скобок и некоторые вещи сделать нельзя. (у меня R$1 разворачивается в r.a.l, что раньше было R$1(R$1()+R$2()) как r.a.l(r.a.l()+r.b.h()) и тяжело визуально наблюдалось. сейчас - просто R$1+=R$2 за r.a.l+=r.b.h, что коротко и понятно) Не просил бы помощи, если бы задача была проще.:lol: Нужно мне именно так, как я описал выше. И никак иначе:yes: |
Не знаю, что тут у вас происходит, много разных непонятных символов :)
Судя по Цитата:
+ https://pegjs.org/ + https://github.com/harc/ohm |
Основные принципы организации шаблона
Конечнo, Спасибо за советы…
В самом начале темы я пост поправил. Вы запускали пример в нём? Имеется конкретный эмулятор (нажмите кнопку V между Console и Full Screen, чтобы увидеть таблицу команд), который расписан классически, как можете убедиться, без выкрутасов (древом switch(код_команды) case код_nn: � �ействие_nn…). Когда переписывался с его автором и пытался чуточку подогнать его под свои фантазии, приходилось модифицировать и древо ассемблера, и древо дизассемблера, и саму визуальную таблицу, и ядро эмуляции. Что крайне сложно для отладки. Тогда появилась мысль, разработать некий автоматический конструктор для эмуляции. Из справочника по конкретному процессору переводишь таблицу в некоторый шаблон, а библиотека сама его "переварит" и всё сделает без особого напряга со стороны пользователя. Если что-то не так работает, правится только шаблон… В детстве изучил кучу советской справочной литературы отца на темы ПЛ/1, ПЛ/М, Ада, Фортран, Лисп, Пролог, Паскаль, Форт, Си, АПЛ и т.д. Т.е. когда сел за более-менее приветливый терминал, имел некоторые представления о том, как заставить машину делать что-то именно моё уникальное. Ассемблер был роскошью и команды писал прямо дампом.:yes: Сейчас задуманное работает. До 512 срабатываний таймера в секунду с обработкой до 2048 инструкций (512*2048=до миллиона команд в секунду). И всё держится на eval, а разумно бы в new Function завернуть. В функцию нельзя передавать аргументы. Вернее, можно, но их будет с несколько десятков, что расходно и бессмысленно. Тем более, на выходе - ничего нет. Процесс игнорирует результат eval и не принимает значения от function-return. Т.е. оперирование идёт со всеми окружающими структурами и массивами. Понимаете?:no: P.S.: Когда впервые пересел с IBM-PC/XT - DOS 3.11 за Pentium-I под Windows'98, через год изучения Visual Basic 4 написал свой первый эмулятор с кучей if-then-else/switch-case и т.д. Теперь хочу автоматизировать всё это дело…:write: P.P.S.: Несколько слов о принципах шаблона и его "разворачивания"… Аналогично оператору casex в шаблоне перечисляются все возможные комбинации битов кода для каждой операции: Код:
1X0X - Бинарная маска шаблона: Здесь X указывает на биты с произвольным линейным содержимым Код:
?XYX? - Здесь за X берутся биты 3 и 1 соответственно. Только за Y берётся бит 2, Код:
0XX1 DO R$Y+=R$X В шаблоне также можно описать псевдонимы для операций и регистров, закрепив за ними конкретные действия: Код:
R0 Door Open_Door Код:
0XX1 DO R$Y+=R$X :R$Y(R$X) Разрабатываемая маленькая библиотека делает всё сама. При этом, когда Вы опишете шаблон абсолютно корректно, на выходе получите полноценный функциональный инструмент:
Код:
DO Door+=Dog Запустив данную "программу" мы заставим цикл эмуляции выполнить следующую последовательность операций: Open_Door(Keep_Home); Make_Purr(Wash_Desk); Make_Purr(Keep_Home); |
<head><script type='text/javascript'> var Register = function(reg) { this.store = [0x43,0x42,0x45,0x44,0x4C,0x48,0x46,0x41,0,0,0,0,0,0,0,0]; for(var id in reg) { var ptr = reg[id].split(/\s+/), num = ptr.length, gets = { "1" : "return this.store[0x_0]", "2" : "return this.store[0x_0] | this.store[0x_1] << 8", "3" : "var tmp = this.store[0x_0] | this.store[0x_1] << 8; return 0x_2==1 ? m[tmp] : m[tmp] | m[tmp + 1] << 8" }, sets = { "1" : "this.store[0x_0] = x & 255", "2" : "this.store[0x_0] = x & 255, this.store[0x_1] = x >> 8 & 255", "3" : "var tmp = this.store[0x_0] | this.store[0x_1] << 8; 0x_2==1 ? m[tmp] = x & 255 : (m[tmp] = x & 255, m[tmp + 1] = x >> 8 & 255)" }; Object.defineProperty(this, id, { enumerable : true, get : new Function( "", gets[num].replace(/_(\d)/g, function(s, i) { return ptr[i]; } ) ), set : new Function( "x", sets[num].replace(/_(\d)/g, function(s, i) { return ptr[i]; } ) ) }); } return this; }; var r; var m = (function(n) { var tmp = []; while(n --) tmp.push(Math.random() * 256 & 255); return tmp; })(65535); var regs = { bc : "0 1", b : "1", c : "0", de : "2 3", d : "3", e : "2", hl : "4 5", h : "5", l : "4", psw : "7 6", f : "6", a : "7", m : "4 5 1", cx : "0 1", ch : "1", cl : "0", dx : "2 3", dh : "3", dl : "2", bx : "4 5", bh : "5", bl : "4", al : "7" }; window.onload = function() { var log = []; r = new Register(regs); for(var id in regs) log.push(id + ": " + r[id].toString(16)); document.getElementsByTagName("pre")[0].innerHTML = log.join("<br />"); } </script> </head> <body> <pre></pre> </body>P.S.: Код работает… Однако, вопрос к настоящим знатокам: Может что-то можно описать красивее?:thanks: |
Цитата:
|
Часовой пояс GMT +3, время: 01:00. |