initial commit

This commit is contained in:
2026-07-18 20:34:55 +02:00
commit 59ad004c96
474 changed files with 226635 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
#include "asm6502.h"
inline byte asm_np(byte * ip, AsmIns ins)
{
ip[0] = ins & 0xff;
return 1;
}
inline byte asm_ac(byte * ip, AsmIns ins)
{
ip[0] = (ins & 0xff) | 0x08;
return 1;
}
inline byte asm_zp(byte * ip, AsmIns ins, byte addr)
{
ip[0] = (ins & 0xff) | 0x04;
ip[1] = addr;
return 2;
}
inline byte asm_rl(byte * ip, AsmIns ins, sbyte addr)
{
ip[0] = ins & 0xff;
ip[1] = (byte)addr;
return 2;
}
inline byte asm_im(byte * ip, AsmIns ins, byte value)
{
ip[0] = (ins & 0xff) | ((ins & 0x01) << 3);
ip[1] = value;
return 2;
}
inline byte asm_zx(byte * ip, AsmIns ins, byte addr)
{
ip[0] = (ins & 0xff) | 0x05;
ip[1] = addr;
return 2;
}
inline byte asm_zy(byte * ip, AsmIns ins, byte addr)
{
ip[0] = (ins & 0xff) | 0x05;
ip[1] = addr;
return 2;
}
inline byte asm_ab(byte * ip, AsmIns ins, unsigned addr)
{
ip[0] = (ins & 0xff) ^ 0x0c;
ip[1] = addr & 0xff;
ip[2] = addr >> 8;
return 3;
}
inline byte asm_in(byte * ip, AsmIns ins, unsigned addr)
{
ip[0] = (ins & 0xff) ^ 0x2c;
ip[1] = addr & 0xff;
ip[2] = addr >> 8;
return 3;
}
inline byte asm_ax(byte * ip, AsmIns ins, unsigned addr)
{
ip[0] = (ins & 0xff) | 0x1c;
ip[1] = addr & 0xff;
ip[2] = addr >> 8;
return 3;
}
inline byte asm_ay(byte * ip, AsmIns ins, unsigned addr)
{
ip[0] = (ins & 0xff) | 0x18;
ip[1] = addr & 0xff;
ip[2] = addr >> 8;
return 3;
}
inline byte asm_ix(byte * ip, AsmIns ins, byte addr)
{
ip[0] = (ins & 0xff) | 0x00;
ip[1] = addr;
return 2;
}
inline byte asm_iy(byte * ip, AsmIns ins, byte addr)
{
ip[0] = (ins & 0xff) | 0x10;
ip[1] = addr;
return 2;
}
+132
View File
@@ -0,0 +1,132 @@
#ifndef C64_ASM_6502_H
#define C64_ASM_6502_H
#include "types.h"
// Base form for the 6502 instructions
enum AsmIns
{
// Implied
ASM_BRK = 0x00,
ASM_RTI = 0x40,
ASM_RTS = 0x60,
ASM_PHP = 0x08,
ASM_CLC = 0x18,
ASM_PLP = 0x28,
ASM_SEC = 0x38,
ASM_PHA = 0x48,
ASM_CLI = 0x58,
ASM_PLA = 0x68,
ASM_SEI = 0x78,
ASM_DEY = 0x88,
ASM_TYA = 0x98,
ASM_TAY = 0xa8,
ASM_CLV = 0xb8,
ASM_INY = 0xc8,
ASM_CLD = 0xd8,
ASM_INX = 0xe8,
ASM_SED = 0xf8,
ASM_TXA = 0x8a,
ASM_TXS = 0x9a,
ASM_TAX = 0xaa,
ASM_TSX = 0xba,
ASM_DEX = 0xca,
ASM_NOP = 0xea,
// Relative
ASM_BPL = 0x10,
ASM_BMI = 0x30,
ASM_BVC = 0x50,
ASM_BVS = 0x70,
ASM_BCC = 0x90,
ASM_BCS = 0xb0,
ASM_BNE = 0xd0,
ASM_BEQ = 0xf0,
// Generic address
ASM_ORA = 0x01,
ASM_AND = 0x21,
ASM_EOR = 0x41,
ASM_ADC = 0x61,
ASM_STA = 0x81,
ASM_LDA = 0xa1,
ASM_CMP = 0xc1,
ASM_SBC = 0xe1,
ASM_STY = 0x80,
ASM_LDY = 0xa0,
ASM_CPY = 0xc0,
ASM_CPX = 0xe0,
ASM_ASL = 0x02,
ASM_ROL = 0x22,
ASM_LSR = 0x42,
ASM_ROR = 0x62,
ASM_STX = 0x82,
ASM_LDX = 0xa2,
ASM_DEC = 0xc2,
ASM_INC = 0xe2,
// Limited Generic
ASM_BIT = 0x20,
// Jump
ASM_JMP = 0x40,
ASM_JSR = 0x2c
};
// the asm_ instructions emit a machine instruction at the given
// location and return the size.
// implied
inline byte asm_np(byte * ip, AsmIns ins);
// accu (e.g. rol/ror)
inline byte asm_ac(byte * ip, AsmIns ins);
// zero page
inline byte asm_zp(byte * ip, AsmIns ins, byte addr);
// relative branch
inline byte asm_rl(byte * ip, AsmIns ins, sbyte addr);
// immediate
inline byte asm_im(byte * ip, AsmIns ins, byte value);
// zero page indexed by x
inline byte asm_zx(byte * ip, AsmIns ins, byte addr);
// zero page indexed by y
inline byte asm_zy(byte * ip, AsmIns ins, byte addr);
// absolute
inline byte asm_ab(byte * ip, AsmIns ins, unsigned addr);
// indirect (jmp)
inline byte asm_in(byte * ip, AsmIns ins, unsigned addr);
// absolute indexed by x
inline byte asm_ax(byte * ip, AsmIns ins, unsigned addr);
// absolute indexed by y
inline byte asm_ay(byte * ip, AsmIns ins, unsigned addr);
// zero page indirect indexed by x
inline byte asm_ix(byte * ip, AsmIns ins, byte addr);
// zero page indirect indexed by y
inline byte asm_iy(byte * ip, AsmIns ins, byte addr);
#pragma compile("asm6502.c")
#endif
+985
View File
@@ -0,0 +1,985 @@
#include "charwin.h"
#include <stdio.h>
static const unsigned mul40[25] = {
0, 40, 80, 120, 160,
200, 240, 280, 320, 360,
400, 440, 480, 520, 560,
600, 640, 680, 720, 760,
800, 840, 880, 920, 960
};
static __native inline void copy_fwd(char * sdp, const char * ssp, char * cdp, const char * csp, char n)
{
for(char i=0; i<n; i++)
{
sdp[i] = ssp[i];
cdp[i] = csp[i];
}
}
static __native inline void fill_fwd(char * sdp, char * cdp, char ch, char color, char n)
{
for(char i=0; i<n; i++)
{
sdp[i] = ch;
cdp[i] = color;
}
}
static __native inline void copy_bwd(char * sdp, const char * ssp, char * cdp, const char * csp, char n)
{
while (n)
{
n--;
sdp[n] = ssp[n];
cdp[n] = csp[n];
}
}
void cwin_init(CharWin * win, char * screen, char sx, char sy, char wx, char wy)
{
win->sx = sx;
win->sy = sy;
win->wx = wx;
win->wy = wy;
win->cx = 0;
win->cy = 0;
win->sp = screen + mul40[sy] + sx;
win->cp = (char *)0xd800 + mul40[sy] + sx;
}
void cwin_clear(CharWin * win)
{
cwin_fill(win, ' ', 1);
}
void cwin_fill(CharWin * win, char ch, char color)
{
char *sp = win->sp, * cp = win->cp;
for(char y=0; y<win->wy; y++)
{
fill_fwd(sp, cp, ch, color, win->wx);
sp += 40;
cp += 40;
}
}
void cwin_cursor_show(CharWin * win, bool show)
{
char * cp = win->sp + mul40[win->cy] + win->cx;
if (show)
*cp |= 0x80;
else
*cp &= 0x7f;
}
void cwin_cursor_move(CharWin * win, char cx, char cy)
{
win->cx = cx;
win->cy = cy;
}
bool cwin_cursor_left(CharWin * win)
{
if (win->cx > 0)
{
win->cx--;
return true;
}
return false;
}
bool cwin_cursor_right(CharWin * win)
{
if (win->cx + 1 < win->wx)
{
win->cx++;
return true;
}
return false;
}
bool cwin_cursor_up(CharWin * win)
{
if (win->cy > 0)
{
win->cy--;
return true;
}
return false;
}
bool cwin_cursor_down(CharWin * win)
{
if (win->cy + 1 < win->wy)
{
win->cy++;
return true;
}
return false;
}
bool cwin_cursor_newline(CharWin * win)
{
win->cx = 0;
if (win->cy + 1 < win->wy)
{
win->cy++;
return true;
}
return false;
}
bool cwin_cursor_forward(CharWin * win)
{
if (win->cx + 1 < win->wx)
{
win->cx++;
return true;
}
else if (win->cy + 1 < win->wy)
{
win->cx = 0;
win->cy++;
return true;
}
return false;
}
bool cwin_cursor_backward(CharWin * win)
{
if (win->cx > 0)
{
win->cx--;
return true;
}
else if (win->cy > 0)
{
win->cx = win->wx - 1;
win->cy--;
return true;
}
return false;
}
//static char p2smap[] = {0x00, 0x20, 0x00, 0x40, 0x00, 0x60, 0x40, 0x60};
static char p2smap[] = {0x00, 0x00, 0x40, 0x20, 0x80, 0xc0, 0x80, 0x80};
//static char s2pmap[] = {0x40, 0x20, 0x60, 0xa0, 0x40, 0x20, 0x60, 0xa0};
static char s2pmap[] = {0x40, 0x00, 0x20, 0xc0, 0xc0, 0x80, 0xa0, 0x40};
static inline char p2s(char ch)
{
return ch ^ p2smap[ch >> 5];
}
static inline char s2p(char ch)
{
return ch ^ s2pmap[ch >> 5];
}
void cwin_read_string(CharWin * win, char * buffer)
{
char * sp = win->sp;
char i = 0;
for(char y=0; y<win->wy; y++)
{
for(char x=0; x<win->wx; x++)
{
buffer[i++] = s2p(sp[x]);
}
sp += 40;
}
while (i > 0 && buffer[i - 1] == ' ')
i--;
buffer[i] = 0;
}
void cwin_write_string(CharWin * win, const char * buffer)
{
char * dp = win->sp;
for(char y=0; y<win->wy; y++)
{
for(char x=0; x<win->wx; x++)
{
char ch = *buffer;
if (ch)
{
dp[x] = p2s(ch);
buffer++;
}
else
dp[x] = ' ';
}
dp += 40;
}
}
void cwin_put_char(CharWin * win, char ch, char color)
{
cwin_putat_char(win, win->cx, win->cy, ch, color);
win->cx++;
if (win->cx == win->wx)
{
win->cx = 0;
win->cy++;
}
}
void cwin_put_chars(CharWin * win, const char * chars, char num, char color)
{
cwin_putat_chars(win, win->cx, win->cy, chars, color);
win->cx += num;
if (win->cx >= win->wx)
{
win->cx = 0;
win->cy++;
}
}
char cwin_put_string(CharWin * win, const char * str, char color)
{
char n = cwin_putat_string(win, win->cx, win->cy, str, color);
win->cx += n;
if (win->cx >= win->wx)
{
win->cx = 0;
win->cy++;
}
return n;
}
void cwin_put_char_raw(CharWin * win, char ch, char color)
{
cwin_putat_char_raw(win, win->cx, win->cy, ch, color);
win->cx++;
if (win->cx == win->wx)
{
win->cx = 0;
win->cy++;
}
}
void cwin_put_chars_raw(CharWin * win, const char * chars, char num, char color)
{
cwin_putat_chars_raw(win, win->cx, win->cy, chars, color);
win->cx += num;
if (win->cx >= win->wx)
{
win->cx = 0;
win->cy++;
}
}
char cwin_put_string_raw(CharWin * win, const char * str, char color)
{
char n = cwin_putat_string_raw(win, win->cx, win->cy, str, color);
win->cx += n;
if (win->cx >= win->wx)
{
win->cx = 0;
win->cy++;
}
return n;
}
void cwin_putat_char(CharWin * win, char x, char y, char ch, char color)
{
int offset = mul40[y] + x;
win->sp[offset] = p2s(ch);
win->cp[offset] = color;
}
#pragma native(cwin_putat_char)
void cwin_putat_chars(CharWin * win, char x, char y, const char * chars, char num, char color)
{
int offset = mul40[y] + x;
char * sp = win->sp + offset;
char * cp = win->cp + offset;
for(char i=0; i<num; i++)
{
char ch = chars[i];
sp[i] = p2s(ch);
cp[i] = color;
}
}
#pragma native(cwin_putat_chars)
char cwin_putat_string(CharWin * win, char x, char y, const char * str, char color)
{
int offset = mul40[y] + x;
char * sp = win->sp + offset;
char * cp = win->cp + offset;
char i = 0;
while (char ch = str[i])
{
sp[i] = p2s(ch);
cp[i] = color;
i++;
}
return i;
}
#pragma native(cwin_putat_string)
void cwin_putat_char_raw(CharWin * win, char x, char y, char ch, char color)
{
int offset = mul40[y] + x;
win->sp[offset] = ch;
win->cp[offset] = color;
}
#pragma native(cwin_putat_char_raw)
void cwin_putat_chars_raw(CharWin * win, char x, char y, const char * chars, char num, char color)
{
int offset = mul40[y] + x;
char * sp = win->sp + offset;
char * cp = win->cp + offset;
for(char i=0; i<num; i++)
{
char ch = chars[i];
sp[i] = ch;
cp[i] = color;
}
}
#pragma native(cwin_putat_chars_raw)
char cwin_putat_string_raw(CharWin * win, char x, char y, const char * str, char color)
{
int offset = mul40[y] + x;
char * sp = win->sp + offset;
char * cp = win->cp + offset;
char i = 0;
while (char ch = str[i])
{
sp[i] = ch;
cp[i] = color;
i++;
}
return i;
}
#pragma native(cwin_putat_string_raw)
char cwin_getat_char(CharWin * win, char x, char y)
{
char * sp = win->sp + mul40[y] + x;
return s2p(*sp);
}
#pragma native(cwin_getat_char)
void cwin_getat_chars(CharWin * win, char x, char y, char * chars, char num)
{
char * sp = win->sp + mul40[y] + x;
for(char i=0; i<num; i++)
{
chars[i] = s2p(sp[i]);
}
}
#pragma native(cwin_getat_chars)
char cwin_getat_char_raw(CharWin * win, char x, char y)
{
char * sp = win->sp + mul40[y] + x;
return *sp;
}
#pragma native(cwin_getat_chars_raw)
void cwin_getat_chars_raw(CharWin * win, char x, char y, char * chars, char num)
{
char * sp = win->sp + mul40[y] + x;
for(char i=0; i<num; i++)
{
chars[i] = sp[i];
}
}
#pragma native(cwin_put_rect_raw)
void cwin_put_rect_raw(CharWin * win, char x, char y, char w, char h, const char * chars, char color)
{
int offset = mul40[y] + x;
char * sp = win->sp + offset;
char * cp = win->cp + offset;
for(char i=0; i<h; i++)
{
for(char j=0; j<w; j++)
{
sp[j] = chars[j];
cp[j] = color;
}
chars += w;
sp += 40;
cp += 40;
}
}
#pragma native(cwin_put_rect)
void cwin_put_rect(CharWin * win, char x, char y, char w, char h, const char * chars, char color)
{
int offset = mul40[y] + x;
char * sp = win->sp + offset;
char * cp = win->cp + offset;
for(char i=0; i<h; i++)
{
for(char j=0; j<w; j++)
{
sp[j] = p2s(chars[j]);
cp[j] = color;
}
chars += w;
sp += 40;
cp += 40;
}
}
#pragma native(cwin_get_rect_raw)
void cwin_get_rect_raw(CharWin * win, char x, char y, char w, char h, char * chars)
{
int offset = mul40[y] + x;
char * sp = win->sp + offset;
for(char i=0; i<h; i++)
{
for(char j=0; j<w; j++)
{
chars[j] = sp[j];
}
chars += w;
sp += 40;
}
}
#pragma native(cwin_get_rect)
void cwin_get_rect(CharWin * win, char x, char y, char w, char h, char * chars)
{
int offset = mul40[y] + x;
char * sp = win->sp + offset;
for(char i=0; i<h; i++)
{
for(char j=0; j<w; j++)
{
chars[j] = s2p(sp[j]);
}
chars += w;
sp += 40;
}
}
#pragma native(cwin_getat_chars_raw)
void cwin_insert_char_raw(CharWin * win, char ch, char color)
{
char y = win->wy - 1, rx = win->wx - 1;
char * sp = win->sp + mul40[y];
char * cp = win->cp + mul40[y];
while (y > win->cy)
{
copy_bwd(sp + 1, sp, cp + 1, cp, rx);
sp -= 40;
cp -= 40;
sp[40] = sp[rx];
cp[40] = cp[rx];
y--;
}
sp += win->cx;
cp += win->cx;
rx -= win->cx;
copy_bwd(sp + 1, sp, cp + 1, cp, rx);
sp[0] = ch;
cp[0] = color;
}
void cwin_insert_char(CharWin * win, char ch, char color)
{
cwin_insert_char_raw(win, p2s(ch), color);
}
void cwin_delete_char(CharWin * win)
{
char * sp = win->sp + mul40[win->cy];
char * cp = win->cp + mul40[win->cy];
char x = win->cx, rx = win->wx - 1;
copy_fwd(sp + x, sp + x + 1, cp + x, cp + x + 1, rx - x);
char y = win->cy + 1;
while (y < win->wy)
{
sp[rx] = sp[40];
cp[rx] = cp[40];
sp += 40;
cp += 40;
copy_fwd(sp, sp + 1, cp, cp + 1, rx);
y++;
}
sp[rx] = ' ';
}
int cwin_getch(void)
{
__asm
{
L1:
jsr 0xffe4
cmp #0
beq L1
sta accu
lda #0
sta accu + 1
}
}
int cwin_checkch(void)
{
__asm
{
L1:
jsr 0xffe4
sta accu
lda #0
sta accu + 1
}
}
bool cwin_edit_char(CharWin * win, char ch)
{
switch (ch)
{
case 13:
case 3:
return true;
case 19:
win->cx = 0;
win->cy = 0;
return false;
case 147:
cwin_clear(win);
win->cx = 0;
win->cy = 0;
return false;
case 17:
cwin_cursor_down(win);
return false;
case 145: // CRSR_UP
cwin_cursor_up(win);
return false;
case 29:
cwin_cursor_forward(win);
return false;
case 157:
cwin_cursor_backward(win);
return false;
case 20:
if (cwin_cursor_backward(win))
cwin_delete_char(win);
return false;
default:
if (ch >= 32 && ch < 128 || ch >= 160)
{
if (win->cy + 1 < win->wy || win->cx + 1 < win->wx)
{
cwin_insert_char(win, ch, 1);
cwin_cursor_forward(win);
}
}
return false;
}
}
char cwin_edit(CharWin * win)
{
for(;;)
{
cwin_cursor_show(win, true);
char ch = cwin_getch();
cwin_cursor_show(win, false);
if (cwin_edit_char(win, ch))
return ch;
}
}
void cwin_scroll_left(CharWin * win, char by)
{
char * sp = win->sp;
char * cp = win->cp;
char rx = win->wx - by;
for(char y=0; y<win->wy; y++)
{
copy_fwd(sp, sp + by, cp, cp + by, rx);
}
}
void cwin_scroll_right(CharWin * win, char by)
{
char * sp = win->sp;
char * cp = win->cp;
char rx = win->wx - by;
for(char y=0; y<win->wy; y++)
{
copy_bwd(sp + by, sp, cp + by, cp, rx);
sp += 40;
cp += 40;
}
}
void cwin_scroll_up(CharWin * win, char by)
{
char * sp = win->sp;
char * cp = win->cp;
char rx = win->wx;
int dst = mul40[by];
for(char y=0; y<win->wy - by; y++)
{
copy_fwd(sp, sp + dst, cp, cp + dst, rx);
sp += 40;
cp += 40;
}
}
void cwin_scroll_down(CharWin * win, char by)
{
char * sp = win->sp + mul40[win->wy];
char * cp = win->cp + mul40[win->wy];
char rx = win->wx;
int dst = mul40[by];
for(char y=0; y<win->wy - by; y++)
{
sp -= 40;
cp -= 40;
copy_bwd(sp, sp - dst, cp, cp - dst, rx);
}
}
void cwin_fill_rect_raw(CharWin * win, char x, char y, char w, char h, char ch, char color)
{
if (w > 0)
{
char * sp = win->sp + mul40[y] + x;
char * cp = win->cp + mul40[y] + x;
for(char y=0; y<h; y++)
{
fill_fwd(sp, cp, ch, color, w);
sp += 40;
cp += 40;
}
}
}
void cwin_fill_rect(CharWin * win, char x, char y, char w, char h, char ch, char color)
{
cwin_fill_rect_raw(win, x, y, w, h, p2s(ch), color);
}
void cwin_console_scroll_up(CharWin * win)
{
win->cy--;
win->ly--;
cwin_scroll_up(win, 1);
cwin_fill_rect(win, 0, win->wy - 1, win->wx, 1, ' ', 1);
}
void cwin_console_newline(CharWin * win)
{
win->cx = 0;
win->cy++;
if (win->cy == win->wy)
cwin_console_scroll_up(win);
}
void cwin_console_write_char(CharWin * win, char ch, char color)
{
if (win->cx == win->wx)
cwin_console_newline(win);
int offset = mul40[win->cy] + win->cx;
win->sp[offset] = p2s(ch);
win->cp[offset] = color;
win->cx++;
}
void cwin_console_write_string(CharWin * win, const char * chars, char color)
{
win->ly = win->cy;
win->lx = win->cx;
char i = 0;
while (char ch = chars[i])
{
if (ch == '\n')
cwin_console_newline(win);
else
cwin_console_write_char(win, ch, color);
i++;
}
}
void cwin_console_clear(CharWin * win)
{
cwin_fill_rect(win, win->lx, win->ly, win->wx - win->lx, 1, ' ', 1);
cwin_fill_rect(win, 0, win->ly + 1, win->wx, win->wy - win->ly - 1, ' ', 1);
}
bool cwin_console_cursor_left(CharWin * win)
{
if (win->cy == win->ly)
{
if (win->cx > win->lx)
{
win->cx--;
return true;
}
}
else if (win->cx > 0)
{
win->cx--;
return true;
}
else
{
win->cy--;
win->cx = win->wx - 1;
return true;
}
return false;
}
bool cwin_console_cursor_right(CharWin * win)
{
if (win->cx + 1 < win->wx)
{
win->cx++;
return true;
}
else if (win->cy + 1 < win->wy)
{
win->cy++;
win->cx = 0;
return true;
}
else if (win->ly > 0)
{
win->cx = 0;
win->cy++;
cwin_console_scroll_up(win);
return true;
}
return false;
}
void cwin_console_delete_char(CharWin * win)
{
cwin_delete_char(win);
}
bool cwin_console_insert_char(CharWin * win, char ch, char color)
{
if (win->sp[mul40[win->wy - 1] + win->wx - 1] != ' ')
{
if (win->ly == 0)
return false;
cwin_console_scroll_up(win);
}
cwin_insert_char(win, ch, color);
return true;
}
bool cwin_console_edit_char(CharWin * win, char ch, char color)
{
switch (ch)
{
case 13:
case 3:
case 17:
case 145: // CRSR_UP
return true;
case 19:
win->cx = win->lx;
win->cy = win->ly;
return false;
case 147:
cwin_console_clear(win);
win->cx = win->lx;;
win->cy = win->ly;
return false;
case 29:
cwin_console_cursor_right(win);
return false;
case 157:
cwin_console_cursor_left(win);
return false;
case 20:
if (cwin_console_cursor_left(win))
cwin_console_delete_char(win);
return false;
default:
if (ch >= 32 && ch < 128 || ch >= 160)
{
if (cwin_console_insert_char(win, ch, color))
cwin_console_cursor_right(win);
}
return false;
}
}
char cwin_console_edit_string(CharWin * win, char color)
{
for(;;)
{
cwin_cursor_show(win, true);
char ch = cwin_getch();
cwin_cursor_show(win, false);
if (cwin_console_edit_char(win, ch, color))
{
win->cx = win->lx;
win->cy = win->ly;
return ch;
}
}
}
void cwin_console_get_string(CharWin * win, char * chars, char size)
{
char i = 0;
char y = win->ly, x = win->lx;
char * cp = win->sp + mul40[y];
while (i < size)
{
chars[i++] = s2p(cp[x++]);
if (x == win->wx)
{
if (y + 1 == win->wy)
break;
x = 0;
cp += 40;
y++;
}
}
while (i > 0 && chars[i - 1] == ' ')
{
i--;
if (x == 0)
{
y--;
x = win->wx;
}
else
x--;
}
win->cx = x;
win->cy = y;
chars[i] = 0;
}
char * sformat(char * buff, const char * fmt, int * fps, bool print);
void cwin_console_printf(CharWin * win, char color, const char * fmt, ...)
{
char buff[200];
sformat(buff, fmt, (int *)&fmt + 1, false);
cwin_console_write_string(win, buff, color);
}
+211
View File
@@ -0,0 +1,211 @@
#ifndef C64_CHARWIN_H
#define C64_CHARWIN_H
struct CharWin
{
char sx, sy, wx, wy;
char cx, cy, lx, ly;
char * sp, * cp;
};
// Initialize the CharWin structure for the given screen and coordinates, does
// not clear the window
//
void cwin_init(CharWin * win, char * screen, char sx, char sy, char wx, char wy);
// Clear the window
//
void cwin_clear(CharWin * win);
// Fill the window with the given character and color
//
void cwin_fill(CharWin * win, char ch, char color);
// Show or hide the cursor by setting or clearing the MSB of the character code
//
void cwin_cursor_show(CharWin * win, bool show);
// Move the cursor to the given location
//
void cwin_cursor_move(CharWin * win, char cx, char cy);
// Move the cursor in the window, returns true if the cursor could be moved
//
bool cwin_cursor_left(CharWin * win);
bool cwin_cursor_right(CharWin * win);
bool cwin_cursor_up(CharWin * win);
bool cwin_cursor_down(CharWin * win);
bool cwin_cursor_forward(CharWin * win);
bool cwin_cursor_backward(CharWin * win);
bool cwin_cursor_newline(CharWin * win);
// Read the full window into a string
//
void cwin_read_string(CharWin * win, char * buffer);
// Write the fill window with the given string
//
void cwin_write_string(CharWin * win, const char * buffer);
// Put a single char at the cursor location and advance the cursor
//
void cwin_put_char(CharWin * win, char ch, char color);
// Put an array of chars at the cursor location and advance the cursor
//
void cwin_put_chars(CharWin * win, const char * chars, char num, char color);
// Put a zero terminated string at the cursor location and advance the cursor
//
char cwin_put_string(CharWin * win, const char * str, char color);
// Put a single raw char at the cursor location and advance the cursor
//
void cwin_put_char_raw(CharWin * win, char ch, char color);
// Put an array of raw chars at the cursor location and advance the cursor
//
void cwin_put_chars_raw(CharWin * win, const char * chars, char num, char color);
// Put a zero terminated raw string at the cursor location and advance the cursor
//
char cwin_put_string_raw(CharWin * win, const char * str, char color);
// Put a single char at the given window location
//
void cwin_putat_char(CharWin * win, char x, char y, char ch, char color);
// Put an array of chars at the given window location
//
void cwin_putat_chars(CharWin * win, char x, char y, const char * chars, char num, char color);
// Put a zero terminated string at the given window location
//
char cwin_putat_string(CharWin * win, char x, char y, const char * str, char color);
// Put a single raw char at the given window location
//
void cwin_putat_char_raw(CharWin * win, char x, char y, char ch, char color);
// Put an array of raw chars at the given window location
//
void cwin_putat_chars_raw(CharWin * win, char x, char y, const char * chars, char num, char color);
// Put a zero terminated string at the given window location
//
char cwin_putat_string_raw(CharWin * win, char x, char y, const char * str, char color);
// Get a single char at the given window location
//
char cwin_getat_char(CharWin * win, char x, char y);
// Get an array of chars at the given window location
//
void cwin_getat_chars(CharWin * win, char x, char y, char * chars, char num);
// Get a single char at the given window location
//
char cwin_getat_char_raw(CharWin * win, char x, char y);
// Get an array of chars at the given window location
//
void cwin_getat_chars_raw(CharWin * win, char x, char y, char * chars, char num);
// Put an array of characters into a rectangle in the char win
void cwin_put_rect_raw(CharWin * win, char x, char y, char w, char h, const char * chars, char color);
void cwin_put_rect(CharWin * win, char x, char y, char w, char h, const char * chars, char color);
// Get an array of characters from a rectangle of a char win
void cwin_get_rect_raw(CharWin * win, char x, char y, char w, char h, char * chars);
void cwin_get_rect(CharWin * win, char x, char y, char w, char h, char * chars);
// Insert one space character at the cursor position
//
void cwin_insert_char_raw(CharWin * win, char ch, char color);
void cwin_insert_char(CharWin * win, char ch, char color);
// Delete the character at the cursor position
//
void cwin_delete_char(CharWin * win);
int cwin_getch(void);
int cwin_checkch(void);
// Edit the window position using the char as the input
//
bool cwin_edit_char(CharWin * win, char ch);
// Edit the window using keyboard input, returns the key the exited
// the edit, either return or stop
//
char cwin_edit(CharWin * win);
// Scroll the window in the given direction, does not fill the new
// empty space
//
void cwin_scroll_left(CharWin * win, char by);
void cwin_scroll_right(CharWin * win, char by);
void cwin_scroll_up(CharWin * win, char by);
void cwin_scroll_down(CharWin * win, char by);
// Fill the given rectangle with the character and color
//
inline void cwin_fill_rect(CharWin * win, char x, char y, char w, char h, char ch, char color);
// Fill the given rectangle with the screen code and color
//
void cwin_fill_rect_raw(CharWin * win, char x, char y, char w, char h, char ch, char color);
void cwin_console_newline(CharWin * win);
void cwin_console_scroll_up(CharWin * win);
void cwin_console_write_char(CharWin * win, char ch, char color);
void cwin_console_write_string(CharWin * win, const char * chars, char color);
void cwin_console_clear(CharWin * win);
bool cwin_console_cursor_left(CharWin * win);
bool cwin_console_cursor_right(CharWin * win);
bool cwin_console_cursor_up(CharWin * win);
bool cwin_console_cursor_down(CharWin * win);
// Insert one space character at the cursor position
//
bool cwin_console_insert_char(CharWin * win, char ch, char color);
// Delete the character at the cursor position
//
void cwin_console_delete_char(CharWin * win);
bool cwin_console_edit_char(CharWin * win, char ch, char color);
char cwin_console_edit_string(CharWin * win, char color);
void cwin_console_get_string(CharWin * win, char * chars, char size);
void cwin_console_printf(CharWin * win, char color, const char * fmt, ...);
#pragma compile("charwin.c")
#endif
+26
View File
@@ -0,0 +1,26 @@
#include "cia.h"
byte ciaa_pra_def;
void cia_init(void)
{
cia1.icr = 0x7f;
cia2.icr = 0x7f;
cia1.pra = 0x7f;
cia1.cra = 0x08;
cia1.crb = 0x08;
cia2.cra = 0x08;
cia2.crb = 0x08;
cia1.ddrb = 0x00;
cia2.ddrb = 0x00;
cia1.ddra = 0xff;
cia2.pra = 0x07;
cia2.ddra = 0x3f;
char i0 = cia1.icr;
char i1 = cia2.icr;
ciaa_pra_def = 0x7f;
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef C64_CIA
#define C64_CIA
#include "types.h"
struct CIA
{
volatile byte pra, prb;
volatile byte ddra, ddrb;
volatile word ta, tb;
volatile byte todt, tods, todm, todh;
volatile byte sdr;
volatile byte icr;
volatile byte cra, crb;
};
#define cia1 (*((struct CIA *)0xdc00))
#define cia2 (*((struct CIA *)0xdd00))
extern byte ciaa_pra_def;
void cia_init(void);
#pragma compile("cia.c")
#endif
+63
View File
@@ -0,0 +1,63 @@
#ifndef C64_EASYFLASH_H
#define C64_EASYFLASH_H
#include "types.h"
struct EasyFlash
{
volatile __memmap byte bank;
byte pad1;
volatile byte control;
};
#define EFCTRL_GAME 0x01
#define EFCTRL_EXROM 0x02
#define EFCTRL_MODE 0x04
#define EFCTRL_LED 0x80
#define eflash (*(EasyFlash *)0xde00)
#ifdef __cplusplus
#ifdef EFPROX_SECTION
#pragma code(EFPROX_SECTION)
#endif
template<int back, class fn, class ... P>
__noinline auto ef_call_p(P... p)
{
if (back != __bankof(fn))
eflash.bank = __bankof(fn);
auto r = fn(p...);
if (back != 0xff && back != __bankof(fn))
eflash.bank = back;
return r;
}
#ifdef EFPROX_SECTION
#pragma code(code)
#endif
template<class fn>
class EFlashCall
{
public:
template<class ... P>
__forceinline auto operator()(P ... p) const
{
switch(__bankof(0))
{
#for(i,64) case i: return ef_call_p<i, fn, P...>(p...);
default:
return ef_call_p<0xff, fn, P...>(p...);
}
}
};
#define EF_CALL(fn) EFlashCall<fn##_p> fn
#endif
#endif
+608
View File
@@ -0,0 +1,608 @@
#include "flossiec.h"
#include <c64/iecbus.h>
#include <c64/vic.h>
#include <c64/cia.h>
#include <c64/kernalio.h>
#ifndef FLOSSIEC_NODISPLAY
#define FLOSSIEC_NODISPLAY 0
#endif
#ifndef FLOSSIEC_NOIRQ
#define FLOSSIEC_NOIRQ 0
#endif
#ifndef FLOSSIEC_BORDER
#define FLOSSIEC_BORDER 0
#endif
#define VIA_ATNIN 0x80
#define VIA_ATNOUT 0x10
#define VIA_CLKOUT 0x08
#define VIA_DATAOUT 0x02
#define VIA_CLKIN 0x04
#define VIA_DATAIN 0x01
#define PORTB1 0x1800
#define PORTB2 0x1c00
#define WR 0x1d
#ifdef FLOSSIEC_CODE
#pragma code(FLOSSIEC_CODE)
#endif
#ifdef FLOSSIEC_BSS
#pragma bss(FLOSSIEC_BSS)
#endif
__asm diskcode
{
nop
nop
lda #VIA_CLKOUT
sta PORTB1
lda 0x0202
sta 0x0c
lda 0x0203
sta 0x0d
lda #$80
sta 0x03
ldx #0
l0:
txa
lsr
lsr
lsr
lsr
sta 0x0700,x
inx
bne l0
lr:
lda 0x03
bmi lr
sei
ldx #0
l2:
lda #0
sta PORTB1
lda 0x0600, x
tay
and #0x0f
ora #VIA_DATAIN
l1:
bit PORTB1
bne l1
l3:
sta PORTB1
tya
asl
and #0x0a
sta PORTB1
lda 0x0700,y
nop
sta PORTB1
asl
nop
and #0x0a
sta PORTB1
inx
bne l2
lda #VIA_CLKOUT
sta PORTB1
lda 0x0600
beq w1
sta 0x0c
lda 0x0601
sta 0x0d
lda #$80
sta 0x03
cli
bne lr
w1:
sta PORTB1
cli
rts
}
#define CIA2B_ATNOUT 0x08
#define CIA2B_CLKOUT 0x10
#define CIA2B_DATAOUT 0x20
#define CIA2B_CLKIN 0x40
#define CIA2B_DATAIN 0x80
#define CIA2PRA 0xdd00
static char remap[256];
static char rbuffer[256];
static char xbuffer[256];
static char flpos;
static char xcmd;
static char xi, xj;
static char fldrive;
static char flvxor;
__noinline void fl_read_buf(void)
{
__asm
{
#if FLOSSIEC_NOIRQ
php
sei
#endif
lda CIA2PRA
and #~CIA2B_CLKOUT
sta accu
sta CIA2PRA
and #~CIA2B_DATAOUT
sta accu + 1
l0:
lda CIA2PRA
and #CIA2B_CLKIN
beq l0
#if !FLOSSIEC_NOIRQ
php
pla
and #$04
beq iq
#endif
ldy #0
sec
l1:
ldx accu + 1
#if !FLOSSIEC_NODISPLAY
l2:
lda 0xd012
sbc #50
bcc w1
and #7
beq l2
#endif
w1:
stx CIA2PRA
#if FLOSSIEC_BORDER
inc 0xd020
#else
nop
nop
nop
#endif
ldx accu
nop
lda CIA2PRA
lsr
lsr
nop
eor CIA2PRA
lsr
lsr
nop
eor CIA2PRA
lsr
lsr
sec
eor CIA2PRA
stx CIA2PRA
sta rbuffer, y
iny
bne l1
jmp done
#if !FLOSSIEC_NOIRQ
iq:
ldy #0
sec
l1i:
ldx accu + 1
l2i:
cli
sei
#if !FLOSSIEC_NODISPLAY
lda 0xd012
sbc #50
bcc w1i
and #7
beq l2i
w1i:
#endif
stx CIA2PRA
#if FLOSSIEC_BORDER
inc 0xd020
#else
nop
nop
nop
#endif
ldx accu
nop
lda CIA2PRA
lsr
lsr
nop
eor CIA2PRA
lsr
lsr
nop
eor CIA2PRA
lsr
lsr
sec
eor CIA2PRA
stx CIA2PRA
sta rbuffer, y
iny
bne l1i
cli
#endif
done:
#if FLOSSIEC_NOIRQ
plp
#endif
}
}
inline char flossiec_get(void)
{
if (!flpos)
{
fl_read_buf();
flpos = 2;
}
return remap[rbuffer[flpos++]];
}
void flossiec_decompress(void)
{
char i = 0, j = xj, cmd = xcmd;
xi = 0;
for(;;)
{
if (cmd & 0x80)
{
if (i < cmd)
{
char t = i - cmd;
do {
char ch = xbuffer[j++];
xbuffer[i++] = ch;
} while (i != t);
cmd = 0;
}
else
{
cmd -= i;
do {
char ch = xbuffer[j++];
xbuffer[i++] = ch;
} while (i);
break;
}
}
else
{
char ch = flossiec_get();
if (cmd)
{
xbuffer[i++] = ch;
cmd--;
if (!i)
break;
}
else
{
cmd = ch;
if (!cmd)
break;
if (cmd & 0x80)
{
cmd ^= 0x7f;
cmd++;
j = i - flossiec_get();
}
}
}
}
xj = j;
xcmd = cmd;
}
inline char flossiec_get_lzo(void)
{
if (!xi)
flossiec_decompress();
return xbuffer[xi++];
}
inline bool flossiec_eof(void)
{
return !remap[rbuffer[0]] && flpos >= remap[rbuffer[1]];
}
char * flossiec_read(char * dp, unsigned size)
{
while (size)
{
*dp++ = flossiec_get();
size--;
}
return dp;
}
char * flossiec_read_lzo(char * dp, unsigned size)
{
char i = xi;
dp -= i;
size += i;
while (size)
{
if (!i)
flossiec_decompress();
if (size >= 256)
{
do {
dp[i] = xbuffer[i];
i++;
} while (i);
dp += 256;
size -= 256;
}
else
{
do {
dp[i] = xbuffer[i];
i++;
} while (i != (char)size);
dp += i;
break;
}
}
xi = i;
return dp;
}
static void vxorcheck(void)
{
char vxor = cia2.pra & 7;
vxor ^= vxor >> 2;
vxor ^= 0xff;
if (vxor != flvxor)
{
flvxor = vxor;
for(int i=0; i<256; i++)
{
char j = i ^ vxor;
char d = ((j & 0x11) << 3) |
(j & 0x66) |
((j & 0x88) >> 3);
remap[i] = d;
}
}
}
bool flossiec_init(char drive)
{
fldrive = drive;
flvxor = 0;
iec_open(drive, 2, "#2");
iec_listen(drive, 2);
for(char j=0; j<127; j++)
iec_write(((char *)diskcode)[j]);
iec_unlisten();
iec_close(drive, 2);
iec_open(drive, 15, "");
return true;
}
void flossiec_shutdown(void)
{
iec_close(fldrive, 15);
}
bool flossiec_open(char track, char sector)
{
iec_listen(fldrive, 15);
iec_write(P'U');
iec_write(P'4');
iec_write(track);
iec_write(sector);
iec_unlisten();
cia2.pra |= CIA2B_DATAOUT;
#if FLOSSIEC_NODISPLAY
vic.ctrl1 &= ~VIC_CTRL1_DEN;
#endif
vic_waitFrame();
vxorcheck();
vic_waitFrame();
flpos = 0;
xi = 0;
return true;
}
void flossiec_close(void)
{
cia2.pra |= CIA2B_DATAOUT;
#if FLOSSIEC_NODISPLAY
vic.ctrl1 |= VIC_CTRL1_DEN;
#endif
}
bool flosskio_init(char drive)
{
fldrive = drive;
flvxor = 0;
krnio_setnam_n("#2", 2);
krnio_open(2, drive, 2);
krnio_write(2, (char *)diskcode, 128);
krnio_close(2);
krnio_setnam_n(nullptr, 0);
krnio_open(15, drive, 15);
return true;
}
void flosskio_shutdown(void)
{
krnio_close(15);
}
bool flosskio_open(char track, char sector)
{
krnio_chkout(15);
krnio_chrout(P'U');
krnio_chrout(P'4');
krnio_chrout(track);
krnio_chrout(sector);
krnio_clrchn();
cia2.pra |= CIA2B_DATAOUT;
#if FLOSSIEC_NODISPLAY
vic.ctrl1 &= ~VIC_CTRL1_DEN;
#endif
vic_waitFrame();
vxorcheck();
vic_waitFrame();
flpos = 0;
xi = 0;
return true;
}
void flosskio_close(void)
{
cia2.pra |= CIA2B_DATAOUT;
#if FLOSSIEC_NODISPLAY
vic.ctrl1 |= VIC_CTRL1_DEN;
#endif
}
static bool mapdir(const char * fnames, floss_blk * blks)
{
do {
fl_read_buf();
char si = 0;
do
{
if (remap[rbuffer[si + 2]] == 0x82)
{
char fname[17];
char j = 0;
while (j < 16 && remap[rbuffer[si + j + 5]] != 0xa0)
{
fname[j] = remap[rbuffer[si + j + 5]];
j++;
}
fname[j] = 0;
char sj = 0;
char k = 0;
while (fnames[sj])
{
j = 0;
while (fname[j] && fname[j] == fnames[sj])
{
j++;
sj++;
}
if (!fname[j] && (!fnames[sj] || fnames[sj] == ','))
{
__assume(k < 128);
blks[k].track = remap[rbuffer[si + 3]];
blks[k].sector = remap[rbuffer[si + 4]];
break;
}
while (fnames[sj] && fnames[sj++] != ',')
;
k++;
}
}
si += 32;
} while (si);
} while (remap[rbuffer[0]]);
return true;
}
bool flosskio_mapdir(const char * fnames, floss_blk * blks)
{
if (flosskio_open(18, 1))
{
mapdir(fnames, blks);
flosskio_close();
return true;
}
return false;
}
bool flossiec_mapdir(const char * fnames, floss_blk * blks)
{
if (flossiec_open(18, 1))
{
mapdir(fnames, blks);
flossiec_close();
return true;
}
return false;
}
+79
View File
@@ -0,0 +1,79 @@
#ifndef FLOSSIEC_H
#define FLOSSIEC_H
// When building you can use various defines to change the behaviour
// FLOSSIEC_BORDER=1 Enable border flashing while loading
// FLOSSIEC_NODISPLAY=1 Disable the display while loading
// FLOSSIEC_NOIRQ=1 Disable IRQ during load
// FLOSSIEC_CODE=cseg Code segment to be used, when defined
// FLOSSIEC_BSS=bseg BSS segment to be used, when defined
// Initialize the fastloader to be used without the kernal
bool flossiec_init(char drive);
// Shutdown the fastloader when used without the kernal
void flossiec_shutdown(void);
// Open a file for read with the fastloader without the kernal.
// The file has to be read to completion before you can close
// it again,
bool flossiec_open(char track, char sector);
// Close a file after reading
void flossiec_close(void);
// Initialize the fastloader to be used with the kernal
bool flosskio_init(char drive);
// Shutdown the fastloader when used with the kernal
void flosskio_shutdown(void);
// Open a file for read with the fastloader with the kernal
// The file has to be read to completion before you can close
// it again,
bool flosskio_open(char track, char sector);
// Close a file after reading
void flosskio_close(void);
// Track and sector start of a file
struct floss_blk
{
char track, sector;
};
// Map a comma separated list of filenames to an array of
// block start positions by reading the directory, using the
// kernal.
bool flosskio_mapdir(const char * fnames, floss_blk * blks);
// Map a comma separated list of filenames to an array of
// block start positions by reading the directory, without the
// kernal.
bool flossiec_mapdir(const char * fnames, floss_blk * blks);
// Check for end of file while reading
inline bool flossiec_eof(void);
// Get one char from uncompressed file
inline char flossiec_get(void);
// Get one char from compressed file
inline char flossiec_get_lzo(void);
// Read a section of a file into memory up to size bytes,
// returns the first address after the read
char * flossiec_read(char * dp, unsigned size);
// Read and expand section of a file into memory up to size
// bytes, returns the first address after the read
char * flossiec_read_lzo(char * dp, unsigned size);
#pragma compile("flossiec.c")
#endif
+353
View File
@@ -0,0 +1,353 @@
#include "iecbus.h"
#include <c64/cia.h>
#include <c64/vic.h>
IEC_STATUS iec_status;
char iec_queue;
#define CIA2B_ATNOUT 0x08
#define CIA2B_CLKOUT 0x10
#define CIA2B_DATAOUT 0x20
#define CIA2B_CLKIN 0x40
#define CIA2B_DATAIN 0x80
#pragma optimize(push)
#pragma optimize(1)
// multiples of 5us
static void delay(char n)
{
__asm {
ldx n
l1:
dex
bne l1
}
}
static inline void data_true(void)
{
cia2.pra &= ~CIA2B_DATAOUT;
}
static inline void data_false(void)
{
cia2.pra |= CIA2B_DATAOUT;
}
static inline void clock_true(void)
{
cia2.pra &= ~CIA2B_CLKOUT;
}
static inline void cdata_true(void)
{
cia2.pra &= ~(CIA2B_CLKOUT | CIA2B_DATAOUT);
}
static inline void clock_false(void)
{
cia2.pra |= CIA2B_CLKOUT;
}
static inline void atn_true(void)
{
cia2.pra &= ~CIA2B_ATNOUT;
}
static inline void atn_false(void)
{
cia2.pra |= CIA2B_ATNOUT;
}
static inline bool data_in(void)
{
return (cia2.pra & CIA2B_DATAIN) != 0;
}
static inline bool clock_in(void)
{
return (cia2.pra & CIA2B_CLKIN) != 0;
}
static bool data_check(void)
{
char cnt = 200;
while (cnt > 0 && data_in())
{
delay(5);
cnt--;
}
if (cnt)
return true;
else
{
iec_status = IEC_DATA_CHECK;
return false;
}
}
static bool iec_eoib(void)
{
clock_true();
while (!data_in());
delay(40);
return data_check();
}
static void iec_writeb(char b)
{
clock_true();
while (!data_in());
delay(5);
for(char i=0; i<8; i++)
{
clock_false();
delay(5);
if (b & 1)
data_true();
else
data_false();
clock_true();
b >>= 1;
delay(5);
}
clock_false();
data_true();
}
bool iec_write(char b)
{
if (iec_status == IEC_QUEUED)
{
__asm
{
php
sei
}
iec_status = IEC_OK;
iec_writeb(iec_queue);
__asm
{
plp
}
data_check();
}
if (iec_status < IEC_ERROR)
{
iec_queue = b;
iec_status = IEC_QUEUED;
return true;
}
return false;
}
char iec_read(void)
{
while (!clock_in());
__asm
{
php
sei
}
data_true();
char cnt = 100;
while (cnt > 0 && clock_in())
cnt--;
if (cnt == 0)
{
iec_status = IEC_EOF;
data_false();
delay(10);
data_true();
cnt = 200;
while (cnt > 0 && clock_in())
cnt--;
if (cnt == 0)
{
iec_status = IEC_TIMEOUT;
__asm
{
plp
}
return 0;
}
}
char b = 0;
for(char i=0; i<8; i++)
{
char c;
while (!((c = cia2.pra) & CIA2B_CLKIN))
;
b >>= 1;
b |= c & 0x80;
while (cia2.pra & CIA2B_CLKIN)
;
}
data_false();
__asm
{
plp
}
return b;
}
void iec_atn(char dev, char sec)
{
clock_true();
data_true();
atn_false();
clock_false();
delay(200);
while (data_in());
iec_writeb(dev);
data_check();
if (sec != 0xff)
{
iec_writeb(sec);
data_check();
}
atn_true();
}
void iec_talk(char dev, char sec)
{
iec_status = IEC_OK;
iec_atn(dev | 0x40, sec | 0x60);
data_false();
__asm
{
php
sei
}
clock_true();
char cnt = 200;
while (cnt > 0 && clock_in())
cnt--;
__asm
{
plp
}
delay(10);
}
void iec_untalk(void)
{
iec_atn(0x5f, 0xff);
}
void iec_listen(char dev, char sec)
{
iec_status = IEC_OK;
iec_atn(dev | 0x20, sec | 0x60);
}
void iec_unlisten(void)
{
__asm
{
php
sei
}
if (iec_status == IEC_QUEUED)
{
iec_status = IEC_OK;
iec_eoib();
iec_writeb(iec_queue);
data_check();
}
iec_atn(0x3f, 0xff);
clock_true();
__asm
{
plp
}
}
void iec_open(char dev, char sec, const char * fname)
{
iec_status = IEC_OK;
iec_atn(dev | 0x20, sec | 0xf0);
char i = 0;
while (fname[i])
{
iec_write(fname[i]);
i++;
}
iec_unlisten();
}
void iec_close(char dev, char sec)
{
iec_atn(dev | 0x20, sec | 0xe0);
iec_unlisten();
}
int iec_write_bytes(const char * data, int num)
{
for(int i=0; i<num; i++)
{
if (!iec_write(data[i]))
return i;
}
return num;
}
int iec_read_bytes(char * data, int num)
{
int i = 0;
while (i < num)
{
char ch = iec_read();
if (iec_status < IEC_ERROR)
data[i++] = ch;
if (iec_status != IEC_OK)
return i;
}
return num;
}
#pragma optimize(pop)
+43
View File
@@ -0,0 +1,43 @@
#ifndef C64_IECBUS_H
#define C64_IECBUS_H
enum IEC_STATUS
{
IEC_OK = 0x00,
IEC_EOF = 0x01,
IEC_QUEUED = 0x02,
IEC_ERROR = 0x80,
IEC_TIMEOUT,
IEC_DATA_CHECK,
};
extern IEC_STATUS iec_status;
bool iec_write(char b);
char iec_read(void);
void iec_atn(char dev, char sec);
void iec_talk(char dev, char sec);
void iec_untalk(void);
void iec_listen(char dev, char sec);
void iec_unlisten(void);
void iec_open(char dev, char sec, const char * fname);
void iec_close(char dev, char sec);
int iec_write_bytes(const char * data, int num);
int iec_read_bytes(char * data, int num);
#pragma compile("iecbus.c")
#endif
+25
View File
@@ -0,0 +1,25 @@
#include "joystick.h"
sbyte joyx[2], joyy[2];
bool joyb[2];
void joy_poll(char n)
{
char b = ((volatile char *)0xdc00)[n];
if (!(b & 1))
joyy[n] = -1;
else if (!(b & 2))
joyy[n] = 1;
else
joyy[n] = 0;
if (!(b & 4))
joyx[n] = -1;
else if (!(b & 8))
joyx[n] = 1;
else
joyx[n] = 0;
joyb[n] = (b & 0x10) == 0;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef C64_JOYSTICK_H
#define C64_JOYSTICK_H
#include "types.h"
extern sbyte joyx[2], joyy[2];
extern bool joyb[2];
// poll joystick input for joystick 0 or 1 and place
// the x/y direction and the button status into the joyx/y/b
// arrays for
void joy_poll(char n);
#pragma compile("joystick.c")
#endif
+541
View File
@@ -0,0 +1,541 @@
#include "kernalio.h"
krnioerr krnio_pstatus[16];
#if defined(__C128__) || defined(__C128B__) || defined(__C128E__)
void krnio_setbnk(char filebank, char namebank)
{
__asm
{
lda filebank
ldx namebank
jsr $ff68 // setbnk
}
}
#pragma native(krnio_setbnk)
#endif
#if defined(__PLUS4__)
#pragma code(lowcode)
#define BANKIN sta 0xff3e
#define BANKOUT sta 0xff3f
#define BANKINLINE __noinline
#else
#define BANKIN
#define BANKOUT
#define BANKINLINE
#endif
#if defined(__CBMPET__)
#define FNLEN 0xD1 // Length of filename
#define LFN 0xD2 // Current Logical File Number
#define SECADR 0xD3 // Secondary address
#define DEVNUM 0xD4 // Device number
#define FNADR 0xDA // Pointer to file name
#define ST 0x96 // IEC status byte
// PET ROM Detection
#define PET_DETECT 0xFFFB // Distinction V2 vs V4 BASIC
#define PET_2000 0xCA
#define PET_3000 0xFC
#define PET_4000 0xFD
__asm k_checkst
{
lda ST
beq l1
lda #5 // Device not present
sec
rts
l1:
clc
rts
}
__asm k_setlfs
{
sta LFN // setlfs replacement
stx DEVNUM
sty SECADR
rts
}
__asm k_open
{
lda PET_DETECT
cmp #PET_4000
bne V2
jsr $f563
jmp k_checkst
V2:
jsr $f524
jmp k_checkst
}
__asm k_close
{
ldx PET_DETECT
cpx #PET_4000
bne l1
jmp $F2E2 // BASIC 4
l1:
jmp $F2AE //BASIC 2&3
}
#endif
BANKINLINE void krnio_setnam(const char * name)
{
__asm
{
lda name
ora name + 1
beq W1
ldy #$ff
L1: iny
lda (name), y
bne L1
tya
W1: ldx name
ldy name + 1
BANKIN
#if defined(__CBMPET__)
sta FNLEN
stx FNADR
sty FNADR+1
#else
jsr $ffbd // setnam
#endif
BANKOUT
}
}
#pragma native(krnio_setnam)
BANKINLINE void krnio_setnam_n(const char * name, char len)
{
__asm
{
lda len
ldx name
ldy name + 1
BANKIN
#if defined(__CBMPET__)
sta FNLEN
stx FNADR
sty FNADR+1
#else
jsr $ffbd // setnam
#endif
BANKOUT
}
}
#pragma native(krnio_setnam_n)
BANKINLINE bool krnio_open(char fnum, char device, char channel)
{
krnio_pstatus[fnum] = KRNIO_OK;
return char(__asm
{
lda #0
sta accu
sta accu + 1
BANKIN
lda fnum
ldx device
ldy channel
#if defined(__CBMPET__)
jsr k_setlfs
jsr k_open
#else
jsr $ffba // setlfs
jsr $ffc0 // open
#endif
bcc W1
lda fnum
#if defined(__CBMPET__)
jsr k_close
#else
jsr $ffc3 // close
#endif
jmp E2
W1:
lda #1
sta accu
BANKOUT
E2:
});
}
#pragma native(krnio_open)
BANKINLINE void krnio_close(char fnum)
{
__asm
{
BANKIN
lda fnum
#if defined(__CBMPET__)
jsr k_close
#else
jsr $ffc3 // close
#endif
BANKOUT
}
}
#pragma native(krnio_close)
BANKINLINE krnioerr krnio_status(void)
{
return __asm
{
#if defined(__CBMPET__)
lda ST
#else
BANKIN
jsr $ffb7 : ->a // readst
BANKOUT
#endif
sta accu
lda #0
sta accu + 1
};
}
#pragma native(krnio_status)
BANKINLINE bool krnio_load(char fnum, char device, char channel)
{
return char(__asm
{
BANKIN
lda fnum
ldx device
ldy channel
jsr $ffba // setlfs
lda #0
ldx #0
ldy #0
jsr $FFD5 // load
BANKOUT
lda #0
rol
eor #1
sta accu
});
}
#pragma native(krnio_load)
BANKINLINE bool krnio_save(char device, const char* start, const char* end)
{
return char(__asm
{
BANKIN
lda #0
ldx device
ldy #0
jsr $ffba // setlfs
lda #start
ldx end
ldy end+1
jsr $FFD8 // save
BANKOUT
lda #0
rol
eor #1
sta accu
});
}
#pragma native(krnio_save)
BANKINLINE bool krnio_chkout(char fnum)
{
return char(__asm
{
BANKIN
ldx fnum
jsr $ffc9 : x->ax // chkout
#if defined(__CBMPET__)
jsr k_checkst
#endif
BANKOUT
lda #0
rol
eor #1
sta accu
});
}
#pragma native(krnio_chkout)
BANKINLINE bool krnio_chkin(char fnum)
{
return char(__asm
{
BANKIN
ldx fnum
jsr $ffc6 : x->axy // chkin
#if defined(__CBMPET__)
jsr k_checkst
#endif
BANKOUT
lda #0
rol
eor #1
sta accu
});
}
#pragma native(krnio_chkin)
BANKINLINE void krnio_clrchn(void)
{
__asm
{
BANKIN
jsr $ffcc : ->ax // clrchn
BANKOUT
}
}
#pragma native(krnio_clrchn)
BANKINLINE bool krnio_chrout(char ch)
{
return char(__asm
{
BANKIN
lda ch
jsr $ffd2 : a->a // chrout
sta accu
BANKOUT
});
}
#pragma native(krnio_chrout)
BANKINLINE char krnio_chrin(void)
{
return __asm
{
BANKIN
jsr $ffcf : a->a // chrin
sta accu
BANKOUT
};
}
#pragma native(krnio_chrin)
#if defined(__PLUS4__)
#pragma code(code)
#endif
int krnio_getch(char fnum)
{
if (krnio_pstatus[fnum] == KRNIO_EOF)
return -1;
int ch = -1;
if (krnio_chkin(fnum))
{
ch = krnio_chrin();
krnioerr err = krnio_status();
krnio_pstatus[fnum] = err;
if (err)
{
if (err == KRNIO_EOF)
ch |= 0x100;
else
ch = -1;
}
}
krnio_clrchn();
return ch;
}
int krnio_putch(char fnum, char ch)
{
if (krnio_chkout(fnum))
{
krnio_chrout(ch);
krnio_clrchn();
return 0;
}
else
return -1;
}
int krnio_puts(char fnum, const char * data)
{
if (krnio_chkout(fnum))
{
int i = 0;
while (data[i])
krnio_chrout(data[i++]);
krnio_clrchn();
return i;
}
else
return -1;
}
#pragma native(krnio_puts)
int krnio_write(char fnum, const char * data, int num)
{
if (krnio_chkout(fnum))
{
for(int i=0; i<num; i++)
krnio_chrout(data[i]);
krnio_clrchn();
return num;
}
else
return -1;
}
#pragma native(krnio_write)
int krnio_read(char fnum, char * data, int num)
{
if (krnio_pstatus[fnum] == KRNIO_EOF)
return 0;
if (krnio_chkin(fnum))
{
int i = 0;
int ch;
while (i < num)
{
ch = krnio_chrin();
krnioerr err = krnio_status();
krnio_pstatus[fnum] = err;
if (err && err != KRNIO_EOF)
break;
data[i++] = (char)ch;
if (err)
break;
}
krnio_clrchn();
return i;
}
else
return -1;
}
#pragma native(krnio_read)
int krnio_read_lzo(char fnum, char * data)
{
if (krnio_pstatus[fnum] == KRNIO_EOF)
return 0;
if (krnio_chkin(fnum))
{
int i = 0;
char ch;
char cmd = 0;
krnioerr err;
for(;;)
{
ch = krnio_chrin();
err = krnio_status();
if (err && err != KRNIO_EOF)
break;
if (cmd & 0x80)
{
char * dp = data + i, * cp = dp - ch;
cmd &= 0x7f;
i += cmd;
char n = 0x00;
do {
dp[n] = cp[n];
n++;
} while (n != cmd);
cmd = 0;
}
else if (cmd)
{
data[i++] = (char)ch;
cmd--;
}
else if (ch)
cmd = ch;
else
break;
if (err)
break;
}
krnio_pstatus[fnum] = err;
krnio_clrchn();
return i;
}
else
return -1;
}
#pragma native(krnio_read_lzo)
int krnio_gets(char fnum, char * data, int num)
{
if (krnio_pstatus[fnum] == KRNIO_EOF)
return 0;
if (krnio_chkin(fnum))
{
krnioerr err = KRNIO_OK;
int i = 0;
int ch;
while (i + 1 < num)
{
ch = krnio_chrin();
err = krnio_status();
if (err && err != KRNIO_EOF)
break;
data[i++] = (char)ch;
if (ch == 13 || ch == 10 || err)
break;
}
krnio_pstatus[fnum] = err;
data[i] = 0;
krnio_clrchn();
return i;
}
else
return -1;
}
#pragma native(krnio_gets)
+102
View File
@@ -0,0 +1,102 @@
#ifndef C64_KERNALIO_H
#define C64_KERNALIO_H
// Error and status codes returned by krnio_status
enum krnioerr
{
KRNIO_OK = 0,
KRNIO_DIR = 0x01,
KRNIO_TIMEOUT = 0x02,
KRNIO_SHORT = 0x04,
KRNIO_LONG = 0x08,
KRNIO_VERIFY = 0x10,
KRNIO_CHKSUM = 0x20,
KRNIO_EOF = 0x40,
KRNIO_NODEVICE = 0x80
};
extern krnioerr krnio_pstatus[16];
#if defined(__C128__) || defined(__C128B__) || defined(__C128E__)
// C128: Set bank for load/save and filename for next file operations
void krnio_setbnk(char filebank, char namebank);
#endif
// Set filename for next krnio_open operation, make sure
// that the string is still valid when calling krnio_open
void krnio_setnam(const char * name);
void krnio_setnam_n(const char * name, char len);
// open a kernal file/stream/io channel, returns true on success
bool krnio_open(char fnum, char device, char channel);
// close a kernal file/stream/io channel
void krnio_close(char fnum);
// get the error / status of the last io operation
krnioerr krnio_status(void);
bool krnio_load(char fnum, char device, char channel);
bool krnio_save(char device, const char* start, const char* end);
// select the given file for stream output
bool krnio_chkout(char fnum);
// select the given file for stream input
bool krnio_chkin(char fnum);
// clear input and output file selection
void krnio_clrchn(void);
// write a single byte to the current output channel
bool krnio_chrout(char ch);
// read a single byte from the current input channel
char krnio_chrin(void);
// read a single byte from the given file/channel, returns
// a negative result on failure. If this was the last byte
// the bit #8 (0x0100) will be set in the return value
int krnio_getch(char fnum);
// write a single byte to the given file/channel, returns
// a negative value on failure.
int krnio_putch(char fnum, char ch);
// write an array of bytes to the given file/channel
int krnio_write(char fnum, const char * data, int num);
// write a zero terminated string to the given file/channel
int krnio_puts(char fnum, const char * data);
// read an array of bytes from the given file, returns the number
// of bytes read, or a negative number on failure
int krnio_read(char fnum, char * data, int num);
int krnio_read_lzo(char fnum, char * data);
// read a line from the given file, terminated by a CR or LF character
// and appends a zero byte.
int krnio_gets(char fnum, char * data, int num);
#pragma compile("kernalio.c")
#endif
+100
View File
@@ -0,0 +1,100 @@
#include "keyboard.h"
#include "cia.h"
const char keyb_codes[128] = {
KEY_DEL, KEY_RETURN, KEY_CSR_RIGHT, KEY_F7, KEY_F1, KEY_F3, KEY_F5, KEY_CSR_DOWN,
'3', 'w', 'a', '4', 'z', 's', 'e', 0,
'5', 'r', 'd', '6', 'c', 'f', 't', 'x',
'7', 'y', 'g', '8', 'b', 'h', 'u', 'v',
'9', 'i', 'j', '0', 'm', 'k', 'o', 'n',
'+', 'p', 'l', '-', '.', ':', '@', ',',
0 , '*', ';', KEY_HOME, 0, '=', '^', '/',
'1', KEY_ARROW_LEFT, 0, '2', ' ', 0, 'q', KEY_ESC,
KEY_INST, KEY_RETURN, KEY_CSR_LEFT, KEY_F8, KEY_F2, KEY_F4, KEY_F6, KEY_CSR_UP,
'#', 'W', 'A', '$', 'Z', 'S', 'E', 0,
'%', 'R', 'D', '&', 'C', 'F', 'T', 'X',
'\'', 'Y', 'G', '(', 'B', 'H', 'U', 'V',
')', 'I', 'J', '0', 'M', 'K', 'O', 'N',
0, 'P', 'L', 0, '>', '[', '@', '<',
0, 0, ']', KEY_CLR, 0, 0, '^', '?',
'!', 0, 0, '"', ' ', 0, 'Q', KEY_ESC,
};
byte keyb_matrix[8];
KeyScanCode keyb_key;
static byte keyb_pmatrix[8];
bool key_pressed(KeyScanCode code)
{
return !(keyb_matrix[code >> 3] & (1 << (code & 7)));
}
bool key_shift(void)
{
return
!(keyb_matrix[6] & 0x10) ||
!(keyb_matrix[1] & 0x80);
}
void keyb_poll(void)
{
cia1.ddra = 0xff;
cia1.pra = 0xff;
keyb_key = 0x00;
if (cia1.prb == 0xff)
{
cia1.ddrb = 0x00;
cia1.pra = 0x00;
if (cia1.prb != 0xff)
{
keyb_matrix[6] &= 0xef;
keyb_matrix[1] &= 0x7f;
byte a = 0xfe;
for(byte i=0; i<8; i++)
{
cia1.pra = a;
a = (a << 1) | 1;
byte p = keyb_matrix[i];
byte k = cia1.prb;
keyb_matrix[i] = k;
k = (k ^ 0xff) & p;
if (k)
{
byte j = 8 * i | 0x80;
if (k & 0xf0)
j += 4;
if (k & 0xcc)
j += 2;
if (k & 0xaa)
j++;
keyb_key = j;
}
}
if (keyb_key && (!(keyb_matrix[1] & 0x80) || (!(keyb_matrix[6] & 0x10))))
keyb_key |= 0x40;
}
else
{
keyb_matrix[0] = 0xff;
keyb_matrix[1] = 0xff;
keyb_matrix[2] = 0xff;
keyb_matrix[3] = 0xff;
keyb_matrix[4] = 0xff;
keyb_matrix[5] = 0xff;
keyb_matrix[6] = 0xff;
keyb_matrix[7] = 0xff;
}
}
cia1.pra = ciaa_pra_def;
}
+134
View File
@@ -0,0 +1,134 @@
#ifndef C64_KEYBOARD_H
#define C64_KEYBOARD_H
#include "types.h"
#define KEY_CSR_DOWN (17)
#define KEY_CSR_RIGHT (29)
#define KEY_CSR_UP (17 + 128)
#define KEY_CSR_LEFT (29 + 128)
#define KEY_ARROW_LEFT (95)
#define KEY_ESC (27)
#define KEY_DEL (20)
#define KEY_INST (148)
#define KEY_RETURN (13)
#define KEY_HOME (19)
#define KEY_CLR (147)
#define KEY_F1 (133)
#define KEY_F3 (134)
#define KEY_F5 (135)
#define KEY_F7 (136)
#define KEY_F2 (137)
#define KEY_F4 (138)
#define KEY_F6 (139)
#define KEY_F8 (140)
enum KeyScanCode
{
KSCAN_DEL,
KSCAN_RETURN,
KSCAN_CSR_RIGHT,
KSCAN_F7,
KSCAN_F1,
KSCAN_F3,
KSCAN_F5,
KSCAN_CSR_DOWN,
KSCAN_3,
KSCAN_W,
KSCAN_A,
KSCAN_4,
KSCAN_Z,
KSCAN_S,
KSCAN_E,
KSCAN_SHIFT_LOCK,
KSCAN_5,
KSCAN_R,
KSCAN_D,
KSCAN_6,
KSCAN_C,
KSCAN_F,
KSCAN_T,
KSCAN_X,
KSCAN_7,
KSCAN_Y,
KSCAN_G,
KSCAN_8,
KSCAN_B,
KSCAN_H,
KSCAN_U,
KSCAN_V,
KSCAN_9,
KSCAN_I,
KSCAN_J,
KSCAN_0,
KSCAN_M,
KSCAN_K,
KSCAN_O,
KSCAN_N,
KSCAN_PLUS,
KSCAN_P,
KSCAN_L,
KSCAN_MINUS,
KSCAN_DOT,
KSCAN_COLON,
KSCAN_AT,
KSCAN_COMMA,
KSCAN_POUND,
KSCAN_STAR,
KSCAN_SEMICOLON,
KSCAN_HOME,
KSCAN_RSHIFT,
KSCAN_EQUAL,
KSCAN_ARROW_UP,
KSCAN_SLASH,
KSCAN_1,
KSCAN_ARROW_LEFT,
KSCAN_CONTROL,
KSCAN_2,
KSCAN_SPACE,
KSCAN_COMMODORE,
KSCAN_Q,
KSCAN_STOP,
KSCAN_QUAL_SHIFT = 0x40,
KSCAN_QUAL_MASK = 0x7f,
KSCAN_QUAL_DOWN = 0x80,
KSCAN_MAX = 0xff
};
// map of keyboard codes to PETSCII, first 64 without shift
// second 64 with shift
extern const char keyb_codes[128];
// current status of key matrix
extern byte keyb_matrix[8];
// current key in scan code - the top level bit KSCAN_QUAL_DOWN is
// used to indicate a key is pressed, so 0 is no key
extern KeyScanCode keyb_key;
// poll keyboard matrix
void keyb_poll(void);
inline bool key_pressed(KeyScanCode code);
inline bool key_shift(void);
#pragma compile("keyboard.c")
#endif
+82
View File
@@ -0,0 +1,82 @@
// This code is meant to keep the C64 running while the program it is part of banks
// out the BASIC or Kernal ROM to have RAM available. If an IRQ or NMI occurs during
// that time and a ROM routine is called that isn't there, the C64 will crash.
// This code replaces the IRQ and NMI handling code to bank the BASIC and Kernal ROMs
// back in, call the original handling routines, and then restore the situation as it
// was when the interrupt happened.
#include "memmap.h"
__asm DoneTrampoline
{
stx $01 // The ROM code at jmp ($fffa) has saved our X value
// so we can restore it to $01. Our banks our back to whatever it was
pla // now we pull X and A and restore them
tax
pla
rti // RTI can now pull the original status byte and return address and
// return to the original code.
}
__asm IRQTrampoline
{
pha
txa
pha
lda #>DoneTrampoline
pha
lda #<DoneTrampoline
pha
tsx
lda $0105, x
pha
ldx $01
lda #$36
sta $01
jmp ($fffe)
}
__asm NMITrampoline
{
// NMI just happend, so stack contains ($01ff for example):
// $01ff High byte of return address
// $01fe Low byte of return address
// $01fd status flag byte
pha // $01fc save A on the stack
txa
pha // $01fb save X on the stack
lda #>DoneTrampoline // $01fa save the high byte of DoneTrampoline()
pha
lda #<DoneTrampoline // $01f9 save the low byte of DoneTrampoline()
pha
tsx // transfer the SP ($f8) to X
lda $0105, x // $0105 + $f8 = $01fd (we have virtually shifted the end of stack
// to $0105 to get to the original status flag)
pha // and we push it again
ldx $01 // Now we save the current $01 value so we can restore it later
lda #$36 // set $01 to its default value (bank ROMs back in)
sta $01
jmp ($fffa) // call the original handler (we are looking at ROM now, not RAM)
// this routine saves A, X and Y and ends in an RTI
// that will pop SP and the DoneTrampoline() address and jump to it
}
void mmap_trampoline(void)
{
// This is to set the IRQ and NMI handler hooks to our own code.
// But note, that his is written to and saved in RAM under ROM at $fffa/$fffb and $fffe/$ffff
*((void **)0xfffa) = NMITrampoline;
*((void **)0xfffe) = IRQTrampoline;
}
#pragma native(mmap_trampoline)
char mmap_set(char pla)
{
char ppla = *((char *)0x01);
*((volatile __memmap char *)0x01) = pla;
return ppla;
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef MEMMAP_H
#define MEMMAP_H
#include "types.h"
// MMAP_ROM : BASIC + I/O + KERNAL -> default power on config
// MMAP_NO_BASIC : I/O + KERNAL -> easy extra chunk of contiguous RAM
// MMAP_NO_ROM : I/O -> I/O and COLOR RAM in $d000-$dfff block, rest is RAM
// MMAP_RAM : -> ALL RAM, you'll need to manage some state switching...
// MMAP_CHAR_ROM : CHAR -> no BASIC or KERNAL or I/O, but can copy CHAR ROM
// MMAP_ALL_ROM : BASIC + CHAR + KERNAL -> All ROM functions available, but no I/O
#define MMAP_ROM 0x37
#define MMAP_NO_BASIC 0x36
#define MMAP_NO_ROM 0x35
#define MMAP_RAM 0x30
#define MMAP_CHAR_ROM 0x31
#define MMAP_ALL_ROM 0x33
// Install an IRQ an NMI trampoline, that routes the kernal interrupts
// through an intermediate trampoline when the kernal ROM is not paged
// in. The trampoline enables the ROM, executes the interrupt and
// restores the memory map setting before returning.
void mmap_trampoline(void);
// Set the memory map in a way that is compatible with the IRQ
// trampoline, returns the previous state
inline char mmap_set(char pla);
#pragma compile("memmap.c")
#endif
+52
View File
@@ -0,0 +1,52 @@
#include "mouse.h"
#include "sid.h"
#include "cia.h"
sbyte mouse_dx, mouse_dy;
bool mouse_lb, mouse_rb;
static char mouse_px, mouse_py;
static char mouse_port;
inline signed char dpos(char * old, char mnew)
{
mnew = (mnew & 0x7f) >> 1;
char diff = (mnew - *old) & 0x3f;
if (diff >= 0x20)
{
*old = mnew;
return diff | 0xe0;
}
else if (diff)
{
*old = mnew;
return diff;
}
return 0;
}
void mouse_poll(void)
{
char b = ((volatile char *)0xdc00)[mouse_port];
mouse_rb = (b & 0x01) == 0;
mouse_lb = (b & 0x10) == 0;
char x = sid.potx, y = sid.poty;
mouse_dx = dpos(&mouse_px, x);
mouse_dy = dpos(&mouse_py, y);
}
void mouse_arm(char n)
{
mouse_port = n;
cia1.pra = ciaa_pra_def = n ? 0x7f : 0xbf;
}
void mouse_init(void)
{
mouse_arm(1);
mouse_poll();
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef C64_MOUSE_H
#define C64_MOUSE_H
#include "types.h"
extern sbyte mouse_dx, mouse_dy;
extern bool mouse_lb, mouse_rb;
void mouse_init(void);
// arm the potentiometer input for the selected mouse input
// needs ~4ms to stabilize
void mouse_arm(char n);
// poll mouse input for selected mouse, but the relative
// movement into mouse_dx/dy and the button state into
// mouse_lb/mouse_rb
void mouse_poll(void);
#pragma compile("mouse.c")
#endif
+716
View File
@@ -0,0 +1,716 @@
#include "rasterirq.h"
#include <c64/vic.h>
#include <c64/cia.h>
#include <c64/asm6502.h>
#include <stdlib.h>
volatile byte rirq_count;
static byte rirq_pcount;
byte rasterIRQRows[NUM_IRQS + 1];
byte rasterIRQIndex[NUM_IRQS + 1]; // Sort order of interrupt index, offset by one
#ifdef ZPAGE_IRQS
__zeropage
#endif
byte rasterIRQNext[NUM_IRQS + 1]; // Rasterline of interrupt, terminated by 0xff
byte rasterIRQLow[NUM_IRQS]; // Address of interrupt code
byte rasterIRQHigh[NUM_IRQS];
#ifdef ZPAGE_IRQS
__zeropage
#endif
volatile byte nextIRQ;
// nextIRQ is the index of the next expected IRQ, or $ff if no IRQ is scheduled
__asm rirq_isr_ram_io
{
stx plrx + 1
ldx nextIRQ
bmi exi
sta plra + 1
sty plry + 1
l1:
lda rasterIRQNext, x
ldy rasterIRQIndex + 1, x
ldx rasterIRQLow, y
stx ji + 1
ldx rasterIRQHigh, y
stx ji + 2
ji:
jsr $0000
inc nextIRQ
ldx nextIRQ
ldy rasterIRQNext, x
asl $d019
cpy #$ff
beq e2
dey
sty $d012
dey
cpy $d012
bcc l1
plry:
ldy #0
plra:
lda #0
plrx:
ldx #0
rti
exi:
asl $d019
jmp plrx
// No more interrupts to service
e2:
inc rirq_count
ldy rasterIRQNext
dey
sty $d012
ldx #0
stx nextIRQ
beq plry
}
__asm rirq_isr_io
{
pha
txa
pha
tya
pha
kentry:
ldx nextIRQ
bmi exi
l1:
lda rasterIRQNext, x
ldy rasterIRQIndex + 1, x
ldx rasterIRQLow, y
stx ji + 1
ldx rasterIRQHigh, y
stx ji + 2
ji:
jsr $0000
inc nextIRQ
ldx nextIRQ
ldy rasterIRQNext, x
asl $d019
cpy #$ff
beq e2
dey
sty $d012
dey
cpy $d012
bcc l1
exd:
pla
tay
pla
tax
pla
rti
exi:
asl $d019
jmp exd
e2:
inc rirq_count
ldy rasterIRQNext
dey
sty $d012
ldx #0
stx nextIRQ
beq exd
}
__asm rirq_isr_noio
{
pha
txa
pha
tya
pha
kentry:
lda $01
pha
lda #$35
sta $01
ldx nextIRQ
bmi exi
l1:
lda rasterIRQNext, x
ldy rasterIRQIndex + 1, x
ldx rasterIRQLow, y
stx ji + 1
ldx rasterIRQHigh, y
stx ji + 2
ji:
jsr $0000
inc nextIRQ
ldx nextIRQ
ldy rasterIRQNext, x
asl $d019
cpy #$ff
beq e2
dey
sty $d012
dey
cpy $d012
bcc l1
exd:
pla
sta $01
pla
tay
pla
tax
pla
rti
exi:
asl $d019
jmp exd
e2:
inc rirq_count
ldy rasterIRQNext
dey
sty $d012
ldx #0
stx nextIRQ
beq exd
}
__asm rirq_isr_kernal_io
{
lda $d019
bpl ex2
ldx nextIRQ
bmi exi
l1:
lda rasterIRQNext, x
ldy rasterIRQIndex + 1, x
ldx rasterIRQLow, y
stx ji + 1
ldx rasterIRQHigh, y
stx ji + 2
ji:
jsr $0000
jx:
inc nextIRQ
ldx nextIRQ
ldy rasterIRQNext, x
asl $d019
cpy #$ff
beq e2
dey
dey
sty $d012
dey
cpy $d012
bcc l1
exd:
jmp $ea81
exi:
asl $d019
jmp $ea81
e2:
inc rirq_count
ldy rasterIRQNext
dey
dey
sty $d012
ldx #0
stx nextIRQ
jmp $ea81
ex2:
LDA $DC0D
cli
jmp $ea31
}
__asm rirq_isr_kernal_noio
{
lda $01
pha
lda #$36
sta $01
lda $d019
bpl ex2
ldx nextIRQ
bmi exi
l1:
lda rasterIRQNext, x
ldy rasterIRQIndex + 1, x
ldx rasterIRQLow, y
stx ji + 1
ldx rasterIRQHigh, y
stx ji + 2
ji:
jsr $0000
jx:
inc nextIRQ
ldx nextIRQ
ldy rasterIRQNext, x
asl $d019
cpy #$ff
beq e2
dey
dey
sty $d012
dey
cpy $d012
bcc l1
exd:
pla
sta $01
jmp $ea81
exi:
asl $d019
jmp exd
e2:
inc rirq_count
ldy rasterIRQNext
dey
dey
sty $d012
ldx #0
stx nextIRQ
beq exd
ex2:
LDA $DC0D
cli
pla
sta $01
jmp $ea31
}
// 0 lda #data0
// 2 ldy #data1
// 4 cpx $d012
// 7 bcc -5
// 9 sta addr0
// 12 sty addr1
// 15 lda #data2
// 17 sta addr2
// 20 lda #data3
// 22 sta addr3
// ...
// rts
void rirq_build(RIRQCode * ic, byte size)
{
__assume(size < 26);
ic->size = size;
asm_im(ic->code + 0, ASM_LDY, 0);
asm_im(ic->code + 2, ASM_LDX, 0);
asm_ab(ic->code + 4, ASM_CMP, 0xd012);
asm_rl(ic->code + 7, ASM_BCS, -5);
asm_ab(ic->code + 9, ASM_STY, 0x0000);
if (size == 0)
{
asm_np(ic->code + 0, ASM_RTS);
}
else if (size == 1)
{
asm_np(ic->code + 12, ASM_RTS);
}
else
{
asm_ab(ic->code + 12, ASM_STX, 0x0000);
byte p = 15;
for(byte i=2; i<size; i++)
{
p += asm_im(ic->code + p, ASM_LDA, 0x00);
p += asm_ab(ic->code + p, ASM_STA, 0x0000);
}
asm_np(ic->code + p, ASM_RTS);
}
}
RIRQCode * rirq_alloc(byte size)
{
RIRQCode * ic = (RIRQCode *)malloc(1 + RIRQ_SIZE + 5 * size);
rirq_build(ic, size);
return ic;
}
#pragma native(rirq_build)
void rirq_set(byte n, byte row, RIRQCode * write)
{
rasterIRQLow[n] = (unsigned)&write->code & 0xff;
rasterIRQHigh[n] = (unsigned)&write->code >> 8;
rasterIRQRows[n] = row;
}
static const byte irqai[26] = {
RIRQ_ADDR_0, RIRQ_ADDR_1, RIRQ_ADDR_2, RIRQ_ADDR_3, RIRQ_ADDR_4, RIRQ_ADDR_5, RIRQ_ADDR_6, RIRQ_ADDR_7,
RIRQ_ADDR_8, RIRQ_ADDR_9, RIRQ_ADDR_10, RIRQ_ADDR_11, RIRQ_ADDR_12, RIRQ_ADDR_13, RIRQ_ADDR_14, RIRQ_ADDR_15,
RIRQ_ADDR_16, RIRQ_ADDR_17, RIRQ_ADDR_18, RIRQ_ADDR_19, RIRQ_ADDR_20, RIRQ_ADDR_21, RIRQ_ADDR_22, RIRQ_ADDR_23,
RIRQ_ADDR_24, RIRQ_ADDR_25
};
static const byte irqdi[26] = {
RIRQ_DATA_0, RIRQ_DATA_1, RIRQ_DATA_2, RIRQ_DATA_3, RIRQ_DATA_4, RIRQ_DATA_5, RIRQ_DATA_6, RIRQ_DATA_7,
RIRQ_DATA_8, RIRQ_DATA_9, RIRQ_DATA_10, RIRQ_DATA_11, RIRQ_DATA_12, RIRQ_DATA_13, RIRQ_DATA_14, RIRQ_DATA_15,
RIRQ_DATA_16, RIRQ_DATA_17, RIRQ_DATA_18, RIRQ_DATA_19, RIRQ_DATA_20, RIRQ_DATA_21, RIRQ_DATA_22, RIRQ_DATA_23,
RIRQ_DATA_24, RIRQ_DATA_25
};
void rirq_addr(RIRQCode * ic, byte n, void * addr)
{
byte p = irqai[n];
((byte *)ic->code)[p + 0] = (unsigned)addr & 0xff;
((byte *)ic->code)[p + 1] = (unsigned)addr >> 8;
}
void rirq_addrhi(RIRQCode * ic, byte n, byte hi)
{
byte p = irqai[n];
((byte *)ic->code)[p + 1] = hi;
}
void rirq_data(RIRQCode * ic, byte n, byte data)
{
byte p = irqdi[n];
// ic->code[p] = data;
(volatile char *)(ic->code)[p] = data;
}
void rirq_write(RIRQCode * ic, byte n, void * addr, byte data)
{
byte p = irqai[n];
((byte *)ic->code)[p + 0] = (unsigned)addr & 0xff;
((byte *)ic->code)[p + 1] = (unsigned)addr >> 8;
p = irqdi[n];
((byte *)ic->code)[p] = data;
}
void rirq_call(RIRQCode * ic, byte n, void * addr)
{
byte p = irqai[n];
((byte *)ic->code)[p - 1] = 0x20;
((byte *)ic->code)[p + 0] = (unsigned)addr & 0xff;
((byte *)ic->code)[p + 1] = (unsigned)addr >> 8;
}
void rirq_delay(RIRQCode * ic, byte cycles)
{
ic->code[ 1] = cycles;
ic->code[ 9] = 0x88; // dey
ic->code[10] = 0xd0; // bne
ic->code[11] = 0xfd; // -3
}
void rirq_move(byte n, byte row)
{
rasterIRQRows[n] = row;
}
void rirq_clear(byte n)
{
rasterIRQRows[n] = 255;
}
void rirq_init_tables(void)
{
for(byte i=0; i<NUM_IRQS; i++)
{
rasterIRQRows[i] = 255;
rasterIRQIndex[i + 1] = i;
}
rasterIRQIndex[0] = NUM_IRQS;
rasterIRQRows[NUM_IRQS] = 0;
rasterIRQNext[NUM_IRQS] = 255;
}
void rirq_init_kernal(void)
{
rirq_init_tables();
__asm
{
sei
}
*(void **)0x0314 = rirq_isr_kernal_io;
vic.intr_enable = 1;
vic.ctrl1 &= 0x7f;
vic.raster = 255;
}
void rirq_init_kernal_noio(void)
{
rirq_init_tables();
__asm
{
sei
}
*(void **)0x0314 = rirq_isr_kernal_noio;
vic.intr_enable = 1;
vic.ctrl1 &= 0x7f;
vic.raster = 255;
}
void rirq_init_crt(void)
{
rirq_init_tables();
__asm
{
sei
}
*(void **)0x0314 = rirq_isr_io.kentry;
*(void **)0xfffe = rirq_isr_io;
vic.intr_enable = 1;
vic.ctrl1 &= 0x7f;
vic.raster = 255;
}
void rirq_init_crt_noio(void)
{
rirq_init_tables();
__asm
{
sei
}
*(void **)0x0314 = rirq_isr_noio.kentry;
*(void **)0xfffe = rirq_isr_noio;
vic.intr_enable = 1;
vic.ctrl1 &= 0x7f;
vic.raster = 255;
}
void rirq_init_io(void)
{
rirq_init_tables();
__asm
{
sei
}
*(void **)0xfffe = rirq_isr_ram_io;
vic.intr_enable = 1;
vic.ctrl1 &= 0x7f;
vic.raster = 255;
}
void rirq_init_memmap(void)
{
rirq_init_tables();
__asm
{
sei
}
*(void **)0xfffe = rirq_isr_noio;
vic.intr_enable = 1;
vic.ctrl1 &= 0x7f;
vic.raster = 255;
}
void rirq_init(bool kernalIRQ)
{
if (kernalIRQ)
rirq_init_kernal();
else
rirq_init_io();
}
void rirq_wait(void)
{
char i0 = rirq_pcount;
char i1;
do {
i1 = rirq_count;
} while (i0 == i1);
rirq_pcount = i1;
}
void rirq_wait_done(void)
{
do {
} while (nextIRQ != 0);
}
void rirq_sort(bool inirq)
{
// disable raster interrupts while sorting
nextIRQ = 0xff;
#if 1
byte maxr = rasterIRQRows[rasterIRQIndex[1]];
for(byte i = 2; i<NUM_IRQS + 1; i++)
{
byte ri = rasterIRQIndex[i];
byte rr = rasterIRQRows[ri];
if (rr < maxr)
{
rasterIRQIndex[i] = rasterIRQIndex[i - 1];
byte j = i, rj;
while (rr < rasterIRQRows[(rj = rasterIRQIndex[j - 2])])
{
rasterIRQIndex[j - 1] = rj;
j--;
}
rasterIRQIndex[j - 1] = ri;
}
else
maxr = rr;
}
#else
for(byte i = 1; i<NUM_IRQS; i++)
{
byte ri = rasterIRQIndex[i];
byte rr = rasterIRQRows[ri];
byte j = i, rj;
while (j > 0 && rr < rasterIRQRows[(rj = rasterIRQIndex[j - 1])])
{
rasterIRQIndex[j] = rj;
j--;
}
rasterIRQIndex[j] = ri;
}
#endif
#if NUM_IRQS & 3
for(sbyte i=NUM_IRQS-1; i>=0; i--)
rasterIRQNext[i] = rasterIRQRows[rasterIRQIndex[i + 1]];
#else
for(sbyte i=NUM_IRQS/4-1; i>=0; i--)
{
#pragma unroll(full)
for(int j=0; j<4; j++)
rasterIRQNext[i + j * NUM_IRQS / 4] = rasterIRQRows[rasterIRQIndex[i + j * NUM_IRQS / 4 + 1]];
}
#endif
rirq_pcount = rirq_count;
if (inirq)
nextIRQ = NUM_IRQS - 1;
else
{
byte yp = rasterIRQNext[0];
if (yp != 0xff)
{
vic.raster = yp - 1;
nextIRQ = 0;
}
}
}
void rirq_start(void)
{
__asm
{
lda $d011
and #$7f
sta $d011
lda #100
sta $d012
asl $d019
cli
}
}
void rirq_stop(void)
{
__asm
{
sei
}
}
#pragma native(rirq_sort)
#pragma native(rirq_wait)
#pragma native(rirq_start)
#pragma native(rirq_stop)
+202
View File
@@ -0,0 +1,202 @@
#ifndef C64_RASTERIRQ_H
#define C64_RASTERIRQ_H
#include "types.h"
#ifndef NUM_IRQS
#define NUM_IRQS 16
#endif
extern volatile byte rirq_count;
enum RIRQCodeIndex
{
RIRQ_DATA_0 = 1,
RIRQ_DATA_1 = 3,
RIRQ_ADDR_0 = 10,
RIRQ_ADDR_1 = 13,
RIRQ_DATA_2 = 16,
RIRQ_ADDR_2 = 18,
RIRQ_DATA_3 = 21,
RIRQ_ADDR_3 = 23,
RIRQ_DATA_4 = 26,
RIRQ_ADDR_4 = 28,
RIRQ_SIZE = 31,
RIRQ_DATA_5 = 31,
RIRQ_ADDR_5 = 33,
RIRQ_DATA_6 = 36,
RIRQ_ADDR_6 = 38,
RIRQ_DATA_7 = 41,
RIRQ_ADDR_7 = 43,
RIRQ_DATA_8 = 46,
RIRQ_ADDR_8 = 48,
RIRQ_DATA_9 = 51,
RIRQ_ADDR_9 = 53,
RIRQ_DATA_10 = 56,
RIRQ_ADDR_10 = 58,
RIRQ_SIZE_10 = 61,
RIRQ_DATA_11 = 61,
RIRQ_ADDR_11 = 63,
RIRQ_DATA_12 = 66,
RIRQ_ADDR_12 = 68,
RIRQ_DATA_13 = 71,
RIRQ_ADDR_13 = 73,
RIRQ_DATA_14 = 76,
RIRQ_ADDR_14 = 78,
RIRQ_DATA_15 = 81,
RIRQ_ADDR_15 = 83,
RIRQ_DATA_16 = 86,
RIRQ_ADDR_16 = 88,
RIRQ_DATA_17 = 91,
RIRQ_ADDR_17 = 93,
RIRQ_DATA_18 = 96,
RIRQ_ADDR_18 = 98,
RIRQ_DATA_19 = 101,
RIRQ_ADDR_19 = 103,
RIRQ_SIZE_20 = 106,
RIRQ_DATA_20 = 106,
RIRQ_ADDR_20 = 108,
RIRQ_DATA_21 = 111,
RIRQ_ADDR_21 = 113,
RIRQ_DATA_22 = 116,
RIRQ_ADDR_22 = 118,
RIRQ_DATA_23 = 121,
RIRQ_ADDR_23 = 123,
RIRQ_DATA_24 = 126,
RIRQ_ADDR_24 = 128,
RIRQ_DATA_25 = 131,
RIRQ_ADDR_25 = 133
};
// One raster interrupt operation, handles up to five writes
// to arbitrary memory location, or one wait and four writes.
typedef struct RIRQCode
{
byte size;
byte code[RIRQ_SIZE];
} RIRQCode;
typedef struct RIRQCode10
{
RIRQCode c;
byte code[RIRQ_SIZE_10 - RIRQ_SIZE];
} RIRQCode10;
typedef struct RIRQCode20
{
RIRQCode c;
byte code[RIRQ_SIZE_20 - RIRQ_SIZE];
} RIRQCode20;
// Build one raster IRQ operation of the given size (wait + #ops) for up to 5 instructions
void rirq_build(RIRQCode * ic, byte size);
// Allocate one raster IRQ operation of the given size (wait + #ops)
RIRQCode * rirq_alloc(byte size);
// Add a write command to a raster IRQ
inline void rirq_write(RIRQCode * ic, byte n, void * addr, byte data);
// Add a call command to a raster IRQ
inline void rirq_call(RIRQCode * ic, byte n, void * addr);
// Change the address of a raster IRQ write command
inline void rirq_addr(RIRQCode * ic, byte n, void * addr);
// Change the high byte of the address of a raster IRQ write command
inline void rirq_addrhi(RIRQCode * ic, byte n, byte hi);
// Change the data of a raster IRQ write command
inline void rirq_data(RIRQCode * ic, byte n, byte data);
// Add a delay of 5 * cycles to a raster IRQ
inline void rirq_delay(RIRQCode * ic, byte cycles);
// Place a raster IRQ into one of the 16 slots, the interrupt will fire
// one line below the given row
inline void rirq_set(byte n, byte row, RIRQCode * write);
// Remove a raster IRQ from one of the 16 slots
inline void rirq_clear(byte n);
// Change the vertical position of the raster IRQ of one of the slots
inline void rirq_move(byte n, byte row);
// Initialize the raster IRQ system with either the kernal IRQ vector
// or the hardware IRQ vector if the kernal ROM is turned off (which is
// the less resource hungry option)
inline void rirq_init(bool kernalIRQ);
// Raster IRQ through kernal, with IO range always enabled
// calls kernal continuation
void rirq_init_kernal(void);
// Raster IRQ through kernal, with IO range not always enabled
// calls kernal continuation
void rirq_init_kernal_noio(void);
// Raster IRQ through RAM and ROM vector, with ROM disabled or not and IO range always enabled
// does not call kernal continuation
void rirq_init_crt(void);
// Raster IRQ through RAM and ROM vector, with ROM disabled or not and IO range not always enabled
// does not call kernal continuation
void rirq_init_crt_noio(void);
// Raster IRQ through RAM vector, with ROM disabled and IO range always enabled
// does not call kernal continuation
void rirq_init_io(void);
// Raster IRQ through RAM vector, with ROM disabled and IO range not always enabled
// does not call kernal continuation
void rirq_init_memmap(void);
// Start raster IRQ
void rirq_start(void);
// Stop raster IRQ
void rirq_stop(void);
// Sort the raster IRQ, must be performed at the end of the frame after changing
// the vertical position of one of the interrupt operations.
// Set the inirq flag to true when calling this from an interrupt
void rirq_sort(bool inirq = false);
// Wait for the last raster IRQ op to have completed. Must be called before a
// sort if the raster IRQ system is active
void rirq_wait_done(void);
void rirq_wait(void);
#pragma compile("rasterirq.c")
#endif
+97
View File
@@ -0,0 +1,97 @@
#include "reu.h"
int reu_count_pages(void)
{
volatile char c, d;
c = 0;
reu_store(0, &c, 1);
reu_load(0, &d, 1);
if (d == 0)
{
c = 0x47;
reu_store(0, &c, 1);
reu_load(0, &d, 1);
if (d == 0x47)
{
for(int i=1; i<256; i++)
{
long l = (long)i << 16;
c = 0x47;
reu_store(l, &c, 1);
c = 0x00;
reu_store(0, &c, 1);
reu_load(l, &d, 1);
if (d != 0x47)
return i;
}
return 256;
}
}
return 0;
}
inline void reu_store(unsigned long raddr, const volatile char * sp, unsigned length)
{
reu.laddr = (word)sp;
reu.raddr = raddr;
reu.rbank = raddr >> 16;
reu.length = length;
reu.ctrl = REU_CTRL_INCL | REU_CTRL_INCR;
reu.cmd = REU_CMD_EXEC | REU_CMD_FF00 | REU_CMD_STORE;
}
inline void reu_load(unsigned long raddr, volatile char * dp, unsigned length)
{
reu.laddr = (word)dp;
reu.raddr = raddr;
reu.rbank = raddr >> 16;
reu.length = length;
reu.ctrl = REU_CTRL_INCL | REU_CTRL_INCR;
reu.cmd = REU_CMD_EXEC | REU_CMD_FF00 | REU_CMD_LOAD;
}
inline void reu_fill(unsigned long raddr, char c, unsigned length)
{
reu.laddr = (word)&c;
reu.raddr = raddr;
reu.rbank = raddr >> 16;
reu.length = length;
reu.ctrl = REU_CTRL_FIXL | REU_CTRL_INCR;
reu.cmd = REU_CMD_EXEC | REU_CMD_FF00 | REU_CMD_STORE;
}
inline void reu_load2d(unsigned long raddr, volatile char * dp, char height, unsigned width, unsigned stride)
{
reu.ctrl = REU_CTRL_INCL | REU_CTRL_INCR;
reu.laddr = (word)dp;
for(char i=0; i<height; i++)
{
reu.length = width;
reu.raddr = raddr;
reu.rbank = raddr >> 16;
reu.cmd = REU_CMD_EXEC | REU_CMD_FF00 | REU_CMD_LOAD;
raddr += stride;
}
}
inline void reu_load2dpage(unsigned long raddr, volatile char * dp, char height, unsigned width, unsigned stride)
{
reu.ctrl = REU_CTRL_INCL | REU_CTRL_INCR;
reu.laddr = (word)dp;
reu.rbank = raddr >> 16;
for(char i=0; i<height; i++)
{
reu.length = width;
reu.raddr = raddr;
reu.cmd = REU_CMD_EXEC | REU_CMD_FF00 | REU_CMD_LOAD;
raddr += stride;
}
}
+68
View File
@@ -0,0 +1,68 @@
#ifndef C64_REU_H
#define C64_REU_H
#include "types.h"
#define REU_STAT_IRQ 0x80
#define REU_STAT_EOB 0x40
#define REU_STAT_FAULT 0x20
#define REU_STAT_SIZE 0x10
#define REU_STAT_VERSION 0x0f
#define REU_CTRL_FIXL 0x80
#define REU_CTRL_FIXR 0x40
#define REU_CTRL_INCL 0x00
#define REU_CTRL_INCR 0x00
#define REU_IRQ_ENABLE 0x80
#define REU_IRQ_EOB 0x40
#define REU_IRQ_FAULT 0x20
#define REU_CMD_EXEC 0x80
#define REU_CMD_AUTO 0x20
#define REU_CMD_FF00 0x10
#define REU_CMD_STORE 0x00
#define REU_CMD_LOAD 0x01
#define REU_CMD_SWAP 0x02
#define REU_CMD_VERIFY 0x03
struct REU
{
volatile byte status;
volatile __memmap byte cmd;
volatile word laddr;
volatile word raddr;
volatile byte rbank;
volatile word length;
volatile byte irqmask;
volatile byte ctrl;
};
#define reu (*((struct REU *)0xdf00))
// Count the number of 64k pages in the REU, the test is destructive
int reu_count_pages(void);
// Copy an array of data from C64 memory to the REU memory
inline void reu_store(unsigned long raddr, const volatile char * sp, unsigned length);
// Copy an array of data from REU memory to the C64 memory
inline void reu_load(unsigned long raddr, volatile char * dp, unsigned length);
// Fill an array of data in the REU with a single value
inline void reu_fill(unsigned long raddr, char c, unsigned length);
// Copy a 2D array from REU memory to the C64 memory. The stride parameter
// is the distance of two rows in REU memory
inline void reu_load2d(unsigned long raddr, volatile char * dp, char height, unsigned width, unsigned stride);
inline void reu_load2dpage(unsigned long raddr, volatile char * dp, char height, unsigned width, unsigned stride);
#pragma compile("reu.c")
#endif
+2
View File
@@ -0,0 +1,2 @@
#include "sid.h"
+107
View File
@@ -0,0 +1,107 @@
#ifndef C64_SID_H
#define C64_SID_H
#include "types.h"
#define SID_ATK_2 0x00
#define SID_ATK_8 0x10
#define SID_ATK_16 0x20
#define SID_ATK_24 0x30
#define SID_ATK_38 0x40
#define SID_ATK_56 0x50
#define SID_ATK_68 0x60
#define SID_ATK_80 0x70
#define SID_ATK_100 0x80
#define SID_ATK_250 0x90
#define SID_ATK_500 0xa0
#define SID_ATK_800 0xb0
#define SID_ATK_1000 0xc0
#define SID_ATK_3000 0xd0
#define SID_ATK_5000 0xe0
#define SID_ATK_8000 0xf0
#define SID_DKY_6 0x00
#define SID_DKY_24 0x01
#define SID_DKY_48 0x02
#define SID_DKY_72 0x03
#define SID_DKY_114 0x04
#define SID_DKY_168 0x05
#define SID_DKY_204 0x06
#define SID_DKY_240 0x07
#define SID_DKY_300 0x08
#define SID_DKY_750 0x09
#define SID_DKY_1500 0x0a
#define SID_DKY_2400 0x0b
#define SID_DKY_3000 0x0c
#define SID_DKY_9000 0x0d
#define SID_DKY_15000 0x0e
#define SID_DKY_24000 0x0f
#define SID_CTRL_GATE 0x01
#define SID_CTRL_SYNC 0x02
#define SID_CTRL_RING 0x04
#define SID_CTRL_TEST 0x08
#define SID_CTRL_TRI 0x10
#define SID_CTRL_SAW 0x20
#define SID_CTRL_RECT 0x40
#define SID_CTRL_NOISE 0x80
#define SID_FILTER_1 0x01
#define SID_FILTER_2 0x02
#define SID_FILTER_3 0x04
#define SID_FILTER_X 0x08
#define SID_FMODE_LP 0x10
#define SID_FMODE_BP 0x20
#define SID_FMODE_HP 0x40
#define SID_FMODE_3_OFF 0x80
#define SID_CLOCK_PAL 985248
#define SID_CLOCK_NTSC 1022727
#define SID_CLKSCALE_PAL 1115974UL
#define SID_CLKSCALE_NTSC 1075078UL
#define SID_FREQ_PAL(f) ((unsigned)(((unsigned long)(f) * SID_CLKSCALE_PAL) >> 16))
#define SID_FREQ_NTSC(f) ((unsigned)(((unsigned long)(f) * SID_CLKSCALE_NTSC) >> 16))
struct SID
{
struct Voice
{
volatile unsigned freq;
volatile unsigned pwm;
volatile byte ctrl;
volatile byte attdec;
volatile byte susrel;
} voices[3];
volatile unsigned ffreq;
volatile byte resfilt;
volatile byte fmodevol;
volatile byte potx;
volatile byte poty;
volatile byte random;
volatile byte env3;
};
#define NOTE_C(o) (16744U >> (10 - (o)))
#define NOTE_CS(o) (17740U >> (10 - (o)))
#define NOTE_D(o) (18794U >> (10 - (o)))
#define NOTE_DS(o) (19912U >> (10 - (o)))
#define NOTE_E(o) (21096U >> (10 - (o)))
#define NOTE_F(o) (22351U >> (10 - (o)))
#define NOTE_FS(o) (23680U >> (10 - (o)))
#define NOTE_G(o) (25087U >> (10 - (o)))
#define NOTE_GS(o) (26580U >> (10 - (o)))
#define NOTE_A(o) (28160U >> (10 - (o)))
#define NOTE_AS(o) (29834U >> (10 - (o)))
#define NOTE_B(o) (31068U >> (10 - (o)))
// reference to the SID chip
#define sid (*((struct SID *)0xd400))
#pragma compile("sid.c")
#endif
+346
View File
@@ -0,0 +1,346 @@
#include "sprites.h"
#include "rasterirq.h"
static volatile char * vspriteScreen;
#ifdef VSPRITE_BSS
#pragma bss(VSPRITE_BSS)
#endif
void spr_init(char * screen)
{
vspriteScreen = screen + 0x3f8;
}
void spr_set(char sp, bool show, int xpos, int ypos, char image, char color, bool multi, bool xexpand, bool yexpand)
{
__assume (sp < 8);
char m = 1 << sp;
if (show)
vic.spr_enable |= m;
else
vic.spr_enable &= ~m;
if (multi)
vic.spr_multi |= m;
else
vic.spr_multi &= ~m;
if (xexpand)
vic.spr_expand_x |= m;
else
vic.spr_expand_x &= ~m;
if (yexpand)
vic.spr_expand_y |= m;
else
vic.spr_expand_y &= ~m;
vic.spr_pos[sp].y = ypos;
vic.spr_pos[sp].x = xpos & 0xff;
if (xpos & 0x100)
vic.spr_msbx |= m;
else
vic.spr_msbx &= ~m;
vspriteScreen[sp] = image;
vic.spr_color[sp] = color;
}
void spr_show(char sp, bool show)
{
__assume (sp < 8);
if (show)
vic.spr_enable |= 1 << sp;
else
vic.spr_enable &= ~(1 << sp);
}
void spr_move(char sp, int xpos, int ypos)
{
__assume (sp < 8);
vic.spr_pos[sp].y = ypos;
vic.spr_pos[sp].x = xpos & 0xff;
if (xpos & 0x100)
vic.spr_msbx |= 1 << sp;
else
vic.spr_msbx &= ~(1 << sp);
}
void spr_move16(char sp, int xpos, int ypos)
{
__assume (sp < 8);
if (ypos < 0 || ypos >= 256 || xpos < 0 || xpos >= 384)
xpos = 384;
vic.spr_pos[sp].y = ypos;
vic.spr_pos[sp].x = xpos & 0xff;
if (xpos & 0x100)
vic.spr_msbx |= 1 << sp;
else
vic.spr_msbx &= ~(1 << sp);
}
int spr_posx(char sp)
{
return vic.spr_pos[sp].x | ((vic.spr_msbx & (1 << sp)) ? 256 : 0);
}
int spr_posy(char sp)
{
return vic.spr_pos[sp].y;
}
void spr_image(char sp, char image)
{
__assume (sp < 8);
vspriteScreen[sp] = image;
}
void spr_color(char sp, char color)
{
__assume (sp < 8);
vic.spr_color[sp] = color;
}
void spr_expand(char sp, bool xexpand, bool yexpand)
{
__assume (sp < 8);
char m = 1 << sp;
if (xexpand)
vic.spr_expand_x |= m;
else
vic.spr_expand_x &= ~m;
if (yexpand)
vic.spr_expand_y |= m;
else
vic.spr_expand_y &= ~m;
}
static char vspriteYLow[VSPRITES_MAX], vspriteXLow[VSPRITES_MAX], vspriteXHigh[VSPRITES_MAX];
static char vspriteImage[VSPRITES_MAX], vspriteColor[VSPRITES_MAX];
static char spriteOrder[VSPRITES_MAX], spriteYPos[VSPRITES_MAX + 1];
static RIRQCode spirq[VSPRITES_MAX - 8], synch;
void vspr_init(char * screen)
{
vspriteScreen = screen + 0x3f8;
vic.spr_expand_x = 0;
vic.spr_expand_y = 0;
vic.spr_enable = 0xff;
for(int i=0; i<VSPRITES_MAX - 8; i++)
{
#ifdef VSPRITE_REVERSE
int j = (i & 7) ^ 7;
#else
int j = i & 7;
#endif
rirq_build(spirq + i, 5);
rirq_write(spirq + i, 0, &vic.spr_color[j], 0);
rirq_write(spirq + i, 1, &vic.spr_pos[j].x, 0);
rirq_write(spirq + i, 2, &vic.spr_pos[j].y, 0);
rirq_write(spirq + i, 3, &vspriteScreen[j], 0);
rirq_write(spirq + i, 4, &vic.spr_msbx, 0);
rirq_set(i, 80 + 4 * i, spirq + i);
}
rirq_build(&synch, 0);
rirq_set(VSPRITES_MAX - 8, 250, &synch);
for(int i=0; i<VSPRITES_MAX; i++)
{
spriteOrder[i] = i;
vspriteYLow[i] = 0xff;
}
}
void vspr_shutdown(void)
{
for(int i=0; i<VSPRITES_MAX - 7; i++)
rirq_clear(i);
}
void vspr_screen(char * screen)
{
vspriteScreen = screen + 0x3f8;
char hi = (unsigned)vspriteScreen >> 8;
#pragma unroll(8)
for(int i=0; i<VSPRITES_MAX - 8; i++)
rirq_addrhi(spirq + i, 3, hi);
}
#pragma native(vspr_init)
void vspr_set(char sp, int xpos, int ypos, char image, char color)
{
char yp = (char)ypos;
if ((ypos & 0xff00 ) || (xpos & 0xfe00))
yp = 0xff;
vspriteYLow[sp] = yp;
vspriteXLow[sp] = (char)xpos;
vspriteXHigh[sp] = (char)(xpos >> 8);
vspriteImage[sp] = image;
vspriteColor[sp] = color;
}
#pragma native(vspr_set)
void vspr_move(char sp, int xpos, int ypos)
{
char yp = (char)ypos;
if ((ypos & 0xff00 ) || (xpos & 0xfe00))
yp = 0xff;
vspriteYLow[sp] = yp;
vspriteXLow[sp] = (char)xpos;
vspriteXHigh[sp] = (char)(xpos >> 8);
}
void vspr_movex(char sp, int xpos)
{
vspriteXLow[sp] = (char)xpos;
vspriteXHigh[sp] = (char)(xpos >> 8);
}
void vspr_movey(char sp, int ypos)
{
char yp = (char)ypos;
if (ypos & 0xff00)
yp = 0xff;
vspriteYLow[sp] = yp;
}
void vspr_image(char sp, char image)
{
vspriteImage[sp] = image;
}
void vspr_color(char sp, char color)
{
vspriteColor[sp] = color;
}
void vspr_hide(char sp)
{
vspriteYLow[sp] = 0xff;
}
void vspr_sort(void)
{
byte rm = vspriteYLow[spriteOrder[0]];
spriteYPos[1] = rm;
for(char i = 1; i<VSPRITES_MAX; i++)
{
byte ri = spriteOrder[i];
byte rr = vspriteYLow[ri];
if (rr < rm)
{
byte j = i, rj = rm;
do {
spriteYPos[j + 1] = rj;
spriteOrder[j] = spriteOrder[j - 1];
rj = spriteYPos[j - 1];
j--;
} while (rr < rj);
spriteOrder[j] = ri;
spriteYPos[j + 1] = rr;
}
else
{
spriteYPos[i + 1] = rr;
rm = rr;
}
}
}
#pragma native(vspr_sort)
void vspr_update(void)
{
char xymask = 0;
volatile char * vsprs = vspriteScreen;
// char sypos[VSPRITES_MAX];
#pragma unroll(full)
for(char ui=0; ui<8; ui++)
{
byte ri = spriteOrder[ui];
#ifdef VSPRITE_REVERSE
char uj = ui ^ 7;
#else
char uj = ui;
#endif
vic.spr_color[uj] = vspriteColor[ri];
vsprs[uj] = vspriteImage[ri];
#ifdef VSPRITE_REVERSE
xymask = (xymask << 1) | (vspriteXHigh[ri] & 1);
#else
xymask = ((unsigned)xymask | (vspriteXHigh[ri] << 8)) >> 1;
#endif
vic.spr_pos[uj].x = vspriteXLow[ri];
vic.spr_pos[uj].y = spriteYPos[ui + 1];
// sypos[ui] = vspriteYLow[ri];
}
vic.spr_msbx = xymask;
#pragma unroll(full)
bool done = false;
for(char ti=0; ti<VSPRITES_MAX - 8; ti++)
{
if (!done && spriteYPos[ti + 9] < 250)
{
byte ri = spriteOrder[ti + 8];
rirq_move(ti, spriteYPos[ti + 1] + 23);
#ifdef VSPRITE_REVERSE
char m = 0x80 >> (ti & 7);
#else
char m = 1 << (ti & 7);
#endif
xymask |= m;
if (!(vspriteXHigh[ri] & 1))
xymask ^= m;
rirq_data(spirq + ti, 2, spriteYPos[ti + 9]);
rirq_data(spirq + ti, 0, vspriteColor[ri]);
rirq_data(spirq + ti, 1, vspriteXLow[ri]);
rirq_data(spirq + ti, 3, vspriteImage[ri]);
rirq_data(spirq + ti, 4, xymask);
// spriteYPos[ti + 9] = vspriteYLow[ri];
}
else
{
rirq_clear(ti);
done = true;
}
}
}
#pragma native(vspr_update)
+113
View File
@@ -0,0 +1,113 @@
#ifndef SPRITES_H
#define SPRITES_H
#include "vic.h"
// initialize non virtualized sprite system, using only the eight hardware sprites
void spr_init(char * screen);
// set one sprite with the given attributes
void spr_set(char sp, bool show, int xpos, int ypos, char image, char color, bool multi, bool xexpand, bool yexpand);
// show or hide a sprite
inline void spr_show(char sp, bool show);
// move a sprite to the given position, only uses 8 bit y and 9 bit x
inline void spr_move(char sp, int xpos, int ypos);
// get current x position of sprite
inline int spr_posx(char sp);
// get current y position of sprite
inline int spr_posy(char sp);
// move a sprite to the given position, only uses 16 bit y and 16 bit x,
// moves the sprite to a zero y position if offscreen
void spr_move16(char sp, int xpos, int ypos);
// change the image of a sprite
inline void spr_image(char sp, char image);
// change the color of a sprite
inline void spr_color(char sp, char color);
// change the image of a sprite
inline void spr_expand(char sp, bool xexpand, bool yexpand);
// The virtual sprite system works with the rasterirq library to multiplex
// 16 virtual sprites onto the actual eight hardware sprites. It uses the slots
// 0 to 8 of the rasterirq library to switch the sprites mid screen. The
// application has to race the beam and call at least the vspr_update every
// bottom of the frame to reset the top eight sprites.
//
// A usual frame would look like this:
//
// - off screen game code
// vspr_sort();
// - more game code
// rirq_wait();
// vspr_update();
// - more raster irq stuff
// rirq_sort();
//
#ifndef VSPRITES_MAX
#define VSPRITES_MAX 16
#endif
// initialize the virtual (multiplexed) sprite system, offering 16 sprites
void vspr_init(char * screen);
void vspr_shutdown(void);
void vspr_screen(char * screen);
// set one sprite with the given attribute
void vspr_set(char sp, int xpos, int ypos, char image, char color);
// move a virtual sprite
inline void vspr_move(char sp, int xpos, int ypos);
inline void vspr_movex(char sp, int xpos);
inline void vspr_movey(char sp, int ypos);
// change the image of a virtual sprite
inline void vspr_image(char sp, char image);
// change the color of a virtual sprite
inline void vspr_color(char sp, char color);
// hide a virtual sprite, show again by moving it into the visual range
inline void vspr_hide(char sp);
// sort the virtual sprites by their y-position
void vspr_sort(void);
// update the virtual sprites. Must be called every frame before sorting
// the raster irq list.
void vspr_update(void);
#pragma compile("sprites.c")
#endif
+10
View File
@@ -0,0 +1,10 @@
#ifndef C64_TYPES_H
#define C64_TYPES_H
typedef unsigned char byte;
typedef unsigned int word;
typedef unsigned long dword;
typedef signed char sbyte;
#endif
+140
View File
@@ -0,0 +1,140 @@
#include "vic.h"
#include "cia.h"
void vic_setbank(char bank)
{
cia2.pra = (cia2.pra & 0xfc) | (bank ^ 0x03);
}
void vic_sprxy(byte s, int x, int y)
{
vic.spr_pos[s].y = y;
vic.spr_pos[s].x = x & 0xff;
if (x & 0x100)
vic.spr_msbx |= 1 << s;
else
vic.spr_msbx &= ~(1 << s);
}
int vic_sprgetx(byte s)
{
return vic.spr_pos[s].x | ((vic.spr_msbx & (1 << s)) ? 256 : 0);
}
void vic_setmode(VicMode mode, const char * text, const char * font)
{
switch (mode)
{
case VICM_TEXT:
vic.ctrl1 = VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3;
vic.ctrl2 = VIC_CTRL2_CSEL;
break;
case VICM_TEXT_MC:
vic.ctrl1 = VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3;
vic.ctrl2 = VIC_CTRL2_CSEL | VIC_CTRL2_MCM;
break;
case VICM_TEXT_ECM:
vic.ctrl1 = VIC_CTRL1_DEN | VIC_CTRL1_ECM | VIC_CTRL1_RSEL | 3;
vic.ctrl2 = VIC_CTRL2_CSEL;
break;
case VICM_HIRES:
vic.ctrl1 = VIC_CTRL1_BMM | VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3;
vic.ctrl2 = VIC_CTRL2_CSEL;
break;
case VICM_HIRES_MC:
vic.ctrl1 = VIC_CTRL1_BMM | VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3;
vic.ctrl2 = VIC_CTRL2_CSEL | VIC_CTRL2_MCM;
break;
default:
__assume(false);
}
cia2.pra = (cia2.pra & 0xfc) | (((unsigned)text >> 14) ^ 0x03);
vic.memptr = (((unsigned)text >> 6) & 0xf0) | (((unsigned)font >> 10) & 0x0e);
}
bool vic_isBottom(void)
{
return (vic.ctrl1 & VIC_CTRL1_RST8) != 0;
}
void vic_waitBottom(void)
{
while (!(vic.ctrl1 & VIC_CTRL1_RST8))
;
}
void vic_waitTop(void)
{
while ((vic.ctrl1 & VIC_CTRL1_RST8))
;
}
void vic_waitFrame(void)
{
while ((vic.ctrl1 & VIC_CTRL1_RST8))
;
while (!(vic.ctrl1 & VIC_CTRL1_RST8))
;
}
void vic_waitFrames(char n)
{
while (n > 0)
{
vic_waitFrame();
n--;
}
}
void vic_waitLine(int line)
{
char upper = (char)(line >> 1) & VIC_CTRL1_RST8;
char lower = (char)line;
do
{
while (vic.raster != lower)
;
} while ((vic.ctrl1 & VIC_CTRL1_RST8) != upper);
}
void vic_waitBelow(int line)
{
char upper = (char)(line >> 1) & VIC_CTRL1_RST8;
char lower = (char)line;
if (upper)
{
do
{
while (vic.raster <= lower)
;
} while (!(vic.ctrl1 & VIC_CTRL1_RST8));
}
else
{
while (vic.raster <= lower)
;
}
}
void vic_waitRange(char below, char above)
{
while (vic.ctrl1 & VIC_CTRL1_RST8)
;
if (vic.raster >= above)
{
while (!(vic.ctrl1 & VIC_CTRL1_RST8))
;
while (vic.ctrl1 & VIC_CTRL1_RST8)
;
}
while (vic.raster < below)
;
}
#pragma native(vic_waitLine)
+135
View File
@@ -0,0 +1,135 @@
#ifndef C64_VIC_H
#define C64_VIC_H
#include "types.h"
#define VIC_CTRL1_RSEL 0x08
#define VIC_CTRL1_DEN 0x10
#define VIC_CTRL1_BMM 0x20
#define VIC_CTRL1_ECM 0x40
#define VIC_CTRL1_RST8 0x80
#define VIC_CTRL2_CSEL 0x08
#define VIC_CTRL2_MCM 0x10
#define VIC_CTRL2_RES 0x20
#define VIC_INTR_RST 0x01
#define VIC_INTR_MBC 0x02
#define VIC_INTR_MMC 0x04
#define VIC_INTR_ILP 0x08
#define VIC_INTR_IRQ 0x80
enum VICColors
{
VCOL_BLACK,
VCOL_WHITE,
VCOL_RED,
VCOL_CYAN,
VCOL_PURPLE,
VCOL_GREEN,
VCOL_BLUE,
VCOL_YELLOW,
VCOL_ORANGE,
VCOL_BROWN,
VCOL_LT_RED,
VCOL_DARK_GREY,
VCOL_MED_GREY,
VCOL_LT_GREEN,
VCOL_LT_BLUE,
VCOL_LT_GREY
};
struct VIC
{
struct XY
{
volatile byte x, y;
} spr_pos[8];
byte spr_msbx;
volatile byte ctrl1;
volatile byte raster;
volatile byte lpx, lpy;
volatile byte spr_enable;
volatile byte ctrl2;
volatile byte spr_expand_y;
volatile byte memptr;
volatile byte intr_ctrl;
volatile byte intr_enable;
volatile byte spr_priority;
volatile byte spr_multi;
volatile byte spr_expand_x;
volatile byte spr_sprcol;
volatile byte spr_backcol;
volatile byte color_border;
volatile byte color_back;
volatile byte color_back1;
volatile byte color_back2;
volatile byte color_back3;
volatile byte spr_mcolor0;
volatile byte spr_mcolor1;
volatile byte spr_color[8];
volatile byte ext_keymap;
volatile byte ext_2mhz;
volatile byte ext_uturbo;
};
// set the 16k Bank for the vic
// 0 : 0x0000..0x3fff
// 1 : 0x4000..0x7fff
// 2 : 0x8000..0xbfff
// 3 : 0xc000..0xffff
void vic_setbank(char bank);
enum VicMode
{
VICM_TEXT,
VICM_TEXT_MC,
VICM_TEXT_ECM,
VICM_HIRES,
VICM_HIRES_MC
};
// set the display mode and base address. This will also
// adapt the bank.
void vic_setmode(VicMode mode, const char * text, const char * font);
// put a sprite at the given x/y location, taking care of the
// x MSB
inline void vic_sprxy(byte s, int x, int y);
// Read the sprite x position from the LSB and MSB register
inline int vic_sprgetx(byte s);
// wait for the beam to reach the bottom of the visual area
inline void vic_waitBottom(void);
// wait for the beam to reach the top of the frame
inline void vic_waitTop(void);
// wait for the top of the frame and then for the bottom of the visual area
inline void vic_waitFrame(void);
// return true if the beam is below the frame
inline bool vic_isBottom(void);
// wait for n frames
void vic_waitFrames(char n);
// wait for a specific raster line
void vic_waitLine(int line);
// wait for beam to be below a line
void vic_waitBelow(int line);
// wait for beam to be in a given range on screen
void vic_waitRange(char below, char above);
// reference to the VIC chip
#define vic (*((struct VIC *)0xd000))
#pragma compile("vic.c")
#endif