The ARM in the Harmony/Melody can only interact with the Atari thru the 4K cartridge port. As such, it does not have access to TIA, RIOT, etc. The upshot of this is the ARM chip has no way to know if you hit GAME SELECT, moved the joystick left, and so on. The other thing is since the 6507 generates the display by updating TIA, the ARM chip doesn't know if the Vertical Blank or Overscan routines should be run.
When I created the Harmony DPC+ ARM programming example, I'd figured out a way to provide that information to the ARM chip by defining storage in Display Data like this:
;Display Data ORG $6000 RORG $0000 ; NOTE the use of RORG $0000 instead of $F000 ; In this demo, the first set of bytes in Display Data are used to pass values; to the ARM codeARMsub ds 1 ; 0 for init, 1 for Over Scan, 2 for Vertical BlankARMswcha ds 1 ; controller input to ARMARMswchb ds 1 ; console switches to ARMARMcxm0p ds 1 ; pass TIA Read registers to ARMARMcxm1p ds 1ARMcxp0fb ds 1ARMcxp1fb ds 1ARMcxm0fb ds 1ARMcxm1fb ds 1ARMcxblpf ds 1ARMcxppmm ds 1ARMinpt4 ds 1ARMinpt5 ds 1
I then had the 6507 put TIA, RIOT, etc information into Display Data before trigging the ARM code:
; prep for call to OverScan() lda #<ARMsub sta DF0LOW lda #>ARMsub sta DF0HI lda #1 ; 1 = run ARM subroutine OverScan(); sta DF0WRITE ; pass TIA and RIOT registers to the ARM code lda SWCHA ; save joystick state sta DF0WRITE lda SWCHB ; save state of console switches sta DF0WRITE lda CXM0P ; save collision registers sta DF0WRITE lda CXM1P ; ... sta DF0WRITE lda CXP0FB ; ... sta DF0WRITE lda CXP1FB ; ... sta DF0WRITE lda CXM0FB ; ... sta DF0WRITE lda CXM1FB ; ... sta DF0WRITE lda CXBLPF ; ... sta DF0WRITE lda CXPPMM ; ... sta DF0WRITE lda INPT4 ; save joystick 0 firebutton sta DF0WRITE lda INPT5 ; save joystick 1 firebutton sta DF0WRITE lda #$FF sta CALLFUNCTION ; triggers ARM code
In the C code, Display Data was defined as queue[] (I do not know why this name was chosen) so the C code used queue[0x0] to access ARMsub in order to know what should be run:
// main() is what gets called when you store $FF into DPC+ register CALLFUNCTION in your 6507 code.int main(){ // We're using the first byte of Display Data to tell the ARM code which subroutine we wish to run // in the 6507 code this is defined as ARMsub switch (queue[0x0]) { case 0: Initialize(); break; case 1: OverScan(); break; case 2: VerticalBlank(); break; } return 0;}
The other information was accessed like this:
int OverScan(){ int i; unsigned char SWCHA = queue[0x01];... unsigned char INPT4 = queue[0x0b];... // process joystick input if (INPT4 & 0x80) { // fire button not pressed if (!(SWCHA & 0x80)) // joystick right gXrotation++; if (!(SWCHA & 0x40)) // joystick left gXrotation--; if (!(SWCHA & 0x20)) // joystick down gYrotation--; if (!(SWCHA & 0x10)) // joystick up gYrotation++; } else { // fire button pressed if (!(SWCHA & 0x80)) // joystick right gZrotation++; if (!(SWCHA & 0x40)) // joystick left gZrotation--; if (!(SWCHA & 0x20)) // joystick down gScale--; if (!(SWCHA & 0x10)) // joystick up gScale++; }...}
While it worked for the demo it wasn't very efficient, plus keeping track of all those queue[] locations would become difficult as the program increased in size. So next up was to figure out how to easily keep that information in sync between DASM (for the 6507 code) and the C compiler (for the ARM code).
After some thinking I came up with the idea of using DASM's ECHO command. The C code would need to know more than just Display Data information, so the echo commands included things that might not be located in Display Data. The echo commands looked like this:
echo "#define SUB queue[",ARMsub,"]" echo "#define SWCHA queue[",ARMswcha,"]" echo "#define SWCHB queue[",ARMswchb,"]" echo "#define INPT1 queue[",ARMinpt1,"]" echo "#define INPT4 queue[",ARMinpt4,"]" echo "#define ElevatorHM26 ",ElevatorHM26 echo "#define ElevatorHM31 ",ElevatorHM31...
and would output the following when DASM was run:
#define SUB queue[ $0 ] #define SWCHA queue[ $1 ] #define SWCHB queue[ $2 ] #define INPT1 queue[ $3 ] #define INPT4 queue[ $4 ] #define ElevatorHM26 $f7d1 #define ElevatorHM31 $f804 ...
I'd copy to that into a C file - originally main.c, but eventually defines.h. The C code already knew the definition for queue (Display Data RAM), as well as flashdata (the 29K of ROM for the cartridge), by including the file src/custom.h:
volatile unsigned char *queue=(unsigned char *)0x40000C00;volatile unsigned char *flashdata=(unsigned char *)0x0C00;
One minor problem was the C compiler expects 0x for hex values, so I'd have to do a mass find/replace of $/0x to get this, otherwise the C compiler would output a number of error messages:
#define SUB queue[ 0x0 ] #define SWCHA queue[ 0x1 ] #define SWCHB queue[ 0x2 ] #define INPT1 queue[ 0x3 ] #define INPT4 queue[ 0x4 ] #define ElevatorHM26 0xf7d1 #define ElevatorHM31 0xf804 ...
This worked quite well, though for performance and ROM size reasons (to be covered later) those ECHO commands became this:
echo "#define SUB ((unsigned char *)(0x40000C00 +",ARMsub,"))" echo "#define SWCHA ((unsigned char *)(0x40000C00 +",ARMswcha,"))" echo "#define SWCHB ((unsigned char *)(0x40000C00 +",ARMswchb,"))" echo "#define INPT1 ((unsigned char *)(0x40000C00 +",ARMinpt1,"))" echo "#define INPT4 ((unsigned char *)(0x40000C00 +",ARMinpt4,"))" echo "#define ElevatorHM26 ",ElevatorHM26 echo "#define ElevatorHM31 ",ElevatorHM31...
and eventually this, which eliminated the need to do the find/replace of $/0x.
echo "#define SUB ((unsigned char *)(0x40000C00 +",[ARMsub]d,"))" echo "#define SWCHA ((unsigned char *)(0x40000C00 +",[ARMswcha]d,"))" echo "#define SWCHB ((unsigned char *)(0x40000C00 +",[ARMswchb]d,"))" echo "#define INPT1 ((unsigned char *)(0x40000C00 +",[ARMinpt1]d,"))" echo "#define INPT4 ((unsigned char *)(0x40000C00 +",[ARMinpt4]d,"))" echo "#define ElevatorHM26 ",[ElevatorHM26]d echo "#define ElevatorHM31 ",[ElevatorHM31]d...
ROM
Source
NOTE: While the ROM works on the Harmony, it does not work in Stella. It might also roll your TV as I'm generating more than 262 scanlines in order to display time-remaining in Vertical Blank and Overscan (the 15 and 14, respectively) for diagnostic purposes.
Blog entry covers October 13 - 16, 2010
- 1
2 Comments
Recommended Comments