diff --git a/src/main/java/mars/ArgumentParser.kt b/src/main/java/mars/ArgumentParser.kt new file mode 100644 index 0000000..ae26176 --- /dev/null +++ b/src/main/java/mars/ArgumentParser.kt @@ -0,0 +1,37 @@ +package mars + +import net.sourceforge.argparse4j.impl.Arguments +import net.sourceforge.argparse4j.inf.Argument +import net.sourceforge.argparse4j.inf.ArgumentParser +import net.sourceforge.argparse4j.inf.Namespace +import kotlin.collections.HashMap +import kotlin.reflect.KProperty + + +val ARG_PARSER_MAP = HashMap() + +fun ArgumentParser.parseToVars(args: Array) = parseArgs(args).also { ARG_PARSER_MAP[this] = it } + +class ArgParserDelegate(val a: ArgumentParser, val name: String) +{ + operator fun getValue(thisRef: Any?, property: KProperty<*>): T? + { + return ARG_PARSER_MAP[a]?.get(name) + } +} + +fun ArgumentParser.arg(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}): ArgParserDelegate +{ + val varname = names.last().trimStart('-').replace('-', '_') + val arg = addArgument(*names).dest(varname) + if (help != null) arg.help(help) + apply(arg) + return ArgParserDelegate(this, varname) +} + +fun ArgumentParser.string(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}) = + arg(*names, help=help, apply=apply) + +fun ArgumentParser.flag(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}) = + arg(*names, help=help) { action(Arguments.storeTrue()); apply() } + diff --git a/src/main/java/mars/Extensions.kt b/src/main/java/mars/Extensions.kt index 672afea..6786b6d 100644 --- a/src/main/java/mars/Extensions.kt +++ b/src/main/java/mars/Extensions.kt @@ -1,38 +1,15 @@ package mars -import net.sourceforge.argparse4j.impl.Arguments -import net.sourceforge.argparse4j.inf.Argument -import net.sourceforge.argparse4j.inf.ArgumentParser -import net.sourceforge.argparse4j.inf.Namespace -import java.util.* -import kotlin.collections.HashMap -import kotlin.reflect.KProperty +fun ULong.toHex(pad: Int = 16) = "0x${this.toString(16).uppercase().padStart(pad, '0')}" +fun Long.toHex() = toULong().toHex(16) +fun UInt.toHex(pad: Int = 8) = toULong().toHex(pad) +fun Int.toHex() = toUInt().toHex(8) +fun Char.toHex() = code.toUInt().toHex(2) -val ARG_PARSER_MAP = HashMap() - -fun ArgumentParser.parseToVars(args: Array) = parseArgs(args).also { ARG_PARSER_MAP[this] = it } - -class ArgParserDelegate(val a: ArgumentParser, val name: String) +fun String.detectRadix() = if (length < 2) 10 else when (substring(0, 2)) { - operator fun getValue(thisRef: Any?, property: KProperty<*>): T? - { - return ARG_PARSER_MAP[a]?.get(name) - } + "0x", "0X" -> 16 + "0b", "0B" -> 2 + else -> 10 } - -fun ArgumentParser.arg(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}): ArgParserDelegate -{ - val varname = names.last().trimStart('-').replace('-', '_') - val arg = addArgument(*names).dest(varname) - if (help != null) arg.help(help) - apply(arg) - return ArgParserDelegate(this, varname) -} - -fun ArgumentParser.string(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}) = - arg(*names, help=help, apply=apply) - -fun ArgumentParser.flag(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}) = - arg(*names, help=help) { action(Arguments.storeTrue()); apply() } - diff --git a/src/main/java/mars/Globals.java b/src/main/java/mars/Globals.java index e6e63d3..e931607 100644 --- a/src/main/java/mars/Globals.java +++ b/src/main/java/mars/Globals.java @@ -80,7 +80,7 @@ public class Globals /** * List of accepted file extensions for MIPS assembly source files. */ - public static final ArrayList fileExtensions = getFileExtensions(); + public static final ArrayList fileExtensions = getFileExtensions(); /** * Maximum length of scrolled message window (MARS Messages and Run I/O) @@ -274,7 +274,7 @@ public class Globals { limit = Integer.parseInt(properties.getProperty(propertyName, Integer.toString(defaultValue))); } - catch (NumberFormatException nfe) + catch (NumberFormatException ignored) { } // do nothing, I already have a default return limit; @@ -283,9 +283,9 @@ public class Globals // Read assembly language file extensions from properties file. Resulting // string is tokenized into array list (assume StringTokenizer default delimiters). - private static ArrayList getFileExtensions() + private static ArrayList getFileExtensions() { - ArrayList extensionsList = new ArrayList(); + ArrayList extensionsList = new ArrayList<>(); String extensions = getPropertyEntry(configPropertiesFile, "Extensions"); if (extensions != null) { @@ -305,9 +305,9 @@ public class Globals * @return ArrayList. Each item is file path to .class file of a class that implements MarsTool. If none, returns * empty list. */ - public static ArrayList getExternalTools() + public static ArrayList getExternalTools() { - ArrayList toolsList = new ArrayList(); + ArrayList toolsList = new ArrayList<>(); String delimiter = ";"; String tools = getPropertyEntry(configPropertiesFile, "ExternalTools"); if (tools != null) @@ -339,11 +339,11 @@ public class Globals * * @return ArrayList of SyscallNumberOverride objects */ - public ArrayList getSyscallOverrides() + public ArrayList getSyscallOverrides() { - ArrayList overrides = new ArrayList(); + ArrayList overrides = new ArrayList<>(); Properties properties = PropertiesFile.loadPropertiesFromFile(syscallPropertiesFile); - Enumeration keys = properties.keys(); + Enumeration keys = properties.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); diff --git a/src/main/java/mars/tools/BitmapDisplay.kt b/src/main/java/mars/tools/BitmapDisplay.kt index fea00db..7a22e10 100644 --- a/src/main/java/mars/tools/BitmapDisplay.kt +++ b/src/main/java/mars/tools/BitmapDisplay.kt @@ -1,13 +1,20 @@ package mars.tools -import mars.mips.hardware.AccessNotice -import mars.mips.hardware.Memory -import mars.mips.hardware.MemoryAccessNotice +import mars.Globals +import mars.detectRadix +import mars.mips.hardware.* +import mars.toHex import mars.util.Binary import java.awt.* +import java.awt.BorderLayout.* +import java.awt.event.KeyEvent +import java.awt.event.KeyListener import java.util.* import javax.swing.* import javax.swing.border.EmptyBorder +import kotlin.collections.ArrayList +import kotlin.collections.HashMap +import kotlin.collections.HashSet /* Copyright (c) 2010-2011, Pete Sanderson and Kenneth Vollmar @@ -49,6 +56,7 @@ class BitmapDisplay : AbstractMarsToolAndApplication private lateinit var uiWidthSelector: JComboBox private lateinit var uiHeightSelector: JComboBox private lateinit var uiBaseAddressSelector: JComboBox + private lateinit var uiKeyboardCheckbox: JCheckBox private lateinit var canvas: JPanel private lateinit var results: JPanel @@ -68,8 +76,18 @@ class BitmapDisplay : AbstractMarsToolAndApplication private val baseAddress get() = baseAddresses[uiBaseAddressSelector.selectedIndex] + // Keyboard++ + private var keyboardAddr: UInt = 0xFFFF0010u + private var keyboardAttached = false + private lateinit var uiKeyboard: JTextField + private lateinit var grid: Grid + val pooledKeyEvents = hashMapOf>( + 0x00u to ArrayList(), 0x10u to ArrayList(), 0x20u to ArrayList() + ) + val downKeys = HashSet() + /** * Simple constructor, likely used to run a stand-alone bitmap display tool. * @@ -78,9 +96,9 @@ class BitmapDisplay : AbstractMarsToolAndApplication */ constructor(title: String, heading: String) : super(title, heading) - constructor() : super("Azalea's Modified Bitmap Display", heading) + constructor() : super("Azalea's Bitmap Display++", HEADING) - override fun getName() = "Bitmap Display" + override fun getName() = HEADING /** * Override the inherited method, which registers us as an Observer over the static data segment (starting address @@ -102,6 +120,7 @@ class BitmapDisplay : AbstractMarsToolAndApplication highAddress = -4 } addAsObserver(baseAddress, highAddress) + addAsObserver(keyboardAddr.toInt(), (keyboardAddr + 0x20u).toInt()) } /** @@ -127,13 +146,72 @@ class BitmapDisplay : AbstractMarsToolAndApplication * Update display when connected MIPS program accesses (data) memory. * * @param memory the attached memory - * @param accessNotice information provided by memory in MemoryAccessNotice object + * @param ac information provided by memory in MemoryAccessNotice object */ - override fun processMIPSUpdate(memory: Observable, accessNotice: AccessNotice) + override fun processMIPSUpdate(memory: Observable, ac: AccessNotice) { - if (accessNotice.accessType == AccessNotice.WRITE) + if (ac !is MemoryAccessNotice || ac.accessType != AccessNotice.WRITE) return + val addr = ac.address.toUInt() + + // For the keyboard + if (addr in keyboardAddr..keyboardAddr + 0x30u) { - updateColorForAddress(accessNotice as MemoryAccessNotice) + // Offset 0x01 or 0x11 or 0x21 bytes are for telling the keyboard that the events are received + var offset = addr - keyboardAddr + + // Clear the segment + if (offset == 0x01u || offset == 0x11u) + { + if (Globals.memory.getByte(addr.toInt()) != 1) return + + println("Offset written: ${offset.toHex(2)}") + + // Clear bytes if 1 is written to the 0x01 offset + offset -= 1u + val range = offset..offset + 0x0Fu + synchronized(Globals.memoryAndRegistersLock) + { + range.map { keyboardAddr + it }.forEach { Globals.memory.setByte(it.toInt(), 0) } + } + pooledKeyEvents[offset]!!.clear() + println("[Keyboard++] Keyboard segment range $range cleared") + } + return + } + + // For the display + updateColorForAddress(ac) + } + + /** + * Event on key press + * + * @param offset Event type / offset + * @param e Event + */ + fun keyEvent(offset: UInt, e: KeyEvent) + { + if (!keyboardAttached) return + println("[Keyboard++] ${e.id} '${e.keyChar}' ${e.keyCode}") + val queue = pooledKeyEvents[offset]!! + + // Check for more than 7 key events queued + if (queue.size == 7) return + + // Add to queue + queue.add(e) + + // Add to memory + synchronized(Globals.memoryAndRegistersLock) + { + // Change 0x0: Number of events + var addr = keyboardAddr + offset + println("[Keyboard++] Address ${addr.toHex(8)} set to ${queue.size} | ${addr.toHex(8)} set to ${e.keyCode} (${e.keyChar.toHex()})") + Globals.memory.setByte(addr.toInt(), queue.size) + + // Set the keycode + addr += queue.size.toUInt() * 2u + Globals.memory.setHalf(addr.toInt(), e.keyCode) } } @@ -213,12 +291,10 @@ class BitmapDisplay : AbstractMarsToolAndApplication // UI components and layout for left half of GUI, where settings are specified. private fun buildOrganizationArea(): JComponent { - val organization = JPanel(GridLayout(8, 1)) - uiUnitWidthSelector = JComboBox(arrayOf(1, 2, 4, 8, 16, 32)).apply { isEditable = false background = backgroundColor - selectedIndex = 2 + selectedIndex = 1 toolTipText = "Width in pixels of rectangle representing memory word" addActionListener { grid = createNewGrid() @@ -229,7 +305,7 @@ class BitmapDisplay : AbstractMarsToolAndApplication uiUnitHeightSelector = JComboBox(arrayOf(1, 2, 4, 8, 16, 32)).apply { isEditable = false background = backgroundColor - selectedIndex = 2 + selectedIndex = 1 toolTipText = "Height in pixels of rectangle representing memory word" addActionListener { grid = createNewGrid() @@ -286,35 +362,100 @@ class BitmapDisplay : AbstractMarsToolAndApplication } } - // ALL COMPONENTS FOR "ORGANIZATION" SECTION - val unitWidthInPixelsRow = panelWithBorderLayout - unitWidthInPixelsRow.border = emptyBorder - unitWidthInPixelsRow.add(JLabel("Unit Width in Pixels "), BorderLayout.WEST) - unitWidthInPixelsRow.add(uiUnitWidthSelector, BorderLayout.EAST) - val unitHeightInPixelsRow = panelWithBorderLayout - unitHeightInPixelsRow.border = emptyBorder - unitHeightInPixelsRow.add(JLabel("Unit Height in Pixels "), BorderLayout.WEST) - unitHeightInPixelsRow.add(uiUnitHeightSelector, BorderLayout.EAST) - val widthInPixelsRow = panelWithBorderLayout - widthInPixelsRow.border = emptyBorder - widthInPixelsRow.add(JLabel("Display Width in Pixels "), BorderLayout.WEST) - widthInPixelsRow.add(uiWidthSelector, BorderLayout.EAST) - val heightInPixelsRow = panelWithBorderLayout - heightInPixelsRow.border = emptyBorder - heightInPixelsRow.add(JLabel("Display Height in Pixels "), BorderLayout.WEST) - heightInPixelsRow.add(uiHeightSelector, BorderLayout.EAST) - val baseAddressRow = panelWithBorderLayout - baseAddressRow.border = emptyBorder - baseAddressRow.add(JLabel("Base address for display "), BorderLayout.WEST) - baseAddressRow.add(uiBaseAddressSelector, BorderLayout.EAST) + val uiKeyboardAddress = JTextField(keyboardAddr.toHex(8)).apply { + addActionListener { + text.toUIntOrNull(text.detectRadix())?.let { + keyboardAddr = it + uiKeyboardCheckbox.isSelected = false + } + } + } + val uiKeyboardCheckbox = JCheckBox("Attach Keyboard++", keyboardAttached).apply { + addActionListener { + keyboardAttached = isSelected + if (isSelected) + { + deleteAsObserver() + addAsObserver() + } + pooledKeyEvents.values.forEach { it.clear() } + } + } + + println("Bitmap Display++ Initialized") + + // Register key listener + KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher { + if (!(keyboardAttached && uiKeyboard.hasFocus())) return@addKeyEventDispatcher false + when (it.id) + { + KeyEvent.KEY_PRESSED -> + { + // Keep track of down keys to avoid the system's key repeat + if (!downKeys.contains(it.keyCode)) + { + keyEvent(0x00u, it) + downKeys.add(it.keyCode) + } + } + KeyEvent.KEY_RELEASED -> + { + keyEvent(0x10u, it) + downKeys.remove(it.keyCode) + } + } + true + } + uiKeyboard = JTextField(2).apply { + class KL : KeyListener + { + override fun keyTyped(e: KeyEvent) { + uiKeyboard.text = "" + } + override fun keyPressed(e: KeyEvent) { + uiKeyboard.text = "" + // keyEvent(0x0u, e) + } + override fun keyReleased(e: KeyEvent) { + uiKeyboard.text = "" + // keyEvent(0x10u, e) + } + } + + addKeyListener(KL()) + } // Lay 'em out in the grid... - organization.add(unitWidthInPixelsRow) - organization.add(unitHeightInPixelsRow) - organization.add(widthInPixelsRow) - organization.add(heightInPixelsRow) - organization.add(baseAddressRow) - return organization + return JPanel(GridLayout(8, 1)).apply { + add(getPanelWithBorderLayout().apply { + add(JLabel("Unit Width in Pixels "), WEST) + add(uiUnitWidthSelector, EAST) + }) + add(getPanelWithBorderLayout().apply { + add(JLabel("Unit Height in Pixels "), WEST) + add(uiUnitHeightSelector, EAST) + }) + add(getPanelWithBorderLayout().apply { + add(JLabel("Display Width in Pixels "), WEST) + add(uiWidthSelector, EAST) + }) + add(getPanelWithBorderLayout().apply { + add(JLabel("Display Height in Pixels "), WEST) + add(uiHeightSelector, EAST) + }) + add(getPanelWithBorderLayout().apply { + add(JLabel("Base address for display "), WEST) + add(uiBaseAddressSelector, EAST) + }) + add(getPanelWithBorderLayout().apply { + add(uiKeyboardCheckbox, WEST) + add(uiKeyboardAddress, EAST) + }) + add(getPanelWithBorderLayout().apply { + add(JLabel("To use keyboard, put your cursor here >"), WEST) + add(uiKeyboard) + }) + } } // UI components and layout for right half of GUI, the visualization display area. @@ -332,8 +473,7 @@ class BitmapDisplay : AbstractMarsToolAndApplication private fun JComboBox.getInt() = selectedItem!!.toString().toInt() // Use this for consistent results. - private val panelWithBorderLayout: JPanel - get() = JPanel(BorderLayout(2, 2)) + private fun getPanelWithBorderLayout(): JPanel = JPanel(BorderLayout(2, 2)).apply { border = emptyBorder } // Method to determine grid dimensions based on current control settings. // Each grid element corresponds to one visualization unit. @@ -353,7 +493,8 @@ class BitmapDisplay : AbstractMarsToolAndApplication try { grid.setElement(offset / grid.cols, offset % grid.cols, value) - } catch (e: IndexOutOfBoundsException) + } + catch (e: IndexOutOfBoundsException) { // If address is out of range for display, do nothing. } @@ -448,8 +589,8 @@ class BitmapDisplay : AbstractMarsToolAndApplication companion object { - private const val version = "Version 1.0" - private const val heading = "Bitmap Display" + private const val VERSION = "Version 1.0" + private const val HEADING = "Bitmap Display++" /** * Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that @@ -459,7 +600,7 @@ class BitmapDisplay : AbstractMarsToolAndApplication @JvmStatic fun main(args: Array) { - BitmapDisplay("Bitmap Display stand-alone, " + version, heading).go() + BitmapDisplay("Bitmap Display stand-alone, " + VERSION, HEADING).go() } } }