first commit

This commit is contained in:
Hojun-Cho 2024-07-16 16:47:13 +09:00
commit a06137a6ee
19 changed files with 37055 additions and 0 deletions

52
.gitignore vendored Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
# coroutine library
https://github.com/Hojun-Cho/coroutine

356
co/channel.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

34440
out Normal file

File diff suppressed because it is too large Load Diff

286
ppu.c Normal file
View 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;
}
}