/*
* HOME(PROJECTS) || RESUME || LINKS || ABOUT
*/

|Newest| . . . |<<Newer| . . . . . . |Older >>| . . . |Oldest|
(view all as one document)

Retrochallenge 2026/10

October 23

First full program runs successfully!

I got 8080PRE to run to completion. I added some extra instrumentation to the linux version of i8080-core, so that it outputs a list of opcodes that were actually executed. I added the same to the glsl version. Diffing them gave a short list of missing opcodes. Most of them were simply copy/paste from the C version. There are some differences, such as the C version using unions to alias bytes and words. This lets H, L, and HL to all be valid. I made a few changes, like POP(HL) is now POP2(H,L), which splits to result into two register variables. POP(PC) is also now POP16, which pops into a single word-sized register. In GLSL, I can't really alias pairs like A and B to also show up as AB, so anything that needs a register pair as a 16-bit value, or needs to store a 16-bit value to a register pair, needs to go thru an extra macro. That's OK! When this is all done, I'm going to diff the glsl and C/CMOC versions, and I suspect I can come up with a version of the switch/case that works on both. For instance, POP2 and POP16 could work in C just fine.

I implemented a function pointer table in C that lets you plug-in whatever syscalls you want. Here is the C_WRITESTR implementation:


/* 

C function pointer syntax is tricky:

if syscalls[256] is an array of 256 entries...

then (*syscalls[256]) means each entry is a pointer...

and then (state8080* state, int* mem) means that pointer to to a function that accepts there two args

*/

(*syscalls[256])(state8080* state, int* mem) = {NULL};


//CP/M call to write a string to the console.  Strings are "$" terminated!  

void C_WRITESTR(state8080* state, int* mem){
	int ptr = (state->D <<8 | state->E) & 0xffff;

	while (mem[ptr]!='$'){
		fputc(mem[ptr++], stdout);
		ptr &= 0xffff;
	}

}


Then when setting up:


	intdata[5] = 0x8000; //trap instruction for CP/M calls
	syscalls[9] = C_WRITESTR;

In glsl, the undefined opcode fault handler catches it:

  switch(op){
	// Actual handlers go here

        default:
            current.fault = 0x10000000 | op;
  }

And in C, it is handled like this:



	if (state.fault & 0x10000000){
		int op = state.fault&0xffff;


		if (op == 0x8000){

			int call = state.C;

			fprintf(stderr, "syscall %x\n", call);

			if (syscalls[call])
				syscalls[call](&state, intdata);

			state.PC = intdata[state.SP];
			state.SP+=2;
		}
	}

To call a C function from the 8080 host, all that needs to happen is you need to write the C function that accepts the CPU state and memory array, and then drop it into the function table.

Next are the rest of the CPU exercisers, and then... Altair BASIC. If Altair BASIC runs (and I would expect it to, since it ran on the TRS-80 version of this emulator), then... we have a GPU implementation of BASIC. What to use it for? I would like to run it in a pixel shader. One 'hiccup' is that the memory array is a SSBO, so to run it for a pixel, each needs their own private space. But, RD_BYTE and WR_BYTE are macros... which means I can replace them with whatever complicated function I want. Things that don't change per pixel, like the BASIC code and the ALTAIR BASIC itself, can be shared. The only thing that needs to be replicated and private are the stack, and any variables, internal state, etc. What I want to do is run a single instance of Altair BASIC up to the point where the program has been 'typed in', RUN is typed, and its just waiting for the return key. This state will then be in the shared SSBO 'ROM.' Then, there will be a small 'page table' for each pixel, and parts modified per-pixel will be stored seperately. If fixed-sized pages are used, then each pixel has a small fixed-sized pre-allocated section of a large shared array to maintain that page table. When a new page is being written, each shader can atomically allocate a page from a shared large array of pages. A similar per-shader dynamic allocation method is done here to allocate sparse structures in glsl shaders: Compacting Draw Indices with Atomic Adds in GLSL.

|Newest| . . . |<<Newer| . . . . . . |Older >>| . . . |Oldest|
(view all as one document)