@@ -61,8 +61,9 @@ pub struct Watch {
6161 pub len : u32 ,
6262}
6363
64- /// The 68000 address-bus width the interactive debugger compares PCs and
65- /// watch addresses through (A0-A23).
64+ /// The 68000/EC020 address-bus mask (A0-A23). Debugger surfaces compare
65+ /// and display through the machine's model mask (`ui_addr_mask()`); this
66+ /// constant remains for tests and 24-bit callers.
6667pub const UI_ADDR_MASK : u32 = 0x00FF_FFFF ;
6768
6869/// An interactive memory watchpoint: a 16-bit word and the value it held
@@ -687,8 +688,11 @@ pub struct Breakpoint {
687688/// The debugger window's breakpoint/watchpoint set. Owned by the CPU
688689/// machine so it stays armed while the window is closed; `armed` is the
689690/// single per-instruction gate the hot loop checks.
690- #[ derive( Default ) ]
691691pub struct InteractiveBreaks {
692+ /// Address-bus mask breakpoint/watch addresses and PCs are compared
693+ /// through: A0-A23 on 24-bit models, full 32 bits on 020+ (set from
694+ /// the CPU model at machine construction).
695+ pub addr_mask : u32 ,
692696 pub breakpoints : Vec < Breakpoint > ,
693697 pub watches : Vec < UiWatch > ,
694698 /// Watched custom-register word offsets into $DFF000 ($000-$1FE).
@@ -706,6 +710,20 @@ pub struct InteractiveBreaks {
706710}
707711
708712impl InteractiveBreaks {
713+ /// An empty break set comparing addresses through `addr_mask` (the
714+ /// owning machine's address-bus mask; see `address_mask_for_model`).
715+ pub fn new ( addr_mask : u32 ) -> Self {
716+ Self {
717+ addr_mask,
718+ breakpoints : Vec :: new ( ) ,
719+ watches : Vec :: new ( ) ,
720+ reg_watches : Vec :: new ( ) ,
721+ catches : Vec :: new ( ) ,
722+ task_catch : None ,
723+ armed : false ,
724+ }
725+ }
726+
709727 pub fn armed ( & self ) -> bool {
710728 self . armed
711729 }
@@ -721,7 +739,7 @@ impl InteractiveBreaks {
721739 /// Whether any breakpoint is set at `pc`, ignoring its condition. Used for
722740 /// display (marking the address) and the reverse-debug scan.
723741 pub fn is_breakpoint ( & self , pc : u32 ) -> bool {
724- let pc = pc & UI_ADDR_MASK ;
742+ let pc = pc & self . addr_mask ;
725743 self . breakpoints . iter ( ) . any ( |bp| bp. addr == pc)
726744 }
727745
@@ -734,7 +752,7 @@ impl InteractiveBreaks {
734752 cond : Option < BreakCond > ,
735753 ignore : u32 ,
736754 ) -> bool {
737- let addr = addr & UI_ADDR_MASK ;
755+ let addr = addr & self . addr_mask ;
738756 let added = match self . breakpoints . iter ( ) . position ( |bp| bp. addr == addr) {
739757 Some ( pos) => {
740758 self . breakpoints . remove ( pos) ;
@@ -759,7 +777,7 @@ impl InteractiveBreaks {
759777 /// ignore count has been exhausted -- each qualifying hit before that is
760778 /// counted and skipped.
761779 pub fn breakpoint_stops ( & mut self , pc : u32 , ctx : & dyn BreakContext ) -> bool {
762- let pc = pc & UI_ADDR_MASK ;
780+ let pc = pc & self . addr_mask ;
763781 let Some ( bp) = self . breakpoints . iter_mut ( ) . find ( |bp| bp. addr == pc) else {
764782 return false ;
765783 } ;
@@ -1118,7 +1136,7 @@ mod tests {
11181136
11191137 #[ test]
11201138 fn interactive_breakpoints_toggle_mask_and_arm ( ) {
1121- let mut breaks = InteractiveBreaks :: default ( ) ;
1139+ let mut breaks = InteractiveBreaks :: new ( UI_ADDR_MASK ) ;
11221140 assert ! ( !breaks. armed( ) ) ;
11231141
11241142 // Adding masks the address to the 68000 bus width.
@@ -1132,6 +1150,16 @@ mod tests {
11321150 assert ! ( !breaks. is_breakpoint( 0x00C0_33C2 ) ) ;
11331151 }
11341152
1153+ #[ test]
1154+ fn full_mask_keeps_z3_breakpoints_distinct_from_chip_aliases ( ) {
1155+ // On a 32-bit CPU a Zorro III breakpoint must not fire at the
1156+ // chip-RAM address it would alias through a 24-bit mask.
1157+ let mut breaks = InteractiveBreaks :: new ( 0xFFFF_FFFF ) ;
1158+ assert ! ( breaks. toggle_breakpoint_full( 0x4000_1000 , None , 0 ) ) ;
1159+ assert ! ( breaks. is_breakpoint( 0x4000_1000 ) ) ;
1160+ assert ! ( !breaks. is_breakpoint( 0x0000_1000 ) ) ;
1161+ }
1162+
11351163 /// Fixed register/memory snapshot for exercising condition evaluation.
11361164 #[ derive( Default ) ]
11371165 struct FakeCtx {
@@ -1162,7 +1190,7 @@ mod tests {
11621190
11631191 #[ test]
11641192 fn conditional_breakpoint_stops_only_when_condition_holds ( ) {
1165- let mut breaks = InteractiveBreaks :: default ( ) ;
1193+ let mut breaks = InteractiveBreaks :: new ( UI_ADDR_MASK ) ;
11661194 breaks. toggle_breakpoint_full (
11671195 0x1000 ,
11681196 Some ( BreakCond {
@@ -1186,7 +1214,7 @@ mod tests {
11861214
11871215 #[ test]
11881216 fn ignore_count_skips_the_first_qualifying_hits ( ) {
1189- let mut breaks = InteractiveBreaks :: default ( ) ;
1217+ let mut breaks = InteractiveBreaks :: new ( UI_ADDR_MASK ) ;
11901218 // Stop on the 4th qualifying hit (ignore the first 3).
11911219 breaks. toggle_breakpoint_full ( 0x1000 , None , 3 ) ;
11921220 let ctx = FakeCtx :: default ( ) ;
@@ -1200,7 +1228,7 @@ mod tests {
12001228
12011229 #[ test]
12021230 fn bit_test_condition_uses_memory_word ( ) {
1203- let mut breaks = InteractiveBreaks :: default ( ) ;
1231+ let mut breaks = InteractiveBreaks :: new ( UI_ADDR_MASK ) ;
12041232 breaks. toggle_breakpoint_full (
12051233 0x40 ,
12061234 Some ( BreakCond {
@@ -1219,7 +1247,7 @@ mod tests {
12191247
12201248 #[ test]
12211249 fn interactive_watches_record_baselines_and_clear ( ) {
1222- let mut breaks = InteractiveBreaks :: default ( ) ;
1250+ let mut breaks = InteractiveBreaks :: new ( UI_ADDR_MASK ) ;
12231251 assert ! ( breaks. toggle_watch( 0x1000 , 0xABCD , None ) ) ;
12241252 assert_eq ! ( breaks. watches[ 0 ] . last, 0xABCD ) ;
12251253 // The register watch normalizes a full $DFFxxx address to the
0 commit comments