Jump to content

Search the Community

Showing results for tags 'rb+'.

More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


  • Atari Systems
    • Atari 2600
    • Atari 5200
    • Atari 7800
    • Atari Lynx
    • Atari Jaguar
    • Dedicated Systems
    • Atari 8-Bit Computers
    • Atari ST/TT/Falcon Computers
  • Gaming General
    • Classic Gaming General
    • Classic Computing
    • Modern Gaming
    • Prototypes
    • Arcade and Pinball
    • Emulation
    • Hardware
    • Gaming Publications and Websites
    • International
  • Marketplace
  • Community
  • Game Programming
  • Site
  • Classic Gaming News
  • The Club of Clubs's Discussion
  • I Hate Sauron's Topics
  • 1088 XEL/XLD Owners and Builders's Topics
  • Atari BBS Gurus's Community Chat
  • Atari BBS Gurus's BBS Callers
  • Atari BBS Gurus's BBS SysOps
  • Atari BBS Gurus's Resources
  • Atari Lynx Programmer Club's CC65
  • Atari Lynx Programmer Club's ASM
  • Atari Lynx Programmer Club's Lynx Programming
  • Atari Lynx Programmer Club's Music/Sound
  • Atari Lynx Programmer Club's Graphics
  • The Official AtariAge Shitpost Club's Shitty meme repository
  • The Official AtariAge Shitpost Club's Read this before you enter too deep
  • Tesla's Vehicles
  • Tesla's Solar
  • Tesla's PowerWall
  • Tesla's General
  • Harmony/Melody's General
  • ZeroPage Homebrew's Discussion
  • Furry Club's Chat/RP
  • PSPMinis.com's General PSP Minis Discussion and Questions
  • PSPMinis.com's Reviews


There are no results to display.

There are no results to display.


  • AtariAge Calendar
  • The Club of Clubs's Events
  • Atari BBS Gurus's Calendar
  • ZeroPage Homebrew's Schedule

Find results in...

Find results that contain...

Date Created

  • Start


Last Updated

  • Start


Filter by number of...


  • Start










Custom Status



Currently Playing

Playing Next

Found 7 results

  1. I've been meaning to make such a post for quite a while now but I always either put it off or thought it'd be too much work. Whatever the reasons, it simply never got done. So I thought I'd try to make an effort towards it and enrich it if people still have questions or some steps are missing/confusing. Don't forget to click on the pictures to show them full size. Windows guide Step 1: obtaining rb+ Even from the start there are multiple choices: you can either download the latest version from github or bitbucket, or download the latest installer. The difference between the installer version and latest is that the installer is generally lagging in features but is more stable and as bug free as possible. I'll cover the latest version for now and the installer at a later point. If you're proficient with git you can always clone the repository instead of downloading it, so keeping up to date will simply require a pull/merge action (also it's much faster to do pull/merges than downloading the archive from scratch every time just to update a few files, as is usually the case when updates are pushed). Otherwise, go to your favorite site: github or bitbucket. For github just press the "clone or download" button and "download zip" at the pop up. Save the zip file. Once the download is done, you can unzip the file pretty much wherever you like (and have permission to). You'll then have a lot of files inside a folder called "bcx-basic-jaguar". You can rename that folder too of course. Step 2: first steps You're now ready to start using the Basic! Open a Windows command prompt, cd to the location you unzipped the folder (for example type "cd c:\rb+" if you unzipped to c:\ and renamed the folder to rb+) and build a sample project by typing "build nyandodge". The project should build and virtualjaguar should appear on screen with the game running: [EDIT] If you're running 32bit Windows, you'll need to replace virtualjaguar.exe (inside the "bin" folder) with a 32bit version, latest releases are here. Thanks to neo for the heads up! (for what it's worth, I'm not using Windows command prompt, but instead I've switched to using cmder which is a reskinned ConEmu with some added stuff - so many choices again! Again, it's not mandatory to use conemu/cmder, simple cmd.exe will do the trick.) If you want to create a new project just go to the console again and type "build <myproject> new", replacing <myproject> with the name of your project (no spaces in the name please!). Building the project should give you something like this: Now you can navigate to the "projects" folder, open the folder that matches the name of your project, open <myproject>.bas and start editing the file! Keep the console open so you can rebuild the project every time you wish so. Step 3: streamlining If you stopped reading the guide on step 2 and started trying things out you'll soon realise that it switching between editor and console to edit and build can become tedious. So let's make this less painful! (note that all these steps are not mandatory, you can configure your own environment as it suits you if you think this is clunky). First of all, go and download Notepad++. It's free, open source (not that we care much!) and it's got a lot of neat features built in. After installing and running it, it's time to customise it. Open the plugin manager from the menu: Go to the "installed" tab and check if "NppExec" plugin is installed. If not, go to "Available" tab, find the plugin and install it. [EDIT] There are high chances you won't be able to see an available plugins list, and NppExec won't be available to you. If this happens then read neo's post and download the plugins manually. Let's add some syntax highlighting. Enable "Visual Basic" from the Language menu. It's not 100% matching the syntax of rb+ but it's good enough! [EDIT] Alternatively you can use OMF's syntax highlighting efforts. To install (thanks to Gemintronic for the test and guide): In Notepad++ go to "Define your language..." and use the [ Import ] button on the .xml file omf made In the "User Defined Language" window select Reboot Basic Plus and SAVE AS the language. I called it "rB+" Now in the languages menu drop down list you'll see "rB+". Select that like it's hot. Now, let's add the magic bits! Bring up the "execute" dialog from plugins->nppexec->execute menu or hit f6. Add the following 3 lines to the command prompt: npp_save CD $(CURRENT_DIRECTORY)\..\.. build.bat $(NAME_PART) This will save the current opened file, go to the rb+ top directory and build the code. A small window will also pop up at the bottom of the notepad++ window, informing you of the build process. So you can catch any errors or warnings there. After the first time you do this you should be able to hit ctrl-f6 to build instantly without having to open the dialog box again! Success! Finally, if you want to change the shortcut keys, go to the shortcut mapper dialog: Then click on "plugin commands" tab, scroll down till you find "execute" and "direct execute previous", click those, then "modify", then choose your favorite key or key combo (personally I use F7 for direct execute previous and never bother with execute). Done! Addendum 1: vim setup Well, you can't just hate something that originates from the Atari ST, can you? (if you don't believe me, just check out wikipedia) Love it or hate it vim is one of the most used editors on the planet, but there's so many extensions and "cool" stuff you can do with it that it bewilders a lot of people. Still, if you can stick to learning 10-15 shortcuts and tricks that's all you're going to need in your lifetime. So let me show you how you can build rb+ stuff with it like we do with Notepad++. First of all, vim auto detects the file syntax from the extension, so we were lucky to use the .bas extension! It uses some flavour of basic which is "good enough" for our purposes. However if anyone wants to try customising the syntax highlight - good luck and please share the results with the rest of us! Here's how it looks on my machine: Now vim being an old dinosaur does actually have support for compilation from the editor and opening a window with any errors and jumping into them. We just have to ask it politely! So open your vimrc file (this can be in various locations, on linux it should be ~/.vimrc, on Windows it's usually on c:\users\<username>\_vimrc) and add the following: """""""""""""""""""""""""""""""" " Lifted from https://hero.handmade.network/forums/code-discussion/t/709/p/4135 and hacked around a bit by ggn " Ensure the buffer for building code opens in a new view set switchbuf=useopen,split comp gcc " Build helper function function! DoBuildBatchFile() " build.bat set makeprg=build " Make sure the output doesnt interfere with anything " (ggn): the ! is needed so copen won't open a window with the first " warning/error silent make! " Open the output buffer "copen cwindow echo 'Build Complete' endfunction "Go to next error nnoremap <F6> :cn<CR> "Go to previous error nnoremap <F5> :cp<CR> " Set F7 to build. nnoremap <F7> :call DoBuildBatchFile()<CR> """""""""""""""""""""""""""""""" So what does that do? Firstly it defines some mumbo jumbo vimscript function to tell it to execute a file called build.bat - this should be on the same directory vim was run from, so typically the directory your .bas file exists. Then it maps key F7 to do run build.bat and if any errors occur, open a window to show them. (that window can be opened using :copen regardless). In the case of errors, F5 and F6 will jump to the previous and next site of error. Beware though, this will jump at the translated .c file, not our .bas file! So there might be some initial confusion about this at first but it is usuable with a bit of practice . Before trying this out though you also need to create a build.bat file. Here's what I use: cd ..\..\ call build myproject And that's it. Of course you can extend this to have multiple build.bat files and map different keys to run them - for example you might want a clean build, or build+send to skunkboard, or just build (BOSSMODE). Addendum 2: 4coder setup 4coder is an up-and coming editor developed by a single person (Allen Webster). While it's focused on C/C++ so far it has been my main editor for a few months now. I quite like the smooth scrolling, quite sensible defaults, and keybindings, although learning to select/copy/paste can be bewildering at first! Also it's commercial with a demo version free to try which is currently what I use. It still has a few nuiances which I've pointed out to the author and it's nice to see they'll get addressed so I'll quite possibly buy this soon. Anyway, it turns out it's quite easy to use with rb+. There's no syntax highlight for it yet (although it really doesn't bother me) but you can automate your builds easily. It turns out that the editor actually has a built-in shortcut to call a "build.bat" as the author is a fan of this method! So simply use the sample build.bat I mentioned above in the vim section and hit alt-m. That's it! Done! That's the end of the guide for now. If things are missing or are still not clear, leave a comment below and I'll address and update this post when possible.
  2. A language like this is not much worth if there aren't any supporting tools that can help create supporting files like graphics or audio files. Since this question keeps popping up in various threads I thought I'd gather everything in a thread of its own. Please share any other packages I missed/forgot (also I'm not up to speed with Linux/Mac tools, so suggestions welcome there too - especially free ones): Graphics editors grafx2 (1,2,4,8bpp images) (Windows, Linux, Mac) ProMotion NG (only for 4bpp/8bpp images) (Windows) - there's a free version with reduced functionality but it's still pretty good The gimp (1/2/4/8/16/24bpp images) (Windows, Linux, Mac) Paint shop pro 8 and earlier (1/2/4/8/16/24bpp images) (Windows) (commercial, but v4.12 is free AFAIK) Graphics gale (1/2/4/8/16/24bpp images) (Windows) - used by a few friends to create sprites. Also supports animation so it might be handy to create spritesheets. Krita (Windows, Linux, Mac) - Only for 24bpp images. paint.net (Windows) (only for 8bpp images) Audio editors Audio is much easier to handle as rb+ uses sox to import sounds which handles most major formats including wav, mp3, etc. With that in mind here are a few audio editors for Windows: Audacity (Windows, Linux, Mac - Free) Wavosaur (Windows - Free) Goldwave (Windows - Free trial available) Cool edit pro (Windows) - Actually I'm not sure this is free or not, any volunteers to try the old versions out? Wavepad (Windows) - Free for non commercial use. So watch out if you're going to burn carts or CDs! General tools bfxr - move the sliders around, hit "mutate" and fiddle till you have your own personal sound effect (or until you annoy people enough to tell you to shut the hell up)! Chiptone - What do you mean you want MOAR KNOBS? Oh well, here you go then! Misc opengameart.org - Don't have a graphics artist in your team? Is your graphics art skills equivalent of womble with a crayon? Have a look here then, lots and lots of free and quality art await to be adopted! freesound.org - a very large library of sound effects and many recorded sounds incompetech - Kevin McLeod's site offers a vast array of royalty free music. I find it a valuable resource for good quality recordings.
  3. Ah, Dropbox. I hate it. First time I tried to use it, it was because some friends wanted to share their project with me. I ended up deleting all files in the folder (because modern hipster tool programmers can't code for shit apparently) and everyone scrambling to find backups because back then there was no undo on deletion. Ossom service and all. Anyway, people told me that they actually try to use rb+ with dropbox to sync their projects. The problem apparently is that some times during build, the process will fail, usually when makelink is running. I can only assume that dropbox is trying to sync the build folder and at the same time build.bat is running and is trying to overwrite files in use by the sync. After offering my condolences for using such a shit program, I can offer a fix for this: You need to make dropbox not sync the "build" folders inside your projects. Now, because hipsters can't code for shit, there's no clean way to do this, but you have to use a workaround apparently. It is explained here. In short, you need to create a folder called "build" in your dropbox root folder, then make it un-syncable, THEN move it to the project folder. It will probably work. I didn't try it. I'm not sure it'll fix the random build errors people report. Try it and tell me. (nope, sorry, really dislike dropbox - whatever happened to good old floppies to sync files between computers? )
  4. At its conception rb+ was very closely tied to raptor, which uses the U235 Sound Engine. Raptor has its own way of doing things and audio is no exception. Including a tune meant opening rapapp.s and adding it there, adding samples would mean converting them to the right format by hand and then feeding it to your project AND editing another .s file... Let's say that early adopters found all this bewildering and counter intuitive . Gradually and with user feedback things improved: rb+ commands were added to give better control for playing music and triggering sfx, assets.txt was added so importing became less of a pain (you can now even feed it an mp3 and it'll convert to the format you need). In general, things improved - not perfect but still. Then a second audio engine was added! People got sick of .mod files and wanted to stream some audio instead. The new audio engine by Zerosquare offers μLaw samples playback and while it does waste a lot of space, the results are quite impressive! A major component of both sound engines is that they both handle joypad/rotary/etc input. Add all this to the mix - confusion ensues! So let's try to de-confuse things a little. First, a comparison between the two engines. U-235 Can play .mod files: yes (4 channel ones) Can play samples: yes, up to 4 raw PCM samples alongside with the .mod Fine control of samples: yes - pitch and volume Supports μLaw compressed samples: no Supports mouse input: no (it probably can, never tested) Zerosquare Can play .mod files: no Can play samples: yes, up to 4 simultaneously Fine control of samples: no - fire and forget affair Supports μLaw compressed samples: yes Supports mouse input: yes Which one to choose? Well, that's up to you. Main difference is of course .mod files vs μLaw music playing. As for the rest... read on. Selecting a sound engine in your project This is quite easy to do. For legacy reasons the U235 engine is selected by default in every new project. In your project folder open rapapp.s and locate the following line: player equ 1 ;0=Zerosquare's player, 1=U-235 playerAs instructed, replace the 1 with 0 if you want to use Zerosquare's engine. U235 usage In order to play a .mod file you first have to import it as an asset. Same goes for sound samples. Have a look at the assets.txt article for this. Especially for samples, raptor (or U235) (or both IDK, LOL) require the use of sample banks. This is in essence a list where you have to define your samples. It is defined in file rapu235.s of your project. Typically an entry in that table looks like this: sample0: dc.l explode_sam ; start of sample dc.l explode_sam_end ; end of sample dc.l 0 ; repeat offset dc.l 0 ; repeat length dc.w 0 ; <NULL> dc.b 0 ; fine tune s2_vol: dc.b 192 ; volume dc.l 8000 ; default play rate The first two parameters are the sample's start and end addresses. These are automatically available for you from assets.txt. In this example if you import a sample and assign it the name explode_sam, rb+ will give you the addresses explode_sam and explode_sam_end for free. The next two parameters control sample looping: which sample from the start you would like looping to occur and how many samples it should be. NULL - well, just leave it to 0 . volume is a value from 0 to 255 and finally play rate is the replay frequency. This probably goes up to 32000 but I'm not sure really - just try it out if you're curious. To play a module from rb+ it pretty straightforward. Just use MODPLAY(mod_address) and replace mod_address with the label you used to import the module in assets.txt. Stopping a module and restarting is a bit more tricky as the SE doesn't have a clear cut mechanism for it. This snippet of code might work but it's not fully tested on real hardware yet: MODPLAY(0) SNDKILL(0) SNDKILL(1) SNDKILL(2) SNDKILL(3) VSYNC U235SE_modregdump[0]=0 U235SE_modregdump[2]=0 U235SE_modregdump[3]=0 U235SE_modregdump[4]=0 U235SE_modregdump[5]=0 MODPLAY((int)strptr(Module2)) To play a sound sample with the default values just use sndplay(voice,x) where voice is the channel you want to use (4 to 7, 0-3 are used for .mod playing) and x is the sample number from the start of the sample bank. If you want to play the sample at a different frequency you can use SNDPLAYFREQ(voice,x,y) which uses channel voice to play sample x at frequency y (Hz). For more info on sample commands have a look at the list below (which is copied from rb_quickref.txt). To handle the jagpads, first of all you have to set rotary_mode1 and rotary_mode2 to correspond to your controllers (1=rotary, -1=jagpad). For classic d-pad handling you first have to use getpad(x) where x=0,1 the joypad you wish to read (alternatively you can just read the variables U235SE_pad1 and U235SE_pad2 directly). These values contain the states for all buttons, so to detect just one button you need to mask out the rest. So something like if x band (PAD_U) then 'checks for up if x band (PAD_U bor PAD_L) then 'checks for up+left Have a look at the mask values below for the full set. Here's the full command set for U235: MODPLAY(mod_address) plays a .mod file at address "mod_address" MODPLAY(0) stops .mod playing GETPAD(x) returns values from either pad 1 or 2. (Can be replaced by reading U235SE_pad1 and U235SE_pad2 directly.) SNDPLAYFREQ(voice,x,y) triggers sample x from module into channel "voice" at y frequency in Hz MODVOL(x) sets mod music volume (x in range 0 - 63) SNDVOL(x) sets global sfx volume (x in range 0 - 63) SNDKILL(x) stops playing sample at channel number x SNDVOLRESET(x) resets volume of current sample on channel x SNDFREQRESET(x) resets frequency of current sample on channel x SNDDELTA(x,y) set or adjust the volume on channel x to y (0 to 63) SNDFREQ(x,y) sets frequency of channel x to y (0 to 65535) rotary_mode1 +1 = rotary, - 1 = jagpad (Port 1) (.l) rotary_mode2 +1 = rotary, - 1 = jagpad (Port 2) (.l) turn_direction1 rotary value (bigger is faster) - +=left / 0=nothing / - =right (Port 1) (.l) turn_direction2 rotary value (bigger is faster) - +=left / 0=nothing / - =right (Port 2) (.l) rotary_interval1 trim value for rotary (Port 1) (.l) rotary_interval2 trim value for rotary (Port 2) (.l) spin_delta1 value to add to turn_direction per increment (Port 1) (.l) spin_delta2 value to add to turn_direction per increment (Port 2) (.l) Direction pad masks PAD_UP, PAD_U, PAD_DOWN, PAD_D, PAD_LEFT, PAD_L, PAD_RIGHT, PAD_R, PAD_HASH, PAD_9, PAD_6, PAD_3, PAD_PAUSE, PAD_A, PAD_OPTION, PAD_STAR, PAD_7, PAD_4, PAD_1, PAD_0, PAD_8, PAD_5, PAD_2, PAD_B, PAD_C. Zerosquare Zerosquare's engine is much more simple in comparison, but is a bit less flexible. You can play a sample in each of the 4 channels it has available using SNDZEROPLAY chan, start_address, len, frequency, params where chan=1 to 4, start_address and len can be obtained from assets.txt like above, frequency is an integer divisor to the main clock of 46168Hz and params sets the replay mode. The syntax is slightly different if playing a sample from RAM or ROM: Example for playing samples from RAM (ABS): SNDZEROPLAY(1, strptr(sample_start), (strptr(sample_end)-strptr(sample_start)+3) and 0xfffffffc, 46168/9233, Zero_Audio_8bit_muLaw|Zero_Audio_Looping)This will play a sample in channel 1 starting from sample_start, with length sample_end-sample_start rounded up to 4 bytes, with a channel divisor of 5 (i.e. 46168/9233) in order to play 9233Hz. The sample will be μLaw packed, so you need to import it as such. Finally we ask the engine to loop the sound forever.Example for playing samples from ROM: SNDZEROPLAY(1, (void *)sample_start, (sample_end-sample_start+3) and 0xfffffffc, 46168/9233, Zero_Audio_8bit_muLaw|Zero_Audio_Looping)This does exactly the same as above, only the sample is in ROM. To silence a channel just play a zero sized sample (len=0). Finally, to read inputs, first choose what inputs you need (d-pad, rotary, mouse) by calling the routines Input_SetNormalPadMode, Input_SetJoyPort1, Input_SetJoyPort2, Input_SetRotaryMode, Input_SetAtariMouseMode, Input_SetAmigaMouseMode. Then each time you need to read input just call ZEROPAD subroutine. Then variables zero_left_pad, zero_right_pad, zero_mousex_delta, zero_mousey_delta and zero_rotary_delta are filled accordgingly. Again you'll need to mask out the status for the buttons you don't need for pad, so something like if zero_left_pad band Input_Pad_Star thenwill check left pad if the star button is pressed. And here's the list of variables and functions exposed when you use the Zerosquare engine: ZEROPAD() reads both pad ports and sends results back to variables zero_left_pad, zero_right_pad, zero_mousex_delta, zero_mousey_delta and zero_rotary_delta. By default the engine is configured to assume 2 joypads connected. This command needs to be used in capitals. Input_SetNormalPadMode sets up the engine to read two pads (enabled by default). zero_left_pad and zero_right_pad can be read using these constants: Input_Pad_Pause, Input_Pad_A, Input_Pad_Up, Input_Pad_Down, Input_Pad_Left, Input_Pad_Right, Input_Pad_C1, Input_Pad_B, Input_Pad_Star, Input_Pad_7, Input_Pad_4, Input_Pad_1, Input_Pad_C2, Input_Pad_C, Input_Pad_0, Input_Pad_8, Input_Pad_5, Input_Pad_2, Input_Pad_C3, Input_Pad_Option, Input_Pad_Sharp, Input_Pad_9, Input_Pad_6, Input_Pad_3. example: "if zero_left_pad band Input_Pad_Star" will check left pad for star button press. Input_SetJoyPort1 enables joypad port 1 to be used for rotary/mouse input. This doesn't enable rotary/mouse mode. zero_rotary_delta will give the number of rotary ticks since the last read command. Input_SetJoyPort2 enables joypad port 2 to be used for rotary/mouse input. This doesn't enable rotary/mouse mode. zero_rotary_delta will give the number of rotary ticks since the last read command. Input_SetRotaryMode enables rotary mode. Input_SetAtariMouseMode enables Atari mouse mode. Input_Mouse_Left and Input_Mouse_Right are the masks to check for button presses zero_mousex_delta, zero_mousey_delta are the number of mouse ticks in x and y axis since the last read command. Input_SetAmigaMouseMode enables Amiga mouse mode. Input_Mouse_Left and Input_Mouse_Right are the masks to check for button presses zero_mousex_delta, zero_mousey_delta are the number of mouse ticks in x and y axis since the last read command. SNDZEROPLAY chan, start_address, len, frequency, params plays a sample starting from start_address with length len to channel chan with speed frequency and with flags params. *Channel* should be from 1 to 4. start_address should be aligned to 4 bytes. len should be a multiple of 4. frequency should be an integer that divides the base frequency of 46168Hz. So for example if it's set to 1, it'll play a sample at 46168Hz, a 2 will play a sample at 23084Hz etc. Available flags are: Zero_Audio_8bit_Signed (plays an 8-bit signed sample), Zero_Audio_8bit_Unsigned (plays an 8 - bit unsigned sample), Zero_Audio_8bit_muLaw (plays a 8 - bit compressed ?w sample) - Zero_Audio_Looping (enables sample loop). Example for playing samples from RAM (ABS): SNDZEROPLAY(1, strptr(sample_start), (strptr(sample_end)-strptr(sample_start)+3) and 0xfffffffc, 46168/9233, Zero_Audio_8bit_muLaw|Zero_Audio_Looping). Example for playing samples from ROM: SNDZEROPLAY(1, (void *)sample_start, (sample_end-sample_start+3) and 0xfffffffc, 46168/9233, Zero_Audio_8bit_muLaw|Zero_Audio_Looping)
  5. Since the rb+ installation article had some good response (i.e. people even responded to it!) I figure I'll start this one as a WIP post and fill/extend it gradually. If people want to contribute with questions or corrections/suggestions/some paragraphs of text, feel free to! So, without further ado... 0. Introduction The Jaguar's Object Procesor (OP) is a great piece of hardware. It's quite capable and flexible, and the raw power it gives makes one forgive its shortcomings and bugs. Which is of course a shame as most of the surrounding hardware is so bad (well, excluding the 68000, that's great of course ). It's also one of the main things one has to master in order to program the console, independant of the language used or library. As the title says, it's scary. It introduces many concepts that can baffle newcomers and/or inexperienced people in general. So what this post aims to do is demistify and explain the chip as much as possible. 1. The Object Processor 1.a. Why it's called like that anyway? A big part of the OP puzzle is hidden in its name. Object Processor. Something that processes objects. An object is a bit of an abstract term though. What constitutes as an object in the console's terms? Instead of using graphs, bar/pie charts, let's go with an example instead. This is the title screen of Downfall, a game which a few of you might be aware of. Click here to see a video in action if you're not aware of the game and come back to read the rest of this. What you see is the game's logo, some info text and some parallax thing in the background. You see that all those things are overlapping more or less. In order to achieve this in a typical bit-mapped screen of 80s/early 90s computers you'd have to do something like this: a) Clear the screen b) Draw the "farthest" layer of the parallax scroll c) Draw the 2nd "farthest" layer of the parallax scroll, but since there are overlapping pixels with the layer drawn in (b), clear those first d) Repeat © for layers 3-5 e) Draw the score/hiscore texts, again erasing all overlapping pixels first f) Draw the logo (erasing again) g) Draw the text (erasing again) As you probably realise by now the CPU (or specialised hardware like the blitter) will spend a considerable time drawing and clearing pixels (also called masking) every time the screen is refreshed. Also important is the fact that a lot of specialised code has to be written in order to perform all these steps fast enough. Could we do better? Enter the OP. In contrast with bitmapped displays, i.e. you get a block of RAM, you fill it with bits and pixels are rendered on screen, the OP doesn't have a display. Instead you have to instruct it to render rectangles of graphics around the screen. Their widths, heights, colour depths, transparency and many more parameters are highly customisable. To put it in another way: to the OP everything is a sprite - player graphics, bullets, backgrounds, you name it. If it's not an object then it doesn't appear on screen. So, to get back to the above example, we could define an object as big as the screen we're rendering and do all that busy work there. But (and this is what makes more sense) we can instuct the OP to render each different layer as a separate object and then combine them by itself. It will the be the OP's responsibility to compose the screen out of the parts we tell it to use. 1.b. Object lists What we learned so far is that the Jaguar can render various boxes of data onto the screen. This of course raises a few questions like "how do we tell it how many boxes to render?" and "how do we describe these boxes so the hardware can understand them?". The answer to both these is Object Lists (OL). An OL is really a forward linked list (If you already know what a linked list is then skip this paragraph). Very briefly put, imagine having an array where we want to store a few parameters per object - say we want to store 10 sprite's x, y positions, width and height. We could go right ahead and dimension an array in BASIC like DIM stuff[4][10]and then use that array to store everything. But what happens if we don't know beforehand how many sprites we'll use? One solution is to just overdimension and hope that limit never gets crossed, but that's wasting RAM and we don't want to. A more RAM optimal way is to dimension a 5th field that will tell us where in ram the next index actually is. Our modified array will store x,y,w,h,adr_of_next_index. So in order to traverse the array we have to know the address of the first index, go there, do what we need to do, then fetch adr_of_next_index, jump to that address, and so on until we reach the list's end (let's say that the list ends when we read a 0 in adr_of_next_index). (No shame if you didn't digest all that info in the first read - go back and read it again if you're not sure how it works. Or reply to this post with a question. Ultimately it doesn't matter but it helps understanding some of the stuff you'll see later). So by using this arrangement we can store as many object as we require with the minumum RAM waste. 1.c. Anatomy of an object Let's see exactly how flexible the OP is by looking at how an object is defined. There are five kinds of objects: Bitmap object Scaled bitmap object Graphics Processor object Branch object Stop object The first two are the most used objects. The fourth is just a terminator for the list and is placed at the end of the OL so it's only used once per list. Branch objects are also specialised and allow for skipping parts of the list - this doesn't sound very practical at first, but since there are a few conditionals that can be executed it becomes quite useful. GPU objects... we'll get to that eventually. Each object is at least one phrase long (one phrase is 64 bits, or 8 bytes). So what probably happens is that the OP reads a phrase off the OL for the current object, and if the type demands it then read extra phrase(s). The first 3 bits of each object's first phrase contain the TYPE field. That's 0 for bitmapped object, 1 for scaled, 2 for GPU object, 3 for branch object and 4 for stop object. 1.c.1. Bitmapped object Let's have a quick look at the fields for a normal bitmapped object. Don't pay too much attention at the descriptionm especially if you don't understand something - we'll explain everything eventually. TYPE - object type (hardcoded to 0) YPOS - object y position in screen HEIGHT - object height LINK - address of next object in OL DATA - address of graphics data for the object XPOS - object x position in screen DEPTH - object's bit depth PITCH - how many bytes is a single scanline of the object INDEX - For bitmapped objects, choose which palette to use DWIDTH - how many phrases wide the object is in memory (can be different than PITCH!) IWIDTH - how many phrases wide the object is on screen (can be different than PITCH and DWIDTH!) REFLECT - flag that controls if the object will be drawn mirrored horizontally RMW - Adds the current pixel value to what's already on the line buffer (advanced topic for now!) TRANS - flag that enables transparency. Colour 0 will not be drawn on screen RELEASE - flag that tells the OP to yield the bus to other chips FIRSTPIX - tells the OP which is to be considered the first pixel to be drawn per scanline Now that's a lot of things, right? If you were to implement those in software you'd consume a lot of bytes. And since CPUs are usually comfortable with 8, 16 or 32 bit values (byte/word/longword) you'd need a word per parameter (even for a flag) and a longword per address to be safe. There are 16 parameters in that list, two of them are addresses. So we're looking at 14*2+2*4=36 bytes=288 bits! How did they squeeze it down to 128? (if you wonder "why 128?" then remember: a bitmapped object is 2 phrases!) Well, for starters some fields are on/off (0/1) so they just need a single bit to represent them. Similarly other fields like YPOS/XPOS don't need the full 16 bits that we allocated because of physical constraints - no reason to have a X value of 65535 for example! So most of the fields are chopped down in a similar fashion. Finally, the address fields are always aligned to 8 bytes due to hardware constraints (burst read access if I'm not mistaken). This means that the address values will be a multiple of 8 - so 8,16,32,48 etc. If you look these numbers in binary or hex notation you'll notice that the last 3 bits are zero. So no need to store them at all! All the above tricks do save a lot of RAM and bandwidth for the OP but it comes as a cost. Namely, it's not very easy to construct and update an OL from a CPU standpoint. Inserting and updating bitfields require a few instuctions per operation and since the 68000 is a bit slow when it tries to shift values (an essential part unless you can avoid it), it can become quite demanding to update even basic values like screen x,y coordinates. So when a lot of objects are used it is recommended to use the GPU (Tom) to update the lists. One final thing worth mentioning here is that during screen composing the OP will trash the OL partially (I am not sure which fields are modified at the moment, perhaps someone can help?). If the OP is allowed to run on the next frame with the list trashed you will definitely see the screen do weird things, from blanking out completely to displaying garbage and then crashing the machine! So it is necessary to update the OL every vertical blank (VBL). I know of two methods here: one is to update the actual list and the other is to keep a second copy of the OL and copy it to the live list during the VBL. The later is what raptor uses and it is much less complicated: you can do the processing during the frame is displayed and not have to struggle modifying things at the last moment. 1.c.2. Scaled bitmapped object The scaled object is exactly the same as the bitmapped, except that the type is of value 1 here, with the addition of third phrase that contains some extra fields. HSCALE - Horizontal scale factor in 3.5 unsigned fixed point format. VSCALE - Vertical scale factor in 3.5 unsigned fixed point format. REMAINDER - This seems to be internal state kept by the OP (anyone can help here?) So what's all this 3.5 fixed point format thing then? First of all, fixed point is a way for a CPU that does not support numbers with fractions to support them. If you consider numbers in the decimal system you have the integer part followed by the fractional. They are separated using a deliminator (comma or dot, depending on your region). If we remove the deliminator from the number then a number like "123.456" becomes "123456". There is no way we can figure out where the fractional part begins unless someone tells us that it's after the 3rd digit. So if we all agree that the integer part is the first 3 digits and the fractional part the last 3 then we've made a fixed point system. We can now encode all numbers from 000.000 to 999.999 using 6 digits. (if you wonder why 000.000 and not -999.999, then notice that the sign is also a digit and we'll have to spend an extra digit to encode it). We call that "3.3 unsigned fixed point format". If we require negative numbers too we have to allocate an extra digit so our numbers can be "-999.999" to "+999.999" - that's a 4.3 signed format. Now, let's switch to binary representation. It's pretty much the same thing as the above only with two available digits (0 and 1). So to our example above a 3.3 fixed point binary format would be able to store numbers from 000.000 to 111.111. So, our 3.5 unsigned format would of course hold numbers from 000.00000 to 111.11111. So much for the definitions. But that doesn't help us much in the way of knowing how much we zoom our object, right? After all, what's 0.11111 binary when converted to decimal system where we're more comfortable? Let's begin with the easy stuff - integers. Since we use 3 bits for integer part we can store decimal numbers 0 to 2^3-1=7. Moving on to the scary part: fractions! So ask yourself: what is 0.1 in the decimal system? It's 1/10th, right? And 0.001? That's 1/100th. 0.0001 is 1/1000th an so on. So what happens is that we divide 1 by the base as many times as we have decimal places. If we formulate this, it's something like "1/(10^fraction_digit)" to represent a fraction digit (10 raised to the power of the number of fraction digits is the same as dividing with 10 as many times as the number of fraction digits). It just so happens though that 10 is the decimal system's base. So we can change the formula to "1/(base^fraction_digit)". Finally, "1" is used because our examples had 1 in them. So the final transformation we do to the formula is "number/(base^fraction_digit". This lets us represent any digit in the fractional part. I hope you've got it by now, but if not... Let's switch to binary number system. Our base is 2 here and the range for number here is 0 to 1 so we can write our generic formula as "0 (or 1)/(2^fraction_digit)". Let's write some examples then: %0.00001 (notice that numbers prefixed with % are considered binary by assemblers like rmac) is actually 1/2^5 in decimal, so 1/32 or 0.03125. %0.01 is 1/2^2=1/4=0.25. %0.1 is 1/2=0.5. So what numbers can we represent on a 3.5 unsigned binary format? I would expect %000.00000 to produce nothing as it's a scale factor of exactly 0 so let's leave that out for now. %000.00001 would be the smallest number and %111.11111 the largest. %000.00001 as we wrote above is 0.03125. So that's actually our increment - all scale factors will be an integer multiple of this. For example, the next number in sequence, %000.00010, is 0.0625 which is, true enough, double of 0.03125. %001.00000 is obviously 1, so that's the number we need to put in order to have no scaling at all. And so on and so forth until we reach %111.11111, which is 7+1/2+1/4+1/8+1/16+1/32=7.96875 - that's the largest scale we can have from the OP. 1.c.3. Graphics Processor object Scary stuff - let's leave that out for now! 1.c.4. Branch object This object enables the OP to skip parts of the OL or even create loops if used carefully. TYPE - object type (hardcoded to 3) YPOS - if a comparison is performed, this is the value to compare against. CC - Condition Code LINK - If a branch is taken, this is the address to branch to. First of all, if we simply want to branch to a different point in the OL we can simply set CC to 0, YPOS to $7fff and fill the LINK field with the address to branch to. This can be used to remove objects that are unused at the time of display (for example, say you have 30 objects that display bullets and you only have 10 active. You could add a branch object before the bullet objects and branch so the OP will skip 20 objects and display the last 10). The other three cases can branch if the Video Counter (VC) is equal (CC=0), smaller (CC=2) or larger (CC=1) compared to the value YPOS contains. This can lighten the OP's load greatly. For example, consider the following playfield: There's no reason for the OP to render the lower parts of the screen while it's rendering the upper half. So we can set YPOS to half the screen height and save tons of bandwidth. Also, using comparison branches you can effectively create loops (i.e. render the same object for the first 50 scanlines) but I'm not sure if there's any vaule to doing this - most likely the objects will become trashed! There are also two other branch types but I'll leave them alone for now as they're more specialised. One final note (careful readers will probably wonder about this): If the branch is not taken, then the OP expects the next object to be on the next phrase from the branch object! If you violate this, then funky things will happen ! 1.c.5. Stop object Pretty straightforward stuff, just stick a 4 in the TYPE field and fill the rest of the phrase with zeros. The OP will stop processing more obejcts after this. You're done! 1.c.6. Reference: the reference manual on Objects. Here's a direct quote from the jaguar reference manual. Bit Mapped Object This object displays an unscaled bit mapped object. The object must be on a 16 byte boundary in 64 bit RAM. First Phrase Bits Field Description 0-2 TYPE Bit mapped object is type zero 3-13 YPOS This field gives the value in the vertical counter (in half lines) for the first (top) line of the object. The vertical counter is latched when the Object Processor starts so it has the same value across the whole line. If the display is interlaced the number is even for even lines and odd for odd lines. If the display is non-interlaced the number is always even. The object will be active while the vertical counter >= YPOS and HEIGHT > 0. 14-23 HEIGHT This field gives the number of data lines in the object. As each line is displayed the height is reduced by one for non-interlaced displays or by two for interlaced displays. (The height becomes zero if this would result in a negative value.) The new value is written back to the object. 24-42 LINK This defines the address of the next object. These nineteen bits replace bits 3 to 21 in the register OLP. This allows an object to link to another object within the same 4 Mbytes. 43-63 DATA This defines where the pixel data can be found. Like LINK this is a phrase address. These twenty-one bits define bits 3 to 23 of the data address. This allows object data to be positioned anywhere in memory. After a line is displayed the new data address is written back to the object. Second Phrase Bits Field Description 0-11 XPOS This defines the X position of the first pixel to be plotted. This 12 bit field defines start positions in the range -2048 to +2047. Address 0 refers to the left-most pixel in the line buffer. 12-14 DEPTH This defines the number of bits per pixel as follows: 0 1 bit/pixel 1 2 bits/pixel 2 4 bits/pixel 3 8 bits/pixel 4 16 bits/pixel 5 24 bits/pixel 15-17 PITCH This value defines how much data, embedded in the image data, must be skipped. For instance two screens and their common Z buffer could be arranged in memory in successive phrases (in order that access to the Z buffer does not cause a page fault). The value 8 * PITCH is added to the data address when a new phrase must be fetched. A pitch value of one is used when the pixel data is contiguous - a value of zero will cause the same phrase to be repeated. 18-27 DWIDTH This is the data width in phrases. i.e. Data for the next line of pixels can be found at 8 * (DATA + DWIDTH) 28-37 IWIDTH This is the image width in phrases (must be non zero), and may be used for clipping. 38-44 INDEX For images with 1 to 4 bits/pixel the top 7 to 4 bits of the index provide the most significant bits of the palette address. 45 REFLECT Flag to draw object from right to left. 46 RMW Flag to add object to data in line buffer. The values are then signed offsets for intensity and the two colour vectors. 47 TRANS Flag to make logical colour zero and reserved physical colours transparent. 48 RELEASE This bit forces the Object Processor to release the bus between data fetches. This should typically be set for low colour resolution objects because there is time for another bus master to use the bus between data fetches. For high colour resolution objects the bus should be held by the Object Processor because there is very little time between data fetches and other bus masters would probably cause DRAM page faults thereby slowing the system. External bus masters, the refresh mechanism and graphics processor DMA mechanism all have higher bus priorities and are unaffected by this bit. 49-54 FIRSTPIX This field identifies the first pixel to be displayed. This can be used to clip an image. The significance of the bits depends on the colour resolution of the object and whether the object is scaled. The least significant bit is only significant for scaled objects where the pixels are written into the line buffer one at a time. The remaining bits define the first pair of pixels to be displayed. In 1 bit per pixel mode all five bits are significant, In 2 bits per pixel mode only the top four bits are significant. Writing zeroes to this field displays the whole phrase. 55-63 Unused write zeroes. Scaled Bit Mapped Object This object displays a scaled bit mapped object. The object must be on a 32 byte boundary in 64 bit RAM. The first 128 bits are identical to the bit mapped object except that TYPE is one. An extra phrase is appended to the object. Bits Field Description 0-7 HSCALE This eight bit field contains a three bit integer part and a five bit fractional part. The number determines how many pixels are written into the line buffer for each source pixel. 8-15 VSCALE This eight bit field contains a three bit integer part and a five bit fractional part. The number determines how many display lines are drawn for each source line. This value equals HSCALE for an object to maintain its aspect ratio. 16-23 REMAINDER This eight bit field contains a three bit integer part and a five bit fractional part. The number determines how many display lines are left to be drawn from the current source line. After each display line is drawn this value is decremented by one. If it becomes negative then VSCALE is added to the remainder until it becomes positive. HEIGHT is decremented every time VSCALE is added to the remainder. The new REMAINDER is written back to the object. 24-63 Unused write zeroes. Graphics Processor Object This object interrupts the graphics processor, which may act on behalf of the Object Processor. The Object Processor resumes when the graphics processor writes to the object flag register. Bits Field Description 0-2 TYPE GPU object is type two 3-13 YPOS This object is active when the vertical count matches YPOS unless YPOS = 07FF in which case it is active for all values of vertical count. 14-63 DATA These bits may be used by the GPU interrupt service routine. They are memory mapped as the object code registers OB0-3, so the GPU can use them as data or as a pointer to additional parameters. Execution continues with the object in the next phrase. The GPU may set or clear the (memory mapped) Object Processor flag and this can be used to redirect the Object Processor using the following object. Branch Object This object directs object processing either to the LINK address or to the object in the following phrase. Bits Field Description 0-2 TYPE Branch object is type three 3-13 YPOS This value may be used to determine whether the LINK address is used. 14-15 CC These bits specify what condition is used to determine whether to branch as follows: 0 Branch if YPOS == VC or YPOS == 7FF 1 Branch if YPOS > VC 2 Branch if YPOS < VC 3 Branch if Object Processor flag is set 4 Branch if on second half of display line (HC10 = 1) 16-23 unused 24-42 LINK This defines the address of the next object if the branch is taken. The address is defined as described for the bit mapped object. 43-63 unused Stop Object This object stops object processing and interrupts the host. Bits Field Description 0-2 TYPE Stop object is type four 3-63 DATA These bits may be used by the CPU interrupt service routine. They are memory mapped so the CPU can use them as data or as a pointer to additional parameters. 1.d. Bit depths - bandwidth After digesting the basics objects one of the most confusing aspects for newcomers (especially rb+) is bit depth. "Since I draw some graphic on my desktop/laptop computer, it should just appear on the screen, right?". Well, yes and no. In an ideal world we'd draw everything in as many colours we like and give it to the hardware to cope. Unfortunately the OP is quite fast but it cannot cope with this idea. It might appear so at first but as you start piling up objects one on top of the other it simply runs out of juice. When the OP is composing the screen, it more or less does the following for each line: Goes through the whole OL until it reaches a stop object (branch objects are of course evaluated) For each object the OP has to parse object coordinates parse screen coordinates translate screen coordinates to object coordinates figure out where it should read from the object's graphics address fetch pixels combine pixels with the pixels of the previous objects (transparency, ordering etc) if any A big bottleneck in the above list is fetch pixels. If we draw uncontrollably and export everything to 16 or 24 bits per pixel (bpp), the OP has to read 2 or 3 bytes. Like said above, this eats up the chip's read bandwidth pretty fast. So if you keep piling up objects like these you're going to end up with garbage on screen - it's simply not possible to read all that data in the time frame allocated.That's why the OP's designers added bit depths in the chip. If you know that your character sprite won't use more than 16 colours on screen (which translates to 4 bits, 2^4=16) then why waste 12 (or 20 in 24bpp mode) more? Multiply that with the number of objects you would like to have on screen (say 50?) and you end up with a lot of saved bandwidth. And that's for 16 colours, if you want to use even less then you can save much more. Combining is also costly, especially when transparency comes into place. There's potentially a lot of read data thrown away just because a lot of objects are piling up on top of the others. Also, because the whole list is parsed per line, the OP also has to parse objects that might not apply in all scanlines, thus waste even more bandwidth. The use of branch objects can help massively here. In conclusion, it makes good sense to plan ahead what you want to do and be bandwidth considerate. (After all, drawing the screen is only part of the problem - you also need logic, audio, inputs, and many more things) 1.e. Bitmapped objects, palettes and pixel formats For 16, 24 and CRY modes it is easy to store colour information. Since each pixel is so many bits, we can encode the intensities for Red, Green and Blue directly on the pixel data. Very briefly, for 16bpp the format is: Bit 0123456789abcdef RRRRRBBBBBGGGGGG so, 5 bits for Red and Blue (0-31), and 6 for Green (0-63). For 24bpp objects we have 8 bits (0-255) for Red, Blue and Green and 8 unused. Bit 0123456789abcdef0123456789abcdef RRRRRRRRBBBBBBBBGGGGGGGG00000000 (note that the RBG order is intentional, that's how the OP expects values to be written) CRY mode - one byte for RGB and one for intensity - let's leave that for later. Let's go back to <16 bpp modes. Since in these modes we don't have enough bits to store component intensities, the solution is to store the intensities in a designated memory area separately and for the object itself just mark down the index to the intensities table. That memory area is called a Palette or CLUT (Colour Look-Up Table). The OP's CLUT table holds 256 entries and it uses the 16bpp format described above. So for example if we use 4bpp mode and our first 4 pixels are 9,2,5,8, the object's first two bytes will look like this: Pixel 0 1 2 3 Values 9 2 5 8 Values 1001 0010 0101 1000 Notice that each pixel has all its bits packed one after the other. This is true for all bit depths and is called chunky format. 1.f. Let's display an object on screen 1.g. Advanced topics (CRY, RMW etc) 2. Raptor lists Here's an object as defined in a raptor list. Values in red are identical (or almost identical) to OL fields. (well, code snippets can't be coloured it seems, so I'll get back to this) (REPEAT COUNTER) - Create this many objects of this type (or 1 for a single object) sprite_active - sprite active flag sprite_x - 16.16 x value to position at sprite_y - 16.16 y value to position at sprite_xadd - 16.16 x addition for sprite movement sprite_yadd - 16.16 y addition for sprite movement sprite_width - width of sprite (in pixels) sprite_height - height of sprite (in pixels) sprite_flip - flag for mirroring data left<>right sprite_coffx - x offset from center for collision box center sprite_coffy - y offset from center for collision box center sprite_hbox - width of collision box sprite_vbox - height of collision box sprite_gfxbase - start of bitmap data (BIT DEPTH) - bitmap depth (1/2/4/8/16/24) (CRY/RGB) - bitmap GFX type (TRANSPARENCY) - bitmap TRANS flag sprite_framesz - size per frame in bytes of sprite data sprite_bytewid - width in bytes of one line of sprite data sprite_animspd - frame delay between animation changes sprite_maxframe - number of frames in animation chain sprite_animloop - repeat or play once sprite_wrap - wrap on screen exit, or remove sprite_timer - frames sprite is active for (or spr_inf) sprite_track - use 16.16 xadd/yadd or point to 16.16 x/y table sprite_tracktop - pointer to loop point in track table (if used) sprite_scaled - flag for scaleable object sprite_scale_x - x scale factor (if scaled) sprite_scale_y - y scale factor (if scaled) sprite_was_hit - initially flagged as not hit sprite_CLUT - no_CLUT (8/16/24 bit) or CLUT (1/2/4 bit) sprite_colchk - if sprite can collide with another sprite_remhit - flag to remove (or keep) on collision sprite_bboxlink - single for normal bounding box, else pointer to table sprite_hitpoint - Hitpoints before death sprite_damage - Hitpoints deducted from target sprite_gwidth - GFX width (of data) So it's evident that raptor lists try to be close to the OP's object definitions while adding extra fields to help the processing of sprites (animation, hitpoints, collision etc.). 3. Wrapping up Hopefully everyone reading this post got something out of it. It's nothing more than re-stating what the hardware manual says with as many explanations to the newcomer as possible. Also it shows how much stuff rb+ and raptor do behind your back (constructing OLs, calculating object parameters, aligning graphics data so it will be processed ok and so much more). Thanks for your patience while reading this! Let me know if there's something not clear, if I omitted something or if there's an error somewhere.
  6. Since this question pops up frequently, and there isn't any proper documentation for it, I decided to write a small post to explain things a bit. Graphics I do plan to cover pixel formats in more detail in the OP thread so I'll make a very brief notice here. The Object Processor supports graphics of various colour depths: 1,2,4,8,16 and 24 bit, as well as the infamous CRY mode. Regardless the mode, the OP expects graphics to be in chunky format. This means that each pixel's data must be consecutive and packed one after the other. So, if we have a 4bpp object and we want its first 4 colours to be palette indices 1,8,10,3 the OP will expect to see this in RAM: Pixel 1 2 3 4 Index 1 8 10 3 0001 1000 1010 0011For 8 bits/pixel it'll read 8 consecutive bits for the pixel etc. etc. Audio For audio the hardware supports 2 16-bit channels but in order to ease mixing the usual format for sound effects is 8bit signed or unsigned. How to do it So, to import assets in code, people more or less were required to roll their own system with its own class of features, bugs and limitations. Raptor also has an internal graphics conversion system, which is what rb+ used initially. However this required modifying the main assembly source and recompiling, so this was less than ideal for people that find assembly scary. Thus a build step in rb+ was added which tries to do all the above (and more) automatically for the user. So in each project's folder there's a file called assets.txt that instructs the build system to import files ready for the rb+ program and convert them if required. Each time you create a new rb+ project from the command line (build.bat <projectname> new"), a sample assets.txt is copied to the project directory. Its contents are more or less the following: ' rB+ assets file. ' ' This is where we tell rB+ to load in our graphics and sounds etc. ' ' Firstly, all these lines are comments and are not useful to the build process. ' To be exact, all lines starting with the characters ";", "#", "'", "*" are considered to be comments. ' ' Each non-comment assets.txt line is considered to have 4 comma separated strings of text like the following: ' A,B,C,D ' | | | +-- Filename of the asset, relative to the project folder (i.e. assets\gfx\background.bmp) ' | | +---- Tells the build process if the file should just be imported as is or some conversion is needed beforehand. See below for details. ' | +------ Name asset which is exposed to rb+. Actually two variables are exposed to rb+: name and name_end which holds the start and end addresses of the asset. ' | Names may start with an uppercase or lowercase letter (A-Z a-z), an underscore (_), a question mark (?) or a period (.). ' +-------- This should either be ABS or ROM. This tells the build process if you want the asset to be included in RAM or ROM. ' ' Parameter C explained further: ' - This can be any text if you have some asset that you want imported in rb+ as is (for example a converted raw sample or a backdrop). For this you can put any text in C. ' - For graphics conversion you can use "gfx_clut" or "gfx_noclut" to convert a .BMP file into jaguar raw format and optionally export the paletter or not (clut=yes, noclut=no). ' This applies to 1-, 2-, 4- and 8-bit graphics only. ' For 16-bit and 24-bit graphics no clut is created (because there isn't a need for one). ' Finally for 16-bit images you have to use gfx_clut16 or gfx_noclut16. ' - For conversion to CRY mode use "gfx_cry". ' - For audio files you can use "sfx_rawXXXXX" to convert any audio file (for example .wav, .mp3, .ogg, etc) into raw format. ' You can optionally set XXXXX to be the desired sample rate, otherwise 8000Hz is used by default. ' - If you want the audio files to be compressed using u-law then use "sfx_mlawXXXX" instead (note - this requires Zerosquare's player!) ' - Rmotion scripts should be placed here, use "rmotion". ' - Finally, if you want an asset packed, append a "_pack" suffix. For example "gfx_noclut_pack". ' Note that rmotion scripts and CLUTs aren't packed for now. ' Also note that it's your responsibility to reserve RAM for unpacking as well as running powaunpack to unpack the asset. ' ' That's all, have fun! ' ' End of file. Scary? Let's break it down a bit. First of all, all lines starting with ";", "#", "'", "*" are not taken into account. So effectively assets.txt is a blank file. The non-comment lines should have 4 parameters, separated by commas. So, something like: ABS,main_sprite,gfx_clut,assets\gfx\main_sprite.bmpThe first parameter tells the tool where to place the imported file. ABS means place in RAM and ROM place in ROM. Usually ABS is prefered for development work, especially if you send the resulting binary to the real machine. You don't have to burn a ROM, just upload the binary to RAM and run. The second parameter controls the name of the asset which is exposed to rb+. For example, if your asset is a graphic, then to define it as a raptor object you will need to know the graphic's start address. Since this is a number not known at build time, traditionally we will pass a symbol name and let the build process sort it out. So this name has to be unique. The fourth parameter is simply the filename of the asset. The third parameter is more tricky, so it's covered last. It's the magic glue that happens in order to convert the file to a format digestable by the jaguar hardware. So let's go through the options one by one. Graphics: - For 1,2,4 and 8 bpp formats it's not enough to simply convert the image to chunky format. We also need the RGB values of the pixel indices we use, also called a "palette" or a "clut" (colour look-up table). So we enter gfx_clut as the third parameter. If for some reason we don't require the palette to be generated we can supply gfx_noclut. Note that the source image has to be in .bmp format and that the file has to be in the number of bpp that you want your object to be. - For 16/24bpp formats there is no need for palette, there are enough bits to store RGB values directly. In this case the third parameter is gfx_clut16 or gfx_noclut16 - the result will be the same. - If your object is in CRY mode then you need to enter gfx_cry as the third parameter. Note that the source image has to be in .bmp format. Audio: The import program uses a program called sOx to convert the sounds to jaguar formats, so it can accept pretty much all input formats you feed it (mp3, ogg, wav etc should be fine). - For a plain uncompressed raw file you can use sfx_rawXXXXX or sfx_raw to convert it. The short format will default to converting the sample to 8000Hz. Otherwise you can specify your own replay rate if you pass a number instead of XXXXX. - Zerosquare's audio code also supports an additional mode, which provides some compression. You need to use sfx_mlawXXXXX to convert to this format. Packing: This can be very handy for graphics, especially 16bit images which can take up quite some space. if you postfix your commands with pack then the asset will be packed after conversion, for example gfx_clut16_pack. There is a sample project caled "pack" that demonstrates packing and unpacking, so be sure to check it out. Rmotion: Finally enter "Rmotion" to import a Rmotion script. This is something of an advanced topic so we'll leave that out for now. General comments As you notice, rb+ tries to do a lot of things behind your back and lower the entry level for doing things. It has its faults of course - .bmp format for input is not ideal and at some point this might be addressed by using third party libraries. One of the most popular questions/bug reports that surface is graphic import conversion errors. It's natural because it is a confusing topic, especially since the import tool expects the bmp files to be the same amount of colours as the object you want to define. Believe it or not this is a design choice: Nobody knows how you want your graphics to look like better than the user. For example, in bitmapped objects colour 0 has to be the transparency colour. If the import tool were to do this automatically then how would it be able to pick colour 0? There's no easy way. So the import tool leaves that choice to the user. We want you to have control of this. We want you to learn to use a pixel editor that supports low bpp modes. Otherwise your imports will work out of luck. And when they don't.... "rb+ is broken!".
  7. build.bat is unfortunately rb+'s front-end. It's not very fancy or cool like an IDE, it's just something that gets the job done. It's responsible for compiling all sources and binaries into a single file, ready to be executed in emulators or real hardware. However it has slowly evolved into a much more sophisticated (read: complex) system. And here's why. Invoking build.bat without any parameters will print something like this Usage: build.bat projectname build.bat projectname sendy build.bat projectname ROM build.bat projectname ROM sendy build.bat projectname ROM UNPACKED build.bat projectname ROM UNPACKED sendy build.bat projectname sendy bjl build.bat projectname new Folder "projectname" must exist inside folder "projects" and have a file called "projectname.bas" inside. When building a ROM it is assumed that there will exist a file called assets.txt inside the project folder and will contain all assets to be included in ROM (if any). By default the rb+ main program is packed into the ROM and depacked during boot. This can be avoided by passing the UNPACKED switch. if you specify "new" then a new project will be created from the "include\template" folder. No project will be created if folder exists. If one of the parameters is BOSSMODE then virtualjaguar is not run. If one of the parameters is CLEAN, the build folder will be wiped. Otherwise, an incremental build is preformed if you specify "sendy" the project, upon succesful compilation will be sent to Skunkboard (caution - when using ROM, bank #1 will be erased without any warning!) "bjl" will upload the binary to Jaguar via BJL. Note that you might need to edit the command line parameters to the uploader in this file (just search for "lo_inp.exe") Current projects: (list of project directories) Let's go through all the options. - To build a new project just type build.bat myproject new. This will create a folder inside your projects directory and copy a few files that are absolutely needed for a project to build. These are: rapapp.s - raptor skeleton assembly file. You generally don't need to mess around with this file, with a single exception when you choose your audio engine code. rapinit.s - raptor init file. You definitely need to mess around with this file as it contains the definitions for all raptor objects and lists. rapu235.s - contains the audio sample definitions when using the U-235 sound engine. Not discussed in this post. <projectname>.bas - Very bare source file that contians some needed initialisations. Generally keep all lines before the first RLOCATE and delete the rest. assets.txt - imported assets (graphics/sound/etc). Covered in another post. assets\partipal.bmp - this bmp file essentially contains the palette values used for the particle/print layer. Edit this for an easy way to change the colours. assets\fonts\f_16x16.bmp / f_8x16.bmp / f_8x8.bmp - these files contain 3 different sized fonts in 4bpp format. Edit these to change the default fonts. - To build a project as an .abs file (i.e. runs from RAM) and send it to virtualjaguar type build.bat myproject. If all goes well, virtualjaguar will start up with your compiled binary. Otherwise the build log will be printed to the console so you can see what went wrong. - To build a project as an .abs file (i.e. runs from RAM) and send it to the jaguar via skunkboard type build.bat myproject sendy. This assumes that a skunkboard is actually connected to your dev machine, working and ready to upload a binary. No checks are performed for this - it's your responsibility! - To build a project as an .abs file (i.e. runs from RAM) and send it to the jaguar via BJL type build.bat myproject sendy bjl. This assumes that a BJL cable is actually connected to your dev machine, working and ready to upload a binary in 8bit mode. No checks are performed for this - it's your responsibility! - To build a project as a .rom file (i.e. runs from ROM) and send it to virtualjaguar type build.bat myproject rom. If all goes well, virtualjaguar will start up with your compiled binary. Otherwise the build log will be printed to the console so you can see what went wrong. - To build a project as an unpacked .rom file (i.e. runs from ROM) and send it to virtualjaguar type build.bat myproject rom unpacked. If all goes well, virtualjaguar will start up with your compiled binary. Otherwise the build log will be printed to the console so you can see what went wrong. - To build a project as a .rom file (i.e. runs from ROM) and send it to the jaguar via skunkboard type build.bat myproject rom sendy. This assumes that a skunkboard is actually connected to your dev machine, working and ready to upload a binary. No checks are performed for this - it's your responsibility! - To build a project as an unpacked .rom file (i.e. runs from ROM) and send it to the jaguar via skunkboard type build.bat myproject rom sendy unpacked. This assumes that a skunkboard is actually connected to your dev machine, working and ready to upload a binary. No checks are performed for this - it's your responsibility! - In order to speed build times up the build system caches imported assets. The cache folder is build and resides in your project folder. The rule to re-convert an asset is simple, if the converted file inside the build folder is newer than the source file, then the conversion is skipped. However this might go wrong for whatever reasons (clock skew, changing machines, timezones, maybe you download a file with a date set into the future etc etc) so this mechanism can screw up. There's not much to do in this case except clean the cache folder. You can do this either manually or by adding clean to all the above command lines, for example build.bat myproject clean, build.bat myproject sendy clean etc. If you don't want to ever want to worry about this issue at the expense of slower build times (this can vary depending on the files to be converted) then you can always build cleanly. - Finally if you want to simply build the project and not run it, prepend your command line with bossmode. For example build.bat myproject bossmode, build.bat myproject clean bossmode etc. Build process explained At first building rb+ binaries sounds pretty straightforward - i.e. just compile and link the damn code! However, as we realised it is a bit more complex than this. But let's cover the bare essentials first. rb+ consists of a few building blocks: bcx, a basic to C translator. This enables you to type code in Basic without having to code in C which is scary for most beginners. gcc 4.6.4 which translates C code to 68000 machine language. Raptor engine, which covers the graphics side of things (also audio and input but we'll get to that). U-235 Sound Engine and Zerosquare Audio Engine, which handle audio and input. rmac which assembles 68000 and Tom/Jerry code into bytecode. rln which links binaries produced by rmac and gcc together into a final binary. A simple build system would do the following in order: Assembles raptor skeleton code (rapapp.s) Translates the .bas file into .c Compiles the resulting .c file Link the above produced files together with raptor and u-235 (which are provided as libraries, as they're close sourced) That's all fine if we want to compile the .bas file provided by the template when we create a new project. But what happens when we want to import graphics and audio? In this case first of all the assets have to be converted into native format data. Then they have to be exposed to the code somehow. Both issues are solved by inserting an extra step to run a specialised program to read assets.txt and produce the converted files, as well as a file called ramassets.inc which is then read by rapapp.s and a file called romassets.h (a bit wrongly named, but bear with me) to be read by gcc when compiling the code. So our build process now becomes: Runs buildlink to do the asset conversion (if any) and produce include source files Assembles raptor skeleton code (rapapp.s) Translates the .bas file into .c Compiles the resulting .c file Link the above produced files together with raptor and u-235 (which are provided as libraries, as they're close sourced) And that would be all that would be required... if we weren't building ROM files... Up till now we didn't have any problem on where assets are placed - we just let rmac and rln deal with it. rmac reads all assets so it knows where inside the binary they will reside, so it marks down their addresses from the start of the binary. Then rln reads all those marked addresses, and since it knows where in RAM the binary will run (we use $4000 always) it can calculate all addresses and update all marks. This is great since we can change the order of the assets or place them wherever we want inside the .s file and together with rln they will find a solution. There are two ways to produce a ROM. One is to place all assets in RAM and just create a ROM that copies its contents to RAM and runs them. In this case nothing is changed: you mark all assets as "abs" in assets.txt, the same prodedure as above happens with the extra step of making a ROM file, which includes writing a valid ROM header, then a small piece of code that copies ROM to $4000 and then moves the execution pointer (or Program Counter) there. In the other mode it's probably not feasible to have all assets in RAM so you also have to use some space in ROM. After all a ROM can be up to 6MB (although 4 is the usual maximum) which is bigger than the 2MB of RAM. But now a problem arises: where do we put the ROM assets? At this stage I could have just shrugged and said: "it's yours, folks!", leaving you to sort out the mess and hardcode addresses. For people that ask "why not just place them after the rb+ binary?" consider that we don't know how much the binary will be before compiling and linking. So with this idea there is absolutely no way to do a 1 pass build and have this info known during building and not after. The solution was very simple: just place assets at the start of the ROM, then the rb+ binary - after all this does get copied to RAM so we don't care much where it will end up. Then their addresses will be fixed and we can expose them to rb+ and raptor. Then we can create a new file called romassets.inc which will contain all these new addresses. romassets.h will now double up to contain all these addresses for gcc too. One extra thing that requires attention here is that all assets are placed aligned to 16 byte boundary (i.e. two phrases), just to be on the safe side (we don't know which of the assets will be used by the OP or the Sound Engine). Finally, all the assets are placed into one large file so we don't have to deal with alignment again. So our build process now becomes: Runs buildlink to do the asset conversion (if any) and produce include source files. Also create a linkfile with all assets and produce extra include files in the case of ROM building. Assembles raptor skeleton code (rapapp.s) Translates the .bas file into .c Compiles the resulting .c file Link the above produced files together with raptor and u-235 (which are provided as libraries, as they're close sourced) Back to ROM building, it did sound like an awful waste to have a binary file in ROM that just gets copied to RAM and then executed. So by default the build process will automatically pack the RAM binary before placing it into ROM. When the ROM boots it will unpack the binary to RAM instead of just copying it. This way quite some memory can be claimed back. The build process now looks like this: Runs buildlink to do the asset conversion (if any) and produce include source files. Also create a linkfile with all assets and produce extra include files in the case of ROM building. Assembles raptor skeleton code (rapapp.s) Translates the .bas file into .c Compiles the resulting .c file Link the above produced files together with raptor and u-235 (which are provided as libraries, as they're close sourced) When creating a ROM, pack the resulting binary When creating a ROM, call makearom to create the ROM The special program simply writes a file with the cartridge header, the boot/unpack code, the ROM assets (if any), the rb+ binary and finally pads the file to 1,2,4 or 6MB. However, people complained that the unpack sometimes takes too long and think that the system has crashed. So the command unpacked was added so it's possible to create an unpacked ROM for testing (or final). For example, build.bat projectname ROM unpacked or build.bat projectname ROM UNPACKED sendy. And that's it! Hopefully next time you open build.bat instead of going "WTF is going on here???" you can appreciate all the hard work rb+ does for you .
  • Create New...