commit
30cd0304d2
@ -0,0 +1 @@ |
||||
/target |
@ -0,0 +1,12 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<module type="CPP_MODULE" version="4"> |
||||
<component name="NewModuleRootManager"> |
||||
<content url="file://$MODULE_DIR$"> |
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> |
||||
<sourceFolder url="file://$MODULE_DIR$/csn_asm/src" isTestSource="false" /> |
||||
<excludeFolder url="file://$MODULE_DIR$/target" /> |
||||
</content> |
||||
<orderEntry type="inheritedJdk" /> |
||||
<orderEntry type="sourceFolder" forTests="false" /> |
||||
</component> |
||||
</module> |
@ -0,0 +1,8 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="ProjectModuleManager"> |
||||
<modules> |
||||
<module fileurl="file://$PROJECT_DIR$/.idea/csnparse.iml" filepath="$PROJECT_DIR$/.idea/csnparse.iml" /> |
||||
</modules> |
||||
</component> |
||||
</project> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="VcsDirectoryMappings"> |
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" /> |
||||
</component> |
||||
</project> |
@ -0,0 +1,265 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="CMakeRunConfigurationManager" shouldGenerate="true" shouldDeleteObsolete="true"> |
||||
<generated /> |
||||
</component> |
||||
<component name="CMakeSettings"> |
||||
<configurations> |
||||
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" /> |
||||
</configurations> |
||||
</component> |
||||
<component name="CargoProjects"> |
||||
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" /> |
||||
</component> |
||||
<component name="ChangeListManager"> |
||||
<list default="true" id="88ed7268-df97-4c85-be28-09f4faa117a4" name="Default Changelist" comment=""> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/data/literal.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/data/mask.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/data/mod.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/data/reg.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/error.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/instr/cond.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/instr/op.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/parse/mod.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/parse/parse_cond.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/parse/parse_data.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/parse/parse_instr.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/parse/parse_routines.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/parse/sexp_expect.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/patches/sexp_is_a.rs" afterDir="false" /> |
||||
<change afterPath="$PROJECT_DIR$/csn_asm/src/patches/try_remove.rs" afterDir="false" /> |
||||
</list> |
||||
<option name="SHOW_DIALOG" value="false" /> |
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" /> |
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> |
||||
<option name="LAST_RESOLUTION" value="IGNORE" /> |
||||
</component> |
||||
<component name="ClangdSettings"> |
||||
<option name="formatViaClangd" value="false" /> |
||||
</component> |
||||
<component name="FileColors"> |
||||
<fileColor scope="Non-Project Files (Material Default)" color="2E3C43" /> |
||||
<fileColor scope="Non-Project Files (Material Darker)" color="323232" /> |
||||
<fileColor scope="Non-Project Files (Material Lighter)" color="eae8e8" /> |
||||
<fileColor scope="Non-Project Files (Material Palenight)" color="2f2e43" /> |
||||
</component> |
||||
<component name="FileTemplateManagerImpl"> |
||||
<option name="RECENT_TEMPLATES"> |
||||
<list> |
||||
<option value="Rust File" /> |
||||
</list> |
||||
</option> |
||||
</component> |
||||
<component name="Git.Settings"> |
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> |
||||
</component> |
||||
<component name="MacroExpansionManager"> |
||||
<option name="directoryName" value="feqwje3c" /> |
||||
</component> |
||||
<component name="ProjectId" id="1hp4uAFugp7o1RX53zbV0AsyASy" /> |
||||
<component name="ProjectViewState"> |
||||
<option name="hideEmptyMiddlePackages" value="true" /> |
||||
<option name="showLibraryContents" value="true" /> |
||||
</component> |
||||
<component name="PropertiesComponent"> |
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" /> |
||||
<property name="ToolWindowDatabase.ShowToolbar" value="false" /> |
||||
<property name="WebServerToolWindowFactoryState" value="false" /> |
||||
<property name="cf.first.check.clang-format" value="false" /> |
||||
<property name="node.js.detected.package.eslint" value="true" /> |
||||
<property name="node.js.detected.package.tslint" value="true" /> |
||||
<property name="node.js.path.for.package.eslint" value="project" /> |
||||
<property name="node.js.path.for.package.tslint" value="project" /> |
||||
<property name="node.js.selected.package.eslint" value="(autodetect)" /> |
||||
<property name="node.js.selected.package.tslint" value="(autodetect)" /> |
||||
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" /> |
||||
<property name="nodejs_npm_path_reset_for_default_project" value="true" /> |
||||
<property name="nodejs_package_manager_path" value="npm" /> |
||||
<property name="org.rust.cargo.project.model.PROJECT_DISCOVERY" value="true" /> |
||||
<property name="settings.editor.selected.configurable" value="preferences.lookFeel" /> |
||||
</component> |
||||
<component name="RecentsManager"> |
||||
<key name="MoveFile.RECENT_KEYS"> |
||||
<recent name="$PROJECT_DIR$/csn_asm/src/patches" /> |
||||
<recent name="$PROJECT_DIR$/csn_asm/src/parse" /> |
||||
<recent name="$PROJECT_DIR$/csn_asm/src/instr" /> |
||||
<recent name="$PROJECT_DIR$/csn_asm/src" /> |
||||
</key> |
||||
</component> |
||||
<component name="RunManager" selected="Cargo Command.Test lib::tests"> |
||||
<configuration name="Build" type="CargoCommandRunConfiguration" factoryName="Cargo Command"> |
||||
<option name="channel" value="DEFAULT" /> |
||||
<option name="command" value="build" /> |
||||
<option name="allFeatures" value="false" /> |
||||
<option name="emulateTerminal" value="false" /> |
||||
<option name="backtrace" value="SHORT" /> |
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" /> |
||||
<envs /> |
||||
<method v="2"> |
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> |
||||
</method> |
||||
</configuration> |
||||
<configuration name="Test lib::tests" type="CargoCommandRunConfiguration" factoryName="Cargo Command" temporary="true"> |
||||
<option name="channel" value="DEFAULT" /> |
||||
<option name="command" value="test --package csn_asm --lib tests" /> |
||||
<option name="allFeatures" value="false" /> |
||||
<option name="emulateTerminal" value="false" /> |
||||
<option name="backtrace" value="SHORT" /> |
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" /> |
||||
<envs /> |
||||
<method v="2"> |
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> |
||||
</method> |
||||
</configuration> |
||||
<configuration name="Test tests::test_parse_basic" type="CargoCommandRunConfiguration" factoryName="Cargo Command" temporary="true"> |
||||
<option name="channel" value="DEFAULT" /> |
||||
<option name="command" value="test --package csn_asm --lib tests::test_parse_basic -- --exact" /> |
||||
<option name="allFeatures" value="false" /> |
||||
<option name="emulateTerminal" value="false" /> |
||||
<option name="backtrace" value="SHORT" /> |
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" /> |
||||
<envs /> |
||||
<method v="2"> |
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> |
||||
</method> |
||||
</configuration> |
||||
<configuration name="Test tests::test_parse_data_formats" type="CargoCommandRunConfiguration" factoryName="Cargo Command" temporary="true"> |
||||
<option name="channel" value="DEFAULT" /> |
||||
<option name="command" value="test --package csn_asm --lib tests::test_parse_data_formats -- --exact" /> |
||||
<option name="allFeatures" value="false" /> |
||||
<option name="emulateTerminal" value="false" /> |
||||
<option name="backtrace" value="SHORT" /> |
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" /> |
||||
<envs /> |
||||
<method v="2"> |
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> |
||||
</method> |
||||
</configuration> |
||||
<configuration name="Test tests::test_parse_empty_routine" type="CargoCommandRunConfiguration" factoryName="Cargo Command" temporary="true"> |
||||
<option name="channel" value="DEFAULT" /> |
||||
<option name="command" value="test --package csn_asm --lib tests::test_parse_empty_routine -- --exact" /> |
||||
<option name="allFeatures" value="false" /> |
||||
<option name="emulateTerminal" value="false" /> |
||||
<option name="backtrace" value="SHORT" /> |
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" /> |
||||
<envs /> |
||||
<method v="2"> |
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> |
||||
</method> |
||||
</configuration> |
||||
<configuration default="true" type="CargoCommandRunConfiguration" factoryName="Cargo Command"> |
||||
<option name="channel" value="DEFAULT" /> |
||||
<option name="command" value="run" /> |
||||
<option name="allFeatures" value="false" /> |
||||
<option name="emulateTerminal" value="false" /> |
||||
<option name="backtrace" value="SHORT" /> |
||||
<envs /> |
||||
<method v="2"> |
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> |
||||
</method> |
||||
</configuration> |
||||
<list> |
||||
<item itemvalue="Cargo Command.Build" /> |
||||
<item itemvalue="Cargo Command.Test tests::test_parse_basic" /> |
||||
<item itemvalue="Cargo Command.Test tests::test_parse_empty_routine" /> |
||||
<item itemvalue="Cargo Command.Test lib::tests" /> |
||||
<item itemvalue="Cargo Command.Test tests::test_parse_data_formats" /> |
||||
</list> |
||||
<recent_temporary> |
||||
<list> |
||||
<item itemvalue="Cargo Command.Test lib::tests" /> |
||||
<item itemvalue="Cargo Command.Test tests::test_parse_data_formats" /> |
||||
<item itemvalue="Cargo Command.Test tests::test_parse_empty_routine" /> |
||||
<item itemvalue="Cargo Command.Test tests::test_parse_basic" /> |
||||
</list> |
||||
</recent_temporary> |
||||
</component> |
||||
<component name="RustProjectSettings"> |
||||
<option name="toolchainHomeDirectory" value="/usr/bin" /> |
||||
</component> |
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> |
||||
<component name="TaskManager"> |
||||
<task active="true" id="Default" summary="Default task"> |
||||
<changelist id="88ed7268-df97-4c85-be28-09f4faa117a4" name="Default Changelist" comment="" /> |
||||
<created>1600694919906</created> |
||||
<option name="number" value="Default" /> |
||||
<option name="presentableId" value="Default" /> |
||||
<updated>1600694919906</updated> |
||||
<workItem from="1600694921394" duration="24965000" /> |
||||
</task> |
||||
<servers /> |
||||
</component> |
||||
<component name="TypeScriptGeneratedFilesManager"> |
||||
<option name="version" value="3" /> |
||||
</component> |
||||
<component name="WindowStateProjectService"> |
||||
<state x="419" y="270" width="1081" height="718" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1600708890006"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state x="419" y="270" width="1081" height="718" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600708890006" /> |
||||
<state x="100" y="130" width="1720" height="970" key="#com.intellij.execution.junit2.states.ComparisonFailureState$DiffDialog" timestamp="1600720457803"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state x="100" y="130" width="1720" height="970" key="#com.intellij.execution.junit2.states.ComparisonFailureState$DiffDialog/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600720457803" /> |
||||
<state x="769" y="331" key="#com.intellij.ide.util.MemberChooser" timestamp="1600717864594"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state x="769" y="331" key="#com.intellij.ide.util.MemberChooser/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600717864594" /> |
||||
<state x="743" y="384" key="FileChooserDialogImpl" timestamp="1600708889153"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state x="743" y="384" key="FileChooserDialogImpl/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600708889153" /> |
||||
<state width="1592" height="1109" key="GridCell.Tab.0.bottom" timestamp="1600722282000"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state width="1592" height="1109" key="GridCell.Tab.0.bottom/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600722282000" /> |
||||
<state width="1592" height="1109" key="GridCell.Tab.0.center" timestamp="1600722282000"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state width="1592" height="1109" key="GridCell.Tab.0.center/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600722282000" /> |
||||
<state width="1592" height="1109" key="GridCell.Tab.0.left" timestamp="1600722282000"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state width="1592" height="1109" key="GridCell.Tab.0.left/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600722282000" /> |
||||
<state width="1592" height="1109" key="GridCell.Tab.0.right" timestamp="1600722282000"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state width="1592" height="1109" key="GridCell.Tab.0.right/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600722282000" /> |
||||
<state width="1870" height="388" key="GridCell.Tab.1.bottom" timestamp="1600717739429"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state width="1870" height="388" key="GridCell.Tab.1.bottom/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600717739429" /> |
||||
<state width="1870" height="388" key="GridCell.Tab.1.center" timestamp="1600717739429"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state width="1870" height="388" key="GridCell.Tab.1.center/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600717739429" /> |
||||
<state width="1870" height="388" key="GridCell.Tab.1.left" timestamp="1600717739429"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state width="1870" height="388" key="GridCell.Tab.1.left/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600717739429" /> |
||||
<state width="1870" height="388" key="GridCell.Tab.1.right" timestamp="1600717739429"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state width="1870" height="388" key="GridCell.Tab.1.right/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600717739429" /> |
||||
<state x="442" y="247" key="SettingsEditor" timestamp="1600713864019"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state x="442" y="247" key="SettingsEditor/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600713864019" /> |
||||
<state x="1960" y="30" width="1575" height="1096" maximized="true" key="dock-window-1" timestamp="1600712938231"> |
||||
<screen x="1920" y="0" width="1600" height="1200" /> |
||||
</state> |
||||
<state x="1960" y="30" width="1575" height="1096" maximized="true" key="dock-window-1/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600712938231" /> |
||||
<state x="1920" y="104" width="1575" height="1096" key="dock-window-2" timestamp="1600721359003"> |
||||
<screen x="1920" y="0" width="1600" height="1200" /> |
||||
</state> |
||||
<state x="1920" y="104" width="1575" height="1096" key="dock-window-2/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600721359003" /> |
||||
<state x="651" y="295" width="617" height="640" key="find.popup" timestamp="1600706360212"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state x="651" y="295" width="617" height="640" key="find.popup/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600706360212" /> |
||||
<state width="1007" height="578" key="javadoc.popup.new" timestamp="1600715844130"> |
||||
<screen x="0" y="32" width="1920" height="1168" /> |
||||
</state> |
||||
<state width="1007" height="578" key="javadoc.popup.new/0.32.1920.1168/1920.0.1600.1200@0.32.1920.1168" timestamp="1600715844130" /> |
||||
</component> |
||||
</project> |
@ -0,0 +1,77 @@ |
||||
# This file is automatically @generated by Cargo. |
||||
# It is not intended for manual editing. |
||||
[[package]] |
||||
name = "anyhow" |
||||
version = "1.0.32" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" |
||||
|
||||
[[package]] |
||||
name = "csn_asm" |
||||
version = "0.1.0" |
||||
dependencies = [ |
||||
"anyhow", |
||||
"sexp", |
||||
"thiserror", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "proc-macro2" |
||||
version = "1.0.21" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" |
||||
dependencies = [ |
||||
"unicode-xid", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "quote" |
||||
version = "1.0.7" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" |
||||
dependencies = [ |
||||
"proc-macro2", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "sexp" |
||||
version = "1.1.4" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "9c8fa7ac9df84000b0238cf497cb2d3056bac2ff2a7d8cf179d2803b4b58571f" |
||||
|
||||
[[package]] |
||||
name = "syn" |
||||
version = "1.0.41" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" |
||||
dependencies = [ |
||||
"proc-macro2", |
||||
"quote", |
||||
"unicode-xid", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "thiserror" |
||||
version = "1.0.20" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" |
||||
dependencies = [ |
||||
"thiserror-impl", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "thiserror-impl" |
||||
version = "1.0.20" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" |
||||
dependencies = [ |
||||
"proc-macro2", |
||||
"quote", |
||||
"syn", |
||||
] |
||||
|
||||
[[package]] |
||||
name = "unicode-xid" |
||||
version = "0.2.1" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" |
@ -0,0 +1,5 @@ |
||||
[workspace] |
||||
members = [ |
||||
"csn_asm", |
||||
] |
||||
|
@ -0,0 +1,11 @@ |
||||
[package] |
||||
name = "csn_asm" |
||||
version = "0.1.0" |
||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||
edition = "2018" |
||||
publish = false |
||||
|
||||
[dependencies] |
||||
sexp = "1.1.4" |
||||
thiserror = "1.0.20" |
||||
anyhow = "1.0.32" |
@ -0,0 +1,113 @@ |
||||
use std::fmt::{self, Display, Formatter}; |
||||
use std::convert::TryFrom; |
||||
use std::sync::atomic::AtomicU32; |
||||
use std::borrow::Cow; |
||||
|
||||
pub type DebugMsg = Cow<'static, str>; |
||||
|
||||
/// Immediate value
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub struct Value(pub i64); |
||||
|
||||
impl From<i64> for Value { |
||||
fn from(n: i64) -> Self { |
||||
Self(n) |
||||
} |
||||
} |
||||
|
||||
impl Display for Value { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
if f.alternate() { |
||||
write!(f, "{:#010x}", self.0) |
||||
} else { |
||||
write!(f, "{}", self.0) |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Value { |
||||
pub fn as_u64(self) -> u64 { |
||||
u64::from_ne_bytes(self.0.to_ne_bytes()) |
||||
} |
||||
|
||||
pub fn as_u32(self) -> Option<u32> { |
||||
u32::try_from(self.as_u64()).ok() |
||||
} |
||||
|
||||
pub fn as_i32(self) -> Option<i32> { |
||||
i32::try_from(self.0).ok() |
||||
} |
||||
} |
||||
|
||||
/// Immediate address
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub struct Addr(pub u64); |
||||
|
||||
impl Display for Addr { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
write!(f, "@{:#010x}", self.0) |
||||
} |
||||
} |
||||
|
||||
impl From<u64> for Addr { |
||||
fn from(n: u64) -> Self { |
||||
Self(n) |
||||
} |
||||
} |
||||
|
||||
/// Label name
|
||||
#[derive(Debug, Clone, Eq, PartialEq)] |
||||
pub enum Label { |
||||
Named(String), |
||||
Numbered(u32), |
||||
} |
||||
|
||||
impl Label { |
||||
/// Generate a unique numbered label from a counter
|
||||
pub fn unique(counter : &AtomicU32) -> Self { |
||||
Label::Numbered(counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed)) |
||||
} |
||||
} |
||||
|
||||
impl Display for Label { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
match self { |
||||
Label::Named(name) => write!(f, ":{}", name), |
||||
Label::Numbered(num) => write!(f, ":#{}", num), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl From<&str> for Label { |
||||
fn from(n: &str) -> Self { |
||||
Self::Named(n.to_string()) |
||||
} |
||||
} |
||||
|
||||
impl From<String> for Label { |
||||
fn from(n: String) -> Self { |
||||
Self::Named(n) |
||||
} |
||||
} |
||||
|
||||
/// Routine name
|
||||
#[derive(Debug, Clone, Eq, PartialEq)] |
||||
pub struct RoutineName(pub String); |
||||
|
||||
impl Display for RoutineName { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
write!(f, "{}", self.0) |
||||
} |
||||
} |
||||
|
||||
impl From<&str> for RoutineName { |
||||
fn from(n: &str) -> Self { |
||||
Self(n.to_string()) |
||||
} |
||||
} |
||||
|
||||
impl From<String> for RoutineName { |
||||
fn from(n: String) -> Self { |
||||
Self(n) |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
//! Mask applied to a data source or destination
|
||||
|
||||
use crate::error::AsmError; |
||||
|
||||
/// Bit mask to apply to a value
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub struct Mask { |
||||
/// Length of the selected bit slice
|
||||
len: u8, |
||||
/// Offset of the selected bit slice from bit zero
|
||||
offset: u8, |
||||
} |
||||
|
||||
impl Default for Mask { |
||||
fn default() -> Self { |
||||
Mask { |
||||
len: 64, |
||||
offset: 0 |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Mask { |
||||
pub const BYTE: Mask = Mask { |
||||
len: 8, |
||||
offset: 0, |
||||
}; |
||||
pub const HALF_WORD: Mask = Mask { |
||||
len: 16, |
||||
offset: 0, |
||||
}; |
||||
pub const WORD: Mask = Mask { |
||||
len: 32, |
||||
offset: 0, |
||||
}; |
||||
pub const DOUBLE_WORD: Mask = Mask { |
||||
len: 64, |
||||
offset: 0, |
||||
}; |
||||
pub const FULL: Mask = Self::DOUBLE_WORD; |
||||
|
||||
pub fn new(len: u8, offset: u8) -> Result<Self, AsmError> { |
||||
if len == 0 || offset >= 64 { |
||||
// create the invalid mask to display it in the error
|
||||
return Err(AsmError::BadMask(Mask { |
||||
len, |
||||
offset |
||||
})); |
||||
} |
||||
|
||||
Ok(Self { |
||||
len: len.min(64 - offset), |
||||
offset, |
||||
}) |
||||
} |
||||
|
||||
/// Get a binary mask representing the span
|
||||
pub fn as_bitmask(self) -> u64 { |
||||
((1 << self.len) - 1) << self.offset |
||||
} |
||||
} |
@ -0,0 +1,98 @@ |
||||
use super::error::AsmError; |
||||
|
||||
|
||||
pub(crate) mod literal; |
||||
mod reg; |
||||
mod mask; |
||||
|
||||
pub use reg::Register; |
||||
pub use mask::Mask; |
||||
use literal::Addr; |
||||
|
||||
|
||||
use std::convert::TryFrom; |
||||
|
||||
use crate::data::literal::Value; |
||||
|
||||
/// Data source disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum DataDisp { |
||||
/// Constant value
|
||||
Immediate(Value), |
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr), |
||||
/// Register
|
||||
Register(Register), |
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register), |
||||
} |
||||
|
||||
/// Data source disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum SrcDisp { |
||||
/// Constant value
|
||||
Immediate(Value), |
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr), |
||||
/// Register
|
||||
Register(Register), |
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register), |
||||
} |
||||
|
||||
impl TryFrom<DataDisp> for SrcDisp { |
||||
type Error = AsmError; |
||||
|
||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> { |
||||
match value { |
||||
DataDisp::Immediate(x) => Ok(SrcDisp::Immediate(x)), |
||||
DataDisp::ImmediatePtr(x) => Ok(SrcDisp::ImmediatePtr(x)), |
||||
DataDisp::Register(x) => Ok(SrcDisp::Register(x)), |
||||
DataDisp::RegisterPtr(x) => Ok(SrcDisp::RegisterPtr(x)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Data destination disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum DstDisp { |
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr), |
||||
/// Register
|
||||
Register(Register), |
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register), |
||||
} |
||||
|
||||
impl TryFrom<DataDisp> for DstDisp { |
||||
type Error = AsmError; |
||||
|
||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> { |
||||
match value { |
||||
DataDisp::Immediate(_x) => Err(AsmError::ValueAsOutput), |
||||
DataDisp::ImmediatePtr(x) => Ok(DstDisp::ImmediatePtr(x)), |
||||
DataDisp::Register(x) => Ok(DstDisp::Register(x)), |
||||
DataDisp::RegisterPtr(x) => Ok(DstDisp::RegisterPtr(x)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Data source argument (read-only)
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub struct Rd(pub SrcDisp, pub Mask); |
||||
|
||||
impl Rd { |
||||
pub fn new(src : SrcDisp) -> Self { |
||||
Rd(src, Mask::default()) |
||||
} |
||||
} |
||||
|
||||
/// Data destination argument (read-write)
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub struct Wr(pub DstDisp, pub Mask); |
||||
|
||||
impl Wr { |
||||
pub fn new(dst : DstDisp) -> Self { |
||||
Wr(dst, Mask::default()) |
||||
} |
||||
} |
@ -0,0 +1,22 @@ |
||||
use std::fmt::{self, Display, Formatter}; |
||||
|
||||
/// Register name
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
||||
pub enum Register { |
||||
/// Argument register, read-only
|
||||
Arg(u8), |
||||
/// Result register, read-only
|
||||
Res(u8), |
||||
/// General purpose register
|
||||
Gen(u8) |
||||
} |
||||
|
||||
impl Display for Register { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
match self { |
||||
Register::Arg(n) => write!(f, "arg{}", n), |
||||
Register::Res(n) => write!(f, "res{}", n), |
||||
Register::Gen(n) => write!(f, "r{}", n), |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,48 @@ |
||||
use crate::instr::{Cond}; |
||||
use crate::data::{Mask, Register}; |
||||
use thiserror::Error; |
||||
use std::borrow::Cow; |
||||
|
||||
|
||||
/// csn_asm unified error type
|
||||
#[derive(Error,Debug)] |
||||
pub enum Error { |
||||
#[error("S-expression syntax error: {0:?}")] |
||||
PreParse(#[from] Box<sexp::Error>), |
||||
#[error("Parse error: {0:?}")] |
||||
Parse(Cow<'static, str>), |
||||
#[error("Parse error in {1:?}: {0:?}")] |
||||
ParseIn(Cow<'static, str>, sexp::Sexp), |
||||
#[error("Assembler error: {0:?}")] |
||||
Asm(AsmError), |
||||
#[error("Architecture error: {0:?}")] |
||||
Arch(ArchError), |
||||
#[error(transparent)] |
||||
Other(#[from] anyhow::Error), |
||||
} |
||||
|
||||
/// Error from the assembler stage (after parsing S-expressions and basic validation)
|
||||
#[derive(Error,Debug)] |
||||
pub enum AsmError { |
||||
#[error("Unknown instruction")] |
||||
UnknownInstruction, |
||||
#[error("Bad bit mask")] |
||||
BadMask(Mask), |
||||
#[error("Uneven operand size")] |
||||
UnevenOperandSize(Mask, Mask), |
||||
#[error("Value provided as output argument")] |
||||
ValueAsOutput, |
||||
#[error("Conditional branch already defined for \"{0}\"")] |
||||
ConditionalAlreadyUsed(Cond), |
||||
} |
||||
|
||||
/// Architectural error - the code is syntactically OK, but cannot run
|
||||
#[derive(Error,Debug)] |
||||
pub enum ArchError { |
||||
#[error("Register {0} does not exist")] |
||||
RegisterNotExist(Register), |
||||
#[error("Register {0} is not writable")] |
||||
RegisterNotWritable(Register), |
||||
#[error("Register {0} is not readable")] |
||||
RegisterNotReadable(Register), |
||||
} |
@ -0,0 +1,73 @@ |
||||
use std::fmt::{self, Display, Formatter}; |
||||
use std::ops::Not; |
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] |
||||
pub enum Cond { |
||||
Equal, |
||||
NotEqual, |
||||
Zero, |
||||
NotZero, |
||||
Less, |
||||
LessOrEqual, |
||||
Greater, |
||||
GreaterOrEqual, |
||||
Positive, |
||||
NonPositive, |
||||
Negative, |
||||
NonNegative, |
||||
Overflow, |
||||
NotOverflow, |
||||
Carry, |
||||
NotCarry, |
||||
} |
||||
|
||||
impl Display for Cond { |
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||
f.write_str(match self { |
||||
Cond::Equal => "eq", |
||||
Cond::NotEqual => "ne", |
||||
Cond::Zero => "z", |
||||
Cond::NotZero => "nz", |
||||
Cond::Less => "lt", |
||||
Cond::LessOrEqual => "le", |
||||
Cond::Greater => "gt", |
||||
Cond::GreaterOrEqual => "ge", |
||||
Cond::Positive => "pos", |
||||
Cond::Negative => "neg", |
||||
Cond::NonPositive => "npos", |
||||
Cond::NonNegative => "nneg", |
||||
Cond::Overflow => "ov", |
||||
Cond::Carry => "c", |
||||
Cond::NotCarry => "nc", |
||||
Cond::NotOverflow => "nov" |
||||
}) |
||||
} |
||||
} |
||||
|
||||
impl Not for Cond { |
||||
type Output = Cond; |
||||
|
||||
fn not(self) -> Self::Output { |
||||
match self { |
||||
Cond::Equal => Cond::NotEqual, |
||||
Cond::Zero => Cond::NotZero, |
||||
Cond::Overflow => Cond::NotOverflow, |
||||
Cond::Carry => Cond::NotCarry, |
||||
|
||||
Cond::Positive => Cond::NonPositive, |
||||
Cond::Negative => Cond::NonNegative, |
||||
Cond::NonPositive => Cond::Positive, |
||||
Cond::NonNegative => Cond::Negative, |
||||
|
||||
Cond::NotEqual => Cond::Equal, |
||||
Cond::NotZero => Cond::Zero, |
||||
Cond::NotOverflow => Cond::Overflow, |
||||
Cond::NotCarry => Cond::Carry, |
||||
|
||||
Cond::Less => Cond::GreaterOrEqual, |
||||
Cond::Greater => Cond::LessOrEqual, |
||||
Cond::LessOrEqual => Cond::Greater, |
||||
Cond::GreaterOrEqual => Cond::Less, |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,68 @@ |
||||
mod op; |
||||
mod cond; |
||||
|
||||
pub use op::Op; |
||||
pub use cond::Cond; |
||||
use crate::data::literal::{Label, RoutineName}; |
||||
use std::sync::atomic::{AtomicU32}; |
||||
use std::collections::HashMap; |
||||
use crate::error::{AsmError, Error}; |
||||
|
||||
/// A higher-level instruction
|
||||
pub struct Instr { |
||||
pub op: Op, |
||||
pub branches: Option<Vec<(Cond, Vec<Instr>)>>, |
||||
} |
||||
|
||||
/// A routine
|
||||
pub struct Routine { |
||||
pub name: RoutineName, |
||||
pub body: Vec<Instr>, |
||||
} |
||||
|
||||
/// A trait for something that can turn into multiple instructions
|
||||
pub trait Flatten { |
||||
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<Op>, Error>; |
||||
} |
||||
|
||||
impl Flatten for Instr { |
||||
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<Op>, Error> { |
||||
let mut ops = vec![self.op]; |
||||
|
||||
if let Some(branches) = self.branches { |
||||
let labels = HashMap::<Cond, u32>::new(); |
||||
let _branch_count = branches.len(); |
||||
for (_cnt, (cond, branch)) in branches.into_iter().enumerate() { |
||||
if labels.contains_key(&cond) { |
||||
return Err(Error::Asm(AsmError::ConditionalAlreadyUsed(cond))); |
||||
} |
||||
|
||||
let next_lbl = Label::unique(label_num); |
||||
ops.push(Op::JumpIf(!cond, next_lbl.clone())); |
||||
|
||||
for branch_instr in branch { |
||||
ops.extend(branch_instr.flatten(label_num)?); |
||||
} |
||||
|
||||
ops.push(Op::Label(next_lbl)); |
||||
} |
||||
} |
||||
|
||||
Ok(ops) |
||||
} |
||||
} |
||||
|
||||
impl Flatten for Routine { |
||||
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<Op>, Error> { |
||||
let mut ops = vec![ |
||||
Op::Routine(self.name.clone()), |
||||
]; |
||||
|
||||
for instr in self.body { |
||||
ops.extend(instr.flatten(label_num)?); |
||||
} |
||||
|
||||
ops.push(Op::Barrier(Some(format!("Routine \"{}\" overrun", self.name).into()))); |
||||
Ok(ops) |
||||
} |
||||
} |
@ -0,0 +1,57 @@ |
||||
use crate::data::{ |
||||
Wr, Rd, |
||||
literal::Label, |
||||
literal::RoutineName, |
||||
literal::DebugMsg, |
||||
}; |
||||
use crate::instr::{Cond}; |
||||
|
||||
/// A low level instruction
|
||||
#[derive(Clone, Debug, Eq, PartialEq)] |
||||
pub enum Op { |
||||
/* Marker instructions */ |
||||
|
||||
/// Mark a jump target.
|
||||
/// Is optimized out when jumps are replaced by relative skips
|
||||
Label(Label), |
||||
/// Mark a far jump target (can be jumped to from another routine).
|
||||
/// This label is preserved in optimized code.
|
||||
FarLabel(Label), |
||||
|
||||
/* Control flow */ |
||||
|
||||
/// Jump to a label
|
||||
Jump(Label), |
||||
/// Jump to a label that can be in another function
|
||||
FarJump(Label), |
||||
/// Call a routine with arguments
|
||||
Call(RoutineName, Vec<Rd>), |
||||
/// Exit the current routine with return values
|
||||
Ret(Vec<Rd>), |
||||
|
||||
/* Synthetic instructions */ |
||||
|
||||
/// Mark a routine entry point (call target)
|
||||
Routine(RoutineName), |
||||
/// Skip backward or forward
|
||||
Skip(Rd), |
||||
/// Jump to a label if a flag is set
|
||||
JumpIf(Cond, Label), |
||||
/// Deny jumps, skips and run across this address, producing a run-time fault with a message.
|
||||
Barrier(Option<DebugMsg>), |
||||
/// Generate a run-time fault with a debugger message
|
||||
Fault(Option<DebugMsg>), |
||||
|
||||
/* Arithmetic */ |
||||
|
||||
/// Copy a value
|
||||
Mov(Wr, Rd), |
||||
/// Compare two values and set conditional flags
|
||||
Cmp(Rd, Rd), |
||||
// Increment a value
|
||||
Inc(Wr), |
||||
// Decrement a value
|
||||
Dec(Wr), |
||||
|
||||
// TODO arithmetics, bit manipulation, byte operations
|
||||
} |
@ -0,0 +1,158 @@ |
||||
mod data; |
||||
mod error; |
||||
mod instr; |
||||
mod parse; |
||||
mod patches; |
||||
|
||||
pub use parse::parse; |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use crate::parse; |
||||
use crate::instr::{Op, Flatten}; |
||||
use crate::data::{Wr, DstDisp, Register, SrcDisp, Rd}; |
||||
use crate::data::literal::{Value, Addr}; |
||||
use std::sync::atomic::AtomicU32; |
||||
|
||||
#[test] |
||||
fn test_parse_empty() { |
||||
let parsed = parse(" |
||||
() |
||||
").unwrap(); |
||||
assert_eq!(Vec::<Op>::new(), parsed); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_parse_empty_routine() { |
||||
let parsed = parse(" |
||||
( |
||||
(hello) |
||||
) |
||||
").unwrap(); |
||||
assert_eq!(vec![ |
||||
Op::Routine("hello".into()), |
||||
Op::Barrier(Some("Routine \"hello\" overrun".into())) |
||||
], parsed); |
||||
|
||||
let parsed = parse(" |
||||
( |
||||
(hello) |
||||
(world) |
||||
) |
||||
").unwrap(); |
||||
assert_eq!(vec![ |
||||
Op::Routine("hello".into()), |
||||
Op::Barrier(Some("Routine \"hello\" overrun".into())), |
||||
Op::Routine("world".into()), |
||||
Op::Barrier(Some("Routine \"world\" overrun".into())) |
||||
], parsed); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_parse_data_formats() { |
||||
let parsed = parse(" |
||||
( |
||||
(move |
||||
(mov r0 r1) |
||||
(mov r15 7) |
||||
(mov r15 0xabcd) |
||||
(mov r7 0b11110000) |
||||
(mov r7 arg1) |
||||
(mov r255 arg255) |
||||
(mov r7 res0) |
||||
(mov r7 res255) |
||||
(mov @r0 @r0) ; test in both Rd and Wr positions |
||||
(mov @r0 @arg0) |
||||
(mov @r0 @res0) |
||||
(mov @123456 @0x123456) |
||||
(mov @0b010101 @0b010101) |
||||
) |
||||
) |
||||
").unwrap(); |
||||
assert_eq!(vec![ |
||||
Op::Routine("move".into()), |
||||
// (mov r0 r1)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::Register(Register::Gen(0))), |
||||
Rd::new(SrcDisp::Register(Register::Gen(1))), |
||||
), |
||||
// (mov r15 7)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::Register(Register::Gen(15))), |
||||
Rd::new(SrcDisp::Immediate(Value(7))), |
||||
), |
||||
// (mov r15 0xabcd)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::Register(Register::Gen(15))), |
||||
Rd::new(SrcDisp::Immediate(Value(0xabcd))), |
||||
), |
||||
// (mov r7 0b11110000)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::Register(Register::Gen(7))), |
||||
Rd::new(SrcDisp::Immediate(Value(0b11110000))), |
||||
), |
||||
// (mov r7 arg1)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::Register(Register::Gen(7))), |
||||
Rd::new(SrcDisp::Register(Register::Arg(1))), |
||||
), |
||||
// (mov r255 arg255)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::Register(Register::Gen(255))), |
||||
Rd::new(SrcDisp::Register(Register::Arg(255))), |
||||
), |
||||
// (mov r7 res0)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::Register(Register::Gen(7))), |
||||
Rd::new(SrcDisp::Register(Register::Res(0))), |
||||
), |
||||
// (mov r7 res255)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::Register(Register::Gen(7))), |
||||
Rd::new(SrcDisp::Register(Register::Res(255))), |
||||
), |
||||
// (mov @r0 @r0)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))), |
||||
Rd::new(SrcDisp::RegisterPtr(Register::Gen(0))), |
||||
), |
||||
// (mov @r0 @arg0)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))), |
||||
Rd::new(SrcDisp::RegisterPtr(Register::Arg(0))), |
||||
), |
||||
// (mov @r0 @res0)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))), |
||||
Rd::new(SrcDisp::RegisterPtr(Register::Res(0))), |
||||
), |
||||
// (mov @123456 @0x123456)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::ImmediatePtr(Addr(123456))), |
||||
Rd::new(SrcDisp::ImmediatePtr(Addr(0x123456))), |
||||
), |
||||
// (mov @0b010101 @0b010101)
|
||||
Op::Mov( |
||||
Wr::new(DstDisp::ImmediatePtr(Addr(0b010101))), |
||||
Rd::new(SrcDisp::ImmediatePtr(Addr(0b010101))), |
||||
), |
||||
Op::Barrier(Some("Routine \"move\" overrun".into())), |
||||
], parsed); |
||||
} |
||||
|
||||
fn parse_single_instr(src : &str) -> anyhow::Result<Vec<Op>> { |
||||
let num = AtomicU32::new(0); |
||||
Ok(parse::parse_instructions(vec![sexp::parse(src)?])?.remove(0).flatten(&num)?) |
||||
} |
||||
|
||||
#[test] |
||||
fn test_parse_single() { |
||||
let parsed = parse_single_instr("(mov r0 r1)").unwrap(); |
||||
assert_eq!(vec![ |
||||
Op::Mov( |
||||
Wr::new(DstDisp::Register(Register::Gen(0))), |
||||
Rd::new(SrcDisp::Register(Register::Gen(1))), |
||||
), |
||||
], parsed); |
||||
} |
||||
} |
@ -0,0 +1,31 @@ |
||||
|
||||
use crate::instr::{Routine, Op, Flatten}; |
||||
use crate::error::Error; |
||||
|
||||
|
||||
use std::sync::atomic::AtomicU32; |
||||
use crate::parse::sexp_expect::expect_list; |
||||
|
||||
mod parse_cond; |
||||
mod parse_instr; |
||||
mod parse_data; |
||||
mod parse_routines; |
||||
mod sexp_expect; |
||||
mod parse_op; |
||||
|
||||
use parse_routines::parse_routines; |
||||
pub use parse_instr::parse_instructions; |
||||
|
||||
pub fn parse(source: &str) -> Result<Vec<Op>, Error> { |
||||
let root = sexp::parse(source)?; |
||||
|
||||
let subs: Vec<Routine> = parse_routines(expect_list(Some(root), true)?)?; |
||||
|
||||
let mut combined = vec![]; |
||||
let label_num = AtomicU32::new(0); |
||||
for sub in subs { |
||||
combined.extend(sub.flatten(&label_num)?); |
||||
} |
||||
|
||||
Ok(combined) |
||||
} |
@ -0,0 +1,41 @@ |
||||
use crate::parse::sexp_expect::{expect_list, expect_string_atom}; |
||||
use sexp::Sexp; |
||||
use crate::instr::{Cond, Instr}; |
||||
use crate::error::Error; |
||||
use crate::parse::parse_instr::parse_instructions; |
||||
use crate::patches::TryRemove; |
||||
|
||||
pub fn parse_cond_branch(tok: Sexp) -> Result<(Cond, Vec<Instr>), Error> { |
||||
let mut list = expect_list(Some(tok), false)?; |
||||
let kw = expect_string_atom(list.try_remove(0))?; |
||||
|
||||
if !kw.ends_with('?') { |
||||
return Err(Error::Parse(format!("Condition must end with '?': {}", kw).into())); |
||||
} |
||||
|
||||
Ok((parse_cond(&kw)?, parse_instructions(list)?)) |
||||
} |
||||
|
||||
pub fn parse_cond(text: &str) -> Result<Cond, Error> { |
||||
Ok(match text.trim_end_matches('?') { |
||||
"eq" | "=" | "==" => Cond::Equal, |
||||
"ne" | "<>" | "!=" | "≠" => Cond::NotEqual, |
||||
"z" | "0" => Cond::Zero, |
||||
"nz" | "<>0" | "!0" => Cond::NotZero, |
||||
"lt" | "<" => Cond::Less, |
||||
"le" | "<=" | "≤" => Cond::LessOrEqual, |
||||
"gt" => Cond::Greater, |
||||
"ge" | ">=" | "≥" => Cond::GreaterOrEqual, |
||||
"pos" | "+" | ">0" => Cond::Positive, |
||||
"neg" | "-" | "<0" => Cond::Negative, |
||||
"npos" | "!+" | "0-" | "<=0" | "≥0" => Cond::NonPositive, |
||||
"nneg" | "!-" | "0+" | ">=0" | "≤0" => Cond::NonNegative, |
||||
"ov" | "^" => Cond::Overflow, |
||||
"c" => Cond::Carry, |
||||
"nc" | "!c" => Cond::NotCarry, |
||||
"nov" | "!ov" | "!^" => Cond::NotOverflow, |
||||
_ => { |
||||
return Err(Error::Parse(format!("Unknown cond: {}", text).into())); |
||||
} |
||||
}) |
||||
} |
@ -0,0 +1,86 @@ |
||||
use sexp::{Sexp, Atom}; |
||||
use crate::data::{DataDisp, Register, Rd, Mask, Wr, DstDisp, SrcDisp}; |
||||
use crate::error::Error; |
||||
use crate::data::literal::{Value, Addr, Label}; |
||||
use std::convert::TryFrom; |
||||
use crate::parse::sexp_expect::expect_string_atom; |
||||
|
||||
pub fn parse_label(name : Option<Sexp>) -> Result<Label, Error> { |
||||
let name = expect_string_atom(name)?; |
||||
Ok(Label::Named(name.trim_start_matches(':').into())) |
||||
} |
||||
|
||||
/// Parse data disposition (address/value, without the read/write restriction)
|
||||
pub fn parse_data_disp(tok: Option<Sexp>) -> Result<DataDisp, Error> { |
||||
let tok = if let Some(tok) = tok { |
||||
tok |
||||
} else { |
||||
return Err(Error::Parse("Expected data disposition token".into())); |
||||
}; |
||||
|
||||
// TODO implement masks
|
||||
|
||||
match &tok { |
||||
Sexp::Atom(Atom::I(val)) => { |
||||
Ok(DataDisp::Immediate(Value(*val))) |
||||
}, |
||||
Sexp::Atom(Atom::S(s)) => { |
||||
if let Some(reference) = s.strip_prefix('@') { |
||||
if reference.starts_with(|c : char| c.is_ascii_digit()) { |
||||
let val : u64 = parse_u64(reference)?; |
||||
Ok(DataDisp::ImmediatePtr(Addr(val))) |
||||
} else { |
||||
Ok(DataDisp::RegisterPtr(parse_reg(reference)?)) |
||||
} |
||||
} else if s.starts_with(|c : char| c.is_ascii_digit()) { |
||||
Ok(DataDisp::Immediate(Value(parse_i64(s)?))) |
||||
} else { |
||||
Ok(DataDisp::Register(parse_reg(s)?)) |
||||
} |
||||
}, |
||||
_ => { |
||||
Err(Error::Parse(format!("bad data disp: {:?}", tok).into())) |
||||
}, |
||||
} |
||||
} |
||||
|
||||
pub fn parse_reg(name : &str) -> anyhow::Result<Register> { |
||||
if let Some(rn) = name.strip_prefix("arg") { |
||||
let val : u8 = rn.parse()?; |
||||
Ok(Register::Arg(val)) |
||||
} else if let Some(rn) = name.strip_prefix("res") { |
||||
let val : u8 = rn.parse()?; |
||||
Ok(Register::Res(val)) |
||||
} else if let Some(rn) = name.strip_prefix("r") { |
||||
let val : u8 = rn.parse()?; |
||||
Ok(Register::Gen(val)) |
||||
} else { |
||||
Err(Error::Parse(format!("Bad reg name: {}", name).into()))? |
||||
} |
||||
} |
||||
|
||||
pub fn parse_u64(literal : &str) -> anyhow::Result<u64> { |
||||
if let Some(hex) = literal.strip_prefix("0x") { |
||||
Ok(u64::from_str_radix(hex, 16)?) |
||||
} else if let Some(hex) = literal.strip_prefix("0b") { |
||||
Ok(u64::from_str_radix(hex, 2)?) |
||||
} else { |
||||
Ok(u64::from_str_radix(literal, 10)?) |
||||
} |
||||
} |
||||
|
||||
pub fn parse_i64(literal : &str) -> anyhow::Result<i64> { |
||||
if let Some(_value) = literal.strip_prefix("-") { |
||||
Ok(-1 * i64::try_from(parse_u64(literal)?)?) |
||||
} else { |
||||
Ok(i64::try_from(parse_u64(literal)?)?) |
||||
} |
||||
} |
||||
|
||||
pub fn parse_rd(tok: Option<Sexp>) -> anyhow::Result<Rd> { |
||||
Ok(Rd(SrcDisp::try_from(parse_data_disp(tok)?)?, Mask::default())) |
||||
} |
||||
|
||||
pub fn parse_wr(tok: Option<Sexp>) -> anyhow::Result<Wr> { |
||||
Ok(Wr(DstDisp::try_from(parse_data_disp(tok)?)?, Mask::default())) |
||||
} |
@ -0,0 +1,51 @@ |
||||
use sexp::Sexp; |
||||
use crate::instr::{Instr, Op}; |
||||
use crate::error::Error; |
||||
use crate::parse::parse_cond::{parse_cond_branch, parse_cond}; |
||||
use crate::data::literal::{Label, RoutineName}; |
||||
use crate::parse::parse_data::{parse_rd, parse_wr}; |
||||
use crate::parse::sexp_expect::{expect_list, expect_string_atom}; |
||||
use crate::patches::SexpIsA; |
||||
use super::parse_op::parse_op; |
||||
|
||||
pub fn parse_instructions(instrs: Vec<Sexp>) -> Result<Vec<Instr>, Error> { |
||||
let mut parsed = vec![]; |
||||
for expr in instrs { |
||||
let tokens = expect_list(Some(expr), false)?; |
||||
|
||||
let mut toki = tokens.into_iter(); |
||||
|
||||
let mut name = expect_string_atom(toki.next())?; |
||||
|
||||
let far = if name == "far" { |
||||
name = expect_string_atom(toki.next())?; |
||||
true |
||||
} else { |
||||
false |
||||
}; |
||||
|
||||
let arg_tokens = toki.clone().take_while(|e| e.is_atom()); |
||||
let branch_tokens = toki |
||||
.skip_while(|e| e.is_atom()) |
||||
.take_while(|e| e.is_list()); |
||||
|
||||
let branches = { |
||||
let mut branches = vec![]; |
||||
for t in branch_tokens { |
||||
branches.push(parse_cond_branch(t)?); |
||||
} |
||||
if branches.is_empty() { |
||||
None |
||||
} else { |
||||
Some(branches) |
||||
} |
||||
}; |
||||
|
||||
parsed.push(Instr { |
||||
op: parse_op(name.as_str(), far, arg_tokens)?, |
||||
branches |
||||
}); |
||||
} |
||||
Ok(parsed) |
||||
} |
||||
|
@ -0,0 +1,105 @@ |
||||
use crate::instr::Op; |
||||
use sexp::Sexp; |
||||
use crate::error::Error; |
||||
use crate::data::literal::{RoutineName, Label}; |
||||
use crate::parse::sexp_expect::expect_string_atom; |
||||
use crate::parse::parse_data::{parse_rd, parse_wr, parse_label}; |
||||
use crate::parse::parse_cond::parse_cond; |
||||
|
||||
pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator<Item=Sexp>) -> Result<Op, Error> { |
||||
Ok(match keyword { |
||||
"jmp" | "j" => { |
||||
let dest = parse_label(arg_tokens.next())?; |
||||
if far { |
||||
Op::Jump(dest) |
||||
} else { |
||||
Op::FarJump(dest) |
||||
} |
||||
} |
||||
|
||||
"call" => { |
||||
let dest = RoutineName(expect_string_atom(arg_tokens.next())?); |
||||
|
||||
let mut args = vec![]; |
||||
for t in arg_tokens { |
||||
args.push(parse_rd(Some(t))?); |
||||
} |
||||
Op::Call(dest, args) |
||||
} |
||||
|
||||
"ret" => { |
||||
let mut args = vec![]; |
||||
for t in arg_tokens { |
||||
args.push(parse_rd(Some(t))?); |
||||
} |
||||
Op::Ret(args) |
||||
} |
||||
|
||||
"rtn" | "fn" => { |
||||
let dest = RoutineName(expect_string_atom(arg_tokens.next())?); |
||||
Op::Routine(dest) |
||||
} |
||||
|
||||
"skip" => { |
||||
Op::Skip(parse_rd(arg_tokens.next())?) |
||||
} |
||||
|
||||
"jmp.if" | "j.if" => { |
||||
let dest = parse_label(arg_tokens.next())?; |
||||
Op::JumpIf(parse_cond(&expect_string_atom(arg_tokens.next())?)?, dest) |
||||
} |
||||
|
||||
"barrier" => { |
||||
Op::Barrier(match arg_tokens.next() { |
||||
None => None, |
||||
Some(s) => Some(expect_string_atom(Some(s))?.into()), |
||||
}) |
||||
} |
||||
|
||||
"fault" => { |
||||
Op::Fault(match arg_tokens.next() { |
||||
None => None, |
||||
Some(s) => Some(expect_string_atom(Some(s))?.into()), |
||||
}) |
||||
} |
||||
|
||||
"mov" | "ld" | "mv" => { |
||||
Op::Mov( |
||||
parse_wr(arg_tokens.next())?, |
||||
parse_rd(arg_tokens.next())? |
||||
) |
||||
} |
||||
|
||||
"cmp" => { |
||||
Op::Cmp( |
||||
parse_rd(arg_tokens.next())?, |
||||
parse_rd(arg_tokens.next())? |
||||
) |
||||
} |
||||
|
||||
"inc" => { |
||||
Op::Inc( |
||||
parse_wr(arg_tokens.next())? |
||||
) |
||||
} |
||||
|
||||
"dec" => { |
||||
Op::Dec( |
||||
parse_wr(arg_tokens.next())? |
||||
) |
||||
} |
||||
|
||||
other => { |
||||
if let Some(label) = other.strip_prefix(':') { |
||||
let label = Label::Named(label.to_string()); |
||||
if far { |
||||
Op::Label(label) |
||||
} else { |
||||
Op::FarLabel(label) |
||||
} |
||||
} else { |
||||
return Err(Error::Parse(format!("Unknown instruction: {}", other).into())); |
||||
} |
||||
} |
||||
}) |
||||
} |
@ -0,0 +1,21 @@ |
||||
use crate::parse::parse_instr::parse_instructions; |
||||
use crate::instr::Routine; |
||||
use crate::data::literal::RoutineName; |
||||
use sexp::Sexp; |
||||
use crate::error::Error; |
||||
use crate::parse::sexp_expect::{expect_list, expect_string_atom}; |
||||
use crate::patches::TryRemove; |
||||
|
||||
pub fn parse_routines(routines: Vec<Sexp>) -> Result<Vec<Routine>, Error> { |
||||
let mut parsed = vec![]; |
||||
for rt in routines { |
||||
let mut def = expect_list(Some(rt), false)?; |
||||
let name = expect_string_atom(def.try_remove(0))?; |
||||
let body = parse_instructions(def)?; |
||||
parsed.push(Routine { |
||||
name: RoutineName(name), |
||||
body, |
||||
}) |
||||
} |
||||
Ok(parsed) |
||||
} |
@ -0,0 +1,60 @@ |
||||
use sexp::{Sexp, Atom}; |
||||
use crate::error::Error; |
||||
|
||||
pub fn expect_list(expr: Option<Sexp>, allow_empty: bool) -> Result<Vec<Sexp>, Error> { |
||||
if let Some(expr) = expr { |
||||
match &expr { |
||||
Sexp::Atom(_) => { |
||||
return Err(Error::ParseIn("Expected a list".into(), expr)); |
||||
} |
||||
Sexp::List(list) => { |
||||
if !allow_empty && list.is_empty() { |
||||
return Err(Error::ParseIn("Routine: Empty list".into(), expr)); |
||||
} |
||||
|
||||
if let Sexp::List(list) = expr { |
||||
return Ok(list); |
||||
} else { |
||||
unreachable!(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
Err(Error::Parse("Expected a list, got nothing".into())) |
||||
} |
||||
|
||||
pub fn expect_atom(expr: Option<Sexp>) -> Result<Atom, Error> { |
||||
if let Some(expr) = expr { |
||||
match &expr { |
||||
Sexp::Atom(_atom) => { |
||||
if let Sexp::Atom(a) = expr { |
||||
return Ok(a); |
||||
} else { |
||||
unreachable!(); |
||||
} |
||||
} |
||||
Sexp::List(_) => { |
||||
return Err(Error::ParseIn("Expected atom got list".into(), expr)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Err(Error::Parse("Expected atom, got nothing".into())) |
||||
} |
||||
|
||||
pub fn expect_string_atom(expr: Option<Sexp>) -> Result<String, Error> { |
||||
match expect_atom(expr) { |
||||
Ok(Atom::S(s)) => Ok(s), |
||||
Ok(atom) => Err(Error::ParseIn("Expected string atom".into(), Sexp::Atom(atom))), |
||||
Err(e) => Err(e), |
||||
} |
||||
} |
||||
|
||||
// pub fn expect_int_atom(expr: Option<Sexp>) -> Result<i64, Error> {
|
||||
// match expect_atom(expr) {
|
||||
// Ok(Atom::I(v)) => Ok(v),
|
||||
// Ok(atom) => Err(Error::ParseIn("Expected int atom".into(), Sexp::Atom(atom))),
|
||||
// Err(e) => Err(e),
|
||||
// }
|
||||
// }
|
@ -0,0 +1,5 @@ |
||||
mod try_remove; |
||||
mod sexp_is_a; |
||||
|
||||
pub use try_remove::TryRemove; |
||||
pub use sexp_is_a::SexpIsA; |
@ -0,0 +1,23 @@ |
||||
use sexp::Sexp; |
||||
|
||||
pub trait SexpIsA { |
||||
fn is_atom(&self) -> bool; |
||||
|
||||
fn is_list(&self) -> bool; |
||||
} |
||||
|
||||
impl SexpIsA for Sexp { |
||||
fn is_atom(&self) -> bool { |
||||
match self { |
||||
Sexp::Atom(_) => true, |
||||
_ => false, |
||||
} |
||||
} |
||||
|
||||
fn is_list(&self) -> bool { |
||||
match self { |
||||
Sexp::List(_) => true, |
||||
_ => false, |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,17 @@ |
||||
pub trait TryRemove { |
||||
type Item; |
||||
fn try_remove(&mut self, index: usize) -> Option<Self::Item>; |
||||
} |
||||
|
||||
impl<T> TryRemove for Vec<T> { |
||||
type Item = T; |
||||
|
||||
fn try_remove(&mut self, index: usize) -> Option<T> { |
||||
if self.is_empty() { |
||||
None |
||||
} else { |
||||
Some(self.remove(index)) |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue