-
Content Count
16,912 -
Joined
-
Last visited
-
Days Won
10
Content Type
Profiles
Member Map
Forums
Blogs
Gallery
Calendar
Store
Everything posted by SpiceWare
-
Was supposed to be a 10 minute stop in Ardmore, OK but only one open spot so slower charging due to paired stalls. It also automatically lowered the charge to 80% no rest stop needed, so we watched a couple episodes of Love, Death & Robots on Netflix. $2.16 for 89 miles of range in 19 minutes.
-
Rest stop in Denton. $0.77 for 35 miles of range in 9 minutes.
-
Next stop was in Corsicana We had lunch at Collin Street Bakery, food was excellent. $0.00 (?!) for 212 miles of range in 44 minutes
-
My folks and I are on our way to Wisconsin for the holidays and nephew #2’s wedding. Did some opportunity charging during a brief rest stop in Huntsville. $1.30 for an 11 minute charge that added 38 miles of range.
-
Oops - missed this comment at first. Good catch, I've fixed it. I don't have a good handle on makefiles - I can butcher an existing one, but wouldn't even know where to begin if I needed to make one from scratch. Way back when (covered in Part 8 and Part 9 of the unfinished DPC+ARM tutorial) we had to type a bunch of echo statements in the 6507 source that would output #define statements for anything in the 6507 code that the C code needed to know about. This is a small example of the ~350 echo statements in Draconian (RAM in Collect3 = QUEUE in Draconian): echo "#define FUNC QUEUE[",[ARMfunc]d,"]" echo "#define SWCHA QUEUE[",[ARMswcha]d,"]" echo "#define SWCHB QUEUE[",[ARMswchb]d,"]" echo "#define INPT4 QUEUE[",[ARMinpt4]d,"]" echo "#define MODE QUEUE[",[ARMmode]d,"]" echo "#define FRAME QUEUE[",[ARMframe]d,"]" ... echo "#define AUDV ((unsigned char *)( DD_BASE +",[ARMaudv0]d,"))" echo "#define AUDF ((unsigned char *)( DD_BASE +",[ARMaudf0]d,"))" echo "#define AUDC ((unsigned char *)( DD_BASE +",[ARMaudc0]d,"))" ... echo "// game screen datastreams" echo "#define DATASTREAM_SIZE ",[DATASTREAM_SIZE]d echo "#define REPOSITION_SIZE ",[REPOSITION_SIZE]d echo "#define CLEAR_DS_INT ",[ClearDatastreamInt]d echo "#define CLEAR_DS_INT_SIZE ",[ClearDatastreamIntSize/4]d echo "#define P0_DATASTREAM ",[Player0DataStream]d echo "#define P1_DATASTREAM ",[Player1DataStream]d After manually running dasm you would copy that output, paste it into defines_dasm.h, compile the C code, then run dasm again to create the ROM. Eventually @cd-w figured out how to automate the entire process by having the makefile capture those defines and automatically create defines_dasm.h: dasm $(SOURCE).asm -f3 -o$(SOURCE).bin | gawk '!x[$$0]++' | grep "#define" > $(BASE)/defines_dasm.h While working on SpiceC I figured out how to use awk and have it parse the symbol file for symbols starting with _ instead, which eliminated the requirement of manually adding all those echo statements. awk '$$0 ~ /^_/ {printf "#define %-25s 0x%s\n", $$1, $$2}' $(PROJECT).sym >> main/$(DASM_TO_C) I like that! Have made that change as well. Both changes will be present in Part 6.
-
You're right. I thought about that when I made the changes, but didn't see any remnants of MENU on screen: so didn't look into it any further as I wanted to get it posted because I had a big checklist* of things to do in preparation for the trip that I needed to start on. I should have taken a look with Fixed Debug Colors turned on: because I didn't think about the zeroing out of the color datastreams would making it invisible on a black background. I've made the correction in my source: void GameVerticalBlank() { // Zero out the datastreams. It's fastest to use myMemsetInt, but requires // proper alignment of the data streams (the ALIGN 4 pseudops found in the // 6507 code). Additionally the offset(_GameZeroOutStart) and // byte count(_GameZeroOUtBytes) must both be divided by 4. myMemsetInt(RAM_INT + _GameZeroOutStart/4, 0, _GameZeroOutBytes/4); // Use Y value to position player 0 in the datastreams myMemcpy(RAM + _PLAYER0 + player_y[0], ROM + (player_shape[0] & 0x0fff) + 0x7000, 16); myMemcpy(RAM + _COLOR0 + player_y[0], ROM + (_COLOR_LEFT & 0x0fff) + 0x7000, 16); // Use Y value to position player 1 in the datastreams myMemcpy(RAM + _PLAYER1 + player_y[1], ROM + (player_shape[1] & 0x0fff) + 0x7000, 16); myMemcpy(RAM + _COLOR1 + player_y[1], ROM + (_COLOR_RIGHT & 0x0fff) + 0x7000, 16); // initialize the Data Streams for 6507 code setPointer(_DS_GRP0, _PLAYER0); setPointer(_DS_GRP1, _PLAYER1); setPointer(_DS_COLUP0, _COLOR0); setPointer(_DS_COLUP1, _COLOR1); // set the X positions of the players P0_X = player_x[0]; P1_X = player_x[1]; } * have 4 things left on the list to do tonight after work, we hit the road early tomorrow morning. One of the things on the list that we did yesterday was a dry run of the AutoSocks. My brother lives out in the country and the roads don't always get plowed right away. I got stuck one year in my S2000, the end of his road had snow covered ice with a slight slope up. He lives on a dead-end road, so getting past that was the only way to get back to the main roads.
-
Last CDFJ tutorial post of the year now online, it will resume in January. Happy Holidays!
https://atariage.com/forums/forum/262-cdfj/ -
I thought Part 3 would be the last one for 2019, but managed to squeeze in a couple more entries. There won't be anymore for 2019 as the next couple of weeks will be hectic with the holidays and the wedding of nephew # 2, who proposed to his high school sweetheart a couple months ago. They accelerated the wedding plans because he's about to be deployed overseas. My folks and I will be driving up to Wisconsin in my Model 3. I'll be documenting the trip over in the Tesla Club if you're interested. I'm curious to see how a winter trip to Wisconsin compares with the trip I took over the summer.
-
Made some improvements to the source code. NOTE: See the comments for a correction that needs to be made in GameVerticalBlank() Source Code Download and unzip this in your shared directory. Collect3_20191219.zip ROM for reference collect3_20191219.bin Makefile revisions The C compiler is Step 2, but its output was shown under Step 1. It now correctly shows up under Step 2 The C compiler now outputs files with a root name of armcode. These are the 3 files created in directory main/bin/. Previously the root was testarm as I had copied the initial make file from one of our test projects from back when we developed the CDFJ driver. New defines.h file Added a new C header file, which currently contains: #define MODE RAM[_MODE] #define RUN_FUNC RAM[_RUN_FUNC] #define P0_X RAM[_P0_X] #define P1_X RAM[_P1_X] #define SWCHA RAM[_SWCHA] #define SWCHB RAM[_SWCHB] #define JOY0_LEFT !(SWCHA & 0x40) #define JOY0_RIGHT !(SWCHA & 0x80) #define JOY0_UP !(SWCHA & 0x10) #define JOY0_DOWN !(SWCHA & 0x20) #define JOY0_FIRE !(INPT4 & 0x80) #define JOY1_LEFT !(SWCHA & 0x04) #define JOY1_RIGHT !(SWCHA & 0x08) #define JOY1_UP !(SWCHA & 0x01) #define JOY1_DOWN !(SWCHA & 0x02) #define JOY1_FIRE !(INPT5 & 0x80) These defines make the C code easier to read and write. As an example, this snippet from GameOverScan() in Part 3: // abort game logic if user hit SELECT to return to the menu if (RAM[_MODE] < 128) return; // left player if (!(RAM[_SWCHA] & 0x80)) // check for joystick right if (player_x[0] < 152) { player_x[0]++; player_shape[0] = _PLAYER_RIGHT; } became this in Part 5: // abort game logic if user hit SELECT to return to the menu if (MODE < 128) return; // left player if (JOY0_RIGHT) if (player_x[0] < 152) { player_x[0]++; player_shape[0] = _PLAYER_RIGHT; } Overlapped Display Data RAM The memory used for the Splash Datastreams is now reused for the Menu and Game Datastreams. The starting point is also 4 byte aligned so we can do a faster initializing using int values instead of unsigned char values, which is covered in the next section below. ;---------------------------------------- ; To save space in RAM we can share the space used by the datastream buffers ; for the Splash, Menu, and Game screens. ;---------------------------------------- align 4 ; using myMemsetInt to zero out RAM is faster than ; myMemset, but it requires the starting address to be ; 4 byte aligned OverlapDisplayDataRam: ; mark the beginning of overlapped RAM ; Splash screen datastreams _SPLASH0: ds 192 _SPLASH1: ds 192 _SPLASH2: ds 192 _SPLASH3: ds 192 echo "----",($1000 - *) , "Splash bytes of Display Data RAM left" ;---------------------------------------- ; this ORG overlaps the Menu datastreams on top of the Splash datastreams ;---------------------------------------- ORG OverlapDisplayDataRam ; Menu datastreams _MENU0: ds 192 _MENU1: ds 192 echo "----",($1000 - *) , "Menu bytes of Display Data RAM left" ;---------------------------------------- ; this ORG overlaps the Game datastreams on top of the Splash and Menu datastreams ;---------------------------------------- ORG OverlapDisplayDataRam ; Game datastreams _GameZeroOutStart: _PLAYER0: ds 192 _PLAYER1: ds 192 _COLOR0: ds 192 _COLOR1: ds 192 align 4 ; need to be 4 byte aligned to use myMemsetInt _GameZeroOutBytes=*-_GameZeroOutStart The key to making this work is to define a label, in this case OverlapDisplayDataRam, before the first Splash datastream buffer, then use ORG with that label before the first menu and first game datastreams. As covered in the final section of this post, the last line defines _GameZeroOutBytes, which is used in GameVerticalBlank() to zero out the game datastreams. The amount of Display Data RAM remaining is shown in the output after you type make. In Part 3 that was: ---- $7d bytes of RAM left (space reserved for 2 byte stack) ---- $876 bytes of Display Data RAM left ---- $530c bytes of ARM space left $876 = 2166 bytes free. In Part 4 that output becomes: ---- $7d bytes of RAM left (space reserved for 2 byte stack) ---- $cf4 Splash bytes of Display Data RAM left ---- $e74 Menu bytes of Display Data RAM left ---- $cf4 Game bytes of Display Data RAM left ---- $5360 bytes of ARM space left $cf4 = 3316 bytes free for Splash Screen $e74 = 3700 bytes free for the Menu $cf4 = 3316 bytes free for Game So overlapping the RAM currently saves 1150 bytes of RAM. This savings will only increase as the project develops. Faster Memory Fill In Part 3 we used myMemset() to zero out RAM. This was done in Initialize(): myMemset(RAM, 0, 4096); and in GameVerticalBlank(): myMemset(RAM + _PLAYER0, 0, 192); myMemset(RAM + _PLAYER1, 0, 192); myMemset(RAM + _COLOR0, 0, 192); myMemset(RAM + _COLOR1, 0, 192); myMemset() is found in defines_cdfj.h: void myMemset(unsigned char* destination, int fill, int count) { int i; for (i=0; i<count; ++i) { destination[i] = fill; } } We use myMemset() instead of the usual C memset() because the later includes a lot of overhead, which uses up precious ROM. The ARM is a 32 bit processor, and each instruction takes a single cycle, so if we make a version of myMemset that uses 32 bit int values instead of 8 bit char values the fill will take about 1/4th the time. You can find that in defines_cdfj.h(): // in theory 4x faster than myMemset(), but data must be WORD (4 byte) aligned void myMemsetInt(unsigned int* destination, int fill, int count) { int i; for (i=0; i<count; ++i) { destination[i] = fill; } } The ARM requires int values to fall on 4-byte boundaries, which is why the align 4 was added when we overlapped the Display Data RAM. The new Initialize(): myMemsetInt(RAM_INT, 0, 4096/4); and the new GameVerticalBlank(). We also merged the original 4 myMemset calls into a single call, which eliminates some overhead, and used the new label that was added when Display Data was overlapped: myMemsetInt(RAM_INT + _GameZeroOutStart, 0, _GameZeroOutBytes/4); When changing memset from char to int it's critical to make sure you divide the byte counts by 4. NOTE: see comments for a correction to this call to myMemsetInt.
-
Stella has excellent support for developers, you can read about it in the Stella documentation in the sections: Integrated Debugger Developer Options/Integrated Debugger Developer Keys in TIA mode Developer Commands for the Command Line Stella does not directly support the debugging of ARM code; however, we can use the extra cartridge tabs to view information before and after the ARM code has run, which can be helpful to track down problems. Note: I'm only going to show Stella's Debugger as it pertains to CDFJ, if you're unfamiliar with using it then read over these to help get you up to speed: Reply 9 - use Stella to create a disassembly Reply 12 - use Stella to help with cycle counts Reply 2 - Fixed Debug Colors mode Video tutorial by @tschak909 Reply 86 Reply 87 additional info from me in Reply 91 if you know of a helpful resource not listed then add a comment with a link Cartridge CDFJ tab Just a brief overview of the CDFJ bank switching scheme. Games using the original CDF spec, such as Draconian and Super Cobra Arcade, will have slightly different text to denote they're using an older version of the scheme. States tab Status of the various CDFJ registers. Before your initialization routine runs the values will not be valid: An example after the values have been initialized. Like the CartridgeCDFJ tab, the States tab will be slightly different for older CDF games. Cartridge RAM tab The Cartridge RAM tab shows the entire 8K of RAM that's available in the Harmony/Melody. The first 2 K contain the CDFJ driver, so should be ignored. The first 2K also contain the various registers, but those are much easier to view on the States tab. You can scroll down Description box for a little more info: Display Data As mentioned in Part 1, ARM RAM begins at 0x40000000 and Display Data begins at 0x40000800. Stella's debugger drops off the leading 0x4000 so if we scroll down to 08xx we'll be at the start of Display Data RAM: The initial values in Display Data RAM are random, so the 6507 InitSystem routine will call our C function Initialize() to zero it out so we have a known starting state: In the 6507 code the first part of Display Data is defined as this: _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 ... If we look into the symbol file collect3.sym we can find the values for each label: _BALL_X 0005 _DS_FROM_ARM 0005 _DS_TO_ARM 0000 (R ) _INPT4 0003 _INPT5 0004 _M0_X 0007 _M1_X 0006 _MODE 0005 _P0_X 0009 _P1_X 0008 _RUN_FUNC 0000 _SPLASH0 000a _SWCHA 0001 _SWCHB 0002 To see them in the CartridgeRAM tab just add 0x0800 to the value. If we advance a single frame we'll see the RAM starting at 0x0800 change: So we can see the following values in RAM are: 05 = _RUN_FUNC ff = _SWCHA 3f = _SWCHB 8c = _INPT4 8d = _INPT5 Advance a few more frames until we see SPLASH in the TIA Display in the upper-left: From here we can see: _P1_X at address 08 has the value of 48 _P0_X at address 09 has the value of 40 _SPLASH0 staring at address 0a is filled in with the graphics to show SPLASH down the screen. If we switch to the TIA tab on the left we can see the positions of the players: P0 Pos# = 64 P1 Pos# = 72 At first glance those look incorrect, but the Pos# values are shown in decimal while the RAM values are shown in hexadecimal. 64 = $40 and 72 = $48, so they do match. C Variables and Stack C Variables and Stack begin at 0x40001800 Global variables are those defined outside of functions. In Collect 3 those are currently: unsigned int frame; unsigned char player_x[2]; unsigned char player_y[2]; unsigned short int player_shape[2]; We can use the Map file to find their RAM address. The Map file for Collect 3 is main/bin/testarm.map At the start of the map file we will find a list of the variables and their sizes: Common symbol size file player_y 0x2 main.o frame 0x4 main.o player_x 0x2 main.o player_shape 0x4 main.o Their addresses can be found towards the end of the file: COMMON 0x0000000040001808 0xe main.o 0x0000000040001808 player_y 0x000000004000180c frame 0x0000000040001810 player_x 0x0000000040001812 player_shape The ARM uses LSB order so looking at the table we see our frame counter is at 180c which contains the bytes de 02 00 00 which is the value $000002de or 734 The values for the 2 byte array player_x are at 1810 and contain the bytes 24 and 74, which are 36 and 116 respectively. If we switch to the TIA tab we can see the Pos # for P0 and P1 match. If we switch to the I/O tab we can check the joystick positions then click the Frame +1 button to advance a frame. We'll see the values for player_x and player_y both changed: However, when we look at the TIA tab the Position numbers have not yet changed: That's because the values for player_x and player_y were changed in Overscan and we've not yet run Vertical Blank which is when the positioning of the players occurs. Click Frame +1 again and the positions will change. The 4 bytes at 1800-1803 are from the random number generator. It uses a static variable within the function: unsigned int getRandom32() { // using a 32-bit Galois LFSR as a psuedo random number generator. // http://en.wikipedia.org/wiki/Linear_feedback_shift_register#Galois_LFSRs static unsigned int random = 0x02468ace; return random = (random >> 1) ^ (unsigned int)(-(random & 1u) & 0xd0000001u); } which for some reason it does not appear in the Map file.
-
While it's a work in progress, as I plan to flesh out the post some more during the week, I did publish Part 3 of the CDFJ Tutorial yesterday with the source for the beginnings of Collect 3. It shows a splash screen, non-functional menu, and a game screen that 2 players can move around on. 😉
-
New release of dasm! You can download it from here, and read more about it in this topic. Install it just like the instructions above, just need a minor change in your .profile file: # set PATH so it includes dasm's bin if it exists if [ -d "$HOME/2600tools/dasm-2.20.12-linux-x64" ] ; then PATH="$HOME/2600tools/dasm-2.20.12-linux-x64:$PATH" fi
-
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. 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.
-
If you're using jEdit I've made an update to the mode files that includes some changes for CDFJ.
-
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
-
NOTE: I think I tracked down all the jEdit blog entries, but am going to hold off updating the navigation links at the bottom-right of each entry for a while in case I missed some. So if you spot any that are not in this index let me know! jEdit is a programmer's editor that I like to use for 2600 development. It's written in Java, so is cross-platform. Key feature I like is the syntax highlighting, which makes it easy to spot typos - notice the first GRP0 in SplashKernel was typed with the letter O rather than the number 0, so is colored white instead of orange like the GRP1 below it. A binary colorization rule also makes it easy to see graphics in your source code: 20060212 - jEdit for IDE? initial look at using jEdit 20060214 - Edit Mode rev 2 additional changes to Mode file 20060519 - Updated MODE file additional changes to Mode file 20060715 - jEdit files for batari BASIC Syntax highlighting for bB 20120216 - ColecoVision Diversion Syntax highlighting for ColecoVision 20120219 - Controller demo Updates for ColecoVision development 20120306 - Frantic reboot?!? minor updates to Mode file 20140626 - Updated MODE file colorization of binary - makes it super easy to see graphics in your source code 20140927 - Updated batari BASIC mode file colorized binary for batari BASIC 20140928 - Updated assembly-6502 and batari BASIC mode files addition of DPC+ registers 20140929 - Add a console to jEdit preparation to configure jEdit to Compile code and launch the results in Stella 20140929 - Compile and Run 2600 assembly code from jEdit configure jEdit to use dasm and Stella 20140930 - changing jEdit colors customize colors used by jEdit 20180306 - C mode file mode file for C that supports colorized binary 20191215 - Minor revision to jEdit Mode files Minor fix for coloration of binary numbers, addition of CDFJ registers JEDIT NAVIGATION <PREVIOUS> <INDEX> <NEXT>
-
While working on the next part of the CDFJ tutorial I noticed an odd coloration issue in jEdit - the 0 and 1 at the end of the labels were sometimes having the binary rules applied to them: Took a look at the rule in the Mode file: <!-- %01 format for binary --> <SPAN_REGEXP TYPE="OPERATOR" HASH_CHAR="%" AT_WORD_START="TRUE" DELEGATE="COLORIZE_BINARY"> <BEGIN>%</BEGIN> <END REGEXP="TRUE">\B</END> </SPAN_REGEXP> The rule stops when Regular Expression \B is encountered, which is a non-word break. I would have thought starting a new line would have matched that, but apparently not. I tried \s to match any whitespace character, no luck. Tried \n for new line, which didn't fix it and added a new problem - other coloration(byte, # and %) went away: I then spotted the [^abc] character class operator which matches any character not in the set a, b or c so tried [^01] to matching anything not 0 or 1 <!-- %01 format for binary --> <SPAN_REGEXP TYPE="OPERATOR" HASH_CHAR="%" AT_WORD_START="TRUE" DELEGATE="COLORIZE_BINARY"> <BEGIN>%</BEGIN> <END REGEXP="TRUE">[^01]</END> </SPAN_REGEXP> That worked. I made this change to my Mode files for 2600, 7800, bB, ColecoVision, and c files. The 7800 entry is commented out in the catalog, so you'll need to uncomment it if you wish to use it. I also updated the 2600 Mode file with the CDFJ registers. catalog.zip Atari 2600 assembly.xml.zip Atari 2600 batari BASIC.xml.zip Atari 7800 assembly.xml.zip c.xml.zip ColecoVision assembly.xml.zip JEDIT NAVIGATION <PREVIOUS> <INDEX> <NEXT>
-
Works quite well. I'm not currently using the console feature though - I don't know of a way to get jEdit running on the Mac to launch a compile via the virtual machine we're using for the Harmony/Melody club. I'm about to post a minor update to the Mode files.
-
The fields on the debugger screen change to that color if they changed since the last time you looked at the debugger screen. If that box went red then the scanline count is not consistent over time, which will cause the screen to jitter and/or roll. Scanlines should be 262 for NTSC or 312 for PAL. Air Raid is 290 scanlines when the game is not being played, so not correct for either NTSC or PAL, but it's not displayed in red*. Start a game and it changes to 291 scan lines, so turns red It mostly stays at 291, so the red goes away but occasionally jumps to 292, which causes the red to return * except for the first time you enter the debugger as everything's considered changed so everything's red
-
Great! Nice 7 digit score! Yep - the ARM in the Harmony/Melody supports multiplication, but not division. I know the compiler's smart enough to use shifting to divide by powers of 2, but hadn't tried other values to see what would happen. I've finished writing code for Part 3 - starts out with the splash screen after a couple seconds it goes to the menu screen hitting RESET in the menu takes you to the game screen, where you can move the players around. Hitting SELECT takes you back to the menu. Next I need to go thru the code to clean it up and comment it better so I can post it.
-
Ran across this: Which is a followup to some rather old discussions we've had here in the blogs:
-
Great hack! You asked about Frosty's colors shifting during the live stream - COLUP0 is getting updated at cycle 36, so well after the scanline has started to be drawn. Arrow at top-left shows where the electron beam is when the color was changed. I cover why that occurs here using the sheared snowball in Stay Frosty 2 as an example. The Berzerk font also looks out of place, maybe a search on frosty font would give you some ideas.
-
To see how long routines run I set up the 6507 code to send time remaining (from RIOT register INTIM) to the C code. The C code can then display it using the score. I'll most likely cover that in Part 4 or 5 of the CDFJ tutorial. If you don't want to wait that long you can check out this blog entry to see how I did it for DPC+. Do note it's a bit messed up (specifically the code blocks) due to the recent forum upgrade, but if you download the source you can see it. Look for TimeLeftOS and TimeLeftVB in the 6507 code. Also note the DPC+ registers start with DF instead of DS. That's a misnomer because Data Fetcher implies read-only even though they can also be used to write data. This is because the names come from DPC 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.
-
The regular data streams are 0-31, DSCOMM is 32. The addresses for regular data streams can only be set by the C code. The address for DSCOMM is set by 6507 code using DSPTR, though could also be set by C code. DSWRITE only writes to DSCOMM. If Display Data is set like this: _DS_TO_ARM: _SWCHA: ds 1 ; controller state to ARM code _INPT4: ds 1 ; firebutton state to ARM code _DS_FROM_ARM: _COLUBK: ds 1 ; background color from ARM code Then the 6507 can send the values to the ARM, call the ARM code which calculates background color based on the joystick, then get the calculated background color by doing this: ldx #<_DS_TO_ARM stx DSPTR ldx #>_DS_TO_ARM stx DSPTR ldx SWCHA ; read state of both joysticks stx DSWRITE ; written to _SWCHA ldx INPT4 ; read state of left joystick firebutton stx DSWRITE ; written to _INPT4 ldx #$FF ; flag to Run ARM code w/out digital audio interrupts stx CALLFN ; runs main() in the C code lda #DSCOMM ; read value in _COLUBK sta COLUBK ; set the background color
-
CDFJ is built around data streams. A data stream is a sequence of data elements made available over time - basically a list of values such as: 10 55 20 25 ... The data stream will auto-advance so that the first time you read it you'd get 10, the next time you'd get 55, then 20 and so on. Data streams are very helpful during the kernel as you can update any TIA register in just 5 cycles: LDA #DS0DATA STA GRP0 General Purpose Data Streams There are 32 general purpose data streams named DS0DATA thru DS31DATA. Each data stream has an increment value associated with it for the auto-advance feature. Example increments: 1.0 for 1LK player graphics 0.20 to repeat chunky playfield graphics over 5 scanlines 2.0 to skip every other value. This is extremely useful for interlaced bitmap graphics, which are typical seen as 96 or 128 pixels across. Communication Data Stream There is a dedicated communication data stream named DSCOMM used for transferring data between the 6507 and ARM processors. Jump Data Streams There are 2 data streams for jumps named DSJMP1 and DSJMP2. These override JMP $0000 and JMP $0001 respectively, providing 3 cycle flow control within the kernel. This means instead of counting scanlines and branching your kernel would look something like this kernel from Draconian: ; data stream usage for game screen DS_GRP0 = DS0DATA DS_GRP1 = DS1DATA DS_HMP0 = DS2DATA DS_HMP1 = DS3DATA DS_MISSILE0 = DS4DATA ; HMM0 and ENAM0 DS_MISSILE1 = DS5DATA ; HMM1 and ENAM1 DS_BALL = DS6DATA ; HMBL and ENABL DS_COLOR = DS7DATA ; color change for players and ball only DS_SIZE = DS8DATA ; size change for all objects NormalKernel: ; 20 lda #DS_SIZE ; 2 22 <- just to keep stream in sync nk1: lda #DS_COLOR ; 2 24 2 33 from Resm0Strobe28 <- just to keep stream in sync lda #DS_HMP0 ; 2 26 sta HMP0 ; 3 29 lda #DS_HMP1 ; 2 31 sta HMP1 ; 3 34 lda #DS_MISSILE0 ; 2 36 tax ; 2 38 stx HMM0 ; 3 41 lda #DS_MISSILE1 ; 2 43 tay ; 2 45 sty HMM1 ; 3 48 lda #DS_BALL ; 2 50 sta HMBL ; 3 53 sta ENABL ; 3 56 lda #DS_GRP0 ; 2 58 sta GRP0 ; 3 61 lda #DS_GRP1 ; 2 63 sta WSYNC ; 3 66/0 sta HMOVE ; 3 3 sta GRP1 ; 3 6 <- also updates GRP0 and BL DIGITAL_AUDIO ; 5 11 stx ENAM0 ; 3 14 sty ENAM1 ; 3 17 jmp FASTJMP1 ; 3 20 ExitKernel: ... Resm0Strobe23: ; 20 sta RESM0 ; 3 23 lda #DS_SIZE ; 2 25 sta NUSIZ0 ; 3 28 <- changes missile size jmp nk1 ; 3 31 Resm0Strobe28: ... The data stream DSJMP1 is initially filled with addresses for NormalKernel, and ends with the address for ExitKernel. The player, missile and ball reuse routines will change individual values in DSJMP1 to jump to reposition kernels such as Resm0Strobe23. Audio Data Stream Lastly there's an audio data stream named AMPLITUDE. It will return a stream of data to play back a digital sample, or to play back 3 voice music with custom waveforms. The macro DIGIT_AUDIO in the above Draconian kernel is defined as: MAC DIGITAL_AUDIO lda #AMPLITUDE sta AUDV0 ENDM 6507 Interface From the Atari's point of view CDFJ only has 4 registers defined in the cartridge space. DSWRITE at $1FF0 DSPTR at $1FF1 SETMODE at $1FF2 CALLFN at $1FF3 DSPTR is used to set the Display Data address for the DSCOMM data stream - basically setting the RAM location the 6507 code wishes to write to. Write the low byte of the address first, then the high byte. DSWRITE writes to the address set by DSPTR. After writing, DSPTR advances to the next RAM location in preparation for the next write: ; define storage in Display Data _DS_TO_ARM: _SWCHA: ds 1 ; controller state to ARM code _SWCHB: ds 1 ; console switches state to ARM code _INPT4: ds 1 ; firebutton state to ARM code _INPT5: ds 1 ; firebutton state to ARM code ... ldx #<_DS_TO_ARM stx DSPTR ldx #>_DS_TO_ARM stx DSPTR ldx SWCHA ; read state of both joysticks stx DSWRITE ; written to _SWCHA ldx SWCHB ; read state of console switches stx DSWRITE ; written to _SWCHB ldx INPT4 ; read state of left joystick firebutton stx DSWRITE ; written to _INPT4 ldx INPT5 ; read state of right joystick firebutton stx DSWRITE ; written to _INPT5 SETMODE controls Fast Fetch Mode and Audio Mode. Fast Fetch mode overrides the LDA #immediate mode instruction and must be turned on to read from the data streams. Audio Mode selects between digital sample mode or 3-voice music mode. CALLFN is used to call the function main() in your C code. The value written to CALLFN determines if an interrupt will run to periodically update AUDV0. The interrupt is needed when playing back digital samples or 3-voice music. ldy #$FE ; generate interrupt to update AUDV0 while running ARM code sty CALLFN ldy #$FF ; do not update AUDV0 sty CALLFN C Interface From the C code a number of functions have been defined to interact with CDFJ and Display Data: setPointer() setPointerFrac() setIncrement() setWaveform() setSamplePtr() setNote() resetWave() getWavePtr() getWavePtr() getPitch() getRandom32() myMemset() myMemcpy() myMemsetInt() myMemcpyInt() This section will be expanded upon later.
