Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

64 Excellent

1 Follower

About damosan

  • Rank
    Chopper Commander

Recent Profile Visitors

2,287 profile views
  1. Huh. They must of added definitions of byte/word in that release...
  2. What version of the compiler are you using? I'm running 2.19.
  3. Yeah - I had aligns in there initially but the linker would complain about the code segment not being aligned. It'd still work of course - I need to modify the linker config to align the data segment to a page. I made some changes to the assembly (using res) but kept the other tables as they were as someone new to 6502 assembly and ca65 may be confused with the repeat/endrep stuff. I'm thinking of new ways to plot pixels where the LDA/ORA/STA sequence will work with absolute addresses instead of lookups. It's going to take a lot of space of course.
  4. Yes... a header named common.h. I forgot to add it to github - it's there now. Sorry about that.
  5. Attached is a link to a graphics mode 8 pixel plotting test harness. It includes all sorts of ways to plot a single pixel on a graphics 8 screen from the slowest (~13 pixels / jiffy) to the fastest I've managed (~332 pixels / jiffy) on an NTSC machine (on a PAL machine the counts are ~17 / ~427 pixels per jiffy). The various methods are all general purpose routines meant to plot a pixel at a given location. There are two "cheat" tests that are optimized for the use case of filling a screen. The project also serves as an example of interfacing CC65 with external assembly code, how to pass variables using the zeropage, and how (in some cases) not to plot pixels. It also includes a routine to plot text on a gr8 screen. This little project was made possible by all the code folks have published over the years as well as the conversations I've had on AtariAge. Next step is to add sprites... https://github.com/damosan314/gr8 gfx.xex
  6. Yeah I've done it with CC65 - once you understand the linker config everything just falls into place.
  7. I would love to see an example of this in action i.e. main program + 1-4 banks. Ideally any data defined within that bank stays there vs. being lumped together with other vars. The bank switcher code would have to reside in main memory outside the window of course and require a stack to remember which bank it came from when making the switch to a new bank.
  8. It's possible to write code that compiles with both compilers depending on what CC65 features you leverage (CC65's atari.h for example). For a time my source code would compile with either CC65 or MOS but I eventually settled on MOS.
  9. If I'm understanding you correctly... d4 = No remainder set d6 = 252 d8 = No remainder set d10 = 250 d12 = 252 d20 = 240 d100 = 200
  10. At first I tried to do this with the C++ compiler and it had issues. So I switched to C. The C compiler is pretty darn good. I started by writing standard C code for everything without applying any of the standard CC65 code optimizations. The code ran pretty quick. Small test apps compile down very small. But once you start writing naïve C apps the binaries get large. "naïve" in this context is standard C e.g. passing structs, using nested structs, structs in general, generic linked lists, etc. As RL2 started to actually take shape the binary was pushing right up against 0xbc00 which is a problem because the static locals area went beyond 0xbc00. So then I started rewriting parts of the game using CC65 style "easy" optimizations such as reducing the amount of structure references across the board, aligning small lookups to page boundaries, etc. In general the standard CC65 optimizations results in smaller code. I compiled the following code snippet in a GR8 test app - with will optimization flags set: void plot_savmsc( word x, word y ) { word address = ( y * 40 ) + ( x / 8 ); byte mask = 0x80; byte current_byte; current_byte = PEEK( SAVMSC + address ); mask = 0x80 / ( x % 8 ); POKE( SAVMSC + address, current_byte | mask ); } word plot_savmsc_test( void ) { word x, y; lbzero( (byte *)SAVMSC, 8192 ); print_at( 0, 23, "Naive plot" ); clear_clock(); for( y = 0; y < 192; y++ ) for( x = 0; x < 320; x++ ) plot_savmsc( x, y ); return getJiffies(); } The above code fills a graphics 8 screen. CC65 ran in 4587 jiffies while MOS did so in 2631. Another test leveraging lookups saw CC65 running in 2663 jiffies while MOS ran in 396 jiffies. I have C code that leverages page zero that brings CC65 down to 664 jiffies while MOS runs in 358. The compiler is pretty good. You don't have complete control over the binary like you do with CC65. At first the game font was aligned to a 1k boundary via a clang attribute. This allows you to create font or player missile regions easily enough but it tends to create gaps in the binary. As you write additional code these gaps get filled. I decided I needed a custom loader for the game so I didn't have to rely on the compiler aligning large memory objects. So I took the ramrom.asm and modified it to act as a font / game loader. I will say the size optimization is pretty good - single use functions are basically inlined to save subroutine pre/post code. I normally compiled with -Oz; when I compiled without this flag the raw binary was very large (over 50k). When I started generating map files to see what the compiler was doing I was shocked to find a three line routine took ~3k of ram...but the compiler was doing the right thing. Dead code removal is 100%. The memory allocator is pretty good. It seems that each malloc() takes 2 bytes to keep track of things. I could be wrong here. Clang has a rather...extensive...syntax on inline assembly. It's *far* easier to write assembly functions and just link them in. Declaring variables VOLATILE is important. My only real complaints are as follows: 1) How the compiler handles locals in a function. It basically creates a memory area named <function_name>_stk (or something like that) which wasted about ~450 bytes. I know software stacks are slow...but I'd like the option to have one. The heap starts at the end of the *_stk definitions. 2) The included supplied printf() is a full implementation so eats a ton of memory (like 4k). If your code references LONGs you will bring in the math libraries for 32+ bit integers. That's a ton of code. Sticking to bytes / words keeps things small. The a_printf() function in the game supports strings, bytes, words, and characters. 3) (Not really a complaint) But the standard library is very small. I had to create fopen()/fclose()/fread()/fwrite() using CIO calls. Not a big deal if you have some C and Atari experience. Atari programmers new to C may wish to stick with CC65 in the beginning. 4) Using extended memory will require some hacking compared to CC65 (once you figure out the linker file format CC65 makes this almost easy).
  11. Then fix it. It works well enough for the standard d4, d6, d8, d10, d12 dice it was meant to support.
  12. Here's what I used in my assembly language roguelike project. It's not as efficient as it should be (see dice2 loop to calculate the modulus) but it works. Usage: ldx #3 ldy #6 jsr dice sta strength ;;; generate player's strength ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; dice - rolls X dice of Y sides storing result in accum. TRND & TSIDES ;; are byte variables defined elsewhere. ;; dice lda #0 sta trnd ; temp result bucket sty tsides ; number of sides on the dice dice1 lda RANDOM ; get random number from hardware sec dice2 sbc tsides ; subtract the number of sides bcs dice2 ; carry set? Subtract again! adc tsides ; now accum = n mod m (0 .. m-1) clc adc trnd ; add accum to trnd sta trnd ; store it back to trnd inc trnd ; increment trnd by one so n mod m range is (1 .. m) dex ; another dice? bne dice1 ; yup, do it again! lda trnd ; store temp random to accum and return rts
  • Create New...