Choosing an assembler
Originally the Oric C development kit was using FRASM1 to assemble the 6502 code generated by the C compiler front-end.One of the first changes we did when starting the OSDK was to use instead a real 6502 macro-assembler with an extended set of features, and ultimately we choose XA because it supported most of the C compiler preprocessor directives.
The C Pre-processor
Modern development practices tend to dismiss preprocessor features as being horrible hacks that should not be used by any self-respecting developer, but in practice it happens that you can't even do modern portable C++11 development without a large dose of #ifdef to abstract platform and compiler specific kludges, so take all this with a grain of salt: The preprocessor is here to stay.You can refer to the XA documentation page for more details, but basically all you need to know is that the following commands will work just as well in the C and 6502 code you are writing using the OSDK:
- #include "filename"
- #define NAME text
- #if/#ifdef/#ifndef/#else/#endif
- #file "filename"
Software Configuration
When developing a large program, it's often practical to be able to easily enable or disable entire sections of the code so you can concentrate on what you are working on at a particular moment.Typically if you write a game, you may want to skip the intro sequence and instead force the loading of a precedent save game, or disable all the small animations between the sequences.
An ideal way to achieve that is to use conditional compilation.
The way I do that in my own code is to add a file shared by both my C and assembler modules, containing the list of all options, one such example would be the defines.h in the Oric Tech demo:
The code that includes the file can then just contain whole sections of #ifdef ENABLE_xxxx and they will automatically get compiled or not depending if the defines are commented out or not.
#ifndef _DEFINES_INCLUDED_ALREADY_H_
#define _DEFINES_INCLUDED_ALREADY_H_
//
// Comment out these defines to enable or disable the various features of the engine
//
#define ENABLE_SOUND
#define ENABLE_VIP_INTRO // Comment out to disable the entire intro
#define ENABLE_TECH_TECH // Comment out to disable the tech tech part
#define ENABLE_MUSIC
//#define FAST_INTRO
#define CHARMAP_WIDTH 256
#define CHARMAP_HEIGHT 21
#define CHARMAP_SIZE CHARMAP_WIDTH*CHARMAP_HEIGHT
#define BIGFONT_WIDTH 193
#define BIGFONT_HEIGHT 5
#define BIGFONT_SIZE BIGFONT_WIDTH*BIGFONT_HEIGHT
#define INVERSE_WIDTH 36
#define INVERSE_HEIGHT 28
#define INVERSE_SIZE INVERSE_WIDTH*INVERSE_HEIGHT
#endif // _DEFINES_INCLUDED_ALREADY_H_
APIs
You can also use a similar method to develop API2s for your reusable code.The issue there is that even if the preprocessor directives are identical, the C and Assembler code on the other hand are vastly different.
Let say you have created a small library to easily draw sprites on the screen, and in particular you want to provide a function to set the screen coordinates of any sprite:
SetSpritePosition, three parameters: Sprite number, X position, Y position
How could you possibly make it usable easily from both C and assembler?
The natural way to express that in C would be with a function call, such as:
In assembler on the other hand we try to use registers as much as possible, so we probably would get something like that:
void SetSpritePosition(int number,int x,int y);
Since most of the reusable libraries are written in Assembler on the Oric, we will assume that the sprite library was written in Assembler, and that the C API will be designed to call assembler code.
; a: sprite index
; x: x position
; y: y position
_SetSpritePosition
One way to abstract the calls would be to use macros instead of function calls:
and something similar would work in C as well:
// SetSpritePosition, assembler version
#define SetSpritePosition(sprite_index,x_position,y_position) lda #sprite_index:ldx #x_position:
ldy #y_position:jsr _SetSpritePosition (same line)
The question is: How do we force the C compiler to use the second definition and the Assembler to use the first one?
// SetSpritePosition, C version
#define SetSpritePosition(sprite_index,x_position,y_position) \
tempA=sprite_index;\
tempX=x_position;\
tempY=y_position;\
SetSpritePositionFromTempVariables();
Detecting the language
The answer is simple: When you build a project with the OSDK, there is a bunch of defines available for you:- ASSEMBLER is defined when XA is called, so should be available in any assembler module
- OSDKNAME_%OSDKNAME% contains the name of the program being compiled so you can share some files between multiple executables3
And that's how you can have both C and Assembler in the same file and abstract complicated things quite easily.
#ifdef ASSEMBLER
// SetSpritePosition, assembler version
#define SetSpritePosition(sprite_index,x_position,y_position) lda #sprite_index:ldx #x_position:
ldy #y_position:jsr _SetSpritePosition (same line)
#else
// SetSpritePosition, C version
#define SetSpritePosition(sprite_index,x_position,y_position) \
tempA=sprite_index;\
tempX=x_position;\
tempY=y_position;\
SetSpritePositionFromTempVariables();
#endif
This is also what the FloppyBuilder is using when it generates the floppy layout file: The list of defines with the file entries are common to the C and Assembler parts, while the actual arrays and metadata elements are only exposed to the LOADER's assembler API.
Hope this will help you :)