Skip to content

Commit 4eb62ae

Browse files
author
Ronny Hansen
committed
fix DAP disassembler reads D-space instead of I-space with split I/D
When the kernel runs with PTM=1 (split I/D), the DAP disassembler and instruction-scanning helpers read memory through the runtime MMS path which can resolve to D-space. This causes overlay code disassembly to show garbage (D-space data/BSS content) instead of actual instructions. Fix: - Add Dbg_MapVirtualToPhysical() that explicitly selects I-space (PT) or D-space (APT) from the PCR, bypassing the STS_PTM+UseAPT logic. No traps generated, no PGU/WIP side effects. - New functions: Dbg_ReadVirtualMemoryISpace/DSpace, Dbg_WriteVirtualMemoryISpace/DSpace - cmd_disassemble and all instruction-scanning code now uses Dbg_ReadVirtualMemoryISpace() for instruction fetches - cmd_read_memory/cmd_write_memory support ispace:/dspace: address prefixes (via updated libdap) alongside existing phys: prefix - Update libdap submodule with I-space/D-space enum + prefix parsing
1 parent 0f32210 commit 4eb62ae

3 files changed

Lines changed: 138 additions & 14 deletions

File tree

src/cpu/cpu_mms.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,5 +960,108 @@ int Dbg_WritePhysicalMemory(uint32_t physicalAddress, uint16_t value)
960960
return 0;
961961
}
962962

963+
// ---------------------------------------------------------------------------
964+
// Debugger-only I-space / D-space virtual memory reads.
965+
//
966+
// These explicitly select the instruction (PT) or data (APT) page table
967+
// from the current level's PCR, bypassing the STS_PTM + UseAPT logic in
968+
// mapVirtualToPhysical. No traps are generated and PGU/WIP bits are not
969+
// modified, so these are safe to call from debugger command handlers
970+
// without disturbing CPU state.
971+
//
972+
// This fixes the split I/D (PTM=1) debugger bug where disassembly of
973+
// overlay code showed D-space garbage instead of I-space instructions.
974+
// ---------------------------------------------------------------------------
975+
static int Dbg_MapVirtualToPhysical(uint virtualAddress, bool useAPT)
976+
{
977+
virtualAddress &= 0xFFFF;
978+
979+
if (!pt.isInitialized)
980+
return -1;
981+
982+
uint16_t pcr = gReg->reg_PCR[CurrLEVEL];
983+
uint8_t ring = pcr & 0x03;
984+
985+
// Ring 3 shadow RAM access (same check as mapVirtualToPhysical)
986+
if ((ring == 3) && (IsAddressShadowMemory(virtualAddress, false)))
987+
return (int)virtualAddress;
988+
989+
// No paging = identity map
990+
if (!STS_PONI)
991+
return (int)(virtualAddress & 0xFFFF);
992+
993+
uint DIP = virtualAddress & 0x3FF;
994+
uint VPN = (virtualAddress >> 10) & 0x3F;
995+
uint pageTable;
996+
PageTableMode ptm;
997+
998+
if (useAPT) {
999+
// D-space: APT field
1000+
if ((pcr & (1 << 2)) != 0 && (mmsType == MMS2)) {
1001+
pageTable = (pcr >> 7) & 0xF;
1002+
ptm = Sixteen;
1003+
} else {
1004+
pageTable = (pcr >> 7) & 0x03;
1005+
ptm = Four;
1006+
}
1007+
} else {
1008+
// I-space: PT field
1009+
if ((pcr & (1 << 2)) != 0 && (mmsType == MMS2)) {
1010+
pageTable = (pcr >> 11) & 0xF;
1011+
ptm = Sixteen;
1012+
} else {
1013+
pageTable = (pcr >> 9) & 0x03;
1014+
ptm = Four;
1015+
}
1016+
}
1017+
1018+
uint32_t pageTableEntry = GetPageTableEntry(pageTable, VPN, ptm);
1019+
1020+
// Check if page is present (any permission bit set)
1021+
if ((pageTableEntry & (7L << 29)) == 0)
1022+
return -1;
1023+
1024+
uint16_t PPN;
1025+
if (STS_SEXI)
1026+
PPN = (uint16_t)(pageTableEntry & 0x3FFF);
1027+
else
1028+
PPN = (uint16_t)(pageTableEntry & 0x1FF);
1029+
1030+
int physicalAddress = ((PPN << 10) | DIP) & 0xFFFFFF;
1031+
1032+
if (physicalAddress >= ND_Memsize)
1033+
return -1;
1034+
1035+
return physicalAddress;
1036+
}
1037+
1038+
int Dbg_ReadVirtualMemoryISpace(uint virtualAddress)
1039+
{
1040+
int pa = Dbg_MapVirtualToPhysical(virtualAddress, false);
1041+
if (pa < 0) return -1;
1042+
return Dbg_ReadPhysicalMemory((uint32_t)pa);
1043+
}
1044+
1045+
int Dbg_ReadVirtualMemoryDSpace(uint virtualAddress)
1046+
{
1047+
int pa = Dbg_MapVirtualToPhysical(virtualAddress, true);
1048+
if (pa < 0) return -1;
1049+
return Dbg_ReadPhysicalMemory((uint32_t)pa);
1050+
}
1051+
1052+
int Dbg_WriteVirtualMemoryISpace(uint virtualAddress, uint16_t value)
1053+
{
1054+
int pa = Dbg_MapVirtualToPhysical(virtualAddress, false);
1055+
if (pa < 0) return -1;
1056+
return Dbg_WritePhysicalMemory((uint32_t)pa, value);
1057+
}
1058+
1059+
int Dbg_WriteVirtualMemoryDSpace(uint virtualAddress, uint16_t value)
1060+
{
1061+
int pa = Dbg_MapVirtualToPhysical(virtualAddress, true);
1062+
if (pa < 0) return -1;
1063+
return Dbg_WritePhysicalMemory((uint32_t)pa, value);
1064+
}
1065+
9631066
// Check if privileged instruction execution is allowed
9641067

src/debugger/debugger.c

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ static uint16_t get_jpl_target_address(uint16_t pc, uint16_t operand)
568568
static bool is_c_function_prologue(uint16_t pc)
569569
{
570570
// Read the instruction at current PC
571-
uint16_t operand = ReadVirtualMemory(pc, false);
571+
uint16_t operand = Dbg_ReadVirtualMemoryISpace(pc);
572572

573573
// ND-100 C calling convention uses ENTR instruction to enter stack frames
574574
// ENTR opcode: 0140135 (octal)
@@ -591,7 +591,7 @@ static bool is_c_function_prologue(uint16_t pc)
591591
static bool is_c_function_epilogue(uint16_t pc)
592592
{
593593
// Read the instruction at current PC
594-
uint16_t operand = ReadVirtualMemory(pc, false);
594+
uint16_t operand = Dbg_ReadVirtualMemoryISpace(pc);
595595

596596
// LEAVE instruction - Normal return from structured stack frame
597597
// Opcode: 0140136 (octal)
@@ -765,7 +765,7 @@ int step_cpu(DAPServer *server, StepType step_type)
765765
}
766766

767767
// Check if current instruction is a procedure call (JPL)
768-
uint16_t current_operand = ReadVirtualMemory(current_pc, false);
768+
uint16_t current_operand = Dbg_ReadVirtualMemoryISpace(current_pc);
769769
if (is_procedure_call(current_operand)) {
770770
// Resolve JPL target to determine return address.
771771
// C calls via csav: JPL sets L = pc+1 (pointing to .word NNN),
@@ -878,7 +878,7 @@ int step_cpu(DAPServer *server, StepType step_type)
878878
uint16_t jpl_operand = 0;
879879

880880
for (uint16_t addr = current_pc; addr < scan_limit; addr++) {
881-
uint16_t operand = ReadVirtualMemory(addr, false);
881+
uint16_t operand = Dbg_ReadVirtualMemoryISpace(addr);
882882
if (is_procedure_call(operand)) {
883883
jpl_addr = addr;
884884
jpl_operand = operand;
@@ -3894,16 +3894,27 @@ static int cmd_read_memory(DAPServer *server)
38943894

38953895
memset(data, 0, byteCount);
38963896

3897-
// For physical reads we use Dbg_ReadPhysicalMemory which bypasses the
3898-
// MMU, watchpoints, and protection traps - this is what the BSD kernel
3899-
// team needs to reach data above 64K (PPN 64+) on split I/D builds.
3900-
// For virtual reads we use ReadVirtualMemory which honors PT/APT and
3901-
// PTM via the current CPU state.
3897+
// Address space selection:
3898+
// physical - bypass MMU entirely (Dbg_ReadPhysicalMemory)
3899+
// ispace - explicit I-space via PT field of PCR (instruction page table)
3900+
// dspace - explicit D-space via APT field of PCR (data page table)
3901+
// virtual - default: ReadVirtualMemory with UseAPT=false
3902+
bool is_ispace = (server->current_command.context.read_memory.address_space
3903+
== DAP_DATA_BP_ADDR_ISPACE);
3904+
bool is_dspace = (server->current_command.context.read_memory.address_space
3905+
== DAP_DATA_BP_ADDR_DSPACE);
39023906

39033907
for (size_t i = 0; i < byteCount / 2; i++)
39043908
{
3905-
int word = is_physical ? Dbg_ReadPhysicalMemory(address)
3906-
: ReadVirtualMemory(address, false);
3909+
int word;
3910+
if (is_physical)
3911+
word = Dbg_ReadPhysicalMemory(address);
3912+
else if (is_ispace)
3913+
word = Dbg_ReadVirtualMemoryISpace(address);
3914+
else if (is_dspace)
3915+
word = Dbg_ReadVirtualMemoryDSpace(address);
3916+
else
3917+
word = ReadVirtualMemory(address, false);
39073918
if (word == -1)
39083919
{
39093920
server->current_command.context.read_memory.unreadable_bytes = byteCount - i * 2;
@@ -3991,13 +4002,23 @@ static int cmd_write_memory(DAPServer *server)
39914002
int words_written = 0;
39924003
bool is_physical = (server->current_command.context.write_memory.address_space
39934004
== DAP_DATA_BP_ADDR_PHYSICAL);
4005+
bool is_ispace = (server->current_command.context.write_memory.address_space
4006+
== DAP_DATA_BP_ADDR_ISPACE);
4007+
bool is_dspace = (server->current_command.context.write_memory.address_space
4008+
== DAP_DATA_BP_ADDR_DSPACE);
39944009

39954010
/* Write word-by-word (ND-100 is word-addressed, 2 bytes per word) */
39964011
for (int i = 0; i + 1 < byte_count; i += 2) {
39974012
uint16_t word = ((uint16_t)buf[i] << 8) | buf[i + 1];
39984013
if (is_physical) {
39994014
if (Dbg_WritePhysicalMemory(addr, word) < 0)
40004015
break;
4016+
} else if (is_ispace) {
4017+
if (Dbg_WriteVirtualMemoryISpace(addr, word) < 0)
4018+
break;
4019+
} else if (is_dspace) {
4020+
if (Dbg_WriteVirtualMemoryDSpace(addr, word) < 0)
4021+
break;
40014022
} else {
40024023
WriteVirtualMemory(addr, word, false, WRITEMODE_WORD);
40034024
}
@@ -4084,7 +4105,7 @@ static int cmd_disassemble(DAPServer *server)
40844105
else
40854106
{
40864107

4087-
uint16_t operand = ReadVirtualMemory(virtualAddress, false);
4108+
uint16_t operand = Dbg_ReadVirtualMemoryISpace(virtualAddress);
40884109

40894110
// Get the address of the instruction (DAP SPEC says it must be hex)
40904111
char address_str[10];
@@ -4553,7 +4574,7 @@ void debugger_kbd_input(char c)
45534574
for (int i = 0; i < 10; i++)
45544575
{
45554576

4556-
uint16_t operand = ReadVirtualMemory(virtualAddress, false);
4577+
uint16_t operand = Dbg_ReadVirtualMemoryISpace(virtualAddress);
45574578

45584579
// Get the address of the instruction (DAP SPEC says it must be hex)
45594580
printf("[%06o] ", virtualAddress);

0 commit comments

Comments
 (0)