DPC+ARM - Part 8, Multiple Functions
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.
Multiple Functions
The code has been revised so the 6507 can now request specific functions for the ARM code to run:
- Initialize() - Run when Atari is first powered up.
- Overscan() - Run during the game screen's overscan. Will eventually update the game state (player movement, sound effects, etc).
- VerticalBlank() - Run during the game screen's vertical blank. Will eventually update the datastreams used to draw the game screen.
- MenuOverscan() - Run during the menu screen's overscan. Will eventually use the joystick to update the options (players 1 or 2; TV Type of NTSC, PAL or SECAM; etc).
- MenuVerticalBlank() - Run during the menu screen's vertical blank. Will eventually manipulate the menu's datastreams in order to display the selected options.
Datastream Revisions
The DS_ToARM datastream has had a few revisions:
DS_ToARM:ARMfunction: ds 1 ; which function to runARMswcha: ds 1 ; controller stateARMswchb: ds 1 ; state of console switchesARMinpt4: ds 1 ; state of left joystick's fire buttonARMinpt5: ds 1 ; state of right joystick's fire button; this is part of DS_ToARM, but is actually used as "from the ARM to the 6507"ARMmode: ds 1 ; tells 6507 to show menu or game screen
It now starts with a byte allocated for function - the 6507 code sets this to tell the ARM code which function to run.
The state of the right joystick's firebutton is now being saved.
Lastly mode, a return value, has been added to the end.
A new datastream has also been added. It's quite large, so I'm going to show just a snippet of it here:
MenuOptionsDatastream: .word Collect2LogoF ; 0 0 first value is unsigned char[] index .word Collect2LogoE ; 2 1 not actually used in the ARM code, but .word Collect2LogoD ; 4 2 keeping track of it helps us to make .word Collect2LogoC ; 6 3 sure each menu entry is exactly 20 bytes .word Collect2LogoB ; 8 4 second value is unsigned short int[] index .word Collect2LogoA ; 10 5 this is used in the ARM code to update .word Collect2LogoColor ; 12 6 the datastream to show selected values .byte %00000000 ; 14 PF0 - reversed .byte %01000000 ; 15 PF1 .byte %00000000 ; 16 PF2 - reversed .byte 100 ; 17 ball x location .byte 25 ; 18 rows to show .byte MM_TITLE_GAP ; 19 scanline padding before next entry ... MENU_START_ID = (* - MenuOptionsDatastream) / 20 .word StartA ; 0 0 first value is unsigned char[] index .word StartE ; 2 1 not actually used in the ARM code, but .word StartD ; 4 2 keeping track of it helps us to make .word StartC ; 6 3 sure each menu entry is exactly 20 bytes .word StartB ; 8 4 second value is unsigned short int[] index .word StartA ; 10 5 this is used in the ARM code to update .word StartColor ; 12 6 the datastream to show selected values .byte %00000000 ; 14 PF0 - reversed .byte %11000000 ; 15 PF1 .byte %00001100 ; 16 PF2 - reversed .byte 0 ; 17 ball x location .byte 8 ; 18 rows to show .byte $80 ; 19 flags end of menu
As you've probably surmised, this datastream controls the display of the main menu.
6507 Revisions, Fast Fetch mode
The InitSystem routine has been modified to turn on Fast Fetch mode:
ldx #0 stx FASTFETCH
What this does is let you change code like this:
lda DF0DATA ; 4 cycle instruction
To this:
lda #<DF0DATA ; 2 cycle instruction
That 2 cycle savings is crucial for DPC+ kernels. There is a tradeoff though, you can no longer use lda # except when reading a DPC+ register. If this proves to be a problem, you could change the code to turn on Fast Fetch mode at the start of your kernel and turn it off at the end - though I don't recommend it as you'll have to be intimately aware of when you can and cannot use lda #.
6507 Revisions, CallArmCode
The 6507 code can trigger the execution of ARM code at 5 different places. Because CALLFUNCTION should be written towards the start of the cartridge ROM's addressing space, a new subroutine has been added at address $F080. This is the earliest it could exist as addresses $F000 - $F07F are the DPC+ registers.
CallArmCode: ldx #<DS_ToARM stx DF0LOW ldx #>DS_ToARM stx DF0HI sty DF0WRITE ; Y holds which function to call ldx SWCHA ; read state of both joysticks stx DF0WRITE ; save in ARMswcha ldx SWCHB ; read state of console switches stx DF0WRITE ; save in ARMswchb ldx INPT4 ; read state of left joystick firebutton stx DF0WRITE ; save in ARMinpt4 ldx INPT5 ; read state of right joystick firebutton stx DF0WRITE ; save in ARMinpt5 ldx #$FF stx CALLFUNCTION ; runs main() in the C code rts
Before calling this routine, your code must specify which function to run by setting the Y value. There are 5 constants used for this:
ARM_INIT = 0ARM_OS = 1ARM_VB = 2ARM_MENU_OS = 3ARM_MENU_VB = 4
An example of calling Initialize():
ldy #ARM_INIT jsr CallArmCode ; run Initialize() in the ARM code
6507 Revisions, Main Menu
Routines have been added to display the main menu. I'm not going to go into detail here, but will point out a things for you to review in the source code:
- There's 4 new constants that control the spacing of the menu elements. Try changing them to see what they do.
- There's 8 new constants that define aliases for the DFxDATA registers. This make it easier to read the 6507 code and understand what it's doing.
- New subroutine ShowTwoColorGraphic. This subroutine is the same as used in Stay Frosty 2 and Space Rocks to show a two color 48 pixel image, though it has been modified to use the new aliases for DFxDATA.
- Search for the label KernelMenu: to find the main menu kernel.
6507 Revisions, Two Banks
6507 code has been revised to use bank 4 to draw the menu and bank 5 to draw the game screen. During Overscan processing, the ARM routines will change the value of mode, which is used to determine which mode the game is currently in. Mode 0 is active game, while mode 1 is at the menu. Additional modes can be added, such as one to show a high score screen or another to show an easter egg. After the ARM overscan routines are done, the 6507 code will check the value in mode to decide if it needs to switch to the other bank.
At this time the game screen is the same joystick controlled rainbow screen from Step 7.
The menu screen shows a menu, but it's just a static display at this point as the code is not in place to let you change the options.
6507 Revisions, Echo statements
In the prior build we had to manually keep track of Display Data usage for the ARM code. To make this easier to do, echo statements have been added to the end of the 6507 code - a subset of which are shown below:
echo "// functions to run" echo "#define ARM_INIT ", [ARM_INIT]d echo "#define ARM_OS ", [ARM_OS]d echo "#define ARM_VB ", [ARM_VB]d echo "#define ARM_MENU_OS ", [ARM_MENU_OS]d echo "#define ARM_MENU_VB ", [ARM_MENU_VB]d echo "" echo "// datastream DS_ToARM" echo "#define FUNCTION queue[", [ARMfunction]d, "]" echo "#define SWCHA queue[", [ARMswcha]d, "]" echo "#define SWCHB queue[", [ARMswchb]d, "]" echo "#define INPT4 queue[", [ARMinpt4]d, "]" echo "#define INPT5 queue[", [ARMinpt5]d, "]" echo "#define MODE queue[", [ARMmode]d, "]" echo ""
These echo statements will output to the terminal/console when the 6507 code is compiled with dasm:
// functions to run #define ARM_INIT 0 #define ARM_OS 1 #define ARM_VB 2 #define ARM_MENU_OS 3 #define ARM_MENU_VB 4 // datastream DS_ToARM #define FUNCTION queue[ 0 ] #define SWCHA queue[ 1 ] #define SWCHB queue[ 2 ] #define INPT4 queue[ 3 ] #define INPT5 queue[ 4 ] #define MODE queue[ 5 ]
By default dasm will output the values in hexadecimal. In and of itself that's not a problem; however, dasm uses the prefix of $ to denote hexadecimal values while C uses 0x.
We could do a find/replace of $ for 0x - instead we'll just ask dasm to output the values in decimal, which does not use a prefix. This is done by using the brackets [ ] and the d.
If you make any revisions to the 6507 code that changes any of those values you'll need to manually copy the output and paste it into the defines.h file.
C Revisions
Function main() now uses the value in FUNCTION to select which function to run:
switch (FUNCTION) // 6507 code sets the value of FUNCTION { case ARM_INIT: Initialize(); break; case ARM_OS: Overscan(); break; case ARM_VB: VerticalBlank(); break; case ARM_MENU_OS: MenuOverscan(); break; case ARM_MENU_VB: MenuVerticalBlank(); break; }
The number of functions the 6507 can call can easily be expanded - for instance Space Rocks adds the functions EasterEggOverscan() and EasterEggVerticalBlank().
Right now these functions are extremely simple, so I won't be going into detail about them - but please review the code so you understand the what and why of it. Post any questions in the comments.
Task for you
Think up a new menu option and add it between Players and TV Type. Your new option should use the same scanline padding constant as the Players' option.
Don't forget to adjust the menu padding constants so that the Atari continues to output 262 scanlines. To check this use the frame stats feature in Stella - you can toggle that by pressing the keys COMMAND + L on the Mac, or ALT + L on Linux and Windows. You can learn more about this, and other Developer Keys, in Stella's help file. You can also access your local copy of the help file via Stella's Help menu.
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!
ROM
Source
- 1
8 Comments
Recommended Comments