Skip to content

Commit f092bc2

Browse files
committed
MEGA65: GEOS65 VDC support #438
From the `hypup` branch, the same commit, introduced now in `next`: 62ebed2 2d91d1b Thanks @gurcei for some "detective work" and bluewaysw_falk from the GEOS side with additional information.
1 parent 22cc3df commit f092bc2

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

targets/mega65/io_mapper.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,84 @@ static const Uint8 cpld_firmware_version[] = { 'N','o','w','!' };
5757
return; } while(0)
5858

5959

60+
// ------------------ BEGIN: VDC specific ------------------
61+
62+
63+
static Uint8 vdc_reg_sel = 0;
64+
#define VDC_ENABLED() ((D7XX[0x10] & 4) && !in_hypervisor)
65+
static Uint8 vdc_regs[0x40];
66+
67+
68+
//#define VDC_DEBUG DEBUGPRINT
69+
#define VDC_DEBUG(...)
70+
71+
static XEMU_INLINE Uint8 *vdc2vicptr ( const Uint16 vdc_address )
72+
{
73+
// The logic here is from mega65-core's VHDL source
74+
const unsigned int line = vdc_address / 80U;
75+
const unsigned int col = vdc_address % 80U;
76+
const unsigned int viciv_line = line / 8U;
77+
const unsigned int result = (viciv_line * 640U) + (line % 8U) + (col * 8U);
78+
// Assuming hires bitmap screen always at address $40000
79+
return main_ram + result + 0x40000U;
80+
}
81+
82+
83+
static inline Uint8 vdc_read_register ( const Uint8 reg )
84+
{
85+
VDC_DEBUG("VDC: reading register $%02X" NL, reg);
86+
if (reg == 0x1F) { // reading the data register which means reading a byte through VDC
87+
Uint16 vdc_mem_addr = (vdc_regs[0x12] << 8) + vdc_regs[0x13];
88+
VDC_DEBUG("VDC: reading VRAM at VDC address $%04X" NL, vdc_mem_addr);
89+
const Uint8 result = *vdc2vicptr(vdc_mem_addr++);
90+
vdc_regs[0x12] = vdc_mem_addr >> 8;
91+
vdc_regs[0x13] = vdc_mem_addr & 0xFF;
92+
return result;
93+
}
94+
return vdc_regs[reg];
95+
}
96+
97+
98+
static inline void vdc_write_register ( const Uint8 reg, const Uint8 data )
99+
{
100+
VDC_DEBUG("VDC: writing register $%02X with data $%02X" NL, reg, data);
101+
vdc_regs[reg] = data;
102+
if (reg == 0x1F) { // writing the data register which means writing a byte through VDC
103+
Uint16 vdc_mem_addr = (vdc_regs[0x12] << 8) + vdc_regs[0x13];
104+
VDC_DEBUG("VDC: writing VRAM at VDC address $%04X with data $%02X" NL, vdc_mem_addr, data);
105+
*vdc2vicptr(vdc_mem_addr++) = data;
106+
vdc_regs[0x12] = vdc_mem_addr >> 8;
107+
vdc_regs[0x13] = vdc_mem_addr & 0xFF;
108+
return;
109+
}
110+
// FIXME: this block copy/write emulation is really bad. Though I am unsure if anyone ever uses/used it. If so, it must be fixed in the future.
111+
if (reg == 0x1E) { // writing the count register triggers a block write or block copy operation
112+
// NOTE: block commands are totally not tested and missing many parts!!! Like the busy check
113+
// And maybe block "dimensions" as well. Also these ops are done in one step, stalling emulation. VERY BAD!
114+
Uint16 vdc_mem_addr = (vdc_regs[0x12] << 8) + vdc_regs[0x13]; // VDC memory update address
115+
int vdc_word_count = data; // block copy fill/word count (the current register value: reg $1E)
116+
if (vdc_regs[0x18] & 0x80) { // ---[ BLOCK COPY OPERATION ]----------
117+
Uint16 vdc_mem_addr_src = (vdc_regs[0x20] << 8) + vdc_regs[0x21]; // block copy source address
118+
VDC_DEBUG("VDC: block copy of VRAM at VDC addresses $%04X -> $%04X with count = $%X" NL, vdc_mem_addr_src, vdc_mem_addr, vdc_word_count);
119+
while (vdc_word_count-- > 0)
120+
*vdc2vicptr(vdc_mem_addr++) = *vdc2vicptr(vdc_mem_addr_src++);
121+
vdc_regs[0x20] = vdc_mem_addr_src >> 8;
122+
vdc_regs[0x21] = vdc_mem_addr_src & 0xFF;
123+
} else { // ---[ BLOCK WRITE OPERATION ]---------
124+
VDC_DEBUG("VDC: block write of RAM at VDC address $%04X with data = $%02X and count = $%X" NL, vdc_mem_addr, vdc_regs[0x1F], vdc_word_count);
125+
while (vdc_word_count-- > 0)
126+
*vdc2vicptr(vdc_mem_addr++) = vdc_regs[0x2A]; // normally attrib control, but on block write, this is the data to be written
127+
}
128+
vdc_regs[0x12] = vdc_mem_addr >> 8;
129+
vdc_regs[0x13] = vdc_mem_addr & 0xFF;
130+
return;
131+
}
132+
}
133+
134+
135+
// ------------------ END: VDC specific ------------------
136+
137+
60138
static XEMU_INLINE void update_hw_multiplier ( void )
61139
{
62140
register const Uint32 input_a = xemu_u8p_to_u32le(D7XX + 0x70);
@@ -189,6 +267,8 @@ Uint8 io_read ( unsigned int addr )
189267
case 0x36: // $D600-$D6FF ~ M65 I/O mode
190268
case 0x26:
191269
addr &= 0xFF;
270+
if (addr <= 1 && VDC_ENABLED()) // $D600 or $D601 if VDC is enabled
271+
return addr ? vdc_read_register(vdc_reg_sel) : 0x80; // 0x80 = always return with "READY" as VDC status if $D600 is read
192272
if (addr < 9)
193273
RETURN_ON_IO_READ_NOT_IMPLEMENTED("UART", 0xFF); // FIXME: UART is not yet supported!
194274
if (addr >= 0x80 && addr <= 0x93) // SDcard controller etc of MEGA65
@@ -445,6 +525,13 @@ void io_write ( unsigned int addr, Uint8 data )
445525
case 0x36: // $D600-$D6FF ~ M65 I/O mode
446526
case 0x26:
447527
addr &= 0xFF;
528+
if (addr <= 1 && VDC_ENABLED()) { // $D600 or $D601 if VDC is enabled
529+
if (addr)
530+
vdc_write_register(vdc_reg_sel, data); // register data to be written ($D601)
531+
else
532+
vdc_reg_sel = data & 0x3F; // register selection (on write of $D600): bit7&6 are not used though
533+
return;
534+
}
448535
if (!in_hypervisor && addr >= 0x40 && addr <= 0x7F) {
449536
// In user mode, writing to $D640-$D67F (in VIC4 iomode) causes to enter hypervisor mode with
450537
// the trap number given by the offset in this range

0 commit comments

Comments
 (0)