1$Id: README,v 18.104.22.168 2003/08/26 16:57:02 varfar Exp $
3This is exhaust version 1.9, a memory array redcode simulator. It is
4mostly in the public domain (see the file COPYING for details).
7 changelog Log of Change.
8 COPYING lack of license.
9 Makefile file for make.
10 README This file.
11 NEWS Notes about things that might break.
12 asm.c (Dis)Assembler.
13 asm.h Token codes and prototypes for assembler.
14 exhaust.c Simplified pMARS-like simulator.
15 exhaust.h Global constants, structures and types.
16 insn.h Instruction encoding specs.
17 sim.c Simulator proper.
18 sim.h Simulator prototypes.
19 pspace.c P-space implementation.
20 pspace.h P-space prototypes.
21 test.pl Perl program to test for differences between
23 t/*.red Redcode for tests.
24 t/*.rc Load files for tests.
26To compile on UNIX or on DOS with djgpp first edit the makefile to
27your comfort and then run `make' to get an executable `exhaust'. If
28you have gcc you hopefully don't need to make any large makefile
29modifications. See the section `How to use the simulator and friends'
30below for an overview of how to use the simulator in your programs.
32Thanks to the pMARS authors and Ken Espiritu for ideas implemented in
33this simulator. Especially Ken's effective addressing calculation code
34from pMARS 0.8.6 was adapted for a great speed-up.
37* How to use the simulator and friends
40This is a quick run-down on how to use the simulator and assembler
41with your program. Special considerations for P-Space are detailed in
42the section about p-space below, so here non-P warriors are assumed.
43After munging your source file, remember to link your executable with
44sim.o, pspace.o, and asm.o! You don't need asm.o if you don't use the
45assembler or disassembler.
47The main things that you should do are:
48 - generate (perhaps by assembling) the warrior code
49 - allocate the internal buffers
50 - clear core and load the warriors into it
51 - call the simulator to do it's thing
53In the examples, I assume you use a warrior structure similar to
54exhaust.h's struct warrior_st, but it is not canonical in any way.
55Here I rely on the following fields:
57 insn_t code[ MAXLENGTH ]; /* code of warrior */
58 int len; /* length of warrior code */
59 int start; /* start relative to first insn */
61* The assembler:
63i) You need some declarations and includes:
68 warrior_t w1, w2; /* warrior structs hold code and misc. info */
69 int CORESIZE = 8000, /* size of core */
70 PROCESSES = 8000, /* max. number of processes per warrior */
71 NWARRIORS = 2, /* number of warriors */
72 CYCLES = 80000; /* cycles until tie */
74ii) Then you want to assemble your warriors, say aeka and Rave:
76 asm_fname( "aeka.rc", &w1, CORESIZE );
77 asm_fname( "rave.rc", &w2, CORESIZE );
79You could also use asm_file(FILE *, warrior_t *, CORESIZE) to assemble
80from a stream.
85i) The simulator interface is simple. First you need to allocate and
86query the address of the core memory:
90 insn_t *core;
92 core = sim_alloc_bufs( NWARRIORS, CORESIZE, PROCESSES, CYCLES );
94If any of the allocations failed the returned value will be NULL. You
95should check for that. When you're done with the core memory and other
96buffers, call sim_free_bufs(void), but note that sim_alloc_bufs()
97frees any memory it previously allocated. i.e. you can successively
98call it without intervening calls to sim_free_bufs().
100For backward compatibility you can also use use sim() as follows to
101allocate with the default CORESIZE=PROCESSES=8000:
103 (void) sim( -1, 0, 0, CYCLES, &core );
105ii) Then you clear core and load the warriors:
107 memset( core, 0, sizeof(insn_t) * CORESIZE );
112 sim_load_warrior(0, &(w1.code), w1.len );
113 sim_load_warrior(pos, &(w2.code), w2.len );
115where `pos' is the core address where you want to load w2. This
116differs slightly from the previous release where you had to:
118 memcpy( core, w1.code, sizeof(insn_t) * w1.len );
119 memcpy( core + pos, w2.code, sizeof(insn_t) * w2.len );
121You can still do that. (Note however, that the flags field of
122instructions should be cleared as the simulator won't do it anymore.
123sim_load_warrior() does this for you. You can get the old behaviour
124by defining the preprocessor variable SIM_STRIP_FLAGS to 1 at compile
128iii) Right, now you're ready to fight. If you have only one or two
129warriors, you can use sim():
131 result = sim( 2, w1.start, (w2.start+pos)%CORESIZE, CYCLES, NULL);
133The result is 0 if w1 won, 1 if w2 won, and 2 on a tie (-1 if the
136For multi-warrior battles with more than two warriors you need
137some extra declarations:
139 field_t war_pos_tab[ NWAR ]; /* a table of core addresses
140 * that specify where the warriors
141 * start their execution. */
142 int death_tab[ NWAR ]; /* the simulator stores the
143 * results here. (see below) */
145Then allocate the buffers and obtain the core address using
146sim_alloc_bufs(), clear the core, and load the warriors into it as
147before. Save the positions where you loaded the warriors into
148war_pos_tab. Now you're ready to fight, so call sim_mw():
150 int alive_cnt;
151 alive_cnt = sim_mw( NWAR, war_pos_tab, death_tab );
153The return value is the number of warriors alive at the end of the
154round. Dead warriors are recorded into death_tab by order of death.
155i.e. death_tab contains the index of the first warrior to die,
156death_tab the index of the second to go, and so on.
159* The disassembler:
161If you want to disassemble an instruction use dis1() or discore() from
167 char str;
168 insn_t instr;
170 instr.in = _OP( SPL, mF, DIRECT, BPREDEC );
171 instr.a = instr.b = 0;
172 core = instr;
174 dis1( str, core, CORESIZE ); printf("%s\n", str);
175 dis1( str, instr, CORESIZE ); printf("%s\n", str);
177 /* disassemble addresses 5..14 */
178 discore( core, 5, 15, CORESIZE);/* and print to stdout. */
184The simulator interface is oriented toward playing single battles, but
185p-space by its nature requires a multi-round interface. Hence there
186are some complications when playing p-space warriors. The main change
187for P-spacer battles over normal ones is that you need to pair
188p-spaces to warriors. Also, if you are running multiple battles you
189need to reinitialise the p-spaces between each battle.
191The basic model is simple: the simulator maintains an array of
192NWARRIORS p-spaces that you may manipulate. The ith p-space in this
193array always corresponds to the ith warrior passed to sim() or
194sim_mw(), so that the first warrior always accesses the first p-space,
195the second the second, and so on. The pairing betweens p-spaces and
196warriors is up to you. After each round the p-space location 0 of
197each warrior is updated with either a) zero if the warrior died or b)
198the number of warriors alive at the end of the battle.
200[A note about naming conventions: pspace_XXX() functions are mostly
201local in nature -- they refer only to their argument p-spaces. The
202sim_XXX_pspace() functions are about the collection of p-spaces
203maintained by the simulator.]
206* Accessing p-spaces
208To get a pointer to the ith p-space you use the function
211 pspace_t *p = sim_get_pspace(i);
213Then to access the cells of `p', use the functions pspace_get() and
216 contents_of_cell_123 = pspace_get(p, 123);
218 pspace_set(p, 123, new_contents);
221* P-space initialisation; resets
223 - The size of p-space is specified at memory allocation time using
224 the function sim_alloc_bufs2(). It is exactly like
225 sim_alloc_bufs(), except it takes as an additional parameter the
226 size of pspace, which must be positive. The later function
227 defaults p-space to CORESIZE/16 cells, so you don't need to change
230 - The function sim_clear_pspaces() clears the p-spaces of all warriors
231 and stores CORESIZE-1 into their p-space location 0. P-Space is
232 cleared on allocation, so you only need to call this function
233 between battles, but not rounds. This function doesn't change
234 the sharing status of p-spaces.
236 - To completely reinitialise p-spaces and to make p-spaces of each
237 warrior private again, call the sim_reset_pspaces() function. This
238 also calls sim_clear_pspaces().
241* Pairing p-spaces to warriors
243If you are changing the order in which warriors execute within each
244cycle on a per-round basis, you need to order p-spaces as well. For
245example, pMARS cyclically permutes the order of warriors so that every
246even round the first warrior executes first in each cycle, and every
247odd round the second warrior executes first. With non-pspace warriors
248you only need to give the starting positions of the warriors in the
249`war_pos_tab' argument in the order you want the warriors to
250execute, but with p-warriors involved you need to match p-spaces to
251warriors as well.
253 - The simulator maintains an array of pointers to p-space structures,
254 one for each warrior. The ith p-space structure corresponds to the
255 ith warrior in the `war_pos_tab' array argument of warrior
256 starting positions passed to sim_mw(). Similarly for the two
257 warriors given to the sim() function. You need to order this array
258 of pointers to match the order in which you are passing warrior
259 starting positions to the simulator.
261 To access the array, use sim_get_pspaces():
263 pspace_t **pspaces;
264 pspaces = sim_get_pspaces();
266 // now permute the pointers in the pspaces array into the
267 // same order as your warriors.
270 Of course, if you are not changing the order in which warriors
271 execute on a per-round basis, you don't need to permute the pspaces
272 array. This may be suitable in some cases.
275* Sharing p-spaces
277There is only minimal support for PINs and p-space sharing in that the
278assembler understands the `PIN' pseudo-op, but you must match PINs to
281 - The warrior structure warrior_st in exhaust.h needs the extra fields
283 int have_pin; // boolean: is field `pin' valid?
284 u32_t pin; // PIN of warrior, possibly.
286 that are used when assembling from files/streams. The interface
287 to asm_line() has changed accordingly so it can identify and
288 return the PIN pseudo-op.
290 - To share p-spaces of two warriors, you need to call the function
291 pspace_share(p1, p2) for two pointers p1, p2 to p-spaces. The
292 contents of p-space p1 becomes the contents of the shared p-space.
293 P-space location 0 is never shared.
295 - The function pspace_privatise(p) resets the p-space p to be
296 private. The contents of p1 are then undefined.
300* Files that the assembler eats
303In short: the load files that the '94 draft specifies are accepted.
304Specifically, the files may contain only `;' comments, white space,
305instructions, and pseudo-ops. The instructions must follow this form:
307 [opt START label] OPCODE.MODIFIER MODE integer , MODE integer
311 - You can't implicitly specify direct addressing mode by dropping `$'.
313 - The start of a warrior is determined by the last seen appearance of
314 the START label or `ORG'. Of the pseudo-ops using `END label' or
315 `ORG label' aren't supported; only `ORG integer' is.
317 - `END' stops warrior assembly. You still can assemble from the
318 same file or stream another warrior that follows though.
320 - special KotH comments aren't recognised.
322To support using pMARS as an assembler any line that begins with
323`Program' is ignored. So you could first say
325 pmars -r 0 Fancy_Source.red >Simple_Source.rc
327and then use the asm.c assembler to import Simple_Source.rc into your
328program. Note that pmars doesn't output PINs of warriors.
331* When the simulator crashes
334... or is just plain wrong by producing incorrect results please tell
335me about it at the address given below. Ditto for the assembler or
336anything else in these files. I'd appreciate a simple test case that
337illustrates the bug if possible.
339The author can be reached by email at: email@example.com