{"id":13782548,"url":"https://github.com/angavrilov/exhaust-asm","last_synced_at":"2026-04-17T05:32:48.299Z","repository":{"id":563188,"uuid":"194216","full_name":"angavrilov/exhaust-asm","owner":"angavrilov","description":"Reimplementation of the interpreter in Intel Core 2 64-bit assembly with SSE3 \u0026 4.1","archived":false,"fork":false,"pushed_at":"2009-05-06T18:40:34.000Z","size":144,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-20T19:11:52.525Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://www.cs.helsinki.fi/u/jpihlaja/exhaust/exhaust.html","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/angavrilov.png","metadata":{"files":{"readme":"README","changelog":"changelog","contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2009-05-06T18:39:50.000Z","updated_at":"2019-08-13T14:24:24.000Z","dependencies_parsed_at":"2022-08-16T10:30:55.049Z","dependency_job_id":null,"html_url":"https://github.com/angavrilov/exhaust-asm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/angavrilov%2Fexhaust-asm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/angavrilov%2Fexhaust-asm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/angavrilov%2Fexhaust-asm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/angavrilov%2Fexhaust-asm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/angavrilov","download_url":"https://codeload.github.com/angavrilov/exhaust-asm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243500771,"owners_count":20300770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T18:01:38.941Z","updated_at":"2025-12-30T05:54:32.989Z","avatar_url":"https://github.com/angavrilov.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"$Id: README,v 1.11 2002/10/01 22:32:27 rowan Exp $\n\nThis is exhaust version 1.9, a memory array redcode simulator.  It is\nmostly in the public domain (see the file COPYING for details).\n\nFiles:\n        changelog          Log of Change.\n        COPYING            lack of license.\n        Makefile           file for make.\n        README             This file.\n\tNEWS\t\t   Notes about things that might break.\n        asm.c              (Dis)Assembler.\n        asm.h              Token codes and prototypes for assembler.\n        exhaust.c          Simplified pMARS-like simulator.\n        exhaust.h          Global constants, structures and types.\n        insn.h             Instruction encoding specs.\n        sim.c              Simulator proper.\n        sim.h              Simulator prototypes.\n\tpspace.c\t   P-space implementation.\n\tpspace.h\t   P-space prototypes.\n        test.pl            Perl program to test for differences between\n                           simulators.\n        t/*.red\t\t   Redcode for tests.\n\tt/*.rc\t\t   Load files for tests.\n\nTo compile on UNIX or on DOS with djgpp first edit the makefile to\nyour comfort and then run `make' to get an executable `exhaust'.  If\nyou have gcc you hopefully don't need to make any large makefile\nmodifications.  See the section `How to use the simulator and friends'\nbelow for an overview of how to use the simulator in your programs.\n\nThanks to the pMARS authors and Ken Espiritu for ideas implemented in\nthis simulator.  Especially Ken's effective addressing calculation code\nfrom pMARS 0.8.6 was adapted for a great speed-up.\n\n\n* How to use the simulator and friends\n--------------------------------------\n\nThis is a quick run-down on how to use the simulator and assembler\nwith your program.  Special considerations for P-Space are detailed in\nthe section about p-space below, so here non-P warriors are assumed.\nAfter munging your source file, remember to link your executable with\nsim.o, pspace.o, and asm.o!  You don't need asm.o if you don't use the\nassembler or disassembler.\n\nThe main things that you should do are:\n  - generate (perhaps by assembling) the warrior code\n  - allocate the internal buffers\n  - clear core and load the warriors into it\n  - call the simulator to do it's thing\n\nIn the examples, I assume you use a warrior structure similar to\nexhaust.h's struct warrior_st, but it is not canonical in any way.\nHere I rely on the following fields:\n\n        insn_t code[ MAXLENGTH ];   /* code of warrior */\n        int len;                    /* length of warrior code */\n        int start;                  /* start relative to first insn */\n\n* The assembler: \n\ni) You need some declarations and includes:\n\n#include \"exhaust.h\"\n#include \"asm.h\"\n\n        warrior_t w1, w2;       /* warrior structs hold code and misc. info */\n        int CORESIZE = 8000,    /* size of core */\n            PROCESSES = 8000,   /* max. number of processes per warrior */\n            NWARRIORS = 2,      /* number of warriors */\n            CYCLES = 80000;     /* cycles until tie */\n\nii) Then you want to assemble your warriors, say aeka and Rave:\n\n        asm_fname( \"aeka.rc\", \u0026w1, CORESIZE );\n        asm_fname( \"rave.rc\", \u0026w2, CORESIZE );\n\nYou could also use asm_file(FILE *, warrior_t *, CORESIZE) to assemble\nfrom a stream.\n\n\n* Simulator:\n\ni) The simulator interface is simple.  First you need to allocate and\nquery the address of the core memory:\n\n#include \"exhaust.h\"\n#include \"sim.h\"\n        insn_t *core;\n\n        core = sim_alloc_bufs( NWARRIORS, CORESIZE, PROCESSES, CYCLES );\n\nIf any of the allocations failed the returned value will be NULL.  You\nshould check for that. When you're done with the core memory and other\nbuffers, call sim_free_bufs(void), but note that sim_alloc_bufs()\nfrees any memory it previously allocated.  i.e. you can successively\ncall it without intervening calls to sim_free_bufs().\n\nFor backward compatibility you can also use use sim() as follows to\nallocate with the default CORESIZE=PROCESSES=8000:\n\n        (void) sim( -1, 0, 0, CYCLES, \u0026core );\n\nii) Then you clear core and load the warriors:\n\n        memset( core, 0, sizeof(insn_t) * CORESIZE );\nor\n\tsim_clear_core();\n\nand\n\tsim_load_warrior(0,   \u0026(w1.code[0]), w1.len );\n\tsim_load_warrior(pos, \u0026(w2.code[0]), w2.len );\n\nwhere `pos' is the core address where you want to load w2.  This\ndiffers slightly from the previous release where you had to:\n\n        memcpy( core,       w1.code, sizeof(insn_t) * w1.len );\n        memcpy( core + pos, w2.code, sizeof(insn_t) * w2.len );\n\nYou can still do that.  (Note however, that the flags field of\ninstructions should be cleared as the simulator won't do it anymore.\nsim_load_warrior() does this for you.  You can get the old behaviour\nby defining the preprocessor variable SIM_STRIP_FLAGS to 1 at compile\ntime.)\n\n\niii) Right, now you're ready to fight.  If you have only one or two\nwarriors, you can use sim():\n\n        result = sim( 2, w1.start, (w2.start+pos)%CORESIZE, CYCLES, NULL);\n\nThe result is 0 if w1 won, 1 if w2 won, and 2 on a tie (-1 if the\nsimulator panics).\n\nFor multi-warrior battles with more than two warriors you need\nsome extra declarations:\n\n        field_t war_pos_tab[ NWAR ];    /* a table of core addresses\n                                         * that specify where the warriors\n                                         * start their execution. */\n        int death_tab[ NWAR ];          /* the simulator stores the\n                                         * results here. (see below) */\n\nThen allocate the buffers and obtain the core address using\nsim_alloc_bufs(), clear the core, and load the warriors into it as\nbefore.  Save the positions where you loaded the warriors into\nwar_pos_tab[].  Now you're ready to fight, so call sim_mw():\n\n        int alive_cnt;\n        alive_cnt = sim_mw( NWAR, war_pos_tab, death_tab );\n\nThe return value is the number of warriors alive at the end of the\nround.  Dead warriors are recorded into death_tab[] by order of death.\ni.e. death_tab[0] contains the index of the first warrior to die,\ndeath_tab[1] the index of the second to go, and so on.\n\n\n* The disassembler:\n\nIf you want to disassemble an instruction use dis1() or discore() from\nasm.c.  Examples:\n\n#include \"exhaust.h\"\n#include \"asm.h\"\n\n        char str[60];\n        insn_t instr;\n\n        instr.in = _OP( SPL, mF, DIRECT, BPREDEC );\n        instr.a = instr.b = 0;\n        core[1234] = instr;\n\n        dis1( str, core[1234], CORESIZE ); printf(\"%s\\n\", str);\n        dis1( str, instr, CORESIZE );      printf(\"%s\\n\", str);\n\n                                        /* disassemble addresses 5..14 */\n        discore( core, 5, 15, CORESIZE);/* and print to stdout. */\n\n\n* P-Space\n---------\n\nThe simulator interface is oriented toward playing single battles, but\np-space by its nature requires a multi-round interface.  Hence there\nare some complications when playing p-space warriors.  The main change\nfor P-spacer battles over normal ones is that you need to pair\np-spaces to warriors.  Also, if you are running multiple battles you\nneed to reinitialise the p-spaces between each battle.\n\nThe basic model is simple: the simulator maintains an array of\nNWARRIORS p-spaces that you may manipulate.  The ith p-space in this\narray always corresponds to the ith warrior passed to sim() or\nsim_mw(), so that the first warrior always accesses the first p-space,\nthe second the second, and so on.  The pairing betweens p-spaces and\nwarriors is up to you.  After each round the p-space location 0 of\neach warrior is updated with either a) zero if the warrior died or b)\nthe number of warriors alive at the end of the battle.\n\n[A note about naming conventions: pspace_XXX() functions are mostly\nlocal in nature -- they refer only to their argument p-spaces.  The\nsim_XXX_pspace() functions are about the collection of p-spaces\nmaintained by the simulator.]\n\n\n* Accessing p-spaces\n\nTo get a pointer to the ith p-space you use the function\nsim_get_pspace():\n\n\tpspace_t *p = sim_get_pspace(i);\n\nThen to access the cells of `p', use the functions pspace_get() and\npspace_set():\n\n\tcontents_of_cell_123 = pspace_get(p, 123);\n\n\tpspace_set(p, 123, new_contents);\n\n\n* P-space initialisation; resets\n\n - The size of p-space is specified at memory allocation time using\n   the function sim_alloc_bufs2().  It is exactly like\n   sim_alloc_bufs(), except it takes as an additional parameter the\n   size of pspace, which must be positive.  The later function\n   defaults p-space to CORESIZE/16 cells, so you don't need to change\n   it.\n\n - The function sim_clear_pspaces() clears the p-spaces of all warriors\n   and stores CORESIZE-1 into their p-space location 0.  P-Space is\n   cleared on allocation, so you only need to call this function\n   between battles, but not rounds.  This function doesn't change\n   the sharing status of p-spaces.\n\n - To completely reinitialise p-spaces and to make p-spaces of each\n   warrior private again, call the sim_reset_pspaces() function.  This\n   also calls sim_clear_pspaces().\n\n\n* Pairing p-spaces to warriors\n\nIf you are changing the order in which warriors execute within each\ncycle on a per-round basis, you need to order p-spaces as well.  For\nexample, pMARS cyclically permutes the order of warriors so that every\neven round the first warrior executes first in each cycle, and every\nodd round the second warrior executes first.  With non-pspace warriors\nyou only need to give the starting positions of the warriors in the\n`war_pos_tab[]' argument in the order you want the warriors to\nexecute, but with p-warriors involved you need to match p-spaces to\nwarriors as well.\n\n - The simulator maintains an array of pointers to p-space structures,\n   one for each warrior.  The ith p-space structure corresponds to the\n   ith warrior in the `war_pos_tab[]' array argument of warrior\n   starting positions passed to sim_mw().  Similarly for the two\n   warriors given to the sim() function.  You need to order this array\n   of pointers to match the order in which you are passing warrior\n   starting positions to the simulator.\n\n   To access the array, use sim_get_pspaces():\n\n\tpspace_t **pspaces;\n\tpspaces = sim_get_pspaces();\n\n\t// now permute the pointers in the pspaces array into the\n\t// same order as your warriors.\n\t...\n\n   Of course, if you are not changing the order in which warriors\n   execute on a per-round basis, you don't need to permute the pspaces\n   array.  This may be suitable in some cases.\n\n\n* Sharing p-spaces\n\nThere is only minimal support for PINs and p-space sharing in that the\nassembler understands the `PIN' pseudo-op, but you must match PINs to\np-spaces yourself.\n\n  - The warrior structure warrior_st in exhaust.h needs the extra fields\n  \n\tint have_pin;\t\t\t// boolean: is field `pin' valid?\n\tu32_t pin;\t\t\t// PIN of warrior, possibly.\n\n    that are used when assembling from files/streams.  The interface\n    to asm_line() has changed accordingly so it can identify and\n    return the PIN pseudo-op.\n\n  - To share p-spaces of two warriors, you need to call the function\n    pspace_share(p1, p2) for two pointers p1, p2 to p-spaces.  The\n    contents of p-space p1 becomes the contents of the shared p-space.\n    P-space location 0 is never shared.\n\n  - The function pspace_privatise(p) resets the p-space p to be\n    private.  The contents of p1 are then undefined.\n\n\n\n* Files that the assembler eats\n-------------------------------\n\nIn short: the load files that the '94 draft specifies are accepted.\nSpecifically, the files may contain only `;' comments, white space,\ninstructions, and pseudo-ops.  The instructions must follow this form:\n\n  [opt START label]    OPCODE.MODIFIER  MODE integer , MODE integer\n\nVarious idiosyncrasies:\n\n - You can't implicitly specify direct addressing mode by dropping `$'.\n\n - The start of a warrior is determined by the last seen appearance of\n   the START label or `ORG'.  Of the pseudo-ops using `END label' or\n   `ORG label' aren't supported; only `ORG integer' is.\n\n - `END' stops warrior assembly.  You still can assemble from the\n   same file or stream another warrior that follows though.\n\n - special KotH comments aren't recognised.\n\nTo support using pMARS as an assembler any line that begins with\n`Program' is ignored.  So you could first say\n\n  pmars -r 0 Fancy_Source.red \u003eSimple_Source.rc\n\nand then use the asm.c assembler to import Simple_Source.rc into your\nprogram.  Note that pmars doesn't output PINs of warriors.\n\n\n* When the simulator crashes\n----------------------------\n\n... or is just plain wrong by producing incorrect results please tell\nme about it at the address given below.  Ditto for the assembler or\nanything else in these files.  I'd appreciate a simple test case that\nillustrates the bug if possible.\n\nThe author can be reached by email at: jpihlaja@cc.helsinki.fi\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fangavrilov%2Fexhaust-asm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fangavrilov%2Fexhaust-asm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fangavrilov%2Fexhaust-asm/lists"}