Jump to content

damosan

Members
  • Posts

    142
  • Joined

  • Last visited

Everything posted by damosan

  1. As an aside. I find that as a long time C programmer I tend to introduce C-isms into code. Thanks all.
  2. Nice. Thank you. I have lots to learn... Your version is twice as fast as the original (10 objects over 300 screens in just under 300 jiffies vs. 597 jiffies for the first cut).
  3. This is what I ended up with...certainly not the best but it works and smokes what cc65 was doing. The idea here is that I have a graphics buffer I'm writing to (destination) and a sprite that I'm reading from (source, 16 bytes). So I basically loop 8 times writing two bytes to the graphics buffer boosting the pointers at the end of the loop. Thanks for the comments above btw. .export _plot_bitmap_fast destination = $d4 ; address of gfx buffer source = $d6 ; address of 16 byte array .proc _plot_bitmap_fast ldx #8 ; 8 rows loop: ldy #$00 ; two updates - index 0 then 1 lda (source), y eor (destination),y sta (destination),y iny ; point to second byte lda (source), y eor (destination),y sta (destination),y inc source ; increment source by one bne nextincrement inc source+1 ; and source high byte if required nextincrement: inc source ; increment source address by one bne boostpointer inc source+1 ; and source high byte if required boostpointer: lda #$28 ; increment destination address by 40 clc adc destination sta destination bcc nextloop inc destination+1 nextloop: dex ; modify loop counter bne loop ; and write the next row of bytes exit_fast_plot: rts .endproc
  4. inc destination+1 is a 3 byte instruction right? Then wouldn't it be bcc *+3? I saw an example online where they state to jump over a 2 byte instruction you need to do *+4... Is that correct? Learning, learning, learning...
  5. I'm continuing to play around with A8 graphics. I'm working on a sprite plotter (as have many before) and I have the following snippet of assembly: lda #$28 clc adc destination sta destination bcc *+6 inc destination+1 This code runs after I plot two precalc'd sprite bytes and I want to increment the destination by 40. Then I plot the next two bytes...then increment destination by 40.... Destination is, of course, a 16 bit value. What I'm seeing on the screen is the sprite spread out over a single line so this isn't doing what I think it's supposed to. ?
  6. damosan

    Wanted: Amiga

    I'd like to add an Amiga to my 68k collection - I'd prefer a A600 or A1200 in good working order. Thanks.
  7. This seems to be a relatively common issue for folks. It certainly was for me when I started playing around with CC65. Here's an example, CC65 linker config file, that should work out of the box. The following is a short example on how to use graphics mode 8 from CC65. Before we get too far we need to create a few typedefs and defines. These elements make later code easier to read (IMHO). If you're familiar with ACTION you'll recognize what BYTE and WORD represent. I leave it as an exercise to the reader as to the utility of upper casing BYTE and WORD. typedef unsigned char byte; typedef unsigned int word; #define SAVMSC *((word *) 0x0058) SAVMSC is a pointer to the lowest addressable area of screen memory. It's a zero page variable maintained by the operating system of the Atari 8bit. There are plenty of resources elsewhere discussing the importance of zero page variables, why you want to use them, as well as their relative lack of availability from within CC65 (you can use them, but the OS and the CC65 runtime consume a lot of page zero). With that out of the way we can now call a CC65 library routine to enter graphics mode. word gfxbuffer; _graphics( 8 + 16 ); gfxbuffer = SAVMSC; RAMTOP = RAMTOP - 32; _graphics() is an Atari 8bit specific routine that is part of the relatively small Atari specific library that comes with CC65. The single parameter operates in a similar fashion to the Atari Basic GRAPHICS command. In this example the parameter being passed to _graphics() is 24 (8, the graphics mode, plus 16 for full screen). The next line of code saves the current value of SAVMSC to a global word variable named gfxbuffer. Finally I modify RAMTOP by reducing it 32 pages to protect the ~8k of space used by the graphics 8 screen. What's a page? In the 6502 world a page of memory is 256 bytes. Do the math...256 * 32 = 8192 bytes. So. We have our defines. We have code to enter graphics mode 8. How do we use it? The resolution of a graphics 8 screen is 320 x 192 pixels. How does the Atari manage that much space with only 8k of ram? Graphics 8 screen memory is laid out as a colletion of 192 rows of 40 bytes each. Each byte contains 8 bits - so 40 bytes * 8 bits per byte = 320 pixels. Each pixel on the graphics 8 screen is represented by a single bit. This is why, by default, you can only use one color on a graphics 8 screen. Yes. You can do artifacting on tube monitors to get more colors and YES you can use DLIs and the like to force color changes on certain lines but those are advanced topics. We're going to keep things simple for the time being. Instead we're going to talk about plotting pixels on the screen or in other words how do you turn on the pixel at coordinate (100, 100)? This is where bitmasks come in. In my early days of using CC65 I wrote code like the following. static const byte bitmasks[ 8 ] = { 0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100, 0b00000010, 0b00000001 }; void plot( word buf, word x, word y ) { byte bitmask_to_use = (byte)x % 8; word b = buf + ( y * 40 ) + x / 8; byte current_byte; if( x > 319 || y > 191 ) return; current_byte = PEEK( b ) | bitmasks[ bitmask_to_use ]; POKE( b, current_byte ); } The function is pretty straight forward - it accepts a buffer to graphics memory and the X and Y coordinate of where to plot a pixel. I calculate the bitmask to use by using the modulo (%) operator on the supplied X coordinate. Remember that there are eight graphics 8 pixels packed into each byte - so, for example, if the passed X coordinate had the value of 3 the result of X % 8 would equal 3 - or the 4th bitmap in the bitmasks array. The next line creates a variable B which points to the byte that is to be modified. In this example we, naturally, want to multiply the Y coordinate by 40 (remember - each row in graphics 8 is 40 bytes long) and then add the result of the X coordinate divided by 8. Afterwards we do basic clip checking to ensure the coordinates can be plotted and then we OR the bitmask with whatever value is presently in RAM. *BOOM* We plot a pixel. If you're coming back to the 8bit world after about 30 years you may have forgotten the very basic fact that the 6502 doesn't have instructions to multiply and divide so the CC65 library has functions to do that sort of math for you. Resulting in slow math. Slow drawing. Pain. Suffering... Here's a better version. word indexes[ 192 ]; void init_graphics( void ) { byte i; word index = 0; _graphics( 8 + 16 ); for( i = 0; i < 192; i++, index += 40 ) indexes[ i ] = index; } void plot( word buf, word x, word y ) { byte current_byte, bitmask_to_use; word b; if( x > 319 || y > 191 ) return; bitmask_to_use = (byte)x % 8; b = buf + indexes[ y ] + ( x >> 3 ); current_byte = PEEK( b ) | bitmasks[ bitmask_to_use ]; POKE( b, current_byte ); } In this example we define a global array of words named indexes. Each element in this array contains a memory offset for each row of a graphics 8 screen. In the init_graphics() function we initialize the array. In this second version of plot we perform the clipping check first and then proceed to the math. We still leverage modulo (%) to determine which bitmask to use but our calculation to determine the byte offset is dramatically different. In this case we leverage our index to quickly determine the offset for the specified Y coordinate and instead of dividing X by 8 we instead shift the value of X three bits to the right. This has the effect of dividing the X coordinate by 8 - but quickly. How quickly? The first plot() function which used multiplication and division is able to plot ~13.6 pixels per jiffy (or about 816 pixels per second) while the second, faster, version can do ~15.5 pixels per jiffy (or about 930 pixels per second). There are, of course, other optimizations and the use of assembly language to get even faster. Here's a complete example. Your results may vary but with the emulators I use the general plot routine does about 35 pixels per jiffy (2100 pixels per second) while the optimized version does 3.072 pixels per jiffy (or 184,320 pixels per second). Anyway - I hope this is helpful to someone. I have examples coming up of how to print text on graphics 8, plotting sprites, some graphics 7 stuff, double buffering in graphics 8 and 7, .... Etc....etc...etc... Anyway - here's the example. I've also attached the source code and the linker config at bottom. /* * build with: * cl65 --static-locals -t atari -Osir -C atari.cfg -o gr8simple.xex gr8simple.c */ #include <atari.h> #include <peekpoke.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <stdbool.h> typedef unsigned char byte; typedef unsigned int word; /* * important memory locations in the Atari OS */ #define SAVMSC *((word *) 0x0058) #define COLOR0 *((byte *) 0x02c4) #define COLOR1 *((byte *) 0x02c5) #define COLOR2 *((byte *) 0x02c6) #define COLOR3 *((byte *) 0x02c7) #define COLBK *((byte *) 0x02c8) #define RTCLOK *((byte *) 0x0012) #define RT2 *((byte *) 0x0013) #define RT3 *((byte *) 0x0014) /* * bitmasks for plotting gr.8 pixels */ static const byte bitmasks[ 8 ] = { 0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100, 0b00000010, 0b00000001 }; static word indexes[ 192 ]; /* * Returns the current jiffy count. A jiffy is approx. 1/60th of a second. */ long jiffies( void ) { return RTCLOK * 65536 + RT2 * 256 + RT3; } /* * routines to change the default colors - nothing fancy. */ void clear_screen( word buf ) { bzero( (byte *)buf, 7680 ); } void regular_border( void ) { COLBK = 0x00; } void regular_drawing_color( void ) { COLOR1 = 0x0e; } void regular_background_color( void ) { COLOR2 = 0x00; } /* * go into full screen graphics mode 8, change colors * to white on black, and calculate our indexes. */ void init_graphics( void ) { byte i; word index = 0; _graphics( 8 + 16 ); regular_border(); regular_drawing_color(); regular_background_color(); for( i = 0; i < 192; i++, index += 40 ) indexes[ i ] = index; } /* * generic pixel plotter */ void plot( word buf, word x, word y ) { byte *b; if( x > 319 || y > 191 ) return; b = (byte *)(buf + indexes[ y ] + ( x >> 3 )); *b |= bitmasks[ (byte)x % 8 ]; } /* * optimized to plot a 320 pixel line at a * specific Y coordinate */ void plot_optimized( word buf, word y ) { byte *b = (byte *)(buf + indexes[ y ]); byte i = 40; while( i-- ) *b++ = 0xff; } void main( void ) { word row, col; long s1,s2,f1,f2; init_graphics(); clear_screen( SAVMSC ); s1 = jiffies(); for( row = 0; row < 192; row++ ) for( col = 0; col < 320; col++ ) plot( SAVMSC, col, row ); s2 = jiffies(); clear_screen( SAVMSC ); f1 = jiffies(); for( row = 0; row < 192; row++ ) plot_optimized( SAVMSC, row ); f2 = jiffies(); _graphics( 0 ); printf( "General Plot = %6ld\n", s2 - s1 ); printf( "Pixels/Jiffy = %6ld\n\n", (long)(192 * 320) / (s2 - s1 )); printf( "Optimized Plot = %6ld\n", f2 - f1 ); printf( "Pixels/Jiffy = %6ld\n\n", (long)(192 * 320) / (f2 - f1 )); while( true ) ; } gr8simple.c atari.cfg
  8. *sigh* I normally run Atari800 in 800 mode with no basic. The executable displayed wonky results. I restarted Atari800 in XL mode and got the same result. I *accidentally* restarted Atari800 in XL mode with basic. Exited out. I then restarted A800 in XL mode with no basic and my executable and everything worked like it should. I tested a few more iterations and I'm suspecting Atari800 isn't my friend. Everything is working now the way it should. Thanks all.
  9. I tried reducing ramtop by 32 after each call to _graphics() but that changed things only slightly. For one of the display lists it seems like the LMS is pointing back to the beginning of the buffer instead of where it should be. The display isn't garbled with random data. Anyone know of some example code for a double buffered graphics 8 display? C/ASM are fine.
  10. Alright - so I thought I'd take the easy route and call _graphics() twice - saving SAVMSC, etc. The idea is that I could swap the generated display lists during a vertical blank. Well - it's not working. The first screen isn't garbled, per se, but it appears to be duplicating part of the screen. The second screen works as advertised. I'd appreciate someone looking at the code and pointing out obvious stupids. Thanks. d.c atari.cfg
  11. The only problem I had/have with 68k assembly are the 405 variations of branch instructions for signed and unsigned data. Or at least it seemed that way when I first started learning it years ago. I came back to it this year to play around with hi res graphics and for the most part it was a piece of cake...except for the branch instructions.
  12. I agree! I use Altirra on my Windows machine but this particular Linux box I'm working with is a small ARM based rig so I stick with Atari800 on that. I'm planning on building a new machine in the future and will run Altirra on that one.
  13. The 4k page boundary...that's almost certainly the issue as I'm simply using a random global byte array for the buffer. The cleaner solution is probably to build a second dlist for the buffer in question and then swap them during a vblank (if a swap has been requested). Thanks.
  14. I'm using CC65 on Atari800 (not that it matters all that much...I hope). I'm working on something that would benefit from double buffering in graphics 8. I have memory buffers I write to and then update the display list word to point to the addresses. One buffer displays fine while the other is "mostly" fine...but with random garbage in it for a segment of the screen. The underlying buffer is good from a data perspective but something is out of whack when it comes time to display. I suspect I have to do more than just update the dlist pointer to the memory region in question. Any pointers? Is there something else in the dlist that I have to swap out? I thought about creating two dlists and swapping them back and forth but wanted to see if there were any other things I should/could be doing. Thanks.
  15. I hacked the following together and it does the trick with the images I'm editing via Gimp. Error checking? What's that? D. #!/usr/bin/python import sys if len( sys.argv ) != 3: print "usage: pbm2raw inputfile outputfile" else: input_data = open( sys.argv[ 1 ] ).readlines() with open( sys.argv[ 2 ], "wb" ) as output_file: output_file.write( input_data[ 3 ] )
  16. Thanks! I was about half way done with a Python app.
  17. I'm working on a game using graphics 8 - are there any Linux utilities out there that will let me paint screens at the proper resolution and save to a format convertible to graphics 8? Thanks.
  18. I agree with this - depending on what you want to do CC65 is a PITA to setup and get running right. On top of that it generates some pretty...busy...code. That's due more to the lineage of the compiler though. I'd be happy with a minimal C[1] that compiled down to decent asm. [1] Minimal being something that supports pointers, standard 1/2/4 byte integers, 1d arrays, was easy to setup for different situations e.g. 400/800, XL, XE, extended memory, etc.
  19. Any particular model? What about the connectors and such? D.
  20. Hello. I have a 220v Megafile 30 running off of a 220->110 step down transformer. Recently the drive will power up and run fine for a random amount of time ranging from 10 minutes to hours and then just go dark. It seems like a PSU issue to me. Quick question - is it possible to replace the 220 PSU with a 110v version from Best (preferred option)? Or should I order a 220v version? Thanks.
  21. When he first posted the vid and announced the FB group he was surprised at the popularity. I think there's a market for something like this but i don't know how large it is. Anywho - the topic of USB ports came up and the team basically said "no thank you...not right now." In a way I can understand their logic which centers on "It's like building a TCP/IP stack" (difficult) and "no matter how limited we say it is in the beginning we'll get non-stop complaints because Keyboard X doesn't work." It just seems like a bad move not to include any...especially when you can add a Pi Zero daughter board to act as an interface to the universe. Dave and company didn't want to go that route either. Which I get. I'll eventually get one to play around with - but I already have 8 Ataris when I want that nostalgia fix. That said - I'd love to see a 68k version built.
  22. Yeah I looked into those - they're pretty slow, at least for pixel / line drawing. I played around further and managed to figure out how to plot points directly and got about a x5 performance boost (each pixel was two pointer lookups and an eor). Someone knowing what they're doing could probably get it going faster but I was pretty happy. From there I went on to implement Horizontal and Vertical line routines which calculated the first pointer and then simply incremented by $80 for each row, etc. That was pretty quick. Color will be next once I get a flatscreen capable of 15khz. It was a fun experiment - now I'm looking at playing with the trace flag to implement a form of threading though I suspect that'll be somewhat slow. Who knows. I've run into a few example co-op multi-tasking examples that I may start with -- implementing a lightweight scheduler and removing the co-op aspects. Who knows. At the end of the day 68k assembly is a fun way to program on the ST. D.
  23. I've been searching for a short tutorial on doing high res graphics via 68k assembly. I've found a few good writeups on low/medium res and I've tried to tweak them to work in high res but things keep exploding. I've written a few linea hacks but the performance is, well, pretty bad for basic line drawing. Anyone have any links to this topic? Thanks.
  24. This. I like cc65 but the linker config is a real pain when you're first starting to use the compiler. What linker config file are you using?
×
×
  • Create New...