first commit
This commit is contained in:
commit
a06137a6ee
52
.gitignore
vendored
Normal file
52
.gitignore
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
18
Makefile
Normal file
18
Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
CC=tcc
|
||||
CFLAGS= -c -Wall -g -Wextra
|
||||
SRC:=$(wildcard *.c)
|
||||
OBJ:=$(SRC:.c=.o)
|
||||
|
||||
all: gb
|
||||
|
||||
clean:
|
||||
make clean -C co/
|
||||
rm -f $(OBJ)
|
||||
rm -f gb
|
||||
|
||||
%.o: %.c gb.h
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
gb: $(OBJ)
|
||||
make all -C co/
|
||||
$(CC) co/*.o *.o -lucontext -lSDL2 -g -o gb
|
||||
15
co/Makefile
Normal file
15
co/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
CC=tcc
|
||||
CFLAGS= -c -Wall -g
|
||||
SRC:=$(wildcard *.c)
|
||||
OBJ:=$(SRC:.c=.o)
|
||||
|
||||
all: co
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
co: $(OBJ)
|
||||
|
||||
2
co/README.md
Normal file
2
co/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# coroutine library
|
||||
https://github.com/Hojun-Cho/coroutine
|
||||
356
co/channel.c
Normal file
356
co/channel.c
Normal file
@ -0,0 +1,356 @@
|
||||
#include "taskimpl.h"
|
||||
|
||||
Channel*
|
||||
newchan(int elemsize, int bufsize)
|
||||
{
|
||||
Channel* c;
|
||||
|
||||
c = malloc(sizeof *c + bufsize * elemsize);
|
||||
if (c == 0) {
|
||||
exit(1);
|
||||
}
|
||||
*c = (Channel){
|
||||
.elemsize = elemsize,
|
||||
.bufsize = bufsize,
|
||||
.nbuf = 0,
|
||||
.buf = (uchar*)&c[1],
|
||||
};
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
deletechan(Channel* c)
|
||||
{
|
||||
if (c == 0)
|
||||
return;
|
||||
free(c->name);
|
||||
free(c->asend.a);
|
||||
free(c->arecv.a);
|
||||
free(c);
|
||||
}
|
||||
|
||||
static void
|
||||
addarray(Altarray* a, Alt* alt)
|
||||
{
|
||||
if (a->n == a->m) {
|
||||
a->m += 16;
|
||||
a->a = realloc(a->a, a->m * sizeof a->a[0]);
|
||||
if (a->a == nil) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
a->a[a->n++] = alt;
|
||||
}
|
||||
|
||||
static void
|
||||
amove(void* dst, void* src, uint n)
|
||||
{
|
||||
if (dst) {
|
||||
if (src == nil)
|
||||
memset(dst, 0, n);
|
||||
else
|
||||
memmove(dst, src, n);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
delarray(Altarray* a, int i)
|
||||
{
|
||||
--a->n;
|
||||
a->a[i] = a->a[a->n];
|
||||
}
|
||||
|
||||
static enum chan_op
|
||||
otherop(enum chan_op op)
|
||||
{
|
||||
return (CHANSND + CHANRCV) - op;
|
||||
}
|
||||
|
||||
static Altarray*
|
||||
chanarray(Channel* c, enum chan_op op)
|
||||
{
|
||||
switch (op) {
|
||||
default:
|
||||
return nil;
|
||||
case CHANSND:
|
||||
return &c->asend;
|
||||
case CHANRCV:
|
||||
return &c->arecv;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 3 players: sender, reciver, 'channel itself'
|
||||
* if chann.empty
|
||||
* send(send to reciver);
|
||||
*/
|
||||
static void
|
||||
altcopy(Alt* s, Alt* r)
|
||||
{
|
||||
Channel* c;
|
||||
uchar* cp;
|
||||
|
||||
if (s == nil && r == nil)
|
||||
return;
|
||||
assert(s != nil); /* sender must not nil */
|
||||
c = s->c;
|
||||
if (s->op == CHANRCV) {
|
||||
Alt* t = s;
|
||||
s = r;
|
||||
r = t;
|
||||
}
|
||||
assert(s == nil || s->op == CHANSND); /* sender */
|
||||
assert(r == nil || r->op == CHANRCV); /* reciver */
|
||||
|
||||
/* Channel is unbufferd */
|
||||
if (s && r && c->nbuf == 0) {
|
||||
amove(r->v, s->v, c->elemsize);
|
||||
return;
|
||||
}
|
||||
if (r) {
|
||||
/* recive buffred data first */
|
||||
cp = c->buf + c->off * c->elemsize;
|
||||
amove(r->v, cp, c->elemsize);
|
||||
--c->nbuf;
|
||||
if (++c->off == c->bufsize)
|
||||
c->off = 0;
|
||||
}
|
||||
if (s) {
|
||||
cp = c->buf + (c->off + c->nbuf) % c->bufsize * c->elemsize;
|
||||
amove(cp, s->v, c->elemsize);
|
||||
++c->nbuf;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
altdeque(Alt* a)
|
||||
{
|
||||
Altarray* ar;
|
||||
|
||||
ar = chanarray(a->c, a->op);
|
||||
if (ar == nil) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < ar->n; ++i) {
|
||||
if (ar->a[i] == a) {
|
||||
delarray(ar, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* can't find self in altdq */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
altqueue(Alt* a)
|
||||
{
|
||||
Altarray* ar;
|
||||
|
||||
ar = chanarray(a->c, a->op);
|
||||
addarray(ar, a);
|
||||
}
|
||||
|
||||
static void
|
||||
altalldeque(Alt* a)
|
||||
{
|
||||
for (int i = 0; a[i].op != CHANEND && a[i].op != CHANNOBLK; i++) {
|
||||
if (a[i].op != CHANNOP)
|
||||
altdeque(&a[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
altexec(Alt* a)
|
||||
{
|
||||
Altarray* ar;
|
||||
Channel* c;
|
||||
|
||||
c = a->c;
|
||||
ar = chanarray(c, otherop(a->op));
|
||||
if (ar && ar->n) {
|
||||
int i = rand() % ar->n; /* find any other palyer */
|
||||
Alt* other = ar->a[i];
|
||||
altcopy(a, other); /* copy data to other's buffer */
|
||||
altalldeque(other->xalt); /* delete because now request is completed */
|
||||
other->xalt[0].xalt = other;
|
||||
taskready(other->task);
|
||||
} else
|
||||
altcopy(a, nil); /* if channel is unbufferd then do nothing */
|
||||
}
|
||||
|
||||
static int
|
||||
altcanexec(Alt* a)
|
||||
{
|
||||
Channel* c;
|
||||
|
||||
if (a->op == CHANNOP)
|
||||
return 0;
|
||||
c = a->c;
|
||||
if (c->bufsize == 0) {
|
||||
Altarray* ar = chanarray(c, otherop(a->op));
|
||||
return ar && ar->n;
|
||||
}
|
||||
switch (a->op) {
|
||||
default:
|
||||
return 0;
|
||||
case CHANSND:
|
||||
return c->nbuf < c->bufsize;
|
||||
case CHANRCV:
|
||||
return c->nbuf > 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
chanalt(Alt* a)
|
||||
{
|
||||
int i, j, ncan, n, canblock;
|
||||
Task* t;
|
||||
|
||||
assertstack(512);
|
||||
for (i = 0; a[i].op != CHANEND && a[i].op != CHANNOBLK; ++i)
|
||||
;
|
||||
n = i;
|
||||
canblock = (a[i].op == CHANEND);
|
||||
t = taskrunning;
|
||||
for (i = 0; i < n; ++i) {
|
||||
a[i].task = t;
|
||||
a[i].xalt = a;
|
||||
}
|
||||
ncan = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (altcanexec(&a[i])) {
|
||||
ncan++;
|
||||
}
|
||||
}
|
||||
if (ncan) {
|
||||
j = rand() % ncan;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (altcanexec(&a[i])) {
|
||||
if (j-- == 0) {
|
||||
altexec(&a[i]);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canblock == 0)
|
||||
return -1;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (a[i].op != CHANNOP)
|
||||
altqueue(&a[i]);
|
||||
}
|
||||
taskswitch();
|
||||
return a[0].xalt - a; /* calculate offset */
|
||||
}
|
||||
|
||||
static int
|
||||
_chanop(Channel* c, int op, void* p, int canblock)
|
||||
{
|
||||
Alt ar[2];
|
||||
|
||||
ar[0] = (Alt){
|
||||
.c = c,
|
||||
.op = op,
|
||||
.v = p,
|
||||
};
|
||||
ar[1] = (Alt){
|
||||
.op = canblock ? CHANEND : CHANNOBLK,
|
||||
};
|
||||
if (chanalt(ar) < 0)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
chansend(Channel* c, void* v)
|
||||
{
|
||||
return _chanop(c, CHANSND, v, 1);
|
||||
}
|
||||
|
||||
int
|
||||
chanbsend(Channel* c, void* v)
|
||||
{
|
||||
return _chanop(c, CHANSND, v, 0);
|
||||
}
|
||||
|
||||
int
|
||||
chanrecv(Channel* c, void* v)
|
||||
{
|
||||
return _chanop(c, CHANRCV, v, 1);
|
||||
}
|
||||
|
||||
int
|
||||
chanbrecv(Channel* c, void* v)
|
||||
{
|
||||
return _chanop(c, CHANSND, v, 0);
|
||||
}
|
||||
|
||||
int
|
||||
chansendp(Channel* c, void* v)
|
||||
{
|
||||
return _chanop(c, CHANSND, &v, 1);
|
||||
}
|
||||
|
||||
void*
|
||||
chanrecvp(Channel* c)
|
||||
{
|
||||
void* v;
|
||||
|
||||
_chanop(c, CHANRCV, &v, 1);
|
||||
return v;
|
||||
}
|
||||
|
||||
int
|
||||
channbsendp(Channel* c, void* v)
|
||||
{
|
||||
return _chanop(c, CHANSND, (void*)&v, 0);
|
||||
}
|
||||
|
||||
void*
|
||||
channbrecvp(Channel* c)
|
||||
{
|
||||
void* v;
|
||||
|
||||
_chanop(c, CHANRCV, &v, 0);
|
||||
return v;
|
||||
}
|
||||
|
||||
int
|
||||
chansendul(Channel* c, ulong v)
|
||||
{
|
||||
return _chanop(c, CHANSND, &v, 1);
|
||||
}
|
||||
|
||||
ulong
|
||||
chanrecvul(Channel* c)
|
||||
{
|
||||
ulong v;
|
||||
|
||||
_chanop(c, CHANRCV, &v, 1);
|
||||
return v;
|
||||
}
|
||||
|
||||
int
|
||||
channbsendul(Channel* c, ulong v)
|
||||
{
|
||||
return _chanop(c, CHANSND, &v, 0);
|
||||
}
|
||||
|
||||
int
|
||||
channbrecvul(Channel* c, ulong v)
|
||||
{
|
||||
return _chanop(c, CHANRCV, &v, 0);
|
||||
}
|
||||
|
||||
int
|
||||
channbsend(Channel* c, void* v)
|
||||
{
|
||||
return _chanop(c, CHANSND, v, 0);
|
||||
}
|
||||
|
||||
int
|
||||
channbrecv(Channel* c, void* v)
|
||||
{
|
||||
return _chanop(c, CHANRCV, v, 0);
|
||||
}
|
||||
60
co/qlock.c
Normal file
60
co/qlock.c
Normal file
@ -0,0 +1,60 @@
|
||||
#include "taskimpl.h"
|
||||
|
||||
int
|
||||
_qlock(QLock* l, int block)
|
||||
{
|
||||
if (l->owner == nil) {
|
||||
l->owner = taskrunning;
|
||||
return 1;
|
||||
}
|
||||
if (!block)
|
||||
return 0;
|
||||
addtask(&l->waiting, taskrunning);
|
||||
taskswitch(); /* wait until own lock */
|
||||
if (l->owner != taskrunning) {
|
||||
/* TODO: */
|
||||
exit(1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
qlock(QLock* l)
|
||||
{
|
||||
_qlock(l, 1);
|
||||
}
|
||||
|
||||
int
|
||||
canqlock(QLock* l)
|
||||
{
|
||||
return _qlock(l, 0);
|
||||
}
|
||||
|
||||
void
|
||||
qunlock(QLock* l)
|
||||
{
|
||||
Task* ready;
|
||||
|
||||
if (l->owner == nil) {
|
||||
/* TODO: */
|
||||
exit(1);
|
||||
}
|
||||
l->owner = ready = l->waiting.head;
|
||||
if (l->owner != nil) {
|
||||
deltask(&l->waiting, ready);
|
||||
taskready(ready);
|
||||
}
|
||||
}
|
||||
|
||||
QLock*
|
||||
newqlock()
|
||||
{
|
||||
QLock* l;
|
||||
|
||||
l = malloc(sizeof *l);
|
||||
if (l == nil)
|
||||
exit(1);
|
||||
l->owner = 0;
|
||||
l->waiting = (Tasklist){ 0 };
|
||||
return l;
|
||||
}
|
||||
54
co/rendez.c
Normal file
54
co/rendez.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include "taskimpl.h"
|
||||
|
||||
void
|
||||
tasksleep(Rendez* r)
|
||||
{
|
||||
addtask(&r->waiting, taskrunning);
|
||||
if (r->l)
|
||||
qunlock(r->l);
|
||||
taskswitch();
|
||||
if (r->l)
|
||||
qlock(r->l);
|
||||
}
|
||||
|
||||
static int
|
||||
_taskwakeup(Rendez* r, int all)
|
||||
{
|
||||
int i;
|
||||
Task* t;
|
||||
|
||||
for (i = 0;; ++i) {
|
||||
if (i == 1 && !all)
|
||||
break;
|
||||
if ((t = r->waiting.head) == nil)
|
||||
break;
|
||||
deltask(&r->waiting, t);
|
||||
taskready(t);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
taskwakeup(Rendez* r)
|
||||
{
|
||||
return _taskwakeup(r, 0);
|
||||
}
|
||||
|
||||
int
|
||||
taskwakeupall(Rendez* r)
|
||||
{
|
||||
return _taskwakeup(r, 1);
|
||||
}
|
||||
|
||||
Rendez*
|
||||
newrendez(QLock* l)
|
||||
{
|
||||
Rendez* r;
|
||||
|
||||
r = malloc(sizeof *r);
|
||||
if (r == nil)
|
||||
exit(1);
|
||||
r->l = l;
|
||||
r->waiting = (Tasklist){ 0 };
|
||||
return r;
|
||||
}
|
||||
253
co/task.c
Normal file
253
co/task.c
Normal file
@ -0,0 +1,253 @@
|
||||
#include "taskimpl.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int taskcount;
|
||||
int taskidgen;
|
||||
int tasknswitch;
|
||||
int taskexitval;
|
||||
|
||||
Task* taskrunning;
|
||||
ucontext_t taskschedcontext;
|
||||
Tasklist taskrunqueue;
|
||||
Task** alltask;
|
||||
int nalltask;
|
||||
|
||||
static void
|
||||
contextswitch(ucontext_t* from, ucontext_t* to);
|
||||
void
|
||||
assertstack(uint n);
|
||||
|
||||
static void
|
||||
taskinfo(int s)
|
||||
{
|
||||
int i;
|
||||
Task* t;
|
||||
char* extra;
|
||||
|
||||
for (i = 0; i < nalltask; ++i) {
|
||||
t = alltask[i];
|
||||
if (t == taskrunning)
|
||||
extra = " (running)";
|
||||
else if (t->ready)
|
||||
extra = " (ready)";
|
||||
else
|
||||
extra = "";
|
||||
printf("%s\n", extra);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
taskstart(uint x, uint y)
|
||||
{
|
||||
Task* t;
|
||||
ulong z;
|
||||
|
||||
z = x << 16;
|
||||
z <<= 16;
|
||||
z |= y;
|
||||
t = (Task*)z;
|
||||
t->startfn(t->startarg);
|
||||
taskexit(0);
|
||||
}
|
||||
|
||||
static Task*
|
||||
taskalloc(void (*fn)(void*), void* arg, uint stk)
|
||||
{
|
||||
Task* t;
|
||||
sigset_t zero;
|
||||
uint x, y;
|
||||
ulong z;
|
||||
|
||||
if ((t = malloc(sizeof *t + stk)) == nil) {
|
||||
exit(1);
|
||||
}
|
||||
*t = (Task){
|
||||
.stk = (uchar*)(&t[1]),
|
||||
.stksize = stk,
|
||||
.id = ++taskidgen,
|
||||
.startfn = fn,
|
||||
.startarg = arg,
|
||||
};
|
||||
sigemptyset(&zero);
|
||||
sigprocmask(SIG_BLOCK, &zero, &t->uc.uc_sigmask);
|
||||
if (getcontext(&t->uc)) {
|
||||
exit(1);
|
||||
}
|
||||
t->uc.uc_stack.ss_sp = t->stk + 8;
|
||||
t->uc.uc_stack.ss_size = t->stksize - 64;
|
||||
|
||||
z = (ulong)t;
|
||||
y = z;
|
||||
z >>= 16;
|
||||
x = z >> 16;
|
||||
makecontext(&t->uc, (void (*)())taskstart, 2, x, y);
|
||||
return t;
|
||||
}
|
||||
|
||||
int
|
||||
taskcreate(void (*fn)(void*), void* arg, uint stk)
|
||||
{
|
||||
int id;
|
||||
Task* t;
|
||||
|
||||
t = taskalloc(fn, arg, stk);
|
||||
taskcount++;
|
||||
id = t->id;
|
||||
if (nalltask % 64 == 0) {
|
||||
alltask = realloc(alltask, (nalltask + 64) * sizeof(alltask[0]));
|
||||
if (alltask == 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
t->alltaskslot = nalltask;
|
||||
alltask[nalltask++] = t;
|
||||
taskready(t);
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
taskready(Task* t)
|
||||
{
|
||||
t->ready = 1;
|
||||
addtask(&taskrunqueue, t);
|
||||
}
|
||||
|
||||
void
|
||||
taskswitch(void)
|
||||
{
|
||||
assertstack(0);
|
||||
contextswitch(&taskrunning->uc, &taskschedcontext);
|
||||
}
|
||||
|
||||
int
|
||||
taskyield(void)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = tasknswitch;
|
||||
taskready(taskrunning);
|
||||
taskswitch();
|
||||
return tasknswitch - n - 1;
|
||||
}
|
||||
|
||||
void
|
||||
taskexit(int val)
|
||||
{
|
||||
taskexitval = val;
|
||||
taskrunning->exiting = 1;
|
||||
taskswitch();
|
||||
}
|
||||
|
||||
void
|
||||
taskexitall(int val)
|
||||
{
|
||||
exit(val);
|
||||
}
|
||||
|
||||
void
|
||||
deltask(Tasklist* l, Task* t)
|
||||
{
|
||||
if (t->prev)
|
||||
t->prev->next = t->next;
|
||||
else
|
||||
l->head = t->next;
|
||||
if (t->next)
|
||||
t->next->prev = t->prev;
|
||||
else
|
||||
l->tail = t->prev;
|
||||
}
|
||||
|
||||
void
|
||||
addtask(Tasklist* l, Task* t)
|
||||
{
|
||||
if (l->tail) {
|
||||
l->tail->next = t;
|
||||
t->prev = l->tail;
|
||||
} else {
|
||||
l->head = t;
|
||||
t->prev = nil;
|
||||
}
|
||||
l->tail = t;
|
||||
t->next = nil;
|
||||
}
|
||||
|
||||
static void
|
||||
contextswitch(ucontext_t* from, ucontext_t* to)
|
||||
{
|
||||
if (swapcontext(from, to) < 0) {
|
||||
printf("swapcontext is fail\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
assertstack(uint n)
|
||||
{
|
||||
Task* t;
|
||||
|
||||
t = taskrunning;
|
||||
if ((uchar*)&t <= (uchar*)t->stk || (uchar*)&t - (uchar*)t->stk < 256 + n) {
|
||||
/* satck over flow */
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
taskscheduler(void)
|
||||
{
|
||||
int i;
|
||||
Task* t;
|
||||
|
||||
for (;;) {
|
||||
if (taskcount == 0)
|
||||
exit(taskexitval);
|
||||
t = taskrunqueue.head;
|
||||
if (t == nil) {
|
||||
/* nothing to do */
|
||||
exit(1);
|
||||
}
|
||||
deltask(&taskrunqueue, t); /* delete from runqueue */
|
||||
t->ready = 0;
|
||||
taskrunning = t;
|
||||
tasknswitch++;
|
||||
contextswitch(&taskschedcontext, &t->uc);
|
||||
taskrunning = nil; /* ready for next task */
|
||||
if (t->exiting) {
|
||||
taskcount--;
|
||||
i = t->alltaskslot;
|
||||
alltask[i] = alltask[--nalltask];
|
||||
alltask[i]->alltaskslot = i;
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char* argv0;
|
||||
static int taskargc;
|
||||
static char** taskargv;
|
||||
int mainstacksize;
|
||||
|
||||
static void
|
||||
taskmainstart(void* v)
|
||||
{
|
||||
taskmain(taskargc, taskargv);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
struct sigaction sa, osa;
|
||||
|
||||
memset(&sa, 0, sizeof(sigaction));
|
||||
sa.sa_handler = taskinfo;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigaction(SIGQUIT, &sa, &osa);
|
||||
/*sigaction(SIGINFO, &sa, &osa);*/
|
||||
argv0 = argv[0];
|
||||
taskargc = argc;
|
||||
taskargv = argv;
|
||||
mainstacksize = 256 * 1024;
|
||||
taskcreate(taskmainstart, nil, mainstacksize);
|
||||
taskscheduler();
|
||||
exit(0);
|
||||
}
|
||||
138
co/task.h
Normal file
138
co/task.h
Normal file
@ -0,0 +1,138 @@
|
||||
#ifndef _TASK_H_
|
||||
#define _TASK_H_
|
||||
|
||||
typedef struct Task Task;
|
||||
typedef struct Tasklist Tasklist;
|
||||
|
||||
struct Tasklist
|
||||
{
|
||||
Task* head;
|
||||
Task* tail;
|
||||
};
|
||||
|
||||
typedef struct QLock
|
||||
{
|
||||
Task* owner;
|
||||
Tasklist waiting;
|
||||
} QLock;
|
||||
|
||||
void
|
||||
taskmain(int argc, char** argv);
|
||||
int
|
||||
taskyield(void);
|
||||
void
|
||||
taskexitall(int val);
|
||||
int
|
||||
taskcreate(void (*fn)(void*), void* arg, unsigned int stk);
|
||||
void
|
||||
taskready(Task* t);
|
||||
void
|
||||
taskexit(int val);
|
||||
void
|
||||
taskswitch(void);
|
||||
void
|
||||
assertstack(unsigned int n);
|
||||
|
||||
void
|
||||
qlock(QLock*);
|
||||
int
|
||||
canqlock(QLock*);
|
||||
void
|
||||
qunlock(QLock*);
|
||||
QLock*
|
||||
newqlock();
|
||||
|
||||
typedef struct Rendez Rendez;
|
||||
struct Rendez
|
||||
{
|
||||
QLock* l;
|
||||
Tasklist waiting;
|
||||
};
|
||||
|
||||
void
|
||||
tasksleep(Rendez*);
|
||||
int
|
||||
taskwakeup(Rendez*);
|
||||
int
|
||||
taskwakeupall(Rendez*);
|
||||
Rendez*
|
||||
newrendez(QLock* l);
|
||||
|
||||
extern Task* taskrunning;
|
||||
extern int taskcount;
|
||||
|
||||
typedef struct Alt Alt;
|
||||
typedef struct Altarray Altarray;
|
||||
typedef struct Channel Channel;
|
||||
|
||||
enum chan_op
|
||||
{
|
||||
CHANEND,
|
||||
CHANSND,
|
||||
CHANRCV,
|
||||
CHANNOP,
|
||||
CHANNOBLK,
|
||||
};
|
||||
|
||||
struct Alt
|
||||
{
|
||||
Channel* c;
|
||||
void* v;
|
||||
enum chan_op op;
|
||||
Task* task;
|
||||
Alt* xalt;
|
||||
};
|
||||
|
||||
struct Altarray
|
||||
{
|
||||
Alt** a;
|
||||
unsigned int n;
|
||||
unsigned int m;
|
||||
};
|
||||
|
||||
struct Channel
|
||||
{
|
||||
unsigned int bufsize;
|
||||
unsigned int elemsize;
|
||||
unsigned char* buf;
|
||||
unsigned int nbuf;
|
||||
unsigned int off;
|
||||
Altarray asend;
|
||||
Altarray arecv;
|
||||
char* name;
|
||||
};
|
||||
|
||||
Channel*
|
||||
newchan(int elemsize, int bufsize);
|
||||
void
|
||||
deletechan(Channel* c);
|
||||
int
|
||||
chansend(Channel* c, void* v);
|
||||
int
|
||||
chanbsend(Channel* c, void* v);
|
||||
int
|
||||
chanrecv(Channel* c, void* v);
|
||||
int
|
||||
chanbrecv(Channel* c, void* v);
|
||||
int
|
||||
chansendp(Channel* c, void* v);
|
||||
void*
|
||||
chanrecvp(Channel* c);
|
||||
int
|
||||
channbsend(Channel* c, void* v);
|
||||
int
|
||||
channbsendp(Channel* c, void* v);
|
||||
void*
|
||||
channbrecvp(Channel* c);
|
||||
int
|
||||
channbrecv(Channel* c, void* v);
|
||||
int
|
||||
chansendul(Channel* c, unsigned long v);
|
||||
unsigned long
|
||||
chanrecvul(Channel* c);
|
||||
int
|
||||
channbsendul(Channel* c, unsigned long v);
|
||||
int
|
||||
channbrecvul(Channel* c, unsigned long v);
|
||||
|
||||
#endif
|
||||
44
co/taskimpl.h
Normal file
44
co/taskimpl.h
Normal file
@ -0,0 +1,44 @@
|
||||
#include "task.h"
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#define nil ((void*)0)
|
||||
#define nelem(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned char uchar;
|
||||
typedef unsigned long long uvlong;
|
||||
typedef long long vlong;
|
||||
|
||||
enum
|
||||
{
|
||||
STACK = 8192,
|
||||
};
|
||||
|
||||
struct Task
|
||||
{
|
||||
Task* next;
|
||||
Task* prev;
|
||||
Task* allnext;
|
||||
Task* allprev;
|
||||
ucontext_t uc;
|
||||
uvlong alarmtime;
|
||||
uint id;
|
||||
uchar* stk;
|
||||
uint stksize;
|
||||
int exiting;
|
||||
int alltaskslot;
|
||||
int ready;
|
||||
void (*startfn)(void*);
|
||||
void* startarg;
|
||||
};
|
||||
|
||||
void
|
||||
deltask(Tasklist* l, Task* t);
|
||||
|
||||
void
|
||||
addtask(Tasklist* l, Task* t);
|
||||
647
cpu.c
Normal file
647
cpu.c
Normal file
@ -0,0 +1,647 @@
|
||||
#include "gb.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define BC() ((u16)r[rB] << 8 | r[rC])
|
||||
#define DE() ((u16)r[rD] << 8 | r[rE])
|
||||
#define HL() ((u16)r[rH] << 8 | r[rL])
|
||||
|
||||
enum
|
||||
{
|
||||
FLAGC = 0x10,
|
||||
FLAGH = 0x20,
|
||||
FLAGN = 0x40,
|
||||
FLAGZ = 0x80,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
rB,
|
||||
rC,
|
||||
rD,
|
||||
rE,
|
||||
rH,
|
||||
rL,
|
||||
rHL,
|
||||
rA,
|
||||
rF = rHL,
|
||||
};
|
||||
|
||||
u8 r[8], ime;
|
||||
u16 pc, sp, curpc;
|
||||
int halt;
|
||||
|
||||
void
|
||||
state(void)
|
||||
{
|
||||
printf("A:%02X F:%02X B:%02X C:%02X D:%02X E:%02X H:%02X L:%02X SP:%04X "
|
||||
"PC:%04X PCMEM:%02X,%02X,%02X,%02X\n",
|
||||
r[rA],
|
||||
r[rF],
|
||||
r[rB],
|
||||
r[rC],
|
||||
r[rD],
|
||||
r[rE],
|
||||
r[rH],
|
||||
r[rL],
|
||||
sp,
|
||||
pc - 1,
|
||||
memread(pc - 1),
|
||||
memread(pc),
|
||||
memread(pc + 1),
|
||||
memread(pc + 2));
|
||||
}
|
||||
|
||||
static u8
|
||||
fetch8(void)
|
||||
{
|
||||
return memread(pc++);
|
||||
}
|
||||
|
||||
static u16
|
||||
fetch16(void)
|
||||
{
|
||||
u16 u = memread(pc++);
|
||||
return u | memread(pc++) << 8;
|
||||
}
|
||||
|
||||
static void
|
||||
push8(u8 u)
|
||||
{
|
||||
memwrite(--sp, u);
|
||||
}
|
||||
|
||||
static void
|
||||
push16(u16 u)
|
||||
{
|
||||
memwrite(--sp, u >> 8);
|
||||
memwrite(--sp, u);
|
||||
}
|
||||
|
||||
static u8
|
||||
pop8(void)
|
||||
{
|
||||
return memread(sp++);
|
||||
}
|
||||
|
||||
static u16
|
||||
pop16(void)
|
||||
{
|
||||
u16 v = memread(sp++);
|
||||
return v | memread(sp++) << 8;
|
||||
}
|
||||
|
||||
static void
|
||||
write16(u16 n, u16 v)
|
||||
{
|
||||
memwrite(n++, v);
|
||||
memwrite(n, v >> 8);
|
||||
}
|
||||
|
||||
static int
|
||||
move(u8 dst, u8 src)
|
||||
{
|
||||
if (dst == rHL) {
|
||||
if (src == rHL) {
|
||||
halt = 1;
|
||||
return 4;
|
||||
}
|
||||
memwrite(HL(), r[src]);
|
||||
return 8;
|
||||
}
|
||||
if (src == rHL) {
|
||||
r[dst] = memread(HL());
|
||||
return 8;
|
||||
}
|
||||
r[dst] = r[src];
|
||||
return 4;
|
||||
}
|
||||
|
||||
static int
|
||||
alu(u8 op, u8 n)
|
||||
{
|
||||
u8 v4, u;
|
||||
u16 v = 0;
|
||||
int t;
|
||||
|
||||
switch (n) {
|
||||
case 8:
|
||||
u = fetch8();
|
||||
t = 8;
|
||||
break;
|
||||
case rHL:
|
||||
u = memread(HL());
|
||||
t = 8;
|
||||
break;
|
||||
default:
|
||||
u = r[n];
|
||||
t = 4;
|
||||
break;
|
||||
}
|
||||
switch (op) {
|
||||
default:
|
||||
v4 = (r[rA] & 0x0F) + (u & 0x0F);
|
||||
v = r[rA] + u;
|
||||
break;
|
||||
case 1:
|
||||
v4 = (r[rA] & 0x0F) + (u & 0x0F) + (r[rF] >> 4 & 1);
|
||||
v = r[rA] + u + (r[rF] >> 4 & 1);
|
||||
break;
|
||||
case 2:
|
||||
case 7:
|
||||
v4 = (r[rA] & 0x0F) + (~u & 0x0F) + 1;
|
||||
v = r[rA] + (u ^ 0xFF) + 1;
|
||||
break;
|
||||
case 3:
|
||||
v4 = (r[rA] & 0x0F) + (~u & 0x0F) + (~r[rF] >> 4 & 1);
|
||||
v = r[rA] + (u ^ 0xFF) + (~r[rF] >> 4 & 1);
|
||||
break;
|
||||
case 4:
|
||||
v = r[rA] & u;
|
||||
break;
|
||||
case 5:
|
||||
v = r[rA] ^ u;
|
||||
break;
|
||||
case 6:
|
||||
v = r[rA] | u;
|
||||
break;
|
||||
}
|
||||
r[rF] = 0;
|
||||
if ((u8)v == 0)
|
||||
r[rF] |= FLAGZ;
|
||||
if (op < 2) {
|
||||
if ((v & 0x100) != 0)
|
||||
r[rF] |= FLAGC;
|
||||
if ((v4 & 0x10) != 0)
|
||||
r[rF] |= FLAGH;
|
||||
} else if (op < 4 || op == 7) {
|
||||
r[rF] |= FLAGN;
|
||||
if ((v & 0x100) == 0)
|
||||
r[rF] |= FLAGC;
|
||||
if ((v4 & 0x10) == 0)
|
||||
r[rF] |= FLAGH;
|
||||
} else if (op == 4) {
|
||||
r[rF] |= FLAGH;
|
||||
}
|
||||
if (op != 7)
|
||||
r[rA] = v;
|
||||
return t;
|
||||
}
|
||||
|
||||
static int
|
||||
branch(int cc, int t)
|
||||
{
|
||||
u16 v = (i8)fetch8();
|
||||
if (cc == 0)
|
||||
return t + 8;
|
||||
pc += v;
|
||||
return t + 12;
|
||||
}
|
||||
|
||||
static u8
|
||||
inc(u8 v)
|
||||
{
|
||||
r[rF] &= FLAGC;
|
||||
++v;
|
||||
if (v == 0)
|
||||
r[rF] |= FLAGZ;
|
||||
if ((v & 0xF) == 0)
|
||||
r[rF] |= FLAGH;
|
||||
return v;
|
||||
}
|
||||
|
||||
static u8
|
||||
dec(u8 v)
|
||||
{
|
||||
r[rF] = (r[rF] & FLAGC) | FLAGN;
|
||||
--v;
|
||||
if (v == 0)
|
||||
r[rF] |= FLAGZ;
|
||||
if ((v & 0xF) == 0xF)
|
||||
r[rF] |= FLAGH;
|
||||
return v;
|
||||
}
|
||||
|
||||
static u8
|
||||
addhl(u16 u)
|
||||
{
|
||||
r[rF] &= ~(FLAGN | FLAGC | FLAGH);
|
||||
u32 v = HL() + u;
|
||||
if ((v & 0x10000) != 0)
|
||||
r[rF] |= FLAGC;
|
||||
if ((HL() & 0xFFF) + (u & 0xFFF) >= 0x1000)
|
||||
r[rF] |= FLAGH;
|
||||
r[rL] = v;
|
||||
r[rH] = v >> 8;
|
||||
return 8;
|
||||
}
|
||||
|
||||
static int
|
||||
jump(int cc)
|
||||
{
|
||||
u16 v = fetch16();
|
||||
if (cc == 0)
|
||||
return 12;
|
||||
pc = v;
|
||||
return 16;
|
||||
}
|
||||
|
||||
static int
|
||||
call(u16 a, int cc)
|
||||
{
|
||||
if (cc == 0)
|
||||
return 12;
|
||||
push16(pc);
|
||||
pc = a;
|
||||
if (cc < 0)
|
||||
return 16;
|
||||
return 24;
|
||||
}
|
||||
|
||||
static int
|
||||
bits(void)
|
||||
{
|
||||
u8 op = fetch8();
|
||||
u8 n = op & 7;
|
||||
u8 m = op >> 3 & 7;
|
||||
u16 a = HL();
|
||||
u8 v;
|
||||
int t;
|
||||
if (n == 6) {
|
||||
v = memread(a);
|
||||
t = 16;
|
||||
} else {
|
||||
v = r[n];
|
||||
t = 8;
|
||||
}
|
||||
u8 c;
|
||||
switch (op >> 6) {
|
||||
case 0:
|
||||
c = r[rF] >> 4 & 1;
|
||||
switch (m) {
|
||||
default:
|
||||
r[rF] = v >> 3 & 0x10;
|
||||
v = v << 1 | v >> 7;
|
||||
break;
|
||||
case 1:
|
||||
r[rF] = v << 4 & 0x10;
|
||||
v = v >> 1 | v << 7;
|
||||
break;
|
||||
case 2:
|
||||
r[rF] = v >> 3 & 0x10;
|
||||
v = v << 1 | c;
|
||||
break;
|
||||
case 3:
|
||||
r[rF] = v << 4 & 0x10;
|
||||
v = v >> 1 | c << 7;
|
||||
break;
|
||||
case 4:
|
||||
r[rF] = v >> 3 & 0x10;
|
||||
v = v << 1;
|
||||
break;
|
||||
case 5:
|
||||
r[rF] = v << 4 & 0x10;
|
||||
v = (v & 0x80) | v >> 1;
|
||||
break;
|
||||
case 6:
|
||||
r[rF] = v << 0;
|
||||
v = v << 4 | v >> 4;
|
||||
break;
|
||||
case 7:
|
||||
r[rF] = v << 4 & 0x10;
|
||||
v >>= 1;
|
||||
break;
|
||||
}
|
||||
if (v == 0)
|
||||
r[rF] |= FLAGZ;
|
||||
break;
|
||||
case 1:
|
||||
r[rF] = (r[rF] & ~(FLAGN | FLAGZ)) | FLAGH;
|
||||
if ((v & 1 << m) == 0)
|
||||
r[rF] |= FLAGZ;
|
||||
if (n == 6)
|
||||
t = 12;
|
||||
return t;
|
||||
case 2:
|
||||
v &= ~(1 << m);
|
||||
break;
|
||||
case 3:
|
||||
v |= (1 << m);
|
||||
}
|
||||
if (n == 6) {
|
||||
memwrite(a, v);
|
||||
} else {
|
||||
r[n] = v;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
reset(void)
|
||||
{
|
||||
r[rA] = 0x01;
|
||||
r[rF] = 0xb0;
|
||||
r[rC] = 0x13;
|
||||
r[rE] = 0xd8;
|
||||
r[rL] = 0x4d;
|
||||
r[rH] = 0x01;
|
||||
sp = 0xfffe;
|
||||
pc = 0x100;
|
||||
}
|
||||
|
||||
int
|
||||
step(void)
|
||||
{
|
||||
u8 op, v4;
|
||||
u16 v, w;
|
||||
i8 s;
|
||||
|
||||
if (halt) {
|
||||
if (reg[IF] & reg[IE])
|
||||
halt = 0;
|
||||
else
|
||||
return 4;
|
||||
}
|
||||
if ((reg[IF] & reg[IE]) != 0 && ime != 0) {
|
||||
push16(pc);
|
||||
ime = 0;
|
||||
v4 = reg[IF] & reg[IE];
|
||||
v4 &= -v4;
|
||||
reg[IF] &= ~v4;
|
||||
for (pc = 0x40; v4 != 1; pc += 8)
|
||||
v4 >>= 1;
|
||||
/* 2nop + push + changepc */
|
||||
return 20;
|
||||
}
|
||||
curpc = pc;
|
||||
op = fetch8();
|
||||
switch (op >> 6) {
|
||||
case 1:
|
||||
return move(op >> 3 & 7, op & 7);
|
||||
case 2:
|
||||
return alu(op >> 3 & 7, op & 7);
|
||||
}
|
||||
switch (op) {
|
||||
default: state(); panic("Unkown opcode OPCODE:[%X]", op);
|
||||
case 0x00: return 4;
|
||||
case 0x10: return 4;
|
||||
case 0x20: return branch((r[rF] & FLAGZ) == 0, 0);
|
||||
case 0x30: return branch((r[rF] & FLAGC) == 0, 0);
|
||||
case 0x01:
|
||||
r[rC] = fetch8();
|
||||
r[rB] = fetch8();
|
||||
return 12;
|
||||
case 0x11:
|
||||
r[rE] = fetch8();
|
||||
r[rD] = fetch8();
|
||||
return 12;
|
||||
case 0x21:
|
||||
r[rL] = fetch8();
|
||||
r[rH] = fetch8();
|
||||
return 12;
|
||||
case 0x31: sp = fetch16(); return 12;
|
||||
case 0x02: memwrite(BC(), r[rA]); return 8;
|
||||
case 0x12: memwrite(DE(), r[rA]); return 8;
|
||||
case 0x22:
|
||||
memwrite(HL(), r[rA]);
|
||||
if (++r[rL] == 0)
|
||||
r[rH]++;
|
||||
return 8;
|
||||
case 0x32:
|
||||
memwrite(HL(), r[rA]);
|
||||
if (r[rL]-- == 0)
|
||||
r[rH]--;
|
||||
return 8;
|
||||
case 0x03:
|
||||
if (++r[rC] == 0)
|
||||
r[rB]++;
|
||||
return 8;
|
||||
case 0x13:
|
||||
if (++r[rE] == 0)
|
||||
r[rD]++;
|
||||
return 8;
|
||||
case 0x23:
|
||||
if (++r[rL] == 0)
|
||||
r[rH]++;
|
||||
return 8;
|
||||
case 0x33: ++sp; return 8;
|
||||
case 0x04: inc(r[rB]++); return 4;
|
||||
case 0x14: inc(r[rD]++); return 4;
|
||||
case 0x24: inc(r[rH]++); return 4;
|
||||
case 0x34: memwrite(HL(), inc(memread(HL()))); return 12;
|
||||
case 0x05: dec(r[rB]--); return 4;
|
||||
case 0x15: dec(r[rD]--); return 4;
|
||||
case 0x25: dec(r[rH]--); return 4;
|
||||
case 0x35: memwrite(HL(), dec(memread(HL()))); return 12;
|
||||
case 0x06: r[rB] = fetch8(); return 8;
|
||||
case 0x16: r[rD] = fetch8(); return 8;
|
||||
case 0x26: r[rH] = fetch8(); return 8;
|
||||
case 0x36: memwrite(HL(), fetch8()); return 12;
|
||||
case 0x07:
|
||||
r[rF] = r[rA] >> 3 & 0x10;
|
||||
r[rA] = r[rA] << 1 | r[rA] >> 7;
|
||||
return 4;
|
||||
case 0x17:
|
||||
v = r[rF] >> 4 & 1;
|
||||
r[rF] = r[rA] >> 3 & 0x10;
|
||||
r[rA] = r[rA] << 1 | v;
|
||||
return 4;
|
||||
case 0x27:
|
||||
if ((r[rA] > 0x99 && (r[rF] & FLAGN) == 0) || (r[rF] & FLAGC) != 0) {
|
||||
r[rF] |= FLAGC;
|
||||
v = 0x60;
|
||||
} else {
|
||||
r[rF] &= ~FLAGC;
|
||||
v = 0;
|
||||
}
|
||||
if (((r[rA] & 0xF) > 9 && (r[rF] & FLAGN) == 0) || (r[rF] & FLAGH) != 0) {
|
||||
v |= 6;
|
||||
}
|
||||
if ((r[rF] & FLAGN) != 0) {
|
||||
r[rA] -= v;
|
||||
} else {
|
||||
r[rA] += v;
|
||||
}
|
||||
r[rF] &= ~(FLAGZ | FLAGH);
|
||||
if (r[rA] == 0)
|
||||
r[rF] |= FLAGZ;
|
||||
return 4;
|
||||
case 0x37: r[rF] = (r[rF] & ~(FLAGN | FLAGH)) | FLAGC; return 4;
|
||||
case 0x08: write16(fetch16(), sp); return 20;
|
||||
case 0x18: return branch(1, 0);
|
||||
case 0x28: return branch((r[rF] & FLAGZ) != 0, 0);
|
||||
case 0x38: return branch((r[rF] & FLAGC) != 0, 0);
|
||||
case 0x09: return addhl(BC());
|
||||
case 0x19: return addhl(DE());
|
||||
case 0x29: return addhl(HL());
|
||||
case 0x39: return addhl(sp);
|
||||
case 0x0A: r[rA] = memread(BC()); return 8;
|
||||
case 0x1A: r[rA] = memread(DE()); return 8;
|
||||
case 0x2A:
|
||||
r[rA] = memread(HL());
|
||||
if (++r[rL] == 0)
|
||||
r[rH]++;
|
||||
return 8;
|
||||
case 0x3A:
|
||||
r[rA] = memread(HL());
|
||||
if (r[rL]-- == 0)
|
||||
r[rH]--;
|
||||
return 8;
|
||||
case 0x0b:
|
||||
if (r[rC]-- == 0)
|
||||
r[rB]--;
|
||||
return 8;
|
||||
case 0x1b:
|
||||
if (r[rE]-- == 0)
|
||||
r[rD]--;
|
||||
return 8;
|
||||
case 0x2b:
|
||||
if (r[rL]-- == 0)
|
||||
r[rH]--;
|
||||
return 8;
|
||||
case 0x3b: sp--; return 8;
|
||||
case 0x0C: inc(r[rC]++); return 4;
|
||||
case 0x1C: inc(r[rE]++); return 4;
|
||||
case 0x2C: inc(r[rL]++); return 4;
|
||||
case 0x3C: inc(r[rA]++); return 4;
|
||||
case 0x0d: dec(r[rC]--); return 4;
|
||||
case 0x1d: dec(r[rE]--); return 4;
|
||||
case 0x2d: dec(r[rL]--); return 4;
|
||||
case 0x3d: dec(r[rA]--); return 4;
|
||||
case 0x0e: r[rC] = fetch8(); return 8;
|
||||
case 0x1e: r[rE] = fetch8(); return 8;
|
||||
case 0x2e: r[rL] = fetch8(); return 8;
|
||||
case 0x3e: r[rA] = fetch8(); return 8;
|
||||
case 0x0F:
|
||||
r[rF] = r[rA] << 4 & 0x10;
|
||||
r[rA] = r[rA] >> 1 | r[rA] << 7;
|
||||
return 4;
|
||||
case 0x1F:
|
||||
v = r[rF] << 3 & 0x80;
|
||||
r[rF] = r[rA] << 4 & 0x10;
|
||||
r[rA] = r[rA] >> 1 | v;
|
||||
return 4;
|
||||
case 0x2F:
|
||||
r[rF] |= FLAGN | FLAGH;
|
||||
r[rA] ^= 0xFF;
|
||||
return 4;
|
||||
case 0x3F:
|
||||
r[rF] = (r[rF] & ~(FLAGN | FLAGH)) ^ FLAGC;
|
||||
return 4;
|
||||
case 0xc0:
|
||||
if ((r[rF] & FLAGZ) == 0) {
|
||||
pc = pop16();
|
||||
return 20;
|
||||
}
|
||||
return 8;
|
||||
case 0xD0:
|
||||
if ((r[rF] & FLAGC) == 0) {
|
||||
pc = pop16();
|
||||
return 20;
|
||||
}
|
||||
return 8;
|
||||
case 0xE0: memwrite(0xFF00 | fetch8(), r[rA]); return 12;
|
||||
case 0xF0: r[rA] = memread(0xFF00 | fetch8()); return 12;
|
||||
case 0xC1:
|
||||
r[rC] = pop8();
|
||||
r[rB] = pop8();
|
||||
return 12;
|
||||
case 0xd1:
|
||||
r[rE] = pop8();
|
||||
r[rD] = pop8();
|
||||
return 12;
|
||||
case 0xe1:
|
||||
r[rL] = pop8();
|
||||
r[rH] = pop8();
|
||||
return 12;
|
||||
case 0xf1:
|
||||
r[rF] = pop8() & 0xF0;
|
||||
r[rA] = pop8();
|
||||
return 12;
|
||||
case 0xC2: return jump((r[rF] & FLAGZ) == 0);
|
||||
case 0xD2: return jump((r[rF] & FLAGC) == 0);
|
||||
case 0xE2: memwrite(0xFF00 | r[rC], r[rA]); return 8;
|
||||
case 0xF2: r[rA] = memread(0xFF00 | r[rC]); return 8;
|
||||
case 0xC3: return jump(1);
|
||||
case 0xF3: ime = 0; return 4;
|
||||
case 0xC4: return call(fetch16(), (r[rF] & FLAGZ) == 0);
|
||||
case 0xD4: return call(fetch16(), (r[rF] & FLAGC) == 0);
|
||||
case 0xC5:
|
||||
push8(r[rB]);
|
||||
push8(r[rC]);
|
||||
return 16;
|
||||
case 0xD5:
|
||||
push8(r[rD]);
|
||||
push8(r[rE]);
|
||||
return 16;
|
||||
case 0xE5:
|
||||
push8(r[rH]);
|
||||
push8(r[rL]);
|
||||
return 16;
|
||||
case 0xF5:
|
||||
push8(r[rA]);
|
||||
push8(r[rF]);
|
||||
return 16;
|
||||
case 0xC6: return alu(0, 8);
|
||||
case 0xD6: return alu(2, 8);
|
||||
case 0xE6: return alu(4, 8);
|
||||
case 0xF6: return alu(6, 8);
|
||||
case 0xC7: return call(0x00, -1);
|
||||
case 0xD7: return call(0x10, -1);
|
||||
case 0xE7: return call(0x20, -1);
|
||||
case 0xF7: return call(0x30, -1);
|
||||
case 0xC8:
|
||||
if ((r[rF] & FLAGZ) != 0) {
|
||||
pc = pop16();
|
||||
return 20;
|
||||
}
|
||||
return 8;
|
||||
case 0xD8:
|
||||
if ((r[rF] & FLAGC) != 0) {
|
||||
pc = pop16();
|
||||
return 20;
|
||||
}
|
||||
return 8;
|
||||
case 0xE8: case 0xF8:
|
||||
s = fetch8();
|
||||
v = sp + s;
|
||||
v4 = (sp & 0xF) + (s & 0xF);
|
||||
w = (sp & 0xFF) + (s & 0xFF);
|
||||
r[rF] = 0;
|
||||
if (v4 >= 0x10)
|
||||
r[rF] |= FLAGH;
|
||||
if (w >= 0x100)
|
||||
r[rF] |= FLAGC;
|
||||
if (op == 0xE8) {
|
||||
sp = v;
|
||||
return 16;
|
||||
} else {
|
||||
r[rL] = v;
|
||||
r[rH] = v >> 8;
|
||||
return 12;
|
||||
}
|
||||
case 0xC9: pc = pop16(); return 16;
|
||||
case 0xD9:
|
||||
pc = pop16();
|
||||
ime = 1;
|
||||
return 16;
|
||||
case 0xE9: pc = HL(); return 4;
|
||||
case 0xF9: sp = HL(); return 8;
|
||||
case 0xCA: return jump((r[rF] & FLAGZ) != 0);
|
||||
case 0xDA: return jump((r[rF] & FLAGC) != 0);
|
||||
case 0xEA: memwrite(fetch16(), r[rA]); return 16;
|
||||
case 0xFA: r[rA] = memread(fetch16()); return 16;
|
||||
case 0xCB: return bits();
|
||||
case 0xFB: ime = 1; return 4;
|
||||
case 0xCC: return call(fetch16(), (r[rF] & FLAGZ) != 0);
|
||||
case 0xDC: return call(fetch16(), (r[rF] & FLAGC) != 0);
|
||||
case 0xCD: return call(fetch16(), 1);
|
||||
case 0xCE: return alu(1, 8);
|
||||
case 0xDE: return alu(3, 8);
|
||||
case 0xEE: return alu(5, 8);
|
||||
case 0xFE: return alu(7, 8);
|
||||
case 0xCF: return call(0x08, -1);
|
||||
case 0xDF: return call(0x18, -1);
|
||||
case 0xEF: return call(0x28, -1);
|
||||
case 0xFF: return call(0x38, -1);
|
||||
}
|
||||
}
|
||||
38
error.c
Normal file
38
error.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include "gb.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
panic(const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "panic:");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
error(const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "panic:");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void*
|
||||
xalloc(long sz)
|
||||
{
|
||||
void* x = malloc(sz);
|
||||
if (x == 0)
|
||||
panic("can't alloc %z", sz);
|
||||
memset(x, 0, sz);
|
||||
return x;
|
||||
}
|
||||
77
eui.c
Normal file
77
eui.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include "gb.h"
|
||||
#define SDL_DISABLE_IMMINTRIN_H
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
int in;
|
||||
u8 keys;
|
||||
void* window;
|
||||
u8* pic;
|
||||
SDL_Renderer* renderer;
|
||||
SDL_Texture* bitmapTex;
|
||||
|
||||
static void
|
||||
render()
|
||||
{
|
||||
SDL_UpdateTexture(bitmapTex, nil, pic, 160 * sizeof(u32));
|
||||
SDL_RenderCopy(renderer, bitmapTex, nil, nil);
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
void
|
||||
joypadevent(void*_)
|
||||
{
|
||||
SDL_Event evt;
|
||||
|
||||
addevent(&evjoypad, JOYPAD_CYCLE);
|
||||
if (SDL_PollEvent(&evt) == 0)
|
||||
return;
|
||||
switch (evt.type) {
|
||||
case SDL_KEYUP: keys = 0; break;
|
||||
case SDL_KEYDOWN:
|
||||
switch (evt.key.keysym.scancode) {
|
||||
case SDL_SCANCODE_X: keys = GB_KEY_A; break;
|
||||
case SDL_SCANCODE_Z: keys = GB_KEY_B; break;
|
||||
case SDL_SCANCODE_UP: keys = GB_KEY_UP; break;
|
||||
case SDL_SCANCODE_DOWN: keys = GB_KEY_DOWN; break;
|
||||
case SDL_SCANCODE_LEFT: keys = GB_KEY_LEFT; break;
|
||||
case SDL_SCANCODE_RIGHT: keys = GB_KEY_RIGHT; break;
|
||||
case SDL_SCANCODE_RETURN: keys = GB_KEY_START; break;
|
||||
case SDL_SCANCODE_ESCAPE: break;
|
||||
default: return;
|
||||
}
|
||||
break;
|
||||
case SDL_QUIT: exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
flush()
|
||||
{
|
||||
render();
|
||||
}
|
||||
|
||||
void
|
||||
initwindow(int scale)
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
printf("SDL Initialization Fail: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
pic = xalloc(PICH * PICW * sizeof(u32));
|
||||
window = SDL_CreateWindow("gb",
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
PICW * scale,
|
||||
PICH * scale,
|
||||
SDL_WINDOW_SHOWN);
|
||||
if (!window)
|
||||
panic("SDL Initialization Fail: %s\n", SDL_GetError());
|
||||
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||
renderer = SDL_GetRenderer(window);
|
||||
bitmapTex = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
PICW,
|
||||
PICH);
|
||||
}
|
||||
60
ev.c
Normal file
60
ev.c
Normal file
@ -0,0 +1,60 @@
|
||||
#include "gb.h"
|
||||
|
||||
Event evhblank, evjoypad;
|
||||
Event* elist;
|
||||
|
||||
void
|
||||
addevent(Event* ev, int time)
|
||||
{
|
||||
Event **p, *e;
|
||||
int t = time;
|
||||
|
||||
for (p = &elist; (e = *p) != nil; p = &e->next) {
|
||||
if (t < e->time) {
|
||||
e->time -= t;
|
||||
break;
|
||||
}
|
||||
t -= e->time;
|
||||
}
|
||||
ev->next = e;
|
||||
ev->time = t;
|
||||
*p = ev;
|
||||
}
|
||||
|
||||
void
|
||||
delevent(Event* ev)
|
||||
{
|
||||
Event **p, *e;
|
||||
|
||||
for (p = &elist; (e = *p) != nil; p = &e->next) {
|
||||
if (e == ev) {
|
||||
*p = e->next;
|
||||
if (e->next != nil)
|
||||
e->next->time += e->time;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
popevent(void)
|
||||
{
|
||||
Event* e;
|
||||
int t;
|
||||
|
||||
do {
|
||||
e = elist;
|
||||
t = e->time;
|
||||
elist = e->next;
|
||||
e->f(e->aux);
|
||||
} while ((elist->time += t) <= 0);
|
||||
}
|
||||
|
||||
void
|
||||
initevent(void)
|
||||
{
|
||||
evjoypad.f = joypadevent;
|
||||
addevent(&evjoypad, JOYPAD_CYCLE);
|
||||
evhblank.f = hblanktick;
|
||||
addevent(&evhblank, 240 * 4);
|
||||
}
|
||||
78
gb.c
Normal file
78
gb.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include "gb.h"
|
||||
#include "co/task.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int cpuhalt;
|
||||
int backup;
|
||||
u64 clock;
|
||||
u8 mbc, feat, mode;
|
||||
|
||||
static void
|
||||
loadrom(const char* file)
|
||||
{
|
||||
FILE* f;
|
||||
long sz;
|
||||
static u8 mbctab[31] = { 0, 1, 1, 1, -1, 2, 2, -1, 0, 0, -1, 6, 6, 6, -1, 3,
|
||||
3, 3, 3, 3, -1, 4, 4, 4, -1, 5, 5, 5, 5, 5, 5 };
|
||||
|
||||
f = fopen(file, "r");
|
||||
if (f == nil)
|
||||
panic("can't open %s", file);
|
||||
fseek(f, 0, SEEK_END);
|
||||
sz = ftell(f);
|
||||
if (sz < 0 || sz > 32 * 1024 * 1024)
|
||||
panic("bad size %d", sz);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
nrom = sz;
|
||||
rom = xalloc(nrom);
|
||||
if (fread(rom, 1, nrom, f) != nrom)
|
||||
panic("siz is different %z", nrom);
|
||||
fclose(f);
|
||||
if (rom[0x147] > 0x1F)
|
||||
panic("bad cartidge type %d\n", rom[0x147]);
|
||||
mbc = mbctab[rom[0x147]];
|
||||
switch (mbc) {
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
panic("unsupported mbc %d", mbc);
|
||||
}
|
||||
if ((rom[0x143] & 0x80) != 0 && (mode & FORCEDMG) == 0)
|
||||
mode = CGB | COL;
|
||||
}
|
||||
|
||||
static void
|
||||
colinit(void)
|
||||
{
|
||||
union
|
||||
{
|
||||
u8 c[4];
|
||||
u32 l;
|
||||
} c;
|
||||
|
||||
c.c[3] = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
c.c[0] = c.c[1] = c.c[2] = i * 0x55;
|
||||
moncols[i] = c.l;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
taskmain(int argc, char* argv[])
|
||||
{
|
||||
colinit();
|
||||
loadrom(argv[1]);
|
||||
initwindow(5);
|
||||
initevent();
|
||||
meminit();
|
||||
reset();
|
||||
taskcreate(pputask, 0, 32768);
|
||||
|
||||
for (;;) {
|
||||
int t = step();
|
||||
clock += t;
|
||||
if (elist && (elist->time -= t) <= 0) {
|
||||
popevent();
|
||||
}
|
||||
}
|
||||
}
|
||||
215
gb.h
Normal file
215
gb.h
Normal file
@ -0,0 +1,215 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define nil ((void*)0)
|
||||
#define JOYPAD_CYCLE (30)
|
||||
|
||||
enum
|
||||
{
|
||||
JOYP = 0x00,
|
||||
SB = 0x01,
|
||||
SC = 0x02,
|
||||
DIV = 0x04,
|
||||
TIMA = 0x05,
|
||||
TMA = 0x06,
|
||||
TAC = 0x07,
|
||||
IF = 0x0F,
|
||||
NR10 = 0x10,
|
||||
NR11 = 0x11,
|
||||
NR12 = 0x12,
|
||||
NR13 = 0x13,
|
||||
NR14 = 0x14,
|
||||
NR21 = 0x16,
|
||||
NR22 = 0x17,
|
||||
NR23 = 0x18,
|
||||
NR24 = 0x19,
|
||||
NR30 = 0x1A,
|
||||
NR31 = 0x1B,
|
||||
NR32 = 0x1C,
|
||||
NR33 = 0x1D,
|
||||
NR34 = 0x1E,
|
||||
NR41 = 0x20,
|
||||
NR42 = 0x21,
|
||||
NR43 = 0x22,
|
||||
NR44 = 0x23,
|
||||
NR50 = 0x24,
|
||||
NR51 = 0x25,
|
||||
NR52 = 0x26,
|
||||
WAVE = 0x30,
|
||||
LCDC = 0x40,
|
||||
STAT = 0x41,
|
||||
SCY = 0x42,
|
||||
SCX = 0x43,
|
||||
LY = 0x44,
|
||||
LYC = 0x45,
|
||||
DMA = 0x46,
|
||||
BGP = 0x47,
|
||||
OBP0 = 0x48,
|
||||
OBP1 = 0x49,
|
||||
WY = 0x4A,
|
||||
WX = 0x4B,
|
||||
KEY1 = 0x4D,
|
||||
VBK = 0x4F,
|
||||
RP = 0x56,
|
||||
HDMASH = 0x51,
|
||||
HDMASL = 0x52,
|
||||
HDMADH = 0x53,
|
||||
HDMADL = 0x54,
|
||||
HDMAC = 0x55,
|
||||
|
||||
BCPS = 0x68,
|
||||
BCPD = 0x69,
|
||||
OCPS = 0x6A,
|
||||
OCPD = 0x6B,
|
||||
SVBK = 0x70,
|
||||
IE = 0xFF
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
LCDEN = 0x80,
|
||||
WINMAP = 0x40,
|
||||
WINEN = 0x20,
|
||||
BGTILE = 0x10,
|
||||
BGMAP = 0x08,
|
||||
SPR16 = 0x04,
|
||||
SPREN = 0x02,
|
||||
BGEN = 0x01,
|
||||
BGPRI = 0x01,
|
||||
|
||||
IRQLYC = 0x40,
|
||||
IRQM2 = 0x20,
|
||||
IRQM1 = 0x10,
|
||||
IRQM0 = 0x08,
|
||||
|
||||
IRQVBL = 1,
|
||||
IRQLCDS = 2,
|
||||
IRQTIM = 4,
|
||||
IRQSER = 8,
|
||||
IRQJOY = 16,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CGB = 1,
|
||||
COL = 2,
|
||||
TURBO = 4,
|
||||
FORCEDMG = 8,
|
||||
|
||||
FEATRAM = 1,
|
||||
FEATBAT = 2,
|
||||
FEATTIM = 4,
|
||||
|
||||
DMAREADY = 1,
|
||||
DMAHBLANK = 2,
|
||||
|
||||
INIT = -1,
|
||||
SAVE = -2,
|
||||
RSTR = -3,
|
||||
READ = -4,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TIMERSIZ = 18,
|
||||
PICW = 160,
|
||||
PICH = 144,
|
||||
FREQ = 1 << 23
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
GB_KEY_RIGHT = 0x01,
|
||||
GB_KEY_LEFT = 0x02,
|
||||
GB_KEY_UP = 0x04,
|
||||
GB_KEY_DOWN = 0x08,
|
||||
GB_KEY_A = 0x10,
|
||||
GB_KEY_B = 0x20,
|
||||
GB_KEY_SELECT = 0x40,
|
||||
GB_KEY_START = 0x80
|
||||
};
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef int8_t i8;
|
||||
typedef uint32_t u32;
|
||||
typedef int32_t i32;
|
||||
typedef uint64_t u64;
|
||||
typedef int64_t i64;
|
||||
typedef struct Event Event;
|
||||
|
||||
struct Event
|
||||
{
|
||||
int time;
|
||||
void (*f)(void*);
|
||||
Event* next;
|
||||
void* aux;
|
||||
};
|
||||
|
||||
extern u8 *rom, *back, reg[256], oam[256];
|
||||
extern u8 vram[16384];
|
||||
extern int nrom;
|
||||
extern u32 pal[64];
|
||||
extern u8 dma;
|
||||
extern u32 divclock;
|
||||
extern u64 clock;
|
||||
extern u8 ppuy, ppustate, ppuw;
|
||||
extern u8 keys;
|
||||
extern int prish;
|
||||
extern u8 mode;
|
||||
extern u8 mbc, feat;
|
||||
extern Event* elist;
|
||||
extern Event evhblank, evjoypad;
|
||||
extern u32 moncols[4];
|
||||
extern u32 white;
|
||||
extern u8* pic;
|
||||
extern int (*mapper)(int, int);
|
||||
|
||||
/* joypad */
|
||||
void
|
||||
joypadevent(void*);
|
||||
|
||||
/* memory */
|
||||
void
|
||||
memwrite(u16 a, u8 v);
|
||||
u8
|
||||
memread(u16 a);
|
||||
void
|
||||
meminit(void);
|
||||
|
||||
/* events */
|
||||
void
|
||||
initevent(void);
|
||||
void
|
||||
addevent(Event*, int);
|
||||
void
|
||||
delevent(Event*);
|
||||
void
|
||||
popevent(void);
|
||||
|
||||
/* ppu */
|
||||
void
|
||||
hblanktick(void*);
|
||||
void
|
||||
ppusync(void);
|
||||
void
|
||||
pputask(void*);
|
||||
|
||||
/* cpu */
|
||||
int
|
||||
step(void);
|
||||
void
|
||||
reset(void);
|
||||
|
||||
/* graphic */
|
||||
void
|
||||
flush();
|
||||
void
|
||||
initwindow(int scale);
|
||||
|
||||
/* error */
|
||||
void
|
||||
error(const char*, ...);
|
||||
void
|
||||
panic(const char*, ...);
|
||||
void*
|
||||
xalloc(long);
|
||||
222
mem.c
Normal file
222
mem.c
Normal file
@ -0,0 +1,222 @@
|
||||
#include "gb.h"
|
||||
|
||||
static int
|
||||
mbc0(int a, int _);
|
||||
|
||||
u8* rom;
|
||||
u8 *romb, *vramb, *wramb, *eramb;
|
||||
u8 wram[32768], vram[16384], oam[256], reg[256];
|
||||
u8* back;
|
||||
u8 palm[128];
|
||||
u32 pal[64];
|
||||
int nrom, nback, nbackbank;
|
||||
u32 divclock;
|
||||
int prish;
|
||||
u8 dma;
|
||||
u32 moncols[4];
|
||||
u32 white;
|
||||
int (*mappers[])(int, int) = { mbc0 };
|
||||
int (*mapper)(int, int);
|
||||
|
||||
static u8
|
||||
regread(u16 a)
|
||||
{
|
||||
u8 v;
|
||||
|
||||
switch (a) {
|
||||
case JOYP:
|
||||
v = 0xff;
|
||||
if ((reg[a] & 0x10) == 0)
|
||||
v &= 0xf0 | ~keys;
|
||||
if ((reg[a] & 0x20) == 0)
|
||||
v &= 0xf0 | ~(keys >> 4);
|
||||
return v;
|
||||
case DIV:
|
||||
return reg[DIV] + (clock - divclock >> 7 - ((mode & TURBO) != 0));
|
||||
case STAT:
|
||||
return reg[a] & 0xf8 | (reg[LYC] == ppuy) << 2 | ppustate;
|
||||
case LY:
|
||||
return ppuy;
|
||||
default:
|
||||
return reg[a];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
regwrite(u16 a, u8 v)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (a) {
|
||||
case JOYP:
|
||||
v |= 0xCF;/* all disabled */
|
||||
break;
|
||||
case DIV:
|
||||
divclock = clock;
|
||||
v = 0;
|
||||
break;
|
||||
case STAT:
|
||||
v |= 0x80;
|
||||
if ((v & IRQLYC) != 0 && ppuy == reg[LYC])
|
||||
reg[IF] |= IRQLCDS;
|
||||
break;
|
||||
case LYC:
|
||||
if ((reg[STAT] & IRQLYC) != 0 && ppuy == v)
|
||||
reg[IF] |= IRQLCDS;
|
||||
break;
|
||||
case LCDC:
|
||||
ppusync();
|
||||
if ((~v & reg[a] & LCDEN) != 0) {
|
||||
ppuy = 0;
|
||||
ppuw = 0;
|
||||
ppustate = 0;
|
||||
delevent(&evhblank);
|
||||
}
|
||||
if ((v & ~reg[a] & LCDEN) != 0)
|
||||
addevent(&evhblank, 456 * 2);
|
||||
break;
|
||||
case SCY:
|
||||
case SCX:
|
||||
case WY:
|
||||
case WX:
|
||||
ppusync();
|
||||
break;
|
||||
case BGP:
|
||||
case OBP0:
|
||||
case OBP1:
|
||||
ppusync();
|
||||
i = a - BGP << 2;
|
||||
pal[i] = moncols[~v & 3];
|
||||
pal[i + 1] = moncols[~v >> 2 & 3];
|
||||
pal[i + 2] = moncols[~v >> 4 & 3];
|
||||
pal[i + 3] = moncols[~v >> 6 & 3];
|
||||
break;
|
||||
case IE:
|
||||
v &= 0x1f;
|
||||
break;
|
||||
case DMA:
|
||||
for (i = 0; i < 160; ++i)
|
||||
oam[i] = memread((u16)v << 8 | i);
|
||||
break;
|
||||
default:
|
||||
if (a >= 0x80)
|
||||
break;
|
||||
v = 0xff;
|
||||
}
|
||||
reg[a] = v;
|
||||
}
|
||||
|
||||
u8
|
||||
memread(u16 a)
|
||||
{
|
||||
switch (a >> 12) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
return rom[a];
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
return romb[a - 0x4000];
|
||||
case 8:
|
||||
case 9:
|
||||
return vramb[a - 0x8000];
|
||||
case 10:
|
||||
case 11:
|
||||
if (eramb != nil)
|
||||
return eramb[a - 0xa000];
|
||||
return mapper(READ, a);
|
||||
case 12:
|
||||
case 14:
|
||||
return wram[a & 0xFFF];
|
||||
case 13:
|
||||
return wramb[a & 0xFFF];
|
||||
case 15:
|
||||
if (a >= 0xFF00)
|
||||
return regread(a - 0xFF00);
|
||||
else if (a >= 0xFE00)
|
||||
return oam[a - 0xFE00];
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
memwrite(u16 a, u8 v)
|
||||
{
|
||||
switch (a >> 12) {
|
||||
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
|
||||
mapper(a, v);
|
||||
return;
|
||||
case 8: case 9:
|
||||
vramb[a - 0x8000] = v;
|
||||
return;
|
||||
case 10: case 11:
|
||||
if (eramb != nil)
|
||||
eramb[a - 0xa000] = v;
|
||||
else
|
||||
mapper(a, v);
|
||||
return;
|
||||
case 12: case 14:
|
||||
wram[a & 0xFFF] = v;
|
||||
return;
|
||||
case 13:
|
||||
wramb[a & 0xFFF] = v;
|
||||
return;
|
||||
case 15:
|
||||
if (a >= 0xFF00)
|
||||
regwrite(a - 0xFF00, v);
|
||||
else if (a >= 0xFE00)
|
||||
oam[a - 0xFE00] = v;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mbc0(int a, int _)
|
||||
{
|
||||
if (a >= 0)
|
||||
return 0;
|
||||
switch (a) {
|
||||
case INIT:
|
||||
return 0;
|
||||
case SAVE:
|
||||
case RSTR:
|
||||
return 0;
|
||||
case READ:
|
||||
return -1;
|
||||
default:
|
||||
panic("MBC0 does not have function of %d", a);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
meminit(void)
|
||||
{
|
||||
union
|
||||
{
|
||||
u8 c[4];
|
||||
u32 l;
|
||||
} c;
|
||||
|
||||
c.c[0] = c.c[1] = c.c[2] = 0;
|
||||
c.c[3] = 1;
|
||||
for (; c.l != 1; prish++)
|
||||
c.l >>= 1;
|
||||
c.c[0] = c.c[1] = c.c[2] = 0xff;
|
||||
c.c[3] = 0;
|
||||
white = c.l;
|
||||
|
||||
romb = rom + 0x4000;
|
||||
wramb = wram + 0x1000;
|
||||
vramb = vram;
|
||||
mapper = mappers[mbc];
|
||||
mapper(INIT, 0);
|
||||
|
||||
reg[LCDC] = 0x91;
|
||||
reg[IF] = 0xE0;
|
||||
reg[SVBK] = 0xff;
|
||||
reg[VBK] = 0xff;
|
||||
}
|
||||
286
ppu.c
Normal file
286
ppu.c
Normal file
@ -0,0 +1,286 @@
|
||||
#include "co/task.h"
|
||||
#include "gb.h"
|
||||
#include <string.h>
|
||||
|
||||
#define eat(nc) \
|
||||
if (cyc <= nc) { \
|
||||
for (i = 0; i < nc; i++) \
|
||||
if (--cyc == 0) \
|
||||
taskyield(); \
|
||||
} else \
|
||||
cyc -= nc;
|
||||
|
||||
typedef struct sprite sprite;
|
||||
struct sprite
|
||||
{
|
||||
u8 dy, x, t;
|
||||
u8 pal;
|
||||
u16 chr;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SPRPRI = 0x80,
|
||||
SPRYFL = 0x40,
|
||||
SPRXFL = 0x20,
|
||||
SPRPAL = 0x10,
|
||||
SPRBANK = 0x08,
|
||||
|
||||
TILCOL0 = 0x01,
|
||||
TILPRI = 0x02,
|
||||
TILSPR = 0x04,
|
||||
};
|
||||
|
||||
u8 ppustate, ppuy, ppuw;
|
||||
u64 hblclock, rendclock;
|
||||
static int cyc, ppux, ppux0;
|
||||
sprite spr[10], *sprm;
|
||||
|
||||
void
|
||||
pputask(void* _)
|
||||
{
|
||||
int x, y, i, n, m, win;
|
||||
u16 ta, ca, chr;
|
||||
u8 tile;
|
||||
u32 sr[8], *picp;
|
||||
|
||||
for (;;) {
|
||||
eat(6 * 2);
|
||||
m = 168 + (reg[SCX] & 7);
|
||||
win = 0;
|
||||
if (reg[LCDC] & WINEN && ppuy >= reg[WY] && reg[WX] <= 166) {
|
||||
if (reg[WX] == 0)
|
||||
m = 7;
|
||||
else if (reg[WX] == 166)
|
||||
m = 0;
|
||||
else {
|
||||
m = reg[WX] + (reg[SCX] & 7) + 1;
|
||||
win = -1;
|
||||
}
|
||||
}
|
||||
ppux0 = ppux = 0;
|
||||
picp = (u32*)pic + ppuy * PICW;
|
||||
y = ppuy + reg[SCY] << 1 & 14;
|
||||
ta = 0x1800 | reg[LCDC] << 7 & 0x400 | ppuy + reg[SCY] << 2 & 0x3e0 |
|
||||
reg[SCX] >> 3;
|
||||
x = -(reg[SCX] & 7);
|
||||
restart:
|
||||
do {
|
||||
tile = vram[ta];
|
||||
if (reg[LCDC] & BGTILE)
|
||||
ca = ((u16)tile << 4) + y;
|
||||
else
|
||||
ca = 0x1000 + (((i32)tile << 24) >> 20) + y;
|
||||
chr = (u16)vram[ca] << 8 | vram[ca + 1];
|
||||
for (i = 0; i < 8; ++i) {
|
||||
sr[i] = pal[chr >> 15 | chr >> 6 & 2] | ((chr & 0x8080) == 0) << prish;
|
||||
chr <<= 1;
|
||||
}
|
||||
if (cyc <= 2 * 8) {
|
||||
for (i = 0; i < 2 * 8; ++i)
|
||||
if (--cyc == 0)
|
||||
taskyield();
|
||||
y = ppuy + reg[SCY] << 1 & 14;
|
||||
ta = 0x1800 | reg[LCDC] << 7 & 0x400 | ppuy + reg[SCY] << 2 & 0x3E0 |
|
||||
ta & 0x1F;
|
||||
} else
|
||||
cyc -= 2 * 8;
|
||||
m -= 8;
|
||||
n = m < 8 ? m : 8;
|
||||
if ((ta & 0x1F) == 0x1F)
|
||||
ta &= ~0x1F;
|
||||
else
|
||||
ta++;
|
||||
for (i = 0; i < n; ++i, ++x) {
|
||||
if (x >= 0)
|
||||
picp[x] = sr[i];
|
||||
}
|
||||
ppux = x;
|
||||
} while (m > 8);
|
||||
if (win == -1) {
|
||||
win = 1;
|
||||
ta = 0x1800 | reg[LCDC] << 4 & 0x400 | ppuw - reg[WY] << 2 & 0x3E0;
|
||||
y = ppuw - reg[WY] << 1 & 14;
|
||||
cyc += 2;
|
||||
m = 175 - reg[WX];
|
||||
goto restart;
|
||||
}
|
||||
taskyield();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
oamsearch(void)
|
||||
{
|
||||
u8* p;
|
||||
sprite* q;
|
||||
int y0, sy;
|
||||
u8 t, tn;
|
||||
u8* chrp;
|
||||
|
||||
y0 = ppuy + 16;
|
||||
sy = (reg[LCDC] & SPR16) != 0 ? 16 : 8;
|
||||
sprm = spr;
|
||||
if ((reg[LCDC] & SPREN) == 0)
|
||||
return;
|
||||
for (p = oam; p < oam + 160; p += 4) {
|
||||
if ((u8)(y0 - p[0]) >= sy)
|
||||
continue;
|
||||
for (q = spr; q < sprm; q++) {
|
||||
if (q->x > p[1]) {
|
||||
if (sprm != spr + 10) {
|
||||
memmove(q + 1, q, (sprm - q) * sizeof(sprite));
|
||||
++sprm;
|
||||
} else
|
||||
memmove(q + 1, q, (sprm - 1 - q) * sizeof(sprite));
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
if (q == spr + 10)
|
||||
continue;
|
||||
++sprm;
|
||||
found:
|
||||
q->dy = y0 - p[0];
|
||||
q->x = p[1];
|
||||
q->t = t = p[3];
|
||||
if ((t & SPRYFL) != 0)
|
||||
q->dy ^= sy - 1;
|
||||
tn = p[2];
|
||||
if (sy == 16)
|
||||
tn = tn & ~1 | q->dy >> 3;
|
||||
chrp = vram + ((u16)tn << 4 | q->dy << 1 & 14);
|
||||
q->pal = 4 + (t >> 2 & 4);
|
||||
q->chr = (u16)chrp[0] << 8 | chrp[1];
|
||||
if (p[1] < 8) {
|
||||
if ((t & SPRXFL) != 0)
|
||||
q->chr >>= 8 - p[1];
|
||||
else
|
||||
q->chr <<= 8 - p[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sprites(void)
|
||||
{
|
||||
sprite* q;
|
||||
u8 attr;
|
||||
u32* picp;
|
||||
int x, x1;
|
||||
u16 chr;
|
||||
|
||||
picp = (u32*)pic + ppuy * PICW;
|
||||
for (q = spr; q < sprm; q++) {
|
||||
if (q->x <= ppux0 || q->x >= ppux + 8)
|
||||
continue;
|
||||
x = q->x - 8;
|
||||
if (x < ppux0)
|
||||
x = ppux0;
|
||||
x1 = q->x;
|
||||
if (x1 > ppux)
|
||||
x1 = ppux;
|
||||
for (; x < x1; ++x) {
|
||||
attr = picp[x] >> prish;
|
||||
chr = q->chr;
|
||||
if ((chr & ((q->t & SPRXFL) != 0 ? 0x0101 : 0x8080)) != 0 &&
|
||||
(attr & TILSPR) == 0 &&
|
||||
((mode & COL) != 0 && (reg[LCDC] & BGPRI) == 0 ||
|
||||
(attr & TILCOL0) != 0 ||
|
||||
(attr & TILPRI) == 0 && (q->t & SPRPRI) == 0)) {
|
||||
if ((q->t & SPRXFL) == 0)
|
||||
picp[x] = pal[q->pal | chr >> 15 | chr >> 6 & 2] | TILSPR << prish;
|
||||
else
|
||||
picp[x] = pal[q->pal | chr << 1 & 2 | chr >> 8 & 1] | TILSPR << prish;
|
||||
}
|
||||
if ((q->t & SPRXFL) != 0)
|
||||
q->chr >>= 1;
|
||||
else
|
||||
q->chr <<= 1;
|
||||
}
|
||||
}
|
||||
ppux0 = ppux;
|
||||
}
|
||||
|
||||
void
|
||||
ppusync(void)
|
||||
{
|
||||
if (ppustate != 3)
|
||||
return;
|
||||
cyc = clock - rendclock;
|
||||
if (cyc != 0)
|
||||
taskyield();
|
||||
sprites();
|
||||
rendclock = clock;
|
||||
}
|
||||
|
||||
static int
|
||||
linelen(void)
|
||||
{
|
||||
int t = 174 + (reg[SCX] & 7);
|
||||
if ((reg[LCDC] & WINEN) && ppuy >= reg[WY] && reg[WX] < 166) {
|
||||
if (reg[WX] == 0)
|
||||
t += 7;
|
||||
else
|
||||
t += 6;
|
||||
}
|
||||
return t * 2;
|
||||
}
|
||||
|
||||
void
|
||||
hblanktick(void* _)
|
||||
{
|
||||
int t;
|
||||
|
||||
switch (ppustate) {
|
||||
case 0:
|
||||
hblclock = clock + evhblank.time;
|
||||
if (reg[WX] <= 166 && reg[WY] <= 143)
|
||||
ppuw++;
|
||||
if (++ppuy == 144) {
|
||||
ppustate = 1;
|
||||
if (reg[STAT] & IRQM1)
|
||||
reg[IF] |= IRQLCDS;
|
||||
addevent(&evhblank, 456 * 2);
|
||||
reg[IF] |= IRQVBL;
|
||||
flush();
|
||||
} else {
|
||||
ppustate = 2;
|
||||
if ((reg[STAT] & IRQM2) != 0)
|
||||
reg[IF] |= IRQLCDS;
|
||||
addevent(&evhblank, 80 * 2);
|
||||
}
|
||||
if ((reg[STAT] & IRQLYC) != 0 && ppuy == reg[LYC])
|
||||
reg[IF] |= IRQLCDS;
|
||||
break;
|
||||
case 1:
|
||||
hblclock = clock + evhblank.time;
|
||||
if (reg[WX] <= 166 && reg[WY] <= 143)
|
||||
ppuw++;
|
||||
if (++ppuy == 154) {
|
||||
ppuy = 0;
|
||||
ppuw = 0;
|
||||
ppustate = 2;
|
||||
if (reg[STAT] & IRQM2)
|
||||
reg[IF] |= IRQLCDS;
|
||||
addevent(&evhblank, 80 * 2);
|
||||
} else
|
||||
addevent(&evhblank, 456 * 2);
|
||||
if ((reg[STAT] & IRQLYC) && ppuy == reg[LYC])
|
||||
reg[IF] |= IRQLCDS;
|
||||
break;
|
||||
case 2:
|
||||
oamsearch();
|
||||
rendclock = clock + evhblank.time;
|
||||
ppustate = 3;
|
||||
addevent(&evhblank, linelen());
|
||||
break;
|
||||
case 3:
|
||||
ppusync();
|
||||
ppustate = 0;
|
||||
if ((reg[STAT] & IRQM0) != 0)
|
||||
reg[IF] |= IRQLCDS;
|
||||
t = hblclock + 456 * 2 - clock;
|
||||
addevent(&evhblank, t < 0 ? 456 * 2 : t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user