OK, turing.

<- leave blank

Mon Feb 26 01:27:33 EST 2024

term% git/walk
no running git/fs
term% git/fs
term% git/walk
T README.md
A bin/rc/riostart
A lib/profile
A scripts/catbox
A scripts/irc7
A scripts/ircrunsrv
A scripts/runbox
term% git/commit -m 'first commit from 9front'
/bin/git/commit: nothing to commit

Sun Feb 25 19:41:54 EST 2024
bind -a $home/bin/rc /bin
bind -a $home/bin/$cputype /bin
if(!  syscall create /tmp/xxx 1 0666 >[2]/dev/null)
	ramfs # in case we're running off a cd

fn acme{
	/bin/tacme -b -c 1 $*
	}

fn mothra{
	/bin/bmothra https://lite.duckduckgo.com
	}

fn cd{
	builtin cd $* && pwd
	}

font=/lib/font/bit/vga/unicode.font
switch($service){
case terminal
	if(!  webcookies >[2]/dev/null)
		webcookies -f /tmp/webcookies
	webfs
	plumber
	echo -n accelerated > '#m/mousectl'
	echo -n 'res 3' > '#m/mousectl'
	prompt=('term% ' ' ')
	fn term%{ $* }
	rio -i riostart -s
case cpu
	bind /mnt/term/dev/cons /dev/cons
	bind -q /mnt/term/dev/consctl /dev/consctl
	>[2] /dev/null {
		cp /dev/sysname /mnt/term/dev/label
		if(test -s /mnt/term/env/wsys)
			wsys=/mnt/term^`{cat /mnt/term/env/wsys}
	}
	bind -a /mnt/term/dev /dev
	prompt=('cpu% ' ' ')
	fn cpu%{ $* }
case con
	prompt=('cpu% ' ' ')
}


Sun Feb 25 19:23:12 EST 2024
term% git/clone https://github.com/shannpersand/comic-shanns
/usr/glenda/repos/comic-shanns
fetching...
indexing 142 objects: 100%
checking out repository...
test: unexpected operator/operand: .git/refs/remotes/origin/master
no default branch
check out your code with git/branch
term% lc
comic-shanns/
term% cd comic-shanns
/usr/glenda/repos/comic-shanns
term% lc
.git/
term% ????

Sun Feb 25 15:15:10 EST 2024
#include <stdio.h>
#include <assert.h>
#define _POSIX_SOURCE
#include <unistd.h>

int
main(int argc, char **argv)
{
	int i;

	write(1, "test\n", 5);
	printf("%d\n", argc);
	fflush(stdout);
	for(i = 0; i < argc; i++)
		printf("%s\n", argv[i]);
	assert(argv[i] == NULL);
	return 0;
}


Sun Feb 25 14:58:14 EST 2024
diff 385b07b3b8db74f96ed14261fc5a236b6a5c2e58 uncommitted
--- a/blk.c
+++ b/blk.c
@@ -830,7 +830,8 @@
		killblk(t, bp);
		return;
	}
-
+assert(bp.addr != 0);
+assert((bp.addr & (Blksz-1)) == 0);
	tracex("freeb", bp, getcallerpc(&t), -1);
	f = emalloc(sizeof(Bfree), 0);
	f->op = DFblk;
@@ -918,6 +919,7 @@
			qe.op = Qfree;
			qe.bp = p->bp;
			qe.b = nil;
+assert(qe.bp.addr != 0);
			qput(a->sync, qe);
			if(p->b != nil){
				clrflag(p->b, Blimbo);
@@ -952,6 +954,7 @@
	qe.op = Qwrite;
	qe.bp = b->bp;
	qe.b = b;
+assert(qe.bp.addr != 0);
	qput(a->sync, qe);
 }

--- a/fs.c
+++ b/fs.c
@@ -59,6 +59,7 @@
 {
	Mount *mnt;
	Arena *a;
+ Dlist dl;
	int i;


@@ -87,6 +88,16 @@
	qlock(&fs->mutlk);
	tracem("packb");
	dlsync();
+ /*
+ * we want to free the dlist that we had at
+ * the end of the dlsync, not without any new
+ * nodes attached to it.
+ */
+ dl = fs->snapdl;
+ fs->snapdl.hd = Zb;
+ fs->snapdl.tl = Zb;
+ fs->snapdl.ins = nil;
+
	for(mnt = agetp(&fs->mounts); mnt != nil; mnt = mnt->next)
		updatesnap(&mnt->root, mnt->root, mnt->name);
	for(i = 0; i < fs->narena; i++){
@@ -154,7 +165,7 @@
	 * Pass 4: clean up the old snap tree's deadlist
	 */
	tracem("snapdl");
- freedl(&fs->snapdl, 1);
+ freedl(&dl, 1);
	fs->snapdl.hd.addr = -1;
	fs->snapdl.hd.hash = -1;
	fs->snapdl.hd.gen = -1;
--- a/main.c
+++ b/main.c
@@ -106,7 +106,7 @@
	vsnprint(c->err, sizeof(c->err), fmt, ap);
	if(broke){
		fprint(2, "%s\n", c->err);
- abort();
+// abort();
	}
	assert(c->nerrlab > 0 && c->nerrlab < Estacksz);
	longjmp(c->errlab[--c->nerrlab], -1);
--- a/snap.c
+++ b/snap.c
@@ -169,7 +169,7 @@
		}
		bp = b->logp;
		traceb("dlfreeb", b->bp);
- freeblk(&fs->snap, b, b->bp);
+ freeblk(nil, b, b->bp);
		dropblk(b);
	}
 }


Sun Feb 25 11:26:51 EST 2024
hello from rollenreiter

Sat Feb 24 10:43:21 EST 2024
const SlowdownFactor = 1.0;
const EjectFactor = 0.5;
const Slop = 5;
const Gravity = 20;
const ContactIterations = 10;

class Contact {
    constructor(vertexObject, edgeObject, position, normal, penetration) {
	this.vertexObject = vertexObject;
	this.edgeObject = edgeObject;
	this.position = position;
	this.normal = normal;
	this.penetration = penetration;
	this.impulseNormal = 0;
	this.impulseTangent = 0;
    }
}

class RigidBody {
    constructor(gameObject, inverseMass, inverseMomentOfInertia) {
	this.gameObject = gameObject;
	this.inverseMass = inverseMass;
	this.inverseMomentOfInertia = inverseMomentOfInertia;
	/* velocity is the velocity of the centre of mass.  */
	this.velocity = new Vec(0, 0);
	/* NB angular velocity is in rad/sec */
	this.angularVelocity = 0;
	/* these are used to track which contacts need updating */
	this.oldVelocity = new Vec(0, 0);
	this.oldAngularVelocity = new Vec(0, 0);
	this.contacts = [];
    }
    clone(newGameObject) {
	let r = new RigidBody(newGameObject, this.inverseMass,
	this.inverseMomentOfInertia);
	r.velocity = this.velocity.clone();
	r.angularVelocity = this.angularVelocity;
	return r;
    }
}

class Hinge {
    constructor(gameObject, position) {
	this.gameObject = gameObject;
	this.origPosition = position.clone();
	this.updatePosition(gameObject.position, gameObject.angle);
    }
    clone(newGameObject) {
	return new Hinge(newGameObject, this.origPosition);
    }
    updatePosition(newObjPosition, newObjAngle) {
	this.position =
	    this.origPosition.sub(this.gameObject.centerOfMass)
	    .rotateLeft(newObjAngle)
	    .add(this.gameObject.centerOfMass)
	    .add(newObjPosition);
    }
    draw(ctx) {
	ctx.strokeStyle = 'black';
	ctx.beginPath();
	ctx.arc(this.position.x, this.position.y, 3, 0, 2 * Math.PI);
	ctx.stroke();
    }
}

class PhysicsSystem {
    constructor(objectManager) {
	this.objectManager = objectManager;
    }
    processContact(contact, dt) {
	/* we treat every collisions as collisions between a vertex and an edge */
	let vo = contact.vertexObject.rigidBody; /* object with the colliding
	vertex */
	let eo = contact.edgeObject.rigidBody; /* object with the colliding edge
	*/
	let n = contact.normal; /* vector normal to the colliding edge */
	let t = new Vec(n.y, -n.x); /* vector tangent to the colliding edge */
	let mv = vo.inverseMass; /* inverse masses of both object */
	let me = eo.inverseMass;
	let Iv = mv * vo.inverseMomentOfInertia; /* inverse moments of inertia */
	let Ie = me * eo.inverseMomentOfInertia;
	/* we calculate vectors pointing from the centre of mass of each object
	   to the contact point */
	let r0 =
	contact.position.sub(vo.gameObject.position).sub(vo.gameObject.centerOfMass);
	let r1 =
	contact.position.sub(eo.gameObject.position).sub(eo.gameObject.centerOfMass);
	/* we need to undo the effect the previous iteration of this contact had
	on the velocities */
	let Dp = t.mul(-contact.impulseTangent).sub(n.mul(contact.impulseNormal));
	vo.velocity = vo.velocity.add(Dp.mul(mv));
	vo.angularVelocity -= Dp.cross(r0) * Iv;
	eo.velocity = eo.velocity.add(Dp.mul(-me));
	eo.angularVelocity += Dp.cross(r1) * Ie;
	/* u is the relative velocity of the two contact points */
	let u = vo.velocity.add(r0.rotateLeft90().mul(vo.angularVelocity));
	u = u.sub(eo.velocity.add(r1.rotateLeft90().mul(eo.angularVelocity)));
	/* resolve vectors into their components along the tangent and the normal
	*/
	let ut = u.dot(t); let un = u.dot(n);
	let r0t = r0.dot(t); let r0n = r0.dot(n);
	let r1t = r1.dot(t); let r1n = r1.dot(n);
	let gamma = 0; /* accumulated normal impulse */
	let pt = 0; /* accumulated tangent impulse */
	let mus = 0.5; /* static coefficient of friction */
	let mud = 0.3; /* dynamic coefficient of friction */

	if(contact.penetration <= 0){
	    /* to prevent objects from penetrating each other, we run the
	    collision code when the distance
	       is less than Slop.  but if there is some actual positive distance
	       (negative penetration) left,
	       we don't actually want to set un to zero; slow movement is still
	       okay!  by offsetting un
	       we allow a maximum velocity of SlowdownFactor * distance/dt */
	    un -= SlowdownFactor * contact.penetration / dt;
	    if(un >= 0){
		/* the objects are moving apart -- nothing to do */
		contact.impulseNormal = 0;
		contact.impulseTangent = 0;
		return;
	    }
	}else{
	    /* pull penetrating objects apart */
	    un -= EjectFactor * contact.penetration / dt;
	    if(un >= 0){
		/* this code is equivalent to running one iteration of the loop
		below,
		   but for alpha = 0 */
		let mn = mv + r0t * r0t * Iv;
		mn += me + r1t * r1t * Ie;
		gamma = -un/mn;
		un = 0;
	    }
	}
	/* we keep processing until the objects are receding */
	while(un < 0){
	    /* first we need to determine the effective coefficient of friction
	    alpha */
	    let alpha;
	    /* are the two objects sliding relative to each other?  */
	    if(ut == 0){
		/* when the two objects are sticking together, the frictional
		force cancels any applied force,
		   which in our case means setting alpha such that ut remains 0 */
		alpha = r0t * r0n * Iv + r1t * r1n * Ie;
		alpha /= mv + r0n * r0n * Iv + me + r1n * r1n * Ie;
		/* however, if the required frictional force is too big, we start
		to slide,
		   and are now using the *dynamic* coefficient of friction */
		if(alpha > mus) alpha = mud;
		else if(alpha < -mus) alpha = -mud;
	    }else
		alpha = ut < 0 ? mud : -mud;

	    /* calculate the ratios dut/dpn and dun/dpn */
	    let mt = alpha * mv + (r0n * r0n * alpha - r0t * r0n) * Iv;
	    mt += alpha * me + (r1n * r1n * alpha - r1t * r1n) * Ie;
	    let mn = mv + (r0t * r0t - r0n * r0t * alpha) * Iv;
	    mn += me + (r1t * r1t - r1n * r1t * alpha) * Ie;
	    /* we need to determine how much momentum we can apply before a
	    relevant condition changes,
	       which forces us to recalculate the value of alpha, or exit the loop
	       entirely */
	    /* if we're sliding, we can apply tangent momentum only until ut == 0
	    */
	    let gammat;
	    if(ut == 0)
		gammat = Infinity;
	    else{
		gammat = gamma - ut / mt;
		if(gammat < gamma) gammat = Infinity; /* TODO: should this be
		an assert?  */
	    }
	    /* we can apply normal momentum only until un == 0 */
	    let gamman = gamma - un / mn;
	    if(gamman < gamma) gamman = Infinity; /* TODO: should this be an
	    assert?  */
	    if(gammat < gamman){
		/* we're continuing until ut == 0 */
		ut = 0;
		un += mn * (gammat - gamma);
		pt += (gammat - gamma) * alpha;
		gamma = gammat;
	    }else{
		/* we're continuing until un == 0 */
		console.assert(gamman < Infinity);
		un = 0;
		ut += mt * (gamman - gamma);
		pt += (gamman - gamma) * alpha;
		gamma = gamman;
	    }
	}

	/* store the values we calculated in the contact, for the next iteration
	*/
	contact.impulseTangent = pt;
	contact.impulseNormal = gamma;
	/* apply the momentum to the velocities and angular velocities */
	Dp = t.mul(pt).add(n.mul(gamma));
	vo.velocity = vo.velocity.add(Dp.mul(mv));
	vo.angularVelocity -= Dp.cross(r0) * Iv;
	eo.velocity = eo.velocity.add(Dp.mul(-me));
	eo.angularVelocity += Dp.cross(r1) * Ie;
    }
    processHinge(hingeA, hingeB, dt) {
	let objA = hingeA.gameObject; let objB = hingeB.gameObject;
	let bodyA = objA.rigidBody; let bodyB = objB.rigidBody;
	let ma = bodyA.inverseMass; let mb = bodyB.inverseMass;
	let Ia = bodyA.inverseMomentOfInertia; let Ib =
	bodyB.inverseMomentOfInertia;
	let r0 = hingeA.position.sub(objA.position).sub(objA.centerOfMass);
	let r1 = hingeB.position.sub(objB.position).sub(objB.centerOfMass);
	let u = bodyA.velocity.add(r0.rotateLeft90().mul(bodyA.angularVelocity));
	u =
	u.sub(bodyB.velocity.add(r1.rotateLeft90().mul(bodyB.angularVelocity)));
	u = u.add(hingeA.position.sub(hingeB.position).mul(EjectFactor / dt));
	let mxx = ma + Ia * r0.x * r0.x + mb + Ib * r1.x * r1.x;
	let mxy = Ia * r0.x * r0.y + Ib * r1.x * r1.y;
	let myy = ma + Ia * r0.y * r0.y + mb + Ib * r1.y * r1.y;
	let det = mxx * myy - mxy * mxy;
	let Dp = new Vec(
	    (mxx * u.x + mxy * u.y) / det,
	    (mxy * u.x + myy * u.y) / det);
	bodyA.velocity = bodyA.velocity.add(Dp.mul(-ma));
	bodyA.angularVelocity += Dp.cross(r0) * Ia;
	bodyB.velocity = bodyB.velocity.add(Dp.mul(mb));
	bodyB.angularVelocity -= Dp.cross(r1) * Ib;
    }
    step(contacts, dt){
	let objects = this.objectManager.findAllWithComponent('rigidBody');
	objects.forEach(obj => {
	    let o = obj.rigidBody;
	    if(o.inverseMass != 0)
		o.velocity.y += Gravity * dt;
	    o.oldVelocity = o.velocity;
	    o.oldAngularVelocity = o.angularVelocity;
	    o.contacts = [];
	});
	let linkGroups = this.objectManager.getAllLinkGroups();
	let activeContacts = new Set();
	for(let i = 0; i < contacts.length; i++){
	    contacts[i].vertexObject.rigidBody.contacts.push(contacts[i]);
	    contacts[i].edgeObject.rigidBody.contacts.push(contacts[i]);
	    activeContacts.add(contacts[i]);
	}
	for(let i = 0; i < ContactIterations && (activeContacts.size > 0 ||
	linkGroups.length > 0); i++){
	    let touchedObjects = new Set();
	    activeContacts.forEach(contact => {
		this.processContact(contact, dt);
		touchedObjects.add(contact.vertexObject.rigidBody);
		touchedObjects.add(contact.edgeObject.rigidBody);
	    });
	    linkGroups.forEach(group => {
		for(let i = 0; i < group.length - 1; i++){
		    this.processHinge(group[i], group[i+1], dt);
		    touchedObjects.add(group[i+1].gameObject.rigidBody);
		}
		touchedObjects.add(group[0].gameObject.rigidBody);
	    });
	    activeContacts.clear();
	    touchedObjects.forEach(object => {
		if(Math.abs(object.velocity.x - object.oldVelocity.x) > Epsilon
		||
		   Math.abs(object.velocity.y - object.oldVelocity.y) > Epsilon
		   ||
		   Math.abs(object.angularVelocity - object.oldAngularVelocity)
		   > Epsilon)
		{
		    object.contacts.forEach(contact =>
		    activeContacts.add(contact));
		    object.oldVelocity = object.velocity.clone();
		    object.oldAngularVelocity = object.angularVelocity;
		}
	    });
	}
	objects.forEach(o => o.moveBy(o.rigidBody.velocity.mul(dt),
	o.rigidBody.angularVelocity * dt / DEG));
    }
}

Sat Feb 24 09:16:55 EST 2024
diff 2259c436be76e22801766f8cd1a056f5b4430139 uncommitted
--- a/sys/man/1/nintendo
+++ b/sys/man/1/nintendo
@@ -28,6 +28,9 @@
 .B -s
 .I savetype
 ] [
+.B -g
+.I gpiotype
+] [
 .B -x
 .I scale
 ]
@@ -127,6 +130,10 @@
 Valid formats and corresponding ids are:
 flash512 (SST), flash512mx (Macronix 64K), flash512pan (Panasonic), flash512atm
 (Atmel),
 flash1024 (Macronix 128K), flash1024san (Sanyo).
+.TP
+.B -g
+GPIO hardware used by the original game.  The only valid type currently is rtc
(real time clock).
+By default, the emulator attempts to automatically detect the GPIO hardware using
the game code found in the rom header.
 .PP
 .B nes
 options:
--- a/sys/src/games/gba/dat.h
+++ b/sys/src/games/gba/dat.h
@@ -9,7 +9,7 @@
 extern u16int reg[];
 extern uchar *rom, *back;
 extern int nrom, nback, backup;
-extern int flashid;
+extern int flashid, gpiogame;

 extern int hblank, ppuy;

--- a/sys/src/games/gba/fns.h
+++ b/sys/src/games/gba/fns.h
@@ -26,3 +26,10 @@
 void loadstate(char *);
 void savestate(char *);
 void cpuload(void);
+void gpioident(void);
+void gpiowdata(u32int);
+void gpiowdir(u32int);
+void gpiowcontrol(u32int);
+u32int gpiordata(void);
+u32int gpiordir(void);
+u32int gpiorcontrol(void);
--- a/sys/src/games/gba/gba.c
+++ b/sys/src/games/gba/gba.c
@@ -255,7 +255,7 @@
 void
 usage(void)
 {
- fprint(2, "usage: %s [-a] [-s savetype] [-b biosfile] [-x scale] rom\n", argv0);
+ fprint(2, "usage: %s [-a] [-s savetype] [-b biosfile] [-x scale] [-g gpiotype]
rom\n", argv0);
	exits("usage");
 }

@@ -281,6 +281,11 @@
	case 'x':
		fixscale = strtol(EARGF(usage()), nil, 0);
		break;
+ case 'g':
+ s = EARGF(usage());
+ if(strcmp(s, "rtc") == 0)
+ gpiogame = 1;
+ break;
	default:
		usage();
	} ARGEND;
@@ -289,6 +294,7 @@

	loadbios();
	loadrom(argv[0]);
+ gpioident();
	initemu(240, 160, 2, CHAN4(CIgnore, 1, CBlue, 5, CGreen, 5, CRed, 5), 1,
	nil);
	regkey("b", 'z', 1<<1);
	regkey("a", 'x', 1<<0);
--- /dev/null
+++ b/sys/src/games/gba/gpio.c
@@ -1,0 +1,173 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+int gpiogame;
+static int gpioen;
+static int rtcclk;
+static uchar rtcdata, rtcout;
+static int rtccount;
+static uchar pinstate[3] = { 1, 1, 1 };
+
+enum {
+ RCLK=1<<0,
+ RSIO=1<<1,
+ RCS=1<<2,
+
+ PCLK=0,
+ PSIO=1,
+ PCS=2,
+};
+
+void
+gpioident(void)
+{
+ char code[4];
+
+ memcpy(code, rom+0xAC, 4);
+
+ /* Pokemon E/S/R.  RTC only */
+ if(memcmp(code, "BPEE", 4) == 0
+ || memcmp(code, "AXPE", 4) == 0
+ || memcmp(code, "AXVE", 4) == 0)
+ gpiogame = 1;
+
+}
+
+#define BCD(x) (x/10 * 16 + x%10)
+
+static void
+getdate(uchar out[7])
+{
+ static Tzone *zone;
+ static Tm date;
+
+ if(zone == nil)
+ zone = tzload("local");
+ tmnow(&date, zone);
+ date.year -= 100;
+ date.mon++;
+ out[0] = BCD(date.year);
+ out[1] = BCD(date.mon);
+ out[2] = BCD(date.mday);
+ out[3] = BCD(date.wday);
+ out[4] = BCD(date.hour);
+ out[5] = BCD(date.min);
+ out[6] = BCD(date.sec);
+}
+
+// https://html.alldatasheet.com/html-pdf/80559/SII/S-3511/45/1/S-3511.html
+static void
+rtcstate(void)
+{
+ enum { CMD, STATUS, DATETIME };
+ static int state = CMD;
+ enum { WR=0, RD=1 };
+ static int dir = WR;
+ static int datetimepos = 0;
+ static uchar date[7];
+
+ uchar cmd;
+
+ switch(state){
+ case CMD:
+ dir = rtcdata&1;
+ if((rtcdata&0xF0) != 0x60){
+ print("wrong rtc state: %X\n", rtcdata);
+ return;
+ }
+ cmd = (rtcdata&0xF)>>1;
+ switch(cmd){
+ case 0: /* reset */
+ break;
+ case 1: /* status */
+ if(dir == RD)
+ rtcdata = 0b01000000;
+ state = STATUS;
+ break;
+ case 2: /* year → seconds */
+ case 3: /* hour → seconds */
+ getdate(date);
+ datetimepos = (cmd-2)<<2;
+ if(dir == RD)
+ rtcdata = date[datetimepos];
+ datetimepos++;
+ state = DATETIME;
+ break;
+ default:
+ print("unsupported rtc cmd: %d %d\n", dir, cmd);
+ rtcdata = 0;
+ break;
+ }
+ break;
+ case STATUS:
+ state = CMD;
+ if(dir == WR)
+ rtcdata = 0;
+ break;
+ case DATETIME:
+ if(dir == RD)
+ rtcdata = date[datetimepos];
+ else
+ rtcdata = 0;
+ if(++datetimepos == 8){
+ datetimepos = 0;
+ state = CMD;
+ }
+ break;
+ }
+}
+
+void
+gpiowdata(u32int v)
+{
+ if((v&RCLK) == 0 && rtcclk == 1){
+ if(pinstate[PSIO] == 1){
+ rtcdata |= ((v&RSIO)>>1)<<(7-rtccount);
+ } else {
+ rtcout = rtcdata&1;
+ rtcdata >>= 1;
+ }
+ rtccount++;
+ }
+ if(rtccount == 8){
+ rtcstate();
+ rtccount = 0;
+ }
+ rtcclk = v&RCLK;
+}
+
+u32int
+gpiordata(void)
+{
+ return rtcout<<1;
+}
+
+void
+gpiowdir(u32int v)
+{
+ pinstate[PCLK] = v&1;
+ pinstate[PSIO] = (v&2)>>1;
+ pinstate[PCS] = (v&4)>>2;
+}
+
+u32int
+gpiordir(void)
+{
+ return (pinstate[PCS]<<2) | (pinstate[PSIO]<<1) |
(u32int)pinstate[PCLK];
+}
+
+void
+gpiowcontrol(u32int v)
+{
+ gpioen = v;
+}
+
+u32int
+gpiorcontrol(void)
+{
+ return gpioen;
+}
--- a/sys/src/games/gba/mem.c
+++ b/sys/src/games/gba/mem.c
@@ -263,7 +263,21 @@
		b = a & sizeof(oam) - 1;
		cyc++;
		return ar16read(oam + b/2, b & 1, n);
- case 8: case 9: case 10: case 11: case 12: case 13:
+ case 8:
+ if(!gpiogame)
+ goto Rom;
+ b = a & 0xffffff;
+ switch(b){
+ case 0xC4:
+ return gpiordata();
+ case 0xC6:
+ return gpiordir();
+ case 0xC8:
+ return gpiorcontrol();
+ }
+
+ case 9: case 10: case 11: case 12: case 13:
+ Rom:
		b = a & 0x1ffffff;
		cyc += waitst[(a >> 25) - 4 | seq << 2 | (n > 2)
		<< 3];
		if(b >= nrom){
@@ -290,7 +304,7 @@
 void
 memwrite(u32int a, u32int v, int n)
 {
- u32int b;
+ u32int b, t;
	assert((a & n-1) == 0);

	switch(a >> 24){
@@ -342,7 +356,28 @@
		if(n != 1)
			ar16write(oam + b/2, b & 1, v, n);
		return;
- case 8: case 9: case 10: case 11: case 12: case 13:
+ case 8:
+ if(!gpiogame)
+ goto Rom;
+ b = a & 0xffffff;
+ switch(b){
+ case 0xC4:
+ t = v&0xFFFF;
+ gpiowdata(t);
+ if(n <= 2)
+ return;
+ v>>=16;
+ case 0xC6:
+ t = v&0xFFFF;
+ gpiowdir(t);
+ return;
+ case 0xC8:
+ t = v&0xFFFF;
+ gpiowcontrol(t);
+ return;
+ }
+ case 9: case 10: case 11: case 12: case 13:
+ Rom:
		if(backup == EEPROM){
			b = a & 0x01ffffff;
			if(b >= eepstart)
--- a/sys/src/games/gba/mkfile
+++ b/sys/src/games/gba/mkfile
@@ -11,6 +11,7 @@
	apu.$O\
	state.$O\
	eui.$O\
+ gpio.$O\

 HFILES=dat.h fns.h



Fri Feb 23 23:24:42 EST 2024
Hello from Happykiddi.

Fri Feb 23 19:43:46 EST 2024
diff 41abe98ac77c8a3b54b86264f910f069484bae02 uncommitted
--- a/sys/src/games/gba/fns.h
+++ b/sys/src/games/gba/fns.h
@@ -26,3 +26,9 @@
 void loadstate(char *);
 void savestate(char *);
 void cpuload(void);
+void gpiowdata(u32int);
+void gpiowdir(u32int);
+void gpiowcontrol(u32int);
+u32int gpiordata(void);
+u32int gpiordir(void);
+u32int gpiorcontrol(void);
--- /dev/null
+++ b/sys/src/games/gba/gpio.c
@@ -1,0 +1,154 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "../eui.h"
+#include "dat.h"
+#include "fns.h"
+
+static int gpioen;
+static int rtcclk;
+static uchar rtcdata, rtcout;
+static int rtccount;
+static uchar pinstate[3] = { 1, 1, 1 };
+
+enum {
+ RCLK=1<<0,
+ RSIO=1<<1,
+ RCS=1<<2,
+
+ PCLK=0,
+ PSIO=1,
+ PCS=2,
+};
+
+#define BCD(x) (x/10 * 16 + x%10)
+
+static void
+getdate(uchar out[7])
+{
+ static Tzone *zone;
+ static Tm date;
+
+ if(zone == nil)
+ zone = tzload("local");
+ tmnow(&date, zone);
+ date.year -= 100;
+ date.mon++;
+ out[0] = BCD(date.year);
+ out[1] = BCD(date.mon);
+ out[2] = BCD(date.mday);
+ out[3] = BCD(date.wday);
+ out[4] = BCD(date.hour);
+ out[5] = BCD(date.min);
+ out[6] = BCD(date.sec);
+}
+
+// https://html.alldatasheet.com/html-pdf/80559/SII/S-3511/45/1/S-3511.html
+static void
+rtcstate(void)
+{
+ enum { CMD, STATUS, DATETIME };
+ static int state = CMD;
+ enum { WR=0, RD=1 };
+ static int dir = WR;
+ static int datetimepos = 0;
+ static uchar date[7];
+
+ uchar cmd;
+
+ if(state == CMD){
+ dir = rtcdata&1;
+ if((rtcdata&0xF0) != 0x60){
+ fprint(2, "wrong rtc state: %X\n", rtcdata);
+ return;
+ }
+ cmd = (rtcdata&0xF)>>1;
+ switch(cmd){
+ case 0: /* reset */
+ break;
+ case 1: /* status */
+ if(dir == RD)
+ rtcdata = 0b01000000;
+ state = STATUS;
+ break;
+ case 2: /* year → seconds */
+ case 3: /* hour → seconds */
+ getdate(date);
+ datetimepos = (cmd-2)<<2;
+ if(dir == RD)
+ rtcdata = date[datetimepos];
+ datetimepos++;
+ state = DATETIME;
+ break;
+ default:
+ fprint(2, "unsupported rtc cmd: %d %d\n", dir, cmd);
+ rtcdata = 0;
+ break;
+ }
+ }
+ else if(state == STATUS){
+ state = CMD;
+ if(dir == WR)
+ rtcdata = 0;
+ } else if(state == DATETIME){
+ if(dir == RD)
+ rtcdata = date[datetimepos];
+ else
+ rtcdata = 0;
+ if(++datetimepos == 8){
+ datetimepos = 0;
+ state = CMD;
+ }
+ }
+}
+
+void
+gpiowdata(u32int v)
+{
+ if((v&RCLK) == 0 && rtcclk == 1){
+ if(pinstate[PSIO] == 1){
+ rtcdata |= ((v&RSIO)>>1)<<(7-rtccount);
+ } else {
+ rtcout = rtcdata&1;
+ rtcdata >>= 1;
+ }
+ rtccount++;
+ }
+ if(rtccount == 8){
+ rtcstate();
+ rtccount = 0;
+ }
+ rtcclk = v&RCLK;
+}
+
+u32int
+gpiordata(void)
+{
+ return rtcout<<1;
+}
+
+void
+gpiowdir(u32int v)
+{
+ pinstate[PCLK] = v&1;
+ pinstate[PSIO] = (v&2)>>1;
+ pinstate[PCS] = (v&4)>>2;
+}
+
+u32int
+gpiordir(void)
+{
+ return (pinstate[PCS]<<2) | (pinstate[PSIO]<<1) |
(u32int)pinstate[PCLK];
+}
+
+void
+gpiowcontrol(u32int v)
+{
+ gpioen = v;
+}
+
+u32int
+gpiorcontrol(void)
+{
+ return gpioen;
+}
--- a/sys/src/games/gba/mem.c
+++ b/sys/src/games/gba/mem.c
@@ -221,7 +221,7 @@
 u32int
 memread(u32int a, int n, int seq)
 {
- u32int b;
+ u32int b, o;
	assert((a & n-1) == 0);

	switch(a >> 24){
@@ -263,7 +263,26 @@
		b = a & sizeof(oam) - 1;
		cyc++;
		return ar16read(oam + b/2, b & 1, n);
- case 8: case 9: case 10: case 11: case 12: case 13:
+ case 8:
+ b = a & 0xffffff;
+ switch(b){
+ case 0xC4:
+ o = gpiordata();
+ break;
+ case 0xC6:
+ o = gpiordir();
+ break;
+ case 0xC8:
+ o = gpiorcontrol();
+ break;
+ default:
+ goto Rom;
+ }
+ //fprint(2, "READ OUT: %X\n", o);
+ return o;
+
+ case 9: case 10: case 11: case 12: case 13:
+ Rom:
		b = a & 0x1ffffff;
		cyc += waitst[(a >> 25) - 4 | seq << 2 | (n > 2)
		<< 3];
		if(b >= nrom){
@@ -290,7 +309,7 @@
 void
 memwrite(u32int a, u32int v, int n)
 {
- u32int b;
+ u32int b, t;
	assert((a & n-1) == 0);

	switch(a >> 24){
@@ -342,7 +361,25 @@
		if(n != 1)
			ar16write(oam + b/2, b & 1, v, n);
		return;
- case 8: case 9: case 10: case 11: case 12: case 13:
+ case 8:
+ b = a & 0xffffff;
+ switch(b){
+ case 0xC4:
+ t = v&0xFFFF;
+ gpiowdata(t);
+ if(n <= 2)
+ break;
+ v>>=16;
+ case 0xC6:
+ t = v&0xFFFF;
+ gpiowdir(t);
+ break;
+ case 0xC8:
+ t = v&0xFFFF;
+ gpiowcontrol(t);
+ break;
+ }
+ case 9: case 10: case 11: case 12: case 13:
		if(backup == EEPROM){
			b = a & 0x01ffffff;
			if(b >= eepstart)


Fri Feb 23 10:59:30 EST 2024
#
# files comprising the database, use as many as you like, see ndb(6)
#
database=
	file=/net/ndb
	file=/lib/ndb/local
	file=/lib/ndb/common
	file=/lib/ndb/dnschallenge

#
# entries defining the dns root.  these will be overridden by any
# authentic info obtained from the root.
#
dom=
	ns=A.ROOT-SERVERS.NET
	ns=B.ROOT-SERVERS.NET
	ns=C.ROOT-SERVERS.NET
	ns=D.ROOT-SERVERS.NET
	ns=E.ROOT-SERVERS.NET
	ns=F.ROOT-SERVERS.NET
	ns=G.ROOT-SERVERS.NET
	ns=H.ROOT-SERVERS.NET
	ns=I.ROOT-SERVERS.NET
	ns=J.ROOT-SERVERS.NET
	ns=K.ROOT-SERVERS.NET
	ns=L.ROOT-SERVERS.NET
	ns=M.ROOT-SERVERS.NET

#
# because the public demands the name localhost
#
ip=127.0.0.1 sys=localhost dom=localhost

# example: adjust to fit your network
auth=cirno authdom=9front
ipnet=9front ip=192.168.64.0 ipmask=255.255.255.0
	ipgw=192.168.64.1
	dns=192.168.64.1
	auth=cirno
	dnsdom=9front
	cpu=cirno
	smtp=cirno

ip=192.168.64.3 sys=cirno dom=cirno.9front ether=16911218e7f


prev | next