stardot / b-em

An opensource BBC Micro emulator for Win32 and Linux
http://stardot.org.uk/forums/viewtopic.php?f=4&t=10823
GNU General Public License v2.0
119 stars 58 forks source link

6502.c's dbg_do_readmem() doesn't handle Integra-B PRVEN bit correctly #197

Open ZornsLemma opened 1 year ago

ZornsLemma commented 1 year ago

TL;DR: I think the constant 0x7f in 6502.c's dbg_do_readmem() should be changed to 0x3f.

On the Integra-B, bit 6 of ROMSEL (&FE30) plays a similar role to bit 7 of ROMSEL on the B+ with regard to paging in the 12K private RAM at &8000-&AFFF. (It's also necessary for at least one of PRVS1/4/8 to be set in RAMSEL (&FE34), but I don't think that's too important here. The details are in section 8-2 of the Integra-B user guide; let me know if you don't already have a copy of that...)

In 6502.c's dbg_do_readmem(), we mask off the high bit of ROMSEL when deciding if the ROM we're reading from is the currently paged in bank, and if it is we use the fancier do_readmem() function which takes account of memlook/memstat. I assume the reason for doing this is so that on a B+ we will show what the CPU actually sees, whether that's the private RAM or the actual underlying ROM/RAM bank.

I think we should mask off bit 6 as well, to get the same effect on the Integra-B. My gut feeling is that it's safe to just always mask off bits 6 and 7 regardless of what machine we're emulating, but I am not 100% sure.

I noticed this while trying to debug a problem on the Integra-B where b6 of ROMSEL is being set while an unexpected combination of PRVS1/4/8 is set in RAMSEL, so the code being executed suddenly disappears from under the CPU and it starts to execute junk. The debugger still shows the "correct" code, but it's obvious from the behaviour that it isn't actually executing that.

You can see this with current master (a4a80919729188f3da7c2baeef1a9de217e24aa7) by starting up the emulator in Integra-B mode, setting a breakpoint at f:bdfd, running the BASIC TEST1 program on the attached zipped ssd and pressing Escape when indicated. Here's a debug session with current master:

steven@ubuntu:~/src/b-em$ ./b-em -debug
D9CD: A9 40       LDA #40     >break f:bdfd
    Breakpoint 0 at F:BDFD set
>d f:bdfd
    F:BDFD: AD 7F 03    LDA 037F    
    F:BE00: 48          PHA         
    F:BE01: 20 55 90    JSR 9055    
    F:BE04: BA          TSX         
    F:BE05: BD 07 01    LDA 0107,X  
    F:BE08: 29 40       AND #40     
    F:BE0A: F0 0D       BEQ BE19    
    F:BE0C: A2 47       LDX #47     
    F:BE0E: 20 70 88    JSR 8870    
    F:BE11: F0 03       BEQ BE16    
    F:BE13: 20 90 BF    JSR BF90    
    F:BE16: 4C DD BD    JMP BDDD    
>c
cpu core6502: BRK at E:9838
E:9838: 00          BRK         >c
cpu core6502: Break at F:BDFD
F:BDFD: AD 7F 03    LDA 037F    >s
F:BE00: 48          PHA         >s
F:BE01: 20 55 90    JSR 9055    >s
F:9055: 48          PHA         >s
F:9056: AD 7F 03    LDA 037F    >s
F:9059: 09 40       ORA #40     >s
F:905B: 8D 7F 03    STA 037F    >s
F:905E: 8D 34 FE    STA FE34    >s
F:9061: A5 F4       LDA F4      >s
F:9063: 09 40       ORA #40     >s
F:9065: 85 F4       STA F4      >s
F:9067: 8D 30 FE    STA FE30    >mb 9060
F:9060 : FE A5 F4 09 40 85 F4 8D 30 FE 68 60 48 A5 F4 29   ....@...0.h`H..)
>s
F:906A: 68          PLA         >s
cpu core6502: BRK at F:906A
F:906A: 68          PLA         >mb 9060
F:9060 : FE A5 F4 09 40 85 F4 8D 30 FE 68 60 48 A5 F4 29   ....@...0.h`H..)

Note that we see a "BRK" at F:906A, caused by the preceding STA FE30 setting b6 of ROMSEL and causing the private RAM to be paged in over the code we're trying to execute. However, the disassembly and the memory dump don't show this, showing "PLA" instead.

If I change 0x7f in dbg_do_readmem() to 0x3f, rebuild and repeat that, I get the following (skipping the first part of the debug session, which is identical to that above):

F:905E: 8D 34 FE    STA FE34    >s
F:9061: A5 F4       LDA F4      >s
F:9063: 09 40       ORA #40     >s
F:9065: 85 F4       STA F4      >mb 9060
F:9060 : FE A5 F4 09 40 85 F4 8D 30 FE 68 60 48 A5 F4 29   ....@...0.h`H..)
>s
F:9067: 8D 30 FE    STA FE30    >mb 9060
F:9060 : FE A5 F4 09 40 85 F4 8D 30 FE 68 60 48 A5 F4 29   ....@...0.h`H..)
>s
F:906A: 00          BRK         >mb 9060
F:9060 : F8 80 00 00 21 A6 E1 45 41 E8 00 21 AA A2 05 41   ....!..EA..!...A

Note that after the STA FE30 executes, the debugger shows a BRK at F:906A and the mb output also changes to show this. I think this reflects what's really happening.

test1.zip

ZornsLemma commented 1 year ago

With this fix (or, presumably, without it on a B+ when doing similar kinds of things with the private RAM), would there be any way to force "m"/"mb" to show the actual contents of the ROM in bank F without taking the CPU's perspective into account? In other words, forcibly avoid the use of do_readmem() inside dbg_do_readmem() in order to just see the contents of the ROM, even though it's the currently paged in ROM?

SteveFosdick commented 1 year ago

I have pushed the change to 0x3f. I agree this is safe even on a non-integra machine.

On seeing the actual ROM, I think the limitation here is that the debugger tries to able to work with multiple CPUs including all the tube processors but assumes that whatever addressing quirks each CPU has can be represented by a 32-bit address. So we have a scheme for encoding the ROM number into the 32-bit address but we have only used for four bits for this which doesn't have a code for "unspecified" which would mean we could have addresses that don't specify a ROM number working from the CPU perspective while if a ROM number is specified we would just show the ROM concerned.

We could add another bit. This will mean revisiting the function to parse an address into the 32-bit form and also think about the way breakpoints are specified. If we specify rom D for example on a breakpoint do we intend it to only fire is actually executing from that ROM or from whatever if is in that address space including overlaid RAM if 'D" has been written to ROMSEL?

ZornsLemma commented 1 year ago

Thanks for pushing the 0x3f change!

I hadn't thought about all the complexities of being able to see the actual ROM. It would actually be quite cool if you could set a breakpoint which fires: 1) only when code is executed/data is read/written from a specific ROM, or 2) only when code is execute/data is read/written from a 16-bit address (CPU perspective) when that ROM is paged in

I don't know if there's another variant I haven't thought of. If it is just these two, my inclination would be that setting a breakpoint on D:xxxx should give behaviour 2, since that's the current behaviour people will have come to expect, and setting a breakpoint on 1D:xxxx should give behaviour 1, as that's new functionality so has a new syntax.

Is it easy to add a new bit (except for deciding how it should work), or is there a lot of work required?