+mizapf Posted May 1, 2013 Share Posted May 1, 2013 The 9938 has a move command where you specify a rectangle on the screen and where to move it. This is quite fast, as it happens all inside the 9938. See the V9938 manual, section 4.3 (high speed move VRAM to VRAM). Quote Link to comment Share on other sites More sharing options...
RXB Posted May 1, 2013 Share Posted May 1, 2013 Yea drawing for games on the 9938 and 9958 are fast and require very little code once set up is done. A program called XHI for the 9938/9958 had a demo that was very impressive and showed what could be done. A program called Y.A.P.P. made TI Artist look pretty primitive. Funnel Web had a 80 Column version that rivaled most PC word processors in function and form. http://en.wikipedia.org/wiki/Yamaha_V9938 http://en.wikipedia.org/wiki/Yamaha_V9958 Built in Mouse routine (9938) so would make a much better interface using a mouse. Hardware Accelerated commands built into the 9958 but removed the Mouse/Light Pen commands. Yea to bad these are not used they would really make the SAMS more useful to use. Rich Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted May 2, 2013 Share Posted May 2, 2013 The 9938 has a move command where you specify a rectangle on the screen and where to move it. This is quite fast, as it happens all inside the 9938. See the V9938 manual, section 4.3 (high speed move VRAM to VRAM). Hi Michael. I have used the 9938 commands in the past - they are quick but not nearly fast enough for any smooth scrolling of the entire screen. Quote Link to comment Share on other sites More sharing options...
RXB Posted May 2, 2013 Share Posted May 2, 2013 Well the 9958 has the command built into hardware that are faster so some options are faster on the 9958 but you give up the mouse commands. Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 2, 2013 Share Posted May 2, 2013 Tim, please don't spoil my dreams ... :-) I always hoped for a bright 9938 future with games with sophisticated graphics, at least at C64 level. (SCNR) Some ideas: 1. Full screen scrolling should rarely be needed, maybe limited to some rectangular area. 2. Maybe I should really try to benchmark the commands to have some numbers. I already mentioned my benchmark tool which takes the time from the RTC on the Geneve. 3. I already suspected that some commands may be too slow to be suitable for all cases. In my Fractals program I used the fill command to wipe the screen, and I had to split it in two actions (because you cannot fill the whole screen in one go). I remember that I could see when the first and second wipe happened, and everything you can see is too slow, i.e. it happens in times of more than 100 ms. Quote Link to comment Share on other sites More sharing options...
RXB Posted May 2, 2013 Share Posted May 2, 2013 Tim, please don't spoil my dreams ... :-) I always hoped for a bright 9938 future with games with sophisticated graphics, at least at C64 level. (SCNR) Some ideas: 1. Full screen scrolling should rarely be needed, maybe limited to some rectangular area. 2. Maybe I should really try to benchmark the commands to have some numbers. I already mentioned my benchmark tool which takes the time from the RTC on the Geneve. 3. I already suspected that some commands may be too slow to be suitable for all cases. In my Fractals program I used the fill command to wipe the screen, and I had to split it in two actions (because you cannot fill the whole screen in one go). I remember that I could see when the first and second wipe happened, and everything you can see is too slow, i.e. it happens in times of more than 100 ms. On the 9938 or 9958 your just change the screen address and it changes the entire screen instantly. Now for SCROLLING in the XB program XHI written in Assembly for use from XB is has a Scroll routine that uses this method and you get no flicker and the screen scrolls FAST! It also has a draw function like Turtle that is fast and very good for use with sprites. Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted May 2, 2013 Share Posted May 2, 2013 Tim, please don't spoil my dreams ... :-) I always hoped for a bright 9938 future with games with sophisticated graphics, at least at C64 level. (SCNR) Some ideas: 1. Full screen scrolling should rarely be needed, maybe limited to some rectangular area. 2. Maybe I should really try to benchmark the commands to have some numbers. I already mentioned my benchmark tool which takes the time from the RTC on the Geneve. 3. I already suspected that some commands may be too slow to be suitable for all cases. In my Fractals program I used the fill command to wipe the screen, and I had to split it in two actions (because you cannot fill the whole screen in one go). I remember that I could see when the first and second wipe happened, and everything you can see is too slow, i.e. it happens in times of more than 100 ms. Heheheh Sorry I should clarify something. I am talking about scrolling mode 6 or mode 7, which is close to 64K of bitmap data. This is why I have been trying to figure out how the horizontal offset register can be used for scrolling purposes. The datasheet has not enlightened me as I have been unable to figure out how one could use the register to their advantage. I suppose I need to encounter a few more VDP/game battles to raise my experience and manna levels Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted May 18, 2013 Share Posted May 18, 2013 (edited) Here's some 3D graphics on the 9918A for reference. Start around 1:55 to see the flow. http://www.youtube.com/watch?v=-6EaIJOEN8U Edited May 18, 2013 by sometimes99er 2 Quote Link to comment Share on other sites More sharing options...
TheMole Posted May 18, 2013 Share Posted May 18, 2013 I'm still working on the code for a ray-tracer, but things are progressing slowly due to lack of free time. I now have a floating-point free algorithm up and running, but I need to fix some boundary/overflow issues, divides by negative numbers. etc... before it'll render faithfully. It does seem slow though, so I might need to move some of the calculations to asm routines (remember, I'm using gcc), but my gut tells me it should be doable on the TI. If only I had more spare time . Quote Link to comment Share on other sites More sharing options...
youki Posted May 18, 2013 Share Posted May 18, 2013 Something that could help you. Here a Raycaster engine done on a colecovision (same VDP) if i remember well the author is a member of AtariAge. I find it quite impressive. Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted June 12, 2013 Share Posted June 12, 2013 (edited) Newbie mistake (DELETED) Edited June 13, 2013 by Kevan Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted June 13, 2013 Share Posted June 13, 2013 I hope I'm not being politically incorrect by making the following non-purist suggestion, but WHY limit the project to backward compatibility with TI hardware? One person already mentioned the Nano-PEB already as a speed-access solution. Does the F18A video not have a little more 'punch' that could be utilized? I'd be more than happy to shell out some money for a 'Wolfenstein-Like' game. The only problem I see is the severe lack of F18A's. If a game like this became available, you can bet there would be a growing demand for the video upgrade kit. Quote Link to comment Share on other sites More sharing options...
matthew180 Posted June 13, 2013 Share Posted June 13, 2013 The F18A does have more punch and could certainly help in a game like this. The entire rendering system could be done with the GPU which is estimated to be about as fast as a 68000 CPU. I started working on a wolfenstien style raycaster demo, but I just don't have the time right now to get it running. Seems to be a common theme lately. The only problem I see is the severe lack of F18A's. Just to be clear on this, the lack of F18As is not due to them being unavailable. I try to keep 4 in stock all the time and build more as necessary. I am out right now, but I have 4 on the bench to be tested and the stock will be updated soon. To date there are about 100 F18A boards out in the wild. 2 Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted June 13, 2013 Share Posted June 13, 2013 Excellent! I'll check back into your site on a regular basis for the next few weeks. You take PayPal? ; I am out right now, but I have 4 on the bench to be tested and the stock will be updated soon. Quote Link to comment Share on other sites More sharing options...
matthew180 Posted June 14, 2013 Share Posted June 14, 2013 Yes, I have a shopping cart that only uses PayPal for payments. Quote Link to comment Share on other sites More sharing options...
TheMole Posted September 19, 2013 Share Posted September 19, 2013 (edited) I've started working on this again, but to make things easier decided to make a PC version first, as this my first ray caster, and introduce all of the TI's limitations to that code first before porting it over to the TI. This screenshots shows how it renders using the limitations of the TI (TI color palette, 64x64 pseudo-resolution using color table manipulations only, integer math only, etc...). *edit* but just to clarify, this is not a real TI screenshot yet! It looks better when moving around, but to me it looks very serviceable. On the F18A I'll add textures and a custom color palette, hopefully render the slices using the GPU, but for a stock machine this is doable (potential speed issues notwithstanding). I'm currently trying to port it to the TI and the code compiles, but running it just gives all kinds of weird results (graphics memory corruption, weird beeps from the sound chip, hanging after a couple of frames, ...). I'm thinking I run into memory location issues or there might be a small bug in the GCC generated code (jumping to unitialized memory maybe?) but having never really programmed the TI in assembly, I have not enough knowledge of the memory map you need to take into account for a cartridge game. I know text segments (code) are supposed to be at the 8k block starting >6000 and data segments in the lower 8k (>2000), which I think I'm doing, but maybe there are other limitations that I'm not aware of? Basically, I believe the C code is up to snuff and this should almost compile out of the box but I am still doing something wrong and I need to figure out what. Any input as to how to debug this type of stuff greatly appreciated, I'll keep on trucking but wanted to put something out there to keep the pressure on and motivation going . Edited September 19, 2013 by TheMole 2 Quote Link to comment Share on other sites More sharing options...
Tursi Posted September 19, 2013 Share Posted September 19, 2013 I'd suggest using the EA#5 builds out of the compiler to start - you have 24k+8k to work with instead of 8k+8k, makes it a little easier to get around. There are still some bugs in the compiler, so you will have to start stepping through the code to see where it goes south, and work out if it's a compiler or programmer error. Thankfully the issues are few, but they seem to be largely centered on conversion between int and char - changing everything to work with int can help you determine if that's the case. As a couple of debug suggestions: make the compiler keep temporary files (--keep-temp, IIRC) so that you have the .s files to compare against the .c files. Then you can look at the various functions, which makes it easier to keep track while single-stepping, too. It's easier to build it bit by bit, so you can narrow down the function(s) that is not working, but a binary search will get you there pretty quickly. If you can't do that, you can at least set breakpoints on each of the entry points (using the symbol table in the map), and just check them off as you hit them till you find the one it crashes on. That said, your assumption of the memory map is correct, as long as you have 32k attached (I assume you are testing on an emulator, which certainly should). I can try to help you troubleshoot if you want to post the code (so I can generate a listing file) and output. Quote Link to comment Share on other sites More sharing options...
TheMole Posted September 20, 2013 Share Posted September 20, 2013 I can try to help you troubleshoot if you want to post the code (so I can generate a listing file) and output. I would certainly welcome a second pair of eyes. I've attached the full source to this post, as well as the cart image. It compiles against your libti99 for VDP functions, but I haven't included the headers or library. You will need to change a path or two in the makefile in order to build it yourself, but it should be fairly straightforward. I used a relative but hardcoded path for the library's h files in the code itself (all relevant logic is in wolfie3.c), so you'll also need to change that to wherever you keep your headers. At some point I'll move all file paths to the makefile but that's a concern for later. Note that the "pixel plot" function might be horribly broken, I added it quickly but haven't been able to get so far as to debug that just yet. Let me know if anything is unclear, I really appreciate the offer for help. wolfie3.c looks like this: /*************************/ /* TI-99/4A Ray caster */ /* 2013 - Danny Lousberg */ /*************************/ // Includes #include "../libti99-master/vdp.h" // Tursi's libti99, VDP functions #include "../libti99-master/halt.h" #include "lookup.h" // sin/cos/etc... lookup tables #include "tistdio.h" // Quick set of functions for keyboard scanning // Defines #define MAX_INT 32768 #define MAP_WIDTH 24 #define MAP_HEIGHT 24 #define CELL_SIZE 32 // Size of each wall "cube" on the map: width, depth and height #define SCREEN_WIDTH 64 // Number of rays to cast, or number of columns, or slices #define SCREEN_HEIGHT 64 // Height of the rendering surface, in pseudo-pixels; not including bottom area #define FOV 64 // Field Of View, aka how wide the arc of rays needs to be #define SCREEN_DISTANCE 22 // Distance to screen projection plane // Formula for this is (SCREEN_WIDTH / 2) / tan(FOV / 2) #define ANGLE_STEP 1 // Should be FOV / SCREEN_WIDTH, so FOV needs // to be a multiple of SCREEN_WIDTH, effectively // giving a max resolution of 64 for // realistic looking rendering #define SCREEN_BUFFER_SIZE 2048 // Global Variables char worldmap[MAP_WIDTH][MAP_HEIGHT] = { {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1}, {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1}, {1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} }; // RAM buffer for first two color tables, or our 64x64x4bit pseudo-pixel buffer unsigned char screenbuffer[SCREEN_BUFFER_SIZE]; // GCC-BUG?: Where this is used, a normal division would throw // GCC-BUG?: the following "internal compiler error": // GCC-BUG?: compiler error: in subreg_highpart_offset, at emit-rtl.c:1304 inline int asm_div(int dividend, int divider) { int quotient = 0; asm ( "LI r0,0 \n\t" "MOV %2,r1 \n\t" "MOV %1,r2 \n\t" "DIV r2,r0 \n\t" "MOV r0,%0 \n\t" :"=r"(quotient) :"r"(divider),"r"(dividend) :"r0","r1","r2" ); return quotient; } // "Plots" a pixel defined by linear coordinates onto the color table #define pattern_size 8 #define patterns_width 32 #define patterns_height 16 inline void plot_pixel_bitmap(int x, int y, int color) { int pattern_location_x, pattern_location_y, membase_pattern; pattern_location_x = x / 2; pattern_location_y = y / 4; membase_pattern = (pattern_location_x * pattern_size) + ((pattern_size * patterns_width) * y); membase_pattern += (y % 4) * 2; if (x % 2) screenbuffer[membase_pattern] |= color; else screenbuffer[membase_pattern] |= (color << 4); } // Return absolute value of an int int abs(int number) { return (number<0)?-number:number; } // Render a vertical line, called a "slice" in ray casting terms void draw_slice(int column, int slicestart, int sliceend, int color) { int y; for (y = slicestart; y < sliceend; y++) plot_pixel_bitmap(column, y, color); } // The actual ray casting algorithm, called once per frame // Basically, we iterate over each screen column and calculate the angle of view // that is associated with that column. Then we "cast a ray" from the player's position // along that angle until we hit a wall in our map. // We use the distance to that wall to calculate the height of the slice to be // rendered for the current column (the further away, the smaller the slice) void cast_rays(int playerx, int playery, int playerangle) { int column; // column iterator, range: 0-SCREEN_WIDTH int rayangle; int hordist, verdist; // distance to the closest wall in horizontal _and_ vertical direction int mapx, mapy; // temp values for checking a hit on the map, will be copied into hitx,hit int hitx, hity; // coords in map space of the _closest_ hit (either vert or hort) int distance; // distance of the _closest_ hit (either vert or hort) int xstep, ystep; // How many units to step from one wall-boundary to the next int checkx, checky; // coords of the first intersection, before we start "stepping" int side = 0; // First ray, left-most column starts at half the FOV counterclockwise from the player's angle rayangle = playerangle - FOV / 2; hitx = hity = 0; for(column = 0; column < SCREEN_WIDTH; column++) { // Normalize angle to 0-359 range if (rayangle < 0) rayangle += 360; if (rayangle >= 360) rayangle -= 360; // Initialize distance, will be used in comparison later on // and there is a code path that could leave this undefined distance = MAX_INT; // Check for horizontal intersections if ((rayangle != 0) && (rayangle != 180)) { int hit; // Boolean to stop checking for walls // initialize to longest distance as we will be picking the shortest distance // to a hit between the two axis hordist = MAX_INT; // Calculate first intersection point on next cell along ray // First we calculate the Y coord (easiest) if (rayangle > 180) { // ray is facing up in the map; so we put the checkpoint RIGHT ABOVE (hence the - 1) // the boundary of the cell we are in on the Y-axis checky = ((playery / CELL_SIZE) * CELL_SIZE) - 1; checkx = playerx - ( (256 * (playery - checky)) / TAN255[rayangle] ); xstep = -STEPX[rayangle]; // = (256 * -CELL_SIZE) / TAN255[rayangle]; ystep = -CELL_SIZE; } else { // ray is facing down in the map; so we put the checkpoint right AT // the boundary of the cell BELOW the one we are in on the Y-axis checky = ((playery / CELL_SIZE) * CELL_SIZE) + CELL_SIZE; checkx = playerx - ( (256 * (playery - checky)) / TAN255[rayangle] ); xstep = STEPX[rayangle]; // = (256 * CELL_SIZE) / TAN255[rayangle]; ystep = CELL_SIZE; } hit = 0; mapx = checkx / CELL_SIZE; mapy = checky / CELL_SIZE; while (!hit) { // Ray has left the map; need to break out of the loop if ( (mapx < 0) || (mapy < 0) || (mapx > MAP_WIDTH) || (mapy > MAP_HEIGHT) ) { hordist = MAX_INT; break; } // if we hit a non-zero field in the map, we're on the edge of a wall if (worldmap[mapx][mapy]) { hit = 1; hordist = abs( 256 * (checky - playery) / SIN255[rayangle] ); } else { // No wall? Then we take steps to the next boundary and // reset the map coords to be checked checkx += xstep; checky += ystep; mapx = checkx / CELL_SIZE; mapy = checky / CELL_SIZE; } } // Only checked horizontal hits yet, so these are by definition our best guess for // the closest hit. We'll check these values against the vertical results later on // so we store in the variables for the final result for now... hitx = mapx; hity = mapy; distance = hordist; } else { // Angles 0 and 180 have a cos of zero // or in other words, we will never see a horizontal intersection hordist = MAX_INT; } // Check for vertical intersections if ((rayangle != 90) && (rayangle != 270)) { int hit; // Boolean to stop checking for walls // initialize to longest distance as we will be picking the shortest distance // to a hit between the two axis verdist = MAX_INT; // Calculate first intersection point on next cell along ray // First we calculate the X coord (easiest) if ( (rayangle < 270) && (rayangle >= 90) ) { // Going left, check against left boundary of current cell checkx = ((playerx / CELL_SIZE) * CELL_SIZE) - 1; checky = playery - (( (playerx - checkx) * TAN255[rayangle] ) >> ; ystep = -STEPY[rayangle]; // = (-CELL_SIZE * TAN255[rayangle]) / 256; xstep = -CELL_SIZE; } else { // facing right, check against LEFT boundary of NEXT cell checkx = ((playerx / CELL_SIZE) * CELL_SIZE) + CELL_SIZE; checky = playery - (( (playerx - checkx) * TAN255[rayangle] ) >> ; ystep = STEPY[rayangle]; // = (CELL_SIZE * TAN255[rayangle]) / 256; xstep = CELL_SIZE; } hit = 0; mapx = checkx / CELL_SIZE; mapy = checky / CELL_SIZE; while (!hit) { // Ray has left the map; need to break out of the loop if ( (mapx < 0) || (mapy <0) || (mapx > MAP_WIDTH) || (mapy > MAP_HEIGHT) ) { verdist = MAX_INT; break; } // if we hit a non-zero field in the map, we're on the edge of a wall if (worldmap[mapx][mapy]) { hit = 1; verdist = abs( 256 * (checkx - playerx) / COS255[rayangle] ); } else { // No wall? Then we take steps to the next boundary and // reset the map coords to be checked checkx += xstep; checky += ystep; mapx = checkx / CELL_SIZE; mapy = checky / CELL_SIZE; } } } else { // cos of 90 and 270 is zero, so there is never an intersection with a vertical wall verdist = MAX_INT; } // See which of our hits we should take into account // Default was horizontal, but we take vertical if that distance is shorter side = 0; if (verdist <= hordist) { hitx = mapx; hity = mapy; distance = verdist; side = 1; } // calculate slice height based on distance int sliceheight, slicestart, sliceend; if (distance) sliceheight = asm_div(SCREEN_DISTANCE * CELL_SIZE, distance); else sliceheight = SCREEN_HEIGHT; slicestart = (SCREEN_HEIGHT - sliceheight) / 2; sliceend = slicestart + sliceheight; // Start of rendering this slice // Color of wall slice char color; if (side != 1) { // Front view, light colors switch(worldmap[hitx][hity]) { case 1: color = 8; break; // red case 2: color = 2; break; // green case 3: color = 5; break; // blue case 4: color = 15; break; // white default: color = 11; break; // yellow } } else { // Side view, dark colors switch(worldmap[hitx][hity]) { case 1: color = 6; break; // dark red case 2: color = 12; break; // dark green case 3: color = 4; break; // dark blue case 4: color = 14; break; // gray default: color = 10; break; // dark yellow } } // draw the pixels of the stripe as a vertical line draw_slice(column, slicestart, sliceend, color); // Prepare for next column rayangle += ANGLE_STEP; } } void init_patterntable() { // Init all patterns in first two pattern tables to 0xF0 vdpmemset(gPattern, 0xF0, 768 * 2); } void init_nametable() { // Init first two out of three nametable with increasing values vdpwriteinc(gImage, 0, 768); vdpwriteinc(gImage, 768, 768); } int main(int argc, char *argv[]) { int x; int posx, posy, angle; // Init graphics system x = set_bitmap(0); VDP_SET_REGISTER(VDP_REG_MODE1, x); init_patterntable(); init_nametable(); // Initialize player position and angle posx = 18 * CELL_SIZE; posy = 18 * CELL_SIZE; angle = 0; while(1) { // raycasting cast_rays(posx, posy, angle); // blit screenbuffer vdpmemcpy(gColor, screenbuffer, SCREEN_BUFFER_SIZE); vdpwaitvint(); // Scan keys scan_keys(); // RIGHT/'D' pressed, turn right if ( check_key(2,0x2000) ) angle += 2; // LEFT/'S' pressed, turn left if ( check_key(1,0x2000) ) angle -= 2; // Normalize angle to 0-359 range if (angle < 0) angle += 360; if (angle >= 360) angle -= 360; } halt(); return 0; } wolfie3d.zip wolfie3dC.bin Quote Link to comment Share on other sites More sharing options...
Astharot Posted September 20, 2013 Share Posted September 20, 2013 maybe I'm wrong something... It crashes on Classic99 v369 File ini: [usercart2] ; *** Wolfie 3D name="Wolfie 3D" rom0=C|6000|2000|mods\wolfie3dC.bin Quote Link to comment Share on other sites More sharing options...
TheMole Posted September 20, 2013 Share Posted September 20, 2013 maybe I'm wrong something... It crashes on Classic99 v369 File ini: [usercart2] ; *** Wolfie 3D name="Wolfie 3D" rom0=C|6000|2000|mods\wolfie3dC.bin Sorry, I should have been more clear. This is indeed non-working code, posted so Tursi could have a look. I hope to have something for everyone to play with soon enough . Quote Link to comment Share on other sites More sharing options...
Tursi Posted September 20, 2013 Share Posted September 20, 2013 Thanks! I'll be home from work in a few more hours, and will try to take a look tonight! Quote Link to comment Share on other sites More sharing options...
microprocessor Posted November 30, 2013 Share Posted November 30, 2013 (edited) Probably the same technique as the C64: You update the scroll register once each frame, (increment it or decrement it) which moves the screen up/down by one pixel. On the 8th frame, rather you reset the scroll register to 0, and do a 'character level' scroll. If the character scroll is done at the right time, you don't see it, as it's done as the raster is on the flyback. This was very simple on the C64, because the video memory was part of the CPU memory. Not so simple on our box with access via an I/O port. :-( So, probably best to keep the video frame in CPU ram, then write it to VRAM as quick as possible (no real problem). Now, you can be working on your character level scrolling in CPU RAM while doing other stuff (like updating the scroll register, reading joysticks, updating sprites etc). Then, when it's time to reset the scroll register, your next frame has been scrolled in CPU RAM and you just blast it out to VRAM. On msx2 the vertical scroll is done in a more smart way than on c64, you do not need to reset every 8 frame the register and do a massive update of the entire screen. Instead, the display is a circular buffer, so you only need to supply the line that comes into visible area, that require a very little effort and cpu time (you write 128 bytes) and it is doable with out instruction in a very fast way. you basically continue to write the register from 0 to 255 then wrap around to 0. The screen area start to be displayed to the value on the register and ends 211 lines down. It's circular, it's easy! The downside is that also sprites move. this is by design but you need to take into consideration that if you want a scrolling background and also the sprites that are not moving vertically. this lead to a very annoying issue. you need to update sat sprite table when you move the background Edited November 30, 2013 by microprocessor 1 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted November 30, 2013 Share Posted November 30, 2013 On msx2 the vertical scroll is done in a more smart way than on c64, you do not need to reset every 8 frame the register and do a massive update of the entire screen. Instead, the display is a circular buffer, so you only need to supply the line that comes into visible area, that require a very little effort and cpu time (you write 128 bytes) and it is doable with out instruction in a very fast way. you basically continue to write the register from 0 to 255 then wrap around to 0. The screen area start to be displayed to the value on the register and ends 211 lines down. It's circular, it's easy! The downside is that also sprites move. this is by design but you need to take into consideration that if you want a scrolling background and also the sprites that are not moving vertically. this lead to a very annoying issue. you need to update sat sprite table when you move the background This is excellent confirmation of what I've been playing around with using a 9938 and its scroll register! There is another thread for 9938/9958 discussion where Eck and I have been talking about character generation, so I've been distracted with that. How would you recommend sending the 128 bytes and updating the sprite table, i.e., is it better (faster?) to use the VDP high speed commands or just write it out? It seems there are some timing and setup trade-offs to both. Quote Link to comment Share on other sites More sharing options...
microprocessor Posted November 30, 2013 Share Posted November 30, 2013 This is excellent confirmation of what I've been playing around with using a 9938 and its scroll register! There is another thread for 9938/9958 discussion where Eck and I have been talking about character generation, so I've been distracted with that. How would you recommend sending the 128 bytes and updating the sprite table, i.e., is it better (faster?) to use the VDP high speed commands or just write it out? It seems there are some timing and setup trade-offs to both. I do not know the exact timings of a 9900 write so i cannot tell you exactly. However, if you are doing data output outside active area i'm pretty sure you can't overload the vdp. in active area, however you can send data to vdp data port with a rate of 1 byte every 4.5 microseconds (worst case). It obviously depend on the situation. the vdp is more or less free depending on: - sprites, if they are turned on or off - active/inactive area - screen mode (tms modes require less bandwidth) do not bother to send data with a vdp command in progress. the internal vdp logic, will suspend I/O operations of vdp commands if you send data from cpu. about using or not vdp commands, depends . Ask yourself and decide: the data i need to put on the scrolled scanline is already in vram and suitable for vdp commands? if yes, use the vdp commands. ohterwise, set vram ptr a la TMS and push the data. 1 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted December 1, 2013 Share Posted December 1, 2013 I do not know the exact timings of a 9900 write so i cannot tell you exactly. However, if you are doing data output outside active area i'm pretty sure you can't overload the vdp. in active area, however you can send data to vdp data port with a rate of 1 byte every 4.5 microseconds (worst case). It obviously depend on the situation. the vdp is more or less free depending on: - sprites, if they are turned on or off - active/inactive area - screen mode (tms modes require less bandwidth) do not bother to send data with a vdp command in progress. the internal vdp logic, will suspend I/O operations of vdp commands if you send data from cpu. about using or not vdp commands, depends . Ask yourself and decide: the data i need to put on the scrolled scanline is already in vram and suitable for vdp commands? if yes, use the vdp commands. ohterwise, set vram ptr a la TMS and push the data. This makes perfect sense. I am using 9938 Graphics mode six. Comparison based on some existing code I am modifying: Old method: Use VDP high speed command to copy up to 54,272 bytes per scroll. Write the next line to the screen. Very slow. New method: Process and write the next displayable line to the inactive area. A single pixel row requires 512 pixels x 1 pixel high x 1 byte/2 pixels = 256 bytes. A character-height scroll requires 256 bytes/row * 8 rows = 2048 bytes. When it is time to scroll, adjust register #23. 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.