lgblgblgb / xemu

Emulations (running on Linux/Unix/Windows/macOS, utilizing SDL2) of some - mainly - 8 bit machines, including the Commodore LCD, Commodore 65, and the MEGA65 as well.
https://github.com/lgblgblgb/xemu/wiki
GNU General Public License v2.0
206 stars 32 forks source link

MEGA65: implement line-drawing DMA mode #404

Open lgblgblgb opened 2 months ago

lgblgblgb commented 2 months ago

As part of issue #198, the main and most important missing piece of DMA emulation is "line drawing mode" (LDM for short from now on).

Till now, the main bottleneck to implement was my mental limitation to understand how this works at all :sweat_smile: OK, ok, time was a factor as well not to try to dig into the VHDL of mega65-core ... :sweat_smile:

Fortunatelly @bjotos (btoschi on MEGA65 Discord) wrote a test implementation in C, which was shared with me, huge thanks!

As I thought before, the major problem is understanding how the address incrementing works where the mentioned work before helps.

Original MEGA65 topic on implementing LDM: https://github.com/MEGA65/mega65-core/issues/290

lgblgblgb commented 2 months ago

https://github.com/lgblgblgb/xemu/commit/3810fed50f933b669d3804f670bc714f5106bc7f <-- Unfinished "WIP" in dev branch. It must reach a maturity/finished level to go into next, it's not ready yet!

bjotos commented 2 months ago

Checking with Shallan's clockhand sample, I can see that major-Y-axis mode isn't working properly.

main-clockhand.prg.zip Original source is here: https://github.com/smnjameson/M65_Examples/blob/main/4-DMATrickery%20Pt2/main%20-%20clockhand.s

I have this code in my DMA SW sim, which works with test290.c from mega65 tests, which includes major-Y-axis modes:

    if (line->slope_type & 0x40)
    {
        /* Y major axis */
        /* we always step in Y */
        uint32_t y_step = 8;
        if (((*addr) & (7<<3))==(7<<3)) /* last row in 8x8 cell */
        {
            y_step += MAKE16(line->row_y_col);
        }
        (*addr) += y_step;
        /* update accum, step in X if overflow */
        (*slope_accu) += MAKE16(line->slope);
        if (*slope_accu > 0xFFFF)
        {
            (*slope_accu) -= 0x10000;
            /* step X */
            uint32_t x_step;
            if (line->slope_type & 0x20) /* minor axis negative stepping */
            {
                if (((*addr) & 7)==0)
                {
                    x_step = MAKE16(line->x_col)+1;
                }
                else
                {
                    x_step = 1;
                }
                (*addr) -= x_step;
            }
            else /* minor axis positive stepping */
            {
                if ((*addr & 7)==7)
                {
                    x_step = MAKE16(line->x_col)+1;
                }
                else
                {
                    x_step = 1;
                }
                (*addr) += x_step;
            }
        }
    }
    // major-X-code follows ...

Not sure if I missed to share that updated code, but two major diffs I can spot:

  1. I'm adding row_y_col when reaching end of 8x8 block (vertically).
  2. I'm using different checks for end of 8x8 block (horizontally) depending on positive/negative stepping.

Note: +8 vs +0x800 is fine, address has 8 fractional bits in your code.