Добавлен проект

This commit is contained in:
Victor 2014-03-16 23:33:45 +02:00
commit 36ab1b7c05
42 changed files with 3268 additions and 0 deletions

95
build.xml Normal file
View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="AsmCompiler" default="default" basedir=".">
<description>Builds, tests, and runs the project AsmCompiler.</description>
<import file="nbproject/build-impl.xml"/>
<target name="-post-jar">
<property name="proguard.jar.path" value="E:/SETUPS/Disk/Programming/Java/proguard4.9beta1/lib/proguard.jar" />
<property name="java.home.path" value="D:/Program Files (x86)/Java/jdk1.7.0_09" />
<echo message="Obfuscating ${dist.jar}..." />
<mkdir dir="${build.dir}/obfuscated" />
<taskdef resource="proguard/ant/task.properties"
classpath="${proguard.jar.path}" />
<proguard configuration="E:/SETUPS/Disk/Programming/Java/java.pro">
<injar file="${dist.jar}" />
<outjar file="${build.dir}/obfuscated/AsmCompiler_obf.jar" />
<libraryjar path="${javac.classpath}" />
<libraryjar file="${java.home.path}/jre/lib/rt.jar" />
</proguard>
</target>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar-with-manifest: JAR building (if you are using a manifest)
-do-jar-without-manifest: JAR building (if you are not using a manifest)
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="AsmCompiler-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

1413
nbproject/build-impl.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
build.xml.data.CRC32=3cfaa78e
build.xml.script.CRC32=02db17b9
build.xml.stylesheet.CRC32=28e38971@1.56.0.46
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=3cfaa78e
nbproject/build-impl.xml.script.CRC32=ad298f4f
nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.0.48

View File

@ -0,0 +1,73 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
application.title=AsmCompiler
application.vendor=aNNiMON
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.test.classpath=\
${run.test.classpath}
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/AsmCompiler.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
includes=**
jar.compress=false
javac.classpath=
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.processorpath=\
${javac.classpath}
javac.source=1.7
javac.target=1.7
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
main.class=com.annimon.asm.Main
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
run.jvmargs=
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
source.encoding=windows-1251
src.dir=src
test.src.dir=test

15
nbproject/project.xml Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>AsmCompiler</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>

View File

@ -0,0 +1,141 @@
package com.annimon.asm;
import com.annimon.asm.exceptions.ExceptionWithLineNumber;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.Utilities;
/**
* Îñíîâíàÿ ïàíåëü ïðèëîæåíèÿ.
* @author aNNiMON
*/
public class AnalyzerPanel extends JPanel {
private JLabel lineNumberLabel;
private JTextPane textPane;
private JButton analyzeButton;
public AnalyzerPanel() {
lineNumberLabel = new JLabel("Line: 1");
initTextPane();
initAnalyzeButton();
setLayout(new BorderLayout(0, 0));
add(lineNumberLabel, BorderLayout.NORTH);
add(new JScrollPane(textPane));
add(analyzeButton, BorderLayout.SOUTH);
}
public AnalyzerPanel(String text) {
textPane = new JTextPane();
textPane.setPreferredSize(new Dimension(300, 300));
textPane.setFont(new Font("Consolas", Font.PLAIN, 14));
textPane.setText(text);
setLayout(new BorderLayout(0, 0));
add(new JScrollPane(textPane));
}
private void initTextPane() {
textPane = new JTextPane();
textPane.setPreferredSize(new Dimension(300, 350));
textPane.addCaretListener(new CaretListener() {
@Override
public void caretUpdate(CaretEvent caretEvent) {
// Âûâåñòè ïîçèöèþ êàðåòêè â lineNumberLabel.
JTextComponent textComponent = (JTextComponent)caretEvent.getSource();
int pos = caretEvent.getDot();
int rowNumber = (pos == 0) ? 1 : 0;
try {
int offset = pos;
while (offset > 0) {
offset = Utilities.getRowStart(textComponent, offset) - 1;
rowNumber++;
}
} catch (BadLocationException ex) {}
lineNumberLabel.setText("Line: " + rowNumber);
}
});
textPane.setFont(new Font("Consolas", Font.PLAIN, 14));
textPane.setText(getTextFromResource("/test.asm"));
}
private void initAnalyzeButton() {
analyzeButton = new JButton("Analyze");
analyzeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
String allText = textPane.getText();
// Ëåêñè÷åñêèé àíàëèç - ïåðåâîäèì íàáîð äèðåêòèâ â êîäîâóþ òàáëèöó.
LexicalAnalyzer lex = new LexicalAnalyzer(allText);
lex.analyze();
// Ñèíòàêñè÷åñêèé àíàëèç - ïðîâåðêà ïðàâèëüíîñòè íàáîðà äèðåêòèâ.
SyntaxAnalyzer syn = new SyntaxAnalyzer(lex.getLexicTable());
try {
syn.analyze();
} catch (ExceptionWithLineNumber ex) {
showMessageBox("Error", ex.getMessage(), true);
return;
}
//showMessageBox("Info","Source is correct!", false);
// Ãåíåðèðóåì ëèñòèíã.
ListingGenerator gen = new ListingGenerator(lex.getLexicTable(), lex.getVarTable(), lex.getLines());
String listing = gen.generate();
showListing(listing);
}
});
}
private void showMessageBox(String title, String text, boolean errorMessage) {
int msgType = errorMessage ? JOptionPane.ERROR_MESSAGE :
JOptionPane.INFORMATION_MESSAGE;
JOptionPane.showMessageDialog(this, text, title, msgType);
}
private void showListing(String listing) {
JOptionPane.showMessageDialog(this, new AnalyzerPanel(listing),
"Listing", JOptionPane.PLAIN_MESSAGE);
}
private String getTextFromResource(String res) {
try {
InputStream is = getClass().getResourceAsStream(res);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder text = new StringBuilder();
String line;
while( (line = reader.readLine()) != null ) {
text.append(line).append(System.lineSeparator());
}
reader.close();
return text.toString();
} catch (IOException ex) {}
return "";
}
}

View File

@ -0,0 +1,99 @@
package com.annimon.asm;
import com.annimon.asm.directives.*;
import java.util.ArrayList;
/**
* Êîíâåðòåð äèðåêòèâ.
* @author aNNiMON
*/
public class DirectiveConverter {
private static final Directive[] DIRECTIVES = {
new Comma(),
new DB(), new DW(),
new InfinityValue(), new ByteValue(), new WordValue(),
new Add(), new Mul(), new Push(), new Pop(), new Idiv(),
new ByteRegister(), new WordRegister(),
new Variable()
};
/**
* Êîíâåðòèðîâàòü äèðåêòèâó â å¸ èäåíòèôèêàòîð.
* @param text äèðåêòèâà (dw, push, 0 è ò.ä.)
* @return èäåíòèôèêàòîð äèðåêòèâû.
*/
public static int convert(String text) {
for (int i = 0; i < DIRECTIVES.length; i++) {
if (DIRECTIVES[i].isDirective(text)) {
return DIRECTIVES[i].getId();
}
}
return -1;
}
/**
* Ïîëó÷èòü çíà÷åíèå ÷èñëà èç ñòðîêè.
* @param text ñòðîêà ñ ÷èñëîì.
* @return Integer ÷èñëî.
*/
public static Integer parseInteger(String text) {
int value;
if (text.toLowerCase().endsWith("h")) {
char first = Character.toLowerCase(text.charAt(0));
if ( (first >= 'a') && (first <= 'f') ) {
// Ïåðâûé ñèìâîë hex ÷èñëà íå äîëæåí áûòü îò a äî f
return null;
}
String hex = text.substring(0, text.length() - 1);
try {
value = Integer.parseInt(hex, 16);
return value;
} catch (NumberFormatException nfe) {
return null;
}
}
try {
value = Integer.parseInt(text);
return value;
} catch (NumberFormatException nfe) {
return null;
}
}
/**
* Ðàçáèòü ñòðîêó íà ïîäñòðîêè ñ ó÷¸òîì çàïÿòûõ.
* @param text òåêñò.
* @return ìàññèâ ñòðîê.
*/
public static String[] split(String text) {
ArrayList<String> parts = new ArrayList<>();
int length = text.length();
StringBuilder sb = new StringBuilder();
for (int ch = 0; ch < length; ch++) {
int i = text.charAt(ch);
if (i == ' ') {
if (sb.length() > 0) {
parts.add(sb.toString().trim());
sb.setLength(0);
}
} else if (i == ',') {
if (sb.length() > 0) {
parts.add(sb.toString().trim());
parts.add(",");
sb.setLength(0);
}
} else {
sb.append((char) i);
}
}
if (sb.length() > 0) {
parts.add(sb.toString().trim());
}
String[] m = new String[parts.size()];
m = parts.toArray(m);
return m;
}
}

View File

@ -0,0 +1,16 @@
package com.annimon.asm;
/**
*
* @author aNNiMON
*/
public class LexicLine {
int lineNumber;
int[] line;
public LexicLine(int lineNumber, int[] line) {
this.lineNumber = lineNumber;
this.line = line;
}
}

View File

@ -0,0 +1,47 @@
package com.annimon.asm;
import java.util.ArrayList;
/**
* @author aNNiMON
*/
public class LexicTable {
private ArrayList<LexicLine> lexicLines;
public LexicTable() {
lexicLines = new ArrayList<>();
}
public void addLexicLine(int lineNumber, int[] line) {
lexicLines.add(new LexicLine(lineNumber, line));
}
public int getSize() {
return lexicLines.size();
}
public LexicLine getLexicAt(int pos) {
return lexicLines.get(pos);
}
public void updateLexicAt(int pos, LexicLine line) {
lexicLines.set(pos, line);
}
@Override
public String toString() {
StringBuilder text = new StringBuilder();
for(LexicLine lexic : lexicLines) {
text.append(lexic.lineNumber).append(':');
int[] array = lexic.line;
for (int i = 0; i < array.length; i++) {
text.append(" ");
text.append(array[i]);
}
text.append(System.lineSeparator());
}
return text.toString();
}
}

View File

@ -0,0 +1,112 @@
package com.annimon.asm;
import com.annimon.asm.directives.ID;
/**
* Ëåêñè÷åñêèé àíàëèçàòîð.
* @author aNNiMON
*/
public class LexicalAnalyzer {
private String[] lines;
private LexicTable lexicTable;
private VarTable varTable;
public LexicalAnalyzer(String text) {
lines = text.split(System.lineSeparator());
lexicTable = new LexicTable();
varTable = new VarTable();
}
public LexicTable getLexicTable() {
return lexicTable;
}
public VarTable getVarTable() {
return varTable;
}
public String[] getLines() {
return lines;
}
public void analyze() {
for (int i = 0; i < lines.length; i++) {
analyzeLine(i);
}
analyzeVariables();
System.out.println(lexicTable.toString());
}
private void analyzeLine(int lineNumber) {
String line = lines[lineNumber];
// Åñëè ñòðîêà íå ñîäåðæèò êîìàíä - ïðîïóñêàåì.
if (line.isEmpty()) return;
String[] parts = DirectiveConverter.split(line);
// Êîíâåðòèðóåì äèðåêòèâû â ñîîòâåòñòâóþùèå èì èäåíòèôèêàòîðû.
int length = parts.length;
int[] lexicIds = new int[length];
for (int i = 0; i < length; i++) {
lexicIds[i] = DirectiveConverter.convert(parts[i].trim());
}
// Äîáàâëÿåì ïîëó÷åííóþ ñòðîêó â òàáëèöó.
lexicTable.addLexicLine(lineNumber+1, lexicIds);
}
private void analyzeVariables() {
// Äîáàâëÿåì ïåðåìåííûå â òàáëèöó.
for (int i = 0; i < lexicTable.getSize(); i++) {
LexicLine line = lexicTable.getLexicAt(i);
addVariableToTable(line);
}
// Ñîïîñòàâëÿåì òèïû ïåðåìåííûõ.
for (int i = 0; i < lexicTable.getSize(); i++) {
LexicLine line = lexicTable.getLexicAt(i);
line = assignVariableTypes(line);
lexicTable.updateLexicAt(i, line);
}
}
private void addVariableToTable(LexicLine lexicLine) {
if (lexicLine.line[0] != ID.VAR) return;
if (lexicLine.line.length != 3) return;
String line = lines[lexicLine.lineNumber - 1];
// Åñëè ñòðîêà íå ñîäåðæèò êîìàíä - ïðîïóñêàåì.
if (line.isEmpty()) return;
String[] parts = DirectiveConverter.split(line);
if (parts.length != 3) return;
String varName = parts[0].trim();
int typeId = lexicLine.line[1];
String value = parts[2].trim();
varTable.addVariable(varName, typeId, value);
}
private LexicLine assignVariableTypes(LexicLine lexicLine) {
// Äëÿ ïåðåìåííûõ íàçíà÷àåì èõ òèï.
int[] lexicIds = lexicLine.line;
for (int i = 0; i < lexicIds.length; i++) {
if (lexicIds[i] == ID.VAR) {
// Íàçíà÷àåì òèï èç òàáëèöû ïåðåìåííûõ.
String line = lines[lexicLine.lineNumber - 1];
if (line.isEmpty()) continue;
String[] parts = DirectiveConverter.split(line);
if (parts.length != lexicIds.length) continue;
lexicIds[i] = varTable.getTypeOfVariable(parts[i]);
}
}
lexicLine.line = lexicIds;
return lexicLine;
}
}

View File

@ -0,0 +1,107 @@
package com.annimon.asm;
import com.annimon.asm.directives.ID;
/**
*
* @author aNNiMON
*/
public class ListingGenerateHelper {
private static final String[] SEGMENT_REGISTERS = {
"es", "cs", "ss", "ds"
};
/**
* Êîíâåðòèðîâàòü ÷èñëî â ñòðîêó â HEX ôîðìàòå.
* @param value ÷èñëî
* @return ñòðîêîâîå çíà÷åíèå ÷èñëà â HEX.
*/
public static String toHexString(int value) {
String str = Integer.toString(value, 16).toUpperCase();
if (str.length() == 1) str = "0" + str;
else if (str.length() == 3) str = "0" + str;
return str;
}
/**
* Çàïèñàòü ñëîâî â Little Endian ôîðìàòå.
* @param value ÷èñëî 2 áàéòà.
* @return ñòðîêà "LL HH"
*/
public static String toLittleEndianString(short value) {
return toHexString(value & 0xFF) + " " +
toHexString((value >> 8) & 0xFF);
}
/**
* Ñêîíâåðòèðîâàòü òåêñò â ñëîâî è çàïèñàòü åãî â Little Endian ôîðìàòå.
* @param text ñòðîêà ñ ÷èñëîì.
* @return ñòðîêà "LL HH" ëèáî "XX" â çàâèñèìîñòè îò ðàçìåðà.
*/
public static String toLittleEndianString(String text) {
Integer value = DirectiveConverter.parseInteger(text);
if (value == null) return "";
if ( (-128 <= value.intValue()) && (value.intValue() <= 255) ) {
return toHexString(value);
}
return toLittleEndianString(value.shortValue());
}
/**
* Ïðîâåðêà, ÿâëÿåòñÿ ëè îïåðàíä ðåãèñòðîì èëè ïàìÿòüþ (r/m).
* @param id òèï îïåðàíäà.
* @return true - îïåðàíä ðåãèñòð èëè ïàìÿòü.
*/
public static boolean isRegisterOrMemory(int id) {
boolean isMemory = ( (id == ID.VAR_BYTE) || (id == ID.VAR_WORD) );
return (isRegister(id) || isMemory);
}
/**
* Ïðîâåðêà, ÿâëÿåòñÿ ëè îïåðàíä ðåãèñòðîì.
* @param id òèï îïåðàíäà.
* @return true - îïåðàíä ðåãèñòð.
*/
public static boolean isRegister(int id) {
return ( (id == ID.REGISTER_BYTE) || (id == ID.REGISTER_WORD) );
}
/**
* Ïîëó÷èòü êîä ðåãèñòðà.
* @param register ñòðîêîâîå çíà÷åíèå ðåãèñòðà.
* @return êîä ðåãèñòðà (3 áèòà).
*/
public static byte getRegisterCode(String register) {
String reg = register.toLowerCase();
switch (reg) {
case "ax": case "al": return 0b000;
case "cx": case "cl": return 0b001;
case "dx": case "dl": return 0b010;
case "bx": case "bl": return 0b011;
case "sp": case "ah": return 0b100;
case "bp": case "ch": return 0b101;
case "si": case "dh": return 0b110;
case "di": case "bh": return 0b111;
}
return 0;
}
/**
* Ïîëó÷èòü êîä ñåãìåíòíîãî ðåãèñòðà.
* @param register ñòðîêîâîå çíà÷åíèå ðåãèñòðà.
* @return êîä ñåãìåíòíîãî ðåãèñòðà, ëèáî -1, åñëè ðåãèñòð íå ñåãìåíòíûé.
*/
public static byte getSegmentRegisterCode(String register) {
String reg = register.toLowerCase();
for (byte i = 0; i < SEGMENT_REGISTERS.length; i++) {
if (reg.equals(SEGMENT_REGISTERS[i])) return i;
}
return -1;
}
}

View File

@ -0,0 +1,63 @@
package com.annimon.asm;
import com.annimon.asm.directives.Add;
import com.annimon.asm.directives.ID;
import com.annimon.asm.directives.IListingGenerator;
import com.annimon.asm.directives.Idiv;
import com.annimon.asm.directives.Mul;
import com.annimon.asm.directives.Pop;
import com.annimon.asm.directives.Push;
import com.annimon.asm.directives.Variable;
/**
* Ãåíåðèðîâàíèå ëèñòèíãà.
* @author aNNiMON
*/
public class ListingGenerator {
private static final IListingGenerator[] DIRECTIVES = {
new Variable(),
new Add(), new Mul(), new Pop(), new Push(), new Idiv()
};
private LexicTable lexicTable;
private VarTable varTable;
private String[] lines;
public ListingGenerator(LexicTable lexicTable, VarTable varTable, String[] lines) {
this.lexicTable = lexicTable;
this.varTable = varTable;
this.lines = lines;
}
public String generate() {
StringBuilder text = new StringBuilder();
short offset = VarTable.getOffset();
for (int i = 0; i < lexicTable.getSize(); i++) {
LexicLine line = lexicTable.getLexicAt(i);
String[] parts = DirectiveConverter.split(lines[line.lineNumber - 1]);
String strLine = generateLine(line.lineNumber, line.line, parts).trim();
// Âûâîä àäðåñà äëÿ âñåõ äèðåêòèâ, êðîìå ïåðåìåííûõ.
if ( (line.line[0] != ID.VAR_BYTE) && (line.line[0] != ID.VAR_WORD) ) {
text.append(ListingGenerateHelper.toHexString(offset)).append(" ");
int lineSize = strLine.split(" ").length;
offset += lineSize;
}
text.append(strLine);
text.append(System.lineSeparator());
}
return text.toString();
}
private String generateLine(int lineNumber, int[] lexic, String[] parts) {
for (int i = 0; i < DIRECTIVES.length; i++) {
String text = DIRECTIVES[i].generate(lineNumber, lexic, parts, varTable);
if (!text.isEmpty()) return text;
}
return "";
}
}

View File

@ -0,0 +1,22 @@
package com.annimon.asm;
import javax.swing.JFrame;
/**
* @author aNNiMON
*/
public class Main extends JFrame {
public static void main(String[] args) {
new Main().setVisible(true);
}
public Main() {
super("Assembler analyzer");
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(new AnalyzerPanel());
pack();
}
}

View File

@ -0,0 +1,44 @@
package com.annimon.asm;
import com.annimon.asm.directives.Add;
import com.annimon.asm.directives.Mul;
import com.annimon.asm.directives.Pop;
import com.annimon.asm.directives.Push;
import com.annimon.asm.directives.ISyntaxChecker;
import com.annimon.asm.directives.Idiv;
import com.annimon.asm.directives.Variable;
import com.annimon.asm.exceptions.ExceptionWithLineNumber;
/**
* Ñèíòàêñè÷åñêèé àíàëèçàòîð.
* @author aNNiMON
*/
public class SyntaxAnalyzer {
private static final ISyntaxChecker[] DIRECTIVES = {
new Add(), new Mul(), new Push(), new Pop(), new Idiv(),
new Variable()
};
private LexicTable lexicTable;
public SyntaxAnalyzer(LexicTable lexicTable) {
this.lexicTable = lexicTable;
}
public void analyze() throws ExceptionWithLineNumber {
for (int i = 0; i < lexicTable.getSize(); i++) {
LexicLine line = lexicTable.getLexicAt(i);
analyzeLine(line.lineNumber, line.line);
}
}
private void analyzeLine(int lineNumber, int[] lexic) throws ExceptionWithLineNumber {
for (int i = 0; i < DIRECTIVES.length; i++) {
if (DIRECTIVES[i].check(lineNumber, lexic)) {
return;
}
}
}
}

View File

@ -0,0 +1,25 @@
package com.annimon.asm;
/**
* Êëàññ ïåðåìåííîé.
* @author aNNiMON
*/
public class Var {
String name;
int type;
String value;
short addr;
public Var(String name, int type, String value, short addr) {
this.name = name;
this.type = type;
this.value = value;
this.addr = addr;
}
public short getAddress() {
return addr;
}
}

View File

@ -0,0 +1,52 @@
package com.annimon.asm;
import com.annimon.asm.directives.ID;
import java.util.ArrayList;
/**
* Òàáëèöà ïåðåìåííûõ.
* @author aNNiMON
*/
public class VarTable {
private ArrayList<Var> variables;
private static short offset;
public VarTable() {
variables = new ArrayList<>();
offset = 0x103;
}
public static short getOffset() {
return offset;
}
public void addVariable(String name, int type, String value) {
addVariable(name, type, value, offset);
int size = (type == ID.DW) ? 2 : 1;
offset += size;
}
public void addVariable(String name, int type, String value, short address) {
variables.add(new Var(name, type, value, address));
}
public int getTypeOfVariable(String name) {
for (Var var : variables) {
if (var.name.equalsIgnoreCase(name)) {
if (var.type == ID.DB) return ID.VAR_BYTE;
else if (var.type == ID.DW) return ID.VAR_WORD;
}
}
return ID.VAR;
}
public short getAddressOfVariable(String name) {
for (Var var : variables) {
if (var.name.equalsIgnoreCase(name)) {
return var.getAddress();
}
}
return ID.VAR;
}
}

View File

@ -0,0 +1,167 @@
package com.annimon.asm.directives;
import com.annimon.asm.ListingGenerateHelper;
import com.annimon.asm.VarTable;
import com.annimon.asm.exceptions.CommaExpectedException;
import com.annimon.asm.exceptions.ExceptionWithLineNumber;
import com.annimon.asm.exceptions.FewArgumentsException;
import com.annimon.asm.exceptions.TooManyArgumentsException;
import com.annimon.asm.exceptions.WrongArgumentException;
/**
*
* @author aNNiMON
*/
public class Add extends Directive implements ISyntaxChecker, IListingGenerator {
public Add() {
super("add", ID.ADD);
}
@Override
public boolean check(int lineNumber, int[] ids) throws ExceptionWithLineNumber {
if (ids[0] != getId()) return false;
if (ids.length < 4) throw new FewArgumentsException(lineNumber);
else if (ids.length > 4) throw new TooManyArgumentsException(lineNumber);
if (ids[2] != ID.COMMA) throw new CommaExpectedException(lineNumber);
int[] pairs = new int[] {
// Ðåãèñòð - Ðåãèñòð
ID.REGISTER_BYTE, ID.REGISTER_BYTE,
ID.REGISTER_WORD, ID.REGISTER_WORD,
ID.REGISTER_WORD, ID.REGISTER_BYTE,
// Ðåãèñòð - Ïàìÿòü
ID.REGISTER_BYTE, ID.VAR_BYTE,
ID.REGISTER_WORD, ID.VAR_WORD,
ID.REGISTER_WORD, ID.VAR_BYTE,
// Ïàìÿòü - Ðåãèñòð
ID.VAR_BYTE, ID.REGISTER_BYTE,
ID.VAR_WORD, ID.REGISTER_WORD,
ID.VAR_WORD, ID.REGISTER_BYTE,
// Ðåãèñòð - Çíà÷åíèå
ID.REGISTER_BYTE, ID.NUMBER_BYTE,
ID.REGISTER_WORD, ID.NUMBER_WORD,
ID.REGISTER_WORD, ID.NUMBER_BYTE,
// Ïàìÿòü - Çíà÷åíèå
ID.VAR_BYTE, ID.NUMBER_BYTE,
ID.VAR_WORD, ID.NUMBER_WORD,
ID.VAR_WORD, ID.NUMBER_BYTE,
};
boolean correct = false;
for(int i = 0; i < pairs.length; i += 2) {
if ( (ids[1] == pairs[i]) && (ids[3] == pairs[i+1]) ) {
correct = true;
break;
}
}
if (!correct) throw new WrongArgumentException(lineNumber);
return true;
}
@Override
public String generate(int lineNumber, int[] ids, String[] strs, VarTable vars) {
if (ids[0] != getId()) return "";
StringBuilder sb = new StringBuilder();
if ( (ids[3] == ID.NUMBER_BYTE) || (ids[3] == ID.NUMBER_WORD) ) {
// ADD acc, imm 0000010w | data
if ( (strs[1].equalsIgnoreCase("ax")) || (strs[1].equalsIgnoreCase("al")) ) {
byte val = 0b00000100;
if (strs[1].equalsIgnoreCase("ax")) val |= 1;
sb.append(ListingGenerateHelper.toHexString(val))
.append(' ')
.append(ListingGenerateHelper.toLittleEndianString(strs[3]));
return sb.toString();
}
// ADD r/m, imm 100000sw | mod000r/m | data
if (ListingGenerateHelper.isRegisterOrMemory(ids[1])) {
int val = 0b100000_00;
if ( (ids[1] == ID.REGISTER_WORD) || (ids[1] == ID.VAR_WORD) ) {
val |= 1;
}
String varaddr = " ";
if ( (ids[1] == ID.VAR_BYTE) || (ids[1] == ID.VAR_WORD)) {
short offset = vars.getAddressOfVariable(strs[1]);
varaddr += ListingGenerateHelper.toLittleEndianString(offset) + " ";
}
// mod
int modreg = 0b00;
if (ids[1] == ID.REGISTER_WORD) modreg = 0b11;
modreg <<= 6;
// r/m
if (strs[1].toLowerCase().indexOf("si") != -1) modreg |= 0b100;
else if (strs[1].toLowerCase().indexOf("di") != -1) modreg |= 0b101;
else modreg |= 0b110;
sb.append(ListingGenerateHelper.toHexString(val))
.append(' ')
.append(ListingGenerateHelper.toHexString(modreg))
.append(varaddr)
.append(ListingGenerateHelper.toLittleEndianString(strs[3]));
return sb.toString();
}
}
// ADD r/m, reg 000000dw | modregr/m
if (ListingGenerateHelper.isRegisterOrMemory(ids[1])) {
int val = 0b000000_00;
if ( (ids[1] == ID.REGISTER_WORD) || (ids[1] == ID.VAR_WORD) ) {
val |= 1;
}
if ( (ids[1] == ID.REGISTER_BYTE) || (ids[1] == ID.REGISTER_WORD) ) {
val |= 0b10;
}
String varaddr = " ";
if ( (ids[1] == ID.VAR_BYTE) || (ids[1] == ID.VAR_WORD)) {
short offset = vars.getAddressOfVariable(strs[1]);
varaddr += ListingGenerateHelper.toLittleEndianString(offset) + " ";
}
// mod
int modreg = 0b00;
if ( (ListingGenerateHelper.isRegister(ids[1])) &&
(ListingGenerateHelper.isRegister(ids[3])) ) {
// Åñëè îáà îïåðàíäà - ðåãèñòðû, òî mod = 11
modreg |= 0b11;
}
modreg <<= 6;
// r/m
if (modreg == 0) modreg = 0b110;
else {
// Åñëè mod != 11, òî r/m - id ðåãèñòðà.
modreg |= ListingGenerateHelper.getRegisterCode(strs[3]);
}
// reg - id ðåãèñòðà ïåðâîãî îïåðàíäà, ëèáî 0.
byte regID = ListingGenerateHelper.getRegisterCode(strs[1]);
if (modreg == 0b110) {
regID = ListingGenerateHelper.getRegisterCode(strs[3]);
}
regID <<= 3;
modreg |= regID;
sb.append(ListingGenerateHelper.toHexString(val))
.append(' ')
.append(ListingGenerateHelper.toHexString(modreg))
.append(varaddr);
}
return sb.toString();
}
}

View File

@ -0,0 +1,28 @@
package com.annimon.asm.directives;
/**
*
* @author aNNiMON
*/
public class ByteRegister extends Register {
public ByteRegister() {
super();
}
@Override
public int getId() {
return ID.REGISTER_BYTE;
}
@Override
protected String[] getRegisterNames() {
return new String[] {
"ah", "al",
"bh", "bl",
"ch", "cl",
"dh", "dl",
};
}
}

View File

@ -0,0 +1,27 @@
package com.annimon.asm.directives;
/**
*
* @author aNNiMON
*/
public class ByteValue extends NumericValue {
public ByteValue() {
super();
}
@Override
public int getId() {
return ID.NUMBER_BYTE;
}
@Override
protected boolean checkRange(Integer value) {
if (value == null) return false;
if ( (-128 <= value.intValue()) && (value.intValue() <= 255) ) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,13 @@
package com.annimon.asm.directives;
/**
*
* @author aNNiMON
*/
public class Comma extends Directive {
public Comma() {
super(",", ID.COMMA);
}
}

View File

@ -0,0 +1,13 @@
package com.annimon.asm.directives;
/**
*
* @author aNNiMON
*/
public class DB extends Directive {
public DB() {
super("db", ID.DB);
}
}

View File

@ -0,0 +1,13 @@
package com.annimon.asm.directives;
/**
*
* @author aNNiMON
*/
public class DW extends Directive {
public DW() {
super("dw", ID.DW);
}
}

View File

@ -0,0 +1,29 @@
package com.annimon.asm.directives;
/**
* Áàçîâûé êëàññ äèðåêòèâ.
* @author aNNiMON
*/
public abstract class Directive {
protected String name;
protected int id;
protected Directive(String name, int id) {
this.name = name;
this.id = id;
}
public int getId() {
return id;
}
/**
* Ïðîâåðêà íà ñîîòâåòñòâèå äèðåêòèâû òåêñòó.
* @param text ñòðîêà äèðåêòèâû, êîòîðóþ íóæíî ïðîâåðèòü.
* @return ðåçóëüòàò ñîâïàäåíèÿ.
*/
public boolean isDirective(String text) {
return text.equalsIgnoreCase(name);
}
}

View File

@ -0,0 +1,33 @@
package com.annimon.asm.directives;
/**
* Ñïèñîê èäåíòèôèêàòîðîâ äèðåêòèâ.
* @author aNNiMON
*/
public interface ID {
public static final int
NULL = -1,
DB = 0,
DW = 1,
COMMA = 5,
ADD = 10,
MUL = 11,
PUSH = 12,
POP = 13,
IDIV = 14,
NUMBER_BYTE = 100,
NUMBER_WORD = 101,
NUMBER_INFINITY = 102,
REGISTER_BYTE = 200,
REGISTER_WORD = 201,
VAR = 500,
VAR_BYTE = 501,
VAR_WORD = 502;
}

View File

@ -0,0 +1,20 @@
package com.annimon.asm.directives;
import com.annimon.asm.VarTable;
/**
* Генерирование листинга.
* @author aNNiMON
*/
public interface IListingGenerator {
/**
* Сгенерировать строку листинга.
* @param lineNumber номер строки.
* @param ids последовательность идентификаторов в строке.
* @param strs строковая последовательность идентификаторов.
* @param var таблица переменных
* @return строка листинга.
*/
public String generate(int lineNumber, int[] ids, String[] strs, VarTable var);
}

View File

@ -0,0 +1,20 @@
package com.annimon.asm.directives;
import com.annimon.asm.exceptions.ExceptionWithLineNumber;
/**
* Ïðîâåðêà ñèíòàêñèñà.
* @author aNNiMON
*/
public interface ISyntaxChecker {
/**
* Ïðîâåðèòü ñèíòàêñèñ.
* @param lineNumber íîìåð ñòðîêè.
* @param ids ïîñëåäîâàòåëüíîñòü èäåíòèôèêàòîðîâ â ñòðîêå.
* @return ïðàâèëüíîñòü ïîñëåäîâàòåëüíîñòè äèðåêòèâ.
* @throws ExceptionWithLineNumber
*/
public boolean check(int lineNumber, int[] ids) throws ExceptionWithLineNumber;
}

View File

@ -0,0 +1,77 @@
package com.annimon.asm.directives;
import com.annimon.asm.ListingGenerateHelper;
import com.annimon.asm.VarTable;
import com.annimon.asm.exceptions.ExceptionWithLineNumber;
import com.annimon.asm.exceptions.FewArgumentsException;
import com.annimon.asm.exceptions.TooManyArgumentsException;
import com.annimon.asm.exceptions.WrongArgumentException;
/**
*
* @author aNNiMON
*/
public class Idiv extends Directive implements ISyntaxChecker, IListingGenerator {
public Idiv() {
super("idiv", ID.IDIV);
}
@Override
public boolean check(int lineNumber, int[] ids) throws ExceptionWithLineNumber {
if (ids[0] != getId()) return false;
if (ids.length < 2) throw new FewArgumentsException(lineNumber, 1);
else if (ids.length > 2) throw new TooManyArgumentsException(lineNumber, 1);
if ( (ids[1] != ID.REGISTER_BYTE) && (ids[1] != ID.REGISTER_WORD) &&
(ids[1] != ID.VAR_BYTE) && (ids[1] != ID.VAR_WORD) ) {
throw new WrongArgumentException(lineNumber);
}
return true;
}
@Override
public String generate(int lineNumber, int[] ids, String[] strs, VarTable vars) {
if (ids[0] != getId()) return "";
// MUL r/m 1111011w | mod111r/m
StringBuilder sb = new StringBuilder();
int val = 0b1111011_0;
if ( (ids[1] == ID.REGISTER_WORD) || (ids[1] == ID.VAR_WORD) ) {
val |= 1;
}
String varaddr = " ";
if ( (ids[1] == ID.VAR_BYTE) || (ids[1] == ID.VAR_WORD)) {
short offset = vars.getAddressOfVariable(strs[1]);
varaddr += ListingGenerateHelper.toLittleEndianString(offset) + " ";
}
// mod
int modrm = 0b00;
if (ListingGenerateHelper.isRegister(ids[1])) {
// Åñëè îïåðàíä - ðåãèñòð, òî mod = 11
modrm |= 0b11;
}
modrm <<= 6;
// r/m
if (modrm == 0) modrm = 0b110;
else {
// Åñëè mod != 11, òî r/m - id ðåãèñòðà.
modrm |= ListingGenerateHelper.getRegisterCode(strs[1]);
}
// reg - 111
byte regID = 0b111;
regID <<= 3;
modrm |= regID;
sb.append(ListingGenerateHelper.toHexString(val))
.append(' ')
.append(ListingGenerateHelper.toHexString(modrm))
.append(varaddr);
return sb.toString();
}
}

View File

@ -0,0 +1,27 @@
package com.annimon.asm.directives;
/**
* Íåîïðåäåë¸ííîå çíà÷åíèå (?)
* @author aNNiMON
*/
public class InfinityValue extends NumericValue {
public InfinityValue() {
super();
}
@Override
public int getId() {
return ID.NUMBER_INFINITY;
}
@Override
public boolean isDirective(String text) {
return text.equals("?");
}
@Override
protected boolean checkRange(Integer value) {
return true;
}
}

View File

@ -0,0 +1,77 @@
package com.annimon.asm.directives;
import com.annimon.asm.ListingGenerateHelper;
import com.annimon.asm.VarTable;
import com.annimon.asm.exceptions.ExceptionWithLineNumber;
import com.annimon.asm.exceptions.FewArgumentsException;
import com.annimon.asm.exceptions.TooManyArgumentsException;
import com.annimon.asm.exceptions.WrongArgumentException;
/**
*
* @author aNNiMON
*/
public class Mul extends Directive implements ISyntaxChecker, IListingGenerator {
public Mul() {
super("mul", ID.MUL);
}
@Override
public boolean check(int lineNumber, int[] ids) throws ExceptionWithLineNumber {
if (ids[0] != getId()) return false;
if (ids.length < 2) throw new FewArgumentsException(lineNumber, 1);
else if (ids.length > 2) throw new TooManyArgumentsException(lineNumber, 1);
if ( (ids[1] != ID.REGISTER_BYTE) && (ids[1] != ID.REGISTER_WORD) &&
(ids[1] != ID.VAR_BYTE) && (ids[1] != ID.VAR_WORD) ) {
throw new WrongArgumentException(lineNumber);
}
return true;
}
@Override
public String generate(int lineNumber, int[] ids, String[] strs, VarTable vars) {
if (ids[0] != getId()) return "";
// MUL r/m 1111011w | mod100r/m
StringBuilder sb = new StringBuilder();
int val = 0b1111011_0;
if ( (ids[1] == ID.REGISTER_WORD) || (ids[1] == ID.VAR_WORD) ) {
val |= 1;
}
String varaddr = " ";
if ( (ids[1] == ID.VAR_BYTE) || (ids[1] == ID.VAR_WORD)) {
short offset = vars.getAddressOfVariable(strs[1]);
varaddr += ListingGenerateHelper.toLittleEndianString(offset) + " ";
}
// mod
int modrm = 0b00;
if (ListingGenerateHelper.isRegister(ids[1])) {
// Åñëè îïåðàíä - ðåãèñòð, òî mod = 11
modrm |= 0b11;
}
modrm <<= 6;
// r/m
if (modrm == 0) modrm = 0b110;
else {
// Åñëè mod != 11, òî r/m - id ðåãèñòðà.
modrm |= ListingGenerateHelper.getRegisterCode(strs[1]);
}
// reg - 100
byte regID = 0b100;
regID <<= 3;
modrm |= regID;
sb.append(ListingGenerateHelper.toHexString(val))
.append(' ')
.append(ListingGenerateHelper.toHexString(modrm))
.append(varaddr);
return sb.toString();
}
}

View File

@ -0,0 +1,26 @@
package com.annimon.asm.directives;
import com.annimon.asm.DirectiveConverter;
/**
*
* @author aNNiMON
*/
public abstract class NumericValue extends Directive {
public NumericValue() {
super("", ID.NULL);
}
@Override
public boolean isDirective(String text) {
if (text == null) return false;
Integer value = DirectiveConverter.parseInteger(text);
return checkRange(value);
}
protected abstract boolean checkRange(Integer value);
}

View File

@ -0,0 +1,64 @@
package com.annimon.asm.directives;
import com.annimon.asm.ListingGenerateHelper;
import com.annimon.asm.VarTable;
import com.annimon.asm.exceptions.ExceptionWithLineNumber;
import com.annimon.asm.exceptions.FewArgumentsException;
import com.annimon.asm.exceptions.WrongArgumentException;
/**
*
* @author aNNiMON
*/
public class Pop extends Directive implements ISyntaxChecker, IListingGenerator {
public Pop() {
super("pop", ID.POP);
}
@Override
public boolean check(int lineNumber, int[] ids) throws ExceptionWithLineNumber {
if (ids[0] != getId()) return false;
if (ids.length < 2) throw new FewArgumentsException(lineNumber, 1);
for (int i = 1; i < ids.length; i++) {
if ( (ids[i] != ID.REGISTER_WORD) &&
(ids[i] != ID.VAR_WORD)) {
throw new WrongArgumentException(lineNumber);
}
}
return true;
}
@Override
public String generate(int lineNumber, int[] ids, String[] strs, VarTable vars) {
if (ids[0] != getId()) return "";
StringBuilder sb = new StringBuilder();
if (ids[1] == ID.REGISTER_WORD) {
byte segmentID = ListingGenerateHelper.getSegmentRegisterCode(strs[1]);
if (segmentID != -1) {
//seg (òîëüêî cs, ds, ss, es) 00seg111
byte val = (byte) (0b00_000_111 | (segmentID << 3));
sb.append(ListingGenerateHelper.toHexString(val));
} else {
// reg16/32 01011reg
byte regID = ListingGenerateHelper.getRegisterCode(strs[1]);
byte val = (byte) (0b01011_000 | regID);
sb.append(ListingGenerateHelper.toHexString(val));
}
} else if (ids[1] == ID.VAR_WORD) {
//r/m16/32 10001111 | mod110r/m
short offset = vars.getAddressOfVariable(strs[1]);
sb.append(ListingGenerateHelper.toHexString(0b10001111))
.append(' ')
.append(ListingGenerateHelper.toHexString(0b00_000_110))
.append(' ')
.append(ListingGenerateHelper.toLittleEndianString(offset));
}
return sb.toString();
}
}

View File

@ -0,0 +1,65 @@
package com.annimon.asm.directives;
import com.annimon.asm.ListingGenerateHelper;
import com.annimon.asm.VarTable;
import com.annimon.asm.exceptions.ExceptionWithLineNumber;
import com.annimon.asm.exceptions.FewArgumentsException;
import com.annimon.asm.exceptions.WrongArgumentException;
/**
*
* @author aNNiMON
*/
public class Push extends Directive implements ISyntaxChecker, IListingGenerator {
public Push() {
super("push", ID.PUSH);
}
@Override
public boolean check(int lineNumber, int[] ids) throws ExceptionWithLineNumber {
if (ids[0] != getId()) return false;
if (ids.length < 2) throw new FewArgumentsException(lineNumber, 1);
for (int i = 1; i < ids.length; i++) {
if ( (ids[i] != ID.REGISTER_WORD) &&
(ids[i] != ID.VAR_WORD)) {
throw new WrongArgumentException(lineNumber);
}
}
return true;
}
@Override
public String generate(int lineNumber, int[] ids, String[] strs, VarTable vars) {
if (ids[0] != getId()) return "";
StringBuilder sb = new StringBuilder();
if (ids[1] == ID.REGISTER_WORD) {
byte segmentID = ListingGenerateHelper.getSegmentRegisterCode(strs[1]);
if (segmentID != -1) {
//seg (òîëüêî cs, ds, ss, es) 00seg110
byte val = (byte) (0b00_000_110 | (segmentID << 3));
sb.append(ListingGenerateHelper.toHexString(val));
} else {
// reg16/32 01010reg
byte regID = ListingGenerateHelper.getRegisterCode(strs[1]);
byte val = (byte) (0b01010_000 | regID);
sb.append(ListingGenerateHelper.toHexString(val));
}
} else if (ids[1] == ID.VAR_WORD) {
// r/m16/32 11111111 | mod110r/m
short offset = vars.getAddressOfVariable(strs[1]);
sb.append(ListingGenerateHelper.toHexString(0b11111111))
.append(' ')
.append(ListingGenerateHelper.toHexString(0b00_110_110))
.append(' ')
.append(ListingGenerateHelper.toLittleEndianString(offset));
}
return sb.toString();
}
}

View File

@ -0,0 +1,25 @@
package com.annimon.asm.directives;
/**
*
* @author aNNiMON
*/
public abstract class Register extends Directive {
public Register() {
super("", ID.NULL);
}
@Override
public boolean isDirective(String text) {
String[] names = getRegisterNames();
for (int i = 0; i < names.length; i++) {
if (text.equalsIgnoreCase(names[i])) {
return true;
}
}
return false;
}
protected abstract String[] getRegisterNames();
}

View File

@ -0,0 +1,61 @@
package com.annimon.asm.directives;
import com.annimon.asm.ListingGenerateHelper;
import com.annimon.asm.VarTable;
import com.annimon.asm.exceptions.ExceptionWithLineNumber;
import com.annimon.asm.exceptions.FewArgumentsException;
import com.annimon.asm.exceptions.TooManyArgumentsException;
import com.annimon.asm.exceptions.WrongArgumentException;
import java.util.regex.Pattern;
/**
*
* @author aNNiMON
*/
public class Variable extends Directive implements ISyntaxChecker, IListingGenerator {
public Variable() {
super("", ID.VAR);
}
@Override
public boolean isDirective(String text) {
if (Pattern.matches(Pattern.compile("^[^0-9]*[\\w.@_$]").pattern(), text)) {
return true;
}
return false;
}
@Override
public boolean check(int lineNumber, int[] ids) throws ExceptionWithLineNumber {
if (ids[0] != getId()) return false;
if (ids.length < 3) throw new FewArgumentsException(lineNumber, 2);
else if (ids.length > 3) throw new TooManyArgumentsException(lineNumber, 2);
boolean db = ( (ids[1] == ID.DB) &&
((ids[2] == ID.NUMBER_BYTE) || (ids[2] == ID.NUMBER_INFINITY)) );
boolean dw = ( (ids[1] == ID.DW) &&
((ids[2] == ID.NUMBER_WORD) || (ids[2] == ID.NUMBER_INFINITY)) );
if (!db && !dw) {
throw new WrongArgumentException(lineNumber);
}
return true;
}
@Override
public String generate(int lineNumber, int[] ids, String[] strs, VarTable vars) {
if ( (ids[0] != ID.VAR_BYTE) && (ids[0] != ID.VAR_WORD) ) return "";
StringBuilder sb = new StringBuilder();
int offset = vars.getAddressOfVariable(strs[0]);
sb.append(ListingGenerateHelper.toHexString(offset)).append(" ");
if (ids[2] == ID.NUMBER_INFINITY) {
sb.append("??");
if (ids[1] == ID.DW) sb.append(" ??");
} else sb.append( ListingGenerateHelper.toLittleEndianString(strs[2]) );
return sb.toString();
}
}

View File

@ -0,0 +1,27 @@
package com.annimon.asm.directives;
/**
*
* @author aNNiMON
*/
public class WordRegister extends Register {
public WordRegister() {
super();
}
@Override
public int getId() {
return ID.REGISTER_WORD;
}
@Override
protected String[] getRegisterNames() {
return new String[] {
"ax", "bx", "cx", "dx",
"cs", "ds", "ss", "es",
"sp", "bp", "si", "di"
};
}
}

View File

@ -0,0 +1,27 @@
package com.annimon.asm.directives;
/**
*
* @author aNNiMON
*/
public class WordValue extends NumericValue {
public WordValue() {
super();
}
@Override
public int getId() {
return ID.NUMBER_WORD;
}
@Override
protected boolean checkRange(Integer value) {
if (value == null) return false;
if ( (-32768 <= value.intValue()) && (value.intValue() <= 65535) ) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,12 @@
package com.annimon.asm.exceptions;
/**
* Êëàññ îøèáêè ïðîïóùåííîé çàïÿòîé.
* @author aNNiMON
*/
public class CommaExpectedException extends ExceptionWithLineNumber {
public CommaExpectedException(int lineNumber) {
super("Comma expected", lineNumber);
}
}

View File

@ -0,0 +1,21 @@
package com.annimon.asm.exceptions;
/**
* Áàçîâûé êëàññ äëÿ îøèáîê ñ îòîáðàæåíèåì íîìåðà îøèáî÷íîé ñòðîêè.
* @author aNNiMON
*/
public abstract class ExceptionWithLineNumber extends Exception {
private int lineNumber;
public ExceptionWithLineNumber(String message, int lineNumber) {
super(message);
this.lineNumber = lineNumber;
}
@Override
public String getMessage() {
return super.getMessage() + " at line " + lineNumber;
}
}

View File

@ -0,0 +1,16 @@
package com.annimon.asm.exceptions;
/**
* Êëàññ îøèáêè, êîãäà óêàçàíî ñëèøêîì ìàëî àðãóìåíòîâ êîìàíäû, íåæåëè îæèäàëîñü.
* @author aNNiMON
*/
public class FewArgumentsException extends ExceptionWithLineNumber {
public FewArgumentsException(int lineNumber) {
super("Few arguments", lineNumber);
}
public FewArgumentsException(int lineNumber, int necessaryArguments) {
super("Few arguments, need " + necessaryArguments + " args", lineNumber);
}
}

View File

@ -0,0 +1,16 @@
package com.annimon.asm.exceptions;
/**
* Êëàññ îøèáêè äëÿ ïðåâûøåííîãî êîëè÷åñòâà àðãóìåíòîâ êîìàíäû.
* @author aNNiMON
*/
public class TooManyArgumentsException extends ExceptionWithLineNumber {
public TooManyArgumentsException(int lineNumber) {
super("Too many arguments", lineNumber);
}
public TooManyArgumentsException(int lineNumber, int necessaryArguments) {
super("Too many arguments, need " + necessaryArguments + " args", lineNumber);
}
}

View File

@ -0,0 +1,12 @@
package com.annimon.asm.exceptions;
/**
* Êëàññ îøèáêè íåâåðíîãî àðãóìåíòà, êîãäà íå ñîâïàäàåò îæèäàåìûé òèï.
* @author aNNiMON
*/
public class WrongArgumentException extends ExceptionWithLineNumber {
public WrongArgumentException(int lineNumber) {
super("Wrong argument", lineNumber);
}
}

20
src/test.asm Normal file
View File

@ -0,0 +1,20 @@
num1 db 12
num2 dw 2000
num3 dw ?
num4 db 77h
num5 dw 12345
add num1, 40
add num2, bx
push num2
pop ax
idiv cx
add al, cl
mul dh
push dx
add si, dx
mul num2
pop num3
push es
pop ss
idiv num1