Jump to content
IGNORED

CC65 - easy and fast way to define a display list


Recommended Posts

the assumption is you already know what a display list is and how it is created and maintained in A8.

 

Just wanted to share,

Here is a code sample on how to use CC65 and its strong A8 capabilities to define a display list in just few simple steps:

 

 

Step 1: define the screen memory and display list fixed addresses

#define ScreenMemory		0x3000
#define DisplayListMemory       0x5000 

in this example, the screen memory address is fixed and set to 0x3000

In this example, the display list memory address is fixed and set to 0x5000

 

Step 2: define the display list

void DisplayList =
{
	DL_BLK8,
	DL_BLK8,
	DL_BLK4,
	DL_LMS(DL_CHR40x8x4),
	ScreenMemory,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_CHR40x8x4,
	DL_JVB,
        DisplayListMemory,
	DL_JMP
};

The display list above is pretty basic, it is Antic mode 4 (characters mode).

in line 5 it refers the screen memory address which we have defined in step 1


Step 3: copy the display list into the fixed memory address

memcpy((void*) DisplayListMemory,&DisplayList,sizeof(DisplayList)); 

Step 4: tell ANTIC what memory address holds the display list and we're done

OS.sdlst = 	(void*)DisplayListMemory; 

the sdlst points to the known memory location 560 and 561 (0x230, 0x231).

 

 

 

Hope this was helpful to anyone.

 

cheers,

Yaron

Edited by Yaron Nir
Link to comment
Share on other sites

Possibly a few issues with this?

 

The 'void' declaration should probably be 'unsigned char' / 'uint8_t'

void DisplayList =

When 'ScreenMemory' is used this should be broken into low and high bytes.

<ScreenMemory,
>ScreenMemory,

The ending of the instruction list doesn't make sense

    DL_JVB,
    DL_JMP

a JVB would be followed by the start address of a Dlist so should this have been

    DL_JVB,
    <DisplayListMemory,
    >DisplayListMemory
Link to comment
Share on other sites

Wrathchild, i've edited the post as i noticed it was missing at the end the display list memory address to jump to.

 

as for your comments:

 

 

 

The 'void' declaration should probably be 'unsigned char' / 'uint8_t'

 

well, could be, but i can void also can be used.

 

 

The ending of the instruction list doesn't make sense

DL_JVB,
DL_JMP

 

yes, fixed.

Link to comment
Share on other sites

of course, one is wait on VBLANK and one is jumping back to DL mem address to loop over again.

listen,all i am saying that in the CC65 of this currect display list implementation it is done for you behind the scenes that you don't have to specify it explicirly in the code.

 

i've seen many examples that the addresses were left out....

Link to comment
Share on other sites

DL_JVB isn't solely to wait for a vblank, it also sets the internal dlist pointer to the operand (next two bytes).

Similarly, DL_JMP is a direct instruction to reload the internal pointer with a new address.

 

Hence the DL_JMP left in at the end of your display list is redundant as it would never be reached as the JVB effectively restarts the dlist.

 

DL_JMPs are rarely used, e.g. a dlist shouldn't span a 1K page boundary and so a jump can be used to handle this, but it can be, say, in the case of a display list that has a fixed header area and the lower content can be redirected based on the game state.

Link to comment
Share on other sites

i've seen many examples that the addresses were left out....

 

 

Granted, a JVB on its own can work, however this is a side-effect of having the operating system reload the address from its shadow during the VBI handling:

 

post-1822-0-40444300-1553351238.png

 

This can be seen in Altirra if you load basic, enable the debugger and type "e 9c3e 00 00".

 

So when you write code that, for example, switches out the O/S and provides its own VBI routines, then this would make you responsible for including the same resetting of the h/w values from a shadow.

 

So overall in trying to save 2 bytes out of a dlist the code now needs 10 or 12 bytes of code and a few instruction cycles that h/w is already giving you for free.

 

Therefore I would consider it better practice to include the operands.

  • Like 1
Link to comment
Share on other sites

Hi!

 

Possibly a few issues with this?

 

The 'void' declaration should probably be 'unsigned char' / 'uint8_t'

void DisplayList =

 

 

This is "special" cc65 syntax: it allows you to define data with different types, allowing mixing bytes and words in the same block.

 

When 'ScreenMemory' is used this should be broken into low and high bytes.

<ScreenMemory,
>ScreenMemory,

 

 

This is not needed thanks to the "void" type above, the compiler will do the correct thing.... but, there are other problems with the code:

 

The ending of the instruction list doesn't make sense

    DL_JVB,
    DL_JMP
a JVB would be followed by the start address of a Dlist so should this have been

    DL_JVB,
    <DisplayListMemory,
    >DisplayListMemory

 

 

Yes, the correct code should be:

 

#include <atari.h>

#define ScreenMemory ((unsigned char *)0x3000)

void DisplayList =
{
    DL_BLK8,
    DL_BLK8,
    DL_BLK8,
    DL_LMS(DL_CHR20x8x2),
    ScreenMemory,
    DL_CHR20x8x2,
    DL_CHR20x8x2,
    DL_CHR20x8x2,
    DL_BLK4,
    DL_CHR20x8x2,
    DL_JVB,
    &DisplayList
};
Note two things:

 

- The ScreenMemory define properly sets the type. Without this, the DL won't work with addresses bellow 0x100, as a byte would be inserted instead of a word.

- The JVB jumps to the start of the DL again. This code assumes that the display list will be used as is in memory, not copied to another location. You should assure that the data does not cross a 1k boundary.

  • Like 2
Link to comment
Share on other sites

Thanks dmsc,

 

With a custom cfg file, using the 'atari' executable FILE format, could you demonstrate how to use a dedicated MEMORY/SEGMENT to place a display list inside such that the code would load directly where it was needed rather than having to relocate it?

 

In the old CC65 world I would define something like this in the cfg file:

 

MEMORY {
    ...
    HEADER2: start = $000C, size = $6, file = %O;
    RAM2: start = $0D00, size = $0100, file = %O;
    ...
}
SEGMENTS {
    ...
    H_PAGE_D: load = HEADER2, type = ro;
    PAGE_D: load = RAM2, type = rw, define = yes;
    ...
}

and then in assembler code:

 

.segment "H_PAGE_D"
;
        .word $FFFF
.word __PAGE_D_LOAD__
.word __PAGE_D_LOAD__ + __PAGE_D_SIZE__ - 1

presumably the LOAD/RUN/SIZE defines would be accessible in 'C' via an 'extern'?

Edited by Wrathchild
  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...