--- /dev/null
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Register dump and debugging for 32-bit ARM
+///
+/// (c) 2019 Straylight/Edgeware
+///
+
+///----- Licensing notice ---------------------------------------------------
+///
+/// This file is part of Catacomb.
+///
+/// Catacomb is free software: you can redistribute it and/or modify it
+/// under the terms of the GNU Library General Public License as published
+/// by the Free Software Foundation; either version 2 of the License, or
+/// (at your option) any later version.
+///
+/// Catacomb is distributed in the hope that it will be useful, but
+/// WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+/// Library General Public License for more details.
+///
+/// You should have received a copy of the GNU Library General Public
+/// License along with Catacomb. If not, write to the Free Software
+/// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+/// USA.
+
+///--------------------------------------------------------------------------
+/// Preliminaries.
+
+#include "config.h"
+#include "asm-common.h"
+#include "regdump.h"
+
+ .arch armv7-a
+ .fpu neon
+
+ .text
+
+///--------------------------------------------------------------------------
+/// Main code.
+
+FUNC(regdump_gpsave)
+ endprologue
+ // On entry, r13 should point to `REGDUMP_GPSIZE' bytes of
+ // word-aligned storage to be the general-purpose save area, with r12
+ // and r14 already saved. On exit, the initial registers are saved
+ // in this space, and modified: r4 points to the general-purpose save
+ // area, r6 holds the focus address (possibly already saved), r0
+ // contains the number of bytes required in the extended save area,
+ // and other general-purpose registers are clobbered or used to
+ // communicate with `regdump_xtsave' below. Doing anything other
+ // than lowering the stack pointer and calling `regdump_xtsave' is
+ // not recommended.
+
+ // Save the easy registers.
+ stmia r13, {r0-r11}
+ mov r4, r13
+
+ // Determine the previous stack pointer and save it.
+ add r0, r4, #REGDUMP_GPSIZE
+ str r0, [r4, #13*4]
+
+ // Capture the status flags and return address. If the return
+ // address has its low bit set, then the caller was in Thumb state:
+ // clear the bit from the reconstructed PC, and set the corresponding
+ // CPSR bit.
+ mrs r0, cpsr
+ tst r14, #1
+ bic r1, r14, #1
+ orrne r0, r0, #0x00000020
+ str r0, [r13, #4*REGIX_CPSR]
+ str r1, [r13, #15*4]
+
+ // Load the focus address and save it as r6.
+ ldr r6, [r4, #4*REGIX_ADDR]
+
+ // Determine the extended save area size.
+ ldgot
+ mov r0, #8 + 8
+ leaext r12, regdump__flags
+ ldr r12, [r12]
+ tst r12, #REGF_VFP
+ addne r0, r0, #REGDUMP_FPSIZE_D16
+ tstne r12, #REGF_D32
+ addne r0, r0, #REGDUMP_FPSIZE_D32 - REGDUMP_FPSIZE_D16
+
+ // Done.
+ bx r14
+
+ENDFUNC
+
+FUNC(regdump_gprstr)
+ endprologue
+ // On entry, r4 points to a general-purpose save area, established by
+ // `regdump_gpsave'. On exit, the general-purpose registers (other
+ // than r13 and r14) are restored to their original values.
+
+ // Restore the processor flags.
+ ldr r0, [r4, #4*REGIX_CPSR]
+ msr cpsr_fs, r0
+
+ // Load the easy registers.
+ ldmia r4, {r0-r12}
+
+ // Done.
+ bx r14
+
+ENDFUNC
+
+FUNC(regdump_xtsave)
+ endprologue
+ // On entry, r13 points to an extended save area, of size determined
+ // by `regdump_gpsave' above. On exit, the save area is filled in
+ // and a handy map placed at its base.
+
+ // Set up the map/extended save area pointer.
+ add r5, r13, #7
+ bic r5, r5, #7
+
+ // Start by filling in the easy part of the map.
+ str r4, [r5, #regmap_gp]
+
+ // Fetch the flags explaining what to do.
+ ldgot
+ leaext r12, regdump__flags
+ ldr r12, [r12]
+
+ // Figure out whether there are VFP/NEON registers.
+ tst r12, #REGF_VFP
+ moveq r3, #0
+ addne r3, r5, #regmap_size
+ str r3, [r5, #regmap_fp]
+ beq 9f
+
+ // Get the FP status register.
+ vmrs r0, fpscr
+ str r0, [r3], #8
+
+ // At least the first 16.
+ vstmia r3!, {d0-d15}
+
+ // Maybe the other 16 too.
+ tst r12, #REGF_D32
+ vstmiane r3!, {d16-d31}
+
+ // Done.
+9: bx r14
+
+ENDFUNC
+
+FUNC(regdump_xtrstr)
+ endprologue
+ // On entry, r5 points to a register-save map. On exit, the extended
+ // registers are restored from the save area, r4 (pointing to the
+ // general-purpose save area) is preserved, and the other general
+ // registers are clobbered.
+
+ // Fetch the flags explaining what to do.
+ ldgot
+ leaext r12, regdump__flags
+ ldr r12, [r12]
+
+ // Figure out if there are VFP/NEON registers.
+ tst r12, #REGF_VFP
+ beq 9f
+ ldr r3, [r5, #regmap_fp]
+
+ // Load the FP status register.
+ ldr r0, [r3], #8
+ vmsr fpscr, r0
+
+ // Load the first 16 registers.
+ vldmia r3!, {d0-d15}
+
+ // And maybe the other 16.
+ tst r12, #REGF_D32
+ vldmiane r3!, {d16-d31}
+
+ // Done.
+9: bx r14
+
+ENDFUNC
+
+///----- That's all, folks --------------------------------------------------