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