TheMole Posted June 2, 2014 Share Posted June 2, 2014 (edited) Hi All, I've found some more time to work on my Alex Kidd port. I manually converted one of the original games' maps to a format that is compatible with my scrolling routines. Naturally, I lost quite a bit of detail, but the end-result isn't too bad. This is what the original looks like: And this is what the same map looks like on the TI: I had to move some blocks around, or remove some blocks to avoid color clash(can't have two different colored blocks directly next to each other), but all-in-all, most of the details are there. I also originally did a very nice conversion of the cave the old man is sitting in at the end, but I ran out of patterns and had to ditch both that and the clouds. I'm thinking of adding it in a static screen that gets tagged onto the end of the map, but those are details for later. Either way, I'm only using some of the original game maps to test playability and will probably design my own at some stage (so it'd be a sequel/prequel instead of a direct port), so I'm not sure how important those issues are. Next up, I started working on real collision detection and fine-tuning the physics to be a bit more in line with the original game. Seems to be working well, although I still need to add a collision check for the top of the sprite (you could jump into one of the blocks now and get stuck if you really try). Next up: finish the collision detection routines, find a way to make Alex face the other direction and implement punching. Also, add the jumping and punching sound effects. After that, I'll need to figure out how to make the blue blocks and the gold boxes destructable in the scrolling data... punching and destroying blocks is kinda the main mechanic of the game . It should be doable, but it will be quite convoluted as I have to decide which patterns to put in place depending on the object on the other side of the block. Finally, here's a video recorded in MESS. Normal caveats apply: youtube makes it a bit more choppy than the real thing, and of course the colors are a bit more washed out here. *edit* The latest work-in-progress version is attached to this post for those that are interested in trying it out. Latest version is from august 24th, 11pm CET. alexkidd.dsk Edited August 24, 2014 by TheMole 21 Quote Link to comment Share on other sites More sharing options...
+Ksarul Posted June 2, 2014 Share Posted June 2, 2014 Looking god so far! My boys will love this one. . .they love to play games on their TI, and they have fun with a lot of the educational titles too. Quote Link to comment Share on other sites More sharing options...
+mizapf Posted June 2, 2014 Share Posted June 2, 2014 (edited) Looking god so far! This seems to me like the ultimate appreciation. Edited June 2, 2014 by mizapf 4 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted June 2, 2014 Share Posted June 2, 2014 Very nice! Quote Link to comment Share on other sites More sharing options...
Asmusr Posted June 2, 2014 Share Posted June 2, 2014 After that, I'll need to figure out how to make the blue blocks and the gold boxes destructable in the scrolling data... punching and destroying blocks is kinda the main mechanic of the game . It should be doable, but it will be quite convoluted as I have to decide which patterns to put in place depending on the object on the other side of the block. I was facing the same problem for Scramble in order to explode and remove ground objects. As you probably know I ended up using a combination of sprites and characters. I wanted to use characters as much as possible to limit sprite flicker, but I was forced to use some sprites because I ran out of characters. It would have been much easier just to use sprites provided you could accept the flicker. Anyway, what I did was to make a lookup table for each character transition, for instance "from space to left side rocket", "from left side rocket to right side rocket", or "from right side rocket to wall". For each transition you could look up a data structure that contained the number of the replacement transitions you needed for different actions. One action was to remove an object. The 'remove' entry for "from space to left side rocket" pointed to the transition for "from space to space" (rocket replaced by space), the one for "from right side rocket to wall" pointed to "from space to wall" and so on. I also had an action to explode an object. The 'explosion' entry for "from right side rocket to wall" pointed to "from explosion frame 1 to wall", which in turn would pointed to "from explosion frame 2 to wall" to animate the explosion. The last explosion entry pointed back to the first to form a loop. You could take the loop a number of times and then follow the 'remove' pointer to finally remove the object. The table was a great performance benefit compared to large number of 'if statements' you would otherwise have to write. If an entry in the table was null I knew the required transition character didn't exist and a had to use a sprite. If you do something like that I suggest to write a program to generate the tables for you, because a spent a lot of time setting up the table manually for each level. 1 Quote Link to comment Share on other sites More sharing options...
TheMole Posted June 5, 2014 Author Share Posted June 5, 2014 I was facing the same problem for Scramble in order to explode and remove ground objects. As you probably know I ended up using a combination of sprites and characters. I wanted to use characters as much as possible to limit sprite flicker, but I was forced to use some sprites because I ran out of characters. It would have been much easier just to use sprites provided you could accept the flicker. Anyway, what I did was to make a lookup table for each character transition, for instance "from space to left side rocket", "from left side rocket to right side rocket", or "from right side rocket to wall". For each transition you could look up a data structure that contained the number of the replacement transitions you needed for different actions. One action was to remove an object. The 'remove' entry for "from space to left side rocket" pointed to the transition for "from space to space" (rocket replaced by space), the one for "from right side rocket to wall" pointed to "from space to wall" and so on. I also had an action to explode an object. The 'explosion' entry for "from right side rocket to wall" pointed to "from explosion frame 1 to wall", which in turn would pointed to "from explosion frame 2 to wall" to animate the explosion. The last explosion entry pointed back to the first to form a loop. You could take the loop a number of times and then follow the 'remove' pointer to finally remove the object. The table was a great performance benefit compared to large number of 'if statements' you would otherwise have to write. If an entry in the table was null I knew the required transition character didn't exist and a had to use a sprite. If you do something like that I suggest to write a program to generate the tables for you, because a spent a lot of time setting up the table manually for each level. Good point, I forgot you must've had to face the same problem with scramble. My goal is to have the block disappear (replace the map data) and do the animation with sprites. The replacement pointer is a good idea though, I'll probably use that. Quote Link to comment Share on other sites More sharing options...
TheMole Posted June 14, 2014 Author Share Posted June 14, 2014 (edited) Small update: Somewhat improved collision detection (all the collision points are there, but the location of them isn't perfect yet) Alex can now turn left and right (I simply swap the sprite pattern table pointer to a mirrored version when he changes direction) Slightly tweaked physics, Alex can now no longer reverse direction in mid-air on a normal jump Added punching animation and the ability to punch the blue blocks in the level. Still some visual glitches here due to rounding errors when calculating the part of the screen to refresh (you can see it in the video at the 30 second mark) Next I plan on cleaning up the codebase a bit, fixing the smaller bugs. Then I'll release a disk image for who's interest in playing with it. After that, I'll have to refactor everything to work with disk files instead of storing all the graphics in one big binary. Currently the game fills nearly the entire 32k, but almost half of that are patterns that can easily be loaded into VRAM and then discarded from memory. I'll also need to optimize a bit here and there as I'm nearly at the limit of what I can do at 60fps and I still need to add enemies. Small video showing the new features: Edited June 14, 2014 by TheMole 6 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted June 14, 2014 Share Posted June 14, 2014 Great job, looks awesome. And the TI really needs this type of game. Quote Link to comment Share on other sites More sharing options...
RobertLM78 Posted June 14, 2014 Share Posted June 14, 2014 Really looking forward to checking this one out . Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 17, 2014 Author Share Posted August 17, 2014 (edited) So... finally found some more time to work on this. A bunch of stuff has been added, and I'd almost call it "a game" now (except for there being no win condition yet). New stuff: I now load most of the graphics and music from files on disk. I had to do this because I was running out of program space, and this took a shitload of time since I had to figure out how to do file loading on the TI for the very first time, and then how to fit all of it in memory where gcc likes it. Added enemies. Unfortunately, I ran out of sprite pattern definition slots and need to use a placeholder for two out of three enemies (the red balls are supposed to be some sort of plant, and the black ball is supposed to be a kind of bird). I'm not sure how I can resolve this except for maybe sacrificing some animation frames or color for Alex himself, but I'd rather not do that as I kinda like how it makes the game look like it's running on a more high-end system with multi-color sprites. Alex can now die, so no more running across lava or through enemies for him! Also, a sad song plays when he dies... Improved Alex-World collision detection, I'm very happy with how this turned out. Although you can still see small glitches (Alex moving into a block for one or two pixels), you never get stuck and it never looks too horrible. Added jumping and punching sounds, still need to look for a "destroyed enemy" sound Sprite flickering routine. Still todo: Make the yellow blocks punchable as well. I would need one more sprite definition for the most straightforward way of implementing this, but as I said before I'm out of space for those. I'll probably have to integrate that in the map definition instead, which is going to be a bit more work but should have the added benefit of not introducing even more sprite flickering then I have to suffer already. Add a win condition F18A support ECM3 and hardware scrolling support Here's a short video showcasing the new features recorded in js99er: Now, I haven't been able to run this on the real deal since my hardware is unfortunately rotting away in my damp basement until I get my house in order (friggin' renovations seem to take forever...), and I have seen some glitches in MESS and some stuttering in Classic99, so I would very much appreciate it if one of you could test it on real hardware and perhaps make a video so I can gauge how smooth it runs on that? For those that want to give it a spin, there's a disk image attached. EA#5, DSK1.ALEXKIDD (needs 32K memory expansion and it the file paths are hardcoded to run from DSK1 for now, sorry about that). *edit* thanks to Rasmus for trying it out and unfortunately reporting that it does not work on real iron. I've removed the download until I've found a fix for this. Edited August 17, 2014 by TheMole 7 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted August 17, 2014 Share Posted August 17, 2014 Now, I haven't been able to run this on the real deal since my hardware is unfortunately rotting away in my damp basement until I get my house in order (friggin' renovations seem to take forever...), and I have seen some glitches in MESS and some stuttering in Classic99, so I would very much appreciate it if one of you could test it on real hardware and perhaps make a video so I can gauge how smooth it runs on that? For those that want to give it a spin, there's a disk image attached. EA#5, DSK1.ALEXKIDD (needs 32K memory expansion and it the file paths are hardcoded to run from DSK1 for now, sorry about that). It's looking great on emulation, but unfortunately you disk code doesn't seem to work on the hardware. I have tried both with my nanoPEB and a real floppy disk. After loading the initial E/A 5 file it stops with a green screen. You can reproduce this on MESS if you set it up to use a standard TI DSSD Floppy controller. I had the same problems with Titanium. You have take care not to overwrite the disk buffers at the top of VDP RAM. You also have to leave various address in PAD intact. What I did was to make a copy of the entire 256 bytes at program start up that I swapped back before any disk operations. Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 17, 2014 Share Posted August 17, 2014 In Classic99, if you enable "corrupt DSK RAM", it will overwrite the most common memory addresses used by disk after a DSR call with a fixed pattern so you can more easily tell if you were using it for something else. This was added to help make it easier to write DSR code that runs on a real TI Disk controller. For harder cases, you can enable real TI Disk Controller support in the Classic99.ini by first configuring a disk as a floppy image (must be an image file, not a folder) and then editing Classic99.ini so that the disk "Type" is "3" (it should be '2' when you open the file). The debug log will confirm that it's working. This will only work on DSK1 right now (maybe 2 and 3 but I wouldn't count on it - this is why it's not in the GUI yet ). Assuming you're using my debugger, anyway. Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 17, 2014 Author Share Posted August 17, 2014 (edited) You can reproduce this on MESS if you set it up to use a standard TI DSSD Floppy controller. Ah, good to know. I've been using the HFDC controller in MESS, so I'll try switching. Thanks Edited August 17, 2014 by TheMole Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 17, 2014 Author Share Posted August 17, 2014 In Classic99, if you enable "corrupt DSK RAM", it will overwrite the most common memory addresses used by disk after a DSR call with a fixed pattern so you can more easily tell if you were using it for something else. This was added to help make it easier to write DSR code that runs on a real TI Disk controller. For harder cases, you can enable real TI Disk Controller support in the Classic99.ini by first configuring a disk as a floppy image (must be an image file, not a folder) and then editing Classic99.ini so that the disk "Type" is "3" (it should be '2' when you open the file). The debug log will confirm that it's working. This will only work on DSK1 right now (maybe 2 and 3 but I wouldn't count on it - this is why it's not in the GUI yet ). Assuming you're using my debugger, anyway. Thanks for the tip, Tursi. Unfortunately, I can only use Classic99 once in a while to test something since I've moved to Mac (I need to borrow my gf's PC to test on Windows). It's a shame too, 'cause although MESS is very powerful, nothing beats Classic99's debugger in terms of ease of use and features. Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 17, 2014 Author Share Posted August 17, 2014 Anyone know the exact VDP and scratchpad locations to mind? I found an article in MG's smart programmer that shows anything below 0x37d8 should be fair game (and 0x3be4 if you do the equivalent of a "call files(1)"), but it still borks even if I avoid that area completely. Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted August 17, 2014 Share Posted August 17, 2014 Anyone know the exact VDP and scratchpad locations to mind? I found an article in MG's smart programmer that shows anything below 0x37d8 should be fair game (and 0x3be4 if you do the equivalent of a "call files(1)"), but it still borks even if I avoid that area completely. (0x37cf if using a compact flash.) Since the code appears to work with the HFDC, I would verify x83E0-83FF (GPLWS) is untouched and in particular, R13/R14/R15 (0x83FA,83FC,83FE). 0x8370 should be preserved as well. Also be sure your own program isn't using 83E0 as its workspace. Both the TI and Myarc cards use VDP for PABs and data passing; however, the Myarc cards (and IDE, SCSI, Horizon) use on-card RAM to buffer all or most of what is normally in the reserved VDP space. CorComp and nanopeb/cf7 devices also use the reserved VDP space. Quote Link to comment Share on other sites More sharing options...
Gazoo Posted August 17, 2014 Share Posted August 17, 2014 (0x37cf if using a compact flash.) Since the code appears to work with the HFDC, I would verify x83E0-83FF (GPLWS) is untouched and in particular, R13/R14/R15 (0x83FA,83FC,83FE). 0x8370 should be preserved as well. Also be sure your own program isn't using 83E0 as its workspace. Both the TI and Myarc cards use VDP for PABs and data passing; however, the Myarc cards (and IDE, SCSI, Horizon) use on-card RAM to buffer all or most of what is normally in the reserved VDP space. CorComp and nanopeb/cf7 devices also use the reserved VDP space. I touch 0x83FA (R13) all the time. Couldn't survive without it. Gazoo Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted August 17, 2014 Share Posted August 17, 2014 I touch 0x83FA (R13) all the time. Couldn't survive without it. Gazoo Don't get sassy now Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 20, 2014 Author Share Posted August 20, 2014 (edited) Are there limitations as to where the PAB can be put in VRAM? I've managed to have it load one file (the song) using the TI controller when putting the PAB at 0x2400, but it doesn't work at 0x3400... which seems very odd to me. I'm still struggling with loading stuff directly to the right location in VRAM as well, but that I can probably figure out once I understand the limitations a bit better. It's still going to be a struggle to get things cleanly in memory without using too many RAM buffers. My VRAM layout is pretty specific and fairly packed. 0x0000-0x0400 Frame0 @ 0x0000 0x0400-0x0800 SIT1 @ 0x0400 0x0800-0x0C00 Frame1 @ 0x0800 0x0C00-0x1000 SIT2 @ 0x0C00 0x1000-0x1400 Frame2 @ 0x1000 0x1400-0x1800 SDT_R @ 0x1400 0x1800-0x1C00 Frame3 @ 0x1800 0x1C00-0x2000 SDT_L @ 0x1C00 0x2000-0x2400 Frame4 @ 0x2000 0x2400-0x2800 PAB @ 0x2400 0x2800-0x2C00 Frame5 @ 0x2800 0x2C00-0x3000 0x3000-0x3400 Frame6 @ 0x3000 0x3400-0x3800 0x3800-0x3C00 Frame7 @ 0x3800 0x3C00-0x3FFF SAL @ 0x3F00 CT @ 0x3FC0 FrameN = Pattern definitions for frame n of smooth scrolling defs (1k each) SIT1/2 = Screen Image Table, nametable for front and backbuffers (756 bytes each) SDT_L/R = Sprite pattern definitions for Alex is facing left/right respectively (1k each) SAL = Sprite attribute table (128 bytes) CT = Color table (16 bytes) Note that I need file loading for two things: to load the music file in RAM (I do this first) and to load the scrolling pattern definitions into VRAM (Frame1-8 in the memory map above). This is all done at the start of a level, so once everything is in place I don't have to take the VDP disk buffers into account anymore. But, I do need an efficient way of getting this stuff into VRAM in the first place. Worst case scenario, I could: Load all patterns, one by one into normal RAM via the song buffer and move them to the lower 8k of VRAM. Load the song into RAM Move the pattern definitions to their correct locations in VRAM a couple of bytes at a time But that seems awfully wasteful. Edited August 20, 2014 by TheMole Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 20, 2014 Share Posted August 20, 2014 You may already know the following and are trying to solve another problem. I apologize in advance if that is so: You should be able to load directly from file to your desired VRAM location as long as your file record length is set up correctly. It seems you are looking to load 1KB blocks for the frames. If you use DF128 files, you can load 8 records at a time to any VRAM location you choose. You can also use direct sector access to load 4 sectors at a time. The one thing that puzzles me is what you mean by "PAB". If you are including the VRAM buffer in the term and implementation and you have not changed the default of 3 simultaneous files, putting your "PAB" at >3400 will step on the system disk buffers at the top of VRAM. I usually think of a PAB as including only the file's I/O parameters, which includes the file's pathname, e.g., DSK1.FILENAME. The PAB, then, would be only 24 bytes and should work just fine at >3400. You can also load any size "PROGRAM" file to load into a spot in VRAM using the DSR's LOAD opcode. You just need a separate file for each load. If everything you want to load is in one long, contiguous section of VRAM, you would only need one "PROGRAM" file. ...lee Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 20, 2014 Author Share Posted August 20, 2014 The one thing that puzzles me is what you mean by "PAB". If you are including the VRAM buffer in the term and implementation and you have not changed the default of 3 simultaneous files, putting your "PAB" at >3400 will step on the system disk buffers at the top of VRAM. I usually think of a PAB as including only the file's I/O parameters, which includes the file's pathname, e.g., DSK1.FILENAME. The PAB, then, would be only 24 bytes and should work just fine at >3400. You can also load any size "PROGRAM" file to load into a spot in VRAM using the DSR's LOAD opcode. You just need a separate file for each load. If everything you want to load is in one long, contiguous section of VRAM, you would only need one "PROGRAM" file. ...lee Thanks for the feedback Lee. I use PAB in the same way as you, basically as a structure holding the I/O parameters. It works if I put it at 0x2400, not if I put it at 0x3400. However, I am not sure what you mean by VRAM buffer, unless you're talking about the "top of VRAM" section reserved for disk access, but I don't think you can move that around, right? I'm loading 1k blocks from disk as PROGRAM files, or at least that's what I'm trying to do. I'm going to have to take a close look at the code again to see if I'm not confusing some of my pointers to the different locations in VRAM. Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 20, 2014 Author Share Posted August 20, 2014 However, I am not sure what you mean by VRAM buffer, unless you're talking about the "top of VRAM" section reserved for disk access, but I don't think you can move that around, right? Nevermind, just figured out that you mean the VDP address that is part of the PAB structure. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 20, 2014 Share Posted August 20, 2014 ..., but I don't think you can move that around, right? You can, but you really need to know what you are doing. It's not for the faint of heart. I believe Harry does it with his XB compiler. I'm loading 1k blocks from disk as PROGRAM files, or at least that's what I'm trying to do. I'm going to have to take a close look at the code again to see if I'm not confusing some of my pointers to the different locations in VRAM. Post some code if you don't mind my looking over your shoulder. ...lee 1 Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 20, 2014 Author Share Posted August 20, 2014 Post some code if you don't mind my looking over your shoulder. Thanks for the offer. These are the relevant functions from the file handling code I've cobbled together (it is heavily inspired by code from Lucien2's Nyogsothep game, so credit should go to him): #define PAB_NAMELENGTH_ADDR (*(volatile int*)0x8356) #define PAB_VDP_LOCATION 0x2400 #define DEFAULT_BUFFER_LOC 0x2C00 // File operations #define OPEN 0 #define CLOSE 1 #define READ 2 #define WRITE 3 #define LOAD_VDP 5 #define SAVE_VDP 6 int dsr_link(int op) { volatile int stat; __asm__ ( "mov %1,r1 \n\t" "blwp @dsrlnk \n\t" "stst %0" :"=r"(stat) :"r"(op) :"r0","r1" ); return stat; } int file_operation(pab* p, int op) { int EOF = 0, stat, err; p->opcode = op; p->flags = p->flags & 0x1F; vdpmemcpy(PAB_VDP_LOCATION, (unsigned char*)p, sizeof(pab)); PAB_NAMELENGTH_ADDR = PAB_VDP_LOCATION + 0x09; stat = dsr_link(; vdpmemread(PAB_VDP_LOCATION, (unsigned char*)p, sizeof(pab)); err = p->flags >> 5; if (err == 5) { EOF = 1; err = 0; } else { if (err) err++; else if (stat & 0x2000) err = 1; } if (err && p->opcode != 1) close(p); return err; } int load_to_vdp(pab* p, const char* filename, int vdp_buffer_location, int n) { p->record_number = n; p->buffer_address = vdp_buffer_location; strcpy(p->name, filename); p->name_length = strlen(p->name); return file_operation(p, LOAD_VDP); } int load(pab* p, const char* filename, char* buf, int n) { int ret = load_to_vdp(p, filename, DEFAULT_BUFFER_LOC, n); vdpmemread(DEFAULT_BUFFER_LOC, (unsigned char*)buf, n); return ret; } Then, for loading I simply use: pab fd; load(&fd, "DSK1.SONGSFX1", (char*)leveltheme, 2624); which loads the song to CPU RAM and actually works. The following however: pab fd; load_to_vdp(&fd, "DSK1.PATT1_0", 0x0000, 1024); is supposed to load 1 frame of pattern definitions to >0000 in VRAM, but doesn't work... As you can see from the code above, the load function simply uses the load_to_vdp function with some defaults, so I'm stumped as the why one would work and the other wouldn't. Both files are in PROGRAM format, by the way. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 21, 2014 Share Posted August 21, 2014 I didn't realize you were coding it in C—nice! I don't understand the __asm__ code, so I'll have to take it on faith that it emits the correct ALC. The fact that load() works certainly implies that it does. The only thing that comes to mind is that the VRAM address of 0x0000, which constitutes a null pointer (I think), perhaps causes a problem. Does it also fail at a non-zero VRAM address? ...lee Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.