Plan 9 from Bell Labs’s /usr/web/sources/contrib/rsc/8i/vbe.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <bio.h>
#include "8i.h"

#define WORD(p) ((p)[0] | ((p)[1]<<8))
#define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
#define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
#define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24

typedef struct Vbe Vbe;
struct Vbe {
	ushort rax;
	ushort rbx;
	ushort rcx;
	ushort rdx;
	uchar buf[1024];
};

int nflag;
uchar *ivec;
Cpu cpu;

typedef struct Flag Flag;
struct Flag {
	int bit;
	char *desc;
};

void
printflags(char *s, Flag *f, int b)
{
	int i;

	print("%s", s);
	for(i=0; f[i].bit; i++)
		if(f[i].bit & b)
			print(" %s", f[i].desc);
	print("\n");
}

void
vbetrap(Cpu *cpu, Inst *inst, int t)
{
	uchar *m;
	ushort cs, pc;

fprint(2, "vbetrap 0x%x\n", t);
	if(t == 0x20)
		longjmp(cpu->exitjmp, 1);

	if(t <= 0xFF){
		m = &cpu->mem[4*t];
		cs = WORD(m+2);
		pc = WORD(m+0);
print("trap %d cs %x pc %x\n", t, cs, pc);
		if(cs != 0 || pc != 0){
if(cs&0xFFF){
print("tried mem+0x%x %x:%x\n", 4*t, cs, pc);
	abort();
}
			push(cpu, 16, cpu->flags);
			push(cpu, 16, cpu->reg[RCS]);
			push(cpu, 16, cpu->npc);
			cpu->ncs = cs;
			cpu->npc = pc;
			longjmp(cpu->jmp, 1);
		}
	}
	dumpreg(cpu);
	print("%.5P %I\n", cpu, inst);
	print("unknown trap %d\n", t);
	abort();
}

int
vbecall(Vbe *vbe)
{
	uchar *buf;
	uchar *sp;

	/*
	 * the first 64k of memory is the stack segment
	 * and also the place we return to.
	 */
	sp = cpu.mem+0x10000;
	*--sp = 0x20;	/* INT 20: exit */
	*--sp = 0xCD;
	*--sp = 0x00;	/* ret segment */
	*--sp = 0x00;
	*--sp = 0xFF;	/* ret offset */
	*--sp = 0xFE;
	
	cpu.reg[RSP] = sp - cpu.mem;
	cpu.reg[RAX] = vbe->rax;
	cpu.reg[RBX] = vbe->rbx;
	cpu.reg[RCX] = vbe->rcx;
	cpu.reg[RDX] = vbe->rdx;

	/*
	 * the second 64k of memory holds our buffer.
	 */
	buf = cpu.mem+0x10000;
	cpu.reg[RES] = 0x1000;
	cpu.reg[RDI] = 0x0000;

	cpu.reg[RCS] = WORD(ivec+0x10*4+2);
	cpu.pc = WORD(ivec+0x10*4);
	memmove(buf, vbe->buf, sizeof(vbe->buf));
	run(&cpu);
	memmove(vbe->buf, buf, sizeof(vbe->buf));

	vbe->rax = cpu.reg[RAX];
	vbe->rbx = cpu.reg[RBX];
	vbe->rcx = cpu.reg[RCX];
	vbe->rdx = cpu.reg[RDX];

	if(vbe->rax&0xFF00)
		return -1;
	return 0;
}

void
interrupt(void *a, char *msg)
{
	USED(a);
	if(strcmp(msg, "interrupt") == 0) {
		print("INTERRUPT -- inst count = %d\n", cpu.instcount);
		dumpreg(&cpu);
		exits(msg);
	}
	noted(NDFLT);
}

void
usage(void)
{
	fprint(2, "usage: vbe [-t]\n");
	exits("usage");
}

void
cmdtrace(int, char**)
{
	cpu.trace = !cpu.trace;
	print("trace %s\n", cpu.trace ? "on" : "off");
}

void*
unfarptr(uchar *m)
{
	int seg, off;

	seg = (m[2]<<4)|(m[3]<<12);
	off = m[0]|(m[1]<<8);
	if(seg == 0 && off == 0)
		return nil;
	return (char*)cpu.mem+seg+off;
}

void
videomodes(uchar *m)
{
	print("\t");
	while(m[0] != 0xFF || m[1] != 0xFF) {
		print("%.4ux ", WORD(m));
		m += 2;
	}
	print("\n");
}

Flag capabilityflag[] = {
	0x01, "8-bit-dac",
	0x02, "not-vga",
	0x04, "ramdac-needs-blank",
	0x08, "stereoscopic",
	0x10, "stereo-evc",
	0
};

void
cmdinfo(int, char**)
{
	uchar *p;
	Vbe vbe;

	memset(&vbe, 0, sizeof vbe);
	p = vbe.buf;
	strcpy((char*)p, "VBE2");

	vbe.rax = 0x4F00;
	if(vbecall(&vbe) < 0){
		fprint(2, "vbe error %.4ux\n", vbe.rax);
		return;
	}

	print("signature: %.4s\n", (char*)p);
	print("version: %d.%d\n", p[5], p[4]);
	if(p[5] < 2)
		return;

	print("oem string: %s\n", unfarptr(p+6));
	printflags("capabilities:", capabilityflag, p[10]);
	print("video modes:\n");
	videomodes(unfarptr(p+14));
	print("total memory: %lud\n", WORD(p+18)*0x10000UL);
	print("oem software rev: %d.%d\n", p[21], p[20]);
	print("vendor name: %s\n", unfarptr(p+22));
	print("product name: %s\n", unfarptr(p+26));
	print("product rev: %s\n", unfarptr(p+30));
}

Flag modeattributesflags[] = {
	1<<0, "supported",
	1<<2, "tty",
	1<<3, "color",
	1<<4, "graphics",
	1<<5, "not-vga",
	1<<6, "no-windowed-vga",
	1<<7, "linear",
	1<<8, "double-scan",
	1<<9, "interlace",
	1<<10, "triple-buffer",
	1<<11, "stereoscopic",
	1<<12, "dual-start-addr",
	0
};

Flag winattributesflags[] = {
	1<<0, "relocatable",
	1<<1, "readable",
	1<<2, "writeable",
	0
};

Flag directcolorflags[] = {
	1<<0, "programmable-color-ramp",
	1<<1, "x-usable",
	0
};

char *modelstr[] = {
	"text", "cga", "hercules", "planar", "packed", "non-chain4", "direct", "YUV"
};

void
cmdmodeinfo(int argc, char **argv)
{
	int i;
	uchar *p;
	Vbe vbe;

	if(argc != 2){
		fprint(2, "?usage: modeinfo 0xMODE\n");
		return;
	}

	memset(&vbe, 0, sizeof vbe);
	p = vbe.buf;
	vbe.rax = 0x4F01;
	vbe.rcx = atoi(argv[1]);
	if(vbecall(&vbe) < 0){
		fprint(2, "vbe error %.4ux\n", vbe.rax);
		return;
	}

	for(i=0; i<18; i++) print("%2.2ux ", p[i]); print("\n");
	for(; i<31; i++) print("%2.2ux ", p[i]); print("\n");
	for(; i<40; i++) print("%2.2ux ", p[i]); print("\n");
	for(; i<50; i++) print("%2.2ux ", p[i]); print("\n");

	printflags("mode attributes:", modeattributesflags, WORD(p));
	print("bytes per scan line: %d\n", WORD(p+16));
	print("resolution: %dx%d\n", WORD(p+18), WORD(p+20));
	print("charsize: %dx%d\n", p[22], p[23]);
	print("%d planes, %d bpp, %d banks @ %d kb each\n", p[24], p[25], p[26], p[28]);
	print("memory model %d (%s)\n", p[27],
		p[27] < nelem(modelstr) ? modelstr[p[27]] : "unknown");
	print("r%dg%db%dx%d r@%d g@%d b@%d x@%d\n",
		p[31], p[33], p[35], p[37], p[32], p[34], p[36], p[38]);
	printflags("directcolor:", directcolorflags, p[39]);

	print("physptr: 0x%uX\n", LONG(p+40));
}

void
cmdsetmode(int argc, char **argv)
{
	int n;
	uchar *p;
	Vbe vbe;

	if(argc != 2 && argc != 2+9){
		fprint(2, "?usage: setmode 0xMODE\n");
		return;
	}
	
	memset(&vbe, 0, sizeof vbe);
	vbe.rax = 0x4F02;
	vbe.rcx = atoi(argv[1]);
	vbe.rcx |= 3<<14;	// use linear, don't clear memory 
	p = vbe.buf;

	if(argc == 2+9){
		vbe.rcx |= 1<<11;
		n = atoi(argv[2]); PWORD(p, n); p+=2;
		n = atoi(argv[3]); PWORD(p, n); p+=2;
		n = atoi(argv[4]); PWORD(p, n); p+=2;
		n = atoi(argv[5]); PWORD(p, n); p+=2;
		n = atoi(argv[6]); PWORD(p, n); p+=2;
		n = atoi(argv[7]); PWORD(p, n); p+=2;
		*p++ = atoi(argv[8]);
		n = atoi(argv[9]); PLONG(p, n); p += 4;
		n = atoi(argv[10]); PWORD(p, n); p += 2;
		if(p != vbe.buf+19){
			fprint(2, "prog error\n");
			return;
		}
	}
		
	if(vbecall(&vbe) < 0){
		fprint(2, "vbe error %.4ux\n", vbe.rax);
		return;
	}
}

void
cmdgetmode(int argc, char**)
{
	Vbe vbe;

	if(argc != 1){
		fprint(2, "?usage: getmode\n");
		return;
	}

	memset(&vbe, 0, sizeof vbe);
	vbe.rax = 0x4F03;
	if(vbecall(&vbe) < 0){
		fprint(2, "vbe error %.4ux\n", vbe.rax);
		return;
	}
	print("mode %x%s%s\n", vbe.rbx&0x3FFF, (vbe.rbx&(1<<14)) ? " linear" : " windowed",
		(vbe.rbx&(1<<15)) ? " notcleared" : " cleared");
}


struct {
	char *s;
	void (*fn)(int, char**);
} cmd[] = {
	"trace", cmdtrace,
	"info", cmdinfo,
	"modeinfo", cmdmodeinfo,
	"getmode", cmdgetmode,
	"setmode", cmdsetmode,
};

#define LOWMEM	((uchar*)(0x50000000))
enum {
	MEMSIZE = (0xFFFF<<4)+0xFFFF+1,
	VECSIZE = 0x400,
};

static int	iobfd=-1, iowfd=-1, iolfd=-1;

static int
devopen(char* device, int mode)
{
	int fd;

	if((fd = open(device, mode)) < 0)
		sysfatal("devopen(%s, %d): %r\n", device, mode);
	return fd;
}

int
inportb(int port)
{
	uchar data;

	if(iobfd == -1)
		iobfd = devopen("#P/iob", ORDWR);

	seek(iobfd, port, 0);
	if(read(iobfd, &data, sizeof(data)) != sizeof(data))
		sysfatal("inportb(0x%4.4x): %r\n", port);
	return data;
}

void
outportb(int port, int d)
{
	uchar data;

	if(iobfd == -1)
		iobfd = devopen("#P/iob", ORDWR);

	data = d;
	seek(iobfd, port, 0);
	if(write(iobfd, &data, sizeof(data)) != sizeof(data))
		sysfatal("outportb(0x%4.4x, 0x%2.2X): %r\n", port, data);
}

int
inportw(int port)
{
	uchar data[2];

	if(iowfd == -1)
		iowfd = devopen("#P/iow", ORDWR);

	seek(iowfd, port, 0);
	if(read(iowfd, data, sizeof(data)) != sizeof(data))
		sysfatal("inportw(0x%4.4x): %r\n", port);
	return data[0]|(data[1]<<8);
}

void
outportw(int port, int d)
{
	uchar data[2];

	if(iowfd == -1)
		iowfd = devopen("#P/iow", ORDWR);

	data[0] = d;
	data[1] = d>>8;
	seek(iowfd, port, 0);
	if(write(iowfd, data, sizeof(data)) != sizeof(data))
		sysfatal("outportw(0x%4.4x, 0x%4.4uX): %r\n", port, d);
}

long
inportl(int port)
{
	uchar data[4];

	if(iolfd == -1)
		iolfd = devopen("#P/iol", ORDWR);

	seek(iolfd, port, 0);
	if(read(iolfd, data, sizeof(data)) != sizeof(data))
		sysfatal("inportl(0x%4.4x): %r\n", port);
	return data[0]|(data[1]<<8)|(data[2]<<16)|(data[3]<<24);
}

void
outportl(int port, long d)
{
	uchar data[4];

	if(iolfd == -1)
		iolfd = devopen("#P/iol", ORDWR);

	data[0] = d;
	data[1] = d>>8;
	data[2] = d>>16;
	data[3] = d>>24;

	seek(iolfd, port, 0);
	if(write(iolfd, data, sizeof(data)) != sizeof(data))
		sysfatal("outportl(0x%4.4x, 0x%8.8lX): %r\n", port, d);
}

void
main(int argc, char **argv)
{
	char buf[40], *p, *f[10];
	int fd, i, nf;
	Biobuf bin;

	ARGBEGIN{
	case 't':
		cpu.trace = 1;
		break;
	default:
		usage();
	}ARGEND

	if(argc != 0)
		usage();

	if(segattach(0, "memory", LOWMEM, MEMSIZE) < 0)
		sysfatal("segattach: low memory: %r");
	cpu.mem = LOWMEM;

	sprint(buf, "/proc/%d/mem", getpid());
	if((fd = open(buf, OREAD)) < 0)
		sysfatal("cannot open %s: %r", buf);

	if(seek(fd, 0x80000000U+0xC0000, 0) < 0 ||
	   readn(fd, cpu.mem+0xC0000, 0x10000) != 0x10000)
		sysfatal("cannot read bios memory: %r");

	if(seek(fd, 0x80000000U+0xF0000, 0) < 0 ||
	   readn(fd, cpu.mem+0xF0000, 0x10000) != 0x10000)
		sysfatal("cannot read bios memory: %r");

	if(seek(fd, 0x80000000U+0, 0) < 0
	|| readn(fd, cpu.mem+0, 0x10000) != 0x10000)
		sysfatal("cannot read bios memory: %r");

	ivec = cpu.mem;

	cpu.present64k = (1<<0) | (1<<1) | (1<<0xC) | (1<<0xF);
	notify(interrupt);

	cpu.trap = vbetrap;
	cpu.inb = inportb;
	cpu.inw = inportw;
	cpu.inl = inportl;
	cpu.outb = outportb;
	cpu.outw = outportw;
	cpu.outl = outportl;

	Binit(&bin, 0, OREAD);
	while((p = Brdline(&bin, '\n')) != nil){
		p[Blinelen(&bin)-1] = '\0';
		nf = tokenize(p, f, nelem(f));
		if(nf == 0)
			continue;
		for(i=0; i<nelem(cmd); i++){
			if(strcmp(f[0], cmd[i].s) == 0){
				cmd[i].fn(nf, f);
				break;
			}
		}
		if(i==nelem(cmd))
			fprint(2, "?\n");
	}
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].