
#include <string.h>

typedef unsigned char byte;
typedef signed char sbyte;
typedef unsigned short word;
typedef enum { false, true } bool;

byte __at (0x0) vidmem[152][256]; // 304x256x4bpp video memory
byte __at (0xc000) palette[16];
volatile byte __at (0xc804) input0;
volatile byte __at (0xc806) input1;
volatile byte __at (0xc80c) input2;
byte __at (0xc80c) sound_pia;
byte __at (0xc900) rom_select;
volatile byte __at (0xcb00) video_counter;
byte __at (0xcbff) watchdog0x39;
byte __at (0xcc00) nvram[0x400];

__sfr __at (0) debug;

// blitter flags
#define SRCSCREEN 0x1
#define DSTSCREEN 0x2
#define ESYNC 0x4
#define FGONLY 0x8
#define SOLID 0x10
#define RSHIFT 0x20
#define EVENONLY 0x40
#define ODDONLY 0x80

struct {
  byte flags;
  byte solid;
  word sstart;
  word dstart;
  byte width;
  byte height;
} __at (0xca00) blitter;

// switch flags
#define UP1 (input0 & 0x1)
#define DOWN1 (input0 & 0x2)
#define LEFT1 (input0 & 0x4)
#define RIGHT1 (input0 & 0x8)
#define START1 (input0 & 0x10)
#define START2 (input0 & 0x20)
#define UP2 (input0 & 0x40)
#define DOWN2 (input0 & 0x80)
#define LEFT2 (input1 & 0x1)
#define RIGHT2 (input1 & 0x2)
#define AUTOUP (input2 & 0x1)
#define ADVANCE (input2 & 0x2)
#define COIN2 (input2 & 0x4)
#define HIGHSCORERESET (input2 & 0x8)
#define COIN1 (input2 & 0x10)
#define COIN3 (input2 & 0x20)
#define TILTSWITCH (input2 & 0x40)
#define SOUNDACK (input2 & 0x80)

#define WATCHDOG watchdog0x39=0x39;

//

void main();

// start routine @ 0x0
// set stack pointer, enable interrupts
void start() {
__asm
        LD      SP,#0xc000
        DI
; copy initialized data
        LD    BC, #l__INITIALIZER
        LD    A, B
        LD    DE, #s__INITIALIZED
        LD    HL, #s__INITIALIZER
        LDIR
__endasm;
  
  main();
}

inline word swapw(word j) {
  return ((j << 8) | (j >> 8));
}

// x1: 0-151
// y1: 0-255
inline void blit_solid(byte x1, byte y1, byte w, byte h, byte color) {
  blitter.width = w^4;
  blitter.height = h^4;
  blitter.dstart = x1+y1*256; // swapped
  blitter.solid = color;
  blitter.flags = DSTSCREEN|SOLID;
}

inline void draw_solid(word x1, byte y1, byte w, byte h, byte color) {
  blitter.width = w^4;
  blitter.height = h^4;
  blitter.dstart = (x1>>1)+y1*256; // swapped
  blitter.solid = color;
  blitter.flags = (x1&1) ? DSTSCREEN|SOLID|RSHIFT : DSTSCREEN|SOLID;
}

inline void draw_vline(word x1, byte y1, byte h, byte color) {
  blitter.width = 1^4;
  blitter.height = h^4;
  blitter.dstart = (x1>>1)+y1*256; // swapped
  blitter.solid = color;
  blitter.flags = (x1&1) ? DSTSCREEN|SOLID|ODDONLY : DSTSCREEN|SOLID|EVENONLY;
}

inline void blit_copy_solid(word x, byte y, byte w, byte h, const byte* data, byte solid) {
  blitter.width = w^4;
  blitter.height = h^4;
  blitter.solid = solid;
  blitter.sstart = swapw((word)data);
  blitter.dstart = (x>>1)+y*256; // swapped
  if (solid)
    blitter.flags = (x&1) ? DSTSCREEN|FGONLY|SOLID|RSHIFT : DSTSCREEN|FGONLY|SOLID;
  else
    blitter.flags = (x&1) ? DSTSCREEN|RSHIFT : DSTSCREEN;
}

// bias sprites by +4 pixels
#define XBIAS 2

inline void draw_sprite(const byte* data, byte x, byte y) {
  blitter.width = data[0]^4;
  blitter.height = data[1]^4;
  blitter.sstart = swapw((word)(data+2));
  blitter.dstart = (x>>1)+y*256+XBIAS; // swapped
  blitter.flags = (x&1) ? DSTSCREEN|FGONLY|RSHIFT : DSTSCREEN|FGONLY;
}

inline void draw_sprite_solid(const byte* data, byte x, byte y, byte color) {
  blitter.width = data[0]^4;
  blitter.height = data[1]^4;
  blitter.sstart = swapw((word)(data+2));
  blitter.dstart = (x>>1)+y*256+XBIAS; // swapped
  blitter.solid = color;
  blitter.flags = (x&1) ? DSTSCREEN|FGONLY|RSHIFT|SOLID : DSTSCREEN|FGONLY|SOLID;
}

void draw_box(word x1, byte y1, word x2, byte y2, byte color) {
  draw_solid(x1, y1, (x2-x1)>>1, 1, color);
  draw_solid(x1, y2, (x2-x1)>>1, 1, color);
  draw_vline(x1, y1, y2-y1, color);
  draw_vline(x2, y1, y2-y1, color);
}

static byte frame = 0;

void main() {
  palette[0] = 0x11;
  palette[1] = 0xff+frame++;
  blit_solid(0,0,25,25,0x11);
  main();
}
