@@ -145,6 +145,20 @@ pub const Bus = struct {
145145 self .ensureZ80HostWindow ();
146146 const zaddr : u16 = @truncate (addr & 0xFFFF );
147147 return self .z80 .readByte (zaddr );
148+ } else if (addr >= 0xC00000 and addr < 0xC00020 ) {
149+ if (addr < 0xC00004 ) {
150+ const word = self .vdp .readData ();
151+ return if ((addr & 1 ) == 0 ) @intCast ((word >> 8 ) & 0xFF ) else @intCast (word & 0xFF );
152+ }
153+ if (addr < 0xC00008 ) {
154+ const word = self .vdp .readControl ();
155+ return if ((addr & 1 ) == 0 ) @intCast ((word >> 8 ) & 0xFF ) else @intCast (word & 0xFF );
156+ }
157+ if (addr < 0xC00010 ) {
158+ const word = self .vdp .readHVCounter ();
159+ return if ((addr & 1 ) == 0 ) @intCast ((word >> 8 ) & 0xFF ) else @intCast (word & 0xFF );
160+ }
161+ return 0 ;
148162 } else if (addr >= 0xA10000 and addr < 0xA10100 ) {
149163 // IO
150164 return self .io .read (addr );
@@ -156,10 +170,7 @@ pub const Bus = struct {
156170
157171 pub fn read16 (self : * Bus , address : u32 ) u16 {
158172 const addr = address & 0xFFFFFF ;
159- if (addr >= 0xA10000 and addr < 0xA10100 ) {
160- const val = self .io .read (addr );
161- return @as (u16 , val ) << 8 ;
162- } else if (addr == 0xA11100 ) { // Z80 Bus Request
173+ if (addr == 0xA11100 ) { // Z80 Bus Request
163174 return self .z80 .readBusReq ();
164175 } else if (addr == 0xA11200 ) { // Z80 Reset
165176 return 0 ; // Write only?
@@ -207,9 +218,13 @@ pub const Bus = struct {
207218 // IO
208219 self .io .write (addr , value );
209220 } else if (addr >= 0xC00000 and addr < 0xC00020 ) {
210- // VDP (Byte writes to VDP are complex, usually MSB/LSB duplicated or ignored depending on port)
211- // For now, ignore or implement as needed.
212- // VDP Data port byte writes are valid?
221+ const word : u16 = if ((addr & 1 ) == 0 ) (@as (u16 , value ) << 8 ) else @as (u16 , value );
222+ if (addr < 0xC00004 ) {
223+ self .vdp .writeData (word );
224+ } else if (addr < 0xC00008 ) {
225+ self .vdp .writeControl (word );
226+ }
227+ return ;
213228 }
214229 }
215230
@@ -263,41 +278,36 @@ pub const Bus = struct {
263278 if (self .vdp .dma_active ) {
264279 // Perform DMA
265280 if (self .vdp .dma_fill ) {
266- // DMA Fill (VRAM Fill)
267- // Triggered by writing to the VDP data port, but the length/destination are set up here.
268- // In a real VDP, the fill happens as the CPU writes to the data port?
269- // Checks regs[23] bit 7 (DMA Type 1 = Fill).
270- // But here we just want to ensure we don't crash or hang.
271- // For now, let's acknowledge it and clear the active flag so we don't loop forever.
272- self .vdp .dma_active = false ;
281+ // DMA fill completes on the next VDP data port write.
282+ // Keep the DMA state armed until VDP.writeData consumes it.
273283 } else {
274284 // 68k -> VRAM Copy (DMA Mode 0 or 1)
275- var len = @as (u32 , self .vdp .dma_length );
276- if (len == 0 ) len = 0xFFFF ; // Usually 0 in reg means 64k word transfer (128kB)?
277- // Docs say: Register value 0 = 0? Or 64k?
278- // Reg 19/20 are 8-bit. reg19<<8 | reg20.
279- // Usually 0 means 0x10000 words.
285+ var remaining = self .vdp .dma_remaining ;
286+ if (remaining == 0 ) {
287+ self .vdp .dma_active = false ;
288+ return ;
289+ }
290+ // Incremental DMA: avoid stalling an entire frame on large transfers.
291+ // This keeps emulation responsive until cycle-accurate DMA timing is added.
292+ var budget_words = @as (u32 , master_cycles / 8 );
293+ if (budget_words == 0 ) budget_words = 1 ;
294+ if (budget_words > remaining ) budget_words = remaining ;
280295
281296 var src_addr = self .vdp .dma_source_addr ;
282-
283- // Transfer loop (16-bit words)
284- // Note: Generic DMA is from 68k Bus to VDP Data Port.
285- // It auto-increments VDP address.
286- while (len > 0 ) {
297+ var words_done : u32 = 0 ;
298+ while (words_done < budget_words ) : (words_done += 1 ) {
287299 const word = self .read16 (src_addr );
288300 self .vdp .writeData (word );
289301 src_addr += 2 ; // Source address increments
290- // Note: If Source is ROM/RAM, it increments.
291- // If Source is fixed (not supported by standard DMA copy?), it wouldn't.
292- // Standard 68k->VDP DMA increments source.
293-
294- if (len == 0 ) break ; // Overflow protection
295- len -= 1 ;
296302 }
297303
298304 self .vdp .dma_source_addr = src_addr ;
299- self .vdp .dma_length = 0 ;
300- self .vdp .dma_active = false ;
305+ remaining -= words_done ;
306+ self .vdp .dma_remaining = remaining ;
307+ if (remaining == 0 ) {
308+ self .vdp .dma_length = 0 ;
309+ self .vdp .dma_active = false ;
310+ }
301311 }
302312 }
303313 }
0 commit comments