[+] Keyboard++

This commit is contained in:
Hykilpikonna
2022-11-11 13:56:40 -05:00
parent 40d803041d
commit 1f679462f5
4 changed files with 242 additions and 87 deletions
+37
View File
@@ -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<ArgumentParser, Namespace>()
fun ArgumentParser.parseToVars(args: Array<String>) = parseArgs(args).also { ARG_PARSER_MAP[this] = it }
class ArgParserDelegate<T>(val a: ArgumentParser, val name: String)
{
operator fun getValue(thisRef: Any?, property: KProperty<*>): T?
{
return ARG_PARSER_MAP[a]?.get(name)
}
}
fun <T> ArgumentParser.arg(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}): ArgParserDelegate<T>
{
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<String>(*names, help=help, apply=apply)
fun ArgumentParser.flag(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}) =
arg<Boolean>(*names, help=help) { action(Arguments.storeTrue()); apply() }
+9 -32
View File
@@ -1,38 +1,15 @@
package mars 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<ArgumentParser, Namespace>() fun String.detectRadix() = if (length < 2) 10 else when (substring(0, 2))
fun ArgumentParser.parseToVars(args: Array<String>) = parseArgs(args).also { ARG_PARSER_MAP[this] = it }
class ArgParserDelegate<T>(val a: ArgumentParser, val name: String)
{ {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? "0x", "0X" -> 16
{ "0b", "0B" -> 2
return ARG_PARSER_MAP[a]?.get(name) else -> 10
}
} }
fun <T> ArgumentParser.arg(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}): ArgParserDelegate<T>
{
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<String>(*names, help=help, apply=apply)
fun ArgumentParser.flag(vararg names: String, help: String? = null, apply: Argument.() -> Unit = {}) =
arg<Boolean>(*names, help=help) { action(Arguments.storeTrue()); apply() }
+9 -9
View File
@@ -80,7 +80,7 @@ public class Globals
/** /**
* List of accepted file extensions for MIPS assembly source files. * List of accepted file extensions for MIPS assembly source files.
*/ */
public static final ArrayList fileExtensions = getFileExtensions(); public static final ArrayList<String> fileExtensions = getFileExtensions();
/** /**
* Maximum length of scrolled message window (MARS Messages and Run I/O) * 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))); limit = Integer.parseInt(properties.getProperty(propertyName, Integer.toString(defaultValue)));
} }
catch (NumberFormatException nfe) catch (NumberFormatException ignored)
{ {
} // do nothing, I already have a default } // do nothing, I already have a default
return limit; return limit;
@@ -283,9 +283,9 @@ public class Globals
// Read assembly language file extensions from properties file. Resulting // Read assembly language file extensions from properties file. Resulting
// string is tokenized into array list (assume StringTokenizer default delimiters). // string is tokenized into array list (assume StringTokenizer default delimiters).
private static ArrayList getFileExtensions() private static ArrayList<String> getFileExtensions()
{ {
ArrayList extensionsList = new ArrayList(); ArrayList<String> extensionsList = new ArrayList<>();
String extensions = getPropertyEntry(configPropertiesFile, "Extensions"); String extensions = getPropertyEntry(configPropertiesFile, "Extensions");
if (extensions != null) 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 * @return ArrayList. Each item is file path to .class file of a class that implements MarsTool. If none, returns
* empty list. * empty list.
*/ */
public static ArrayList getExternalTools() public static ArrayList<String> getExternalTools()
{ {
ArrayList toolsList = new ArrayList(); ArrayList<String> toolsList = new ArrayList<>();
String delimiter = ";"; String delimiter = ";";
String tools = getPropertyEntry(configPropertiesFile, "ExternalTools"); String tools = getPropertyEntry(configPropertiesFile, "ExternalTools");
if (tools != null) if (tools != null)
@@ -339,11 +339,11 @@ public class Globals
* *
* @return ArrayList of SyscallNumberOverride objects * @return ArrayList of SyscallNumberOverride objects
*/ */
public ArrayList getSyscallOverrides() public ArrayList<SyscallNumberOverride> getSyscallOverrides()
{ {
ArrayList overrides = new ArrayList(); ArrayList<SyscallNumberOverride> overrides = new ArrayList<>();
Properties properties = PropertiesFile.loadPropertiesFromFile(syscallPropertiesFile); Properties properties = PropertiesFile.loadPropertiesFromFile(syscallPropertiesFile);
Enumeration keys = properties.keys(); Enumeration<Object> keys = properties.keys();
while (keys.hasMoreElements()) while (keys.hasMoreElements())
{ {
String key = (String) keys.nextElement(); String key = (String) keys.nextElement();
+187 -46
View File
@@ -1,13 +1,20 @@
package mars.tools package mars.tools
import mars.mips.hardware.AccessNotice import mars.Globals
import mars.mips.hardware.Memory import mars.detectRadix
import mars.mips.hardware.MemoryAccessNotice import mars.mips.hardware.*
import mars.toHex
import mars.util.Binary import mars.util.Binary
import java.awt.* import java.awt.*
import java.awt.BorderLayout.*
import java.awt.event.KeyEvent
import java.awt.event.KeyListener
import java.util.* import java.util.*
import javax.swing.* import javax.swing.*
import javax.swing.border.EmptyBorder 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 Copyright (c) 2010-2011, Pete Sanderson and Kenneth Vollmar
@@ -49,6 +56,7 @@ class BitmapDisplay : AbstractMarsToolAndApplication
private lateinit var uiWidthSelector: JComboBox<Int> private lateinit var uiWidthSelector: JComboBox<Int>
private lateinit var uiHeightSelector: JComboBox<Int> private lateinit var uiHeightSelector: JComboBox<Int>
private lateinit var uiBaseAddressSelector: JComboBox<String> private lateinit var uiBaseAddressSelector: JComboBox<String>
private lateinit var uiKeyboardCheckbox: JCheckBox
private lateinit var canvas: JPanel private lateinit var canvas: JPanel
private lateinit var results: JPanel private lateinit var results: JPanel
@@ -68,8 +76,18 @@ class BitmapDisplay : AbstractMarsToolAndApplication
private val baseAddress get() = baseAddresses[uiBaseAddressSelector.selectedIndex] 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 private lateinit var grid: Grid
val pooledKeyEvents = hashMapOf<UInt, ArrayList<KeyEvent>>(
0x00u to ArrayList(), 0x10u to ArrayList(), 0x20u to ArrayList()
)
val downKeys = HashSet<Int>()
/** /**
* Simple constructor, likely used to run a stand-alone bitmap display tool. * 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(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 * 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 highAddress = -4
} }
addAsObserver(baseAddress, highAddress) 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. * Update display when connected MIPS program accesses (data) memory.
* *
* @param memory the attached 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. // UI components and layout for left half of GUI, where settings are specified.
private fun buildOrganizationArea(): JComponent private fun buildOrganizationArea(): JComponent
{ {
val organization = JPanel(GridLayout(8, 1))
uiUnitWidthSelector = JComboBox(arrayOf(1, 2, 4, 8, 16, 32)).apply { uiUnitWidthSelector = JComboBox(arrayOf(1, 2, 4, 8, 16, 32)).apply {
isEditable = false isEditable = false
background = backgroundColor background = backgroundColor
selectedIndex = 2 selectedIndex = 1
toolTipText = "Width in pixels of rectangle representing memory word" toolTipText = "Width in pixels of rectangle representing memory word"
addActionListener { addActionListener {
grid = createNewGrid() grid = createNewGrid()
@@ -229,7 +305,7 @@ class BitmapDisplay : AbstractMarsToolAndApplication
uiUnitHeightSelector = JComboBox(arrayOf(1, 2, 4, 8, 16, 32)).apply { uiUnitHeightSelector = JComboBox(arrayOf(1, 2, 4, 8, 16, 32)).apply {
isEditable = false isEditable = false
background = backgroundColor background = backgroundColor
selectedIndex = 2 selectedIndex = 1
toolTipText = "Height in pixels of rectangle representing memory word" toolTipText = "Height in pixels of rectangle representing memory word"
addActionListener { addActionListener {
grid = createNewGrid() grid = createNewGrid()
@@ -286,35 +362,100 @@ class BitmapDisplay : AbstractMarsToolAndApplication
} }
} }
// ALL COMPONENTS FOR "ORGANIZATION" SECTION val uiKeyboardAddress = JTextField(keyboardAddr.toHex(8)).apply {
val unitWidthInPixelsRow = panelWithBorderLayout addActionListener {
unitWidthInPixelsRow.border = emptyBorder text.toUIntOrNull(text.detectRadix())?.let {
unitWidthInPixelsRow.add(JLabel("Unit Width in Pixels "), BorderLayout.WEST) keyboardAddr = it
unitWidthInPixelsRow.add(uiUnitWidthSelector, BorderLayout.EAST) uiKeyboardCheckbox.isSelected = false
val unitHeightInPixelsRow = panelWithBorderLayout }
unitHeightInPixelsRow.border = emptyBorder }
unitHeightInPixelsRow.add(JLabel("Unit Height in Pixels "), BorderLayout.WEST) }
unitHeightInPixelsRow.add(uiUnitHeightSelector, BorderLayout.EAST) val uiKeyboardCheckbox = JCheckBox("Attach Keyboard++", keyboardAttached).apply {
val widthInPixelsRow = panelWithBorderLayout addActionListener {
widthInPixelsRow.border = emptyBorder keyboardAttached = isSelected
widthInPixelsRow.add(JLabel("Display Width in Pixels "), BorderLayout.WEST) if (isSelected)
widthInPixelsRow.add(uiWidthSelector, BorderLayout.EAST) {
val heightInPixelsRow = panelWithBorderLayout deleteAsObserver()
heightInPixelsRow.border = emptyBorder addAsObserver()
heightInPixelsRow.add(JLabel("Display Height in Pixels "), BorderLayout.WEST) }
heightInPixelsRow.add(uiHeightSelector, BorderLayout.EAST) pooledKeyEvents.values.forEach { it.clear() }
val baseAddressRow = panelWithBorderLayout }
baseAddressRow.border = emptyBorder }
baseAddressRow.add(JLabel("Base address for display "), BorderLayout.WEST)
baseAddressRow.add(uiBaseAddressSelector, BorderLayout.EAST) 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... // Lay 'em out in the grid...
organization.add(unitWidthInPixelsRow) return JPanel(GridLayout(8, 1)).apply {
organization.add(unitHeightInPixelsRow) add(getPanelWithBorderLayout().apply {
organization.add(widthInPixelsRow) add(JLabel("Unit Width in Pixels "), WEST)
organization.add(heightInPixelsRow) add(uiUnitWidthSelector, EAST)
organization.add(baseAddressRow) })
return organization 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. // UI components and layout for right half of GUI, the visualization display area.
@@ -332,8 +473,7 @@ class BitmapDisplay : AbstractMarsToolAndApplication
private fun <T> JComboBox<T>.getInt() = selectedItem!!.toString().toInt() private fun <T> JComboBox<T>.getInt() = selectedItem!!.toString().toInt()
// Use this for consistent results. // Use this for consistent results.
private val panelWithBorderLayout: JPanel private fun getPanelWithBorderLayout(): JPanel = JPanel(BorderLayout(2, 2)).apply { border = emptyBorder }
get() = JPanel(BorderLayout(2, 2))
// Method to determine grid dimensions based on current control settings. // Method to determine grid dimensions based on current control settings.
// Each grid element corresponds to one visualization unit. // Each grid element corresponds to one visualization unit.
@@ -353,7 +493,8 @@ class BitmapDisplay : AbstractMarsToolAndApplication
try try
{ {
grid.setElement(offset / grid.cols, offset % grid.cols, value) 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. // If address is out of range for display, do nothing.
} }
@@ -448,8 +589,8 @@ class BitmapDisplay : AbstractMarsToolAndApplication
companion object companion object
{ {
private const val version = "Version 1.0" private const val VERSION = "Version 1.0"
private const val heading = "Bitmap Display" private const val HEADING = "Bitmap Display++"
/** /**
* Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that * 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 @JvmStatic
fun main(args: Array<String>) fun main(args: Array<String>)
{ {
BitmapDisplay("Bitmap Display stand-alone, " + version, heading).go() BitmapDisplay("Bitmap Display stand-alone, " + VERSION, HEADING).go()
} }
} }
} }