8bitworkshop/presets/astrocade/bios.c

453 lines
14 KiB
C

#define TEST
#include <string.h>
typedef unsigned char byte;
typedef signed char sbyte;
typedef unsigned short word;
typedef signed short sword;
/// HARDWARE
__sfr __at(0x00) hw_col0r; // palette 0
__sfr __at(0x01) hw_col1r;
__sfr __at(0x02) hw_col2r;
__sfr __at(0x03) hw_col3r;
__sfr __at(0x04) hw_col0l;
__sfr __at(0x05) hw_col1l;
__sfr __at(0x06) hw_col2l;
__sfr __at(0x07) hw_col3l; // palette 7
__sfr __at(0x09) hw_horcb; // horiz color boundary
__sfr __at(0x0a) hw_verbl; // vertical blanking line * 2
__sfr __at(0x0c) hw_magic; // magic register
__sfr __at(0x0e) hw_inmod; // interrupt enable
__sfr __at(0x19) hw_xpand; // expander register
__sfr __at(0x08) hw_intst; // intercept test feedback
__sfr __at(0x10) hw_p1ctrl; // player controls
__sfr __at(0x11) hw_p2ctrl; // player controls
__sfr __at(0x12) hw_p3ctrl; // player controls
__sfr __at(0x13) hw_p4ctrl; // player controls
#define M_SHIFT0 0x00
#define M_SHIFT1 0x01
#define M_SHIFT2 0x02
#define M_SHIFT3 0x03
#define M_XPAND 0x08
#define M_MOVE 0x00
#define M_OR 0x10
#define M_XOR 0x20
#define M_FLOP 0x40
#define M_SHIFT(x) ((x)&3)
#define XPAND_COLORS(off,on) (((off)&3) | (((on)&3)<<2))
/// GRAPHICS FUNCTIONS
#define VHEIGHT 89 // number of scanlines
#define VBWIDTH 40 // number of bytes per scanline
#define PIXWIDTH 160 // 4 pixels per byte
#define EXIT_CLIPDEST(addr) if ((((word)addr)&0xfff) >= 0xe10) return
byte __at (0x0000) vmagic[VHEIGHT][VBWIDTH];
byte __at (0x4000) vidmem[VHEIGHT][VBWIDTH];
// Used by MUSIC PROCESSOR:
word MUZPC; // $4FCE // MUSic Program Counter
word MUZSP; // $4FD0 // MUSic Stack Pointer
byte PVOLAB; // $4FD2 // Preset VOLume for tones A and B
byte PVOLMC; // $4FD3 // Preset VOLuMe for tone C and Noise Mode
byte VOICES; // $4FD4 // music VOICES mask
// COUNTER TIMERS (used by DECCTS,ACTINT,CTIMER):
byte CT[8];
// Used by SENTRY to track controls:
byte CNT; // $4FDD // Counter update & Number Tracking
byte SEMI4S; // $4FDE // SEMAPHORE flag bitS
byte OPOT[4]; // $4FDF // Old POT 0 tracking byte
byte KEYSEX; // $4FE3 // KEYS-EX tracking byte
byte OSW[4]; // $4FE4 // Old SWitch 0 tracking byte
byte COLLST; // $4FE8 // COLset LaST address for P.B. A
// Used by STIMER:
byte DURAT; // $4FEA // note DURATion
byte TMR60; // $4FEB // TiMeR for SIXTY'ths of sec
byte TIMOUT; // $4FEC // TIMer for blackOUT
byte GTSECS; // $4FED // Game Time SECondS
byte GTMINS; // $4FEE // Game Time MINuteS
// Used by MENU:
long RANSHT; // $4FEF // RANdom number SHifT register
byte NUMPLY; // $4FF3 // NUMber of PLaYers
byte ENDSCR[3]; // $4FF4 // END SCoRe to 'play to'
byte MRLOCK; // $4FF7 // Magic Register LOCK out flag
byte GAMSTB; // $4FF8 // GAMe STatus Byte
byte PRIOR; // $4FF9 // PRIOR music protect flag
byte SENFLG; // $4FFA // SENtry control seizure FLaG
// User UPI Routines, even numbers from $80 to $FE ( + 1 for SUCK):
byte* UMARGT; // $4FFB // User Mask ARGument Table + (routine / 2)
word* USERTB; // $4FFD // USER Table Base + routine = JumP address
// start routine @ 0x0
void bios_start() __naked {
__asm
DI
LD SP,#0x4fce
CALL _bios_init
LD HL,#0x2005
LD A,(HL)
INC HL
#ifndef TEST
LD H,(HL)
LD L,A
JP (HL)
#else
jp _main
#endif
.ds 0x38 - 0xf ; skip to 0x38
__endasm;
}
void interrupt_0x38() __naked {
__asm
push hl
push af
push bc
push de
push ix
push iy
ld hl,#0
add hl,sp
push hl
call _SYSCALL
pop hl
pop iy
pop ix
pop de
pop bc
pop af
pop hl
ret
__endasm;
}
void _predope() __naked {
__asm
.ds 0x200-0x52 ; skip to 0x200
__endasm;
}
// DOPE vector @ 0x200
void DOPE() __naked {
__asm
JP _STIMER
JP _CTIMER
__endasm;
}
// TODO: font defs
void STIMER() {
}
void CTIMER() {
}
void bios_init() {
memset((void*)0x4fce, 0, 0x5000-0x4fce);
}
///// INTERPRETER
typedef struct {
union {
struct {
word iy,ix,de,bc,af,hl;
} w;
struct {
byte iyl,iyh,ixl,ixh,e,d,c,b,f,a,l,h;
} b;
} regs;
byte* params;
} ContextBlock;
#define REG_IY 0x1
#define REG_E 0x2
#define REG_D 0x4
#define REG_C 0x8
#define REG_B 0x10
#define REG_IX 0x20
#define REG_A 0x40
#define REG_HL 0x80
#define REG_DE (REG_D|REG_E)
#define REG_BC (REG_B|REG_C)
typedef void (Routine)();
typedef void (SysRoutine)(ContextBlock *ctx);
typedef struct {
SysRoutine* routine;
byte argmask;
} SysCallEntry;
void SYSCALL(ContextBlock *ctx);
void INTPC(ContextBlock *ctx) {
while (ctx->params[0] != 2) { // 2 = exit
SYSCALL(ctx);
}
}
// never called, hopefully
void EXIT(ContextBlock *ctx) {
ctx;
}
// jumps to HL
void RCALL(ContextBlock *ctx) {
((Routine*)ctx->regs.w.hl)();
}
// Outputs D to port 0A, B to port 09, A to port 0E.
void SETOUT(ContextBlock *ctx) {
hw_verbl = ctx->regs.b.d;
hw_horcb = ctx->regs.b.b;
hw_inmod = ctx->regs.b.a;
}
// sets color palettes from (HL)
void COLSET(ContextBlock *ctx) {
byte* palette = (byte*) ctx->regs.w.hl;
hw_col0r = *palette++;
hw_col1r = *palette++;
hw_col2r = *palette++;
hw_col3r = *palette++;
hw_col0l = *palette++;
hw_col1l = *palette++;
hw_col2l = *palette++;
hw_col3l = *palette++;
}
// Stores A in BC bytes starting at location DE.
void FILL(ContextBlock *ctx) {
byte* dest = (byte*) ctx->regs.w.de;
word count = ctx->regs.w.bc;
byte val = ctx->regs.b.a;
memset(dest, val, count);
}
#define LOCHAR 32
#define HICHAR 127
#define FONT_BWIDTH 1
#define FONT_HEIGHT 8
const char FONT[HICHAR-LOCHAR+1][FONT_HEIGHT*FONT_BWIDTH] = {
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, },{ 0x00,0x00,0x00,0x20,0x20,0x20,0x00,0x20, },{ 0x00,0x00,0x50,0x50,0x50,0x00,0x00,0x00, },{ 0x00,0x00,0x00,0x50,0xF8,0x50,0xF8,0x50, },{ 0x00,0x00,0x00,0xF8,0xA0,0xF8,0x28,0xF8, },{ 0x00,0x00,0x00,0xC8,0xD0,0x20,0x58,0x98, },{ 0x00,0x00,0x00,0xE0,0xA8,0xF8,0x90,0xF8, },{ 0x00,0x00,0x40,0x40,0x40,0x00,0x00,0x00, },{ 0x00,0x00,0x30,0x20,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x60,0x20,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x00,0x20,0xA8,0x70,0xA8,0x20, },{ 0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20, },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60, },{ 0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x00, },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60, },{ 0x00,0x00,0x00,0x08,0x10,0x20,0x40,0x80, },{ 0x00,0x00,0x00,0xF8,0x88,0xE8,0x88,0xF8, },{ 0x00,0x00,0x00,0x10,0x30,0x50,0x10,0x10, },{ 0x00,0x00,0x00,0xF8,0x08,0xF8,0x80,0xF8, },{ 0x00,0x00,0x00,0xF8,0x08,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0x38,0x48,0x88,0xF8,0x08, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x08,0x10,0x20,0x40, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0x30,0x30,0x00,0x30,0x30, },{ 0x00,0x00,0x00,0x30,0x30,0x00,0x30,0x30, },{ 0x00,0x00,0x08,0x10,0x20,0x40,0x20,0x10, },{ 0x00,0x00,0x00,0x00,0xF8,0x00,0xF8,0x00, },{ 0x00,0x00,0x40,0x20,0x10,0x08,0x10,0x20, },{ 0x00,0x00,0x00,0xF8,0x08,0x78,0x00,0x60, },{ 0x00,0x00,0x00,0xF8,0xA8,0xB8,0x80,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x88,0x88, },{ 0x00,0x00,0x00,0xF0,0x90,0xF8,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0x80,0x80,0xF8, },{ 0x00,0x00,0x00,0xE0,0x90,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x80,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x80,0x80, },{ 0x00,0x00,0x00,0xF8,0x80,0xB8,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x88,0xF8,0x88,0x88, },{ 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40, },{ 0x00,0x00,0x00,0x08,0x08,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x90,0xA0,0x90,0x88, },{ 0x00,0x00,0x00,0x80,0x80,0x80,0x80,0xF8, },{ 0x00,0x00,0x00,0xFE,0x92,0x92,0x92,0x92, },{ 0x00,0x00,0x00,0x88,0xC8,0xA8,0x98,0x88, },{ 0x00,0x00,0x00,0xF8,0x88,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0x88,0xF8,0x80, },{ 0x00,0x00,0x00,0xF8,0x88,0xA8,0xA8,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x90,0x88, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x00,0x88,0x88,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x88,0x90,0xA0,0xC0, },{ 0x00,0x00,0x00,0x92,0x92,0x92,0x92,0xFE, },{ 0x00,0x00,0x00,0x88,0x50,0x20,0x50,0x88, },{ 0x00,0x00,0x00,0x88,0x88,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x10,0x20,0x40,0xF8, },{ 0x00,0x00,0x38,0x20,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x00,0x80,0x40,0x20,0x10,0x08, },{ 0x00,0x00,0x70,0x10,0x10,0x10,0x10,0x10, },{ 0x00,0x00,0x00,0x20,0x50,0x88,0x00,0x00, },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, },{ 0x00,0x00,0x40,0x20,0x10,0x00,0x00,0x00, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x88,0x88, },{ 0x00,0x00,0x00,0xF0,0x90,0xF8,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0x80,0x80,0xF8, },{ 0x00,0x00,0x00,0xE0,0x90,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x80,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x80,0x80, },{ 0x00,0x00,0x00,0xF8,0x80,0xB8,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x88,0xF8,0x88,0x88, },{ 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40, },{ 0x00,0x00,0x00,0x08,0x08,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x90,0xA0,0x90,0x88, },{ 0x00,0x00,0x00,0x80,0x80,0x80,0x80,0xF8, },{ 0x00,0x00,0x00,0xFE,0x92,0x92,0x92,0x92, },{ 0x00,0x00,0x00,0x88,0xC8,0xA8,0x98,0x88, },{ 0x00,0x00,0x00,0xF8,0x88,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0x88,0xF8,0x80, },{ 0x00,0x00,0x00,0xF8,0x88,0xA8,0xA8,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x90,0x88, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x00,0x88,0x88,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x88,0x90,0xA0,0xC0, },{ 0x00,0x00,0x00,0x92,0x92,0x92,0x92,0xFE, },{ 0x00,0x00,0x00,0x88,0x50,0x20,0x50,0x88, },{ 0x00,0x00,0x00,0x88,0x88,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x10,0x20,0x40,0xF8, },{ 0x00,0x00,0x38,0x20,0x20,0xE0,0x20,0x20, },{ 0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0xE0,0x20,0x20,0x38,0x20,0x20, },{ 0x00,0x00,0x00,0xE8,0xB8,0x00,0x00,0x00, },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, },};
// draw a letter
void draw_char(byte ch, byte x, byte y, byte op) {
const byte* src = &FONT[(ch-LOCHAR)][0];
byte xb = x>>2; // divide x by 4
byte* dest = &vmagic[y][xb]; // destination address
hw_magic = M_SHIFT(x) | M_XPAND | op;
for (byte i=0; i<8; i++) {
char b = *src++;
EXIT_CLIPDEST(dest);
*dest++ = b; // expand lower nibble -> 1st byte
*dest++ = b; // expand upper nibble -> 2nd byte
*dest++ = 0; // leftover -> 3rd byte
*dest = 0; // reset upper/lower flag
dest += VBWIDTH-3; // we incremented 3 bytes for this line
}
}
void draw_string(const char* str, byte x, byte y, byte op) {
do {
byte ch = *str++;
if (!ch) break;
draw_char(ch, x, y, op);
x += 8;
} while (1);
}
// String display routine
void STRDIS(ContextBlock *ctx) {
byte opts = ctx->regs.b.c;
byte x = ctx->regs.b.e;
byte y = ctx->regs.b.d;
char* str = (char*) ctx->regs.w.hl;
void* fontdesc = (void*) ctx->regs.w.ix;
hw_xpand = opts & 0xf;
draw_string(str, x, y, 3&(opts>>4)); // TODO: opts
}
// Character display routine
void CHRDIS(ContextBlock *ctx) {
char chstr[2];
chstr[0] = ctx->regs.b.a;
chstr[1] = 0;
ctx->regs.w.hl = (word) &chstr;
STRDIS(ctx);
}
const SysCallEntry SYSCALL_TABLE[64] = {
/* 0 */
{ &INTPC, 0 },
{ &EXIT, 0 },
{ &RCALL, REG_HL },
{ 0, 0 },
{ 0, 0 },
/* 10 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 20 */
{ 0, 0 },
{ &SETOUT, REG_D|REG_B|REG_A },
{ &COLSET, REG_HL },
{ &FILL, REG_A|REG_BC|REG_DE },
{ 0, 0 },
/* 30 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 40 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 50 */
{ &CHRDIS, REG_E|REG_D|REG_C|REG_A },
{ &STRDIS, REG_E|REG_D|REG_C|REG_HL },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 60 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 70 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 80 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 90 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 100 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 110 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 120 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
};
void suckParams(ContextBlock *ctx, byte argmask) {
byte* dest = (byte*) ctx;
byte* src = ctx->params;
if (argmask & REG_IX) {
ctx->regs.b.ixl = *src++;
ctx->regs.b.ixh = *src++;
}
if (argmask & REG_E)
ctx->regs.b.e = *src++;
if (argmask & REG_D)
ctx->regs.b.d = *src++;
if (argmask & REG_C)
ctx->regs.b.c = *src++;
if (argmask & REG_B)
ctx->regs.b.b = *src++;
if (argmask & REG_A)
ctx->regs.b.a = *src++;
if (argmask & REG_HL) {
ctx->regs.b.l = *src++;
ctx->regs.b.h = *src++;
}
ctx->params = src;
}
void SYSCALL(ContextBlock *ctx) {
byte op = *ctx->params++;
byte argmask;
SysRoutine* routine;
// user-defined?
if (op & 0x80) {
argmask = UMARGT[op & 0x7f];
routine = (SysRoutine*) USERTB[op & 0x7f];
} else {
const SysCallEntry* entry = &SYSCALL_TABLE[op>>1];
argmask = entry->argmask;
routine = entry->routine;
}
// suck params into context block?
if (op & 1) {
suckParams(ctx, argmask);
}
// call the routine
if (routine) {
routine(ctx);
}
}
#ifdef TEST
void main() {
__asm
RST 0x38
.db 0 ; INTPC
.db 22+1 ; SETOUT
.db 102*2, 0x1c, 0x08
.db 24+1 ; COLSET
.dw _main ; palette???
.db 26+1 ; FILL
.dw 0x4000
.dw 90*40
.db 0xaa
.db 52+1 ; draw string
.db 0
.db 32
.db 0x0c
.dw HelloString
.db 50+1 ; draw char
.db 0
.db 64
.db 0x0c
.db 65
.db 2 ; EXIT
jp .end
HelloString:
.ascii "HELLO WORLD!"
.db 0
.end:
__endasm;
while (1) CT[0]++;
}
#endif