Retrac
New Members-
Posts
5 -
Joined
-
Last visited
Recent Profile Visitors
The recent visitors block is disabled and is not being shown to other users.
Retrac's Achievements
Combat Commando (1/9)
9
Reputation
-
Millfork - a new programming language for 8-bit machines
Retrac replied to Retrac's topic in Atari 2600 Programming
I had to read up a bit more on the SC. Some glue code would be necessary for handling the bank-switching and SC ROM routines to read them in. Otherwise I don't see why it shouldn't work similar to the C64 in that regard. (Haven't tried that myself). I think one would just need to load the binary image into a target address, and jump to it. I'm not used to thinking of the 2600 as having streaming input like that. It's a little hard to wrap the head around! -
Millfork - a new programming language for 8-bit machines
Retrac replied to Retrac's topic in Atari 2600 Programming
It has basic segment support for bankswitching. You still have to do things like issuing explicit bankswapping calls, but macros are nice for that. It is possible to implement what I've called fcall() fret() for example, which do a long jump to subroutine from any bank to any bank (pushing return bank and then address) and long return (inverse). Wow, that's an impressive project! I like the use of the cassette. We are almost thinking along parallel lines! I have only a sketch of a game idea. I'm not much of a game designer... so for now this is a game engine? Just scaffolding for other things. I started with wanting to spread out big calculations over multiple frames. Now it's more of a general quest to create a nice programming environment for the 2600. Or to see how much unnecessary abstraction I can fit in 4 KB address space before everything collapses under its own weight The first thing I tried was a Forth-like virtual machine (in assembly, unfinished). It did work. Elegant really. Handled all the bankswitching for me. Multitasking. Even tried virtualized addresses where 16 bits is a flat address space translated into the bank-switching arrangement. But it was terribly slow, kind of like I expected. Probably too slow. (Though it's not *that* bad since the heavy lifting routines should be in assembly and just called by the VM code). But the idea of using a virtual machine may still be worth holding on to, just with native, not interpreted, code. As long as I call my routines yield() (pass control to scheduler) or sleep() (stop running and pass control to scheduler) at least once in a while (such as a routine's outer loop) then I can largely ignore concerns like "will this calculation finish before overscan is done?". Just write medium-sized (1 - 2 KB of code) self-contained routines using a common interface (probably something like get a message, do its calculation, write a message, sleep and repeat). The overhead for that kind of tasking is less than I had worried. Then I just need to write nice modules that respect a common bankswitching arrangement, and behave cooperatively by yield()ing regularly. That way the modules can largely ignore VCS-specific details that aren't relevant to them. And all this silliness is because I'm quite prone to writing spaghetti code if I let myself. Which is fine, even downright necessary, for a 2K or 4K game... but it won't bear out for a big RPG or simulator game with many working parts. I know how you feel! I'm sinking quickly though. -
I discovered something recently, and it seems like it might be of interest to maybe a few of you. It has been discussed on AtariAge for other platforms, but I didn't see anything specific to the 2600. Millfork is a C-like language, with adjustments to make it particularly suitable for 8-bit machines, and it targets the 8080/Z80/6809 as well as its best supported target -- the 6502. It has an optimizing whole-program compiler with some nice tricks that often surprise me at the quality of the code generated. It is only a couple years old and it is a hobby project, but it has some users with NES games and demos in particular. The developer is quite friendly and responsive to language-related issues. There is no 2600-specific support to speak of. The example program given is all of it at the moment which dos the classic coloured bars and doesn't really demand anything fancy. (I've done the same thing in C myself, as have others here, I think.) Still, I've been writing some scaffolding for another project and, on a whim, I thought I'd try to cram some Millfork code in, just to see if it fits and works. I got carried away and ended up rewriting essentially everything in Millfork. So, I guess it was successful! At this point, with some caution, I'd suggest it is possible to write game logic and so on in this. Only a few dozen lines in my graphics kernel are in assembly. Though it doesn't actually do anything yet, besides display a simple monochrome bitmap. Similar to my previous code posted, except in a high level language. With fancy LZSA compression and a framebuffer in RAM Speaking of which, cartridge RAM is a little awkward, but I have some macros that improvise a tolerable solution. (The optimizer does like to cause a headache though. INC is a problem. Careful. Write short routines, annotate them with volatile or inline and so on as necessary.) Here's some 3E bankswitching nastiness which is not at all readable. (Sorry.) It takes a variable and writes to either $3E (bank_ram_reg) or $3F (bank_rom_reg) depending on what segment (>$80 is RAM) it is placed in. It also extracts one of the 4 3E+ slots and writes that into the top 2 bits so it all bankswitches good and right. And memory_barrier() is a hint for the optimizer not to elide or rearrange this operation: macro void swap (void ref v) { if bool(v.segment & $80) { bank_ram_reg = (v.segment & $7f) | ((v.hi & %0000_1100) << 4) } else { bank_rom_reg = v.segment | ((v.hi & %0000_1100) << 4) } memory_barrier() } Gives me a chance to show off its optimization abilities: That's a lot of code above but it becomes, for something in ROM at least: LDA #1 | ((hi(mydata)) & ($C)) << (4) // the assembler can handle that, it's an immediate STA $3F Because Millfork is so low-level it lets you specify the locations of things literally. There is fairly seamless support for inline assembly. You can switch in and out of assembly within a function, or specify the whole function is written in assembly and then invoke some Millfork macros within your assembly. struct mystruct_t { byte a word b } array (mystruct_t) foo[16] @ $F043 // 48 bytes in memory starting at f043 asm void myfunc(byte register(a) char) !preserves_x @ $FCD2 extern // external assembler function that takes a byte in A reg asm void myotherfunc() { nop nop rts } It does not do integer promotion and explicit is casting required. Defaults to unsigned arithmetic. It has structs, unions, user-defined type synonyms and aliases, which are all strongly type-checked. That's really wonderful. The syntax is pretty friendly for the kinds of operations you need to do, with suffixes for extracting bytes out of words (16 bit) as well as 32 bit values, for extracting pointers, and so on. There is no support for recursion and it generally avoids using the stack. That's great because it's the only way you can cram a compiled language into only 128 bytes of general purpose RAM. It does well with ROM'd code. Otherwise it's basically slightly dumbed down C that avoids some of C's less pleasant syntax pitfalls (IMHO). I'll write more about my experience so far if there's interest, and otherwise I'll probably eventually post my code, but for now it's just a hack unworthy of being called an alpha for a bad demo.
-
Hi all. I spent a bit of time over the weekend and finally got my 96x192 bitmap display kernel working. I've decided to ditch DASM and try CA65 and I'm liking it so far. This is my first attempt at a high-resolution bitmap display and I'm fairly pleased with the results, though it does flicker. I've used the new 3E+ bankswitching mode. I've also made some use of the CC65 suite's segment support, which is pretty nifty. As to the bitmap, I think I understand the timing, but it was a fair bit of trial-and-error. I'd like to go back and automatically calculate the position values to prove I got it right. I'm also a little ashamed of how I display the last line, but I just can't seem to wrap my head around the off-by-one that's behind it. Not quite sure what direction I'll take this next, but thought it might be of interest or help to others. Haven't tested it on hardware, but it seems to produce a reasonably standard picture in Stella. If you want to try it yourself and generate your own image, you'll need make, Python, etc. and a 96x192 monochrome PNG image as 'input.png' and then run make. To generate the right kind of PNG, I use GIMP and set it to indexed 1-bit colour mode. I've put the source code up here. I tried to add comments for once, but the timings in the comments may be incorrect. I owe a big thank you to everyone, especially Andrew Davie, on this forum to get me this far. So, thank you! game.3ep
-
Jentzsch/Davie '2600 tile/character graphics engine
Retrac replied to Andrew Davie's topic in Atari 2600 Programming
Thanks for posting this. It's been very instructional! You are both wizards. I'd love your opinion on something. I've been working on a bit of code that displays a bitmap stored in RAM for my game idea. I'm working on rendering proportional text from fonts at the moment. I've been using 3E bankswitching too, but as you say, it is pretty limiting. My code's only a small demo so far, so I'm not experience with how larger programs should be laid out yet. I've thought about creating my own mapper. Probably something like 1 KB pages which can independently be set to a 1 KB RAM read, RAM write, or ROM bank. I think I can see how to do that in hardware. It would allow fast and easy direct copying from RAM and ROM to the bitmap RAM. Anyway, do you think you could you share some insight on what to look for in a bankswitching system, when it comes to choosing or designing one for a large bitmap or tile-oriented game? Did you find 3E limiting or unwieldly for Boulder Dash?
