diff --git a/nbproject/project.properties b/nbproject/project.properties index 65b55d0..c5ce80d 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -34,7 +34,7 @@ jar.compress=false javac.classpath=\ ${libs.beans-binding.classpath} # Space-separated list of extra javac options -javac.compilerargs= +javac.compilerargs=-Xlint:unchecked javac.deprecation=false javac.processorpath=\ ${javac.classpath} diff --git a/src/Jatm/JaTape.java b/src/Jatm/JaTape.java index b0456cb..7b86e09 100644 --- a/src/Jatm/JaTape.java +++ b/src/Jatm/JaTape.java @@ -35,7 +35,7 @@ public class JaTape { public static final byte BYT_FILE = (byte) 0x20; /** - * Header Parameter Index + * Header Parameter Index * to be used with getParameter and setParameter methods */ public static final int BLOCK_TYPE = 0; // 1 byte @@ -49,31 +49,31 @@ public class JaTape { public static final int VOCLNK = 22; // 2 bytes public static final int STKBOT = 24; // 2 bytes public static final int CRC = 26; // 1 byte - + /** * Header Block Size in Bytes */ public static final int HEADER_LENGTH = 27; // Header Block Size - + private final JaTapeBlock header; private final JaTapeBlock data; private int baseAddress; - + /** * Constructor: Empty Tape File */ public JaTape() { this(new byte[HEADER_LENGTH], new byte[2]); } - + /** * Copy Constructor - * @param tape + * @param tape */ public JaTape(JaTape tape) { this(tape.getHeaderBlock(), tape.getDataBlock()); } - + /** * Constructor: Tape File from header and data blocks * @param hdr header block byte array @@ -92,13 +92,13 @@ public JaTape(byte[] hdr, byte[] dat) { /** * Set a whole data block array, including block type and CRC bytes - * @param block data block + * @param block data block */ public void setDataBlock(byte[] block) { data.set(block); } - + /** * Get the whole data block array, including block type and CRC bytes * @return data block @@ -114,7 +114,7 @@ public byte[] getDataBlock() { public byte[] getData() { return data.getPart(1,data.length()-2); } - + /** * Set byte value to a Data block position * @param index position in Data block array (excluding block type byte) @@ -123,7 +123,7 @@ public byte[] getData() { public void setDataByte(int index, byte b) { data.setByte(index+1,b); // skip block type byte } - + /** * Get byte value from Data block position * @param index position in Data block array (excluding block type byte) @@ -149,7 +149,7 @@ public void setDataWord(int index, int w) { */ public int getDataWord(int index) { return data.getWord(index+1); // skip block type byte - } + } /** * Set the Header block from a byte array @@ -160,7 +160,7 @@ public void setHeaderBlock(byte[] b) { byte[] h = new byte[HEADER_LENGTH]; int len = (h.length > b.length)?b.length:h.length; System.arraycopy(h, 0, b, 0, len); - + header.set(h); baseAddress = getParameter(JaTape.ADDRESS); // file base address } @@ -175,10 +175,10 @@ public byte[] getHeaderBlock() { /** * Set Header Parameters to a canonical Byt file type - */ + */ public void makeByt() { header.setType(JaTapeBlock.HEADER_BLOCK); - + header.setByte(FILE_TYPE, BYT_FILE); header.setWord(CURR_WRD, 0x2020); header.setWord(CURRENT, 0x2020); @@ -186,13 +186,13 @@ public void makeByt() { header.setWord(VOCLNK, 0x2020); header.setWord(STKBOT, 0x2020); } - + /** * Set Header Parameters to a canonical Dict file type */ public void makeDict() { header.setType(JaTapeBlock.HEADER_BLOCK); - + header.setByte(FILE_TYPE, DICT_FILE); header.setWord(ADDRESS, 0x3C51); header.setWord(CURRENT, 0x3C4C); @@ -209,14 +209,26 @@ public Boolean isDict() { return (header.getByte(FILE_TYPE)==0x00); } + /** + * get file type string + * @return file type as a String "dict" or " byt" + */ + public String getFileType() { + if(isDict()) { + return "dict"; + } else { + return " byt"; + } + } + /** * Set file type: Dict or Byt * @param type File type byte */ public void setType(byte type) { header.setByte(FILE_TYPE, type); - } - + } + /** * Get File name * @return file name string @@ -224,10 +236,10 @@ public void setType(byte type) { public String getFilename() { return header.getString(FILE_NAME, 10); } - + /** * Set the File name (max 10 characters) - * @param filename + * @param filename */ public void setFilename(String filename) { filename = filename + " "; // Pad with Spaces @@ -251,12 +263,12 @@ public int getParameter(int index) { */ public void setParameter(int index, int parameter) { header.setWord(index, parameter); - } + } /** * Check if Header and Data blocks CRC's are correct * @return True if Header and Data blocks CRC's are ok - */ + */ public Boolean crcOk() { return (header.crcOk() && data.crcOk()); } @@ -268,7 +280,7 @@ public Boolean crcOk() { public Boolean headerCrcOk() { return header.crcOk(); } - + /** * Check if Data block CRC is correct * @return True if Data block CRC is ok @@ -276,7 +288,7 @@ public Boolean headerCrcOk() { public Boolean dataCrcOk() { return data.crcOk(); } - + /** * Calculate and set correct Header and Data blocks CRC bytes */ @@ -284,14 +296,14 @@ public void fixCrc() { header.fixCrc(); data.fixCrc(); } - + /** * Calculate and set a correct Header block CRC byte */ public void fixHeaderCrc() { header.fixCrc(); } - + /** * Calculate and set a correct Data block CRC byte */ @@ -306,7 +318,7 @@ public void fixDataCrc() { public byte getHeaderCrc() { return header.getCrc(); } - + /** * Get Data block CRC byte * @return Data block CRC byte @@ -314,23 +326,23 @@ public byte getHeaderCrc() { public byte getDataCrc() { return data.getCrc(); } - + /** * Set Header block CRC byte - * @param crc + * @param crc */ public void setHeaderCrc(byte crc) { header.setCrc(crc); } - + /** * Set Data block CRC byte - * @param crc + * @param crc */ public void setDataCrc(byte crc) { data.setCrc(crc); } - + /** * Check for a valid file address * @param address Address to be checked @@ -340,7 +352,7 @@ public boolean validAddress(int address) { return (address >= baseAddress) && (address < baseAddress + getParameter(JaTape.LENGTH)); } - + /** * Get byte from memory address. Returns Zero if invalid address * @param address byte location @@ -363,5 +375,5 @@ public int getMemWord(int address) { return getDataWord(address - baseAddress); } return 0; - } + } } diff --git a/src/Jatm/JaVocabulary.java b/src/Jatm/JaVocabulary.java index c5583ef..8fe86e0 100644 --- a/src/Jatm/JaVocabulary.java +++ b/src/Jatm/JaVocabulary.java @@ -23,31 +23,19 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import static java.lang.Integer.parseInt; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.Stack; /* - RAM Vocabulary Header Fields: + RAM Vocabulary Word Structure: 0 to 63 bytes Name Word name last char with bit-7 set - 2 bytes Length Lebgth of Parameter Field + 7 + 2 bytes Length Length of Parameter Field + 7 2 bytes Link Points to previous word Name Length Field 1 byte Name Length (bits 0-5): Name Length bit 6: IMMEDIATE word flag 2 bytes Code strBuf address to execute this word n bytes Parameter (n = Length Field - 7) - - Offset from CFA: - -n-5: Name (0-63 characters) - -5: Length (x) - -3: Link (point to previous word Name Length) - -1: Name Length (n) (bit-6 = IMMEDIATE flag) - 0: Code - +2: Parameter (x-7 bytes) */ /** @@ -55,13 +43,22 @@ n bytes Parameter (n = Length Field - 7) * @author Ricardo F. Lopes */ public class JaVocabulary { + private final JaTape tape; // The tape file + private final Stack vocStack; // VOCABULARY words + private static final HashMap ROM_CFA_LIST; // ROM CFA list + private final HashMap fileCfaList; // File CFA list + private String vlist; // VLIST text - private final HashMap vocabularyMap; // Tape file vocabulary - List cfaList; - private final JaTape tapeFile; + private static final int MAX_WORDS_PER_LINE = 5; // Max Number of Words per line in code listing - // Max Number of Words per line in code listing - private static final int MAX_WORDS_PER_LINE = 5; + // Address offsets from a word name length position + private static final int LENGTH_OFFSET = -4; // Word Length + private static final int LINK_OFFSET = -2; // Next Link offset from link address + private static final int CFA_OFFSET = 1; // CFA offset from link address + private static final int PFA_OFFSET = 3; // CFA offset from link address + // Address ofsets for a VOCABULARY word + private static final int TOPWRD_OFFSET = 3; // Top Word Link offset from link address + private static final int VOCLNK_OFFSET = 6; // VOCLNK offset from link address // Word Types based on CFA (Code Field Address) private static final int DOCOLON = 0x0EC3; @@ -69,6 +66,7 @@ public class JaVocabulary { private static final int VARIABLE = 0x0FF0; private static final int CONSTANT = 0x0FF5; private static final int DEFINER = 0x1085; + private static final int VOCAB = 0x11B5; // private static final int COMPILER = 0x10F5; private static final int COMPILER = 0x1108; @@ -93,293 +91,372 @@ public class JaVocabulary { private static final int ROM_REPEAT = 0x1276; // REPEAT private static final int ROM_SEMICOLON = 0x04B6; // ; - //------------------ Static ROM Vocabulary read from a txt file ---------------- - private static final HashMap ROM_VOC; + // ROM Vocabulary. Read from a txt file + static { - ROM_VOC = new HashMap<>(); + ROM_CFA_LIST = new HashMap<>(); try (InputStream in = JaVocabulary.class.getResourceAsStream("resources/ROM_words.txt")) { BufferedReader buf = new BufferedReader(new InputStreamReader(in)); String line; while ((line = buf.readLine()) != null) { - String parts[] = line.split(" "); // Space delimited values - ROM_VOC.put(parseInt(parts[0], 16), parts[1]); // Build Hash Map + String parts[] = line.split(" "); // Space delimited values + ROM_CFA_LIST.put(parseInt(parts[0], 16), parts[1]); // Build Hash Map } in.close(); } catch (IOException e) { System.out.println("IOException"); } } - // ----------------------------------------------------------------------------- /** * Constructor - * @param tape Jupiter Ace tape object + * @param tapeFile Jupiter Ace tape file */ - public JaVocabulary(JaTape tape) { - vocabularyMap = new HashMap<>(); // Tape File vocabulary - cfaList = new ArrayList<>(); // CFA list - tapeFile = tape; - buildVocabularyList(); + public JaVocabulary(JaTape tapeFile) { + tape = tapeFile; + + fileCfaList = new HashMap<>(); // Tape File words + vocStack = new Stack<>(); // File Vocabularies + + if (tape.isDict()) { + vocabularyScan(); // scan Vocabularies only if Dictionary file + buildVlist(); // Build vlist string and CFA list + } + } + + public String getVlist() { + return vlist; } /** - * Scan file and build a vocabulary list populating cfaList + * Build vocabulary list */ - private void buildVocabularyList() { - if (tapeFile.isDict()) { // scan Words only if it is a Dictionary file - int link = tapeFile.getParameter(JaTape.CURR_WRD); // get first word link - // scan until link to outside file boudaries - while (tapeFile.validAddress(link)) { - // Get Word Name Length - byte nameLength = (byte) (tapeFile.getMemByte(link) & 0x3F); // clear bits 6,7 - // Get Word Name Characters - byte[] nameChars = new byte[nameLength]; - for (int i = 0; i < nameLength; i++) { // clear bit-7 of all chars - nameChars[i] = (byte) (tapeFile.getMemByte(link - 4 - nameLength + i) & 0x7F); - } - // Create Word Name String - String wordName = new String(nameChars, StandardCharsets.UTF_8); - // Build CFA to Word name map - vocabularyMap.put(link + 1, wordName); // CFA = link+1 - cfaList.add(link + 1); // Build CFA List - - link = tapeFile.getMemWord(link - 2); // get next word link + private void vocabularyScan() { + int link = tape.getParameter(JaTape.CURR_WRD); // newest word link in dictionary + while(link > 0x2000) { // limit scan to RAM words only + if( isVocabulary(link) ) { + vocStack.push(link); // collect VOCABULARY words } + link = getNextWord(link); // get next word link } } /** - * @return Number of words in vocabulary + * Get Word Name + * @param link + * @return word name string */ - public int vocabularySize() { - return vocabularyMap.size(); + private String getWordName(int link) { + byte nameLength = (byte) (tape.getMemByte(link) & 0x3F); // clear bits 6,7 + // Get Word Name Characters + byte[] nameChars = new byte[nameLength]; + for (int i = 0; i < nameLength; i++) { // clear bit-7 of all chars + nameChars[i] = (byte) (tape.getMemByte(link - 4 - nameLength + i) & 0x7F); + } + // Create Word Name String + String name = new String(nameChars, StandardCharsets.UTF_8); + return name ; } /** - * Lists words in vocabulary (similar to VLIST). One word per line - * @return vocabulary list as a String + * Get next linked word link + * @param link name length field address of a vocabulary word + * @return link to previous word in linking chain */ - public String vlist() { - StringBuilder wordList = new StringBuilder(); - cfaList.stream().forEach((cfa) -> { - wordList.append(vocabularyMap.get(cfa)).append(" "); - }); - return wordList.toString(); + private int getNextWord(int link) { + return tape.getMemWord(link + LINK_OFFSET); } /** - * Lists words in vocabulary (similar to VLIST). One word per line - * @return vocabulary list as a String + * get Code Field Value + * @param link name length field address of a vocabulary word + * @return contents of the Code Field */ - public String listCfa() { - StringBuilder wordList = new StringBuilder(); - cfaList.stream().forEach((cfa) -> { - wordList.append(String.format("%04Xh: ", cfa)); - wordList.append(vocabularyMap.get(cfa)).append("\n"); - }); - return wordList.toString(); - } - + private int getCodeField(int link) { + return tape.getMemWord(link + CFA_OFFSET); + } + /** - * Disassemble FORTH vocabulary - * @return Decoded vocabulary as a String + * get Top Word link in a Vocabulary + * @param link name length field address of a vocabulary word + * @return link to newest word in Vocabulary */ - public String listAllWords() { - StringBuilder strBuf = new StringBuilder(); // resulting String buffer - // Reverse word list order to disassemble older words first - int[] reverseCfaList = new int[cfaList.size()]; - for (int i = 0; i < cfaList.size(); i++) { - reverseCfaList[reverseCfaList.length - i - 1] = cfaList.get(i); + private int getVocabTopWordLink(int link) { + return tape.getMemWord(link + TOPWRD_OFFSET); + } + + /** + * Check if a word is a VOCABULARY word + * @param link name length field address of a vocabulary word + * @return True if link points to a VOCABULARY word + */ + private boolean isVocabulary(int link) { + return getCodeField(link) == VOCAB; // check code field address + } + + /** + * @return Number of words in vocabulary + */ + public int vocabularySize() { + return fileCfaList.size(); + } + + /** + * List linked words with it's CFA + * @param link words in the link chain + * @param out resulting String Buffer + * @return number of words listed + */ + private int listWords(int link, StringBuilder out ) { + int count = 0; + while( tape.validAddress(link) ) { + fileCfaList.put(link + CFA_OFFSET, getWordName(link)); // build list of CFA in tape file + out.append(String.format("%04Xh: ", link+CFA_OFFSET)); // CFA + out.append(getWordName(link)).append("\n"); // Word name + link = getNextWord(link); // get next word link + count++; // count words in dictionary } - // Disassemble each Forth word in list - for (int cfa : reverseCfaList) { - listWord(strBuf, cfa); + return count; + } + + /** + * Lists words in vocabulary. One word per line, all vocabularies + * @return vocabulary list as a String + */ + private void buildVlist() { + StringBuilder out = new StringBuilder(); + int link; + int wordCount = 0; + // List Vocabulary Words + for (Integer vocLink : vocStack) { + out.append(String.format("\n%s DEFINITIONS\n",getWordName(vocLink))); + link = getVocabTopWordLink(vocLink); + wordCount += listWords(link, out ); } - return strBuf.toString(); + + // List FORTH Vocabulary words + out.append("\nFORTH DEFINITIONS\n"); + link = tape.getParameter(JaTape.CURR_WRD); // newest word link in dictionary + wordCount += listWords(link, out); + + out.append("\nTotal: ").append(wordCount).append(" words in dictionary."); + vlist = out.toString(); } /** - * List contents of a FORTH word - * @param strBuf listing - * @param cfa Word Code Field Address + * Decompile a single Forth Word + * @param link Link to the word to be decoded + * @param out StringBuilder were decoding text will be appended */ - private void listWord(StringBuilder strBuf, int cfa) { - String wordName = vocabularyMap.get(cfa); // get word Name - int codeField = tapeFile.getMemWord(cfa); // get word Code Field - // Append Word prefix according to code field - switch (codeField) { + private void decodeWord(int link, StringBuilder out) { + String wordName = getWordName(link); // get word Name + int cfa = getCodeField(link); // get word Code Field content + int wordLength = tape.getMemWord(link + LENGTH_OFFSET) - 7; + + switch (cfa) { // Append Word prefix according to code field case CREATE: // CREATE name (size) - strBuf.append("CREATE"); + out.append(String.format("CREATE %s ( %d bytes )", wordName, wordLength)); break; case VARIABLE: // VARIABLE name - strBuf.append("VARIABLE"); + out.append(String.format("VARIABLE %s", wordName)); break; case CONSTANT: // xx CONSTANT name - strBuf.append(getInt(cfa + 2)); - strBuf.append(" CONSTANT"); + out.append(String.format("%d CONSTANT %s",getInt(link + PFA_OFFSET), wordName)); break; - case DEFINER: // DEFINER name - strBuf.append("DEFINER"); + case VOCAB: // VOCABULARY name + out.append(String.format("VOCABULARY %s", wordName)); break; - case COMPILER: // COMPILER name - strBuf.append("COMPILER"); - break; - default: // : name - strBuf.append(":"); - break; - } - - // Append Word Name - strBuf.append(" ").append(wordName).append(" "); - - // Append Word Body according to code field - switch (codeField) { - case CREATE: // CREATE name (size) - strBuf.append(" ( ").append(tapeFile.getMemWord(cfa - 5)).append(" bytes )"); + case DEFINER: // DEFINER name DOES> ; + out.append(String.format("DEFINER %s", wordName)); + decodeDoColon(link, out); break; - case VARIABLE: // VARIABLE name - case CONSTANT: // xx CONSTANT name + case COMPILER: // COMPILER name RUNS> ; + out.append(String.format("COMPILER %s", wordName)); + decodeDoColon(link, out); break; - case DOCOLON: // : name ; - case DEFINER: // DEFINER name DOES> ; - case COMPILER: // COMPILER name RUNS> ; - listDoColonWord(strBuf, cfa); + case DOCOLON: // : name ; + out.append(String.format(": %s", wordName)); + decodeDoColon(link, out); break; - default: // : name ( CFA=xxxx ) ... unlistable word - strBuf.append("( CFA=").append(String.format("%04Xh", cfa)).append(" )"); + default: // : name ( CFA:xxxx ) ... (Unlistable word) + out.append(String.format(": %s ( CFA:%04Xh )\n;", wordName, cfa) ); break; - } - strBuf.append("\n"); // Next line + } + out.append("\n"); // Next line } - + + /** + * Decompile all words in a vocabulary + * @param link + * @param out + */ + private void decodeVocab(int link, StringBuilder out) { + Stack wordStack = new Stack<>(); // list of words in vocabulary + // build word list + while( tape.validAddress(link) ) { + wordStack.push(link); + link = getNextWord(link); // get next word link // count words in dictionary + } + // decode each words in vocabulary in proper order + while( !wordStack.empty() ) { + link = wordStack.pop(); + decodeWord(link, out); + } + } + + /** + * Decompile all words in file + * @return String code text + */ + public String dictCodeListing() { + StringBuilder codeListing = new StringBuilder(); // resulting String buffer + + //Decode words in FORTH vocabulary + codeListing.append("FORTH DEFINITIONS\n"); + int wordLink = tape.getParameter(JaTape.CURR_WRD); // start with newest word in dictionary + decodeVocab(wordLink, codeListing); // decode all linked words in vocabulary + + // Decode words in other vocabularies + for (int i=vocStack.size()-1; i>=0; i--) { // Decode words in all vocabularies + int vocLink = vocStack.get(i); + codeListing.append(String.format("\n%s DEFINITIONS\n",getWordName(vocLink))); + wordLink = getVocabTopWordLink(vocLink); // start with newest word in this vocabulary + decodeVocab(wordLink, codeListing); // decode all linked words in vocabulary + } + return codeListing.toString(); + } + /** * List DOCOLON word contents - * @param strBuf is the output StringBuilder buffer + * @param out is the output StringBuilder buffer * @param cfa is te Code Field Address of the word to disassemble */ - private void listDoColonWord(StringBuilder strBuf, int cfa) { - int parameterFieldSize = tapeFile.getMemWord(cfa - 5) - 7; // Parameter Field Length - int parameterFieldAddress = cfa + 2; // Parameter Field Address - int address = parameterFieldAddress; // current address - int wordCount = MAX_WORDS_PER_LINE; // Start with a new line now - int tab = 1; // start with one space tab - while (address < parameterFieldAddress + parameterFieldSize) { - wordCount++; // Count words printed in in this line - int parameter = tapeFile.getMemWord(address); // get parameter disassemble - // Check for words that decreases identation - if ( parameter == ROM_THEN || parameter == ROM_WHILE || - parameter == ROM_REPEAT || parameter == ROM_SEMICOLON || - parameter == ROM_LOOP || parameter == ROM_PLOOP || - parameter == ROM_ELSE || parameter == ROM_UNTIL || - parameter == ROM_DOES || parameter == ROM_RUNS ) { - tab--; // decrease tab + private void decodeDoColon(int link, StringBuilder out) { + int parameterLength = tape.getMemWord(link + LENGTH_OFFSET) - 7; // Parameter Field Length + int parameterField = link + PFA_OFFSET; // Parameter Field Address + int address = parameterField; // current address + int wordCount = MAX_WORDS_PER_LINE; // Start with a new line now + int tab = 1; // start with one space tab + while (address < parameterField + parameterLength) { + int cfa = tape.getMemWord(address); // get token to disassemble + + wordCount++; // Count words printed in this line + // Check for ROM words that decreases identation + if ( cfa == ROM_THEN || cfa == ROM_WHILE || + cfa == ROM_REPEAT || cfa == ROM_SEMICOLON || + cfa == ROM_LOOP || cfa == ROM_PLOOP || + cfa == ROM_ELSE || cfa == ROM_UNTIL || + cfa == ROM_DOES || cfa == ROM_RUNS ) { + tab--; // decrease tab } - // Check for words that starts a new line - if ( wordCount >= MAX_WORDS_PER_LINE || parameter == ROM_THEN || - parameter == ROM_WHILE || parameter == ROM_REPEAT || - parameter == ROM_SEMICOLON || parameter == ROM_LOOP || - parameter == ROM_PLOOP || parameter == ROM_ELSE || - parameter == ROM_UNTIL || parameter == ROM_DOES || - parameter == ROM_RUNS || parameter == ROM_DO || - parameter == ROM_BEGIN || parameter == ROM_IF ) { - wordCount = 0; // Reset Words per Line Counter - strBuf.append("\n"); // Start a new line - for(int i = 0; i < tab; i++) { // Identation - strBuf.append(" "); - } + // Check for ROM words that starts a new line + if ( wordCount >= MAX_WORDS_PER_LINE || cfa == ROM_THEN || + cfa == ROM_WHILE || cfa == ROM_REPEAT || + cfa == ROM_SEMICOLON || cfa == ROM_LOOP || + cfa == ROM_PLOOP || cfa == ROM_ELSE || + cfa == ROM_UNTIL || cfa == ROM_DOES || + cfa == ROM_RUNS || cfa == ROM_DO || + cfa == ROM_BEGIN || cfa == ROM_IF ) { + wordCount = 0; // Reset Words per Line Counter + out.append("\n"); // Start a new line + for(int i = 0; i < tab; i++) { // Do Identation + out.append(" "); + } } - // Search parameter in Tape Vocabulary - String wordString = vocabularyMap.get(parameter); // search in tape vocabulary - // Search parameter in ROM vocabulary - if (wordString == null) { // word not in tape vocabulary.. - wordString = ROM_VOC.get(parameter); // search in ROM vocabulary + + // Search CFA in Dictionary + String wordString = fileCfaList.get(cfa); // Search in file first.. + if (wordString == null) { + wordString = ROM_CFA_LIST.get(cfa); // ..than search in ROM } - // Check if it is a number - if( parameter == ROM_STK_INT || parameter == ROM_STK_FP ) { + // is it a Number ? + if( cfa == ROM_STK_INT || cfa == ROM_STK_FP ) { wordString = ""; } - // Check if unknow word + // Unknow Word ? if (wordString == null) { - wordString = String.format("[%04Xh]", parameter); + wordString = String.format("[%04Xh]", cfa); } - strBuf.append(wordString); // Append word name - address += 2; // point to next parameter field data + out.append(wordString); // Append word name + address += 2; // point to next parameter field data // Complement immediate words decoding - switch (parameter) { - case ROM_PRINT_STRING: // ." - address += appendString(strBuf, address); - strBuf.append("\""); + switch (cfa) { + case ROM_PRINT_STRING: // ." (string) + out.append(" "); + address += appendString(out, address); + out.append("\""); break; - case ROM_COMMENT: // ( - address += appendString(strBuf, address); - strBuf.append(")"); + case ROM_COMMENT: // ( (comment string) + out.append(" "); + address += appendString(out, address); + out.append(")"); break; - case ROM_STK_BYTE: // Stack Single Byte (ASCII) - strBuf.append(" ").append((char) (tapeFile.getMemByte(address))); // Character - address++; // drop 1 bytes + case ROM_STK_BYTE: // Stack Single Byte (ASCII) + out.append(" ").append((char) (tape.getMemByte(address))); // Character + address++; // drop 1 bytes break; - case ROM_STK_INT: // Stack Integer Number (2 bytes) - strBuf.append(getInt(address)); - address += 2; // drop 2 bytes + case ROM_STK_INT: // Stack Integer Number (2 bytes) + out.append(getInt(address)); + address += 2; // drop 2 bytes break; - case ROM_STK_FP: // Stack Floating Point Number (4 bytes) - address += appendFloat(strBuf, address); + case ROM_STK_FP: // Stack Floating Point Number (4 bytes) + address += appendFloat(out, address); break; case ROM_DOES: case ROM_RUNS: - address += 5; // drop 5 bytes + address += 5; // drop 5 bytes break; - default: // do nothing + default: // do nothing } // Check for words with additional 2 byte parameter - if( parameter == ROM_IF || parameter == ROM_ELSE || - parameter == ROM_WHILE || parameter == ROM_LOOP || - parameter == ROM_PLOOP || parameter == ROM_UNTIL || - parameter == ROM_REPEAT ) { + if( cfa == ROM_IF || cfa == ROM_ELSE || + cfa == ROM_WHILE || cfa == ROM_LOOP || + cfa == ROM_PLOOP || cfa == ROM_UNTIL || + cfa == ROM_REPEAT ) { address += 2; // drop 2 bytes } // Check words that starts a new line after it - if( parameter == ROM_THEN || parameter == ROM_DO || - parameter == ROM_BEGIN || parameter == ROM_IF || - parameter == ROM_ELSE || parameter == ROM_WHILE || - parameter == ROM_LOOP || parameter == ROM_PLOOP || - parameter == ROM_UNTIL || parameter == ROM_REPEAT || - parameter == ROM_PRINT_STRING || parameter == ROM_COMMENT || - parameter == ROM_DOES || parameter == ROM_RUNS ) { + if( cfa == ROM_THEN || cfa == ROM_DO || + cfa == ROM_BEGIN || cfa == ROM_IF || + cfa == ROM_ELSE || cfa == ROM_WHILE || + cfa == ROM_LOOP || cfa == ROM_PLOOP || + cfa == ROM_UNTIL || cfa == ROM_REPEAT || + cfa == ROM_PRINT_STRING || cfa == ROM_COMMENT || + cfa == ROM_DOES || cfa == ROM_RUNS ) { wordCount = MAX_WORDS_PER_LINE; // Start a new line } // Check for words that increase identation after it - if( parameter == ROM_DO || parameter == ROM_BEGIN || - parameter == ROM_IF || parameter == ROM_ELSE || - parameter == ROM_WHILE || parameter == ROM_DOES || - parameter == ROM_RUNS ) { - tab++; // increase identation + if( cfa == ROM_DO || cfa == ROM_BEGIN || + cfa == ROM_IF || cfa == ROM_ELSE || + cfa == ROM_WHILE || cfa == ROM_DOES || + cfa == ROM_RUNS ) { + tab++; // increase identation } - strBuf.append(" "); // append a space between words + out.append(" "); // append a space between words } - if ((tapeFile.getMemByte(cfa - 1) & 0x40) != 0) { // bit-6 = immediate flag - strBuf.append("IMMEDIATE"); + if ((tape.getMemByte(link) & 0x40) != 0) { // bit6 = immediate flag + out.append("IMMEDIATE"); } } /** * Decode a String - * @param strBuf Output StringBuilder buffer to append the string + * @param out Output StringBuilder buffer to append the string * @param address String location * @return string length in bytes */ - private int appendString(StringBuilder strBuf, int address) { - int strlen = tapeFile.getMemWord(address); // String Length + private int appendString(StringBuilder out, int address) { + int strlen = tape.getMemWord(address); // String Length address = address + 2; int c; for (int i = 0; i < strlen; i++) { - c = tapeFile.getMemByte(address) & 0xFF; - strBuf.append((char)c); + c = tape.getMemByte(address) & 0xFF; + out.append((char)c); address++; } - return strlen + 2; // return number of bytes + return strlen + 2; // return number of bytes } /** @@ -390,69 +467,69 @@ private int appendString(StringBuilder strBuf, int address) { * s eeeeeee mmmm mmmm mmmm mmmm mmmm mmmm * s = sign, eeeeeee = exponential (offseted by 65) * mmmm = mantissa (6 BCD digits) - * @param strBuf Output StringBuilder buffer to append decoded number + * @param out Output StringBuilder buffer to append decoded number * @param address Floatinf point number structure location */ - private int appendFloat(StringBuilder strBuf, int address) { - int loWord = tapeFile.getMemWord(address); - int hiWord = tapeFile.getMemWord(address + 2); - if ((hiWord & 0x8000) != 0) { // Negative Number - strBuf.append("-"); + private int appendFloat(StringBuilder out, int address) { + int loWord = tape.getMemWord(address); + int hiWord = tape.getMemWord(address + 2); + if ((hiWord & 0x8000) != 0) { // Negative Number + out.append("-"); } - int exp = ((hiWord & 0x7F00) >> 8) - 65; // Normalized Exponent - if((hiWord & 0x7F00) == 0) { // special case for 0. + int exp = ((hiWord & 0x7F00) >> 8) - 65; // Normalized Exponent + if((hiWord & 0x7F00) == 0) { // special case for 0. exp = 0; } int mantissa = ((hiWord & 0xFF) << 16) | loWord; // 6 BCD digits Mantissa - int e = 0; // Print Exponential Format: 1.23456E10 + int e = 0; // Print Exponential Format: 1.23456E10 if (exp >= -4 && exp <= 9) { // Print Normal Format: 123.456 - e = exp; // decimal point position - exp = 0; // flag Not Exponential Format + e = exp; // decimal point position + exp = 0; // flag Not Exponential Format } - if(e < 0) { // print numbers like .000123 - strBuf.append("."); + if(e < 0) { // print numbers like .000123 + out.append("."); while(e < -1) { - strBuf.append("0"); + out.append("0"); e++; } - e--; // force e = -1 + e--; // force e = -1 } - do { // print mantissa - strBuf.append((mantissa >> 20) & 0x0F); // print BCD digit - if(e == 0) { // decimal point position - strBuf.append("."); + do { // print mantissa + out.append((mantissa >> 20) & 0x0F); // print BCD digit + if(e == 0) { // decimal point position + out.append("."); } - e--; // decrement decimal place - mantissa = (mantissa & 0xFFFFF) << 4; // roll digits + e--; // decrement decimal place + mantissa = (mantissa & 0xFFFFF) << 4; // roll digits } while(mantissa != 0); - e++; // revert last decrement during mantissa printing + e++; // revert last decrement during mantissa printing - while(e > 0) { // print trailing zeros - strBuf.append("0"); - e--; // decrement decimal place - if(e == 0) { // decimal point position - strBuf.append("."); + while(e > 0) { // print trailing zeros + out.append("0"); + e--; // decrement decimal place + if(e == 0) { // decimal point position + out.append("."); } } - if(exp != 0) { // Print exponential value - strBuf.append("E").append(exp); + if(exp != 0) { // Print exponential value + out.append("E").append(exp); } return 4; } /** - * Decode an integer number (2 bytes) + * Decode an signed integer number (2 bytes) * @param address Integer number structure location * @return Decoded integer number */ private int getInt(int address) { - int number = tapeFile.getMemWord(address); - if ((number & 0x8000) != 0) { // if negative number.. - number = -(1 + (number ^ 0xFFFF)); // convert + int number = tape.getMemWord(address); + if ((number & 0x8000) != 0) { // if negative number.. + number = -(1 + (number ^ 0xFFFF)); // ..convert } return number; } diff --git a/src/JatmUI/CompareDialog.form b/src/JatmUI/CompareDialog.form new file mode 100644 index 0000000..c51047d --- /dev/null +++ b/src/JatmUI/CompareDialog.form @@ -0,0 +1,68 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/JatmUI/CompareDialog.java b/src/JatmUI/CompareDialog.java new file mode 100644 index 0000000..a2fa99f --- /dev/null +++ b/src/JatmUI/CompareDialog.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2016 Ricardo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package JatmUI; + +import Jatm.JaTape; + +/** + * + * @author Ricardo + */ +public class CompareDialog extends javax.swing.JDialog { + + /** + * Creates new form CompareDialog + */ + public CompareDialog(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + } + + public void compare(JaTape t1, JaTape t2) { + String s1 = t1.getFilename(); + String s2 = t2.getFilename(); + if(s1 == null ? s2 != null : !s1.equals(s2)) { + compareTextArea.append("#"); + } else { + compareTextArea.append(" "); + } + compareTextArea.append(String.format(" File Name: %s\t%s\n", s1, s2)); + + s1 = t1.getFileType(); + s2 = t2.getFileType(); + if(s1 == null ? s2 != null : !s1.equals(s2)) { + compareTextArea.append("#"); + } else { + compareTextArea.append(" "); + } + compareTextArea.append(String.format(" File Type: %s\t\t%s\n", s1, s2 )); + + int x1 = t1.getParameter(JaTape.LENGTH); + int x2 = t2.getParameter(JaTape.LENGTH); + if(x1 != x2) { + compareTextArea.append("#"); + } else { + compareTextArea.append(" "); + } + compareTextArea.append(String.format(" File Size: %5d\t\t%5d\n", x1, x2)); + + x1 = t1.getParameter(JaTape.ADDRESS); + x2 = t2.getParameter(JaTape.ADDRESS); + if(x1 != x2) { + compareTextArea.append("#"); + } else { + compareTextArea.append(" "); + } + compareTextArea.append(String.format(" Address: %4Xh\t\t%4Xh\n", x1, x2)); + + x1 = t1.getParameter(JaTape.CURR_WRD); + x2 = t2.getParameter(JaTape.CURR_WRD); + if(x1 != x2) { + compareTextArea.append("#"); + } else { + compareTextArea.append(" "); + } + compareTextArea.append(String.format(" Current Word: %4Xh\t\t%4Xh\n", x1, x2)); + + x1 = t1.getParameter(JaTape.CURRENT); + x2 = t2.getParameter(JaTape.CURRENT); + if(x1 != x2) { + compareTextArea.append("#"); + } else { + compareTextArea.append(" "); + } + compareTextArea.append(String.format(" CURRENT: %4Xh\t\t%4Xh\n", x1, x2 )); + + x1 = t1.getParameter(JaTape.CONTEXT); + x2 = t2.getParameter(JaTape.CONTEXT); + if(x1 != x2) { + compareTextArea.append("#"); + } else { + compareTextArea.append(" "); + } + compareTextArea.append(String.format(" CONTEXT: %4Xh\t\t%4Xh\n", x1, x2 )); + + x1 = t1.getParameter(JaTape.VOCLNK); + x2 = t2.getParameter(JaTape.VOCLNK); + if(x1 != x2) { + compareTextArea.append("#"); + } else { + compareTextArea.append(" "); + } + compareTextArea.append(String.format(" VOCLNK: %4Xh\t\t%4Xh\n", x1, x2 )); + + x1 = t1.getParameter(JaTape.STKBOT); + x2 = t2.getParameter(JaTape.STKBOT); + if(x1 != x2) { + compareTextArea.append("#"); + } else { + compareTextArea.append(" "); + } + compareTextArea.append(String.format(" Stack Bottom: %4Xh\t\t%4Xh\n", x1, x2)); + + compareTextArea.append("\nBlocks Similarity: Header\tData\n"); + compareTextArea.append(String.format(" %.1f%%\t%.1f%%", + compareBlocks(t1.getHeaderBlock(),t2.getHeaderBlock()), + compareBlocks(t1.getDataBlock(),t2.getDataBlock()) )); + } + + private double compareBlocks(byte[] b1, byte[] b2) { + int size1 = b1.length; + int size2 = b2.length; + int equalities = 0; // similarities counter + int smallestSize = (size1size2)?size1:size2; // largest size + for(int i=0; i < smallestSize; i++) { + if(b1[i] == b2[i]) { // count equalities + equalities++; + } + } + return 100.0 * (double)equalities / (double)largestSize ; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + compareScrollPane = new javax.swing.JScrollPane(); + compareTextArea = new javax.swing.JTextArea(); + closeButton = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("Compare Files"); + + compareTextArea.setColumns(20); + compareTextArea.setRows(5); + compareScrollPane.setViewportView(compareTextArea); + + closeButton.setText("Close"); + closeButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + closeButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(compareScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(closeButton) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(compareScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 273, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(closeButton) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed + dispose(); + }//GEN-LAST:event_closeButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton closeButton; + private javax.swing.JScrollPane compareScrollPane; + private javax.swing.JTextArea compareTextArea; + // End of variables declaration//GEN-END:variables +} diff --git a/src/JatmUI/JatmUI.form b/src/JatmUI/JatmUI.form index 956f6c4..a37a8db 100644 --- a/src/JatmUI/JatmUI.form +++ b/src/JatmUI/JatmUI.form @@ -383,6 +383,9 @@ + + + diff --git a/src/JatmUI/JatmUI.java b/src/JatmUI/JatmUI.java index b23c5bd..ce05248 100644 --- a/src/JatmUI/JatmUI.java +++ b/src/JatmUI/JatmUI.java @@ -53,8 +53,8 @@ * @author Ricardo F. Lopes */ public class JatmUI extends javax.swing.JFrame { - public static final String version = "0.8"; - + public static final String version = "0.85"; + List jaTapeList; // Tape Files List JaTapeListTableModel tapeListTableModel; // GUI List Table JatmFileTap fileTap; // TAP tape @@ -81,26 +81,26 @@ public JatmUI() { | IllegalAccessException e) { } - userCharSet = new CharacterSet(); - - jatmIcon = new ImageIcon("src/JatmUI/resources/jatm.png"); // Title Icon + userCharSet = new CharacterSet(); + + jatmIcon = new ImageIcon("src/JatmUI/resources/jatm.png"); // Title Icon jaTapeList = new ArrayList<>(); // List of Tape Files tapeListTableModel = new JaTapeListTableModel(jaTapeList); // GUI list - + // File Load/Save Objects fileTap = new JatmFileTap(); fileJac = new JatmFileJac(); fileBin = new JatmFileBin(); fileHex = new JatmFileHex(); fileWav = new JatmFileWav(); - + // File Filters fileFilterTap = new BasicFileFilter(fileTap.getExtension(), fileTap.getDescription()); fileFilterJac = new BasicFileFilter(fileJac.getExtension(), fileJac.getDescription()); fileFilterBin = new BasicFileFilter(fileBin.getExtension(), fileBin.getDescription()); fileFilterHex = new BasicFileFilter(fileHex.getExtension(), fileHex.getDescription()); fileFilterWav = new BasicFileFilter(fileWav.getExtension(), fileWav.getDescription()); - + // File Chooser jatmFileChooser = new JFileChooser(); jatmFileChooser.setFileFilter(fileFilterTap); // Default Choose @@ -110,12 +110,12 @@ public JatmUI() { jatmFileChooser.addChoosableFileFilter(fileFilterWav); initComponents(); - + setTitle("JAtm - version " + version); setIconImage(jatmIcon.getImage()); statusBarRightText.setText("Java runtime v" + System.getProperty("java.version")); } - + // Right Text on Status Bar: shows the number of tape files in list private void setStatusBarLeft(int files) { if(files == 0) { @@ -646,6 +646,11 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { compare.setIcon(new javax.swing.ImageIcon(getClass().getResource("/JatmUI/resources/View.png"))); // NOI18N compare.setText("Compare.."); compare.setToolTipText("Compare Selected Files"); + compare.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + compareActionPerformed(evt); + } + }); toolsMenu.add(compare); fixCrc.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F, java.awt.event.InputEvent.CTRL_MASK)); @@ -722,11 +727,11 @@ private void msgNoSelectionError() { "No file selected", "Error", JOptionPane.ERROR_MESSAGE); - } - + } + private void loadFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadFileActionPerformed int result; - + jatmFileChooser.setMultiSelectionEnabled(true); // Multiple files selection if(jatmFileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { @@ -735,7 +740,7 @@ private void loadFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS for (File file : files) { String filename = file.getName(); String ext = filename.substring(filename.lastIndexOf(".")+1,filename.length()); - + JatmFile jatmFile = fileBin; // BIN file load if( ext.equalsIgnoreCase(fileTap.getExtension()) ) { // TAP file load jatmFile = fileTap; @@ -748,8 +753,8 @@ private void loadFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS } if( ext.equalsIgnoreCase(fileWav.getExtension()) ) { // WAV file load jatmFile = fileWav; - } - + } + result = jatmFile.load(file.toPath(), jaTapeList); if(result <= 0) { // an error occured JOptionPane.showMessageDialog(this, @@ -758,9 +763,9 @@ private void loadFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS JOptionPane.ERROR_MESSAGE); break; } else { - + } - } + } tapeListTableModel.fireTableDataChanged(); // refresh table draw this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } @@ -770,8 +775,8 @@ private void loadFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS private void exitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitActionPerformed if(!jaTapeList.isEmpty()) { int action; - action = JOptionPane.showConfirmDialog(this, - "Do you really want to quit?", + action = JOptionPane.showConfirmDialog(this, + "Do you really want to quit?", "Confirm Exit", JOptionPane.OK_CANCEL_OPTION); if(action != JOptionPane.OK_OPTION) { @@ -788,7 +793,7 @@ private void aboutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:e +" - version " + version +"
www.jupiter-ace.co.uk" +"


Copyright \u00a9 2015-2016 Ricardo Fernandes Lopes

" - + +"


This program is free software: you can redistribute it and/or modify" +"
it under the terms of the GNU General Public License as published by" +"
the Free Software Foundation, either version 3 of the License, or" @@ -800,12 +805,12 @@ private void aboutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:e +"
See the GNU General Public License for more details.

" +"


You should have received a copy of the GNU General Public License" - +"
along with this program. If not, see http://www.gnu.org/licenses/

" - + +"
along with this program. If not, see http://www.gnu.org/licenses/

" + +"


JAtm includes or uses:" +"

  • Silk Icon Set http://www.famfamfam.com/lab/icon/

", "About JAtm", - JOptionPane.INFORMATION_MESSAGE, icon); + JOptionPane.INFORMATION_MESSAGE, icon); }//GEN-LAST:event_aboutActionPerformed private void editNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editNameActionPerformed @@ -819,7 +824,7 @@ private void editNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS tape.setFilename(result); tape.fixCrc(); tapeListTable.updateUI(); - } + } } else { // Abort if no user selection msgNoSelectionError(); } @@ -829,7 +834,7 @@ private void moveUpActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST: int[] selection = tapeListTable.getSelectedRows(); // Selection list int row; JaTape tape; - + if(selection.length > 0) { // Check if anything is selected if(selection[0] > 0) { // Check first row selection for (int i=0; i0) { int action; - action = JOptionPane.showConfirmDialog(this, - "Do you really want to clear all list?", + action = JOptionPane.showConfirmDialog(this, + "Do you really want to clear all list?", "Confirm Clear", JOptionPane.OK_CANCEL_OPTION); if(action == JOptionPane.OK_OPTION) { @@ -894,7 +899,7 @@ private void moveDownActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS int[] selection = tapeListTable.getSelectedRows(); // Selection list int row; JaTape tape; - + if(selection.length > 0) { // Check if anything is selected if(selection[selection.length-1] < jaTapeList.size()-1) { // Check last row selection for (int i=selection.length-1; i >= 0; i--) { // MOve All selection down @@ -913,14 +918,14 @@ private void moveDownActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS } } else { msgNoSelectionError(); // No files selected Error - } + } }//GEN-LAST:event_moveDownActionPerformed private void moveTopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moveTopActionPerformed int[] selection = tapeListTable.getSelectedRows(); // Selection list int row; JaTape tape; - + if(selection.length > 0) { // Check if anything is selected int firstSelection = selection[0]; if(firstSelection > 0) { // Check first row selection @@ -944,14 +949,14 @@ private void moveTopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST } } else { msgNoSelectionError(); // No files selected Error - } + } }//GEN-LAST:event_moveTopActionPerformed private void moveBottomActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moveBottomActionPerformed int[] selection = tapeListTable.getSelectedRows(); // Selection list int row; JaTape tape; - + if(selection.length > 0) { // Check if anything is selected int lastSelection = selection[selection.length-1]; if(lastSelection < jaTapeList.size()-1) { // Check last row selection @@ -975,12 +980,12 @@ private void moveBottomActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI } } else { msgNoSelectionError(); // No files selected Error - } + } }//GEN-LAST:event_moveBottomActionPerformed private void removeItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeItemActionPerformed int[] selection = tapeListTable.getSelectedRows(); // Selection list - + if(selection.length > 0) { // Check if anything is selected for (int row=selection.length-1; row>=0; row--) { jaTapeList.remove(selection[row]); // delete selected rows @@ -999,7 +1004,7 @@ private void saveFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS msgNoSelectionError(); return; } - + jatmFileChooser.setMultiSelectionEnabled(false); jatmFileChooser.setSelectedFile(new File("")); // no default filename @@ -1046,7 +1051,7 @@ private void saveFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS JatmFile jatmFile = null; Boolean isMultiTapeFile = false; // File Format accepts more than one tape - + if( ext.equalsIgnoreCase(fileTap.getExtension()) ) { // TAP file save jatmFile = fileTap; isMultiTapeFile = true; @@ -1071,7 +1076,7 @@ private void saveFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRS this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); jatmFile.save(path, jaTapeList, selection); this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } else { // Multiple Tape files not allowed with selected tape format + } else { // Multiple Tape files not allowed with selected tape format JOptionPane.showMessageDialog(this, "File Format: ."+ext +"\ndo not accept multiple tape files", @@ -1093,7 +1098,7 @@ private void viewCharactersActionPerformed(java.awt.event.ActionEvent evt) {//GE JaTape tape = jaTapeList.get( selection[0] ); ViewCharacterSetDialog dialog; dialog = new ViewCharacterSetDialog(this, true, tape.getData(), 0, userCharSet, tape.getFilename()); - dialog.setVisible(true); + dialog.setVisible(true); } else { // No files selected to save msgNoSelectionError(); } @@ -1106,7 +1111,7 @@ private void viewScreenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI JaTape tape = jaTapeList.get( selection[0] ); ViewScreenDialog dialog; dialog = new ViewScreenDialog(this,true,tape.getData(),userCharSet,tape.getFilename()); - dialog.setVisible(true); + dialog.setVisible(true); } else { // No files selected to save msgNoSelectionError(); } @@ -1184,22 +1189,39 @@ private void duplicateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIR if(selection.length > 0) { JaTape tape; for (int row = 0; row < selection.length; row++) { - + tape = new JaTape(jaTapeList.get( selection[row] )); // create a copy jaTapeList.add(tape); // append to list end } tapeListTable.clearSelection(); tapeListTableModel.fireTableDataChanged(); // refresh table draw - setStatusBarLeft(jaTapeList.size()); // Update Status Bar + setStatusBarLeft(jaTapeList.size()); // Update Status Bar } else { // Abort if no selection msgNoSelectionError(); } }//GEN-LAST:event_duplicateActionPerformed + private void compareActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_compareActionPerformed + int[] selection = tapeListTable.getSelectedRows(); // Get User Selection + if(selection.length == 2 ) { + CompareDialog dialog; + dialog = new CompareDialog(this,true); + JaTape t1 = jaTapeList.get(selection[0]); + JaTape t2 = jaTapeList.get(selection[1]); + dialog.compare(t1,t2); + dialog.setVisible(true); + } else { // Abort if no selection + JOptionPane.showMessageDialog(this, + "Select exactly 2 files for comparison!", + "Compare Error", + JOptionPane.ERROR_MESSAGE); + } + }//GEN-LAST:event_compareActionPerformed + /** * @param args the command line arguments */ - + public static void main(String args[]) { /* Create and display the form */ java.awt.EventQueue.invokeLater(new Runnable() { diff --git a/src/JatmUI/ViewFileContentsDialog.form b/src/JatmUI/ViewFileContentsDialog.form index e5682c8..46e3b13 100644 --- a/src/JatmUI/ViewFileContentsDialog.form +++ b/src/JatmUI/ViewFileContentsDialog.form @@ -1,6 +1,10 @@
+ + + + @@ -37,37 +41,31 @@ - - + + + - + - + - - - + + + + + + - - - - - - - - - - @@ -168,5 +166,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/JatmUI/ViewFileContentsDialog.java b/src/JatmUI/ViewFileContentsDialog.java index cb02fbd..69127a4 100644 --- a/src/JatmUI/ViewFileContentsDialog.java +++ b/src/JatmUI/ViewFileContentsDialog.java @@ -30,21 +30,26 @@ public class ViewFileContentsDialog extends javax.swing.JDialog { + private final JaTape tape; + private final JaVocabulary forthVocabulary; + private final byte[] data; + + // Special Jupiter Ace like Character Fonts private static Font baseAceFont; - private static final Font aceFont; + private static Font aceFontSmall; + private static Font aceFontMedium; + private static Font aceFontLarge; static { InputStream fontStream = ViewFileContentsDialog.class.getResourceAsStream("resources/JupiterAce2.ttf"); try { baseAceFont = Font.createFont(Font.TRUETYPE_FONT, fontStream); + } catch (FontFormatException | IOException ex) { Logger.getLogger(ViewFileContentsDialog.class.getName()).log(Level.SEVERE, null, ex); } - aceFont = baseAceFont.deriveFont(Font.PLAIN,8); + + } - - private final JaTape tape; - private final byte[] data; - private final JaVocabulary forthVocabulary; /** * Creates new form HexViewDialog @@ -57,14 +62,22 @@ public ViewFileContentsDialog(java.awt.Frame parent, boolean modal, JaTape t) { tape = t; data = t.getData(); - forthVocabulary = new JaVocabulary(t); + forthVocabulary = new JaVocabulary(t); // Tape File Decompiler + aceFontSmall = baseAceFont.deriveFont(Font.PLAIN,8); // Small Fonts + aceFontMedium = baseAceFont.deriveFont(Font.PLAIN,12); // Medium Fonts + aceFontLarge = baseAceFont.deriveFont(Font.PLAIN,16); // Large Fonts initComponents(); - hexDumpTextArea.setFont(aceFont); - codeTextArea.setFont(aceFont); - vocTextArea.setFont(aceFont); + + // Set initial Fonts + vocTextArea.setFont(aceFontSmall); + hexDumpTextArea.setFont(aceFontSmall); + codeTextArea.setFont(aceFontSmall); + + // Set Dialog Title this.setTitle("File Contents [ " + t.getFilename()+" ]"); + // Populate frames showVocabulary(); showCode(); showHexDump(); @@ -74,93 +87,94 @@ public ViewFileContentsDialog(java.awt.Frame parent, boolean modal, JaTape t) { * Show File Vocabulary */ private void showVocabulary() { - if (tape.isDict()) { - vocTextArea.append(" Dict: "); - } else { - vocTextArea.append(" Byt: "); - } - vocTextArea.append(tape.getFilename()); - - vocTextArea.append(String.format("\nAddress: %04Xh (%d)\n Size: %d bytes\n\n", + vocTextArea.append(invert(" "+tape.getFileType()+": ")+" "+tape.getFilename()); + vocTextArea.append(String.format(" %d bytes\n\n", + tape.getParameter(JaTape.LENGTH))); + vocTextArea.append(String.format(" Address: %04Xh ( %5d )\n", tape.getParameter(JaTape.ADDRESS), - tape.getParameter(JaTape.ADDRESS), - tape.getParameter(JaTape.LENGTH) - )); - vocTextArea.append("WordLink | CURRENT | CONTEXT | VocLink | Stack\n"); - vocTextArea.append(String.format(" %04Xh | %04Xh | %04Xh | %04Xh | %04Xh\n", + tape.getParameter(JaTape.ADDRESS))); + vocTextArea.append(String.format("Current Word: %04Xh ( %5d )\n", tape.getParameter(JaTape.CURR_WRD), + tape.getParameter(JaTape.CURR_WRD))); + vocTextArea.append(String.format(" CURRENT: %04Xh ( %5d )\n", tape.getParameter(JaTape.CURRENT), + tape.getParameter(JaTape.CURRENT))); + vocTextArea.append(String.format(" CONTEXT: %04Xh ( %5d )\n", tape.getParameter(JaTape.CONTEXT), + tape.getParameter(JaTape.CONTEXT))); + vocTextArea.append(String.format(" VOCLNK: %04Xh ( %5d )\n", tape.getParameter(JaTape.VOCLNK), - tape.getParameter(JaTape.STKBOT) - )); - vocTextArea.append(String.format(" (%5d) | (%5d) | (%5d) | (%5d) | (%5d)\n", - tape.getParameter(JaTape.CURR_WRD), - tape.getParameter(JaTape.CURRENT), - tape.getParameter(JaTape.CONTEXT), - tape.getParameter(JaTape.VOCLNK), - tape.getParameter(JaTape.STKBOT) - )); - - if (tape.isDict()) { - vocTextArea.append(String.format("\nVLIST ( %d words )\n", - forthVocabulary.vocabularySize() - )); - vocTextArea.append(forthVocabulary.vlist()); - vocTextArea.append("\n\nCode Field Addresses:\n"); - vocTextArea.append(forthVocabulary.listCfa()); + tape.getParameter(JaTape.VOCLNK))); + vocTextArea.append(String.format("Stack Bottom: %04Xh ( %5d )\n\n", + tape.getParameter(JaTape.STKBOT), + tape.getParameter(JaTape.STKBOT))); + + if (tape.isDict()) { // Dictionary File Type (dict) + vocTextArea.append(invert(" CFA and Word list ")); + vocTextArea.append("\n"); + vocTextArea.append(forthVocabulary.getVlist()); } vocTextArea.setEditable(false); // Avoid user editing - vocTextArea.setCaretPosition(0); // move to top of text + vocTextArea.setCaretPosition(0); // Move to text top } /** - * Show file code + * Show file code listings */ private void showCode() { - if (tape.isDict()) { // Dcit File Disassemble - codeTextArea.append(forthVocabulary.listAllWords()); - } else { // Z80 File Disassemble + if (tape.isDict()) { + // Dictionary File Type (dict) + codeTextArea.append(forthVocabulary.dictCodeListing()); + } else { + // Binary File Type (byt) codeTextArea.append("; Jupiter Ace Binary File \"" + tape.getFilename() + "\"\n\n" ); codeTextArea.append(String.format(" .org %04Xh\n\n", tape.getParameter(JaTape.ADDRESS) )); - codeTextArea.append("*** Z80 Disassembler not implemented ***\n"); + codeTextArea.append(invert("*** Z80 Disassembler not implemented ***\n")); } + codeTextArea.setEditable(false); // Avoid user editing codeTextArea.setCaretPosition(0); // move to top of text } + private String invert(String str) { + char[] charArray = str.toCharArray(); + for(int i=0; i//GEN-BEGIN:initComponents private void initComponents() { - CloseButton = new javax.swing.JButton(); + buttonGroup1 = new javax.swing.ButtonGroup(); jTabbedPane1 = new javax.swing.JTabbedPane(); vocScrollPane = new javax.swing.JScrollPane(); vocTextArea = new javax.swing.JTextArea(); @@ -189,6 +203,11 @@ private void initComponents() { codeTextArea = new javax.swing.JTextArea(); hexDumpScrollPane = new javax.swing.JScrollPane(); hexDumpTextArea = new javax.swing.JTextArea(); + CloseButton = new javax.swing.JButton(); + jPanel1 = new javax.swing.JPanel(); + SmallFontRadioButton = new javax.swing.JRadioButton(); + MediumFontRadioButton = new javax.swing.JRadioButton(); + LargeFontRadioButton = new javax.swing.JRadioButton(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("File Info"); @@ -198,15 +217,6 @@ private void initComponents() { setMinimumSize(new java.awt.Dimension(300, 100)); setPreferredSize(new java.awt.Dimension(650, 500)); - CloseButton.setMnemonic('C'); - CloseButton.setText("Close"); - CloseButton.setToolTipText(""); - CloseButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - CloseButtonActionPerformed(evt); - } - }); - jTabbedPane1.setToolTipText(""); jTabbedPane1.setMinimumSize(new java.awt.Dimension(120, 100)); jTabbedPane1.setPreferredSize(new java.awt.Dimension(640, 480)); @@ -236,23 +246,83 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { jTabbedPane1.addTab("Hex Dump", new javax.swing.ImageIcon(getClass().getResource("/JatmUI/resources/cog.png")), hexDumpScrollPane, "Hex and ASCII file dump"); // NOI18N + CloseButton.setMnemonic('C'); + CloseButton.setText("Close"); + CloseButton.setToolTipText(""); + CloseButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + CloseButtonActionPerformed(evt); + } + }); + + jPanel1.setBorder(javax.swing.BorderFactory.createEtchedBorder()); + + buttonGroup1.add(SmallFontRadioButton); + SmallFontRadioButton.setSelected(true); + SmallFontRadioButton.setText("Small Font"); + SmallFontRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + SmallFontRadioButtonActionPerformed(evt); + } + }); + + buttonGroup1.add(MediumFontRadioButton); + MediumFontRadioButton.setText("Medium Font"); + MediumFontRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + MediumFontRadioButtonActionPerformed(evt); + } + }); + + buttonGroup1.add(LargeFontRadioButton); + LargeFontRadioButton.setText("Large Font"); + LargeFontRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + LargeFontRadioButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(SmallFontRadioButton) + .addGap(18, 18, 18) + .addComponent(MediumFontRadioButton) + .addGap(18, 18, 18) + .addComponent(LargeFontRadioButton) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(MediumFontRadioButton) + .addComponent(SmallFontRadioButton) + .addComponent(LargeFontRadioButton)) + ); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(18, 18, 18) .addComponent(CloseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 127, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) - .addComponent(jTabbedPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jTabbedPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 652, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(1, 1, 1) - .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(CloseButton) + .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 421, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(CloseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()) ); @@ -263,16 +333,41 @@ private void CloseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-F dispose(); }//GEN-LAST:event_CloseButtonActionPerformed + private void SmallFontRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_SmallFontRadioButtonActionPerformed + // Set Small Fonts + hexDumpTextArea.setFont(aceFontSmall); + codeTextArea.setFont(aceFontSmall); + vocTextArea.setFont(aceFontSmall); + }//GEN-LAST:event_SmallFontRadioButtonActionPerformed + + private void LargeFontRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_LargeFontRadioButtonActionPerformed + // Set Large Fonts + hexDumpTextArea.setFont(aceFontLarge); + codeTextArea.setFont(aceFontLarge); + vocTextArea.setFont(aceFontLarge); + }//GEN-LAST:event_LargeFontRadioButtonActionPerformed + + private void MediumFontRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_MediumFontRadioButtonActionPerformed + // Set Medium Fonts + hexDumpTextArea.setFont(aceFontMedium); + codeTextArea.setFont(aceFontMedium); + vocTextArea.setFont(aceFontMedium); + }//GEN-LAST:event_MediumFontRadioButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton CloseButton; + private javax.swing.JRadioButton LargeFontRadioButton; + private javax.swing.JRadioButton MediumFontRadioButton; + private javax.swing.JRadioButton SmallFontRadioButton; + private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JScrollPane codeScrollPane; private javax.swing.JTextArea codeTextArea; private javax.swing.JScrollPane hexDumpScrollPane; private javax.swing.JTextArea hexDumpTextArea; + private javax.swing.JPanel jPanel1; private javax.swing.JTabbedPane jTabbedPane1; private javax.swing.JScrollPane vocScrollPane; private javax.swing.JTextArea vocTextArea; // End of variables declaration//GEN-END:variables - }