Yaron Nir Posted September 24, 2018 Share Posted September 24, 2018 Hi All, i am trying to create antic mod 4 (graphics 12) using display list in CC65. i want to use a "fixed" memory location for screen memory, as i DO NOT want to use the linker config option. asi understood the DL can't cross the 1K boundary. looking at this example: https://github.com/cc65/cc65/blob/master/testcode/lib/atari/displaylist.c i see that the screen memory is being defined locally as an array. unsigned char DummyScreen[400]; and then it is used as a parameter to the DL: void DisplayList = { DL_BLK1, DL_BLK2, DL_BLK3, DL_BLK4, DL_BLK5, DL_BLK6, DL_BLK7, DL_DLI(DL_BLK8), DL_LMS(DL_CHR40x8x1), DummyScreen, DL_HSCROL(DL_CHR40x10 .... and then poke it into location 560: POKEW(560,(unsigned int)&dlStartScreen); my question is , if i want to change the screen memory to be fixed for example i want my DL to start at address 0x8000. what do i need to change? am i doing this right? i tried changing to work with fixed address, but then i saw strange behaviors like random characters appearing on screen with no reasn and PMG was erased.... help? Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted September 24, 2018 Share Posted September 24, 2018 i want my DL to start at address 0x8000. How are you protecting the compiler suite from not using that area of memory for something else? Generally, the custom linker config is the way-to-go. An alternative approach has been to protect your dlist (and or screen memory) from crossing a page boundary by allocating a larger block and splicing out what you need, see here. Additionally, if you are happy the area is 'free' then you could simply memcpy the dlist from your program area to the target area. Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 24, 2018 Author Share Posted September 24, 2018 Additionally, if you are happy the area is 'free' then you could simply memcpy the dlist from your program area to the target area. weathchild, thanks for your reply. can you share an example for the above quote? thanks Quote Link to comment Share on other sites More sharing options...
funkheld Posted September 24, 2018 Share Posted September 24, 2018 (edited) char dl[] = {112,112,112,66,0,0x20,............displaylist memory starts at $ 2000 this is the test for $2000: for(i=0; i<128; ++i) { POKE(0x2000+i,i); } greeting #include <peekpoke.h> #include <atari.h> #include <stdio.h> #include <stdlib.h> #include <conio.h> #define SAVMSC *(unsigned int *) 88 // Screen address #define NMIEN *(unsigned char *) 0xD40E // NMI enable #define SDMCTL *(unsigned char *) 559 // Antic DMA control shadow #define SDLSTL *(unsigned int *) 560 // Display list start shadow #define VDSLST *(unsigned int *) 0x200 // Display list interrupt vector char dl[] = {112,112,112,66,0,0x20,130,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,65,0,0}; void main(void) { unsigned char i; dl[sizeof(dl)-2] = ((unsigned) &dl) % 256; dl[sizeof(dl)-1] = ((unsigned) &dl) / 256; SDMCTL = 0; // turn off antic SDLSTL = (unsigned int) &dl; // new dlist address SDMCTL = 34; // turn on antic for(i=0; i<128; ++i) { POKE(0x2000+i,i); } while(1); return; } Edited September 24, 2018 by funkheld Quote Link to comment Share on other sites More sharing options...
funkheld Posted September 24, 2018 Share Posted September 24, 2018 (edited) STARTADDRESS: default = $3000 >>>>> >is startaddress size = $CFFF - __STACKSIZE__ - __RESERVED_MEMORY__ - %S; >>>>> size with $CFFF I own only the memory and must also manage it FEATURES { STARTADDRESS: default = $3000; } SYMBOLS { __EXEHDR__: type = import; __SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk __AUTOSTART__: type = import; # force inclusion of autostart "trailer" __STACKSIZE__: type = weak, value = $0800; # 2k stack __STARTADDRESS__: type = export, value = %S; __RESERVED_MEMORY__: type = weak, value = $0000; } MEMORY { ZP: file = "", define = yes, start = $0082, size = $007E; # file header, just $FFFF HEADER: file = %O, start = $0000, size = $0002; # "system check" load chunk SYSCHKHDR: file = %O, start = $0000, size = $0004; SYSCHKCHNK: file = %O, start = $2E00, size = $0300; SYSCHKTRL: file = %O, start = $0000, size = $0006; # "main program" load chunk MAINHDR: file = %O, start = $0000, size = $0004; MAIN: file = %O, define = yes, start = %S, size = $CFFF - __STACKSIZE__ - __RESERVED_MEMORY__ - %S; TRAILER: file = %O, start = $0000, size = $0006; } SEGMENTS { ZEROPAGE: load = ZP, type = zp; EXTZP: load = ZP, type = zp, optional = yes; EXEHDR: load = HEADER, type = ro; SYSCHKHDR: load = SYSCHKHDR, type = ro, optional = yes; SYSCHK: load = SYSCHKCHNK, type = rw, define = yes, optional = yes; SYSCHKTRL: load = SYSCHKTRL, type = ro, optional = yes; MAINHDR: load = MAINHDR, type = ro; STARTUP: load = MAIN, type = ro, define = yes; LOWBSS: load = MAIN, type = rw, optional = yes; # not zero initialized LOWCODE: load = MAIN, type = ro, define = yes, optional = yes; ONCE: load = MAIN, type = ro, optional = yes; CODE: load = MAIN, type = ro, define = yes; RODATA: load = MAIN, type = ro; DATA: load = MAIN, type = rw; INIT: load = MAIN, type = rw, optional = yes; BSS: load = MAIN, type = bss, define = yes; AUTOSTRT: load = TRAILER, type = ro; } FEATURES { CONDES: type = constructor, label = __CONSTRUCTOR_TABLE__, count = __CONSTRUCTOR_COUNT__, segment = ONCE; CONDES: type = destructor, label = __DESTRUCTOR_TABLE__, count = __DESTRUCTOR_COUNT__, segment = RODATA; CONDES: type = interruptor, label = __INTERRUPTOR_TABLE__, count = __INTERRUPTOR_COUNT__, segment = RODATA, import = __CALLIRQ__; } Edited September 24, 2018 by funkheld Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted September 24, 2018 Share Posted September 24, 2018 char dl[] = {112,112,112,66,0,0x20,130,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,65,0,0}; This is exactly what the OP is getting at... as your program changes, what guarantees that this display dlist isn't going to span a boundary? #include <string.h> #define SDMCTL *((unsigned char *) 559) // Antic DMA control shadow #define SDLSTL *((unsigned int *) 560) // Display list start shadow #define MY_DLIST 0x8000 #define MY_SCREEN 0x8100 char dl[] = {0x70,0x70,0x70,0x42,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0x41,0,0}; void main(void) { unsigned char i; dl[4] = MY_SCREEN % 256; dl[5] = MY_SCREEN / 256; dl[sizeof(dl)-2] = MY_DLIST % 256; dl[sizeof(dl)-1] = MY_DLIST / 256; SDMCTL = 0; // turn off antic - technically it would be good to wait for a vblank to effect the SDMCTL->DMACTL memcpy((void *)MY_DLIST, dl, sizeof(dl)); for(i=0; i<128; ++i) { *((unsigned char *)MY_SCREEN+i) = i; } SDLSTL = (unsigned int) &dl; // new dlist address SDMCTL = 34; // turn on antic while(1); return; } Quote Link to comment Share on other sites More sharing options...
danwinslow Posted September 24, 2018 Share Posted September 24, 2018 (edited) I've always seen people allocating extra size and at runtime adjusting the 'start' pointer via math that ensures a good boundary. Or, of course you can set aside a segment in the config, but OP said he does not want to do that. Edited September 24, 2018 by danwinslow Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 24, 2018 Author Share Posted September 24, 2018 sooo, using the reference code by wrathchild did the trick. so basically, to whom it may concern, these are the steps to use a display list in CC65 WITHOUT using the cfg config linker file: 1. define 2 fixed addresses: 1 for display list, 1 for screen memory example: unsigned int txtDisplayList = (unsigned int)0x8000; unsigned int screenMemory = (unsigned int)0x8100; 2. declare a char array of display list example: char dlStartScreen[] = { 112, // BLANK 112, // BLANK 112, // BLANK 66, // tell the address of screen memory, 2 following zeros will be replaced by actual address 0, 0, 4, // antic mode 4 char mod multicolor x 21 lines 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 65, // jmp to DL address, , 2 following zeros will be replaced by actual address 0, 0 }; 3. set the zero with the proper addresses, one for screen memory, other for jump display list. example: // set the screen memory address in the DL array to replace the 2 zeros at pos 4,5 dlStartScreen[4] = screenMemory % 256; dlStartScreen[5] = screenMemory / 256; // set the jump address in the DL array to replace the 2 zeros at pos last-1,last dlStartScreen[sizeof(dlStartScreen)-2] = txtDisplayList % 256; dlStartScreen[sizeof(dlStartScreen)-1] = txtDisplayList / 256; 4. copy the array of display list into the fixed memory address example: memcpy((void *)txtDisplayList, dlStartScreen, sizeof(dlStartScreen)); 5. set the display list POKE(560,(unsigned int) &dlStartScreen); you're all set thanks all, and i hope this small example will help someone.... Quote Link to comment Share on other sites More sharing options...
sanny Posted September 24, 2018 Share Posted September 24, 2018 Please explain, exactly "why" you don't want to use a changed linker config file. I'm just curious. regards, chris Quote Link to comment Share on other sites More sharing options...
funkheld Posted September 24, 2018 Share Posted September 24, 2018 Whenever does he have to change the cfg. he will probably also make graphics with pm. greeting Quote Link to comment Share on other sites More sharing options...
sanny Posted September 24, 2018 Share Posted September 24, 2018 (edited) Yaron's example to use fixed addresses of 0x8000 and 0x8100 might work at first glance. But the runtime probably uses this memory, too, unless one adapts it with __RESERVED_MEMORY__. So, depending on program size, and when during the execution the DL and display memory are being set up, the program can crash, or you get strange screen output (if heap or stack enters the 0x8000... area). regards, chris Edited September 24, 2018 by sanny Quote Link to comment Share on other sites More sharing options...
baktra Posted September 24, 2018 Share Posted September 24, 2018 Linker.cfg is better option. No memory is wasted for original copy of the DL. LD65 also handles proper alignment. And overall memory layout can be adjusted to ensure no interference between the program and data. Good option is to define the DL in assembler file and dedicated segment while benefitting from separate compilation. Quote Link to comment Share on other sites More sharing options...
Irgendwer Posted September 24, 2018 Share Posted September 24, 2018 Linker.cfg is better option. No memory is wasted for original copy of the DL. LD65 also handles proper alignment. And overall memory layout can be adjusted to ensure no interference between the program and data. Good option is to define the DL in assembler file and dedicated segment while benefitting from separate compilation. Sure, but especially for beginners the config is a riddle and appears unhandy if you also have to provide loader segments for each individual data block. (Thinking about a config generator which provides automatically memory and segments + header defined by a simpler syntax...?) Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 24, 2018 Author Share Posted September 24, 2018 Please explain, exactly "why" you don't want to use a changed linker config file. I'm just curious. regards, chris as mentioned above, the cfg file is too complicated for me to understand now. as for the asm suggestion. well, my current project is pure CC65 (c lang) and i am trying to prove something (atleast to myself ) so, no linkers, no asms i want to be able to do what i need in pure CC65 hope that make sense.... Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 24, 2018 Author Share Posted September 24, 2018 Yaron's example to use fixed addresses of 0x8000 and 0x8100 might work at first glance. But the runtime probably uses this memory, too, unless one adapts it with __RESERVED_MEMORY__. So, depending on program size, and when during the execution the DL and display memory are being set up, the program can crash, or you get strange screen output (if heap or stack enters the 0x8000... area). regards, chris i a curious, how can one use __RESERVED_MEMORY__ in CC65 code? can you post an example? Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 24, 2018 Author Share Posted September 24, 2018 Linker.cfg is better option. No memory is wasted for original copy of the DL. LD65 also handles proper alignment. And overall memory layout can be adjusted to ensure no interference between the program and data. Good option is to define the DL in assembler file and dedicated segment while benefitting from separate compilation. soo, this is a cfg file i've downloaded from one of the games posted here on AA called 'Viper' (a snake clone): FEATURES { STARTADDRESS: default = $3900; } SYMBOLS { __STACKSIZE__ = $100; # small stack __RESERVED_MEMORY__: value = $1, weak = yes; } MEMORY { ZP: start = $0082, size = $007E, type = rw, define = yes; HEADER: start = $0000, size = $0006, file = %O; RAM: start = %S, size = $BC20 - __STACKSIZE__ - %S, file = %O; TRAILER: start = $0000, size = $0006, file = %O; } SEGMENTS { EXEHDR: load = HEADER, type = ro; LOWCODE: load = RAM, type = ro, define = yes, optional = yes; INIT: load = RAM, type = ro, optional = yes; CODE: load = RAM, type = ro, define = yes; RODATA: load = RAM, type = ro; FONT: load = RAM, type = rw, align=$1000, define=yes; DATA: load = RAM, type = rw, align=$200, define=yes; BSS: load = RAM, type = bss, define = yes; HEAP: load = RAM, type = bss, optional = yes; # must sit just below stack ZEROPAGE: load = ZP, type = zp; EXTZP: load = ZP, type = zp, optional = yes; AUTOSTRT: load = TRAILER, type = ro; } FEATURES { CONDES: segment = INIT, type = constructor, label = __CONSTRUCTOR_TABLE__, count = __CONSTRUCTOR_COUNT__; CONDES: segment = RODATA, type = destructor, label = __DESTRUCTOR_TABLE__, count = __DESTRUCTOR_COUNT__; CONDES: type = interruptor, segment = RODATA, label = __INTERRUPTOR_TABLE__, count = __INTERRUPTOR_COUNT__; if someone could tell me what the hell is this? and what do i need to change line by line, that could be a good starting point.... } Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 24, 2018 Author Share Posted September 24, 2018 i only see 2 lines that make sense that needs to be changed, other leave "as is": FONT: load = RAM, type = rw, align=$1000, define=yes; DATA: load = RAM, type = rw, align=$200, define=yes; while i looked at the code i saw a segment was defined using the #pragma directive. i just need to understand how to translate it to the example i have posted above.... can any help? step by step? Quote Link to comment Share on other sites More sharing options...
funkheld Posted September 24, 2018 Share Posted September 24, 2018 (edited) STARTADDRESS: default = $ 3000 size = $ CFFF - __STACKSIZE__ - __RESERVED_MEMORY__ -% S; is safe with cc65. and you own the world in programming greeting Edited September 24, 2018 by funkheld Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 24, 2018 Author Share Posted September 24, 2018 STARTADDRESS: default = $ 3000 size = $ CFFF - __STACKSIZE__ - __RESERVED_MEMORY__ -% S; is safe with cc65. and you own the world in programming greeting yes, but you haven't mentioned what do i need to change in order to place the display list in a fixed memory within the cfg file.....? Quote Link to comment Share on other sites More sharing options...
sanny Posted September 24, 2018 Share Posted September 24, 2018 Sure, but especially for beginners the config is a riddle and appears unhandy if you also have to provide loader segments for each individual data block. Yes, it requires some learning. But if one knows what memory is, what addresses are, and so, it's no rocket science. See https://cc65.github.io/doc/ld65.html#s5for documentation. Quote Link to comment Share on other sites More sharing options...
sanny Posted September 24, 2018 Share Posted September 24, 2018 soo, this is a cfg file i've downloaded from one of the games posted here on AA called 'Viper' (a snake clone): Better start with the one you are currently using, the default atari.cfg of cc65 (https://github.com/cc65/cc65/blob/master/cfg/atari.cfg). And maybe look at the other cfg files in this directory for "inspiration". Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 24, 2018 Author Share Posted September 24, 2018 Better start with the one you are currently using, the default atari.cfg of cc65 (https://github.com/cc65/cc65/blob/master/cfg/atari.cfg). And maybe look at the other cfg files in this directory for "inspiration". thanks chris, both links are helpful. let's say i use the default atari.cfg from your link, can you point me to where i need to "stash" my display list from the above posted code ? Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted September 24, 2018 Author Share Posted September 24, 2018 STARTADDRESS: default = $ 3000 size = $ CFFF - __STACKSIZE__ - __RESERVED_MEMORY__ -% S; is safe with cc65. and you own the world in programming greeting will try this.. do i need to do anything with the %s? Quote Link to comment Share on other sites More sharing options...
sanny Posted September 24, 2018 Share Posted September 24, 2018 STARTADDRESS: default = $ 3000 size = $ CFFF - __STACKSIZE__ - __RESERVED_MEMORY__ -% S; is safe with cc65. and you own the world in programming I'm wondering how you handle the $C000..$CFFF area? Are you turning off the ROM? Quote Link to comment Share on other sites More sharing options...
sanny Posted September 24, 2018 Share Posted September 24, 2018 %S will be replaced by the value of STARTADDRESS, which can be overridden in the compiler command line (--start-addr 0xXXXX) Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.