New game uploaded: Core War
New game uploaded: Core War
I want to add visualization effects next. Particularly, when an instruction changes an address, I want that address to be lit up in the owning warrior's color. Etc.
The core size is just over 4000 locations, which just about fills the screen with some room to spare for status text.
New game uploaded: Core War
OK, visualization added.
Not very pretty yet though. I'll clean that up.
New game uploaded: Core War
Cleaned it up a little bit. Needs more work.
New game uploaded: Core War
A bit better. Getting there.
Well, it's usable, if unfriendly, and at least some of it works correctly.
New game uploaded: Core War
OK, moved the Core into RAM banks, increased the address range of instruction operands, and upped the Core size to 7919 locations (a prime number).
I'm quite happy with that. I now have room for some of the features of normal Core War engines -- e.g. label parsing. I might do that.
New game uploaded: Core War
Optional Operands
The earliest Redcode didn't have labels, but it DID have "optional" operands. In these cases, the omitted operand was set to #0. Per '88, these are:
DAT: the B operand is required.
JMP: the A operand is required.
SPL: the A operand is required.
XCH: the A operand is required.
This allows a shorthand, where some opcodes appear to be of a different valence.
Of course, my parser currently laughs at such silliness.
But it's easy to implement this.
New game uploaded: Core War
I'm working through the implications of moving to multiple banks.
Since I hold _pointers_ to some data, I have to take care that the underlying memory doesn't change out from under them!
Or more correctly, I have to know when to copy out values instead of using pointers.
Yes, I have some pointers into banked RAM. Not very many, and I'm fixing the places where that's a bad idea.
* * *
The Arena
The arena is (currently) 8191 cells wide. Bank determination is used with two constants:
#define CELLS_PER_BANK 1000
#define CORE_MEMORY(n) ((Cell *)0xA000)[n % CELLS_PER_BANK]
And setting the bank number is:
unsigned char bank = 10 + (position / CELLS_PER_BANK);
* * *
Cell Size
1000 cells per bank lets the cell size range up to 8 bytes. But what's a reasonable upper limit?
Assume an arena would never be larger than 16,000 cells -- in other words, assume a cell operand doesn't have to address more than a 14 bit space, ever.
A cell has an opcode, two operands, and each operand has a mode.
The opcode is 4 bits.
Each operand is (as above) 14 bits, and each operand's mode is 2 bits.
That's 4 + 14 + 2 + 14 + 2 = 36 bits = 5 bytes. 4 bits left over for whatever.
So our current usage of each bank is 5,000 bytes. I could pack in 1,600 cells per bank.
New game uploaded: Core War
I'm slowly tracking bugs down.
My current bug is in bank handling of course.
I have a redcode program that "bombs" the core systematically. It works great... until it hits a bank-boundary.
The apparent behavior is that, when crossing the bank boundary, that the next instruction to be executed doesn't reset the bank. It's as if the execution tries to execute in the incremented bank -- with disastrous results, since the entire core except for the program itself is zeros, causing the program to die.
However, the instruction pointer appears to be pointing to the right place.
* * *
Ah, instead of the bank blowing up, it is possible that I'm blowing the stack. Likely even. OK.
New game uploaded: Core War
At the point of presumed failure, the call stack looks something like this:
main() -> repl() -> run corewar() -> vm_execute() -> get location(ip).
I judiciously removed most calling parameters, except for the last one there.
I encountered the stack-blowing problem yesterday when I couldn't LOAD redcode programs into the core. I reorganized my code to have fewer levels of nested calls and the problem went away.
So now for example the call stack for loading redcode into the core is:
main() -> repl() -> loadfile( filename )
main() -> repl() -> parse bank()
main() -> repl() -> copy program into core()
So I bet I need to remove a level in the runner above.
It only fails when the bank boundary is crossed; presumably, there's one more level of call where the stack finally collapses (so to speak). Moving the runner down one level might prevent that failure.
New game uploaded: Core War
I could replace "run corewar()" with a function that figures out the nextProcessToRun. It can use global variables to track not only the current program, but the current subprocess number to run. The function would increment to the next live process.
That would remove a level.