@@ -59,6 +59,9 @@ sio: SpecialIoRegisters,
5959flash : Flash ,
6060data : DataBus ,
6161io : IOBus ,
62+ sram_base : u16  =  0 ,
63+ sram_size : u16  =  0 ,
64+ eeprom_size : u16  =  0 ,
6265
6366// State 
6467pc : u24  =  0 ,
@@ -84,6 +87,110 @@ pub const RunResult = enum {
8487    program_exit ,
8588};
8689
90+ pub  fn  dump_system_state (cpu : * Cpu ) void  {
91+     // Dump complete system state 
92+     std .debug .print ("\n === SYSTEM STATE DUMP ===\n " , .{});
93+     std .debug .print ("PC: 0x{X:0>4}\n " , .{cpu .pc });
94+     std .debug .print ("SP: 0x{X:0>4}\n " , .{cpu .get_sp ()});
95+     std .debug .print ("SREG: {f}\n " , .{cpu .sreg });
96+ 
97+     // Dump X, Y, Z pointer registers 
98+     const  x_reg  =  (@as (u16 , cpu .regs [27 ]) <<  8 ) |  cpu .regs [26 ];
99+     const  y_reg  =  (@as (u16 , cpu .regs [29 ]) <<  8 ) |  cpu .regs [28 ];
100+     const  z_reg  =  (@as (u16 , cpu .regs [31 ]) <<  8 ) |  cpu .regs [30 ];
101+     std .debug .print ("\n POINTER REGISTERS:\n " , .{});
102+     std .debug .print ("X (r27:r26): 0x{X:0>4}\n " , .{x_reg });
103+     std .debug .print ("Y (r29:r28): 0x{X:0>4}\n " , .{y_reg });
104+     std .debug .print ("Z (r31:r30): 0x{X:0>4}\n " , .{z_reg });
105+ 
106+     // Dump all registers 
107+     std .debug .print ("\n REGISTERS:\n " , .{});
108+     for  (cpu .regs , 0.. ) | reg , i |  {
109+         if  (i  %  8  ==  0 ) std .debug .print ("r{d:0>2}-r{d:0>2}: " , .{ i , @min (i  +  7 , 31 ) });
110+         std .debug .print ("{X:0>2} " , .{reg });
111+         if  ((i  +  1 ) %  8  ==  0 ) std .debug .print ("\n " , .{});
112+     }
113+ 
114+     // Dump SRAM using absolute addresses based on configured base 
115+     const  sram_end : u16  =  cpu .sram_base  +  @as (u16 , @truncate (cpu .sram_size  -  1 ));
116+     std .debug .print ("\n SRAM DUMP (0x{X:0>4}-0x{X:0>4}, {d} bytes):\n " , .{ cpu .sram_base , sram_end , cpu .sram_size  });
117+ 
118+     const  row_width  =  16 ;
119+     var  prev_row : ? [row_width ]u8  =  null ;
120+     var  prev_len : usize  =  0 ;
121+     var  elided  =  false ;
122+ 
123+     var  i : usize  =  0 ;
124+     while  (i  <  cpu .sram_size ) : (i  +=  @min (row_width , cpu .sram_size  -  i )) {
125+         const  row_len : usize  =  @min (row_width , cpu .sram_size  -  i );
126+         var  cur_row : [row_width ]u8  =  undefined ;
127+ 
128+         var  j : usize  =  0 ;
129+         while  (j  <  row_len ) : (j  +=  1 ) {
130+             cur_row [j ] =  cpu .data .read (cpu .sram_base  +  @as (u16 , @intCast (i  +  j )));
131+         }
132+ 
133+         // Only elide repeated lines of all zeroes 
134+         const  is_all_zeros  =  blk : {
135+             for  (cur_row [0.. row_len ]) | byte |  {
136+                 if  (byte  !=  0 ) break  :blk  false ;
137+             }
138+             break  :blk  true ;
139+         };
140+         const  same_as_prev  =  if  (prev_row ) | prev | 
141+             prev_len  ==  row_len  and  std .mem .eql (u8 , prev [0.. prev_len ], cur_row [0.. row_len ])
142+         else 
143+             false ;
144+         if  (same_as_prev  and  is_all_zeros ) {
145+             elided  =  true ;
146+         } else  {
147+             if  (elided ) {
148+                 std .debug .print ("*\n " , .{});
149+                 elided  =  false ;
150+             }
151+ 
152+             std .debug .print ("0x{X:0>4}: " , .{cpu .sram_base  +  i });
153+             // hex bytes 
154+             j  =  0 ;
155+             while  (j  <  row_len ) : (j  +=  1 ) {
156+                 std .debug .print ("{X:0>2} " , .{cur_row [j ]});
157+             }
158+ 
159+             // pad hex area for short rows to align ASCII column 
160+             if  (row_len  <  row_width ) {
161+                 var  pad : usize  =  row_width  -  row_len ;
162+                 while  (pad  >  0 ) : (pad  -=  1 ) {
163+                     std .debug .print ("   " , .{});
164+                 }
165+             }
166+ 
167+             // ASCII representation 
168+             std .debug .print (" |" , .{});
169+             j  =  0 ;
170+             while  (j  <  row_len ) : (j  +=  1 ) {
171+                 const  b  =  cur_row [j ];
172+                 if  (b  >=  32  and  b  <=  126 ) {
173+                     std .debug .print ("{c}" , .{b });
174+                 } else  {
175+                     std .debug .print ("." , .{});
176+                 }
177+             }
178+             std .debug .print ("|\n " , .{});
179+ 
180+             // store as previous row 
181+             if  (prev_row  ==  null ) {
182+                 prev_row  =  [_ ]u8 {0 } **  row_width ;
183+             }
184+             @memcpy (prev_row .? [0.. row_len ], cur_row [0.. row_len ]);
185+             prev_len  =  row_len ;
186+         }
187+     }
188+     if  (elided )
189+         std .debug .print ("*\n " , .{});
190+ 
191+     std .debug .print ("=== END DUMP ===\n " , .{});
192+ }
193+ 
87194pub  fn  run (cpu : * Cpu , mileage : ? u64 , breakpoint : ? u24 ) RunError ! RunResult  {
88195    var  rest_gas  =  mileage ;
89196
@@ -172,12 +279,29 @@ fn shift_program_counter(cpu: *Cpu, by: i12) void {
172279}
173280
174281fn  fetch_code (cpu : * Cpu ) u16  {
175-     const  value  =  cpu .flash .read (cpu .pc );
282+     // Program memory is word-addressed; PC is in words. 
283+     const  value : u16  =  cpu .flash .read (@intCast (cpu .pc ));
176284    cpu .pc  +%=  1 ; // increment with wraparound 
177285    cpu .pc  &=  @intFromEnum (cpu .code_model ); // then wrap to lower bit size 
178286    return  value ;
179287}
180288
289+ inline  fn  data_read (cpu : * Cpu , addr : u24 ) u8  {
290+     return  cpu .data .read8 (addr ) catch  | e |  switch  (e ) {
291+         error .Unmapped  = >  @panic ("Read from unmapped memory address" ),
292+         error .OutOfRange  = >  @panic ("Read out of range" ),
293+         error .ReadOnly  = >  @panic ("ReadOnly error on read" ),
294+     };
295+ }
296+ 
297+ inline  fn  data_write (cpu : * Cpu , addr : u24 , value : u8 ) void  {
298+     cpu .data .write8 (addr , value ) catch  | e |  switch  (e ) {
299+         error .Unmapped  = >  @panic ("Write to unmapped memory address" ),
300+         error .OutOfRange  = >  @panic ("Write out of range" ),
301+         error .ReadOnly  = >  @panic ("Write to read-only memory" ),
302+     };
303+ }
304+ 
181305fn  push (cpu : * Cpu , val : u8 ) void  {
182306    // AVR PUSH: Write to [SP] first, then decrement SP 
183307    // SP points to the first unused location 
@@ -213,6 +337,7 @@ fn push_code_loc(cpu: *Cpu, val: u24) void {
213337fn  pop_code_loc (cpu : * Cpu ) u24  {
214338    const  mask  =  @intFromEnum (cpu .code_model );
215339
340+     // With increment-then-read POP, we pop most significant byte first. 
216341    var  pc : u24  =  0 ;
217342    if  ((mask  & 0xFF0000 ) !=  0 ) {
218343        pc  |=  (@as (u24 , cpu .pop ()) <<  16 );
@@ -242,11 +367,11 @@ fn read_wide_reg(cpu: *Cpu, comptime reg: WideReg, comptime mode: IndexRegReadMo
242367        switch  (mode ) {
243368            .raw  = >  0 ,
244369            .ramp  = >  switch  (reg ) {
245-                 .x  = >  if  (cpu .sio .ramp_x ) | ramp |  cpu .io .read (ramp ) else  0 ,
246-                 .y  = >  if  (cpu .sio .ramp_y ) | ramp |  cpu .io .read (ramp ) else  0 ,
247-                 .z  = >  if  (cpu .sio .ramp_z ) | ramp |  cpu .io .read (ramp ) else  0 ,
370+                 .x  = >  if  (cpu .sio .ramp_x ) | ramp |  ( cpu .io .read (@intCast ( ramp )) ) else  0 ,
371+                 .y  = >  if  (cpu .sio .ramp_y ) | ramp |  ( cpu .io .read (@intCast ( ramp )) ) else  0 ,
372+                 .z  = >  if  (cpu .sio .ramp_z ) | ramp |  ( cpu .io .read (@intCast ( ramp )) ) else  0 ,
248373            },
249-             .eind  = >  if  (cpu .sio .e_ind ) | e_ind |  cpu .io .read (e_ind ) else  0 ,
374+             .eind  = >  if  (cpu .sio .e_ind ) | e_ind |  ( cpu .io .read (@intCast ( e_ind )) ) else  0 ,
250375        },
251376        cpu .regs [reg .base () +  1 ],
252377        cpu .regs [reg .base () +  0 ],
@@ -778,7 +903,7 @@ const instructions = struct {
778903    /// Loads data from the I/O Space (Ports, Timers, Configuration Registers, etc.) into register Rd in the Register File. 
779904    inline  fn  in (cpu : * Cpu , info : isa.opinfo.a6d5 ) void  {
780905        // Rd ← I/O(A) 
781-         cpu .regs [info .d .num ()] =  cpu .io .read (info .a );
906+         cpu .regs [info .d .num ()] =  cpu .io .read (@intCast ( info .a ) );
782907    }
783908
784909    /// CBI – Clear Bit in I/O Register 
@@ -828,7 +953,7 @@ const instructions = struct {
828953    /// instruction operates on the lower 32 I/O Registers – addresses 0-31. 
829954    inline  fn  sbic (cpu : * Cpu , info : isa.opinfo.a5b3 ) void  {
830955        // If I/O(A,b) = 0 then PC ← PC + 2 (or 3) else PC ← PC + 1 
831-         const  val  =  cpu .io .read (info .a );
956+         const  val  =  cpu .io .read (@intCast ( info .a ) );
832957        if  ((val  & info .b .mask ()) ==  0 ) {
833958            cpu .instr_effect  =  .skip_next ;
834959        }
@@ -839,7 +964,7 @@ const instructions = struct {
839964    /// instruction operates on the lower 32 I/O Registers – addresses 0-31. 
840965    inline  fn  sbis (cpu : * Cpu , info : isa.opinfo.a5b3 ) void  {
841966        // If I/O(A,b) = 1 then PC ← PC + 2 (or 3) else PC ← PC + 1 
842-         const  val  =  cpu .io .read (info .a );
967+         const  val  =  cpu .io .read (@intCast ( info .a ) );
843968        if  ((val  & info .b .mask ()) !=  0 ) {
844969            cpu .instr_effect  =  .skip_next ;
845970        }
0 commit comments