From 727a1c0f2acd088b15cb73cba9786555ee63be55 Mon Sep 17 00:00:00 2001 From: David Banks Date: Tue, 30 Jun 2015 14:19:19 +0100 Subject: [PATCH] Implemented IO watches/breakpoints; fixed a bug with stepping through CB/DD/ED/FD prefixed opcodes which have 2 M1 cycles; version not 0.47 Change-Id: Iad5cb406bd96a8020ccb65be5cd440bebec20481 --- firmware/AtomBusMon.c | 180 +++++++++++++++++++++++++++--------------- src/BusMonCore.vhd | 147 ++++++++++++++++------------------ src/Z80CpuMon.vhd | 52 +++++++++--- 3 files changed, 220 insertions(+), 159 deletions(-) diff --git a/firmware/AtomBusMon.c b/firmware/AtomBusMon.c index 4ca58e0..8459d2e 100644 --- a/firmware/AtomBusMon.c +++ b/firmware/AtomBusMon.c @@ -6,7 +6,7 @@ #include "AtomBusMon.h" -#define VERSION "0.46" +#define VERSION "0.47" #if (CPU == Z80) #define NAME "ICE-T80" @@ -16,7 +16,7 @@ #ifdef CPUEMBEDDED #if (CPU == Z80) - #define NUM_CMDS 24 + #define NUM_CMDS 28 #else #define NUM_CMDS 22 #endif @@ -136,19 +136,49 @@ #define BW_ACTIVE_MASK 0x80 // Breakpoint Modes -#define BRKPT_EXEC 0 -#define BRKPT_READ 1 -#define BRKPT_WRITE 2 -#define WATCH_EXEC 3 -#define WATCH_READ 4 -#define WATCH_WRITE 5 -#define UNDEFINED 6 +#define BRKPT_MEM_READ 0 +#define WATCH_MEM_READ 1 +#define BRKPT_MEM_WRITE 2 +#define WATCH_MEM_WRITE 3 +#define BRKPT_IO_READ 4 +#define WATCH_IO_READ 5 +#define BRKPT_IO_WRITE 6 +#define WATCH_IO_WRITE 7 +#define BRKPT_EXEC 8 +#define WATCH_EXEC 9 +#define UNDEFINED 10 -#define B_MASK ((1<>= 1; @@ -420,21 +445,19 @@ int logDetails() { unsigned int b_addr = hwRead16(OFFSET_BW_BAL); unsigned int b_data = hwRead8(OFFSET_BW_BD); unsigned int mode = hwRead8(OFFSET_BW_M); - unsigned int watch = mode & 8; + unsigned int watch = mode & 1; + // Convert from 4-bit compressed to 10 bit expanded mode representation + mode = 1 << mode; - // Convert from 4-bit compressed to 6 bit expanded mode representation - if (watch) { - mode = (mode & 7) << 3; - } // Update the serial console if (mode & W_MASK) { logCycleCount(OFFSET_BW_CNTL, OFFSET_BW_CNTH); } logMode(mode); log0(" hit at %04X", i_addr); - if (mode & BW_MEM_MASK) { - if (mode & W_MEM_MASK) { + if (mode & BW_RDWR_MASK) { + if (mode & BW_WR_MASK) { log0(" writing"); } else { log0(" reading"); @@ -444,7 +467,7 @@ int logDetails() { log0("\n"); } #ifdef CPUEMBEDDED - if (mode & B_MEM_MASK) { + if (mode & B_RDWR_MASK) { // It's only safe to do this for brkpts, as it makes memory accesses logCycleCount(OFFSET_BW_CNTL, OFFSET_BW_CNTH); disMem(i_addr); @@ -846,7 +869,7 @@ void doCmdTrace(char *params) { setTrace(i); } -void doCmdBList(char *params) { +void doCmdList(char *params) { int i; if (numbkpts) { for (i = 0; i < numbkpts; i++) { @@ -913,30 +936,51 @@ void doCmdBreak(char *params, unsigned int mode) { } } + void doCmdBreakI(char *params) { doCmdBreak(params, 1 << BRKPT_EXEC); } -void doCmdBreakR(char *params) { - doCmdBreak(params, 1 << BRKPT_READ); -} - -void doCmdBreakW(char *params) { - doCmdBreak(params, 1 << BRKPT_WRITE); -} - void doCmdWatchI(char *params) { doCmdBreak(params, 1 << WATCH_EXEC); } -void doCmdWatchR(char *params) { - doCmdBreak(params, 1 << WATCH_READ); +void doCmdBreakRdMem(char *params) { + doCmdBreak(params, 1 << BRKPT_MEM_READ); } -void doCmdWatchW(char *params) { - doCmdBreak(params, 1 << WATCH_WRITE); +void doCmdWatchRdMem(char *params) { + doCmdBreak(params, 1 << WATCH_MEM_READ); } +void doCmdBreakWrMem(char *params) { + doCmdBreak(params, 1 << BRKPT_MEM_WRITE); +} + +void doCmdWatchWrMem(char *params) { + doCmdBreak(params, 1 << WATCH_MEM_WRITE); +} + +#if (CPU == Z80) + +void doCmdBreakRdIO(char *params) { + doCmdBreak(params, 1 << BRKPT_IO_READ); +} + +void doCmdWatchRdIO(char *params) { + doCmdBreak(params, 1 << WATCH_IO_READ); +} + +void doCmdBreakWrIO(char *params) { + doCmdBreak(params, 1 << BRKPT_IO_WRITE); +} + +void doCmdWatchWrIO(char *params) { + doCmdBreak(params, 1 << WATCH_IO_WRITE); +} + +#endif + void doCmdClear(char *params) { int i; int n = lookupBreakpoint(params); @@ -983,7 +1027,7 @@ void shiftBreakpointRegister(unsigned int addr, unsigned int mask, unsigned int hwCmd(CMD_LOAD_BRKPT, mask & 1); mask >>= 1; } - for (i = 0; i <= 5; i++) { + for (i = 0; i <= 9; i++) { hwCmd(CMD_LOAD_BRKPT, mode & 1); mode >>= 1; } @@ -1106,13 +1150,19 @@ void (*cmdFuncs[NUM_CMDS])(char *params) = { doCmdReset, doCmdStep, doCmdTrace, - doCmdBList, + doCmdList, doCmdBreakI, - doCmdBreakR, - doCmdBreakW, doCmdWatchI, - doCmdWatchR, - doCmdWatchW, + doCmdBreakRdMem, + doCmdWatchRdMem, + doCmdBreakWrMem, + doCmdWatchWrMem, +#if (CPU == Z80) + doCmdBreakRdIO, + doCmdWatchRdIO, + doCmdBreakWrIO, + doCmdWatchWrIO, +#endif doCmdClear, doCmdTrigger }; diff --git a/src/BusMonCore.vhd b/src/BusMonCore.vhd index f0162c3..6c2ab14 100644 --- a/src/BusMonCore.vhd +++ b/src/BusMonCore.vhd @@ -25,18 +25,20 @@ use work.OhoPack.all ; entity BusMonCore is generic ( num_comparators : integer := 8; - reg_width : integer := 42; + reg_width : integer := 46; fifo_width : integer := 72 ); port ( clock49 : in std_logic; - -- 6502 Signals + -- CPU Signals Addr : in std_logic_vector(15 downto 0); Data : in std_logic_vector(7 downto 0); Phi2 : in std_logic; Rd_n : in std_logic; Wr_n : in std_logic; + RdIO_n : in std_logic; + WrIO_n : in std_logic; Sync : in std_logic; Rdy : out std_logic; nRSTin : in std_logic; @@ -44,11 +46,11 @@ entity BusMonCore is CountCycle : in std_logic; - -- 6502 Registers + -- CPU Registers -- unused in pure bus monitor mode Regs : in std_logic_vector(255 downto 0); - -- 6502 Memory Read/Write + -- CPU Memory Read/Write -- unused in pure bus monitor mode RdMemOut : out std_logic; WrMemOut : out std_logic; @@ -296,52 +298,22 @@ begin Regs(8 * to_integer(unsigned(muxsel(4 downto 0))) + 7 downto 8 * to_integer(unsigned(muxsel(4 downto 0)))); --- Regs( 15 downto 8) when muxsel = 33 else --- Regs( 23 downto 16) when muxsel = 34 else --- Regs( 31 downto 24) when muxsel = 35 else --- Regs( 39 downto 32) when muxsel = 36 else --- Regs( 47 downto 40) when muxsel = 37 else --- Regs( 55 downto 48) when muxsel = 38 else --- Regs( 63 downto 56) when muxsel = 39 else --- Regs( 7 downto 64) when muxsel = 40 else --- Regs( 15 downto 72) when muxsel = 41 else --- Regs( 23 downto 80) when muxsel = 42 else --- Regs( 31 downto 88) when muxsel = 43 else --- Regs( 39 downto 96) when muxsel = 44 else --- Regs( 47 downto 104) when muxsel = 45 else --- Regs( 55 downto 112) when muxsel = 46 else --- Regs( 63 downto 120) when muxsel = 47 else --- Regs( 7 downto 128) when muxsel = 48 else --- Regs( 15 downto 136) when muxsel = 49 else --- Regs( 23 downto 144) when muxsel = 50 else --- Regs( 31 downto 24) when muxsel = 51 else --- Regs( 39 downto 32) when muxsel = 52 else --- Regs( 47 downto 40) when muxsel = 53 else --- Regs( 55 downto 48) when muxsel = 54 else --- Regs( 63 downto 56) when muxsel = 55 else --- Regs( 7 downto 0) when muxsel = 56 else --- Regs( 15 downto 8) when muxsel = 57 else --- Regs( 23 downto 16) when muxsel = 58 else --- Regs( 31 downto 24) when muxsel = 59 else --- Regs( 39 downto 32) when muxsel = 60 else --- Regs( 47 downto 40) when muxsel = 61 else --- Regs( 55 downto 48) when muxsel = 62 else --- Regs( 63 downto 56) when muxsel = 63 else --- --- --- "10101010"; - -- Combinatorial set of comparators to decode breakpoint/watch addresses - brkpt_active_process: process (brkpt_reg, brkpt_enable, Addr, Sync) + brkpt_active_process: process (brkpt_reg, brkpt_enable, Addr, Sync, Rd_n, Wr_n, RdIO_n, WrIO_n) variable i : integer; variable reg_addr : std_logic_vector(15 downto 0); variable reg_mask : std_logic_vector(15 downto 0); - variable reg_mode_bi : std_logic; - variable reg_mode_bar : std_logic; - variable reg_mode_baw : std_logic; - variable reg_mode_wi : std_logic; - variable reg_mode_war : std_logic; - variable reg_mode_waw : std_logic; + variable reg_mode_bmr : std_logic; + variable reg_mode_bmw : std_logic; + variable reg_mode_bir : std_logic; + variable reg_mode_biw : std_logic; + variable reg_mode_bx : std_logic; + variable reg_mode_wmr : std_logic; + variable reg_mode_wmw : std_logic; + variable reg_mode_wir : std_logic; + variable reg_mode_wiw : std_logic; + variable reg_mode_wx : std_logic; + variable reg_mode_all : std_logic_vector(9 downto 0); variable bactive : std_logic; variable wactive : std_logic; variable status : std_logic_vector(3 downto 0); @@ -354,47 +326,60 @@ begin for i in 0 to num_comparators - 1 loop reg_addr := brkpt_reg(i * reg_width + 15 downto i * reg_width); reg_mask := brkpt_reg(i * reg_width + 31 downto i * reg_width + 16); - reg_mode_bi := brkpt_reg(i * reg_width + 32); - reg_mode_bar := brkpt_reg(i * reg_width + 33); - reg_mode_baw := brkpt_reg(i * reg_width + 34); - reg_mode_wi := brkpt_reg(i * reg_width + 35); - reg_mode_war := brkpt_reg(i * reg_width + 36); - reg_mode_waw := brkpt_reg(i * reg_width + 37); - trigval := brkpt_reg(i * reg_width + 38 + to_integer(unsigned(trig))); - if (trigval = '1' and ((Addr and reg_mask) = reg_addr or - (reg_mode_bi = '0' and reg_mode_bar = '0' and reg_mode_baw = '0' and - (reg_mode_wi = '0' and reg_mode_war = '0' and reg_mode_waw = '0')))) then + reg_mode_bmr := brkpt_reg(i * reg_width + 32); + reg_mode_wmr := brkpt_reg(i * reg_width + 33); + reg_mode_bmw := brkpt_reg(i * reg_width + 34); + reg_mode_wmw := brkpt_reg(i * reg_width + 35); + reg_mode_bir := brkpt_reg(i * reg_width + 36); + reg_mode_wir := brkpt_reg(i * reg_width + 37); + reg_mode_biw := brkpt_reg(i * reg_width + 38); + reg_mode_wiw := brkpt_reg(i * reg_width + 39); + reg_mode_bx := brkpt_reg(i * reg_width + 40); + reg_mode_wx := brkpt_reg(i * reg_width + 41); + reg_mode_all := brkpt_reg(i * reg_width + 41 downto i * reg_width + 32); + trigval := brkpt_reg(i * reg_width + 42 + to_integer(unsigned(trig))); + if (trigval = '1' and ((Addr and reg_mask) = reg_addr or (reg_mode_all = "0000000000"))) then if (Sync = '1') then - if (reg_mode_bi = '1') then + if (reg_mode_bx = '1') then bactive := '1'; - status := "0001"; - end if; - if (reg_mode_wi = '1') then + status := "1000"; + elsif (reg_mode_wx = '1') then wactive := '1'; status := "1001"; end if; - else - if (Rd_n = '0') then - if (reg_mode_bar = '1') then - bactive := '1'; - status := "0010"; - end if; - if (reg_mode_war = '1') then - wactive := '1'; - status := "1010"; - end if; + elsif (Rd_n = '0') then + if (reg_mode_bmr = '1') then + bactive := '1'; + status := "0000"; + elsif (reg_mode_wmr = '1') then + wactive := '1'; + status := "0001"; end if; - if (Wr_n = '0') then - if (reg_mode_baw = '1') then - bactive := '1'; - status := "0100"; - end if; - if (reg_mode_waw = '1') then - wactive := '1'; - status := "1100"; - end if; + elsif (Wr_n = '0') then + if (reg_mode_bmw = '1') then + bactive := '1'; + status := "0010"; + elsif (reg_mode_wmw = '1') then + wactive := '1'; + status := "0011"; end if; - end if; + elsif (RdIO_n = '0') then + if (reg_mode_bir = '1') then + bactive := '1'; + status := "0100"; + elsif (reg_mode_wir = '1') then + wactive := '1'; + status := "0101"; + end if; + elsif (WrIO_n = '0') then + if (reg_mode_biw = '1') then + bactive := '1'; + status := "0110"; + elsif (reg_mode_wiw = '1') then + wactive := '1'; + status := "0111"; + end if; + end if; end if; end loop; end if; @@ -515,7 +500,7 @@ begin Rdy_int <= (not Sync); end if; - -- 6502 Reset needs to be open collector + -- CPU Reset needs to be open collector if (reset = '1') then nRSTout <= '0'; else diff --git a/src/Z80CpuMon.vhd b/src/Z80CpuMon.vhd index 7ae4b3c..f910c72 100644 --- a/src/Z80CpuMon.vhd +++ b/src/Z80CpuMon.vhd @@ -98,6 +98,7 @@ signal SS_Single : std_logic; signal SS_Step : std_logic; signal SS_Step_held : std_logic; signal CountCycle : std_logic; +signal skipNextOpcode : std_logic; signal Regs : std_logic_vector(255 downto 0); signal io_not_mem : std_logic; @@ -119,8 +120,14 @@ signal Read_n0 : std_logic; signal Read_n1 : std_logic; signal Write_n : std_logic; signal Write_n0 : std_logic; +signal ReadIO_n : std_logic; +signal ReadIO_n0 : std_logic; +signal ReadIO_n1 : std_logic; +signal WriteIO_n : std_logic; +signal WriteIO_n0 : std_logic; signal Sync : std_logic; signal Sync0 : std_logic; +signal Mem_IO_n : std_logic; signal nRST : std_logic; signal MemState : std_logic_vector(2 downto 0); @@ -145,6 +152,8 @@ begin Phi2 => busmon_clk, Rd_n => Read_n, Wr_n => Write_n, + RdIO_n => ReadIO_n, + WrIO_n => WriteIO_n, Sync => Sync, Rdy => Rdy, nRSTin => RESET_n_int, @@ -217,7 +226,7 @@ begin elsif rising_edge(CLK_n) then NMI_n_sync <= NMI_n; INT_n_sync <= INT_n; - if (M1_n_int = '0' and TState = "001") then + if (Sync0 = '1') then -- stop at the end of T1 instruction fetch SS_Step_held <= '0'; elsif (SS_Step = '1') then @@ -227,19 +236,33 @@ begin end if; end process; + + -- Logic to ignore the second M1 in multi-byte opcodes + skip_opcode_latch : process(CLK_n) + begin + if rising_edge(CLK_n) then + if (M1_n_int = '0' and WAIT_n_int = '1' and TState = "010") then + if (skipNextOpcode = '0' and (Data = x"CB" or Data = x"DD" or Data = x"ED" or Data = x"FD")) then + skipNextOpcode <= '1'; + else + skipNextOpcode <= '0'; + end if; + end if; + end if; + end process; + -- For instruction breakpoints, we make the monitoring decision as early as possibe -- to allow time to stop the current instruction, which is possible because we don't -- really care about the data (it's re-read from memory by the disassembler). - Sync0 <= not M1_n_int when TState = "001" else '0'; + Sync0 <= '1' when M1_n_int = '0' and TState = "001" and skipNextOpcode = '0' else '0'; - -- For reads/write breakpoints we make the monitoring decision in the middle of T2 + -- For memory reads/write breakpoints we make the monitoring decision in the middle of T2 -- but only if WAIT_n is '1' so we catch the right data. Read_n0 <= not (WAIT_n_int and (not RD_n_int) and (not MREQ_n_int) and (M1_n_int)) when TState = "010" else '1'; Write_n0 <= not (WAIT_n_int and (not WR_n_int) and (not MREQ_n_int) and (M1_n_int)) when TState = "010" else '1'; - -- These are useful for debugging IO Requests: - -- Read_n0 <= not (WAIT_n_int and (not RD_n_int) and (not IORQ_n_int) and (M1_n_int)) when TState = "010" else '1'; - -- Write_n0 <= not ( ( RD_n_int) and (not IORQ_n_int) and (M1_n_int)) when TState = "011" else '1'; + ReadIO_n0 <= not (WAIT_n_int and (not RD_n_int) and (not IORQ_n_int) and (M1_n_int)) when TState = "010" else '1'; + WriteIO_n0 <= not ( ( RD_n_int) and (not IORQ_n_int) and (M1_n_int)) when TState = "011" else '1'; -- Hold the monitoring decision so it is valid on the rising edge of the clock -- For instruction fetches and writes, the monitor sees these at the start of T3 @@ -247,10 +270,13 @@ begin watch_gen : process(CLK_n) begin if falling_edge(CLK_n) then - Sync <= Sync0; - Read_n1 <= Read_n0; - Read_n <= Read_n1; - Write_n <= Write_n0; + Sync <= Sync0; + Read_n1 <= Read_n0; + Read_n <= Read_n1; + Write_n <= Write_n0; + ReadIO_n1 <= ReadIO_n0; + ReadIO_n <= ReadIO_n1; + WriteIO_n <= WriteIO_n0; end if; end process; @@ -258,7 +284,7 @@ begin ex_data_latch : process(CLK_n) begin if rising_edge(CLK_n) then - if (Sync = '1' or Write_n = '0') then + if (Sync = '1' or Write_n = '0' or WriteIO_n = '0') then ex_data <= Data; end if; end if; @@ -268,7 +294,7 @@ begin rd_data_latch : process(CLK_n) begin if falling_edge(CLK_n) then - if (Read_n1 = '0') then + if (Read_n1 = '0' or ReadIO_n1 = '0') then rd_data <= Data; end if; memory_din <= Data; @@ -276,7 +302,7 @@ begin end process; -- Mux the data seen by the bus monitor appropriately - mon_data <= rd_data when Read_n <= '0' else ex_data; + mon_data <= rd_data when Read_n <= '0' or ReadIO_n = '0' else ex_data; -- Memory access Addr <= memory_addr when (state /= idle) else Addr_int;