DPC+ARM - Part 7, 6507/ARM Exchange of Information
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: I want to clarify that this is an advanced Atari 2600 programming series. As such, I will not be covering basic things like triggering a Vertical Sync, what a Kernel is, why you need to set a timer for Vertical Blank, etc. If you need to learn that you should 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.
Main Loop
We're going to start off this series with a short example of exchanging information between the 6507 and ARM. The 6507 will send the state of the left joystick to the ARM. The ARM will use that information to manipulate a color value and send it back to the 6507. That color value will be used as the initial color of a typical Atari rainbow screen.
- 6507 reads state of left joystick and saves it in Display Data RAM
- 6507 triggers function main() in the ARM code
- ARM reads joystick state from Display Data RAM
- ARM updates color value in Display Data RAM
- ARM returns control to 6507
- 6507 retrieves color value from Display Data RAM and uses it to generate display
The net result is that manipulating the left joystick will change the rainbow.
Display Data RAM
As mentioned in the previous entry, the 4K of Display Data RAM is used to transfer information between the 6507 and the ARM chip. In this simple program, the first 3 bytes of Display Data RAM are allocated like this:
RORG $0000 DS_ToARM: ARMswcha: ds 1 ; controller state to ARM code ARMinpt4: ds 1 ; firebutton state to ARM code DS_FromARM: ARMcolubk ds 1 ; background color from ARM code
These will be used as 2 data streams - the first consisting of 2 bytes, the second of just 1 byte. The first data stream is used to send data from the 6507 to the ARM. The second data stream is used to send data from the ARM to the 6507.
The RORG is used to set the starting address to 0, so ARMswcha is at address 0, ARMinpt4 is at 1 and ARMcolubk is at 2.
In the C code, which is located in custom directory, the file defines.h is used to define the same RAM locations as seen by the ARM code:
#define SWCHA queue[0] #define INPT4 queue[1] #define COLUBK queue[2]
queue1 is a C array that's define so that the first element is the first byte of Display Data RAM. As such, ARMswcha in the 6507 code refers to the same RAM as SWCHA in the ARM code.
6507 sending data to ARM
The ARM chip does not have access to the hardware within the Atari so the 6507 must obtain information, like the state of the controllers, and save it in Display Data RAM. This is done by pointing a Data Fetcher2 to the appropriate data stream, then writing those values to the stream.
This bit of 6507 code saves the state of the left joystick in ARMswcha and ARMinput4.
ldx #<DS_ToARM stx DF0LOW ldx #>DS_ToARM stx DF0HI ldx SWCHA ; read state of both joysticks stx DF0WRITE ; save in ARMswcha ldx INPT4 ; read state of left joystick firebutton stx DF0WRITE ; save in ARMinpt4
Run ARM code
After the information's been saved to Display Data, the ARM code needs to be run. This is done by writing the value $FF3 to DPC+ register CALLFUNCTION:
ldx #$FF stx CALLFUNCTION ; runs main() in the C code
At this point function main() in your C code will be run.
NOTE: While the ARM code is running, the DPC+ driver will put a NOP on the cartridge port's data lines in order to keep the 6507 idle. During this time the 6507's PC will be advancing thru memory, so you'll want CALLFUNCTION to be done towards the beginning of the cartridge's 4K addressing space. If CALLFUNCTION is triggered towards the end of the 4K space your program will crash if the PC wraps back to address $0000.
ARM sending/reading data from 6507
Unlike the 6507, which must use a data stream, the ARM chip can directly access any value in Display Data RAM. The following function, which is called by main(), will update the background color based on the current joystick state:
void ProcessJoystick() { unsigned char color; unsigned char luma; if (!(INPT4 & 0x80)) // test if firebutton is held { if (gFrame & 0x03) // if so, abort for 3 out of 4 frames return; } // extract current color and luma values for the background color = COLUBK & 0xF0; luma = COLUBK & 0x0F; if (!(SWCHA & 0x80)) // test if joystick held right { color += 0x10; } if (!(SWCHA & 0x40)) // test if joystick held left { color -= 16; } if (!(SWCHA & 0x20)) // test if joystick held down { luma--; } if (!(SWCHA & 0x10)) // test if joystick is held up { luma++; } COLUBK = color | (luma & 0x0f);}
6507 reading data from ARM
Once the ARM code has finished, control will be returned to the 6507. The 6507 will use a data stream to access the information generated by the ARM routines.
ldx #<DS_FromARM stx DF0LOW ldx #>DS_FromARM stx DF0HI ldy DF0DATA ; read value in ARMcolubk, Y is used in Kernel as well sty COLUBK ; set the background color
Task for you
To help you get up to speed, I have a simple task for you - change the program to use the right joystick instead of the left.
After making changes to the C code, you'll compile using these steps:
- Start up your Linux VM
- Open a Terminal session
-
change directory to where the custom code is:
cd /media/sf_Atari/Collect2/custom -
optionally type make clean to remove the prior build
make clean -
type make to compile the code
make
Then you'll just run DASM to compile your 6507 code as normal.
ROM
Source
1 I don't know where the name "queue" came from.
2 The Data Fetcher register names used in DPC+ are a misnomer because they imply read-only even though they can also be used to write data. This is because the names come from DPC (David Crane's custom coprocessor used for Pitfall II) where the only thing you could use them for was to fetch values from Display Data. In DPC+ these registers evolved to also include the ability to write to Display Data.
3 If you're using the DPC+ ability to generate 3-voice music you'll need to write $FE to CALLFUNCTION. This will set up an interrupt routine that causes AUDV0 to be updated once per scan line.
8 Comments
Recommended Comments