B-Em Debugging/Tracing

want to talk about MESS/model b/beebem/b-em/electrem/elkulator? do it here!
User avatar
fordp
Posts: 923
Joined: Sun Feb 12, 2012 9:08 pm
Location: Kent, England

Re: B-Em Debugging/Tracing

Postby fordp » Mon Mar 27, 2017 7:18 am

I suggest even on those processors with a single memory space using the io calls to access hardware registers (such as the tube chip on the CoPro's). The Memory ones can then access only memory and can be guaranteed to not have side effects? I know reading addresses that are actually IO will read junk but this is way better than having side effects.
FordP (Simon Ellwood)
Time is an illusion. Lunchtime, doubly so!

Coeus
Posts: 474
Joined: Mon Jul 25, 2016 11:05 am

Re: B-Em Debugging/Tracing

Postby Coeus » Thu Mar 30, 2017 12:09 am

hoglet wrote:I've done both of those now for PiTubeDirect - feel free to leverage what you can:
https://github.com/hoglet67/PiTubeDirec ... 86_debug.c
https://github.com/hoglet67/PiTubeDirec ... rm_debug.c


Dave, I have now mostly done ARM too - your work was very helpful. I have pushed the latest B-Em implementation to https://github.com/stardot/b-em/tree/sf/debugging

What I have not quite finished is a getting/setting a couple of the registers. The ARM core in B-Em has the registers stored in various arrays but I don't see which slots would correspond with SP_svc and LR_svc. If you have a spare moment would you mind looking at the B-Em arm.c and see if you can spot where these are stored, please?

Returning to the question of X86 segmented addressing I think if we were to have the CPU call the debugging functions with a 32bit value which is actually two 16 bit registers side by side rather than the 24bit effective address it might cause breakpoints/watchpoints to be missed as the debugger stores the 32 bit value as an opaque value in the array of breakpoints and simply checks if the address the CPU claims to be accessing matches an entry in the table. With the two registers together in one 32 bit word the breakpoint would only take effect when these matched how the breakpoint was specified, not the other equivalent ways of representing the same address.

User avatar
hoglet
Posts: 6629
Joined: Sat Oct 13, 2012 6:21 pm
Location: Bristol

Re: B-Em Debugging/Tracing

Postby hoglet » Thu Mar 30, 2017 7:15 am

Coeus wrote:What I have not quite finished is a getting/setting a couple of the registers. The ARM core in B-Em has the registers stored in various arrays but I don't see which slots would correspond with SP_svc and LR_svc. If you have a spare moment would you mind looking at the B-Em arm.c and see if you can spot where these are stored, please?

It looks like it is copying registers on mode change, which makes it slightly more complicated.

So the relevant code is here:
https://github.com/stardot/b-em/blob/ma ... /arm.c#L67

It looks like you'll need to check the mode bits in the ARM CPSR and do something like:

Code: Select all

if (mode == SUPERVISOR) {
   SP_svc = armregs[13];
   LR_svc = armregs[14];
} else {
   SP_svc = superregs[0];
   LR_svc = superregs[1];
}

You probably need this pattern for all the registers.

Coeus wrote:Returning to the question of X86 segmented addressing I think if we were to have the CPU call the debugging functions with a 32bit value which is actually two 16 bit registers side by side rather than the 24bit effective address it might cause breakpoints/watchpoints to be missed as the debugger stores the 32 bit value as an opaque value in the array of breakpoints and simply checks if the address the CPU claims to be accessing matches an entry in the table. With the two registers together in one 32 bit word the breakpoint would only take effect when these matched how the breakpoint was specified, not the other equivalent ways of representing the same address.

I agree this would be a problem.

The only way I can think of solving is if cpu_debug_t implemented a function like:
- unit32_t logical_to_physical(uint32_t addr);

In the case of the 80x86 this would translate 32 bit addresses down to 20 bit addresses. The breakpoint testing would always work against physical addresses. This might be quite expensive (performance wise) to add.

Or possibly we could just cheat for the 80x86, at least for the tracing/stepping, as we know the value in the cs register was used to form the code segment base.

Dave

Coeus
Posts: 474
Joined: Mon Jul 25, 2016 11:05 am

Re: B-Em Debugging/Tracing

Postby Coeus » Thu Mar 30, 2017 8:35 pm

hoglet wrote:It looks like it is copying registers on mode change, which makes it slightly more complicated.

So the relevant code is here:
https://github.com/stardot/b-em/blob/ma ... /arm.c#L67

It looks like you'll need to check the mode bits in the ARM CPSR and do something like:

Code: Select all

if (mode == SUPERVISOR) {
   SP_svc = armregs[13];
   LR_svc = armregs[14];
} else {
   SP_svc = superregs[0];
   LR_svc = superregs[1];
}

You probably need this pattern for all the registers.]


So reading the code in the function to switch mode it looks to me like it maintains usrregs (as opposed to userregs) as an array of pointers to the elements of one of the other arrays to indicate where the current user-mode register values are stored. Using that I came up with the following:

Code: Select all

static uint32_t arm_dbg_reg_get(int which) {
    if (which <= i_LR) {
        return *(usrregs[which]);
    } else if (which == i_PC) {
        return PC;
    } else if (which == i_PSR) {
        return ((armregs[i_PC] >> 24) & 0xFC) | (armregs[i_PC] & 0x03);
    } else if (which <= i_LR_fiq) {
        if (mode == FIQ)
            return armregs[8+(which-i_R8_fiq)];
        else
            return fiqregs[8+(which-i_R8_fiq)];
    } else if (which <= i_LR_irq) {
        if (mode == IRQ)
            return armregs[13+(which-i_LR_irq)];
        else
            return fiqregs[13+(which-i_LR_irq)];
    } else if (which <= i_LR_svc) {
        if (mode == SUPERVISOR)
            return armregs[13+(which-i_LR_svc)];
        else
            return superregs[13+(which-i_LR_svc)];
    } else {
        log_warn("arm: unrecognised register %d", which);
        return 0;
    }
};


The set version is very similar. With these banked registers perhaps I should go back to the Z80 and include the alternate banks there too.

hoglet wrote:The only way I can think of solving is if cpu_debug_t implemented a function like:
- unit32_t logical_to_physical(uint32_t addr);

In the case of the 80x86 this would translate 32 bit addresses down to 20 bit addresses. The breakpoint testing would always work against physical addresses. This might be quite expensive (performance wise) to add.


It would work, though dependent on whether we pre-normalise the values stored as breakpoints it would mean 2 or 3 time the number of function calls per memory access and still doesn't print addresses in the usual segmented form.

Another possibility is to add address parse/print functions. Most processors would use a standard printf/scanf but the 80186 could accept segmented addresses and convert them into physical representation. On printing it could check the CS, DS, SS registers and choose the segmented version of the phyiaddress that has the segment matching one of those registers, else a canonical form.

hoglet wrote:Or possibly we could just cheat for the 80x86, at least for the tracing/stepping, as we know the value in the cs register was used to form the code segment base.


I am not sure I understand that.

In the latest update I also introduced a -debugtube option in place or the CPU menu so it will debug whatever is the current tube processor. Interstingly, if you turn on -debug and -debugtube it will debug both at once. It could get interesting as there is only one of each kind of table, but if you trace to file, for example, it will show the tube interaction.

User avatar
hoglet
Posts: 6629
Joined: Sat Oct 13, 2012 6:21 pm
Location: Bristol

Re: B-Em Debugging/Tracing

Postby hoglet » Thu Mar 30, 2017 9:18 pm

Coeus wrote:So reading the code in the function to switch mode it looks to me like it maintains usrregs (as opposed to userregs) as an array of pointers to the elements of one of the other arrays to indicate where the current user-mode register values are stored.

Indeed, but the only use of this seems to be to support the "S" option of the STM and LDM instruction.

Your arm_dbg_reg_get looks correct to me.

Coeus wrote:
hoglet wrote:Or possibly we could just cheat for the 80x86, at least for the tracing/stepping, as we know the value in the cs register was used to form the code segment base.


I am not sure I understand that.

In the 80x86 the Physical memory address is generated as:
- (segment << 4) + offset

The problem we currently have is reversing this, so the traces etc could display addresses as:
<segment>:<offset>

It's my understanding that for instructions the relevant segment register is always <cs>

As the disassembler is running in the core, it could just lookup the cs register:

Code: Select all

static uint32_t dbg_disassemble(uint32_t addr, char *buf, size_t bufsize) {
   char instr[100];
   int oplen = i386_dasm_one(instr, addr, 0, 0) & 0xffff;
   int len;
   int segment = getsegreg(regcs);
   int offset = addr - (segment << 4);
   if (offset >= 0 && offset <= 0xffff) {
      len = snprintf(buf, bufsize, "%04x:%04x ", segment, offset);
   } else {
      len = snprintf(buf, bufsize, "%06"PRIx32" ", addr);
   }


This does sort of work:

Code: Select all

06bb:0768 ec                in al, dx
06bb:0769 a8 80             test al, $80
06bb:076b 79 02             jns [$0000731F]
06bb:076f e8 74 07          call [$00007A96]
06bb:0ee6 e4 8c             in al, $8C
06bb:0ee8 84 c0             test al, al
06bb:0eea 78 09             js [$00007AA5]

But the problem is that i386_dasm_one() just uses with physical addresses, hence the address in the call instruction not matching up (06bb << 4 + 0ee6 == 7a96)

I'm inclined to just stick with physical addresses.

Dave


Return to “emulators”

Who is online

Users browsing this forum: No registered users and 1 guest