I was trying to figure out how the atari 800 worked, so I could add a printer device to it.
As it happens, you end up getting interested in how things are put together, and want to figure out the entire system.
I remembered how much I wanted to play choplifter on the a800 driver and it always bugged me that it flickered and was unplayable.
After seeing how display lists work, it appeared that it was writing DLISTH while it was in the middle of executing a displaylist with the ANTIC.
WRITE DLISTH = 2000 ':maincpu' (E7F4)
WRITE DLISTL = 2000 ':maincpu' (E7FA)
scanline = 8 new_cmd = 70 m_dpage=2000 m_doffs=1
scanline = 10 new_cmd = 70 m_dpage=2000 m_doffs=2
scanline = 18 new_cmd = 70 m_dpage=2000 m_doffs=3
WRITE DLISTH = 2100 ':maincpu' (8E32)
scanline = 20 new_cmd = 10 m_dpage=2000 m_doffs=4
scanline = 22 new_cmd = c2 m_dpage=2000 m_doffs=5
scanline = 2a new_cmd = 4f m_dpage=2000 m_doffs=8
scanline = 2b new_cmd = 4f m_dpage=2000 m_doffs=b
so according to the manuals that I could find online, DLISTH is supposed to write the upper byte, and DLISTL is supposed to write the lower byte.
from the Altirra hardware reference manual:
Instruction pointer
The DLISTL and DLISTH registers contain the instruction pointer used to fetch the display list. At the end of each
mode line, ANTIC fetches a new instruction at the location pointed to by DLISTL/DLISTH into the instruction
register (IR), and then increments the pointer. This continues until a jump instruction is reached, which then
loads a new address into DLISTL/DLISTH. ANTIC does not store the start of the display list and has no registers
to do so; the display list must either loop or be restarted by the CPU.
The display list can reside anywhere in the 64K address space, but it cannot cross a 1K boundary. This is
because the DLISTL/DLISTH register is actually split into 6 bit and 10 bit portions, where the lower 10 bits
increment and the upper 6 bits do not.14 As a result, during normal execution the display list will wrap from the
top of a 1K block to the bottom during fetching, e.g. $07FF to $0400. This will happen even in the middle of a
three-byte LMS or jump instruction. Jump instructions are not limited and can cross 1K boundaries to any
address.
DLISTL/DLISTH are live during display list execution and any write to either will immediately change the address
used for the next display list fetch. Because of the possibility of display list interrupts, it is dangerous to do this in
the middle of a display list, as changing only one of the address bytes may cause ANTIC to execute random
memory as a display list and therefore issue spurious DLIs. A $C1 instruction is particularly dangerous as it will
cause a DLI to activate every scan line until vertical blank and can easily cause a crash. Therefore, the display
list pointer should normally only be updated when either display list DMA is disabled or during vertical blank.
However, the routine updates both DLISTH and DLISTL upon a write when (I think) it's only supposed to write the single byte.
// #define DPAGE 0xfc00 /* 1K page mask for display list */
// #define DOFFS 0x03ff /* 1K offset mask for display list */
current code:
void antic_device::write(offs_t offset, uint8_t data)
{
...
case 2:
LOG("ANTIC 02 write DLISTL $%02X\n", data);
m_w.dlistl = data;
temp = (m_w.dlisth << 8) + m_w.dlistl;
m_dpage = temp & DPAGE;
m_doffs = temp & DOFFS;
break;
case 3:
LOG("ANTIC 03 write DLISTH $%02X\n", data);
m_w.dlisth = data;
temp = (m_w.dlisth << 8) + m_w.dlistl;
m_dpage = temp & DPAGE;
m_doffs = temp & DOFFS;
break;
and if I change it to only update the part of each byte that's stored in m_dpage and m_doffs:
case 2:
LOG("ANTIC 02 write DLISTL $%02X\n", data);
m_w.dlistl = data;
temp = (m_w.dlisth << 8) + m_w.dlistl;
m_doffs = (m_doffs & 0x0300) | (temp & 0xff & DOFFS); // keep upper 2 bits of doffs
// printf("WRITE DLISTL = %x %s\n",temp,machine().describe_context().c_str());
break;
case 3:
LOG("ANTIC 03 write DLISTH $%02X\n", data);
m_w.dlisth = data;
temp = (m_w.dlisth << 8) + m_w.dlistl;
m_dpage = temp & DPAGE;
m_doffs = (m_doffs & 0xff) | (temp & 0x300 & DOFFS); // keep the low 2 bits of DLISTH and or with low byte of doffs
// printf("WRITE DLISTH = %x %s\n",temp,machine().describe_context().c_str());
break;
With this change, Choplifter seems to work just fine.
I don't know if this breaks anything else, but I was super happy to see Choplifter running.