/*
    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)	memory_read_dword_32be(cpustate->program, (address)<<2)
#define apexc_writemem(address, data)	memory_write_dword_32be(cpustate->program, (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;
	const address_space *program;
	const 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(running_device *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 memory_read_byte_8be(cpustate->io, 0) & 0x1f;
}

static void papertape_punch(apexc_state *cpustate, int data)
{
	memory_write_byte_8be(cpustate->io, 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);

	state_save_register_device_item(device, 0, cpustate->a);
	state_save_register_device_item(device, 0, cpustate->r);
	state_save_register_device_item(device, 0, cpustate->cr);
	state_save_register_device_item(device, 0, cpustate->ml);
	state_save_register_device_item(device, 0, cpustate->working_store);
	state_save_register_device_item(device, 0, cpustate->current_word);
	state_save_register_device_item(device, 0, cpustate->running);
	state_save_register_device_item(device, 0, 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 + ADDRESS_SPACE_PROGRAM:	info->i = 32;					break;
	case DEVINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 15;	/*13+2 ignored bits to make double word address*/	break;
	case DEVINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_PROGRAM: info->i = 0;					break;
	case DEVINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_DATA:	info->i = 0;					break;
	case DEVINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_DATA:	info->i = 0;					break;
	case DEVINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_DATA:	info->i = 0;					break;
	case DEVINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_IO:		info->i = /*5*/8;	/* no I/O bus, but we use address 0 for punchtape I/O */	break;
	case DEVINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_IO:		info->i = /*0*/1;	/*0 is quite enough but the MAME core does not understand*/	break;
	case DEVINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_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);
