summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/cpu/apexc/apexc.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/cpu/apexc/apexc.c')
-rw-r--r--trunk/src/emu/cpu/apexc/apexc.c954
1 files changed, 954 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/apexc/apexc.c b/trunk/src/emu/cpu/apexc/apexc.c
new file mode 100644
index 00000000000..e50ab8e3ce1
--- /dev/null
+++ b/trunk/src/emu/cpu/apexc/apexc.c
@@ -0,0 +1,954 @@
+/*
+ cpu/apexc/apexc.c: APE(X)C CPU emulation
+
+ By Raphael Nabet
+
+ APE(X)C (All Purpose Electronic X-ray Computer) was a computer built by Andrew D. Booth
+ and others for the Birkbeck College, in London, which was used to compute cristal
+ structure using X-ray diffraction.
+
+ It was one of the APEC series of computer, which were simple electronic computers
+ built in the early 1950s for various British Universities. Known members of this series
+ are:
+ * APE(X)C: Birkbeck College, London (before 1953 (1951?))
+ * APE(N)C: Board of Mathematical Machines, Oslo
+ * APE(H)C: British Tabulating Machine Company
+ * APE(R)C: British Rayon Research Association
+ * UCC: University College, London (circa january 1956)
+ * MAC (Magnetic Automatic Calculator): "built by Wharf Engineering Laboratories"
+ (february 1955), which used some germanium diodes
+ * The HEC (built by the British Tabulating Machine Company), a commercial machine sold
+ in two models at least (HEC 2M and HEC 4) (before 1955)
+
+ References:
+ * Andrew D. Booth & Kathleen H. V. Booth: Automatic Digital Calculators, 2nd edition
+ (Buttersworth Scientific Publications, 1956) (referred to as 'Booth&Booth')
+ * Kathleen H. V. Booth: Programming for an Automatic Digital Calculator
+ (Buttersworth Scientific Publications, 1958) (referred to as 'Booth')
+ * Digital Engineering Newsletter vol 7 nb 1 p 60 and vol 8 nb 1 p 60-61 provided some
+ dates
+*/
+
+/*
+ Generals specs:
+ * 32-bit data word size (10-bit addresses): uses fixed-point, 2's complement arithmetic
+ * CPU has one accumulator (A) and one register (R), plus a Control Register (this is
+ what we would call an "instruction register" nowadays). No Program Counter, each
+ instruction contains the address of the next instruction (!).
+ * memory is composed of 256 (maximal value only found on the UCC - APE(X)C only has
+ 32 tracks) circular magnetic tracks of 32 words: only 32 tracks can
+ be accessed at a time (the 16 first ones, plus 16 others chosen by the programmer),
+ and the rotation rate is 3750rpm (62.5 rotations per second).
+ * two I/O units: tape reader and tape puncher. A teletyper was designed to read
+ specially-encoded punched tapes and print decoded text. (See /systems/apexc.c)
+ * machine code has 15 instructions (!), including add, substract, shift, multiply (!),
+ test and branch, input and punch. A so-called vector mode allow to repeat the same
+ operation 32 times with 32 successive memory locations. Note the lack of bitwise
+ and/or/xor (!) .
+ * 1 kIPS, although memory access times make this figure fairly theorical (drum rotation
+ time: 16ms, which would allow about 60IPS when no optimization is made)
+ * there is no indirect addressing whatever, although dynamic modification of opcodes (!)
+ allows to simulate it...
+ * a control panel allows operation and debugging of the machine. (See /systems/apexc.c)
+
+ Conventions:
+ Bits are numbered in big-endian order, starting with 1: bit #1 is the
+ MSBit, and bit #32 is the LSBit.
+
+ References:
+ * Andrew D. Booth & Kathleen H. V. Booth: Automatic Digital Calculators, 2nd edition
+ (Buttersworth Scientific Publications, 1956)
+ * Kathleen H. V. Booth: Programming for an Automatic Digital Calculator
+ (Buttersworth Scientific Publications, 1958)
+*/
+
+/*
+ Machine code (reference: Booth):
+
+ Format of a machine instruction:
+bits: 1-5 6-10 11-15 16-20 21-25 26-31 32
+field: X address X address Y address Y address Function C6 Vector
+ (track) (location) (track) (location)
+
+ Meaning of fields:
+ X: address of an operand, or immediate, or meaningless, depending on Function
+ (When X is meaningless, it should be a duplicate of Y. Maybe this is because
+ X is unintentionnally loaded into the memory address register, and if track # is
+ different, we add unneeded track switch delays (this theory is either wrong or
+ incomplete, since it cannot be true for B or X))
+ Y: address of the next instruction
+ Function: code for the actual instruction executed
+ C6: immediate value used by shift, multiply and store operations
+ Vector: repeat operation 32 times (on all 32 consecutive locations of a track,
+ starting with the location given by the X field)
+
+ Function code:
+ # Mnemonic C6 Description
+
+ 0 Stop
+
+ 2 I(y) Input. A 5-bit word is read from tape and loaded
+ into the 5 MSBits of R. (These bits of R must be
+ cleared initially.)
+
+ 4 P(y) Punch. The 5 MSBits of R are punched onto the
+ output tape.
+
+ 6 B<(x)>=(y) Branch. If A<0, next instruction is fetched from @x, whereas
+ if A>=0, next instruction is fetched from @y
+
+ 8 l (y) n Shift left: the 64 bits of A and R are rotated left n times.
+ n
+
+ 10 r (y) 64-n Shift right: the 64 bits of A and R are shifted right n times.
+ n The sign bit of A is duplicated.
+
+ 14 X (x)(y) 33-n Multiply the contents of *track* x by the last n digits of the
+ n number in R, sending the 32 MSBs to A and 31 LSBs to R
+
+ 16 +c(x)(y) A <- (x)
+
+ 18 -c(x)(y) A <- -(x)
+
+ 20 +(x)(y) A <- A+(x)
+
+ 22 -(x)(y) A <- A-(x)
+
+ 24 T(x)(y) R <- (x)
+
+ 26 R (x)(y) 32+n Store first or last bits of R into (x). The remaining bits of (x)
+ 1-n are unaffected. "The contents of R are filled with 0s or 1s
+ according as the original contents were positive or negative".
+ R (x)(y) n-1
+ n-32
+
+ 28 A (x)(y) 32+n Same as 26, except that source is A, and the contents of A are
+ 1-n not modified.
+
+ A (x)(y) n-1
+ n-32
+
+ 30 S(x)(y) Block Head switch. This enables the block of heads specified
+ in x to be loaded into the working store.
+
+ Note: Mnemonics use subscripts (!), which I tried to render the best I could. Also,
+ ">=" is actually one single character. Last, "1-n" and "n-32" in store mnemonics
+ are the actual sequences "1 *DASH* <number n>" and "<number n> *DASH* 32"
+ (these are NOT formulas with substract signs).
+
+ Note2: Short-hand notations: X stands for X , A for A , and R for R .
+ 32 1-32 1-32
+
+ Note3: Vectors instruction are notated with a subscript 'v' following the basic
+ mnemonic. For instance:
+
+ A (x)(y), + (x)(y)
+ v v
+
+ are the vector counterparts of A(x)(y) and +(x)(y).
+
+
+
+
+ Note that the code has been presented so far as it was in 1957. It appears that
+ it was somewhat different in 1953 (Booth&Booth):
+
+ Format of a machine instruction:
+ Format for r, l, A:
+bits: 1-9 10-15 16-17 18-21 22-30 31-32
+field: X address C6 spare Function Y address spare
+ Format for other instructions:
+bits: 1-9 10-17 18-21 22-30 31-32
+field: X address D Function Y address D (part 2)
+
+ Meaning of fields:
+ D (i.e. drum #): MSBs for the address of the X operand. I don't know whether this feature
+ was actually implemented, since it is said in Booth&Booth that the APE(X)C does
+ not use this feature (it had only one drum of 16 tracks at the time, hence the 9
+ address bits).
+
+ Function code:
+ # Mnemonic C6 Description
+
+ 1 A (x)(y) 32+n(?) record first bits of A in (x). The remaining bits of x
+ 1-n are unaffected.
+
+ 2 +c(x)(y) A <- (x)
+
+ 3 -c(x)(y) A <- -(x)
+
+ 4 +(x)(y) A <- A+(x)
+
+ 5 -(x)(y) A <- A-(x)
+
+ 6 T(x)(y) R <- (x)
+
+ 7 X (x)(y) Multiply the contents of (x) by the number in R,
+ sending the 32 MSBs to A and 31 LSBs to R
+
+ 8 r (y) 64-n(?) Shift right: the 64 bits of A and R are shifted right n times.
+ n The sign bit of A is duplicated.
+
+ 9 l (y) n(?) Shift left: the 64 bits of A and R are rotated left n times.
+ n
+
+ 10 R (x)(y) 32+n record R into (x).
+ 1-n "the contents of R are filled with 0s or 1s
+ according as the original contents were positive or negative".
+
+ 11 B<(x)>=(y) Branch. If A<0, next instruction is read from @x, whereas
+ if A>=0, next instruction is read from @y
+
+ 12 Print(y) Punch. Contents of A are printed.
+
+ 13 C(d+x) branch ("switch Control") to instruction located in position
+ (D:X)
+
+ 14 Stop
+
+ You will notice the absence of input instruction. It seems that program and data were
+ meant to be entered with a teletyper or a card reader located on the control panel.
+
+ I don't know whether this computer really was in operation with this code. Handle
+ these info with caution.
+*/
+
+/*
+ memory interface:
+
+ Data is exchanged on a 1-bit (!) data bus, 10-bit address bus.
+
+ While the bus is 1-bit wide, read/write operation can only be take place on word
+ (i.e. 32 bit) boundaries. However, it is possible to store only the n first bits or
+ n last bits of a word, leaving other bits in memory unaffected.
+
+ The LSBits are transferred first, since this enables to perform bit-per-bit add and
+ substract. Otherwise, the CPU would need an additionnal register to store the second
+ operand, and it would be probably slower, since the operation could only
+ take place after all the data has been transfered.
+
+ Memory operations are synchronous with 2 clocks found on the memory controller:
+ * word clock: a pulse on each word boundary (3750rpm*32 -> 2kHz)
+ * bit clock: a pulse when a bit is present on the bus (word clock * 32 -> 64kHz)
+
+ CPU operation is synchronous with these clocks, too. For instance, the AU does bit-per-bit
+ addition and substraction with a memory operand, synchronously with bit clock,
+ starting and stopping on word clock boundaries. Similar thing with a Fetch operation.
+
+ There is a 10-bit memory location (i.e. address) register on the memory controller.
+ It is loaded with the contents of X after when instruction fetch is complete, and
+ with the contents of Y when instruction execution is complete, so that the next fetch
+ can be executed correctly.
+*/
+
+/*
+ Instruction timings:
+
+
+ References: Booth p. 14 for the table below
+
+
+ Mnemonic delay in word clock cycles
+
+ I 32
+
+ P 32
+
+ B 0
+
+ l 1 if n>=32 (i.e. C6>=32) (see 4.)
+ n 2 if n<32 (i.e. C6<32)
+
+ r 1 if n<=32 (i.e. C6>=32) (see 4.)
+ n 2 if n>32 (i.e. C6<32)
+
+ X 32
+
+ +c, -c, +, -, T 0
+
+ R , R , A , A 1 (see 1. & 4.)
+ 1-n n-32 1-n n-32
+
+ track switch 6 (see 2.)
+
+ vector 12 (see 3.)
+
+
+ (S and stop are missing in the table)
+
+
+ Note that you must add the fetch delay (at least 1 cycle), and, when applicable, the
+ operand read/write delay (at least 1 cycle).
+
+
+ Notes:
+
+ 1. The delay is applied after the store is done (from the analysis of the example
+ in Booth p.52)
+
+ 2. I guess that the memory controller needs 6 cycles to stabilize whenever track
+ switching occurs, i.e. when X does not refer to the current track, and then when Y
+ does not refer to the same track as X. This matches various examples in Booth,
+ although it appears that this delay is not applied when X is not read (cf cross-track
+ B in Booth p. 49).
+ However, and here comes the wacky part, analysis of Booth p. 55 shows that
+ no additionnal delay is caused by an X instruction having its X operand
+ on another track. Maybe, just maybe, this is related to the fact that X does not
+ need to take the word count into account, any word in track is as good as any (yet,
+ this leaves the question of why this optimization could not be applied to vector
+ operations unanswered).
+
+ 3. This is an ambiguous statement. Analysis of Booth p. 55 shows that
+ an instance of an Av instruction with its destination on another track takes no more
+ than 45 cycles, as follow:
+ * 1 cycle for fetch
+ * 6-cycle delay (at most) before write starts (-> track switch)
+ * 32 memory cycles
+ * 6-cycle delay (at most) after write completion (-> track switch)
+ It appears that the delay associated with the vector mode is not distinguishable from
+ the delay caused by track switch and even the delay associated to the Av instruction.
+ Is there really a specific delay associated with the vector mode? To know this, we
+ would need to see a vector instruction on the same track as its operands, which is
+ unlikely to be seen (the only reasonnable application I can see is running a '+_v'
+ to compute the checksum of the current track).
+
+ 4. Example in Booth p. 76 ("20/4 A (27/27) (21/2)") seems to imply that
+ when doing a store with a destination on a track other than the track where next
+ instruction is located, the 1-cycle post-store delay is merged with the 6-cycle track
+ switch delay. (I assume this because there is lots of room on track 21, and if
+ the delays were not merged, it should be easy to move the instruction forward
+ to speed up loop execution time.
+ Similarly, example in Booth p. 49-50 ("4/24 l 32 (5/31)") seems to show that
+ a similar delay merge occurs when doing a shift with the next instruction located on
+ another track.
+*/
+
+#include "emu.h"
+#include "debugger.h"
+#include "apexc.h"
+
+#ifndef SUPPORT_ODD_WORD_SIZES
+#define apexc_readmem(address) cpustate->program->read_dword((address)<<2)
+#define apexc_writemem(address, data) cpustate->program->write_dword((address)<<2, (data))
+/* eewww ! - Fortunately, there is no memory mapped I/O, so we can simulate masked write
+without danger */
+#define apexc_writemem_masked(address, data, mask) \
+ apexc_writemem((address), (apexc_readmem(address) & ~(mask)) | ((data) & (mask)))
+#else
+#define apexc_readmem(address) cpu_readmem13_32(address)
+#define apexc_writemem(address, data) cpu_writemem13_32((address), (data))
+#define apexc_writemem_masked(address, data, mask) cpu_writemem13_32masked((address), (data), (mask))
+#endif
+
+
+#define apexc_readop(address) apexc_readmem(address)
+
+typedef struct _apexc_state apexc_state;
+struct _apexc_state
+{
+ UINT32 a; /* accumulator */
+ UINT32 r; /* register */
+ UINT32 cr; /* control register (i.e. instruction register) */
+ int ml; /* memory location (current track in working store, and requested
+ word position within track) (10 bits) */
+ int working_store; /* current working store (group of 16 tracks) (1-15) */
+ int current_word; /* current word position within track (0-31) */
+
+ int running; /* 1 flag: */
+ /* running: flag implied by the existence of the stop instruction */
+ UINT32 pc; /* address of next instruction for the disassembler */
+
+ legacy_cpu_device *device;
+ address_space *program;
+ address_space *io;
+ int icount;
+};
+
+/* decrement ICount by n */
+#define DELAY(n) {cpustate->icount -= (n); cpustate->current_word = (cpustate->current_word + (n)) & 0x1f;}
+
+
+INLINE apexc_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == APEXC);
+ return (apexc_state *)downcast<legacy_cpu_device *>(device)->token();
+}
+
+
+/*
+ word accessor functions
+
+ take a 10-bit word address
+ 5 bits (MSBs): track address within working store
+ 5 bits (LSBs): word position within track
+
+ 'special' flag: if true, read first word found in track (used by X instruction only)
+
+ 'mask': one bit is set for each bit to write (used by store instructions)
+
+ memory latency delays are taken into account, but not track switching delays
+*/
+
+/* compute complete word address (i.e. translate a logical track address (expressed
+in current working store) to an absolute track address) */
+static int effective_address(apexc_state *cpustate, int address)
+{
+ if (address & 0x200)
+ {
+ address = (address & 0x1FF) | (cpustate->working_store) << 9;
+ }
+
+ return address;
+}
+
+/* read word */
+static UINT32 word_read(apexc_state *cpustate, int address, int special)
+{
+ UINT32 result;
+
+ /* compute absolute track address */
+ address = effective_address(cpustate, address);
+
+ if (special)
+ {
+ /* ignore word position in x - use current position instead */
+ address = (address & ~ 0x1f) | cpustate->current_word;
+ }
+ else
+ {
+ /* wait for requested word to appear under the heads */
+ DELAY(((address /*& 0x1f*/) - cpustate->current_word) & 0x1f);
+ }
+
+ /* read 32 bits */
+#if 0
+ /* note that the APEXC reads LSBits first */
+ result = 0;
+ for (i=0; i<31; i++)
+ {
+ /*if (mask & (1 << i))*/
+ result |= bit_read((address << 5) | i) << i;
+ }
+#else
+ result = apexc_readmem(address);
+#endif
+
+ /* read takes one memory cycle */
+ DELAY(1);
+
+ return result;
+}
+
+/* write word (or part of a word, according to mask) */
+static void word_write(apexc_state *cpustate, int address, UINT32 data, UINT32 mask)
+{
+ /* compute absolute track address */
+ address = effective_address(cpustate, address);
+
+ /* wait for requested word to appear under the heads */
+ DELAY(((address /*& 0x1f*/) - cpustate->current_word) & 0x1f);
+
+ /* write 32 bits according to mask */
+#if 0
+ /* note that the APEXC reads LSBits first */
+ for (i=0; i<31; i++)
+ {
+ if (mask & (1 << i))
+ bit_write((address << 5) | i, (data >> i) & 1);
+ }
+#else
+ apexc_writemem_masked(address, data, mask);
+#endif
+
+ /* write takes one memory cycle (2, actually, but the 2nd cycle is taken into
+ account in execute) */
+ DELAY(1);
+}
+
+/*
+ I/O accessors
+
+ no address is used, these functions just punch or read 5 bits
+*/
+
+static int papertape_read(apexc_state *cpustate)
+{
+ return cpustate->io->read_byte(0) & 0x1f;
+}
+
+static void papertape_punch(apexc_state *cpustate, int data)
+{
+ cpustate->io->write_byte(0, data);
+}
+
+/*
+ now for emulation code
+*/
+
+/*
+ set the memory location (i.e. address) register, and compute the associated delay
+*/
+INLINE int load_ml(apexc_state *cpustate, int address, int vector)
+{
+ int delay;
+
+ /* additionnal delay appears if we switch tracks */
+ if (((cpustate->ml & 0x3E0) != (address & 0x3E0)) /*|| vector*/)
+ delay = 6; /* if tracks are different, delay to allow for track switching */
+ else
+ delay = 0; /* else, no problem */
+
+ cpustate->ml = address; /* save ml */
+
+ return delay;
+}
+
+/*
+ execute one instruction
+
+ TODO:
+ * test!!!
+
+ NOTE:
+ * I do not know whether we should fetch instructions at the beginning or the end of the
+ instruction cycle. Either solution is roughly equivalent to the other, but changes
+ the control panel operation (and I know virtually nothing on the control panel).
+ Currently, I fetch each instruction right after executing the previous instruction, so that
+ the user may enter an instruction into the control register with the control panel, then
+ execute it.
+ This solution makes timing simulation much simpler, too.
+*/
+static void execute(apexc_state *cpustate)
+{
+ int x, y, function, c6, vector; /* instruction fields */
+ int i = 0; /* misc counter */
+ int has_operand; /* true if instruction is an AU operation with an X operand */
+ static const char has_operand_table[32] = /* table for has_operand - one entry for each function code */
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0
+ };
+ int delay1; /* pre-operand-access delay */
+ int delay2; /* post-operation delay */
+ int delay3; /* pre-operand-fetch delay */
+
+ /* first isolate the instruction fields */
+ x = (cpustate->cr >> 22) & 0x3FF;
+ y = (cpustate->cr >> 12) & 0x3FF;
+ function = (cpustate->cr >> 7) & 0x1F;
+ c6 = (cpustate->cr >> 1) & 0x3F;
+ vector = cpustate->cr & 1;
+ cpustate->pc = y<<2;
+
+ function &= 0x1E; /* this is a mere guess - the LSBit is reserved for future additions */
+
+ /* determinates if we need to read an operand*/
+ has_operand = has_operand_table[function];
+
+ if (has_operand)
+ {
+ /* load ml with X */
+ delay1 = load_ml(cpustate, x, vector);
+ /* burn pre-operand-access delay if needed */
+ if (delay1)
+ {
+ DELAY(delay1);
+ }
+ }
+
+ delay2 = 0; /* default */
+
+ do
+ {
+ switch (function)
+ {
+ case 0:
+ /* stop */
+
+ cpustate->running = FALSE;
+
+ /* BTW, I don't know whether stop loads y into ml or not, and whether
+ subsequent fetch is done */
+ break;
+
+ case 2:
+ /* I */
+ /* I do not know whether the CPU does an OR or whatever, but since docs say that
+ the 5 bits must be cleared initially, an OR kind of makes sense */
+ cpustate->r |= papertape_read(cpustate) << 27;
+ delay2 = 32; /* no idea whether this should be counted as an absolute delay
+ or as a value in delay2 */
+ break;
+
+ case 4:
+ /* P */
+ papertape_punch(cpustate, (cpustate->r >> 27) & 0x1f);
+ delay2 = 32; /* no idea whether this should be counted as an absolute delay
+ or as a value in delay2 */
+ break;
+
+ case 6:
+ /* B<(x)>=(y) */
+ /* I have no idea what we should do if the vector bit is set */
+ if (cpustate->a & 0x80000000UL)
+ {
+ /* load ml with X */
+ delay1 = load_ml(cpustate, x, vector);
+ cpustate->pc = x<<2;
+ /* burn pre-fetch delay if needed */
+ if (delay1)
+ {
+ DELAY(delay1);
+ }
+ /* and do fetch at X */
+ goto special_fetch;
+ }
+ /* else, the instruction ends with a normal fetch */
+ break;
+
+ case 8:
+ /* l_n */
+ delay2 = (c6 & 0x20) ? 1 : 2; /* if more than 32 shifts, it takes more time */
+
+ /* Yes, this code is inefficient, but this must be the way the APEXC does it ;-) */
+ while (c6 != 0)
+ {
+ int shifted_bit = 0;
+
+ /* shift and increment c6 */
+ shifted_bit = cpustate->r & 1;
+ cpustate->r >>= 1;
+ if (cpustate->a & 1)
+ cpustate->r |= 0x80000000UL;
+ cpustate->a >>= 1;
+ if (shifted_bit)
+ cpustate->a |= 0x80000000UL;
+
+ c6 = (c6+1) & 0x3f;
+ }
+
+ break;
+
+ case 10:
+ /* r_n */
+ delay2 = (c6 & 0x20) ? 1 : 2; /* if more than 32 shifts, it takes more time */
+
+ /* Yes, this code is inefficient, but this must be the way the APEXC does it ;-) */
+ while (c6 != 0)
+ {
+ /* shift and increment c6 */
+ cpustate->r >>= 1;
+ if (cpustate->a & 1)
+ cpustate->r |= 0x80000000UL;
+ cpustate->a = ((INT32) cpustate->a) >> 1;
+
+ c6 = (c6+1) & 0x3f;
+ }
+
+ break;
+
+ case 12:
+ /* unused function code. I assume this results into a NOP, for lack of any
+ specific info... */
+
+ break;
+
+ case 14:
+ /* X_n(x) */
+
+ /* Yes, this code is inefficient, but this must be the way the APEXC does it ;-) */
+ /* algorithm found in Booth&Booth, p. 45-48 */
+ {
+ int shifted_bit;
+
+ cpustate->a = 0;
+ shifted_bit = 0;
+ while (1)
+ {
+ /* note we read word at current word position */
+ if (shifted_bit && ! (cpustate->r & 1))
+ cpustate->a += word_read(cpustate, x, 1);
+ else if ((! shifted_bit) && (cpustate->r & 1))
+ cpustate->a -= word_read(cpustate, x, 1);
+ else
+ /* Even if we do not read anything, the loop still takes 1 cycle of
+ the memory word clock. */
+ /* Anyway, maybe we still read the data even if we do not use it. */
+ DELAY(1);
+
+ /* exit if c6 reached 32 ("c6 & 0x20" is simpler to implement and
+ essentially equivalent, so this is most likely the actual implementation) */
+ if (c6 & 0x20)
+ break;
+
+ /* else increment c6 and shift */
+ c6 = (c6+1) & 0x3f;
+
+ /* shift */
+ shifted_bit = cpustate->r & 1;
+ cpustate->r >>= 1;
+ if (cpustate->a & 1)
+ cpustate->r |= 0x80000000UL;
+ cpustate->a = ((INT32) cpustate->a) >> 1;
+ }
+ }
+
+ //DELAY(32); /* mmmh... we have already counted 32 wait states */
+ /* actually, if (n < 32) (which is an untypical case), we do not have 32 wait
+ states. Question is: do we really have 32 wait states if (n < 32), or is
+ the timing table incomplete? */
+ break;
+
+ case 16:
+ /* +c(x) */
+ cpustate->a = + word_read(cpustate, cpustate->ml, 0);
+ break;
+
+ case 18:
+ /* -c(x) */
+ cpustate->a = - word_read(cpustate, cpustate->ml, 0);
+ break;
+
+ case 20:
+ /* +(x) */
+ cpustate->a += word_read(cpustate, cpustate->ml, 0);
+ break;
+
+ case 22:
+ /* -(x) */
+ cpustate->a -= word_read(cpustate, cpustate->ml, 0);
+ break;
+
+ case 24:
+ /* T(x) */
+ cpustate->r = word_read(cpustate, cpustate->ml, 0);
+ break;
+
+ case 26:
+ /* R_(1-n)(x) & R_(n-32)(x) */
+
+ {
+ UINT32 mask;
+
+ if (c6 & 0x20)
+ mask = 0xFFFFFFFFUL << (64 - c6);
+ else
+ mask = 0xFFFFFFFFUL >> c6;
+
+ word_write(cpustate, cpustate->ml, cpustate->r, mask);
+ }
+
+ cpustate->r = (cpustate->r & 0x80000000UL) ? 0xFFFFFFFFUL : 0;
+
+ delay2 = 1;
+ break;
+
+ case 28:
+ /* A_(1-n)(x) & A_(n-32)(x) */
+
+ {
+ UINT32 mask;
+
+ if (c6 & 0x20)
+ mask = 0xFFFFFFFFUL << (64 - c6);
+ else
+ mask = 0xFFFFFFFFUL >> c6;
+
+ word_write(cpustate, cpustate->ml, cpustate->a, mask);
+ }
+
+ delay2 = 1;
+ break;
+
+ case 30:
+ /* S(x) */
+ cpustate->working_store = (x >> 5) & 0xf; /* or is it (x >> 6)? */
+ DELAY(32); /* no idea what the value is... All I know is that it takes much
+ more time than track switching (which takes 6 cycles) */
+ break;
+ }
+ if (vector)
+ /* increment word position in vector operations */
+ cpustate->ml = (cpustate->ml & 0x3E0) | ((cpustate->ml + 1) & 0x1F);
+ } while (vector && has_operand && (++i < 32)); /* iterate 32 times if vector bit is set */
+ /* the has_operand is a mere guess */
+
+ /* load ml with Y */
+ delay3 = load_ml(cpustate, y, 0);
+
+ /* compute max(delay2, delay3) */
+ if (delay2 > delay3)
+ delay3 = delay2;
+
+ /* burn pre-fetch delay if needed */
+ if (delay3)
+ {
+ DELAY(delay3);
+ }
+
+ /* entry point after a successful Branch (which alters the normal instruction sequence,
+ in order not to load ml with Y) */
+special_fetch:
+
+ /* fetch current instruction into control register */
+ cpustate->cr = word_read(cpustate, cpustate->ml, 0);
+}
+
+
+static CPU_INIT( apexc )
+{
+ apexc_state *cpustate = get_safe_token(device);
+
+ cpustate->device = device;
+ cpustate->program = device->space(AS_PROGRAM);
+ cpustate->io = device->space(AS_IO);
+
+ device->save_item(NAME(cpustate->a));
+ device->save_item(NAME(cpustate->r));
+ device->save_item(NAME(cpustate->cr));
+ device->save_item(NAME(cpustate->ml));
+ device->save_item(NAME(cpustate->working_store));
+ device->save_item(NAME(cpustate->current_word));
+ device->save_item(NAME(cpustate->running));
+ device->save_item(NAME(cpustate->pc));
+}
+
+static CPU_RESET( apexc )
+{
+ apexc_state *cpustate = get_safe_token(device);
+
+ /* mmmh... I don't know what happens on reset with an actual APEXC. */
+
+ cpustate->working_store = 1; /* mere guess */
+ cpustate->current_word = 0; /* well, we do have to start somewhere... */
+
+ /* next two lines are just the product of my bold fantasy */
+ cpustate->cr = 0; /* first instruction executed will be a stop */
+ cpustate->running = TRUE; /* this causes the CPU to load the instruction at 0/0,
+ which enables easy booting (just press run on the panel) */
+}
+
+static CPU_EXECUTE( apexc )
+{
+ apexc_state *cpustate = get_safe_token(device);
+
+ do
+ {
+ debugger_instruction_hook(device, cpustate->pc);
+
+ if (cpustate->running)
+ execute(cpustate);
+ else
+ {
+ DELAY(cpustate->icount); /* burn cycles once for all */
+ }
+ } while (cpustate->icount > 0);
+}
+
+static CPU_SET_INFO( apexc )
+{
+ apexc_state *cpustate = get_safe_token(device);
+
+ switch (state)
+ {
+ /* --- the following bits of info are set as 64-bit signed integers --- */
+ /*case CPUINFO_INT_INPUT_STATE + ...:*/ /* no interrupts */
+
+ case CPUINFO_INT_PC:
+ /* keep address 9 LSBits - 10th bit depends on whether we are accessing the permanent
+ track group or a switchable one */
+ cpustate->ml = info->i & 0x1ff;
+ if (info->i & 0x1e00)
+ { /* we are accessing a switchable track group */
+ cpustate->ml |= 0x200; /* set 10th bit */
+
+ if (((info->i >> 9) & 0xf) != cpustate->working_store)
+ { /* we need to do a store switch */
+ cpustate->working_store = ((info->i >> 9) & 0xf);
+ }
+ }
+ break;
+
+ case CPUINFO_INT_SP: (void) info->i; /* no SP */ break;
+
+ case CPUINFO_INT_REGISTER + APEXC_CR: cpustate->cr = info->i; break;
+ case CPUINFO_INT_REGISTER + APEXC_A: cpustate->a = info->i; break;
+ case CPUINFO_INT_REGISTER + APEXC_R: cpustate->r = info->i; break;
+ case CPUINFO_INT_REGISTER + APEXC_ML: cpustate->ml = info->i & 0x3ff; break;
+ case CPUINFO_INT_REGISTER + APEXC_PC: cpustate->pc = info->i; break;
+ case CPUINFO_INT_REGISTER + APEXC_WS: cpustate->working_store = info->i & 0xf; break;
+ case CPUINFO_INT_REGISTER + APEXC_STATE: cpustate->running = info->i ? TRUE : FALSE; break;
+ }
+}
+
+CPU_GET_INFO( apexc )
+{
+ apexc_state *cpustate = (device != NULL && device->token() != NULL) ? get_safe_token(device) : NULL;
+
+ switch (state)
+ {
+ case CPUINFO_INT_CONTEXT_SIZE: info->i = sizeof(apexc_state); break;
+ case CPUINFO_INT_INPUT_LINES: info->i = 0; break;
+ case CPUINFO_INT_DEFAULT_IRQ_VECTOR: info->i = 0; break;
+ case DEVINFO_INT_ENDIANNESS: info->i = ENDIANNESS_BIG; /*don't care*/ break;
+ case CPUINFO_INT_CLOCK_MULTIPLIER: info->i = 1; break;
+ case CPUINFO_INT_CLOCK_DIVIDER: info->i = 1; break;
+ case CPUINFO_INT_MIN_INSTRUCTION_BYTES: info->i = 4; break;
+ case CPUINFO_INT_MAX_INSTRUCTION_BYTES: info->i = 4; break;
+ case CPUINFO_INT_MIN_CYCLES: info->i = 2; /* IIRC */ break;
+ case CPUINFO_INT_MAX_CYCLES: info->i = 75; /* IIRC */ break;
+
+ case DEVINFO_INT_DATABUS_WIDTH + AS_PROGRAM: info->i = 32; break;
+ case DEVINFO_INT_ADDRBUS_WIDTH + AS_PROGRAM: info->i = 15; /*13+2 ignored bits to make double word address*/ break;
+ case DEVINFO_INT_ADDRBUS_SHIFT + AS_PROGRAM: info->i = 0; break;
+ case DEVINFO_INT_DATABUS_WIDTH + AS_DATA: info->i = 0; break;
+ case DEVINFO_INT_ADDRBUS_WIDTH + AS_DATA: info->i = 0; break;
+ case DEVINFO_INT_ADDRBUS_SHIFT + AS_DATA: info->i = 0; break;
+ case DEVINFO_INT_DATABUS_WIDTH + AS_IO: info->i = /*5*/8; /* no I/O bus, but we use address 0 for punchtape I/O */ break;
+ case DEVINFO_INT_ADDRBUS_WIDTH + AS_IO: info->i = /*0*/1; /*0 is quite enough but the MAME core does not understand*/ break;
+ case DEVINFO_INT_ADDRBUS_SHIFT + AS_IO: info->i = 0; break;
+
+ case CPUINFO_INT_SP: info->i = 0; /* no SP */ break;
+ case CPUINFO_INT_PC:
+ case CPUINFO_INT_PREVIOUSPC: info->i = cpustate->pc; /* psuedo-PC */ break;
+
+ /*case CPUINFO_INT_INPUT_STATE + ...:*/ /* no interrupts */
+
+ case CPUINFO_INT_REGISTER + APEXC_CR: info->i = cpustate->cr; break;
+ case CPUINFO_INT_REGISTER + APEXC_A: info->i = cpustate->a; break;
+ case CPUINFO_INT_REGISTER + APEXC_R: info->i = cpustate->r; break;
+ case CPUINFO_INT_REGISTER + APEXC_ML: info->i = cpustate->ml; break;
+ case CPUINFO_INT_REGISTER + APEXC_PC: info->i = cpustate->pc; break;
+ case CPUINFO_INT_REGISTER + APEXC_WS: info->i = cpustate->working_store; break;
+ case CPUINFO_INT_REGISTER + APEXC_STATE: info->i = cpustate->running; break;
+ case CPUINFO_INT_REGISTER + APEXC_ML_FULL: info->i = effective_address(cpustate, cpustate->ml); break;
+
+ case CPUINFO_FCT_SET_INFO: info->setinfo = CPU_SET_INFO_NAME(apexc); break;
+ case CPUINFO_FCT_INIT: info->init = CPU_INIT_NAME(apexc); break;
+ case CPUINFO_FCT_RESET: info->reset = CPU_RESET_NAME(apexc); break;
+ case CPUINFO_FCT_EXECUTE: info->execute = CPU_EXECUTE_NAME(apexc); break;
+ case CPUINFO_FCT_BURN: info->burn = NULL; break;
+ case CPUINFO_FCT_DISASSEMBLE: info->disassemble = CPU_DISASSEMBLE_NAME(apexc); break;
+ case CPUINFO_PTR_INSTRUCTION_COUNTER: info->icount = &cpustate->icount; break;
+
+ case DEVINFO_STR_NAME: strcpy(info->s, "APEXC"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "APEC"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Raphael Nabet"); break;
+
+ case CPUINFO_STR_FLAGS: sprintf(info->s, "%c", (cpustate->running) ? 'R' : 'S'); break;
+
+ case CPUINFO_STR_REGISTER + APEXC_CR: sprintf(info->s, "CR:%08X", cpustate->cr); break;
+ case CPUINFO_STR_REGISTER + APEXC_A: sprintf(info->s, "A :%08X", cpustate->a); break;
+ case CPUINFO_STR_REGISTER + APEXC_R: sprintf(info->s, "R :%08X", cpustate->r); break;
+ case CPUINFO_STR_REGISTER + APEXC_ML: sprintf(info->s, "ML:%03X", cpustate->ml); break;
+ case CPUINFO_STR_REGISTER + APEXC_PC: sprintf(info->s, "PC:%03X", cpustate->pc); break;
+ case CPUINFO_STR_REGISTER + APEXC_WS: sprintf(info->s, "WS:%01X", cpustate->working_store); break;
+
+ case CPUINFO_STR_REGISTER + APEXC_STATE: sprintf(info->s, "CPU state:%01X", cpustate->running ? TRUE : FALSE); break;
+ }
+}
+
+DEFINE_LEGACY_CPU_DEVICE(APEXC, apexc);