Добавил:
inrad
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:по ВМСиС транслятор кода KR580
.htm<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
<head>
<title>Pretty i8080 Assembler</title>
<script type="text/javascript">
<!--
//
// Pretty i8080 Assembler
//
// Send comments to svofski at gmail dit com
//
// Copyright (c) 2009 Viacheslav Slavinsky
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
// Translation help:
// Leonid Kirillov, Alexander Timoshenko, Upi Tamminen,
// Cristopher Green, Nard Awater, Ali Asadzadeh,
// Guillermo S. Romero, Anna Merkulova, Stephan Henningsen
//
// Revison Log
// Rev.A: Initial release
// Rev.B: A lot of fixes to compile TINIDISK.ASM by Dr. Li-Chen Wang
// Rev.C: Performance optimizations and cleanup, labels->hash
// Rev.D: More syntax fixes; opera navigation and Back Button Toolbar
// Rev.E: Navigation to label references (backref menu)
// Nice labels table
// Some Opera-related fixes
// Rev.F: fixed '.' and semi-colon in db
// tab scroll fixed
// Rev.G: $ can now work as hex prefix
// Rev.H: Fixed spaces in reg-reg, .binfile, .hexfile
//
// TODO: evaluation should ignore precedence, it's all left-to-right
//
// -- global DOM elements
var debug = false;
var inTheOpera = navigator.appName.indexOf('Opera') != -1;
var binFileName = 'test.bin';
var hexFileName = 'test.hex';
var objCopy = 'gobjcopy';
var postbuild = '';
var doHexDump = true;
// -- utility stuffs --
function fromBinary(val) {
x = 0;
n = 1;
for (i = val.length - 1; i >= 0; i--) {
if (val[i] == '1')
x += n;
else if (val[i] != '0')
return Number.NaN;
n *= 2;
}
return new Number(x);
}
function char8(val) {
if (val > 32 && val < 127) return String.fromCharCode(val);
return '.';
}
function hex8(val) {
if (val < 0 || val > 255) return "??";
var hexstr = "0123456789ABCDEF";
return hexstr[(val & 0xf0) >> 4] + hexstr[val & 0x0f];
}
function hex16(val) {
return hex8((val & 0xff00) >> 8) + hex8(val & 0x00ff);
}
function isValidIm16(s) {
return s != null && s.length > 0;
}
function isValidIm8(s) {
return s != null && s.length > 0;
}
function isWhitespace(c) {
return c=='\t' || c == ' ';// this is too slow c.match(/\s/);
}
Array.prototype.indexOf = function (element) {
for (var i = 0; i < this.length; i++) {
if (this[i] == element) {
return i;
}
}
return -1;
};
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); };
String.prototype.endsWith = function(c) { return this[this.length-1] == c; };
// -- Assembler --
var ops0 = {
"nop": "00",
"hlt": "76",
"ei": "fb",
"di": "f3",
"sphl": "f9",
"xchg": "eb",
"xthl": "e3",
"daa": "27",
"cma": "2f",
"stc": "37",
"cmc": "3f",
"rlc": "07",
"rrc": "0f",
"ral": "17",
"rar": "1f",
"pchl": "e9",
"ret": "c9",
"rnz": "c0",
"rz": "c8",
"rnc": "d0",
"rc": "d8",
"rpo": "e0",
"rpe": "e8",
"rp": "f0",
"rm": "f8"
};
var opsIm16 = {
"lda": "3a",
"sta": "32",
"lhld": "2a",
"shld": "22",
"jmp": "c3",
"jnz": "c2",
"jz": "ca",
"jnc": "d2",
"jc": "da",
"jpo": "e2",
"jpe": "ea",
"jp": "f2",
"jm": "fa",
"call": "cd",
"cnz": "c4",
"cz": "cc",
"cnc": "d4",
"cc": "dc",
"cpo": "e4",
"cpe": "ec",
"cp": "f4",
"cm": "fc"
};
// lxi rp, im16
var opsRpIm16 = {
"lxi": "01" // 00rp0001, bc=00, de=01,hl=10, sp=11
};
// adi 33, out 10
var opsIm8 = {
"adi": "c6",
"aci": "ce",
"sui": "d6",
"sbi": "de",
"ani": "e6",
"xri": "ee",
"ori": "f6",
"cpi": "fe",
"in": "0db",
"out": "d3"
};
var opsRegIm8 = {
"mvi": "06"
};
var opsRegReg = {
"mov": "40"
};
var opsReg = {
"add": "80", // regsrc
"adc": "88",
"sub": "90",
"sbb": "98",
"ana": "a0",
"xra": "a8",
"ora": "b0",
"cmp": "b8",
"inr": "04", // regdst (<<3)
"dcr": "05"
};
// these are the direct register ops, regdst
var opsRegDst = new Array("inr", "dcr");
var opsRp = {
"ldax": "0A", // rp << 4 (only B, D)
"stax": "02", // rp << 4 (only B, D)
"dad": "09", // rp << 4
"inx": "03", // rp << 4
"dcx": "0b", // rp << 4
"push": "c5", // rp << 4
"pop": "c1" // rp << 4
};
var LabelsCount = 0;
var labels = new Object();
var resolveTable = Array(); // label negative id, resolved address
var mem = Array();
var textlabels = Array();
var references = Array();
var errors = Array();
function clearLabels() {
LabelsCount = 0;
labels = new Object();
}
function resolveNumber(identifier) {
if (identifier == undefined || identifier.length == 0) return;
if ((identifier[0] == "'" || identifier[0] == "'")
&& identifier.length == 3) {
return (0xff & identifier.charCodeAt(1));
}
if (identifier[0] == '$') {
identifier = "0x" + identifier.substr(1, identifier.length-1);
}
if ("0123456789".indexOf(identifier[0]) != -1) {
var test;
test = new Number(identifier);
if (!isNaN(test)) {
return test;
}
var suffix = identifier[identifier.length-1].toLowerCase();
switch (suffix) {
case 'd':
test = new Number(identifier.substr(0, identifier.length-1));
if (!isNaN(test)) {
return test;
}
break;
case 'h':
test = new Number("0x" + identifier.substr(0, identifier.length-1));
if (!isNaN(test)) {
return test;
}
break;
case 'b':
test = fromBinary(identifier.substr(0, identifier.length-1));
if (!isNaN(test)) {
return test;
}
break;
case 'q':
try {
var oct = identifier.substr(0, identifier.length-1);
for (var i = oct.length; --i >= 0;) {
if (oct[i] == '8' || oct[i] == '9') return -1;
}
return new Number(
eval('0' + identifier.substr(0, identifier.length-1)));
} catch(err) {}
break;
}
}
return -1;
}
function referencesLabel(identifier, linenumber) {
identifier = identifier.toLowerCase();
if (references[linenumber] == undefined) {
references[linenumber] = identifier;
}
}
function markLabel(identifier, address, linenumber, override) {
identifier = identifier.replace(/\$([0-9a-fA-F]+)/, '0x$1');
identifier = identifier.replace(/(^|[^'])(\$|\.)/, ' '+address+' ');
var number = resolveNumber(identifier.trim());
if (number != -1) return number;
if (linenumber == undefined) {
LabelsCount++;
address = -1 - LabelsCount;
}
identifier = identifier.toLowerCase();
var found = labels[identifier];
if (found != undefined) {
if (address >= 0) {
resolveTable[-found] = address;
} else {
address = found;
}
}
if (!found || override) {
labels[identifier] = address;
}
if (linenumber != undefined) {
textlabels[linenumber] = identifier;
}
return address;
}
function setmem16(addr, immediate) {
if (immediate >= 0) {
mem[addr] = immediate & 0xff;
mem[addr+1] = immediate >> 8;
} else {
mem[addr] = immediate;
mem[addr+1] = immediate;
}
}
function setmem8(addr, immediate) {
mem[addr] = immediate < 0 ? immediate : immediate & 0xff;
}
function parseRegisterPair(s) {
if (s != undefined) {
s = s.split(';')[0].toLowerCase();
if (s == 'b' || s == 'bc') return 0;
if (s == 'd' || s == 'de') return 1;
if (s == 'h' || s == 'hl') return 2;
if (s == 'sp'|| s == 'psw' || s == 'a') return 3;
}
return -1;
}
// b=000, c=001, d=010, e=011, h=100, l=101, m=110, a=111
function parseRegister(s) {
if (s == undefined) return -1;
s = s.toLowerCase();
return "bcdehlma".indexOf(s[0]);
}
function tokenDBDW(s, addr, len, linenumber) {
var size = -1;
if (s.length == 0) return 0;
n = markLabel(s, addr);
referencesLabel(s, linenumber);
if (len == undefined) len = 1;
if (len == 1 && n < 256) {
setmem8(addr, n);
size = 1;
} else if (len == 2 && n < 65536) {
setmem16(addr, n);
size = 2;
}
return size;
}
function tokenString(s, addr, linenumber) {
for (var i = 0; i < s.length; i++) {
setmem8(addr+i, s.charCodeAt(i));
}
return s.length;
}
function parseDeclDB(args, addr, linenumber, dw) {
var text = args.slice(1).join(' ');
var arg = "";
var mode = 0;
var cork = false;
var nbytes = 0;
for (var i = 0; i < text.length; i++) {
switch (mode) {
case 0:
if (text[i] == '"' || text[i] == "'") {
mode = 1; cork = text[i];
break;
} else if (text[i] == ',') {
var len = tokenDBDW(arg, addr+nbytes, dw, linenumber);
if (len < 0) {
return -1;
}
nbytes += len;
arg = "";
} else if (text[i] == ';') {
i = text.length;
break;
} else {
arg += text[i];
}
break;
case 1:
if (text[i] != cork) {
arg += text[i];
} else {
cork = false;
mode = 0;
len = tokenString(arg, addr+nbytes, linenumber);
if (len < 0) {
return -1;
}
nbytes += len;
arg = "";
}
break;
}
}
if (mode == 1) return -1; // unterminated string
var len = tokenDBDW(arg, addr+nbytes, dw, linenumber);
if (len < 0) return -1;
nbytes += len;
return nbytes;
}
function getExpr(arr) {
var ex = arr.join(' ').trim();
if (ex[0] == '"' || ex[0] == "'") {
return ex;
}
return ex.split(';')[0];
}
function useExpr(s, addr, linenumber) {
var expr = getExpr(s);
if (expr == undefined || expr.trim().length == 0) return false;
var immediate = markLabel(expr, addr);
referencesLabel(expr, linenumber);
return immediate;
}
function parseInstruction(s, addr, linenumber) {
var parts = s.split(/\s+/);
for (var i = 0; i < parts.length; i++) {
if (parts[i][0] == ';') {
parts.length = i;
break;
}
}
var labelTag;
var immediate;
for (;parts.length > 0;) {
var opcs;
var mnemonic = parts[0].toLowerCase();
if (mnemonic.length == 0) {
parts = parts.slice(1);
continue;
}
// no operands
if ((opcs = ops0[mnemonic]) != undefined) {
mem[addr] = new Number("0x" + opcs);
return 1;
}
// immediate word
if ((opcs = opsIm16[mnemonic]) != undefined) {
mem[addr] = new Number("0x" + opcs);
immediate = useExpr(parts.slice(1), addr, linenumber);
//if (!immediate) return -3;
setmem16(addr+1, immediate);
return 3;
}
// register pair <- immediate
if ((opcs = opsRpIm16[mnemonic]) != undefined) {
subparts = parts.slice(1).join(" ").split(",");
if (subparts.length < 2) return -3;
rp = parseRegisterPair(subparts[0]);
if (rp == -1) return -3;
mem[addr] = (new Number("0x" + opcs)) | (rp << 4);
immediate = useExpr(subparts.slice(1), addr, linenumber);
setmem16(addr+1, immediate);
return 3;
}
// immediate byte
if ((opcs = opsIm8[mnemonic]) != undefined) {
mem[addr] = new Number("0x" + opcs);
immediate = useExpr(parts.slice(1), addr, linenumber);
//if (!immediate) return -2;
setmem8(addr+1, immediate);
return 2;
}
// single register, im8
if ((opcs = opsRegIm8[mnemonic]) != undefined) {
subparts = parts.slice(1).join(" ").split(",");
if (subparts.length < 2) return -2;
reg = parseRegister(subparts[0]);
if (reg == -1) return -2;
mem[addr] = new Number("0x" + opcs) | reg << 3;
immediate = useExpr(subparts.slice(1), addr, linenumber);
setmem8(addr+1, immediate);
return 2;
}
// dual register (mov)
if ((opcs = opsRegReg[mnemonic]) != undefined) {
subparts = parts.slice(1).join(" ").split(",");
if (subparts.length < 2) return -1;
reg1 = parseRegister(subparts[0].trim());
reg2 = parseRegister(subparts[1].trim());
if (reg1 == -1 || reg2 == -1) return -1;
mem[addr] = new Number("0x" + opcs) | reg1 << 3 | reg2;
return 1;
}
// single register
if ((opcs = opsReg[mnemonic]) != undefined) {
reg = parseRegister(parts[1]);
if (reg == -1) return -1;
if (opsRegDst.indexOf(mnemonic) != -1) {
reg <<= 3;
}
mem[addr] = new Number("0x" + opcs) | reg;
return 1;
}
// single register pair
if ((opcs = opsRp[mnemonic]) != undefined) {
rp = parseRegisterPair(parts[1]);
if (rp == -1) return -1;
mem[addr] = new Number("0x" + opcs) | rp << 4;
return 1;
}
// rst
if (mnemonic == "rst") {
n = resolveNumber(parts[1]);
if (n >= 0 && n < 8) {
mem[addr] = 0xC7 | n << 3;
return 1;
}
return -1;
}
if (mnemonic == ".org" || mnemonic == "org") {
n = resolveNumber(parts[1]);
if (n >= 0) {
return -100000-n;
}
return -1;
}
if (mnemonic == ".binfile") {
if (parts[1] != undefined && parts[1].trim().length > 0) {
binFileName = parts[1];
}
return -100000;
}
if (mnemonic == ".hexfile") {
if (parts[1] != undefined && parts[1].trim().length > 0) {
hexFileName = parts[1];
}
return -100000;
}
if (mnemonic == ".objcopy") {
objCopy = parts.slice(1).join(' ');
return -100000;
}
if (mnemonic == ".postbuild") {
postbuild = parts.slice(1).join(' ');
return -100000;
}
if (mnemonic == ".nodump") {
doHexDump = false;
return -100000;
}
// assign immediate value to label
if (mnemonic == ".equ" || mnemonic == "equ") {
if (labelTag == undefined) return -1;
var value = evaluateExpression(parts.slice(1).join(' '), addr);
markLabel(labelTag, value, linenumber, true);
return 0;
}
if (mnemonic == 'cpu' ||
mnemonic == 'aseg' ||
mnemonic == '.aseg') return 0;
if (mnemonic == 'db' || mnemonic == '.db' || mnemonic == 'str') {
return parseDeclDB(parts, addr, linenumber, 1);
}
if (mnemonic == 'dw' || mnemonic == '.dw') {
return parseDeclDB(parts, addr, linenumber, 2);
}
if (mnemonic == 'ds' || mnemonic == '.ds') {
var size = evaluateExpression(parts.slice(1).join(' '), addr);
if (size >= 0) {
for (var i = 0; i < size; i++) {
setmem8(addr+i, 0);
}
return size;
}
return -1;
}
if (parts[0][0] == ";") {
return 0;
}
// nothing else works, it must be a label
if (labelTag == undefined) {
var splat = mnemonic.split(':');
labelTag = splat[0];
markLabel(labelTag, addr, linenumber);
parts.splice(0, 1, splat.slice(1).join(':'));
continue;
}
mem[addr] = -2;
return -1; // error
}
return 0; // empty
}
// -- output --
function labelList() {
labelList.s = " ";
labelList.f = function(label, addr) {
var result = label.substring(0, labelList.s.length);
if (result.length < labelList.s.length) {
result += labelList.s.substring(result.length);
}
result += addr < 0 ? "????" : hex16(addr);
return result;
}
var sorted = [];
for (var i in labels) {
sorted[sorted.length] = i;
}
sorted.sort();
var result = "<pre>Labels:</pre>";
result += '<div class="hordiv"></div>';
result += '<pre>';
var col = 1;
for (var j = 0; j < sorted.length; j++) {
var i = sorted[j];
var label = labels[i];
// hmm?
if (label == undefined) continue;
if (i.length == 0) continue; // resolved expressions
result += "<span class='" +
(col%4 == 0 ? 't2' : 't1') +
"' onclick=\"return gotoLabel('"+i+"');\"";
if (label < 0) result += ' style="background-color:pink;" ';
result += ">";
result += labelList.f(i,label);
result += "</span>";
if (col % 4 == 0) result += "<br/>";
col++;
}
result += "</pre>";
return result;
}
function dumpspan(org, mode) {
var result = "";
var nonempty = false;
conv = mode ? char8 : hex8;
for (var i = org; i < org+16; i++) {
if (mem[i] != undefined) nonempty = true;
if (mode == 1) {
result += conv(mem[i]);
} else {
result += (i > org && i%8 == 0) ? "-" : " ";
if (mem[i] == undefined) {
result += ' ';
}
else if (mem[i] < 0) {
result += '<span class="errorline">' + conv(mem[i]) + '</span>';
} else {
result += conv(mem[i]);
}
}
}
return nonempty ? result : false;
}
function dump() {
var org;
for (org = 0; org < mem.length && mem[org] == undefined; org++);
if (org % 16 != 0) org = org - org % 16;
var result = "<pre>Memory dump:</pre>";
result += '<div class="hordiv"></div>';
var lastempty;
var printline = 0;
for (i = org; i < mem.length; i += 16) {
span = dumpspan(i, 0);
if (span || !lastempty) {
result += '<pre ' + 'class="d' + (printline++%2) + '"';
result += ">";
}
if (span) {
result += hex16(i) + ": ";
result += span;
result += ' ';
result += dumpspan(i, 1);
result += "</pre><br/>";
lastempty = false;
}
if (!span && !lastempty) {
result += " </pre><br/>";
lastempty = true;
}
}
return result;
}
function intelHex() {
var i, j;
var line = "";
var r = "";
var pureHex = "";
r += "<pre>Intel HEX:</pre>";
r += '<div class="hordiv"></div>';
r += "<pre>";
r += 'cat >' + hexFileName + ' <<X<br/>';
//r += 'ed<br>i<br>';
for (i = 0; i < mem.length;) {
for (j = i; j < mem.length && mem[j] == undefined; j++);
i = j;
if (i >= mem.length) break;
line = ":";
cs = 0;
rec = "";
for (j = 0; j < 32 && mem[i+j] != undefined; j++) {
if (mem[i+j] < 0) mem[i+j] = 0;
rec += hex8(mem[i+j]);
cs += mem[i+j];
}
cs += j; line += hex8(j); // byte count
cs += (i>>8)&255; cs+=i&255; line += hex16(i); // record address
cs += 0; line += "00"; // record type 0, data
line += rec;
cs = 0xff&(-(cs&255));
line += hex8(cs);
pureHex += line + '|n';
r += line + '<br/>';
i += j;
}
r += ':00000001FF<br/>';
pureHex += ':00000001FF\n';
//r += '.<br>w ' + hexFileName +'<br>q<br>';
r += 'X<br/>';
r += objCopy + ' -I ihex ' + hexFileName + ' -O binary ' +
binFileName + '<br/>';
if (postbuild.length > 0) {
r += postbuild + '<br/>';
}
r += '</pre>';
var formData = document.getElementById('hex');
formData.value = pureHex;
var formBinName = document.getElementById('formbinname');
formBinName.value = binFileName;
return r;
}
function getListHeight() {
var listElement = document.getElementById('list');
return inTheOpera ?
listElement.style.pixelHeight : listElement.offsetHeight;
}
function gotoLabel(label) {
var sought = textlabels.indexOf(label.toLowerCase());
var element = document.getElementById("label" + sought);
if (element != undefined) {
startHighlighting(sought, element);
element = element.parentNode;
var destination = element.offsetTop - getListHeight()/2;
scrollTo(destination, true);
}
return false;
}
function getReferencedLabel(lineno) {
var refto = references[lineno];
if (refto != undefined) {
var sought = textlabels.indexOf(refto.toLowerCase());
return document.getElementById("label" + sought);
}
return undefined;
}
function getReferencingLines(lineno) {
var refs = new Array();
var fullrefs = new Array();
var label = textlabels[lineno];
if (label != undefined) {
for(var i = 0; i < references.length; i++) {
if (references[i] == label) {
var element = document.getElementById("code" + i);
refs[refs.length] = element;
element = document.getElementById("l" + i);
fullrefs[fullrefs.length] = element;
}
}
}
referencingLinesFull = fullrefs;
return refs;
}
function getLabel(l) {
return labels[l.toLowerCase()];
}
function listing(text,lengths,addresses) {
var result = "";
var addr = 0;
for(var i = 0; i < text.length; i++) {
var labeltext = "";
var remainder = text[i];
var parts = text[i].split(/[\:\s]/);
if (parts.length > 1) {
if (getLabel(parts[0]) != -1) {
labeltext = parts[0];
remainder = text[i].substring(labeltext.length);
}
}
var id = "l" + i;
var labelid = "label" + i;
var remid = "code" + i;
var hexes = "";
var unresolved = false;
var width = 0;
var len = lengths[i] > 4 ? 4 : lengths[i];
for (var b = 0; b < len; b++) {
hexes += hex8(mem[addresses[i]+b]) + ' ';
width += 3;
if (mem[addresses[i]+b] < 0) unresolved = true;
}
for (b = 0; b < 16 - width; b++) { hexes += ' '; }
result += '<pre id="' + id + '"'
// +
// ' onmouseover="return mouseover('+i+');"' +
// ' onmouseout="return mouseout('+i+');"';
if (unresolved || errors[i] != undefined) {
result += ' class="errorline" ';
}
result +=
'>' + (lengths[i] > 0 ? hex16(addresses[i]) : "");
result += '\t';
result += hexes;
if (labeltext.length > 0) {
var t = '<span class="l" id="' + labelid + '"' +
' onmouseover="return mouseovel('+i+');"' +
' onmouseout="return mouseout('+i+');"' +
'>' + labeltext + '</span>';
result += t;
}
for (b = 0; b < remainder.length && isWhitespace(remainder[b]); b++) {
result += ' ';
}
remainder = remainder.substring(b);
if (remainder.length > 0) {
result += '<span id="' + remid + '"' +
' onmouseover="return mouseover('+i+');"' +
' onmouseout="return mouseout('+i+');"' +
'>' + remainder + '</span>';
}
// hacked this into displaying only first and last lines
// of db thingies
if (len < lengths[i]) {
result += '<br/>\t. . . <br/>';
for (var subline = 1; subline*4 < lengths[i]; subline++) {
var subresult = '';
subresult += hex16(addresses[i]+subline*4) + '\t';
for (var sofs = 0; sofs < 4; sofs++) {
var adr = subline*4+sofs;
if (adr < lengths[i]) {
subresult += hex8(mem[addresses[i]+adr]) + ' ';
}
}
//result += "<br/>";
}
result += subresult + "<br/>";
}
result += '</pre>';
addr += lengths[i];
}
result += labelList();
result += "<div> </div>";
if (doHexDump) {
result += dump();
}
result += "<div> </div>";
result += intelHex();
result += "<div> </div>";
return result;
}
function error(line, text) {
errors[line] = text;
}
// last known source to compare against
var last_src = false;
// assembler main entry point
function assemble() {
var src = document.getElementById('source').value;
if (last_src == src) {
return;
}
var list = document.getElementById('list');
var savedScroll = list.scrollTop;
var lengths = Array();
var addresses = Array();
var inputlines = src.split('\n');
var addr = 0;
clearLabels();
resolveTable.length = 0;
mem.length = 0;
list.innerHTML = '';
backrefWindow = false;
references.length = 0;
textlabels.length = 0;
errors.length = 0;
doHexDump = true;
postbuild = '';
objCopy = 'gobjcopy';
for (var line = 0; line < inputlines.length; line++) {
var size = parseInstruction(inputlines[line].trim(), addr, line);
if (size <= -100000) {
addr = -size-100000;
size = 0;
} else if (size < 0) {
error(line, "syntax error");
size = -size;
}
lengths[line] = size;
addresses[line] = addr;
addr += size;
}
resolveLabelsTable();
evaluateLabels();
resolveLabelsInMem();
list.innerHTML += listing(inputlines, lengths, addresses);
list.scrollTop = savedScroll;
updateSizes();
last_src = src;
autotranslate = false;
}
function evaluateExpression(input, addr) {
var q;
var originput = input;
//console.log("input=" + input + " addr=" + addr);
try {
input = input.replace(/\$([0-9a-fA-F]+)/, '0x$1');
input = input.replace(/(^|[^'])\$|\./gi, ' '+addr+' ');
input = input.replace(/([\d\w]+)\s(shr|shl|and|or|xor)\s([\d\w]+)/gi,'($1 $2 $3)');
input = input.replace(/\b(shl|shr|xor|or|and|[+\-*\/()])\b/gi,
function(m) {
switch (m) {
case 'and':
return '&';
case 'or':
return '|';
case 'xor':
return '^';
case 'shl':
return '<<';
case 'shr':
return '>>';
default:
return m;
}
});
q = input.split(/<<|>>|[+\-*\/()\^\&]/);
} catch (e) {
return -1;
}
input = input;
var expr = '';
for (var ident = 0; ident < q.length; ident++) {
var qident = q[ident].trim();
if (-1 != resolveNumber(qident)) continue;
var addr = labels[qident];//.indexOf(qident);
if (addr != undefined) {
//addr = labels[idx+1];
if (addr >= 0) {
expr += 'var _' + qident + '=' + addr +';\n';
var rx = new RegExp('\\b'+qident+'\\b', 'gm');
input = input.replace(rx, '_' + qident);
} else {
expr = false;
break;
}
}
}
//console.log('0 input=', input);
//console.log('1 expr=', expr);
expr += input.replace(/[0-9][0-9a-fA-F]*[hbqdHBQD]|'.'/g,
function(m) {
return resolveNumber(m);
});
//console.log('expr=', expr);
try {
return eval(expr.toLowerCase());
} catch (err) {
//console.log('expr was:',expr.toLowerCase(), originput);
//console.log(err);
}
return -1;
}
function evaluateLabels() {
for (var i in labels) {
var label = labels[i];
if (label < 0 && resolveTable[-label] == undefined) {
var result = evaluateExpression(i,-1);
if (result >= 0) {
resolveTable[-label] = result;
labels[i] = undefined;
}
}
}
}
function resolveLabelsInMem() {
for (var i = 0; i < mem.length;) {
var negativeId;
if ((negativeId = mem[i]) < 0) {
newvalue = resolveTable[-negativeId];
if (newvalue != undefined) mem[i] = newvalue & 0xff;
i++;
if (mem[i] == negativeId) {
if (newvalue != undefined) mem[i] = 0xff & (newvalue >> 8);
i++;
}
} else {
i++;
}
}
}
function resolveLabelsTable(nid) {
for (var i in labels) {
var label = labels[i];
if (label < 0) {
var addr = resolveTable[-label];
if (addr != undefined) {
labels[i] = addr;
}
}
}
}
// automatic assembler launcher
var autotranslate = false;
function keypress(e) {
cock(1500);
}
function cock(timeout) {
if (autotranslate) {
clearTimeout(autotranslate);
}
autotranslate = setTimeout('assemble()', timeout);
}
function keydown(e) {
if (e.keyCode == 9) {
var obj = document.getElementById('source');
var savedScroll = obj.scrollTop;
/* Find the Start and End Position */
var start = obj.selectionStart;
var end = obj.selectionEnd;
/* Remember obj is a textarea or input field */
obj.value = obj.value.substr(0, start)
+ "\t"
+ obj.value.substr(end, obj.value.length);
obj.setSelectionRange(start+1,start+1);
obj.scrollTop = savedScroll;
return false;
}
return true;
}
scrollHistory = [];
function scrollMark(location) {
scrollHistory[scrollHistory.length] = location;
if (scrollHistory.length > 32) {
scrollHistory = scrollHistory.slice(1);
}
}
// gobak i sosak
function scrollBack() {
if (scrollHistory.length == 0) return;
var dest = scrollHistory[scrollHistory.length - 1];
scrollHistory.length = scrollHistory.length - 1;
var l = document.getElementById('list');
l.scrollTop = dest;
magicToolbar(0);
}
// -- Highlighting and navigation --
var highlightTimeout = false;
var highlightTimeout2 = false;
var highlightLabel = false;
var highlightLineNo = false;
var highlightLines = new Array();
var highlightArrow = false;
var highlightOrigin = false;
var highlightDir = false;
var highlightDelayed = false;
// backreferences window
var backrefTimeout = false;
var backrefWindow = false;
var backrefTop = 0, backrefLeft = 0;
var backrefLabel = "?";
var referencingLinesFull = [];
function startHighlighting(lineno, label) {
if (highlightTimeout == false) {
highlightLineNo = lineno;
highlightOrigin = document.getElementById('code'+lineno);
highlightTimeout = setTimeout('highlightStage1()', 500);
if (label != undefined) {
highlightLabel = label;
} else {
highlightLabel = false;
}
}
}
function scrollTo(n, dontdelay) {
if (!dontdelay) {
highlightDelayed = true;
}
var l = document.getElementById('list');
scrollMark(l.scrollTop);
l.scrollTop = n;
if (highlightOrigin) {
highlightOrigin.removeAttribute('onclick');
highlightOrigin.style.cursor = null;
}
if (highlightArrow) {
highlightOrigin.removeChild(highlightArrow);
}
}
function highlightStage1() {
//highlightLines = getReferencingLines(highlightLineNo);
if (!highlightLabel) {
highlightLabel = getReferencedLabel(highlightLineNo);
}
if (highlightLabel != undefined) {
var listElement = document.getElementById('list');
var scrollTop = listElement.scrollTop;
var height = getListHeight();
// highlightLabel would only have relative offsetTop in Opera
var labelTop = highlightLabel.parentNode.offsetTop;
var labelHeight = highlightLabel.offsetHeight;
if (highlightArrow == false && (labelTop-labelHeight) <= scrollTop) {
highlightArrow = document.createElement('span');
highlightArrow.innerHTML = '▲'; //uarr
highlightDir = 'uarr';
} else if (highlightArrow == false && labelTop > scrollTop+height) {
highlightArrow = document.createElement('span');
highlightArrow.innerHTML = '▼'; //darr
highlightDir = 'darr';
}
if (highlightArrow != false) {
highlightArrow.className = highlightDir+1;
highlightOrigin.insertBefore(highlightArrow, highlightOrigin.firstChild);
highlightArrow.style.display='inline-block';
highlightArrow.style.marginLeft ='-30px';
highlightArrow.style.paddingLeft ='15px';
highlightArrow.style.width = '15px';
highlightArrow.style.backgroundColor = 'white';
highlightOrigin.setAttribute('onclick',
'scrollTo('+(labelTop-height/2)+'); return false;');
highlightOrigin.style.cursor = 'pointer';
}
highlightLabel.className = 'highlight1';
}
//for (src = 0; src < highlightLines.length; src++) {
// highlightLines[src].className = 'srchl1';
//}
highlightTimeout = setTimeout('highlightStage2()', 50);
}
function highlightStage2() {
if (highlightLabel != undefined) {
highlightLabel.className = 'highlight2';
}
//for (src = 0; src < highlightLines.length; src++) {
// highlightLines[src].className = 'srchl2';
//}
if (highlightArrow != false) {
highlightArrow.className = highlightDir + 2;
}
highlightTimeout = setTimeout('highlightStage3()', 100);
}
function highlightStage3() {
if (highlightLabel != undefined) {
highlightLabel.className = 'highlight3';
}
//for (src = 0; src < highlightLines.length; src++) {
// highlightLines[src].className = 'srchl3';
//}
if (highlightArrow != false) {
highlightArrow.className = highlightDir + 3;
}
}
function endHighlighting(lineno) {
if (lineno == -2) {
highlightTimeout2 = setTimeout('endHighlighting(-1)', 1000);
highlightStage1();
return;
} else if (lineno == -1) {
highlightDelayed = false;
highlightTimeout2 = false;
} else {
if (highlightDelayed) {
if (highlightTimeout2 == false) {
highlightTimeout2 = setTimeout('endHighlighting(-2)', 350);
}
return;
}
}
clearTimeout(highlightTimeout);
highlightTimeout = false;
if (highlightLabel != undefined) {
highlightLabel.className = "highlight0";
highlightLabel = undefined;
}
for (src = 0; src < highlightLines.length; src++) {
highlightLines[src].className = null;//'srchl0';
}
highlightLines.length = 0;
if (highlightArrow != false) {
if (highlightArrow.parentNode != null) {
highlightArrow.parentNode.removeChild(highlightArrow);
}
highlightArrow = false;
}
if (highlightOrigin) {
highlightOrigin.removeAttribute('onclick');
highlightOrigin.style.cursor = null;
}
}
function formatBackrefText(element) {
formatBackrefText.spaces = " ";
var adr = element.innerHTML.substr(0, 4);
var label = "";
var text = "";
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes[i];
if (child.id == undefined) continue;
if (child.id.indexOf("label") == 0) {
label = child.innerHTML;
} else if (child.id.indexOf("code") == 0) {
text = child.innerHTML;
}
}
if (label.length < formatBackrefText.spaces.length) {
label += formatBackrefText.spaces.substring(label.length);
}
return [adr,label,text].join(' ').replace(/ /g,' ');
}
function showBackrefReturn(on) {
var sosak = document.getElementById('backrefgoback');
if (sosak != undefined) {
sosak.style.display= on ? 'block' : 'none';
}
return false;
}
function backrefHintLine(n) {
if (n == -1) {
if (backrefHintLine.unhint != undefined) {
backrefHintLine.unhint.className = null;
backrefHintLine.unhint = undefined;
}
return;
}
backrefHintLine(-1);
var line = document.getElementById(n);
if (line != undefined) {
backrefHintLine.unhint = line.childNodes[line.childNodes.length-1];
backrefHintLine.unhint.className = 'srchl3';
}
}
function startBackrefWindow(lineno) {
if (lineno != -1) {
highlightLines = getReferencingLines(lineno);
highlightOrigin = document.getElementById('code'+lineno);
backrefLabel = document.getElementById('label'+lineno);
setTimeout('startBackrefWindow(-1)', 250);
return;
}
if (backrefTimeout == false &&
highlightLines.length > 0) {
backrefTimeout = setTimeout('showBackref(0)', 500);
backrefLeft = backrefLabel.offsetLeft;
backrefTop = highlightOrigin.offsetTop;
backrefTop += backrefLabel.offsetHeight;
list = document.getElementById('list');
if (!inTheOpera) {
backrefTop -= document.getElementById('list').scrollTop;
}
}
}
function showBackref(n) {
if (n == 0) {
endHighlighting(0);
var list = document.getElementById('list');
var height = getListHeight();
backrefTimeout = false;
// start display
if (!backrefWindow) {
backrefWindow = document.createElement('div');
backrefWindow.id = 'backrefpopup';
backrefWindow.style.position = 'fixed';
backrefWindow.setAttribute("onmouseover",
"clearTimeout(backrefTimeout);backrefTimeout=false;return false;");
backrefWindow.setAttribute("onmouseout",
"showBackref(-1);return false;");
document.getElementById('list').appendChild(backrefWindow);
}
backrefWindow.style.left = backrefLeft + 'px';
backrefWindow.style.top = backrefTop + 'px';
backrefWindow.style.display = 'block';
backrefWindow.innerHTML = '';
for (var src = 0; src < referencingLinesFull.length; src++) {
var labelTop = referencingLinesFull[src].offsetTop;
var text = formatBackrefText(referencingLinesFull[src]);
var scrollTo = labelTop - backrefTop + 15;
backrefWindow.innerHTML +=
'<div onclick="scrollTo('+scrollTo+');' +
'backrefHintLine(\'' + referencingLinesFull[src].id +'\');' +
'showBackrefReturn(1);' +
'return false;"' +
' class="brmenuitem" ' +
'>' +
text +
'</div>';
}
// append return
var returnTo = list.scrollTop;
backrefWindow.innerHTML +=
'<div id="backrefgoback" onclick="scrollTo(' +returnTo+ ');'+
'showBackref(-1);return false;"' +
' class="brmenuitem" ' +
' style="border-top:1px solid black;font-size:120%;">' +
' ◀ ' + backrefLabel.innerHTML +
'</div>';
showBackrefReturn(0);
backrefWindow.style.opacity = 0;
showBackref.opacity = 0;
showBackref(1);
}
if (n == 1) {
if (backrefWindow.style.opacity == 0.9) {
backrefTimeout = false;
} else {
showBackref.opacity += .3;
backrefWindow.style.opacity = showBackref.opacity;
setTimeout('showBackref(1);', 50);
}
}
// start hiding
if (n == -1) {
clearTimeout(backrefTimeout);
backrefTimeout = setTimeout('showBackref(-2)', 100);
backrefHintLine(-1);
}
if (n == -2) {
clearTimeout(backrefTimeout);
backrefTimeout = false;
if (backrefWindow != false) {
backrefWindow.style.display = 'none';
}
}
return false;
}
function mouseover(lineno) {
startHighlighting(lineno);
return false;
}
function mouseovel(lineno) {
startBackrefWindow(lineno);
return false;
}
function mouseout(lineno) {
endHighlighting(lineno);
showBackref(-1);
return false;
}
function boo() {
document.write('<h1>Unfortunately…</h1>');
document.write('<p>The <b>Pretty i8080 Assembler</b> only works in Internet Browsers.</p>');
document.write('<p>You\'re using Microsoft Internet Explorer, which was called an "internet browser" by mistake.</p>');
document.write('<p>Please upgrade and come back with a Firefox, Iceweasel, Konqueror, Safari, Chrome or even Opera.</p>');
document.write('<p>Or try b2m\'s <a href="http://bashkiria-2m.narod.ru/i8080.html">Good i8080 Assembler.</p>');
}
function loaded() {
if (navigator.appName == 'Microsoft Internet Explorer' ||
navigator.appVersion.indexOf('MSIE') != -1) {
boo();
return false;
}
i18n();
updateSizes();
cock(100);
}
function updateSizes() {
height = window.innerHeight - 95;
var ti = document.getElementById('source');
ti.style.height = height + "px";
var to = document.getElementById('list');
to.style.height = height + "px";
}
// toolbar
var toolbarOpacity = 0;
var toolbarTimeout = false;
function magicToolbar(n) {
var tulba = document.getElementById('toolbar');
if (n == 0) {
if (scrollHistory.length == 0) {
// force hiding if no history
n = 2;
} else {
tulba.style.cursor = 'pointer';
clearTimeout(toolbarTimeout);
toolbarTimeout = setTimeout('magicToolbar(1);', 100);
}
}
if (n == 1) {
toolbarOpacity += 0.2;
tulba.style.opacity = toolbarOpacity;
if (Math.abs(toolbarOpacity - 1.0) > 0.05) {
clearTimeout(toolbarTimeout);
toolbarTimeout = setTimeout('magicToolbar(1);', 50);
}
}
if (n == 2) {
tulba.style.cursor = 'default';
if (toolbarOpacity > 0) {
clearTimeout(toolbarTimeout);
toolbarTimeout = setTimeout('magicToolbar(3);', 50);
}
}
if (n == 3) {
toolbarOpacity -= 0.4;
if (toolbarOpacity < 0) toolbarOpacity = 0;
tulba.style.opacity = toolbarOpacity;
if (toolbarOpacity > 0) {
clearTimeout(toolbarTimeout);
toolbarTimeout = setTimeout('magicToolbar(3);', 50);
}
}
}
// -- i18n --
var languages =
{
"en":["Pretty i8080 Assembler", "Make Beautiful Code"],
"se":["Fin i8080 assembler", "Översätt den snygga"],
"ru":["Прекрасный ассемблер КР580ВМ80А", "Транслировать прелесть"],
"uk":["Прекрасний асемблер КР580ВМ80А", "Транслювати принаду"],
"es":["Bonito ensamblador de i8080", "Crear código precioso"],
"nl":["Fraaie i8080 Assembler", "Vertaal dit juweel"],
"de":["Schöne i8080 Assembler","Übersetze diese Schatz"],
"fi":["Sievä i8080 assembleri", "Tee kaunista koodia"],
"dk":["Smuk i8080 Assembler","Skriv pæn kode"],
"cz":["Dobrý i8080 Assembler","Kompilaci drahé"],
"tr":["Temiz montajcı kodu i8080","Güzel kodu yapmak"],
"ja":["美しい i8080アセンブラ","美しい コードをしよう"],
"he":["i8080 יופי של שפת סף","לקודד יופי של קידוד"],
// Persian translation by Ali Asadzadeh, thanks Ali!
"fa":["یک اسمبلر جالب برای i8080","کامپایل کردن این کد زیبا"]
};
function i18n() {
var lang = navigator.language;
if (lang != undefined) lang = lang.split('-')[0];
explang = document.URL.split('?')[1];
if (explang != undefined) lang = explang;
messages = languages[lang];
if (messages == undefined) messages = languages["en"];
var m_header = messages[0];
var m_button = messages[1];
var header = document.getElementById('header');
var baton = document.getElementById('baton');
header.innerHTML = m_header;
//baton.innerHTML = m_button;
baton.value = m_button;
if (lang == 'he' || lang == 'fa') {
header.style.textAlign = 'right';
baton.style.cssFloat = 'right';
baton.style.clear = 'right';
document.getElementById('buttbr').style.cssFloat = 'right';
document.getElementById('ta').style.cssFloat = 'right';
document.getElementById('list').style.cssFloat = 'left';
}
}
-->
</script>
<style type="text/css" media="screen">
html { }
body {font-size:small; background-color: white; }
.hordiv { width:80%; text-align: left; border-bottom:2px solid black;}
.code { font-family: monospace; }
pre { margin-top:0ex; margin-bottom:0ex; }
.highlight0 { border:none; background-color: transparent; }
.highlight1 { background-color: wheat; border: 1px dotted red; margin:-1px -1px -1px -1px;}
.highlight2 { background-color: white; border: 2px solid red; margin:-2px -2px -2px -2px;}
.highlight3 { background-color: yellow; border: 1px solid pink; margin:-1px -1px -1px -1px;}
.srchl0 { border:none; background-color: transparent; }
.srchl1 { background-color: wheat; border: 1px dotted red; margin:-1px -1px -1px -1px;}
.srchl2 { background-color: white; border: 2px solid red; margin:-2px -2px -2px -2px;}
.srchl3 { background-color: yellow; border: 1px solid pink; margin:-1px -1px -1px -1px;}
.errorline { background-color: pink; }
.d1 { padding-right:0.5em; background-color: #f0f0f0; display:inline;}
.d0 { padding-right:0.5em; background-color: #e0ffff; display:inline;}
#header {
font-family:Gill Sans, Franklin Gothic Medium, sans-serif;
font-size:150%;
font-weight:bold;
background-color:maroon;
color:white;
padding:5px 5px 2px 5px;
border-bottom: 1px dotted darkgray;
user-select:none;
-moz-user-select: none;
-khtml-user-select: none;
}
#toolbar {
float:right;
font-family:Gill Sans, Franklin Gothic Medium, sans-serif;
font-size:150%;
font-weight:bold;
background:transparent;
color:white;
padding:5px 1em 2px 1em;
opacity:0;
user-select:none;
-moz-user-select: none;
-khtml-user-select: none;
}
#baton {
cursor:pointer;
font-family:Gill Sans, Franklin Gothic Medium, sans-serif;
border: 1px solid black; display: inline-block;
font-weight:bold;
font-variant: small-caps;
letter-spacing: 0.4ex;
padding-left:10px;
padding-right:10px;
padding-top:2px;
color: white;
background-color: black;
color: white;
background-color: black;
margin-top:0.65ex;
user-select:none;
-moz-user-select: none;
-khtml-user-select: none;
}
#textinput {
overflow:hidden;
white-space:nowrap;}
#source {
font-family:monospace; font-size:100%;
margin:0px 0px 0px 0px;
padding:0px 0px 0px 5px;
width:100%;
border: 1px solid black;
border-top: 1px dotted darkgray;
}
#ta {
display: inline-block;
float:left;
width:39%;
direction:ltr;
}
#list {
direction:ltr;
float:right;
border: 1px solid black;
border-top: 1px dotted darkgray;
vertical-align:top; width:40em;
padding-left:1ex;
overflow:scroll;
display:inline-block;
width:60%;
}
.uarr1 {
top:2px;
font-family:monospace; font-size:90%; font-weight:bold; position:relative;
color:#fff0f0;
opacity:0;
}
.uarr2{
top:1px;
font-family:monospace; font-size:90%; font-weight:bold; position:relative;
color:#ffa0a0;
opacity:0.4;
}
.uarr3 {
top:0px;
font-family:monospace; font-size:90%; font-weight:bold; position:relative;
color:#b00000;
opacity:0.8;
}
.darr1 {
font-family:monospace; font-size:90%; font-weight:bold; position:relative;
color:#fff0f0;
opacity:0;
}
.darr2{
font-family:monospace; font-size:90%; font-weight:bold; position:relative;
color:#ffa0a0;
opacity:0.4;
}
.darr3 {
font-family:monospace; font-size:90%; font-weight:bold; position:relative;
top:1px;
color:#b00000;
opacity:0.8;
}
#backrefpopup {
border:2px solid #ffff7d;
background:#ffff7d;
font-size:75%;
font-family:monospace;
opacity:0.9;
max-width:30em;
overflow:hidden;
}
.brmenuitem {
background:transparent;
cursor:pointer;
user-select:none;
-moz-user-select: none;
-khtml-user-select: none;
}
.brmenuitem:hover {
background:maroon;
color:white;
}
span.l {
cursor:default;
}
span.t1 {
padding-right:1em;padding-left:0.4em;
border-right:1px solid lightgray;
cursor:pointer;
}
span.t2 {
padding-right:1em;padding-left:0.4em;
cursor:pointer;
}
span.t1:hover { background:#ffff7d; }
span.t2:hover { background:#ffff7d; }
</style>
</head>
<body id='main' onload="loaded(); return false;" onresize="updateSizes(); return false;">
<div id="toolbar"
onclick="scrollBack(); return false;"
onmouseover="magicToolbar(0); return false;"
onmouseout="magicToolbar(2); return false;"
>
◀
</div>
<div id="header"
onmouseover="magicToolbar(0); return false;"
onmouseout="magicToolbar(2); return false;"
>
Прекрасный ассемблер КР580ВМ80А
</div>
<div id="textinput">
<div id="list">…</div>
<div id="ta">
<textarea
onkeydown="return keydown(event);"
onkeypress="keypress(event);return true;" id="source" rows="30">
; i8080 assembler code
.hexfile test.hex
.binfile test.bin
.objcopy gobjcopy
.postbuild echo "Hooray!"
;.nodump
bdos equ 5
intv equ 38h
.org 100h
jmp begin
db 27
msg:
db 'Assembled by Pretty i8080 Assembler',0dh,0ah,'$'
yeah:
db 'Hoorj!',0dh,0ah,'$'
begin:
lxi d, msg
mvi c, 9
call bdos
call delay
mvi c, 9
lxi d, yeah
call bdos
ret
delay:
mvi a, 33
hlt
dcr a
jnz .-2
ret
</textarea></div>
</div>
<div id="messagepane">
</div>
<br/>
<div id="buttbr" class="hordiv"></div>
<!--div id="baton" onclick="assemble(); return false;">Транслировать прелесть</div-->
<form action="hex2bin.cgi" method="post">
<input type="text" id="hex" name="hex" style="display:none;">
<input type="text" id="formbinname" name="formbinname" style="display:none;">
<input type="submit" id="baton" name="baton" value="Translate">
</form>
<!--div id="debug">idebug</div-->
</body>
</html>
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]