+SpiceWare Posted December 15, 2019 Share Posted December 15, 2019 Source Code Download and unzip this in your shared directory. Collect3_20191215.zip ROM for reference collect3_20191215.bin Main files to look at: cdfj.j - constants for CDFJ used by the 6507 assembly code collect3.asm - 6507 assembly code main/defines_cdfj.h - constants and functions for CDFJ used by the C code main/main.c - C code Build To build the program open the Konsole and: use cd command to change into the Collect 3 source directory type make Test program Launch Stella and run the collect3.bin. You see a splash screen for a couple seconds: Followed by a Menu screen: Hit GAME RESET to start a game. Both players are moveable. hit GAME SELECT to return to menu. Main Loop Overscan 6507 saves state joysticks and console switches in Display Data RAM 6507 code calls ARM code requesting the appropriate OverScan routine to run based on MODE (0=Splash, 1=Menu, 128=Game) ARM retrieves values from Display Data RAM and takes appropriate action ARM returns updated MODE value Vertical Sync 6507 triggers vertical sync ARM - nothing Vertical Blank 6507 code calls ARM code requesting the appropriate Vertical Blank routine to run based on MODE (0=Splash, 1=Menu, 128=Game) ARM preps datastreams with what to display for the current frame ARM returns initial X positions for the players, missiles, and ball 6507 positions the players, missiles, and ball 6507 does additional prep based on value in MODE Kernel 6507 runs appropriate kernel routine based on value in MODE ARM acts as a coprocessor, feeding values to the 6507 via the Data Streams back to Overscan Display Data RAM The 4K of Display Data is used to transfer information between the 6507 and ARM chip. The usage is defined in the 6507 assembly file in the same way that Zero Page RAM is defined: SEG.U DISPLAYDATA ORG $0000 _DS_TO_ARM: _RUN_FUNC: ds 1 ; function to run _SWCHA: ds 1 ; joystick directions to ARM code _SWCHB: ds 1 ; console switches to ARM code _INPT4: ds 1 ; left firebutton state to ARM code _INPT5: ds 1 ; right firebutton state to ARM code _DS_FROM_ARM: ; ARM OverScan routines return value for MODE _MODE: ; $00 = splash, $01 = menu, $80 = game _BALL_X: ds 1 ; ARM VerticalBLank routines do not return MODE, instead _M1_X: ds 1 ; theyreturn values for the 5 X positions _M0_X: ds 1 _P1_X: ds 1 _P0_X: ds 1 ; Splash screen datastreams _SPLASH0: ds 192 _SPLASH1: ds 192 _SPLASH2: ds 192 _SPLASH3: ds 192 ; Menu datastreams _MENU0: ds 192 _MENU1: ds 192 ; Game datastreams _PLAYER0: ds 192 _PLAYER1: ds 192 _COLOR0: ds 192 _COLOR1: ds 192 The make process will automatically transfer labels prefixed with _ to the C code. In the C code these values are accessed using the variable RAM. As an example this bit of code from SplashVerticalBlank() sets the X positions of the players: // set the X positions of the players RAM[_P0_X] = 64; RAM[_P1_X] = 72; 6507 sending data to ARM The ARM chip does not have access to the hardware within the Atari, so the 6507 must obtain things like the state of the controllers and save it in Display Data RAM. This is done by pointing the Communication Data Stream to _DS_TO_ARM, then writing those values to the stream. This bit of code sets the pointer and populates _RUN_FUNC, _SWCHA, _SWCHB, _INPT4, and _INPT5. ldx #<_DS_TO_ARM stx DSPTR ldx #>_DS_TO_ARM ; NOTE: _DS_TO_ARM = 0, so we could leave this LDX out stx DSPTR sty DSWRITE ; save in _RUN_FUNC, Y holds which function to call ldx SWCHA ; read state of both joysticks stx DSWRITE ; save in _SWCHA ldx SWCHB ; read state of console switches stx DSWRITE ; save in _SWCHB ldx INPT4 ; read state of left joystick firebutton stx DSWRITE ; save in _INPT4 ldx INPT5 ; read state of right joystick firebutton stx DSWRITE ; save in _INPT5 NOTE: In collect3.asm the "save in" comments use incorrect names such as ARMswcha instead of _SWCHA. Those were left over from before we figured out how to use _ to automatically transfer values from dasm to the C code. Run ARM code After the information has been saved to Display Data, the ARM code needs to run. This is done by writing $FF to CALLFN. ldx #$FF ; FF = Run ARM code w/out digital audio interrupts stx CALLFN ; runs main() in the C code at this point function main() in the C code will run. It uses the value stored in _RUN_FUNC to determine which C routine to run: int main() { // main() is called when the 6507 code writes to CALLFUNCTION switch(RAM[_RUN_FUNC]) { case _FN_INIT: Initialize(); break; case _FN_GAME_OS: GameOverScan(); break; case _FN_GAME_VB: GameVerticalBlank(); break; case _FN_MENU_OS: MenuOverScan(); break; case _FN_MENU_VB: MenuVerticalBlank(); break; case _FN_SPLASH_OS: SplashOverScan(); break; case _FN_SPLASH_VB: SplashVerticalBlank(); break; } return 0; } Prepare datastreams for 6507 Before the ARM returns control to the 6507 it needs to prepare the datastream pointers and increments for the 6507. This is done by using these functions: setIncrement(stream, whole, frac) setPointer(stream, offset) setPointer(stream, offset, frac) the parameters are: stream - 0-31 for the 32 datastreams, 32 for the Communication Datastream, or 33-34 for the Jump Datastreams whole - the whole value of the increment. If you're incrementing 1.5 the whole value is 1 frac - the fraction part of the increment. value is in a byte so for .25 = 64, .5 = 128, .75 = 192 offset - location within the 4K Display Data. 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. Reading from the datastreams makes use of Fast Fetch mode, which overrides the LDA # immediate mode instruction. Fast Fetch mode is turned on by storing FASTON to SETMODE: ldx #FASTON stx SETMODE In Collect this is turned on during InitSystem and never turned off. If you ever need to turn it off store FASTOFF to SETMODE. Collect3 uses this bit of code to read from DSCOMM, the communication datastream, to set the initial X position of TIA's 5 moveable objects: ; ARM VB routines send back the initial positions of the 5 objects ldx #4 vbSetInitialX: lda #DSCOMM ; will get _BALL_X, _M1_X, _M0_X, _P1_X, and _P0_X jsr PosObject dex bpl vbSetInitialX sta WSYNC sta HMOVE The other datastreams are named DS0DATA thru DS31DATA, though I prefer to define constants to make the code easier to read: ; datastream usage for Game _DS_GRP0 = DS0DATA _DS_GRP1 = DS1DATA _DS_COLUP0 = DS2DATA _DS_COLUP1 = DS3DATA ... ldy #192 GameKernel: sta WSYNC lda #_DS_GRP0 ; 2 2 values from datastream pointing at _PLAYER0 sta GRP0 ; 3 5 lda #_DS_GRP1 ; 2 7 values from datastream pointing at _PLAYER1 sta GRP1 ; 3 10 lda #_DS_COLUP0 ; 2 12 values from datastream pointing at _COLOR0 sta COLUP0 ; 3 15 lda #_DS_COLUP1 ; 2 17 values from datastream pointing at _COLOR1 sta COLUP1 ; 3 20 dey bne GameKernel 1 Link to comment Share on other sites More sharing options...
+SpiceWare Posted December 15, 2019 Author Share Posted December 15, 2019 If you're using jEdit I've made an update to the mode files that includes some changes for CDFJ. Link to comment Share on other sites More sharing options...
Dionoid Posted December 15, 2019 Share Posted December 15, 2019 Hi Darrell, this contains a lot of interesting things! I will try to get the example code running on my machine this week. A quick question: when (re)setting the pointers for the data streams, why is the offset shifted by 20? Link to comment Share on other sites More sharing options...
+SpiceWare Posted December 15, 2019 Author Share Posted December 15, 2019 On 12/15/2019 at 1:47 PM, Dionoid said: when (re)setting the pointers for the data streams, why is the offset shifted by 20? Great question! For ARM performance reasons the various CDFJ registers are stored as 32 bit values. The ARM has an inline barrel shifter which works efficiently with a number of operations. Quote Operand 2 is sent to the ALU by barrel shifter Amount to shift is contained in 5 bit instruction field No overhead, shift is done free in one cycle Datastreams can be set to any address within the 4K Display Data block, or 2^12 address. Since the barrel shifters are so efficient, the address is stored in the upper 12 bits of the value and the next 8 bits are the fractional part. The 12 bits below that could be used as fractional as well, but the C functions aren't set up for that so you'd have to set the values yourself. Likewise with the increments, uppermost 12 bits are the whole number, next 8 for fraction. What this does is provide automatic wrapping from 4095 to 0 when the increment gets added to the address. Without the shifting we'd need to use an AND instruction to force the wrap, which would use additional CPU time. 2 Link to comment Share on other sites More sharing options...
flickertail Posted July 14, 2020 Share Posted July 14, 2020 Darrell, I'm a bit confused on how the 6502 passes data to the ARM processor. CDFJ seems to recognize registers such as SWCHA and INPUT4 - etc. when identified like so: _SWCHA: ds 1 ; joystick directions to ARM code .. but what if I want to define a variable in 6507 ASM code, increment the variable value throughout the Kernel, and then make the final value available to the ARM processor in overscan? With the understanding that I would still need to include it in the "CallArmCode:" routine in order to pass it to the ARM, would still I declare the variable in the same location as _SWCHA in the same manner like so: _MYVAR: ds 1 ;a variable that is not a register ...could I then use that variable as I normally would in a pure ASM program, like so: ldx #5 stx _MYVAR inc _MYVAR Link to comment Share on other sites More sharing options...
+SpiceWare Posted July 14, 2020 Author Share Posted July 14, 2020 8 hours ago, flickertail said: Darrell, I'm a bit confused on how the 6502 passes data to the ARM processor. You're getting ahead of the tutorial Display Data RAM is not mapped into 6507 address space so you cannot directly load, store, increment, decrement, etc. the value. You would: put MyVar: ds 1 as part of the Zero Page RAM put _MYVAR: ds 1 as part of Display Data RAM update CallArmCode to copy MyVar into _MYVAR Then your 6507 code would use MyVar. In Part 8 the 6502 variables TimeLeftOS and TimeLeftVB are added and get passed to the ARM code. 1 Link to comment Share on other sites More sharing options...
flickertail Posted July 15, 2020 Share Posted July 15, 2020 Ahh... excellent, I get it. Thanks for the response. I can never get all the way through the instructions before beginning to play with code or a new device! ? Link to comment Share on other sites More sharing options...
Recommended Posts