#include <stdint.h>

const int res = 32;

#define setbit(arr, i) arr[i/8] |= (1 << (i%8))
#define clrbit(arr, i) arr[(i)/8] &= ~(1 << ((i) % 8))
#define flpbit(arr, i) arr[(i)/8] ^= 1 << ((i) % 8)
#define getbit(arr, i) arr[(i)/8] & (1 << ((i) % 8))

#ifdef RLE
#define NAME_BIN "badapple_rle_bin"
#else
#define NAME_BIN "badapple_diff_bin"
#endif

extern uint8_t _binary_badapple_bin_start[] asm("_binary_" NAME_BIN "_start");
extern uint8_t _binary_badapple_bin_end[] asm("_binary_" NAME_BIN "_end");

static uint8_t screen[0x80];

#ifdef __LINUX__
#include <unistd.h>
#define clear_screen "\033[2J\033[1;1H"

void display() {
    char buf[res*res*2 + res];
    char* p = buf;
    for (int i = 0; i < res; i++) {
        for (int j = 0; j < res; j++) {
            uint8_t on = getbit(screen, (i * res) + j);
            *p++ = on ? '#' : ' ';
            *p++ = on ? '#' : ' ';
        }
        *p = '\n';
        p++;
    }
    write(1, clear_screen, sizeof(clear_screen)-1);
    write(1, buf, sizeof(buf));
    // ""vsync"""
    usleep(16666*FRAMESKIP);
}
#define ROMByteAccess(addr) *(uint8_t*)(addr)
#endif

#ifdef __POCKETSTATION__
#define FPS 96
#define CLOCK_SPEED CLOCK_8M
#define VSYNC_COUNT (FRAMESKIP * (FPS/24))
#include "pocketlib.h"

static void display() {
    // ????
    VSync(VSYNC_COUNT);
    uint32_t *src = (uint32_t*)screen;
	volatile unsigned int *dst = LCD_VRAM_ADRS;
	int i = 32;
	while (i--) {*dst++ = *src++;}
}

uint8_t	ROMByteAccess(unsigned char *addr)
{
	uint16_t data = *(uint16_t*)(((uint32_t)addr) & ~1);

	if (((uint32_t)addr) & 1)
		return (data >> 8);
	return (data & 0xFF);
}
#endif

int main(void) {
    #ifdef __POCKETSTATION__
    PocketInit();
    #endif
    
    do {
        // read all frames
        uint8_t* p = _binary_badapple_bin_start;
        while(p < _binary_badapple_bin_end) {
            // read a frame
            uint16_t px = 0;
            while(px < res*res && p < _binary_badapple_bin_end) {
                uint8_t value = ROMByteAccess(p++);
                uint8_t changed = value & 1;
                uint8_t count = value >> 1;

                #ifdef RLE
                    for(int i = px; i < count+px; i++) {
                        if(changed) setbit(screen, i);
                        else clrbit(screen, i);
                    }
                #else // diff + rle
                    if(changed) {
                        for (int i = px; i < px+count; i++) flpbit(screen, i);
                    }
                #endif
                px += count;
            }
            display();
        }
    #ifdef LOOP
    } while(1);
    #else
    } while(0);
    #endif

    #ifdef __POCKETSTATION__
    PocketExit();
    #endif
    return 0;
}
