Import gdbstub_arch-0.1.0 am: 52a992f950 am: e54eca01fa am: bd0fc1143e

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/gdbstub_arch/+/1783794

Change-Id: Ia1dea790bb09de94325049da2673cafd64769c0a
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..5653d83
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "90aad2e136d15d486324f1985a398c02da982cdb"
+  }
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..f2e6592
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,32 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "gdbstub_arch"
+version = "0.1.0"
+authors = ["Daniel Prilik <danielprilik@gmail.com>"]
+description = "Implementations of `gdbstub::arch::Arch` for various architectures."
+homepage = "https://github.com/daniel5151/gdbstub"
+documentation = "https://docs.rs/gdbstub_arch"
+readme = "README.md"
+keywords = ["gdb", "emulation", "no_std", "debugging"]
+categories = ["development-tools::debugging", "embedded", "emulators", "no-std"]
+license = "MIT"
+repository = "https://github.com/daniel5151/gdbstub"
+[dependencies.gdbstub]
+version = "0.5"
+default-features = false
+
+[dependencies.num-traits]
+version = "0.2"
+default-features = false
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..d4b7ddb
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,18 @@
+[package]
+name = "gdbstub_arch"
+description = "Implementations of `gdbstub::arch::Arch` for various architectures."
+authors = ["Daniel Prilik <danielprilik@gmail.com>"]
+version = "0.1.0"
+license = "MIT"
+edition = "2018"
+readme = "README.md"
+documentation = "https://docs.rs/gdbstub_arch"
+homepage = "https://github.com/daniel5151/gdbstub"
+repository  = "https://github.com/daniel5151/gdbstub"
+keywords = ["gdb", "emulation", "no_std", "debugging"]
+categories = ["development-tools::debugging", "embedded", "emulators", "no-std"]
+
+[dependencies]
+gdbstub = { path = "../", version = "0.5", default-features = false }
+
+num-traits = { version = "0.2", default-features = false }
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..92a2e6e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,22 @@
+# gdbstub_arch
+
+[![](http://meritbadge.herokuapp.com/gdbstub_arch)](https://crates.io/crates/gdbstub_arch)
+[![](https://docs.rs/gdbstub_arch/badge.svg)](https://docs.rs/gdbstub_arch)
+
+Community-contributed implementations of `gdbstub::arch::Arch` for various
+architectures.
+
+_Note:_ If an architecture is missing from this crate, that does _not_ mean
+that it can't be used with `gdbstub`! So-long as there's support for the
+target architecture in GDB, it should be fairly straightforward to implement
+`Arch` manually.
+
+Please consider upstreaming any missing `Arch` implementations you happen to
+implement yourself! Aside from the altruistic motive of improving `gdbstub`,
+upstreaming your `Arch` implementation will ensure that it's kept up-to-date
+with any future breaking API changes.
+
+**Disclaimer:** These implementations are all community contributions, and
+while they are tested (by the PR's author) and code-reviewed, it's not
+particularly feasible to write detailed tests for each architecture! If you
+spot a bug in any of the implementations, please file an issue / open a PR!
diff --git a/src/arm/mod.rs b/src/arm/mod.rs
new file mode 100644
index 0000000..96fc404
--- /dev/null
+++ b/src/arm/mod.rs
@@ -0,0 +1,45 @@
+//! Implementations for various ARM architectures.
+
+use gdbstub::arch::Arch;
+
+pub mod reg;
+
+/// ARM-specific breakpoint kinds.
+///
+/// Extracted from the GDB documentation at
+/// [E.5.1.1 ARM Breakpoint Kinds](https://sourceware.org/gdb/current/onlinedocs/gdb/ARM-Breakpoint-Kinds.html#ARM-Breakpoint-Kinds)
+#[derive(Debug)]
+pub enum ArmBreakpointKind {
+    /// 16-bit Thumb mode breakpoint.
+    Thumb16,
+    /// 32-bit Thumb mode (Thumb-2) breakpoint.
+    Thumb32,
+    /// 32-bit ARM mode breakpoint.
+    Arm32,
+}
+
+impl gdbstub::arch::BreakpointKind for ArmBreakpointKind {
+    fn from_usize(kind: usize) -> Option<Self> {
+        let kind = match kind {
+            2 => ArmBreakpointKind::Thumb16,
+            3 => ArmBreakpointKind::Thumb32,
+            4 => ArmBreakpointKind::Arm32,
+            _ => return None,
+        };
+        Some(kind)
+    }
+}
+
+/// Implements `Arch` for ARMv4T
+pub enum Armv4t {}
+
+impl Arch for Armv4t {
+    type Usize = u32;
+    type Registers = reg::ArmCoreRegs;
+    type RegId = reg::id::ArmCoreRegId;
+    type BreakpointKind = ArmBreakpointKind;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(r#"<target version="1.0"><architecture>armv4t</architecture></target>"#)
+    }
+}
diff --git a/src/arm/reg/arm_core.rs b/src/arm/reg/arm_core.rs
new file mode 100644
index 0000000..179d590
--- /dev/null
+++ b/src/arm/reg/arm_core.rs
@@ -0,0 +1,82 @@
+use gdbstub::arch::Registers;
+
+/// 32-bit ARM core registers.
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/arm/arm-core.xml
+#[derive(Debug, Default, Clone, Eq, PartialEq)]
+pub struct ArmCoreRegs {
+    /// General purpose registers (R0-R12)
+    pub r: [u32; 13],
+    /// Stack Pointer (R13)
+    pub sp: u32,
+    /// Link Register (R14)
+    pub lr: u32,
+    /// Program Counter (R15)
+    pub pc: u32,
+    /// Current Program Status Register (cpsr)
+    pub cpsr: u32,
+}
+
+impl Registers for ArmCoreRegs {
+    type ProgramCounter = u32;
+
+    fn pc(&self) -> Self::ProgramCounter {
+        self.pc
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_bytes {
+            ($bytes:expr) => {
+                for b in $bytes {
+                    write_byte(Some(*b))
+                }
+            };
+        }
+
+        for reg in self.r.iter() {
+            write_bytes!(&reg.to_le_bytes());
+        }
+        write_bytes!(&self.sp.to_le_bytes());
+        write_bytes!(&self.lr.to_le_bytes());
+        write_bytes!(&self.pc.to_le_bytes());
+
+        // Floating point registers (unused)
+        for _ in 0..25 {
+            (0..4).for_each(|_| write_byte(None))
+        }
+
+        write_bytes!(&self.cpsr.to_le_bytes());
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        // ensure bytes.chunks_exact(4) won't panic
+        if bytes.len() % 4 != 0 {
+            return Err(());
+        }
+
+        use core::convert::TryInto;
+        let mut regs = bytes
+            .chunks_exact(4)
+            .map(|c| u32::from_le_bytes(c.try_into().unwrap()));
+
+        for reg in self.r.iter_mut() {
+            *reg = regs.next().ok_or(())?
+        }
+        self.sp = regs.next().ok_or(())?;
+        self.lr = regs.next().ok_or(())?;
+        self.pc = regs.next().ok_or(())?;
+
+        // Floating point registers (unused)
+        for _ in 0..25 {
+            regs.next().ok_or(())?;
+        }
+
+        self.cpsr = regs.next().ok_or(())?;
+
+        if regs.next().is_some() {
+            return Err(());
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/arm/reg/id.rs b/src/arm/reg/id.rs
new file mode 100644
index 0000000..c7e11a2
--- /dev/null
+++ b/src/arm/reg/id.rs
@@ -0,0 +1,36 @@
+use gdbstub::arch::RegId;
+
+/// 32-bit ARM core register identifier.
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum ArmCoreRegId {
+    /// General purpose registers (R0-R12)
+    Gpr(u8),
+    /// Stack Pointer (R13)
+    Sp,
+    /// Link Register (R14)
+    Lr,
+    /// Program Counter (R15)
+    Pc,
+    /// Floating point registers (F0-F7)
+    Fpr(u8),
+    /// Floating point status
+    Fps,
+    /// Current Program Status Register (cpsr)
+    Cpsr,
+}
+
+impl RegId for ArmCoreRegId {
+    fn from_raw_id(id: usize) -> Option<(Self, usize)> {
+        let reg = match id {
+            0..=12 => Self::Gpr(id as u8),
+            13 => Self::Sp,
+            14 => Self::Lr,
+            15 => Self::Pc,
+            16..=23 => Self::Fpr((id as u8) - 16),
+            25 => Self::Cpsr,
+            _ => return None,
+        };
+        Some((reg, 4))
+    }
+}
diff --git a/src/arm/reg/mod.rs b/src/arm/reg/mod.rs
new file mode 100644
index 0000000..8f5a8be
--- /dev/null
+++ b/src/arm/reg/mod.rs
@@ -0,0 +1,8 @@
+//! `Register` structs for various ARM architectures.
+
+/// `RegId` definitions for ARM architectures.
+pub mod id;
+
+mod arm_core;
+
+pub use arm_core::ArmCoreRegs;
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..ccf9b2d
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,65 @@
+//! Community-created implementations of [`gdbstub::arch::Arch`] for various
+//! architectures.
+//!
+//! _Note:_ If an architecture is missing from this crate, that does _not_ mean
+//! that it can't be used with `gdbstub`! So-long as there's support for the
+//! target architecture in GDB, it should be fairly straightforward to implement
+//! `Arch` manually.
+//!
+//! Please consider upstreaming any missing `Arch` implementations you happen to
+//! implement yourself! Aside from the altruistic motive of improving `gdbstub`,
+//! upstreaming your `Arch` implementation will ensure that it's kept up-to-date
+//! with any future breaking API changes.
+//!
+//! **Disclaimer:** These implementations are all community contributions, and
+//! while they are tested (by the PR's author) and code-reviewed, it's not
+//! particularly feasible to write detailed tests for each architecture! If you
+//! spot a bug in any of the implementations, please file an issue / open a PR!
+//!
+//! # What's with `RegIdImpl`?
+//!
+//! Supporting the `Target::read/write_register` API required introducing a new
+//! [`RegId`] trait + [`Arch::RegId`] associated type. `RegId` is used by
+//! `gdbstub` to translate raw GDB register ids (a protocol level arch-dependent
+//! `usize`) into human-readable enum variants.
+//!
+//! Unfortunately, this API was added after several contributors had already
+//! upstreamed their `Arch` implementations, and as a result, there are several
+//! built-in arch implementations which are missing proper `RegId` enums
+//! (tracked under [issue #29](https://github.com/daniel5151/gdbstub/issues/29)).
+//!
+//! As a stop-gap measure, affected `Arch` implementations have been modified to
+//! accept a `RegIdImpl` type parameter, which requires users to manually
+//! specify a `RegId` implementation.
+//!
+//! If you're not interested in implementing the `Target::read/write_register`
+//! methods and just want to get up-and-running with `gdbstub`, it's fine to
+//! set `RegIdImpl` to `()` and use the built-in stubbed `impl RegId for ()`.
+//!
+//! A better approach would be to implement (and hopefully upstream!) a proper
+//! `RegId` enum. While this will require doing a bit of digging through the GDB
+//! docs + [architecture XML definitions](https://github.com/bminor/binutils-gdb/tree/master/gdb/features/),
+//! it's not too tricky to get a working implementation up and running, and
+//! makes it possible to safely and efficiently implement the
+//! `Target::read/write_register` API. As an example, check out
+//! [`ArmCoreRegId`](arm::reg::id::ArmCoreRegId#impl-RegId).
+//!
+//! Whenever a `RegId` enum is upstreamed, the associated `Arch`'s `RegIdImpl`
+//! parameter will be defaulted to the newly added enum. This will simplify the
+//! API without requiring an explicit breaking API change. Once all `RegIdImpl`
+//! have a default implementation, only a single breaking API change will be
+//! required to remove `RegIdImpl` entirely (along with this documentation).
+
+#![cfg_attr(not(test), no_std)]
+#![deny(missing_docs)]
+
+pub mod arm;
+pub mod mips;
+pub mod msp430;
+pub mod ppc;
+pub mod riscv;
+pub mod x86;
+
+// used as part of intra-doc link
+#[allow(unused_imports)]
+use gdbstub::arch::*;
diff --git a/src/mips/mod.rs b/src/mips/mod.rs
new file mode 100644
index 0000000..7784fe8
--- /dev/null
+++ b/src/mips/mod.rs
@@ -0,0 +1,110 @@
+//! Implementations for the MIPS architecture.
+
+use gdbstub::arch::Arch;
+use gdbstub::arch::RegId;
+
+pub mod reg;
+
+/// MIPS-specific breakpoint kinds.
+///
+/// Extracted from the GDB documentation at
+/// [E.5.1.1 MIPS Breakpoint Kinds](https://sourceware.org/gdb/current/onlinedocs/gdb/MIPS-Breakpoint-Kinds.html#MIPS-Breakpoint-Kinds)
+#[derive(Debug)]
+pub enum MipsBreakpointKind {
+    /// 16-bit MIPS16 mode breakpoint.
+    Mips16,
+
+    /// 16-bit microMIPS mode breakpoint.
+    MicroMips16,
+
+    /// 32-bit standard MIPS mode breakpoint.
+    Mips32,
+
+    /// 32-bit microMIPS mode breakpoint.
+    MicroMips32,
+}
+
+impl gdbstub::arch::BreakpointKind for MipsBreakpointKind {
+    fn from_usize(kind: usize) -> Option<Self> {
+        let kind = match kind {
+            2 => MipsBreakpointKind::Mips16,
+            3 => MipsBreakpointKind::MicroMips16,
+            4 => MipsBreakpointKind::Mips32,
+            5 => MipsBreakpointKind::MicroMips32,
+            _ => return None,
+        };
+        Some(kind)
+    }
+}
+
+/// Implements `Arch` for 32-bit MIPS.
+///
+/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for
+/// more info about the `RegIdImpl` type parameter.
+pub enum Mips<RegIdImpl: RegId = reg::id::MipsRegId<u32>> {
+    #[doc(hidden)]
+    _Marker(core::marker::PhantomData<RegIdImpl>),
+}
+
+/// Implements `Arch` for 64-bit MIPS.
+///
+/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for
+/// more info about the `RegIdImpl` type parameter.
+pub enum Mips64<RegIdImpl: RegId = reg::id::MipsRegId<u64>> {
+    #[doc(hidden)]
+    _Marker(core::marker::PhantomData<RegIdImpl>),
+}
+
+/// Implements `Arch` for 32-bit MIPS with the DSP feature enabled.
+pub enum MipsWithDsp {}
+
+/// Implements `Arch` for 64-bit MIPS with the DSP feature enabled.
+pub enum Mips64WithDsp {}
+
+impl<RegIdImpl: RegId> Arch for Mips<RegIdImpl> {
+    type Usize = u32;
+    type Registers = reg::MipsCoreRegs<u32>;
+    type RegId = RegIdImpl;
+    type BreakpointKind = MipsBreakpointKind;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(r#"<target version="1.0"><architecture>mips</architecture></target>"#)
+    }
+}
+
+impl<RegIdImpl: RegId> Arch for Mips64<RegIdImpl> {
+    type Usize = u64;
+    type Registers = reg::MipsCoreRegs<u64>;
+    type RegId = RegIdImpl;
+    type BreakpointKind = MipsBreakpointKind;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(r#"<target version="1.0"><architecture>mips64</architecture></target>"#)
+    }
+}
+
+impl Arch for MipsWithDsp {
+    type Usize = u32;
+    type Registers = reg::MipsCoreRegsWithDsp<u32>;
+    type RegId = reg::id::MipsRegId<u32>;
+    type BreakpointKind = MipsBreakpointKind;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(
+            r#"<target version="1.0"><architecture>mips</architecture><feature name="org.gnu.gdb.mips.dsp"></feature></target>"#,
+        )
+    }
+}
+
+impl Arch for Mips64WithDsp {
+    type Usize = u64;
+    type Registers = reg::MipsCoreRegsWithDsp<u64>;
+    type RegId = reg::id::MipsRegId<u64>;
+    type BreakpointKind = MipsBreakpointKind;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(
+            r#"<target version="1.0"><architecture>mips64</architecture><feature name="org.gnu.gdb.mips.dsp"></feature></target>"#,
+        )
+    }
+}
diff --git a/src/mips/reg/id.rs b/src/mips/reg/id.rs
new file mode 100644
index 0000000..57665c6
--- /dev/null
+++ b/src/mips/reg/id.rs
@@ -0,0 +1,123 @@
+use gdbstub::arch::RegId;
+
+/// MIPS register identifier.
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum MipsRegId<U> {
+    /// General purpose registers (R0-R31)
+    Gpr(u8),
+    /// Status register
+    Status,
+    /// Low register
+    Lo,
+    /// High register
+    Hi,
+    /// Bad Virtual Address register
+    Badvaddr,
+    /// Exception Cause register
+    Cause,
+    /// Program Counter
+    Pc,
+    /// Floating point registers (F0-F31)
+    Fpr(u8),
+    /// Floating-point Control Status register
+    Fcsr,
+    /// Floating-point Implementation Register
+    Fir,
+    /// High 1 register
+    Hi1,
+    /// Low 1 register
+    Lo1,
+    /// High 2 register
+    Hi2,
+    /// Low 2 register
+    Lo2,
+    /// High 3 register
+    Hi3,
+    /// Low 3 register
+    Lo3,
+    /// DSP Control register
+    Dspctl,
+    /// Restart register
+    Restart,
+    #[doc(hidden)]
+    _Size(U),
+}
+
+fn from_raw_id<U>(id: usize) -> Option<(MipsRegId<U>, usize)> {
+    let reg = match id {
+        0..=31 => MipsRegId::Gpr(id as u8),
+        32 => MipsRegId::Status,
+        33 => MipsRegId::Lo,
+        34 => MipsRegId::Hi,
+        35 => MipsRegId::Badvaddr,
+        36 => MipsRegId::Cause,
+        37 => MipsRegId::Pc,
+        38..=69 => MipsRegId::Fpr((id as u8) - 38),
+        70 => MipsRegId::Fcsr,
+        71 => MipsRegId::Fir,
+        72 => MipsRegId::Hi1,
+        73 => MipsRegId::Lo1,
+        74 => MipsRegId::Hi2,
+        75 => MipsRegId::Lo2,
+        76 => MipsRegId::Hi3,
+        77 => MipsRegId::Lo3,
+        // `MipsRegId::Dspctl` is the only register that will always be 4 bytes wide
+        78 => return Some((MipsRegId::Dspctl, 4)),
+        79 => MipsRegId::Restart,
+        _ => return None,
+    };
+
+    let ptrsize = core::mem::size_of::<U>();
+    Some((reg, ptrsize))
+}
+
+impl RegId for MipsRegId<u32> {
+    fn from_raw_id(id: usize) -> Option<(Self, usize)> {
+        from_raw_id::<u32>(id)
+    }
+}
+
+impl RegId for MipsRegId<u64> {
+    fn from_raw_id(id: usize) -> Option<(Self, usize)> {
+        from_raw_id::<u64>(id)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use gdbstub::arch::RegId;
+    use gdbstub::arch::Registers;
+
+    fn test<Rs: Registers, RId: RegId>() {
+        // Obtain the data length written by `gdb_serialize` by passing a custom
+        // closure.
+        let mut serialized_data_len = 0;
+        let counter = |b: Option<u8>| {
+            if b.is_some() {
+                serialized_data_len += 1;
+            }
+        };
+        Rs::default().gdb_serialize(counter);
+
+        // Accumulate register sizes returned by `from_raw_id`.
+        let mut i = 0;
+        let mut sum_reg_sizes = 0;
+        while let Some((_, size)) = RId::from_raw_id(i) {
+            sum_reg_sizes += size;
+            i += 1;
+        }
+
+        assert_eq!(serialized_data_len, sum_reg_sizes);
+    }
+
+    #[test]
+    fn test_mips32() {
+        test::<crate::mips::reg::MipsCoreRegsWithDsp<u32>, crate::mips::reg::id::MipsRegId<u32>>()
+    }
+
+    #[test]
+    fn test_mips64() {
+        test::<crate::mips::reg::MipsCoreRegsWithDsp<u64>, crate::mips::reg::id::MipsRegId<u64>>()
+    }
+}
diff --git a/src/mips/reg/mips.rs b/src/mips/reg/mips.rs
new file mode 100644
index 0000000..be14e33
--- /dev/null
+++ b/src/mips/reg/mips.rs
@@ -0,0 +1,271 @@
+use core::convert::TryInto;
+
+use num_traits::PrimInt;
+
+use gdbstub::arch::Registers;
+use gdbstub::internal::LeBytes;
+
+/// MIPS registers.
+///
+/// The register width is set to `u32` or `u64` based on the `<U>` type.
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-cpu.xml
+#[derive(Debug, Default, Clone, Eq, PartialEq)]
+pub struct MipsCoreRegs<U> {
+    /// General purpose registers (R0-R31)
+    pub r: [U; 32],
+    /// Low register (regnum 33)
+    pub lo: U,
+    /// High register (regnum 34)
+    pub hi: U,
+    /// Program Counter (regnum 37)
+    pub pc: U,
+    /// CP0 registers
+    pub cp0: MipsCp0Regs<U>,
+    /// FPU registers
+    pub fpu: MipsFpuRegs<U>,
+}
+
+/// MIPS CP0 (coprocessor 0) registers.
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-cp0.xml
+#[derive(Debug, Default, Clone, Eq, PartialEq)]
+pub struct MipsCp0Regs<U> {
+    /// Status register (regnum 32)
+    pub status: U,
+    /// Bad Virtual Address register (regnum 35)
+    pub badvaddr: U,
+    /// Exception Cause register (regnum 36)
+    pub cause: U,
+}
+
+/// MIPS FPU registers.
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-fpu.xml
+#[derive(Debug, Default, Clone, Eq, PartialEq)]
+pub struct MipsFpuRegs<U> {
+    /// FP registers (F0-F31) starting at regnum 38
+    pub r: [U; 32],
+    /// Floating-point Control Status register
+    pub fcsr: U,
+    /// Floating-point Implementation Register
+    pub fir: U,
+}
+
+/// MIPS DSP registers.
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-dsp.xml
+#[derive(Debug, Default, Clone, Eq, PartialEq)]
+pub struct MipsDspRegs<U> {
+    /// High 1 register (regnum 72)
+    pub hi1: U,
+    /// Low 1 register (regnum 73)
+    pub lo1: U,
+    /// High 2 register (regnum 74)
+    pub hi2: U,
+    /// Low 2 register (regnum 75)
+    pub lo2: U,
+    /// High 3 register (regnum 76)
+    pub hi3: U,
+    /// Low 3 register (regnum 77)
+    pub lo3: U,
+    /// DSP Control register (regnum 78)
+    /// Note: This register will always be 32-bit regardless of the target
+    /// https://sourceware.org/gdb/current/onlinedocs/gdb/MIPS-Features.html#MIPS-Features
+    pub dspctl: u32,
+    /// Restart register (regnum 79)
+    pub restart: U,
+}
+
+/// MIPS core and DSP registers.
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-dsp-linux.xml
+#[derive(Debug, Default, Clone, Eq, PartialEq)]
+pub struct MipsCoreRegsWithDsp<U> {
+    /// Core registers
+    pub core: MipsCoreRegs<U>,
+    /// DSP registers
+    pub dsp: MipsDspRegs<U>,
+}
+
+impl<U> Registers for MipsCoreRegs<U>
+where
+    U: PrimInt + LeBytes + Default + core::fmt::Debug,
+{
+    type ProgramCounter = U;
+
+    fn pc(&self) -> Self::ProgramCounter {
+        self.pc
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_le_bytes {
+            ($value:expr) => {
+                let mut buf = [0; 16];
+                // infallible (unless digit is a >128 bit number)
+                let len = $value.to_le_bytes(&mut buf).unwrap();
+                let buf = &buf[..len];
+                for b in buf {
+                    write_byte(Some(*b));
+                }
+            };
+        }
+
+        // Write GPRs
+        for reg in self.r.iter() {
+            write_le_bytes!(reg);
+        }
+
+        // Status register is regnum 32
+        write_le_bytes!(&self.cp0.status);
+
+        // Low and high registers are regnums 33 and 34
+        write_le_bytes!(&self.lo);
+        write_le_bytes!(&self.hi);
+
+        // Badvaddr and Cause registers are regnums 35 and 36
+        write_le_bytes!(&self.cp0.badvaddr);
+        write_le_bytes!(&self.cp0.cause);
+
+        // Program Counter is regnum 37
+        write_le_bytes!(&self.pc);
+
+        // Write FPRs
+        for reg in self.fpu.r.iter() {
+            write_le_bytes!(&reg);
+        }
+
+        // Write FCSR and FIR registers
+        write_le_bytes!(&self.fpu.fcsr);
+        write_le_bytes!(&self.fpu.fir);
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        let ptrsize = core::mem::size_of::<U>();
+
+        // Ensure bytes contains enough data for all 72 registers
+        if bytes.len() < ptrsize * 72 {
+            return Err(());
+        }
+
+        // All core registers are the same size
+        let mut regs = bytes
+            .chunks_exact(ptrsize)
+            .map(|c| U::from_le_bytes(c).unwrap());
+
+        // Read GPRs
+        for reg in self.r.iter_mut() {
+            *reg = regs.next().ok_or(())?
+        }
+
+        // Read Status register
+        self.cp0.status = regs.next().ok_or(())?;
+
+        // Read Low and High registers
+        self.lo = regs.next().ok_or(())?;
+        self.hi = regs.next().ok_or(())?;
+
+        // Read Badvaddr and Cause registers
+        self.cp0.badvaddr = regs.next().ok_or(())?;
+        self.cp0.cause = regs.next().ok_or(())?;
+
+        // Read the Program Counter
+        self.pc = regs.next().ok_or(())?;
+
+        // Read FPRs
+        for reg in self.fpu.r.iter_mut() {
+            *reg = regs.next().ok_or(())?
+        }
+
+        // Read FCSR and FIR registers
+        self.fpu.fcsr = regs.next().ok_or(())?;
+        self.fpu.fir = regs.next().ok_or(())?;
+
+        Ok(())
+    }
+}
+
+impl<U> Registers for MipsCoreRegsWithDsp<U>
+where
+    U: PrimInt + LeBytes + Default + core::fmt::Debug,
+{
+    type ProgramCounter = U;
+
+    fn pc(&self) -> Self::ProgramCounter {
+        self.core.pc
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_le_bytes {
+            ($value:expr) => {
+                let mut buf = [0; 16];
+                // infallible (unless digit is a >128 bit number)
+                let len = $value.to_le_bytes(&mut buf).unwrap();
+                let buf = &buf[..len];
+                for b in buf {
+                    write_byte(Some(*b));
+                }
+            };
+        }
+
+        // Serialize the core registers first
+        self.core.gdb_serialize(&mut write_byte);
+
+        // Write the DSP registers
+        write_le_bytes!(&self.dsp.hi1);
+        write_le_bytes!(&self.dsp.lo1);
+        write_le_bytes!(&self.dsp.hi2);
+        write_le_bytes!(&self.dsp.lo2);
+        write_le_bytes!(&self.dsp.hi3);
+        write_le_bytes!(&self.dsp.lo3);
+
+        for b in &self.dsp.dspctl.to_le_bytes() {
+            write_byte(Some(*b));
+        }
+
+        write_le_bytes!(&self.dsp.restart);
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        // Deserialize the core registers first
+        self.core.gdb_deserialize(bytes)?;
+
+        // Ensure bytes contains enough data for all 79 registers of target-width
+        // and the dspctl register which is always 4 bytes
+        let ptrsize = core::mem::size_of::<U>();
+        if bytes.len() < (ptrsize * 79) + 4 {
+            return Err(());
+        }
+
+        // Calculate the offsets to the DSP registers based on the ptrsize
+        let dspregs_start = ptrsize * 72;
+        let dspctl_start = ptrsize * 78;
+
+        // Read up until the dspctl register
+        let mut regs = bytes[dspregs_start..dspctl_start]
+            .chunks_exact(ptrsize)
+            .map(|c| U::from_le_bytes(c).unwrap());
+
+        self.dsp.hi1 = regs.next().ok_or(())?;
+        self.dsp.lo1 = regs.next().ok_or(())?;
+        self.dsp.hi2 = regs.next().ok_or(())?;
+        self.dsp.lo2 = regs.next().ok_or(())?;
+        self.dsp.hi3 = regs.next().ok_or(())?;
+        self.dsp.lo3 = regs.next().ok_or(())?;
+
+        // Dspctl will always be a u32
+        self.dsp.dspctl =
+            u32::from_le_bytes(bytes[dspctl_start..dspctl_start + 4].try_into().unwrap());
+
+        // Only 4 or 8 bytes should remain to be read
+        self.dsp.restart = U::from_le_bytes(
+            bytes[dspctl_start + 4..]
+                .chunks_exact(ptrsize)
+                .next()
+                .ok_or(())?,
+        )
+        .unwrap();
+
+        Ok(())
+    }
+}
diff --git a/src/mips/reg/mod.rs b/src/mips/reg/mod.rs
new file mode 100644
index 0000000..3cafbd1
--- /dev/null
+++ b/src/mips/reg/mod.rs
@@ -0,0 +1,11 @@
+//! `Register` structs for MIPS architectures.
+
+/// `RegId` definitions for MIPS architectures.
+pub mod id;
+
+mod mips;
+
+pub use mips::MipsCoreRegs;
+pub use mips::MipsCoreRegsWithDsp;
+pub use mips::MipsCp0Regs;
+pub use mips::MipsFpuRegs;
diff --git a/src/msp430/mod.rs b/src/msp430/mod.rs
new file mode 100644
index 0000000..456ce18
--- /dev/null
+++ b/src/msp430/mod.rs
@@ -0,0 +1,26 @@
+//! Implementations for the TI-MSP430 family of MCUs.
+
+use gdbstub::arch::Arch;
+use gdbstub::arch::RegId;
+
+pub mod reg;
+
+/// Implements `Arch` for standard 16-bit TI-MSP430 MCUs.
+///
+/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for
+/// more info about the `RegIdImpl` type parameter.
+pub enum Msp430<RegIdImpl: RegId = reg::id::Msp430RegId> {
+    #[doc(hidden)]
+    _Marker(core::marker::PhantomData<RegIdImpl>),
+}
+
+impl<RegIdImpl: RegId> Arch for Msp430<RegIdImpl> {
+    type Usize = u16;
+    type Registers = reg::Msp430Regs;
+    type RegId = RegIdImpl;
+    type BreakpointKind = usize;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(r#"<target version="1.0"><architecture>msp430</architecture></target>"#)
+    }
+}
diff --git a/src/msp430/reg/id.rs b/src/msp430/reg/id.rs
new file mode 100644
index 0000000..1c38763
--- /dev/null
+++ b/src/msp430/reg/id.rs
@@ -0,0 +1,71 @@
+use gdbstub::arch::RegId;
+
+/// TI-MSP430 register identifier.
+///
+/// GDB does not provide a XML file for the MSP430.
+/// The best file to reference is [msp430-tdep.c](https://github.com/bminor/binutils-gdb/blob/master/gdb/msp430-tdep.c).
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum Msp430RegId {
+    /// Program Counter (R0)
+    Pc,
+    /// Stack Pointer (R1)
+    Sp,
+    /// Status Register (R2)
+    Sr,
+    /// Constant Generator (R3)
+    Cg,
+    /// General Purpose Registers (R4-R15)
+    Gpr(u8),
+}
+
+impl RegId for Msp430RegId {
+    fn from_raw_id(id: usize) -> Option<(Self, usize)> {
+        let reg = match id {
+            0 => Self::Pc,
+            1 => Self::Sp,
+            2 => Self::Sr,
+            3 => Self::Cg,
+            4..=15 => Self::Gpr((id as u8) - 4),
+            _ => return None,
+        };
+        Some((reg, 2))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use gdbstub::arch::RegId;
+    use gdbstub::arch::Registers;
+
+    fn test<Rs: Registers, RId: RegId>() {
+        // Obtain the data length written by `gdb_serialize` by passing a custom
+        // closure.
+        let mut serialized_data_len = 0;
+        let counter = |b: Option<u8>| {
+            if b.is_some() {
+                serialized_data_len += 1;
+            }
+        };
+        Rs::default().gdb_serialize(counter);
+
+        // The `Msp430Regs` implementation does not increment the size for
+        // the CG register since it will always be the constant zero.
+        serialized_data_len += 4;
+
+        // Accumulate register sizes returned by `from_raw_id`.
+        let mut i = 0;
+        let mut sum_reg_sizes = 0;
+        while let Some((_, size)) = RId::from_raw_id(i) {
+            sum_reg_sizes += size;
+            i += 1;
+        }
+
+        assert_eq!(serialized_data_len, sum_reg_sizes);
+    }
+
+    #[test]
+    fn test_msp430() {
+        test::<crate::msp430::reg::Msp430Regs, crate::msp430::reg::id::Msp430RegId>()
+    }
+}
diff --git a/src/msp430/reg/mod.rs b/src/msp430/reg/mod.rs
new file mode 100644
index 0000000..2b1285b
--- /dev/null
+++ b/src/msp430/reg/mod.rs
@@ -0,0 +1,8 @@
+//! `Register` structs for various TI-MSP430 CPUs.
+
+/// `RegId` definitions for various TI-MSP430 CPUs.
+pub mod id;
+
+mod msp430;
+
+pub use msp430::Msp430Regs;
diff --git a/src/msp430/reg/msp430.rs b/src/msp430/reg/msp430.rs
new file mode 100644
index 0000000..652fc1b
--- /dev/null
+++ b/src/msp430/reg/msp430.rs
@@ -0,0 +1,71 @@
+use gdbstub::arch::Registers;
+
+/// 16-bit TI-MSP430 registers.
+#[derive(Debug, Default, Clone, Eq, PartialEq)]
+pub struct Msp430Regs {
+    /// Program Counter (R0)
+    pub pc: u16,
+    /// Stack Pointer (R1)
+    pub sp: u16,
+    /// Status Register (R2)
+    pub sr: u16,
+    /// General Purpose Registers (R4-R15)
+    pub r: [u16; 11],
+}
+
+impl Registers for Msp430Regs {
+    type ProgramCounter = u16;
+
+    fn pc(&self) -> Self::ProgramCounter {
+        self.pc
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_bytes {
+            ($bytes:expr) => {
+                for b in $bytes {
+                    write_byte(Some(*b))
+                }
+            };
+        }
+
+        write_bytes!(&self.pc.to_le_bytes());
+        write_bytes!(&self.sp.to_le_bytes());
+        write_bytes!(&self.sr.to_le_bytes());
+        (0..4).for_each(|_| write_byte(None)); // Constant Generator (CG/R3)
+        for reg in self.r.iter() {
+            write_bytes!(&reg.to_le_bytes());
+        }
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        // ensure bytes.chunks_exact(2) won't panic
+        if bytes.len() % 2 != 0 {
+            return Err(());
+        }
+
+        use core::convert::TryInto;
+        let mut regs = bytes
+            .chunks_exact(2)
+            .map(|c| u16::from_le_bytes(c.try_into().unwrap()));
+
+        self.pc = regs.next().ok_or(())?;
+        self.sp = regs.next().ok_or(())?;
+        self.sr = regs.next().ok_or(())?;
+
+        // Constant Generator (CG/R3) should always be 0
+        if regs.next().ok_or(())? != 0 {
+            return Err(());
+        }
+
+        for reg in self.r.iter_mut() {
+            *reg = regs.next().ok_or(())?
+        }
+
+        if regs.next().is_some() {
+            return Err(());
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/ppc/mod.rs b/src/ppc/mod.rs
new file mode 100644
index 0000000..60beed3
--- /dev/null
+++ b/src/ppc/mod.rs
@@ -0,0 +1,28 @@
+//! Implementations for various PowerPC architectures.
+
+use gdbstub::arch::Arch;
+use gdbstub::arch::RegId;
+
+pub mod reg;
+
+/// Implements `Arch` for 32-bit PowerPC + AltiVec SIMD.
+///
+/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for
+/// more info about the `RegIdImpl` type parameter.
+pub enum PowerPcAltivec32<RegIdImpl: RegId> {
+    #[doc(hidden)]
+    _Marker(core::marker::PhantomData<RegIdImpl>),
+}
+
+impl<RegIdImpl: RegId> Arch for PowerPcAltivec32<RegIdImpl> {
+    type Usize = u32;
+    type Registers = reg::PowerPcCommonRegs;
+    type RegId = RegIdImpl;
+    type BreakpointKind = usize;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(
+            r#"<target version="1.0"><architecture>powerpc:common</architecture><feature name="org.gnu.gdb.power.core"></feature><feature name="org.gnu.gdb.power.fpu"></feature><feature name="org.gnu.gdb.power.altivec"></feature></target>"#,
+        )
+    }
+}
diff --git a/src/ppc/reg/common.rs b/src/ppc/reg/common.rs
new file mode 100644
index 0000000..45bdb1c
--- /dev/null
+++ b/src/ppc/reg/common.rs
@@ -0,0 +1,166 @@
+use gdbstub::arch::Registers;
+
+use super::PpcVector;
+
+use core::convert::TryInto;
+
+/// 32-bit PowerPC core registers, FPU registers, and AltiVec SIMD registers.
+///
+/// Sources:
+/// * https://github.com/bminor/binutils-gdb/blob/master/gdb/features/rs6000/powerpc-altivec32.xml
+/// * https://github.com/bminor/binutils-gdb/blob/master/gdb/features/rs6000/power-core.xml
+/// * https://github.com/bminor/binutils-gdb/blob/master/gdb/features/rs6000/power-fpu.xml
+/// * https://github.com/bminor/binutils-gdb/blob/master/gdb/features/rs6000/power-altivec.xml
+#[derive(Debug, Default, Clone, PartialEq)]
+pub struct PowerPcCommonRegs {
+    /// General purpose registers
+    pub r: [u32; 32],
+    /// Floating Point registers
+    pub f: [f64; 32],
+    /// Program counter
+    pub pc: u32,
+    /// Machine state
+    pub msr: u32,
+    /// Condition register
+    pub cr: u32,
+    /// Link register
+    pub lr: u32,
+    /// Count register
+    pub ctr: u32,
+    /// Integer exception register
+    pub xer: u32,
+    /// Floating-point status and control register
+    pub fpscr: u32,
+    /// Vector registers
+    pub vr: [PpcVector; 32],
+    /// Vector status and control register
+    pub vscr: u32,
+    /// Vector context save register
+    pub vrsave: u32,
+}
+
+impl Registers for PowerPcCommonRegs {
+    type ProgramCounter = u32;
+
+    fn pc(&self) -> Self::ProgramCounter {
+        self.pc
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_bytes {
+            ($bytes:expr) => {
+                for b in $bytes {
+                    write_byte(Some(*b))
+                }
+            };
+        }
+
+        macro_rules! write_regs {
+            ($($reg:ident),*) => {
+                $(
+                    write_bytes!(&self.$reg.to_be_bytes());
+                )*
+            }
+        }
+
+        for reg in &self.r {
+            write_bytes!(&reg.to_be_bytes());
+        }
+
+        for reg in &self.f {
+            write_bytes!(&reg.to_be_bytes());
+        }
+
+        write_regs!(pc, msr, cr, lr, ctr, xer, fpscr);
+
+        for &reg in &self.vr {
+            let reg: u128 = reg;
+            write_bytes!(&reg.to_be_bytes());
+        }
+
+        write_regs!(vscr, vrsave);
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        if bytes.len() < 0x3a4 {
+            return Err(());
+        }
+
+        let mut regs = bytes[0..0x80]
+            .chunks_exact(4)
+            .map(|x| u32::from_be_bytes(x.try_into().unwrap()));
+
+        for reg in &mut self.r {
+            *reg = regs.next().ok_or(())?;
+        }
+
+        let mut regs = bytes[0x80..0x180]
+            .chunks_exact(8)
+            .map(|x| f64::from_be_bytes(x.try_into().unwrap()));
+
+        for reg in &mut self.f {
+            *reg = regs.next().ok_or(())?;
+        }
+
+        macro_rules! parse_regs {
+            ($start:literal..$end:literal, $($reg:ident),*) => {
+                let mut regs = bytes[$start..$end]
+                    .chunks_exact(4)
+                    .map(|x| u32::from_be_bytes(x.try_into().unwrap()));
+                $(
+                    self.$reg = regs.next().ok_or(())?;
+                )*
+            }
+        }
+
+        parse_regs!(0x180..0x19c, pc, msr, cr, lr, ctr, xer, fpscr);
+
+        let mut regs = bytes[0x19c..0x39c]
+            .chunks_exact(0x10)
+            .map(|x| u128::from_be_bytes(x.try_into().unwrap()));
+
+        for reg in &mut self.vr {
+            *reg = regs.next().ok_or(())?;
+        }
+
+        parse_regs!(0x39c..0x3a4, vscr, vrsave);
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn ppc_core_round_trip() {
+        let regs_before = PowerPcCommonRegs {
+            r: [1; 32],
+            pc: 2,
+            msr: 3,
+            cr: 4,
+            lr: 5,
+            ctr: 6,
+            xer: 7,
+            fpscr: 8,
+            f: [9.0; 32],
+            vr: [52; 32],
+            vrsave: 10,
+            vscr: 11,
+        };
+
+        let mut data = vec![];
+
+        regs_before.gdb_serialize(|x| {
+            data.push(x.unwrap_or(b'x'));
+        });
+
+        assert_eq!(data.len(), 0x3a4);
+
+        let mut regs_after = PowerPcCommonRegs::default();
+        regs_after.gdb_deserialize(&data).unwrap();
+
+        assert_eq!(regs_before, regs_after);
+    }
+}
diff --git a/src/ppc/reg/id.rs b/src/ppc/reg/id.rs
new file mode 100644
index 0000000..97ccfea
--- /dev/null
+++ b/src/ppc/reg/id.rs
@@ -0,0 +1,2 @@
+// TODO: Add proper `RegId` implementation. See [issue #29](https://github.com/daniel5151/gdbstub/issues/29)
+// pub enum PowerPc32RegId {}
diff --git a/src/ppc/reg/mod.rs b/src/ppc/reg/mod.rs
new file mode 100644
index 0000000..fde8e55
--- /dev/null
+++ b/src/ppc/reg/mod.rs
@@ -0,0 +1,9 @@
+//! `Register` structs for PowerPC architectures
+
+/// `RegId` definitions for PowerPC architectures.
+pub mod id;
+
+mod common;
+
+pub use common::PowerPcCommonRegs;
+type PpcVector = u128;
diff --git a/src/riscv/mod.rs b/src/riscv/mod.rs
new file mode 100644
index 0000000..62ce98e
--- /dev/null
+++ b/src/riscv/mod.rs
@@ -0,0 +1,35 @@
+//! Implementations for the [RISC-V](https://riscv.org/) architecture.
+//!
+//! *Note*: currently only supports integer versions of the ISA.
+
+use gdbstub::arch::Arch;
+
+pub mod reg;
+
+/// Implements `Arch` for 32-bit RISC-V.
+pub enum Riscv32 {}
+
+/// Implements `Arch` for 64-bit RISC-V.
+pub enum Riscv64 {}
+
+impl Arch for Riscv32 {
+    type Usize = u32;
+    type Registers = reg::RiscvCoreRegs<u32>;
+    type RegId = reg::id::RiscvRegId<u32>;
+    type BreakpointKind = usize;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(r#"<target version="1.0"><architecture>riscv</architecture></target>"#)
+    }
+}
+
+impl Arch for Riscv64 {
+    type Usize = u64;
+    type Registers = reg::RiscvCoreRegs<u64>;
+    type RegId = reg::id::RiscvRegId<u64>;
+    type BreakpointKind = usize;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(r#"<target version="1.0"><architecture>riscv64</architecture></target>"#)
+    }
+}
diff --git a/src/riscv/reg/id.rs b/src/riscv/reg/id.rs
new file mode 100644
index 0000000..b6e589d
--- /dev/null
+++ b/src/riscv/reg/id.rs
@@ -0,0 +1,43 @@
+use gdbstub::arch::RegId;
+
+/// RISC-V Register identifier.
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum RiscvRegId<U> {
+    /// General Purpose Register (x0-x31).
+    Gpr(u8),
+    /// Floating Point Register (f0-f31).
+    Fpr(u8),
+    /// Program Counter.
+    Pc,
+    /// Control and Status Register.
+    Csr(u16),
+    /// Privilege level.
+    Priv,
+
+    #[doc(hidden)]
+    _Marker(core::marker::PhantomData<U>),
+}
+
+macro_rules! impl_riscv_reg_id {
+    ($usize:ty) => {
+        impl RegId for RiscvRegId<$usize> {
+            fn from_raw_id(id: usize) -> Option<(Self, usize)> {
+                const USIZE: usize = core::mem::size_of::<$usize>();
+
+                let reg_size = match id {
+                    0..=31 => (Self::Gpr(id as u8), USIZE),
+                    32 => (Self::Pc, USIZE),
+                    33..=64 => (Self::Fpr((id - 33) as u8), USIZE),
+                    65..=4160 => (Self::Csr((id - 65) as u16), USIZE),
+                    4161 => (Self::Priv, 1),
+                    _ => return None,
+                };
+                Some(reg_size)
+            }
+        }
+    };
+}
+
+impl_riscv_reg_id!(u32);
+impl_riscv_reg_id!(u64);
diff --git a/src/riscv/reg/mod.rs b/src/riscv/reg/mod.rs
new file mode 100644
index 0000000..e501c47
--- /dev/null
+++ b/src/riscv/reg/mod.rs
@@ -0,0 +1,8 @@
+//! `Register` structs for RISC-V architectures.
+
+/// `RegId` definitions for RISC-V architectures.
+pub mod id;
+
+mod riscv;
+
+pub use riscv::RiscvCoreRegs;
diff --git a/src/riscv/reg/riscv.rs b/src/riscv/reg/riscv.rs
new file mode 100644
index 0000000..9875ad2
--- /dev/null
+++ b/src/riscv/reg/riscv.rs
@@ -0,0 +1,77 @@
+use num_traits::PrimInt;
+
+use gdbstub::arch::Registers;
+use gdbstub::internal::LeBytes;
+
+/// RISC-V Integer registers.
+///
+/// The register width is set to `u32` or `u64` based on the `<U>` type.
+///
+/// Useful links:
+/// * [GNU binutils-gdb XML descriptions](https://github.com/bminor/binutils-gdb/blob/master/gdb/features/riscv)
+/// * [riscv-tdep.h](https://github.com/bminor/binutils-gdb/blob/master/gdb/riscv-tdep.h)
+#[derive(Debug, Default, Clone, PartialEq)]
+pub struct RiscvCoreRegs<U> {
+    /// General purpose registers (x0-x31)
+    pub x: [U; 32],
+    /// Program counter
+    pub pc: U,
+}
+
+impl<U> Registers for RiscvCoreRegs<U>
+where
+    U: PrimInt + LeBytes + Default + core::fmt::Debug,
+{
+    type ProgramCounter = U;
+
+    fn pc(&self) -> Self::ProgramCounter {
+        self.pc
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_le_bytes {
+            ($value:expr) => {
+                let mut buf = [0; 16];
+                // infallible (unless digit is a >128 bit number)
+                let len = $value.to_le_bytes(&mut buf).unwrap();
+                let buf = &buf[..len];
+                for b in buf {
+                    write_byte(Some(*b));
+                }
+            };
+        }
+
+        // Write GPRs
+        for reg in self.x.iter() {
+            write_le_bytes!(reg);
+        }
+
+        // Program Counter is regnum 33
+        write_le_bytes!(&self.pc);
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        let ptrsize = core::mem::size_of::<U>();
+
+        // ensure bytes.chunks_exact(ptrsize) won't panic
+        if bytes.len() % ptrsize != 0 {
+            return Err(());
+        }
+
+        let mut regs = bytes
+            .chunks_exact(ptrsize)
+            .map(|c| U::from_le_bytes(c).unwrap());
+
+        // Read GPRs
+        for reg in self.x.iter_mut() {
+            *reg = regs.next().ok_or(())?
+        }
+        self.pc = regs.next().ok_or(())?;
+
+        if regs.next().is_some() {
+            return Err(());
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/x86/mod.rs b/src/x86/mod.rs
new file mode 100644
index 0000000..e7fec5c
--- /dev/null
+++ b/src/x86/mod.rs
@@ -0,0 +1,52 @@
+//! Implementations for various x86 architectures.
+
+use gdbstub::arch::Arch;
+use gdbstub::arch::RegId;
+
+pub mod reg;
+
+/// Implements `Arch` for 64-bit x86 + SSE Extensions.
+///
+/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for
+/// more info about the `RegIdImpl` type parameter.
+#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
+pub enum X86_64_SSE<RegIdImpl: RegId = reg::id::X86_64CoreRegId> {
+    #[doc(hidden)]
+    _Marker(core::marker::PhantomData<RegIdImpl>),
+}
+
+impl<RegIdImpl: RegId> Arch for X86_64_SSE<RegIdImpl> {
+    type Usize = u64;
+    type Registers = reg::X86_64CoreRegs;
+    type RegId = RegIdImpl;
+    type BreakpointKind = usize;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(
+            r#"<target version="1.0"><architecture>i386:x86-64</architecture><feature name="org.gnu.gdb.i386.sse"></feature></target>"#,
+        )
+    }
+}
+
+/// Implements `Arch` for 32-bit x86 + SSE Extensions.
+///
+/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for
+/// more info about the `RegIdImpl` type parameter.
+#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
+pub enum X86_SSE<RegIdImpl: RegId = reg::id::X86CoreRegId> {
+    #[doc(hidden)]
+    _Marker(core::marker::PhantomData<RegIdImpl>),
+}
+
+impl<RegIdImpl: RegId> Arch for X86_SSE<RegIdImpl> {
+    type Usize = u32;
+    type Registers = reg::X86CoreRegs;
+    type RegId = RegIdImpl;
+    type BreakpointKind = usize;
+
+    fn target_description_xml() -> Option<&'static str> {
+        Some(
+            r#"<target version="1.0"><architecture>i386:intel</architecture><feature name="org.gnu.gdb.i386.sse"></feature></target>"#,
+        )
+    }
+}
diff --git a/src/x86/reg/core32.rs b/src/x86/reg/core32.rs
new file mode 100644
index 0000000..d91e707
--- /dev/null
+++ b/src/x86/reg/core32.rs
@@ -0,0 +1,185 @@
+use core::convert::TryInto;
+
+use gdbstub::arch::Registers;
+
+use super::{X86SegmentRegs, X87FpuInternalRegs, F80};
+
+/// 32-bit x86 core registers (+ SSE extensions).
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-core.xml
+/// Additionally: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-sse.xml
+#[derive(Debug, Default, Clone, PartialEq)]
+pub struct X86CoreRegs {
+    /// Accumulator
+    pub eax: u32,
+    /// Count register
+    pub ecx: u32,
+    /// Data register
+    pub edx: u32,
+    /// Base register
+    pub ebx: u32,
+    /// Stack pointer
+    pub esp: u32,
+    /// Base pointer
+    pub ebp: u32,
+    /// Source index
+    pub esi: u32,
+    /// Destination index
+    pub edi: u32,
+    /// Instruction pointer
+    pub eip: u32,
+    /// Status register
+    pub eflags: u32,
+    /// Segment registers: CS, SS, DS, ES, FS, GS
+    pub segments: X86SegmentRegs,
+    /// FPU registers: ST0 through ST7
+    pub st: [F80; 8],
+    /// FPU internal registers
+    pub fpu: X87FpuInternalRegs,
+    /// SIMD Registers: XMM0 through XMM7
+    pub xmm: [u128; 8],
+    /// SSE Status/Control Register
+    pub mxcsr: u32,
+}
+
+impl Registers for X86CoreRegs {
+    type ProgramCounter = u32;
+
+    fn pc(&self) -> Self::ProgramCounter {
+        self.eip
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_bytes {
+            ($bytes:expr) => {
+                for b in $bytes {
+                    write_byte(Some(*b))
+                }
+            };
+        }
+
+        macro_rules! write_regs {
+            ($($reg:ident),*) => {
+                $(
+                    write_bytes!(&self.$reg.to_le_bytes());
+                )*
+            }
+        }
+
+        write_regs!(eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags);
+
+        self.segments.gdb_serialize(&mut write_byte);
+
+        // st0 to st7
+        for st_reg in &self.st {
+            write_bytes!(st_reg);
+        }
+
+        self.fpu.gdb_serialize(&mut write_byte);
+
+        // xmm0 to xmm15
+        for xmm_reg in &self.xmm {
+            write_bytes!(&xmm_reg.to_le_bytes());
+        }
+
+        // mxcsr
+        write_bytes!(&self.mxcsr.to_le_bytes());
+
+        // padding
+        (0..4).for_each(|_| write_byte(None))
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        if bytes.len() < 0x138 {
+            return Err(());
+        }
+
+        macro_rules! parse_regs {
+            ($($reg:ident),*) => {
+                let mut regs = bytes[0..0x28]
+                    .chunks_exact(4)
+                    .map(|x| u32::from_le_bytes(x.try_into().unwrap()));
+                $(
+                    self.$reg = regs.next().ok_or(())?;
+                )*
+            }
+        }
+
+        parse_regs!(eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags);
+
+        self.segments.gdb_deserialize(&bytes[0x28..0x40])?;
+
+        let mut regs = bytes[0x40..0x90].chunks_exact(10).map(TryInto::try_into);
+
+        for reg in self.st.iter_mut() {
+            *reg = regs.next().ok_or(())?.map_err(|_| ())?;
+        }
+
+        self.fpu.gdb_deserialize(&bytes[0x90..0xb0])?;
+
+        let mut regs = bytes[0xb0..0x130]
+            .chunks_exact(0x10)
+            .map(|x| u128::from_le_bytes(x.try_into().unwrap()));
+
+        for reg in self.xmm.iter_mut() {
+            *reg = regs.next().ok_or(())?;
+        }
+
+        self.mxcsr = u32::from_le_bytes(bytes[0x130..0x134].try_into().unwrap());
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn x86_core_round_trip() {
+        let regs_before = X86CoreRegs {
+            eax: 1,
+            ecx: 2,
+            edx: 3,
+            ebx: 4,
+            esp: 5,
+            ebp: 6,
+            esi: 7,
+            edi: 8,
+            eip: 9,
+            eflags: 10,
+            segments: X86SegmentRegs {
+                cs: 11,
+                ss: 12,
+                ds: 13,
+                es: 14,
+                fs: 15,
+                gs: 16,
+            },
+            st: Default::default(),
+            fpu: X87FpuInternalRegs {
+                fctrl: 17,
+                fstat: 18,
+                ftag: 19,
+                fiseg: 20,
+                fioff: 21,
+                foseg: 22,
+                fooff: 23,
+                fop: 24,
+            },
+            xmm: Default::default(),
+            mxcsr: 99,
+        };
+
+        let mut data = vec![];
+
+        regs_before.gdb_serialize(|x| {
+            data.push(x.unwrap_or(b'x'));
+        });
+
+        let mut regs_after = X86CoreRegs::default();
+        regs_after.gdb_deserialize(&data).unwrap();
+
+        assert_eq!(regs_before, regs_after);
+    }
+}
diff --git a/src/x86/reg/core64.rs b/src/x86/reg/core64.rs
new file mode 100644
index 0000000..e0fc5af
--- /dev/null
+++ b/src/x86/reg/core64.rs
@@ -0,0 +1,119 @@
+use core::convert::TryInto;
+
+use gdbstub::arch::Registers;
+
+use super::{X86SegmentRegs, X87FpuInternalRegs, F80};
+
+/// 64-bit x86 core registers (+ SSE extensions).
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-core.xml
+/// Additionally: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-sse.xml
+#[derive(Debug, Default, Clone, PartialEq)]
+pub struct X86_64CoreRegs {
+    /// RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15
+    pub regs: [u64; 16],
+    /// Status register
+    pub eflags: u32,
+    /// Instruction pointer
+    pub rip: u64,
+    /// Segment registers: CS, SS, DS, ES, FS, GS
+    pub segments: X86SegmentRegs,
+    /// FPU registers: ST0 through ST7
+    pub st: [F80; 8],
+    /// FPU internal registers
+    pub fpu: X87FpuInternalRegs,
+    /// SIMD Registers: XMM0 through XMM15
+    pub xmm: [u128; 0x10],
+    /// SSE Status/Control Register
+    pub mxcsr: u32,
+}
+
+impl Registers for X86_64CoreRegs {
+    type ProgramCounter = u64;
+
+    fn pc(&self) -> Self::ProgramCounter {
+        self.rip
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_bytes {
+            ($bytes:expr) => {
+                for b in $bytes {
+                    write_byte(Some(*b))
+                }
+            };
+        }
+
+        // rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8-r15
+        for reg in &self.regs {
+            write_bytes!(&reg.to_le_bytes());
+        }
+
+        // rip
+        write_bytes!(&self.rip.to_le_bytes());
+
+        // eflags
+        write_bytes!(&self.eflags.to_le_bytes());
+
+        self.segments.gdb_serialize(&mut write_byte);
+
+        // st0 to st7
+        for st_reg in &self.st {
+            write_bytes!(st_reg);
+        }
+
+        self.fpu.gdb_serialize(&mut write_byte);
+
+        // xmm0 to xmm15
+        for xmm_reg in &self.xmm {
+            write_bytes!(&xmm_reg.to_le_bytes());
+        }
+
+        // mxcsr
+        write_bytes!(&self.mxcsr.to_le_bytes());
+
+        // padding?
+        // XXX: Couldn't figure out what these do and GDB doesn't actually display any
+        // registers that use these values.
+        (0..0x18).for_each(|_| write_byte(None))
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        if bytes.len() < 0x218 {
+            return Err(());
+        }
+
+        let mut regs = bytes[0..0x80]
+            .chunks_exact(8)
+            .map(|x| u64::from_le_bytes(x.try_into().unwrap()));
+
+        for reg in self.regs.iter_mut() {
+            *reg = regs.next().ok_or(())?;
+        }
+
+        self.rip = u64::from_le_bytes(bytes[0x80..0x88].try_into().unwrap());
+        self.eflags = u32::from_le_bytes(bytes[0x88..0x8C].try_into().unwrap());
+
+        self.segments.gdb_deserialize(&bytes[0x8C..0xA4])?;
+
+        let mut regs = bytes[0xA4..0xF4].chunks_exact(10).map(TryInto::try_into);
+
+        for reg in self.st.iter_mut() {
+            *reg = regs.next().ok_or(())?.map_err(|_| ())?;
+        }
+
+        self.fpu.gdb_deserialize(&bytes[0xF4..0x114])?;
+
+        let mut regs = bytes[0x114..0x214]
+            .chunks_exact(0x10)
+            .map(|x| u128::from_le_bytes(x.try_into().unwrap()));
+
+        for reg in self.xmm.iter_mut() {
+            *reg = regs.next().ok_or(())?;
+        }
+
+        self.mxcsr = u32::from_le_bytes(bytes[0x214..0x218].try_into().unwrap());
+
+        Ok(())
+    }
+}
diff --git a/src/x86/reg/id.rs b/src/x86/reg/id.rs
new file mode 100644
index 0000000..03de64b
--- /dev/null
+++ b/src/x86/reg/id.rs
@@ -0,0 +1,227 @@
+use gdbstub::arch::RegId;
+
+/// FPU register identifier.
+#[derive(Debug, Clone, Copy)]
+pub enum X87FpuInternalRegId {
+    /// Floating-point control register
+    Fctrl,
+    /// Floating-point status register
+    Fstat,
+    /// Tag word
+    Ftag,
+    /// FPU instruction pointer segment
+    Fiseg,
+    /// FPU instruction pointer offset
+    Fioff,
+    /// FPU operand segment
+    Foseg,
+    /// FPU operand offset
+    Fooff,
+    /// Floating-point opcode
+    Fop,
+}
+
+impl X87FpuInternalRegId {
+    fn from_u8(val: u8) -> Option<Self> {
+        use self::X87FpuInternalRegId::*;
+
+        let r = match val {
+            0 => Fctrl,
+            1 => Fstat,
+            2 => Ftag,
+            3 => Fiseg,
+            4 => Fioff,
+            5 => Foseg,
+            6 => Fooff,
+            7 => Fop,
+            _ => return None,
+        };
+        Some(r)
+    }
+}
+
+/// Segment register identifier.
+#[derive(Debug, Clone, Copy)]
+#[allow(clippy::upper_case_acronyms)]
+pub enum X86SegmentRegId {
+    /// Code Segment
+    CS,
+    /// Stack Segment
+    SS,
+    /// Data Segment
+    DS,
+    /// Extra Segment
+    ES,
+    /// General Purpose Segment
+    FS,
+    /// General Purpose Segment
+    GS,
+}
+
+impl X86SegmentRegId {
+    fn from_u8(val: u8) -> Option<Self> {
+        use self::X86SegmentRegId::*;
+
+        let r = match val {
+            0 => CS,
+            1 => SS,
+            2 => DS,
+            3 => ES,
+            4 => FS,
+            5 => GS,
+            _ => return None,
+        };
+        Some(r)
+    }
+}
+
+/// 32-bit x86 core + SSE register identifier.
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-core.xml
+/// Additionally: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-sse.xml
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum X86CoreRegId {
+    /// Accumulator
+    Eax,
+    /// Count register
+    Ecx,
+    /// Data register
+    Edx,
+    /// Base register
+    Ebx,
+    /// Stack pointer
+    Esp,
+    /// Base pointer
+    Ebp,
+    /// Source index
+    Esi,
+    /// Destination index
+    Edi,
+    /// Instruction pointer
+    Eip,
+    /// Status register
+    Eflags,
+    /// Segment registers
+    Segment(X86SegmentRegId),
+    /// FPU registers: ST0 through ST7
+    St(u8),
+    /// FPU internal registers
+    Fpu(X87FpuInternalRegId),
+    /// SIMD Registers: XMM0 through XMM7
+    Xmm(u8),
+    /// SSE Status/Control Register
+    Mxcsr,
+}
+
+impl RegId for X86CoreRegId {
+    fn from_raw_id(id: usize) -> Option<(Self, usize)> {
+        use self::X86CoreRegId::*;
+
+        let r = match id {
+            0 => (Eax, 4),
+            1 => (Ecx, 4),
+            2 => (Edx, 4),
+            3 => (Ebx, 4),
+            4 => (Esp, 4),
+            5 => (Ebp, 4),
+            6 => (Esi, 4),
+            7 => (Edi, 4),
+            8 => (Eip, 4),
+            9 => (Eflags, 4),
+            10..=15 => (Segment(X86SegmentRegId::from_u8(id as u8 - 10)?), 4),
+            16..=23 => (St(id as u8 - 16), 10),
+            24..=31 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 24)?), 4),
+            32..=39 => (Xmm(id as u8 - 32), 16),
+            40 => (Mxcsr, 4),
+            _ => return None,
+        };
+        Some(r)
+    }
+}
+
+/// 64-bit x86 core + SSE register identifier.
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-core.xml
+/// Additionally: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-sse.xml
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum X86_64CoreRegId {
+    /// General purpose registers:
+    /// RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15
+    Gpr(u8),
+    /// Instruction pointer
+    Rip,
+    /// Status register
+    Eflags,
+    /// Segment registers
+    Segment(X86SegmentRegId),
+    /// FPU registers: ST0 through ST7
+    St(u8),
+    /// FPU internal registers
+    Fpu(X87FpuInternalRegId),
+    /// SIMD Registers: XMM0 through XMM15
+    Xmm(u8),
+    /// SSE Status/Control Register
+    Mxcsr,
+}
+
+impl RegId for X86_64CoreRegId {
+    fn from_raw_id(id: usize) -> Option<(Self, usize)> {
+        use self::X86_64CoreRegId::*;
+
+        let r = match id {
+            0..=15 => (Gpr(id as u8), 8),
+            16 => (Rip, 4),
+            17 => (Eflags, 8),
+            18..=23 => (Segment(X86SegmentRegId::from_u8(id as u8 - 18)?), 4),
+            24..=31 => (St(id as u8 - 24), 10),
+            32..=39 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 32)?), 4),
+            40..=55 => (Xmm(id as u8 - 40), 16),
+            56 => (Mxcsr, 4),
+            _ => return None,
+        };
+        Some(r)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use gdbstub::arch::RegId;
+    use gdbstub::arch::Registers;
+
+    /// Compare the following two values which are expected to be the same:
+    /// * length of data written by `Registers::gdb_serialize()` in byte
+    /// * sum of sizes of all registers obtained by `RegId::from_raw_id()`
+    fn test<Rs: Registers, RId: RegId>() {
+        // Obtain the data length written by `gdb_serialize` by passing a custom
+        // closure.
+        let mut serialized_data_len = 0;
+        let counter = |b: Option<u8>| {
+            if b.is_some() {
+                serialized_data_len += 1;
+            }
+        };
+        Rs::default().gdb_serialize(counter);
+
+        // Accumulate register sizes returned by `from_raw_id`.
+        let mut i = 0;
+        let mut sum_reg_sizes = 0;
+        while let Some((_, size)) = RId::from_raw_id(i) {
+            sum_reg_sizes += size;
+            i += 1;
+        }
+
+        assert_eq!(serialized_data_len, sum_reg_sizes);
+    }
+
+    #[test]
+    fn test_x86() {
+        test::<crate::x86::reg::X86CoreRegs, crate::x86::reg::id::X86CoreRegId>()
+    }
+
+    #[test]
+    fn test_x86_64() {
+        test::<crate::x86::reg::X86_64CoreRegs, crate::x86::reg::id::X86_64CoreRegId>()
+    }
+}
diff --git a/src/x86/reg/mod.rs b/src/x86/reg/mod.rs
new file mode 100644
index 0000000..bac64cd
--- /dev/null
+++ b/src/x86/reg/mod.rs
@@ -0,0 +1,155 @@
+//! `Register` structs for x86 architectures.
+
+use core::convert::TryInto;
+
+use gdbstub::arch::Registers;
+
+/// `RegId` definitions for x86 architectures.
+pub mod id;
+
+mod core32;
+mod core64;
+
+pub use core32::X86CoreRegs;
+pub use core64::X86_64CoreRegs;
+
+/// 80-bit floating point value
+pub type F80 = [u8; 10];
+
+/// FPU registers
+#[derive(Debug, Default, Clone, PartialEq)]
+pub struct X87FpuInternalRegs {
+    /// Floating-point control register
+    pub fctrl: u32,
+    /// Floating-point status register
+    pub fstat: u32,
+    /// Tag word
+    pub ftag: u32,
+    /// FPU instruction pointer segment
+    pub fiseg: u32,
+    /// FPU instruction pointer offset
+    pub fioff: u32,
+    /// FPU operand segment
+    pub foseg: u32,
+    /// FPU operand offset
+    pub fooff: u32,
+    /// Floating-point opcode
+    pub fop: u32,
+}
+
+impl Registers for X87FpuInternalRegs {
+    type ProgramCounter = u32;
+
+    // HACK: this struct is never used as an architecture's main register file, so
+    // using a dummy value here is fine.
+    fn pc(&self) -> Self::ProgramCounter {
+        0
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_bytes {
+            ($bytes:expr) => {
+                for b in $bytes {
+                    write_byte(Some(*b))
+                }
+            };
+        }
+
+        // Note: GDB section names don't make sense unless you read x87 FPU section 8.1:
+        // https://web.archive.org/web/20150123212110/http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-1-manual.pdf
+        write_bytes!(&self.fctrl.to_le_bytes());
+        write_bytes!(&self.fstat.to_le_bytes());
+        write_bytes!(&self.ftag.to_le_bytes());
+        write_bytes!(&self.fiseg.to_le_bytes());
+        write_bytes!(&self.fioff.to_le_bytes());
+        write_bytes!(&self.foseg.to_le_bytes());
+        write_bytes!(&self.fooff.to_le_bytes());
+        write_bytes!(&self.fop.to_le_bytes());
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        if bytes.len() != 0x20 {
+            return Err(());
+        }
+
+        let mut regs = bytes
+            .chunks_exact(4)
+            .map(|x| u32::from_le_bytes(x.try_into().unwrap()));
+
+        self.fctrl = regs.next().ok_or(())?;
+        self.fstat = regs.next().ok_or(())?;
+        self.ftag = regs.next().ok_or(())?;
+        self.fiseg = regs.next().ok_or(())?;
+        self.fioff = regs.next().ok_or(())?;
+        self.foseg = regs.next().ok_or(())?;
+        self.fooff = regs.next().ok_or(())?;
+        self.fop = regs.next().ok_or(())?;
+
+        Ok(())
+    }
+}
+
+/// x86 segment registers.
+///
+/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-core.xml
+#[derive(Debug, Default, Clone, PartialEq)]
+pub struct X86SegmentRegs {
+    /// Code Segment
+    pub cs: u32,
+    /// Stack Segment
+    pub ss: u32,
+    /// Data Segment
+    pub ds: u32,
+    /// Extra Segment
+    pub es: u32,
+    /// General Purpose Segment
+    pub fs: u32,
+    /// General Purpose Segment
+    pub gs: u32,
+}
+
+impl Registers for X86SegmentRegs {
+    type ProgramCounter = u32;
+
+    // HACK: this struct is never used as an architecture's main register file, so
+    // using a dummy value here is fine.
+    fn pc(&self) -> Self::ProgramCounter {
+        0
+    }
+
+    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
+        macro_rules! write_bytes {
+            ($bytes:expr) => {
+                for b in $bytes {
+                    write_byte(Some(*b))
+                }
+            };
+        }
+
+        write_bytes!(&self.cs.to_le_bytes());
+        write_bytes!(&self.ss.to_le_bytes());
+        write_bytes!(&self.ds.to_le_bytes());
+        write_bytes!(&self.es.to_le_bytes());
+        write_bytes!(&self.fs.to_le_bytes());
+        write_bytes!(&self.gs.to_le_bytes());
+    }
+
+    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        if bytes.len() != core::mem::size_of::<u32>() * 6 {
+            return Err(());
+        }
+
+        let mut regs = bytes
+            .chunks_exact(4)
+            .map(|x| u32::from_le_bytes(x.try_into().unwrap()));
+
+        self.cs = regs.next().ok_or(())?;
+        self.ss = regs.next().ok_or(())?;
+        self.ds = regs.next().ok_or(())?;
+        self.es = regs.next().ok_or(())?;
+        self.fs = regs.next().ok_or(())?;
+        self.gs = regs.next().ok_or(())?;
+
+        Ok(())
+    }
+}