Jump to content
  • entries
  • comments
  • views

DPC+ARM - Part 9, Functional Menu




NOTE: This blog series is obsolete.  Head on over to the Harmony/Melody Club where you'll find information on the new Linaro compiler and the new CDFJ coprocessor/bankswitch scheme that has many improvements over DPC+.


NOTE: This is an advanced Atari 2600 programming series. It will not cover basic things like triggering a Vertical Sync, what a Kernel is, why a timer is used in Vertical Blank, etc. If you need to learn that, check out with the following:

  • Collect - detailed development of a 2K game
  • 2600 Programming for Newbies - use the Sorted Table of Contents topic that's pinned at the top in order to easily access the tutorial topics in order.

6507 Revision, Display Data


Storage has been allocated for the C code to use in order to maintain the state of the menu options. This is done because there's a limited amount of space in the C/C-Stack storage pool.

MM_JoystickTimer:   .byte 0MM_FireDown:        .byte 0MM_SelectedOption:  .byte MENU_START_IDMM_Players:         .byte 0 ; 0=1 player, 1=2 playersMM_TVtype:          .byte 0 ; 0=NTSC, 1=PAL, 2=SECAM

A new datastream was added for setting the color the selected menu option. The C code changes this on the fly so the currently selected option will flash.


MenuSelectedColor:        .byte GREY+6, GREY+ 8;  0        .byte GREY+6, GREY+10;  1        .byte GREY+6, GREY+12;  2        .byte GREY+6, GREY+14;  3        .byte GREY+6, GREY+14;  4        .byte GREY+6, GREY+12;  5        .byte GREY+6, GREY+10;  6        .byte GREY+6, GREY+ 8;  7

C Revision, New header file


Joe Musashi posted some handy defines over in the Tasks(s) for you topic. In my prior projects, defines.h contains more than just the output from dasm; however, from working on Collect 2, I've discovered that I really liked having defines.h contain only the dasm echo output as it lets me use Select All then Paste to update the file. In all of my prior projects I had to carefully select just the dasm #define statements before I could paste in the updated values.


So I've revised the project to use 2 header files:

  • defines.h - defines just for the C code, such as the one's Joe created.
  • defines_dasm.h - defines that are output by the echo statements in the 6507 code.

This required a minor change in the Makefile, which is used to control the build process when you type make. The file is located at custom/Makefile, the line changed used to look like this:

main.o : defines.h

it now looks like this:


main.o : defines.h defines_dasm.h

To save time, make is smart enough to only compiles files when necessary1. This line says that main.o, the compiled output of main.c, depends upon defines.h and defines_dasm.h, so if either of those files has a newer date/time stamp than main.o then main.c needs to be recompiled. The compiler already knows that if main.c has a date/time stamp newer than main.o then it needs to be recompiled, so we don't have to specify that.


C Revision, helper functions


  • my_memcpy() - As covered in The Story of Stay Frosty, Part 14, the standard C memcpy() function (memory copy) takes up a lot of space. my_memcpy() is a custom routine that uses 352 fewer bytes than if we'd used the standard C routine.
  • ColorConvert() - Used to change a given NTSC color to the appropriate value based on the selected TV Type. PAL conversion done using values posted by SeaGtGruff.

C Revision, MenuOverscan()


Function MenuOverscan() has been changed so the player can use their joystick to change the options and start the game.


The routine's fairly large, but very straight forward, so I'm not going to post it here. There are plenty of comments to explain how it works though, so be sure to review the code.



C Revision, MenuVerticalBlank()


Function MenuVerticalBlank() has been changed to update the menu datastream so the player sees their selected options. The first thing the routine does is copy the original datastream from Display Data ROM to Display Data RAM:

my_memcpy((unsigned char *)queue + MENU_DATA,              (unsigned char *)ddrom + MENU_DATA,              MENU_DATA_SIZE);

We do this in order to set all the menu options to display their default values, which saves quite a bit of code when there's a lot of options in the menu as we don't need to have code to set anything that's already at the default setting.


Next thing the routine does is update the color values used in the menu options to reflect the current TV Type setting of NTSC, PAL or SECAM:

unsigned char * const MenuColors =(unsigned char *)(0x40000C00 + MENU_COLORS);    for(i=0;i<MENU_COLORS_SIZE;i++)        MenuColors[i] = ColorConvert(MenuColors[i]);

Then it updates any menu option that's been changed from their default value:


unsigned short int * const MenuOptionsDatastream =(unsigned short int *)(0x40000C00 + MENU_DATA);    if (MM_PLAYERS)        MenuOptionsDatastream[MENU_PLAYERS_ID*10 + 0] = OPTION_DIGIT_2;        switch (MM_TV_TYPE)    {   // TV Type: [0]=NTSC, 1=PAL, 2=SECAM        case 1: MenuOptionsDatastream[MENU_TV_TYPE_ID*10 + 0] = OPTION_PAL_B;                MenuOptionsDatastream[MENU_TV_TYPE_ID*10 + 1] = OPTION_PAL_A;                break;        case 2: MenuOptionsDatastream[MENU_TV_TYPE_ID*10 + 0] = OPTION_SECAM_C;                MenuOptionsDatastream[MENU_TV_TYPE_ID*10 + 1] = OPTION_SECAM_B;                MenuOptionsDatastream[MENU_TV_TYPE_ID*10 + 2] = OPTION_SECAM_A;                break;    }

In this we're using a C array of unsigned short integers to update the values in Display Data. This works because the 6507 and ARM are both little-endian processors - so the .word values in the 6507 source code are represented exactly the same way as the short integer values in the C source code.


The formula of OPTION_ID * 10 + offset figures out the correct location within the menu option's datastream to make the change. You can see the offset value in the second column of numbers found in the comments of the datastream:

MENU_TV_TYPE_ID = (* - MenuOptionsDatastream) / 20     .word OptionNTSCB       ; 0  0  first value is unsigned char[] index    .word OptionNTSCA       ; 2  1      not actually used in the ARM code, but    .word TVtypeD           ; 4  2      keeping track of it helps us to make    .word TVtypeC           ; 6  3      sure each menu entry is exactly 20 bytes    .word TVtypeB           ; 8  4  second value is unsigned short int[] index    .word TVtypeA           ; 10 5      this is used in the ARM code to update    .word MenuOptionGreen   ; 12 6      the datastream to show selected values    .byte %11100000         ; 14    PF0 - reversed    .byte %11000000         ; 15    PF1    .byte %00000000         ; 16    PF2 - reversed    .byte 0                 ; 17    ball x location    .byte 7                 ; 18    rows to show       .byte 0                 ; 19    scanline padding before next entry      

So if the TV Type is SECAM the value of OptionNTSCB is replaced with OptionSECAMC, OptionNTSCA is replaced with OptionSECAMB and TVtypeD is replaced with OptionSECAMA. You'll notice that OptionSECAMA has the rightmost pixels of the E in the TV-TYPE graphic.




Option SECAM A-C


The last thing the routine does is revise the datastream so the selected menu option will be visually indicated by using the traditional Atari color cycle effect:


for(i=0;i<16;i+=2)    {        queue[MENU_SELECTED_COLOR + i+1] = gFrame;  // visible on NTSC, PAL and SECAM systems        queue[MENU_SELECTED_COLOR + i+0] = queue[MenuOptionsDatastream[MM_SELECTED_OPTION*10 + 6] + i];    }    MenuOptionsDatastream[MM_SELECTED_OPTION*10 + 6] = MENU_SELECTED_COLOR;

Stella and TV-TYPE


By default, due the scanline count, Stella will recognize Collect2 as an NTSC game. The menu will look like this:


NTSC menu


If you change the TV-TYPE to PAL or SECAM you'll see this:






You can use <CONTROL>-F to cycle between the different TV Types that Stella supports. When changing them, be sure to select PAL60 and SECAM60 instead of PAL and SECAM.






Task for you


Add the ability to change the value of the menu option you added in Part 8.


Upload your changes to the forum topic DPC+ ARM Development - Task(s) for you. Who knows, your new option might become part of this project!


Question - do I need to take a break on this series so y'all can catch up on the Tasks for you?






1 While the time difference is negligible for this small example, the main project for my day job takes over 20 minutes to build just one version (32-bit or 64-bit) if it has to compile the entire project from scratch. Since we support 32-bit and 64-bit, that's over 40 minutes to build them both.



Recommended Comments

There are no comments to display.

Add a comment...

×   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.

  • Recently Browsing   0 members

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