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
This commit is contained in:
David Banks 2015-06-30 14:19:19 +01:00
parent caec07483d
commit 727a1c0f2a
3 changed files with 220 additions and 159 deletions

View File

@ -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<<BRKPT_READ) | (1<<BRKPT_WRITE) | (1<<BRKPT_EXEC))
#define W_MASK ((1<<WATCH_READ) | (1<<WATCH_WRITE) | (1<<WATCH_EXEC))
#define B_MEM_MASK ((1<<BRKPT_READ) | (1<<BRKPT_WRITE))
#define W_MEM_MASK ((1<<BRKPT_WRITE) | (1<<WATCH_WRITE))
#define BW_MEM_MASK ((1<<BRKPT_READ) | (1<<BRKPT_WRITE) | (1<<WATCH_READ) | (1<<WATCH_WRITE))
// Breakpoint Mode Strings, should match the modes
char *modeStrings[10] = {
"Mem Rd Brkpt",
"Mem Rd Watch",
"Mem Wr Brkpt",
"Mem Wr Watch",
"IO Rd Brkpt",
"IO Rd Watch",
"IO Wr Brkpt",
"IO Wr Watch",
"Ex Brkpt",
"Ex Watch"
};
// Mask for all breakpoint types
#define B_MASK ((1<<BRKPT_MEM_READ) | (1<<BRKPT_MEM_WRITE) | (1<<BRKPT_IO_READ) | (1<<BRKPT_IO_WRITE) | (1<<BRKPT_EXEC))
// Mask for all watch types
#define W_MASK ((1<<WATCH_MEM_READ) | (1<<WATCH_MEM_WRITE) | (1<<WATCH_IO_READ) | (1<<WATCH_IO_WRITE) | (1<<WATCH_EXEC))
// Mask for all breakpoints/watches that read memory or IO
#define BW_RD_MASK ((1<<BRKPT_MEM_READ) | (1<<WATCH_MEM_READ) | (1<<BRKPT_IO_READ) | (1<<WATCH_IO_READ))
// Mask for all breakpoints/watches that write memory or IO
#define BW_WR_MASK ((1<<BRKPT_MEM_WRITE) | (1<<WATCH_MEM_WRITE) | (1<<BRKPT_IO_WRITE) | (1<<WATCH_IO_WRITE))
// Mask for all breakpoint or watches that read/write Memory or IO
#define BW_RDWR_MASK (BW_RD_MASK | BW_WR_MASK)
// Mask for all breakpoints that read/write Memory or IO
#define B_RDWR_MASK (BW_RD_MASK & B_MASK)
char *testNames[6] = {
"Fixed",
@ -159,16 +189,6 @@ char *testNames[6] = {
"Random"
};
char *modeStrings[7] = {
"Ex Breakpoint",
"Rn Breakpoint",
"Wr Breakpoint",
"Ex watch",
"Rd watch",
"Wr watch",
"Undefined"
};
#define NUM_TRIGGERS 16
#define TRIGGER_ALWAYS 15
@ -224,12 +244,12 @@ char *cmdStrings[NUM_CMDS] = {
"fill",
"crc",
"mem",
"readmem",
"writemem",
"rdm",
"wrm",
#if (CPU == Z80)
"io",
"readio",
"writeio",
"rdi",
"wri",
#else
"test",
#endif
@ -238,12 +258,18 @@ char *cmdStrings[NUM_CMDS] = {
"step",
"trace",
"blist",
"breaki",
"breakr",
"breakw",
"watchi",
"watchr",
"watchw",
"breakx",
"watchx",
"breakrm",
"watchrm",
"breakwm",
"watchwm",
#if (CPU == Z80)
"breakri",
"watchri",
"breakwi",
"watchwi",
#endif
"clear",
"trigger"
};
@ -396,11 +422,10 @@ void logMode(unsigned int mode) {
int first = 1;
for (i = 0; i < UNDEFINED; i++) {
if (mode & 1) {
if (first) {
log0("%s", modeStrings[i]);
} else {
log0(", %c%s", tolower(*modeStrings[i]), modeStrings[i] + 1);
if (!first) {
log0(", ");
}
log0("%s", modeStrings[i]);
first = 0;
}
mode >>= 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
};

View File

@ -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

View File

@ -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;