Skip to content

Commit

Permalink
MEGA65: first try of line drawing DMA (WIP!!) #404
Browse files Browse the repository at this point in the history
Incomplete and possible buggy first try implementation of DMA line
drawing. Ideas are based on a "draft implementation" by btoschi
on Discord (@bjotos on github).

The only reason I present this highly incomplete work in the next branch
of Xemu (so not in dev) because I am on holiday, so it's a bit random
when I have time to do more work. Thus it's better to release it in
this unfinished form, I guess, if someone wants to play with it.
  • Loading branch information
lgblgblgb committed Aug 19, 2024
1 parent bd47ac9 commit c33bf19
Showing 1 changed file with 108 additions and 8 deletions.
116 changes: 108 additions & 8 deletions targets/mega65/dma65.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,13 @@ static unsigned int list_entry_pos = 0;
// channel.addr is a fixed-point value, with the lower 8 bits being the fractional part
#define DMA_ADDRESSING(channel) (((channel.addr >> 8) & 0xFFFFF) + channel.base)

struct ldm_st {
Uint32 x_col, y_col, slope, slope_accu;
Uint8 slope_type;
};

// source and target DMA "channels":
static struct {
static struct dma_channel_st {
int addr; // address of the current operation, it's a fixed-point math value
int base; // base address for "addr", always a "pure" number! It also contains the "megabyte selection", pre-shifted by << 20
int step; // step value, zero(HOLD)/negative/positive, this is a fixed point arithmetic value!!
Expand All @@ -79,6 +84,7 @@ static struct {
Uint8 mbyte; // megabyte slice selected during option read
int is_modulo; // modulo mode, if it's non-zero
int also_io; // channel access I/O instead of memory, if it's non-zero
struct ldm_st ldm; // LDM = Line Drawing Mode
} source, target;

static struct {
Expand Down Expand Up @@ -165,17 +171,51 @@ static Uint8 dma_read_list_next_byte ( void )
return data;
}


static XEMU_INLINE void address_stepping ( struct dma_channel_st *const channel )
{
if (XEMU_LIKELY(!(channel->ldm.slope_type & 0x80))) {
// normal, non-LDM (line drawning mode) method
channel->addr += channel->step;
return;
}
// otherwise, we must deal with LDM. The following code is based
// on ideas found in a sample C implementation written by btoschi. THANKS!!
// WARNING: in Xemu, I use a single variable for "addr" and lower 8 bit is the fractional part!!
if (channel->ldm.slope_type & 0x40) {
channel->addr += 0x800U; // +8 -> we always step in Y
if (channel->ldm.slope_accu > 0xFFFFU) {
channel->ldm.slope_accu &= 0xFFFFU;
if (channel->ldm.slope_type & 0x20)
channel->addr -= ((channel->addr & 0x700) == 0) ? channel->ldm.x_col + 0x100 : 0x100;
else
channel->addr += ((channel->addr & 0x700) == 0) ? channel->ldm.x_col + 0x100 : 0x100;
}
} else {
channel->addr += ((channel->addr & 0x700) == 0x700) ? channel->ldm.x_col + 0x100 : 0x100;
channel->ldm.slope_accu += channel->ldm.slope;
if (channel->ldm.slope_accu > 0xFFFFU) {
channel->ldm.slope_accu &= 0xFFFFU;
channel->addr += (channel->ldm.slope_type & 0x20) ? -0x800 : 0x800;
}
}
}


static XEMU_INLINE void copy_next ( void )
{
dma_write_target(dma_read_source());
source.addr += source.step;
target.addr += target.step;
//source.addr += source.step;
//target.addr += target.step;
address_stepping(&source);
address_stepping(&target);
}

static XEMU_INLINE void fill_next ( void )
{
dma_write_target(filler_byte);
target.addr += target.step;
//target.addr += target.step;
address_stepping(&target);
}

static XEMU_INLINE void swap_next ( void )
Expand All @@ -184,8 +224,10 @@ static XEMU_INLINE void swap_next ( void )
Uint8 da = dma_read_target();
dma_write_source(da);
dma_write_target(sa);
source.addr += source.step;
target.addr += target.step;
//source.addr += source.step;
//target.addr += target.step;
address_stepping(&source);
address_stepping(&target);
}

static XEMU_INLINE void mix_next ( void )
Expand All @@ -203,8 +245,10 @@ static XEMU_INLINE void mix_next ( void )
((~sa) & ( da) & minterms[1]) |
((~sa) & (~da) & minterms[0]) ;
dma_write_target(da);
source.addr += source.step;
target.addr += target.step;
//source.addr += source.step;
//target.addr += target.step;
address_stepping(&source);
address_stepping(&target);
}


Expand Down Expand Up @@ -291,6 +335,8 @@ void dma_write_reg ( int addr, Uint8 data )
source.mbyte = 0; // source MB
target.mbyte = 0; // target MB
length_byte3 = 0; // length byte for >=64K DMA sessions
source.ldm.slope_type = 0; // source: line drawing mode, slope type, do not enable line drawing mode by default
target.ldm.slope_type = 0; // target: -- "" --
if (enhanced_mode)
DEBUGDMA("DMA: initiation of ENCHANCED MODE DMA!!!!\n");
else
Expand Down Expand Up @@ -375,9 +421,63 @@ int dma_update ( void )
case 0x86: // byte value to be treated as "transparent" (ie: skip writing that data), if enabled
transparency = (transparency & 0x100) | (unsigned int)optval;
break;
case 0x87: // DMA line drawing mode TARGET - X col (LSB)
target.ldm.x_col = (target.ldm.x_col & 0xFF0000U) + (optval << 8); // Xemu integer + 8 bit fractional part arithmetic!
break;
case 0x88: // DMA line drawing mode TARGET - X col (MSB)
target.ldm.x_col = (target.ldm.x_col & 0x00FF00U) + (optval << 16); // Xemu integer + 8 bit fractional part arithmetic!
break;
case 0x89: // DMA line drawing mode TARGET - Row Y col (LSB)
target.ldm.y_col = (target.ldm.y_col & 0xFF00U) + optval;
break;
case 0x8A: // DMA line drawing mode TARGET - Row Y col (MSB)
target.ldm.y_col = (target.ldm.y_col & 0x00FFU) + (optval << 8);
break;
case 0x8B: // DMA line drawing mode TARGET - Slope (LSB)
target.ldm.slope = (target.ldm.slope & 0xFF00U) + optval;
break;
case 0x8C: // DMA line drawing mode TARGET - Slope (MSB)
target.ldm.slope = (target.ldm.slope & 0x00FFU) + (optval << 8);
break;
case 0x8D: // DMA line drawing mode TARGET - Slope init value (LSB)
target.ldm.slope_accu = (target.ldm.slope_accu & 0xFF00U) + optval;
break;
case 0x8E: // DMA line drawing mode TARGET - Slope init value (MSB)
target.ldm.slope_accu = (target.ldm.slope_accu & 0x00FFU) + (optval << 8);
break;
case 0x8F: // DMA line drawing mode TARGET - Slope type
target.ldm.slope_type = optval;
break;
case 0x90: // extra high byte of DMA length (bits 23-16) to allow to have >64K DMA
length_byte3 = optval;
break;
case 0x97: // DMA line drawing mode SOURCE - X col (LSB)
source.ldm.x_col = (source.ldm.x_col & 0xFF0000U) + (optval << 8); // Xemu integer + 8 bit fractional part arithmetic!
break;
case 0x98: // DMA line drawing mode SOURCE - X col (MSB)
source.ldm.x_col = (source.ldm.x_col & 0x00FF00U) + (optval << 16); // Xemu integer + 8 bit fractional part arithmetic!
break;
case 0x99: // DMA line drawing mode SOURCE - Row Y col (LSB)
source.ldm.y_col = (source.ldm.y_col & 0xFF00U) + optval;
break;
case 0x9A: // DMA line drawing mode SOURCE - Row Y col (MSB)
source.ldm.y_col = (source.ldm.y_col & 0x00FFU) + (optval << 8);
break;
case 0x9B: // DMA line drawing mode SOURCE - Slope (LSB)
source.ldm.slope = (source.ldm.slope & 0xFF00U) + optval;
break;
case 0x9C: // DMA line drawing mode SOURCE - Slope (MSB)
source.ldm.slope = (source.ldm.slope & 0x00FFU) + (optval << 8);
break;
case 0x9D: // DMA line drawing mode SOURCE - Slope init value (LSB)
source.ldm.slope_accu = (source.ldm.slope_accu & 0xFF00U) + optval;
break;
case 0x9E: // DMA line drawing mode SOURCE - Slope init value (MSB)
source.ldm.slope_accu = (source.ldm.slope_accu & 0x00FFU) + (optval << 8);
break;
case 0x9F: // DMA line drawing mode SOURCE - Slope type
source.ldm.slope_type = optval;
break;
default:
// maybe later we should keep this quiet ...
if ((opt & 0x80))
Expand Down

0 comments on commit c33bf19

Please sign in to comment.