3b2: Initial release of an AT&T 3B2 model 400 emulator.
For information on usage, please see the file 3B2/README.md
This commit is contained in:
parent
7246e74004
commit
804ea8e322
28 changed files with 12528 additions and 1 deletions
3402
3B2/3b2_cpu.c
Normal file
3402
3B2/3b2_cpu.c
Normal file
File diff suppressed because it is too large
Load diff
490
3B2/3b2_cpu.h
Normal file
490
3B2/3b2_cpu.h
Normal file
|
@ -0,0 +1,490 @@
|
||||||
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 CPU (WE32100) Header
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _3B2_CPU_H_
|
||||||
|
#define _3B2_CPU_H_
|
||||||
|
|
||||||
|
#include "3b2_defs.h"
|
||||||
|
#include "3b2_io.h"
|
||||||
|
|
||||||
|
/* Execution Modes */
|
||||||
|
#define EX_LVL_KERN 0
|
||||||
|
#define EX_LVL_EXEC 1
|
||||||
|
#define EX_LVL_SUPR 2
|
||||||
|
#define EX_LVL_USER 3
|
||||||
|
|
||||||
|
/* Processor Version Number */
|
||||||
|
#define WE32100_VER 0x1A
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Mode Syntax Mode Reg. Bytes Notes
|
||||||
|
* ----------------------------------------------------------------------
|
||||||
|
* Absolute $expr 7 15 5
|
||||||
|
* Abs. Deferred *$expr 14 15 5
|
||||||
|
* Byte Disp. expr(%rn) 12 0-10,12-15 2
|
||||||
|
* Byte Disp. Def. *expr(%rn) 13 0-10,12-15 2
|
||||||
|
* Halfword Disp. expr(%rn) 10 0-10,12-15 3
|
||||||
|
* Halfword Disp. Def. *expr(%rn) 11 0-10,12-15 3
|
||||||
|
* Word Disp. expr(%rn) 8 0-10,12-15 5
|
||||||
|
* Word Disp. Def. *expr(%rn) 9 0-10,12-15 5
|
||||||
|
* AP Short Offset so(%ap) 7 0-14 1 1
|
||||||
|
* FP Short Offset so(%fp) 6 0-14 1 1
|
||||||
|
* Byte Immediate &imm8 6 15 2 2,3
|
||||||
|
* Halfword Immediate &imm16 5 15 3 2,3
|
||||||
|
* Word Immediate &imm32 4 15 5 2,3
|
||||||
|
* Positive Literal &lit 0-3 0-15 1 2,3
|
||||||
|
* Negative Literal &lit 15 0-15 1 2,3
|
||||||
|
* Register %rn 4 0-14 1 1,3
|
||||||
|
* Register Deferred (%rn) 5 0-10,12-14 1 1
|
||||||
|
* Expanded Op. Type {type}opnd 14 0-14 2-6 4
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
*
|
||||||
|
* 1. Mode field has special meaning if register field is 15; see
|
||||||
|
* absolute or immediate mode.
|
||||||
|
* 2. Mode may not be used for a destination operand.
|
||||||
|
* 3. Mode may not be used if the instruction takes effective address
|
||||||
|
* of the operand.
|
||||||
|
* 4. 'type' overrides instruction type; 'type' determines the operand
|
||||||
|
* type, except that it does not determine the length for immediate
|
||||||
|
* or literals or whether literals are signed or unsigned. 'opnd'
|
||||||
|
* determines actual address mode. For total bytes, add 1 to byte
|
||||||
|
* count for address mode determined by 'opnd'.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opcodes
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPOPRD = 0x02,
|
||||||
|
SPOPD2 = 0x03,
|
||||||
|
MOVAW = 0x04,
|
||||||
|
SPOPRT = 0x06,
|
||||||
|
SPOPT2 = 0x07,
|
||||||
|
RET = 0x08,
|
||||||
|
MOVTRW = 0x0C,
|
||||||
|
SAVE = 0x10,
|
||||||
|
SPOPWD = 0x13,
|
||||||
|
EXTOP = 0x14,
|
||||||
|
SPOPWT = 0x17,
|
||||||
|
RESTORE = 0x18,
|
||||||
|
SWAPWI = 0x1C,
|
||||||
|
SWAPHI = 0x1E,
|
||||||
|
SWAPBI = 0x1F,
|
||||||
|
POPW = 0x20,
|
||||||
|
SPOPRS = 0x22,
|
||||||
|
SPOPS2 = 0x23,
|
||||||
|
JMP = 0x24,
|
||||||
|
CFLUSH = 0x27,
|
||||||
|
TSTW = 0x28,
|
||||||
|
TSTH = 0x2A,
|
||||||
|
TSTB = 0x2B,
|
||||||
|
CALL = 0x2C,
|
||||||
|
BPT = 0x2E,
|
||||||
|
WAIT = 0x2F,
|
||||||
|
EMB = 0x30, // Multi-byte
|
||||||
|
SPOP = 0x32,
|
||||||
|
SPOPWS = 0x33,
|
||||||
|
JSB = 0x34,
|
||||||
|
BSBH = 0x36,
|
||||||
|
BSBB = 0x37,
|
||||||
|
BITW = 0x38,
|
||||||
|
BITH = 0x3A,
|
||||||
|
BITB = 0x3B,
|
||||||
|
CMPW = 0x3C,
|
||||||
|
CMPH = 0x3E,
|
||||||
|
CMPB = 0x3F,
|
||||||
|
RGEQ = 0x40,
|
||||||
|
BGEH = 0x42,
|
||||||
|
BGEB = 0x43,
|
||||||
|
RGTR = 0x44,
|
||||||
|
BGH = 0x46,
|
||||||
|
BGB = 0x47,
|
||||||
|
RLSS = 0x48,
|
||||||
|
BLH = 0x4A,
|
||||||
|
BLB = 0x4B,
|
||||||
|
RLEQ = 0x4C,
|
||||||
|
BLEH = 0x4E,
|
||||||
|
BLEB = 0x4F,
|
||||||
|
RGEQU = 0x50,
|
||||||
|
BGEUH = 0x52, // Also BCCH
|
||||||
|
BGEUB = 0x53, // Also BCCB
|
||||||
|
RGTRU = 0x54,
|
||||||
|
BGUH = 0x56,
|
||||||
|
BGUB = 0x57,
|
||||||
|
BLSSU = 0x58, // Also BCS
|
||||||
|
BLUH = 0x5A, // Also BCSH
|
||||||
|
BLUB = 0x5B, // Also BCSB
|
||||||
|
RLEQU = 0x5C,
|
||||||
|
BLEUH = 0x5E,
|
||||||
|
BLEUB = 0x5F,
|
||||||
|
RVC = 0x60,
|
||||||
|
BVCH = 0x62,
|
||||||
|
BVCB = 0x63,
|
||||||
|
RNEQU = 0x64,
|
||||||
|
BNEH_D = 0x66, // Duplicate of 0x76
|
||||||
|
BNEB_D = 0x67, // Duplicate of 0x77
|
||||||
|
RVS = 0x68,
|
||||||
|
BVSH = 0x6A,
|
||||||
|
BVSB = 0x6B,
|
||||||
|
REQLU = 0x6C,
|
||||||
|
BEH_D = 0x6E, // Duplicate of 0x7E
|
||||||
|
BEB_D = 0x6F, // Duplicate of 0x7F
|
||||||
|
NOP = 0x70,
|
||||||
|
NOP3 = 0x72,
|
||||||
|
NOP2 = 0x73,
|
||||||
|
BNEQ = 0x74,
|
||||||
|
RNEQ = 0x74,
|
||||||
|
BNEH = 0x76,
|
||||||
|
BNEB = 0x77,
|
||||||
|
RSB = 0x78,
|
||||||
|
BRH = 0x7A,
|
||||||
|
BRB = 0x7B,
|
||||||
|
REQL = 0x7C,
|
||||||
|
BEH = 0x7E,
|
||||||
|
BEB = 0x7F,
|
||||||
|
CLRW = 0x80,
|
||||||
|
CLRH = 0x82,
|
||||||
|
CLRB = 0x83,
|
||||||
|
MOVW = 0x84, /* done */
|
||||||
|
MOVH = 0x86, /* done */
|
||||||
|
MOVB = 0x87, /* done */
|
||||||
|
MCOMW = 0x88,
|
||||||
|
MCOMH = 0x8A,
|
||||||
|
MCOMB = 0x8B,
|
||||||
|
MNEGW = 0x8C,
|
||||||
|
MNEGH = 0x8E,
|
||||||
|
MNEGB = 0x8F,
|
||||||
|
INCW = 0x90,
|
||||||
|
INCH = 0x92,
|
||||||
|
INCB = 0x93,
|
||||||
|
DECW = 0x94,
|
||||||
|
DECH = 0x96,
|
||||||
|
DECB = 0x97,
|
||||||
|
ADDW2 = 0x9C,
|
||||||
|
ADDH2 = 0x9E,
|
||||||
|
ADDB2 = 0x9F,
|
||||||
|
PUSHW = 0xA0,
|
||||||
|
MODW2 = 0xA4,
|
||||||
|
MODH2 = 0xA6,
|
||||||
|
MODB2 = 0xA7,
|
||||||
|
MULW2 = 0xA8,
|
||||||
|
MULH2 = 0xAA,
|
||||||
|
MULB2 = 0xAB,
|
||||||
|
DIVW2 = 0xAC,
|
||||||
|
DIVH2 = 0xAE,
|
||||||
|
DIVB2 = 0xAF,
|
||||||
|
ORW2 = 0xB0,
|
||||||
|
ORH2 = 0xB2,
|
||||||
|
ORB2 = 0xB3,
|
||||||
|
XORW2 = 0xB4,
|
||||||
|
XORH2 = 0xB6,
|
||||||
|
XORB2 = 0xB7,
|
||||||
|
ANDW2 = 0xB8,
|
||||||
|
ANDH2 = 0xBA,
|
||||||
|
ANDB2 = 0xBB,
|
||||||
|
SUBW2 = 0xBC,
|
||||||
|
SUBH2 = 0xBE,
|
||||||
|
SUBB2 = 0xBF,
|
||||||
|
ALSW3 = 0xC0,
|
||||||
|
ARSW3 = 0xC4,
|
||||||
|
ARSH3 = 0xC6,
|
||||||
|
ARSB3 = 0xC7,
|
||||||
|
INSFW = 0xC8,
|
||||||
|
INSFH = 0xCA,
|
||||||
|
INSFB = 0xCB,
|
||||||
|
EXTFW = 0xCC,
|
||||||
|
EXTFH = 0xCE,
|
||||||
|
EXTFB = 0xCF,
|
||||||
|
LLSW3 = 0xD0,
|
||||||
|
LLSH3 = 0xD2,
|
||||||
|
LLSB3 = 0xD3,
|
||||||
|
LRSW3 = 0xD4,
|
||||||
|
ROTW = 0xD8,
|
||||||
|
ADDW3 = 0xDC,
|
||||||
|
ADDH3 = 0xDE,
|
||||||
|
ADDB3 = 0xDF,
|
||||||
|
PUSHAW = 0xE0,
|
||||||
|
MODW3 = 0xE4,
|
||||||
|
MODH3 = 0xE6,
|
||||||
|
MODB3 = 0xE7,
|
||||||
|
MULW3 = 0xE8,
|
||||||
|
MULH3 = 0xEA,
|
||||||
|
MULB3 = 0xEB,
|
||||||
|
DIVW3 = 0xEC,
|
||||||
|
DIVH3 = 0xEE,
|
||||||
|
DIVB3 = 0xEF,
|
||||||
|
ORW3 = 0xF0,
|
||||||
|
ORH3 = 0xF2,
|
||||||
|
ORB3 = 0xF3,
|
||||||
|
XORW3 = 0xF4,
|
||||||
|
XORH3 = 0xF6,
|
||||||
|
XORB3 = 0xF7,
|
||||||
|
ANDW3 = 0xF8,
|
||||||
|
ANDH3 = 0xFA,
|
||||||
|
ANDB3 = 0xFB,
|
||||||
|
SUBW3 = 0xFC,
|
||||||
|
SUBH3 = 0xFE,
|
||||||
|
SUBB3 = 0xFF,
|
||||||
|
MVERNO = 0x3009,
|
||||||
|
ENBVJMP = 0x300d,
|
||||||
|
DISVJMP = 0x3013,
|
||||||
|
MOVBLW = 0x3019,
|
||||||
|
STREND = 0x301f,
|
||||||
|
INTACK = 0x302f,
|
||||||
|
STRCPY = 0x3035,
|
||||||
|
RETG = 0x3045,
|
||||||
|
GATE = 0x3061,
|
||||||
|
CALLPS = 0x30ac,
|
||||||
|
RETPS = 0x30c8
|
||||||
|
} opcode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DECODE_SUCCESS,
|
||||||
|
DECODE_ERROR
|
||||||
|
} decode_result;
|
||||||
|
|
||||||
|
/* Addressing Mode enum */
|
||||||
|
typedef enum {
|
||||||
|
ABS, // Absolute
|
||||||
|
ABS_DEF, // Absolute Deferred
|
||||||
|
BYTE_DISP, // Byte Displacement
|
||||||
|
BYTE_DISP_DEF, // Byte Displacement Deferred
|
||||||
|
HFWD_DISP, // Halfword Displacement
|
||||||
|
HFWD_DISP_DEF, // Halfword Displacement Deferred
|
||||||
|
WORD_DISP, // Word Displacement
|
||||||
|
WORD_DISP_DEF, // Word Displacement Deferred
|
||||||
|
AP_SHORT_OFF, // AP Short Offset
|
||||||
|
FP_SHORT_OFF, // FP Short Offset
|
||||||
|
BYTE_IMM, // Byte Immediate
|
||||||
|
HFWD_IMM, // Halfword Immediate
|
||||||
|
WORD_IMM, // Word Immediate
|
||||||
|
POS_LIT, // Positive Literal
|
||||||
|
NEG_LIT, // Negative Literal
|
||||||
|
REGISTER, // Register
|
||||||
|
REGISTER_DEF, // Register Deferred
|
||||||
|
EXP // Expanded-operand type
|
||||||
|
} addr_mode;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Each instruction expects operands of a certain type.
|
||||||
|
*
|
||||||
|
* The large majority of instructions expect operands that have a
|
||||||
|
* descriptor as the first byte. This descriptor carries all the
|
||||||
|
* information necessary to compute the addressing mode of the
|
||||||
|
* operand.
|
||||||
|
*
|
||||||
|
* e.g.:
|
||||||
|
*
|
||||||
|
* MOVB 6(%r1),%r0
|
||||||
|
* +------+------+------+------+
|
||||||
|
* | 0x87 | 0xc1 | 0x06 | 0x40 |
|
||||||
|
* +------+------+------+------+
|
||||||
|
* ^^^^^^
|
||||||
|
* Descriptor byte. mode = 13 (0x0c), register = 1 (0x01)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Branch instructions have either an 8-bit or a 16-bit signed
|
||||||
|
* displacement value, and lack a descriptor byte.
|
||||||
|
*
|
||||||
|
* e.g.:
|
||||||
|
*
|
||||||
|
* BCCB 0x03
|
||||||
|
* +------+------+
|
||||||
|
* | 0x53 | 0x03 | 8-bit displacement
|
||||||
|
* +------+------+
|
||||||
|
*
|
||||||
|
* BCCH 0x01ff
|
||||||
|
* +------+------+------+
|
||||||
|
* | 0x52 | 0xff | 0x01 | 16-bit displacement
|
||||||
|
* +------+------+------+
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* TODO: Describe coprocessor instructions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
OP_NONE, /* NULL type */
|
||||||
|
OP_DESC, /* Descriptor byte */
|
||||||
|
OP_BYTE, /* 8-bit signed value */
|
||||||
|
OP_HALF, /* 16-bit signed value */
|
||||||
|
OP_COPR /* Coprocessor instruction */
|
||||||
|
} op_mode;
|
||||||
|
|
||||||
|
/* Describes a mnemonic */
|
||||||
|
typedef struct _mnemonic {
|
||||||
|
uint16 opcode;
|
||||||
|
int8 op_count; /* Number of operands */
|
||||||
|
op_mode mode; /* Dispatch mode */
|
||||||
|
int8 dtype; /* Default data type */
|
||||||
|
char mnemonic[8];
|
||||||
|
int8 src_op1;
|
||||||
|
int8 src_op2;
|
||||||
|
int8 src_op3;
|
||||||
|
int8 dst_op;
|
||||||
|
} mnemonic;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure that describes each operand in a decoded instruction
|
||||||
|
*/
|
||||||
|
typedef struct _operand {
|
||||||
|
uint8 mode; /* Embedded data addressing mode */
|
||||||
|
uint8 reg; /* Operand register (0-15) */
|
||||||
|
int8 dtype; /* Default type for the operand */
|
||||||
|
int8 etype; /* Expanded type (-1 if none) */
|
||||||
|
union {
|
||||||
|
uint32 w;
|
||||||
|
uint16 h;
|
||||||
|
uint8 b;
|
||||||
|
} embedded; /* Data consumed as part of the instruction
|
||||||
|
stream, i.e. literals, displacement,
|
||||||
|
etc. */
|
||||||
|
uint32 data; /* Data either read or written during
|
||||||
|
instruction execution */
|
||||||
|
} operand;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An inst is a combination of a decoded instruction and
|
||||||
|
* 0 to 4 operands. Also used for history record keeping.
|
||||||
|
*/
|
||||||
|
typedef struct _instr {
|
||||||
|
mnemonic *mn;
|
||||||
|
uint32 psw;
|
||||||
|
uint32 sp;
|
||||||
|
uint32 pc;
|
||||||
|
t_bool valid;
|
||||||
|
operand operands[4];
|
||||||
|
} instr;
|
||||||
|
|
||||||
|
/* Function prototypes */
|
||||||
|
|
||||||
|
t_stat cpu_svc(UNIT *uptr);
|
||||||
|
t_stat cpu_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
||||||
|
t_stat cpu_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
||||||
|
t_stat cpu_reset(DEVICE *dptr);
|
||||||
|
t_stat cpu_set_size(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||||||
|
t_stat cpu_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||||||
|
t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||||
|
t_stat cpu_set_halt(UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||||
|
t_stat cpu_clear_halt(UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||||
|
t_stat cpu_boot(int32 unit_num, DEVICE *dptr);
|
||||||
|
|
||||||
|
t_bool cpu_is_pc_a_subroutine_call (t_addr **ret_addrs);
|
||||||
|
|
||||||
|
void cpu_register_name(uint8 reg, char *buf, size_t len);
|
||||||
|
void cpu_show_operand(FILE *st, operand *op);
|
||||||
|
void fprint_sym_m(FILE *st, instr *ip);
|
||||||
|
|
||||||
|
instr *cpu_next_instruction(void);
|
||||||
|
|
||||||
|
uint8 decode_instruction(instr *instr);
|
||||||
|
t_bool cpu_on_interrupt(uint8 ipl);
|
||||||
|
|
||||||
|
static uint32 cpu_effective_address(operand * op);
|
||||||
|
static uint32 cpu_read_op(operand * op);
|
||||||
|
static void cpu_write_op(operand * op, t_uint64 val);
|
||||||
|
static void cpu_set_nz_flags(t_uint64 data, operand * op);
|
||||||
|
|
||||||
|
static SIM_INLINE uint8 cpu_ipl();
|
||||||
|
static SIM_INLINE void cpu_on_normal_exception(uint8 isc);
|
||||||
|
static SIM_INLINE void cpu_on_stack_exception(uint8 isc);
|
||||||
|
static SIM_INLINE void cpu_on_process_exception(uint8 isc);
|
||||||
|
static SIM_INLINE void cpu_on_reset_exception(uint8 isc);
|
||||||
|
static SIM_INLINE void cpu_perform_gate(uint32 index1, uint32 index2);
|
||||||
|
static SIM_INLINE t_bool mem_exception();
|
||||||
|
static SIM_INLINE void clear_exceptions();
|
||||||
|
static SIM_INLINE void set_psw_tm(t_bool val);
|
||||||
|
static SIM_INLINE void clear_instruction(instr *inst);
|
||||||
|
static SIM_INLINE int8 op_type(operand *op);
|
||||||
|
static SIM_INLINE t_bool op_signed(operand *op);
|
||||||
|
static SIM_INLINE t_bool is_byte_immediate(operand * oper);
|
||||||
|
static SIM_INLINE t_bool is_halfword_immediate(operand * oper);
|
||||||
|
static SIM_INLINE t_bool is_word_immediate(operand * oper);
|
||||||
|
static SIM_INLINE t_bool is_positive_literal(operand * oper);
|
||||||
|
static SIM_INLINE t_bool is_negative_literal(operand * oper);
|
||||||
|
static SIM_INLINE t_bool invalid_destination(operand * oper);
|
||||||
|
static SIM_INLINE uint32 sign_extend_n(uint8 val);
|
||||||
|
static SIM_INLINE uint32 sign_extend_b(uint8 val);
|
||||||
|
static SIM_INLINE uint32 zero_extend_b(uint8 val);
|
||||||
|
static SIM_INLINE uint32 sign_extend_h(uint16 val);
|
||||||
|
static SIM_INLINE uint32 zero_extend_h(uint16 val);
|
||||||
|
static SIM_INLINE t_bool cpu_z_flag();
|
||||||
|
static SIM_INLINE t_bool cpu_n_flag();
|
||||||
|
static SIM_INLINE t_bool cpu_c_flag();
|
||||||
|
static SIM_INLINE t_bool cpu_v_flag();
|
||||||
|
static SIM_INLINE void cpu_set_z_flag(t_bool val);
|
||||||
|
static SIM_INLINE void cpu_set_n_flag(t_bool val);
|
||||||
|
static SIM_INLINE void cpu_set_c_flag(t_bool val);
|
||||||
|
static SIM_INLINE void cpu_set_v_flag(t_bool val);
|
||||||
|
static SIM_INLINE void cpu_set_v_flag_op(t_uint64 val, operand *op);
|
||||||
|
static SIM_INLINE uint8 cpu_execution_level();
|
||||||
|
static SIM_INLINE void cpu_set_execution_level(uint8 level);
|
||||||
|
static SIM_INLINE void cpu_push_word(uint32 val);
|
||||||
|
static SIM_INLINE uint32 cpu_pop_word();
|
||||||
|
static SIM_INLINE void irq_push_word(uint32 val);
|
||||||
|
static SIM_INLINE uint32 irq_pop_word();
|
||||||
|
static SIM_INLINE void cpu_context_switch_1(uint32 pcbp);
|
||||||
|
static SIM_INLINE void cpu_context_switch_2(uint32 pcbp);
|
||||||
|
static SIM_INLINE void cpu_context_switch_3(uint32 pcbp);
|
||||||
|
static SIM_INLINE t_bool op_is_psw(operand *op);
|
||||||
|
static SIM_INLINE t_bool op_is_sp(operand *op);
|
||||||
|
static SIM_INLINE uint32 cpu_next_pc();
|
||||||
|
static SIM_INLINE void add(t_uint64 a, t_uint64 b, operand *dst);
|
||||||
|
static SIM_INLINE void sub(t_uint64 a, t_uint64 b, operand *dst);
|
||||||
|
|
||||||
|
/* Helper macros */
|
||||||
|
|
||||||
|
#define MOD(A,B,OP1,OP2,SZ) { \
|
||||||
|
if (op_signed(OP1) && !op_signed(OP2)) { \
|
||||||
|
result = (SZ)(B) % (A); \
|
||||||
|
} else if (!op_signed(OP1) && op_signed(OP2)) { \
|
||||||
|
result = (B) % (SZ)(A); \
|
||||||
|
} else if (op_signed(OP1) && op_signed(OP2)) { \
|
||||||
|
result = (SZ)(B) % (SZ)(A); \
|
||||||
|
} else { \
|
||||||
|
result = (B) % (A); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DIV(A,B,OP1,OP2,SZ) { \
|
||||||
|
if (op_signed(OP1) && !op_signed(OP2)) { \
|
||||||
|
result = (SZ)(B) / (A); \
|
||||||
|
} else if (!op_signed(OP1) && op_signed(OP2)) { \
|
||||||
|
result = (B) / (SZ)(A); \
|
||||||
|
} else if (op_signed(OP1) && op_signed(OP2)) { \
|
||||||
|
result = (SZ)(B) / (SZ)(A); \
|
||||||
|
} else { \
|
||||||
|
result = (B) / (A); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
362
3B2/3b2_defs.h
Normal file
362
3B2/3b2_defs.h
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
/* 3b2_defs.h: AT&T 3B2 Model 400 Simulator Definitions
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _3B2_DEFS_H_
|
||||||
|
#define _3B2_DEFS_H_
|
||||||
|
|
||||||
|
#include "sim_defs.h"
|
||||||
|
#include "sim_tmxr.h"
|
||||||
|
#include <setjmp.h>
|
||||||
|
|
||||||
|
#define FALSE 0
|
||||||
|
#define TRUE 1
|
||||||
|
|
||||||
|
#if defined (__GNUC__)
|
||||||
|
#define noret void __attribute__((noreturn))
|
||||||
|
#else
|
||||||
|
#define noret void
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GLIBC__) && !defined(__cplusplus)
|
||||||
|
/* use glibc internal longjmp to bypass fortify checks */
|
||||||
|
noret __libc_longjmp (jmp_buf buf, int val);
|
||||||
|
#define longjmp __libc_longjmp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* -v flag for examine routine */
|
||||||
|
#define EX_V_FLAG 1 << 21
|
||||||
|
|
||||||
|
#define MAX_HIST_SIZE 1000000
|
||||||
|
#define MIN_HIST_SIZE 64
|
||||||
|
#define MAXMEMSIZE (1 << 22) /* 4 MB */
|
||||||
|
#define MEM_SIZE (cpu_unit.capac) /* actual memory size */
|
||||||
|
#define UNIT_V_MSIZE (UNIT_V_UF)
|
||||||
|
|
||||||
|
#define UNIT_MSIZE (1 << UNIT_V_MSIZE)
|
||||||
|
|
||||||
|
#define WD_MSB 0x80000000
|
||||||
|
#define HW_MSB 0x8000
|
||||||
|
#define BT_MSB 0x80
|
||||||
|
#define WORD_MASK 0xffffffff
|
||||||
|
#define HALF_MASK 0xffffu
|
||||||
|
#define BYTE_MASK 0xff
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Physical memory in the 3B2 is arranged as follows:
|
||||||
|
*
|
||||||
|
* 0x00000000 - 0x0000FFFF: 64KB ROM (32K used)
|
||||||
|
* 0x00040000 - 0x0004FFFF: IO
|
||||||
|
* 0x02000000 - 0x023FFFFF: 4MB RAM ("Mainstore"),
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define PHYS_MEM_BASE 0x2000000
|
||||||
|
|
||||||
|
#define ROM_BASE 0
|
||||||
|
#define IO_BASE 0x40000
|
||||||
|
#define IO_SIZE 0x10000
|
||||||
|
#define IOB_BASE 0x200000
|
||||||
|
#define IOB_SIZE 0x1E00000
|
||||||
|
|
||||||
|
/* Register numbers */
|
||||||
|
#define NUM_FP 9
|
||||||
|
#define NUM_AP 10
|
||||||
|
#define NUM_PSW 11
|
||||||
|
#define NUM_SP 12
|
||||||
|
#define NUM_PCBP 13
|
||||||
|
#define NUM_ISP 14
|
||||||
|
#define NUM_PC 15
|
||||||
|
|
||||||
|
#define CPU_CM (cpu_km ? L_KERNEL : ((R[NUM_PSW] >> PSW_CM) & 3))
|
||||||
|
|
||||||
|
/* Simulator stop codes */
|
||||||
|
#define STOP_RSRV 1
|
||||||
|
#define STOP_IBKPT 2 /* Breakpoint encountered */
|
||||||
|
#define STOP_OPCODE 3 /* Invalid opcode */
|
||||||
|
#define STOP_IRQ 4 /* Interrupt */
|
||||||
|
#define STOP_EX 5 /* Exception */
|
||||||
|
#define STOP_ESTK 6 /* Exception stack too deep */
|
||||||
|
#define STOP_MMU 7 /* Unimplemented MMU Feature */
|
||||||
|
|
||||||
|
/* Exceptional conditions handled within the instruction loop */
|
||||||
|
#define ABORT_EXC 1 /* CPU exception */
|
||||||
|
#define ABORT_TRAP 2 /* CPU trap */
|
||||||
|
|
||||||
|
/* Contexts for aborts */
|
||||||
|
#define C_NONE 0 /* No context. Normal handling. */
|
||||||
|
#define C_NORMAL_GATE_VECTOR 1
|
||||||
|
#define C_PROCESS_GATE_PCB 2
|
||||||
|
#define C_PROCESS_OLD_PCB 3
|
||||||
|
#define C_PROCESS_NEW_PCB 4
|
||||||
|
#define C_RESET_GATE_VECTOR 5
|
||||||
|
#define C_RESET_INT_STACK 6
|
||||||
|
#define C_RESET_NEW_PCB 7
|
||||||
|
#define C_RESET_SYSTEM_DATA 8
|
||||||
|
#define C_STACK_FAULT 9
|
||||||
|
|
||||||
|
/* Debug flags */
|
||||||
|
#define READ_MSG 0x01
|
||||||
|
#define WRITE_MSG 0x02
|
||||||
|
#define DECODE_MSG 0x04
|
||||||
|
#define EXECUTE_MSG 0x08
|
||||||
|
#define INIT_MSG 0x10
|
||||||
|
#define IRQ_MSG 0x20
|
||||||
|
#define IO_D_MSG 0x40
|
||||||
|
#define TRACE_MSG 0x80
|
||||||
|
|
||||||
|
/* Data types operated on by instructions. NB: These integer values
|
||||||
|
have meaning when decoding instructions, so this is not just an
|
||||||
|
enum. Please don't change them. */
|
||||||
|
#define UW 0 /* Unsigned Word */
|
||||||
|
#define UH 2 /* Unsigned Halfword */
|
||||||
|
#define BT 3 /* Unsigned Byte */
|
||||||
|
#define WD 4 /* Signed Word */
|
||||||
|
#define HW 6 /* Signed Halfword */
|
||||||
|
#define SB 7 /* Signed Byte */
|
||||||
|
|
||||||
|
#define NA -1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exceptions are described on page 2-66 of the WE32100 manual
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Exception Types */
|
||||||
|
|
||||||
|
#define RESET_EXCEPTION 0
|
||||||
|
#define PROCESS_EXCEPTION 1
|
||||||
|
#define STACK_EXCEPTION 2
|
||||||
|
#define NORMAL_EXCEPTION 3
|
||||||
|
|
||||||
|
/* Reset Exceptions */
|
||||||
|
#define OLD_PCB_FAULT 0
|
||||||
|
#define SYSTEM_DATA_FAULT 1
|
||||||
|
#define INTERRUPT_STACK_FAULT 2
|
||||||
|
#define EXTERNAL_RESET 3
|
||||||
|
#define NEW_PCB_FAULT 4
|
||||||
|
#define GATE_VECTOR_FAULT 6
|
||||||
|
|
||||||
|
/* Processor Exceptions */
|
||||||
|
#define GATE_PCB_FAULT 1
|
||||||
|
|
||||||
|
/* Stack Exceptions */
|
||||||
|
#define STACK_BOUND 0
|
||||||
|
#define STACK_FAULT 1
|
||||||
|
#define INTERRUPT_ID_FETCH 3
|
||||||
|
|
||||||
|
/* Normal Exceptions */
|
||||||
|
#define INTEGER_ZERO_DIVIDE 0
|
||||||
|
#define TRACE_TRAP 1
|
||||||
|
#define ILLEGAL_OPCODE 2
|
||||||
|
#define RESERVED_OPCODE 3
|
||||||
|
#define INVALID_DESCRIPTOR 4
|
||||||
|
#define EXTERNAL_MEMORY_FAULT 5
|
||||||
|
#define N_GATE_VECTOR 6
|
||||||
|
#define ILLEGAL_LEVEL_CHANGE 7
|
||||||
|
#define RESERVED_DATATYPE 8
|
||||||
|
#define INTEGER_OVERFLOW 9
|
||||||
|
#define PRIVILEGED_OPCODE 10
|
||||||
|
#define BREAKPOINT_TRAP 14
|
||||||
|
#define PRIVILEGED_REGISTER 15
|
||||||
|
|
||||||
|
#define PSW_ET 0
|
||||||
|
#define PSW_TM 2u
|
||||||
|
#define PSW_ISC 3u
|
||||||
|
#define PSW_I 7
|
||||||
|
#define PSW_R 8
|
||||||
|
#define PSW_PM 9
|
||||||
|
#define PSW_CM 11
|
||||||
|
#define PSW_IPL 13
|
||||||
|
#define PSW_TE 17
|
||||||
|
#define PSW_C 18
|
||||||
|
#define PSW_V 19
|
||||||
|
#define PSW_Z 20
|
||||||
|
#define PSW_N 21
|
||||||
|
#define PSW_OE 22
|
||||||
|
#define PSW_CD 23
|
||||||
|
#define PSW_QIE 24
|
||||||
|
#define PSW_CFD 25
|
||||||
|
|
||||||
|
/* Access Request types */
|
||||||
|
#define ACC_MT 0 /* Move Translated */
|
||||||
|
#define ACC_SPW 1 /* Support processor write */
|
||||||
|
#define ACC_SPF 3 /* Support processor fetch */
|
||||||
|
#define ACC_IR 7 /* Interlocked read */
|
||||||
|
#define ACC_AF 8 /* Address fetch */
|
||||||
|
#define ACC_OF 9 /* Operand fetch */
|
||||||
|
#define ACC_W 10 /* Write */
|
||||||
|
#define ACC_IFAD 12 /* Instruction fetch after discontinuity */
|
||||||
|
#define ACC_IF 13 /* Instruction fetch */
|
||||||
|
|
||||||
|
|
||||||
|
#define L_KERNEL 0
|
||||||
|
#define L_EXEC 1
|
||||||
|
#define L_SUPER 2
|
||||||
|
#define L_USER 3
|
||||||
|
|
||||||
|
#define PSW_ET_MASK 3u
|
||||||
|
#define PSW_TM_MASK (1u << PSW_TM)
|
||||||
|
#define PSW_ISC_MASK (15u << PSW_ISC)
|
||||||
|
#define PSW_I_MASK (1u << PSW_I)
|
||||||
|
#define PSW_R_MASK (1u << PSW_R)
|
||||||
|
#define PSW_PM_MASK (3u << PSW_PM)
|
||||||
|
#define PSW_CM_MASK (3u << PSW_CM)
|
||||||
|
#define PSW_IPL_MASK (15u << PSW_IPL)
|
||||||
|
#define PSW_TE_MASK (1u << PSW_TE)
|
||||||
|
#define PSW_C_MASK (1u << PSW_C)
|
||||||
|
#define PSW_V_MASK (1u << PSW_V)
|
||||||
|
#define PSW_N_MASK (1u << PSW_N)
|
||||||
|
#define PSW_Z_MASK (1u << PSW_Z)
|
||||||
|
#define PSW_OE_MASK (1u << PSW_OE)
|
||||||
|
#define PSW_CD_MASK (1u << PSW_CD)
|
||||||
|
#define PSW_QIE_MASK (1u << PSW_QIE)
|
||||||
|
#define PSW_CFD_MASK (1u << PSW_CFD)
|
||||||
|
|
||||||
|
#define PSW_CUR_IPL (((R[NUM_PSW] & PSW_IPL_MASK) >> PSW_IPL) & 0xf)
|
||||||
|
|
||||||
|
#define TODBASE 0x41000
|
||||||
|
#define TODSIZE 0x40
|
||||||
|
#define TIMERBASE 0x42000
|
||||||
|
#define TIMERSIZE 0x20
|
||||||
|
#define NVRAMBASE 0x43000
|
||||||
|
#define NVRAMSIZE 0x1000
|
||||||
|
#define CSRBASE 0x44000
|
||||||
|
#define CSRSIZE 0x100
|
||||||
|
|
||||||
|
#define CSRTIMO 0x8000 /* Bus Timeout Error */
|
||||||
|
#define CSRPARE 0x4000 /* Memory Parity Error */
|
||||||
|
#define CSRRRST 0x2000 /* System Reset Request */
|
||||||
|
#define CSRALGN 0x1000 /* Memory Alignment Fault */
|
||||||
|
#define CSRLED 0x0800 /* Failure LED */
|
||||||
|
#define CSRFLOP 0x0400 /* Floppy Motor On */
|
||||||
|
#define CSRRES 0x0200 /* Reserved */
|
||||||
|
#define CSRITIM 0x0100 /* Inhibit Timers */
|
||||||
|
#define CSRIFLT 0x0080 /* Inhibit Faults */
|
||||||
|
#define CSRCLK 0x0040 /* Clock Interrupt */
|
||||||
|
#define CSRPIR8 0x0020 /* Programmed Interrupt 8 */
|
||||||
|
#define CSRPIR9 0x0010 /* Programmed Interrupt 9 */
|
||||||
|
#define CSRUART 0x0008 /* UART Interrupt */
|
||||||
|
#define CSRDISK 0x0004 /* Floppy Interrupt */
|
||||||
|
#define CSRDMA 0x0002 /* DMA Interrupt */
|
||||||
|
#define CSRIOF 0x0001 /* I/O Board Fail */
|
||||||
|
|
||||||
|
#define TIMER_REG_DIVA 0x03
|
||||||
|
#define TIMER_REG_DIVB 0x07
|
||||||
|
#define TIMER_REG_DIVC 0x0b
|
||||||
|
#define TIMER_REG_CTRL 0x0f
|
||||||
|
#define TIMER_CLR_LATCH 0x13
|
||||||
|
|
||||||
|
/* Clock state bitmasks */
|
||||||
|
#define CLK_MD 0x0E /* Mode mask */
|
||||||
|
#define CLK_RW 0x30 /* RW mask */
|
||||||
|
#define CLK_SC 0xC0 /* SC mask */
|
||||||
|
|
||||||
|
#define CLK_LAT 0x00
|
||||||
|
#define CLK_LSB 0x10
|
||||||
|
#define CLK_MSB 0x20
|
||||||
|
#define CLK_LMB 0x30
|
||||||
|
|
||||||
|
#define CLK_MD0 0x00
|
||||||
|
#define CLK_MD1 0x02
|
||||||
|
#define CLK_MD2 0x04
|
||||||
|
#define CLK_MD3 0x06
|
||||||
|
#define CLK_MD4 0x08
|
||||||
|
#define CLK_MD5 0x0a
|
||||||
|
|
||||||
|
/* Timer definitions */
|
||||||
|
|
||||||
|
#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */
|
||||||
|
#define TMR_TOD 1 /* The Time-of-Day clock */
|
||||||
|
|
||||||
|
#define TPS_CLK 100
|
||||||
|
#define TPS_TOD 10
|
||||||
|
|
||||||
|
|
||||||
|
/* TIMING SECTION */
|
||||||
|
/* ----------------------------------------------- */
|
||||||
|
/* Calculate delays (in simulator steps) for times */
|
||||||
|
/* System clock runs at 10MHz; 100ns period. */
|
||||||
|
|
||||||
|
#define US_PER_INST 0.9
|
||||||
|
|
||||||
|
#define INST_PER_MS (1000.0 / US_PER_INST)
|
||||||
|
|
||||||
|
#define DELAY_US(us) ((uint32)((us) / US_PER_INST))
|
||||||
|
#define DELAY_MS(ms) ((uint32)(((ms) * 1000) / US_PER_INST))
|
||||||
|
|
||||||
|
/* global symbols from the CPU */
|
||||||
|
|
||||||
|
extern jmp_buf save_env;
|
||||||
|
extern uint32 *ROM;
|
||||||
|
extern uint32 *RAM;
|
||||||
|
extern uint32 R[16];
|
||||||
|
extern REG cpu_reg[];
|
||||||
|
extern DEVICE cpu_dev;
|
||||||
|
extern UNIT cpu_unit;
|
||||||
|
extern uint8 fault;
|
||||||
|
extern DEBTAB sys_deb_tab[];
|
||||||
|
extern t_bool cpu_km;
|
||||||
|
|
||||||
|
/* Generic callback function */
|
||||||
|
typedef void (*callback)(void);
|
||||||
|
|
||||||
|
/* global symbols from the DMAC */
|
||||||
|
extern DEVICE dmac_dev;
|
||||||
|
|
||||||
|
/* global symbols from the CSR */
|
||||||
|
extern uint16 csr_data;
|
||||||
|
|
||||||
|
/* global symbols from the IU */
|
||||||
|
extern t_bool iu_increment_a;
|
||||||
|
extern t_bool iu_increment_b;
|
||||||
|
extern void increment_modep_a();
|
||||||
|
extern void increment_modep_b();
|
||||||
|
|
||||||
|
/* global symbols from the MMU */
|
||||||
|
extern t_bool mmu_enabled();
|
||||||
|
extern void mmu_enable();
|
||||||
|
extern void mmu_disable();
|
||||||
|
extern uint8 read_b(uint32 va, uint8 acc);
|
||||||
|
extern uint16 read_h(uint32 va, uint8 acc);
|
||||||
|
extern uint32 read_w(uint32 va, uint8 acc);
|
||||||
|
extern void write_b(uint32 va, uint8 val);
|
||||||
|
extern void write_h(uint32 va, uint16 val);
|
||||||
|
extern void write_w(uint32 va, uint32 val);
|
||||||
|
|
||||||
|
/* Globally scoped CPU functions */
|
||||||
|
void cpu_abort(uint8 et, uint8 isc);
|
||||||
|
void cpu_set_irq(uint8 ipl, uint8 id, uint16 csr_flags);
|
||||||
|
void cpu_clear_irq(uint8 ipl, uint16 csr_flags);
|
||||||
|
|
||||||
|
/* Globally scoped IO functions */
|
||||||
|
uint32 io_read(uint32 pa, size_t size);
|
||||||
|
void io_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
|
||||||
|
#endif
|
443
3B2/3b2_dmac.c
Normal file
443
3B2/3b2_dmac.c
Normal file
|
@ -0,0 +1,443 @@
|
||||||
|
/* 3b2_dmac.c: AT&T 3B2 Model 400 AM9517A DMA Controller Implementation
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "3b2_dmac.h"
|
||||||
|
|
||||||
|
DMA_STATE dma_state;
|
||||||
|
|
||||||
|
UNIT dmac_unit[] = {
|
||||||
|
{ UDATA (NULL, 0, 0), 0, 0 },
|
||||||
|
{ UDATA (NULL, 0, 0), 0, 1 },
|
||||||
|
{ UDATA (NULL, 0, 0), 0, 2 },
|
||||||
|
{ UDATA (NULL, 0, 0), 0, 3 },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
REG dmac_reg[] = {
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE dmac_dev = {
|
||||||
|
"DMAC", dmac_unit, dmac_reg, NULL,
|
||||||
|
1, 16, 8, 4, 16, 32,
|
||||||
|
NULL, NULL, &dmac_reset,
|
||||||
|
NULL, NULL, NULL, NULL,
|
||||||
|
DEV_DEBUG, 0, sys_deb_tab
|
||||||
|
};
|
||||||
|
|
||||||
|
dmac_drq_handler dmac_drq_handlers[] = {
|
||||||
|
{DMA_ID_CHAN, IDBASE+ID_DATA_REG, &id_drq, id_drq_handled},
|
||||||
|
{DMA_IF_CHAN, IFBASE+IF_DATA_REG, &if_state.drq, if_drq_handled},
|
||||||
|
{DMA_IUA_CHAN, IUBASE+IUA_DATA_REG, &iu_state.drqa, iua_drq_handled},
|
||||||
|
{DMA_IUB_CHAN, IUBASE+IUB_DATA_REG, &iu_state.drqb, iub_drq_handled},
|
||||||
|
{0, 0, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
t_stat dmac_reset(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(&dma_state, 0, sizeof(dma_state));
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
dma_state.channels[i].page = 0;
|
||||||
|
dma_state.channels[i].addr = 0;
|
||||||
|
dma_state.channels[i].wcount = 0;
|
||||||
|
dma_state.channels[i].addr_c = 0;
|
||||||
|
dma_state.channels[i].wcount_c = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 dmac_read(uint32 pa, size_t size)
|
||||||
|
{
|
||||||
|
uint8 reg, base, data;
|
||||||
|
|
||||||
|
base =(uint8) (pa >> 12);
|
||||||
|
reg = pa & 0xff;
|
||||||
|
|
||||||
|
switch (base) {
|
||||||
|
case DMA_C: /* 0x48xxx */
|
||||||
|
switch (reg) {
|
||||||
|
case 0: /* channel 0 current address reg */
|
||||||
|
data = ((dma_state.channels[0].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] Reading Channel 0 Addr Reg: %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
dma_state.bff ^= 1;
|
||||||
|
break;
|
||||||
|
case 1: /* channel 0 current address reg */
|
||||||
|
data = ((dma_state.channels[0].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] Reading Channel 0 Addr Count Reg: %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
dma_state.bff ^= 1;
|
||||||
|
break;
|
||||||
|
case 2: /* channel 1 current address reg */
|
||||||
|
data = ((dma_state.channels[1].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] Reading Channel 1 Addr Reg: %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
dma_state.bff ^= 1;
|
||||||
|
break;
|
||||||
|
case 3: /* channel 1 current address reg */
|
||||||
|
data = ((dma_state.channels[1].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] Reading Channel 1 Addr Count Reg: %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
dma_state.bff ^= 1;
|
||||||
|
break;
|
||||||
|
case 4: /* channel 2 current address reg */
|
||||||
|
data = ((dma_state.channels[2].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] Reading Channel 2 Addr Reg: %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
dma_state.bff ^= 1;
|
||||||
|
break;
|
||||||
|
case 5: /* channel 2 current address reg */
|
||||||
|
data = ((dma_state.channels[2].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] Reading Channel 2 Addr Count Reg: %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
dma_state.bff ^= 1;
|
||||||
|
break;
|
||||||
|
case 6: /* channel 3 current address reg */
|
||||||
|
data = ((dma_state.channels[3].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] Reading Channel 3 Addr Reg: %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
dma_state.bff ^= 1;
|
||||||
|
break;
|
||||||
|
case 7: /* channel 3 current address reg */
|
||||||
|
data = ((dma_state.channels[3].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] Reading Channel 3 Addr Count Reg: %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
dma_state.bff ^= 1;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
data = dma_state.status;
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] Reading DMAC Status %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
dma_state.status = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] DMAC READ %lu B @ %08x\n",
|
||||||
|
R[NUM_PC], size, pa);
|
||||||
|
data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
default:
|
||||||
|
sim_debug(READ_MSG, &dmac_dev,
|
||||||
|
"[%08x] [BASE: %08x] DMAC READ %lu B @ %08x\n",
|
||||||
|
R[NUM_PC], base, size, pa);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Program the DMAC
|
||||||
|
*/
|
||||||
|
void dmac_program(uint8 reg, uint8 val)
|
||||||
|
{
|
||||||
|
uint8 channel_id, i, chan_num;
|
||||||
|
dma_channel *channel;
|
||||||
|
|
||||||
|
if (reg < 8) {
|
||||||
|
switch (reg) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
chan_num = 0;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
chan_num = 1;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
chan_num = 2;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
chan_num = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel = &dma_state.channels[chan_num];
|
||||||
|
|
||||||
|
if (channel == NULL) {
|
||||||
|
/* This should never happen */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reg & 1) {
|
||||||
|
case 0: /* Address */
|
||||||
|
channel->addr &= ~(0xff << dma_state.bff * 8);
|
||||||
|
channel->addr |= (val & 0xff) << (dma_state.bff * 8);
|
||||||
|
channel->addr_c = channel->addr;
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev,
|
||||||
|
"Set address channel %d byte %d = %08x\n",
|
||||||
|
chan_num, dma_state.bff, channel->addr);
|
||||||
|
break;
|
||||||
|
case 1: /* Word Count */
|
||||||
|
channel->wcount &= ~(0xff << dma_state.bff * 8);
|
||||||
|
channel->wcount |= (val & 0xff) << (dma_state.bff * 8);
|
||||||
|
channel->wcount_c = channel->wcount;
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev,
|
||||||
|
"Set word count channel %d byte %d = %08x\n",
|
||||||
|
chan_num, dma_state.bff, channel->wcount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle the byte flip-flop */
|
||||||
|
dma_state.bff ^= 1;
|
||||||
|
|
||||||
|
/* Handled. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If it hasn't been handled, it must be one of the following
|
||||||
|
registers. */
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case 8: /* Command */
|
||||||
|
dma_state.command = val;
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev,
|
||||||
|
"[%08x] Command: val=%02x\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
break;
|
||||||
|
case 9: /* Request */
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev,
|
||||||
|
"[%08x] Request set: val=%02x\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
dma_state.request = val;
|
||||||
|
break;
|
||||||
|
case 10: /* Write Single Mask Register Bit */
|
||||||
|
channel_id = val & 3;
|
||||||
|
|
||||||
|
/* "Clear or Set" is bit 2 */
|
||||||
|
if ((val >> 2) & 1) {
|
||||||
|
dma_state.mask |= (1 << channel_id);
|
||||||
|
} else {
|
||||||
|
dma_state.mask &= ~(1 << channel_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev,
|
||||||
|
"[%08x] Write Single Mask Register Bit. channel=%d set/clear=%02x\n",
|
||||||
|
R[NUM_PC], channel_id, (val >> 2) & 1);
|
||||||
|
break;
|
||||||
|
case 11: /* Mode */
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev,
|
||||||
|
"[%08x] Mode Set. val=%02x\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
dma_state.mode = val;
|
||||||
|
break;
|
||||||
|
case 12: /* Clear Byte Pointer Flip/Flop */
|
||||||
|
dma_state.bff = 0;
|
||||||
|
break;
|
||||||
|
case 13: /* Master Clear */
|
||||||
|
dma_state.bff = 0;
|
||||||
|
dma_state.command = 0;
|
||||||
|
dma_state.status = 0;
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
dma_state.channels[i].addr = 0;
|
||||||
|
dma_state.channels[i].wcount = 0;
|
||||||
|
dma_state.channels[i].page = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 15: /* Write All Mask Register Bits */
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev,
|
||||||
|
"[%08x] Clear DMAC Interrupt. val=%02x\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
dma_state.mask = val & 0xf;
|
||||||
|
break;
|
||||||
|
case 16: /* Clear DMAC Interrupt */
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev,
|
||||||
|
"[%08x] Clear DMAC Interrupt in DMAC. val=%02x\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev,
|
||||||
|
"[%08x] Unhandled DMAC write. reg=%x val=%02x\n",
|
||||||
|
R[NUM_PC], reg, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmac_page_update(uint8 base, uint8 reg, uint8 val)
|
||||||
|
{
|
||||||
|
uint8 shift = 0;
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
if (reg > 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The actual register is a 32-bit, byte-addressed register, so
|
||||||
|
that address 4x000 is the highest byte, 4x003 is the lowest
|
||||||
|
byte. */
|
||||||
|
|
||||||
|
shift = -(reg - 3) * 8;
|
||||||
|
|
||||||
|
switch (base) {
|
||||||
|
case DMA_ID:
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 0 = %x\n", val);
|
||||||
|
dma_state.channels[DMA_ID_CHAN].page &= ~(0xff << shift);
|
||||||
|
dma_state.channels[DMA_ID_CHAN].page |= (val << shift);
|
||||||
|
break;
|
||||||
|
case DMA_IF:
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 1 = %x\n", val);
|
||||||
|
dma_state.channels[DMA_IF_CHAN].page &= ~(0xff << shift);
|
||||||
|
dma_state.channels[DMA_IF_CHAN].page |= (val << shift);
|
||||||
|
break;
|
||||||
|
case DMA_IUA:
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 2 = %x\n", val);
|
||||||
|
dma_state.channels[DMA_IUA_CHAN].page &= ~(0xff << shift);
|
||||||
|
dma_state.channels[DMA_IUA_CHAN].page |= (val << shift);
|
||||||
|
break;
|
||||||
|
case DMA_IUB:
|
||||||
|
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 3 = %x\n", val);
|
||||||
|
dma_state.channels[DMA_IUB_CHAN].page &= ~(0xff << shift);
|
||||||
|
dma_state.channels[DMA_IUB_CHAN].page |= (val << shift);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmac_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
uint8 reg, base;
|
||||||
|
|
||||||
|
base = (uint8) (pa >> 12);
|
||||||
|
reg = pa & 0xff;
|
||||||
|
|
||||||
|
switch (base) {
|
||||||
|
case DMA_C: /* 0x48xxx */
|
||||||
|
dmac_program(reg, (uint8) val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DMA_ID: /* 0x45xxx */
|
||||||
|
case DMA_IUA: /* 0x46xxx */
|
||||||
|
case DMA_IUB: /* 0x47xxx */
|
||||||
|
case DMA_IF: /* 0x4Exxx */
|
||||||
|
dmac_page_update(base, reg, (uint8) val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE uint32 dma_address(uint8 channel, uint32 offset, t_bool r) {
|
||||||
|
uint32 addr;
|
||||||
|
|
||||||
|
addr = (PHYS_MEM_BASE +
|
||||||
|
dma_state.channels[channel].addr +
|
||||||
|
offset);
|
||||||
|
|
||||||
|
/* The top bit of the page address is a R/W bit, so we mask it here */
|
||||||
|
addr |= (uint32) (((uint32)dma_state.channels[channel].page & 0x7f) << 16);
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmac_transfer(uint8 channel, uint32 service_address)
|
||||||
|
{
|
||||||
|
uint8 data;
|
||||||
|
int32 i;
|
||||||
|
uint16 offset;
|
||||||
|
uint32 addr;
|
||||||
|
|
||||||
|
dma_channel *chan = &dma_state.channels[channel];
|
||||||
|
|
||||||
|
/* TODO: This does not handle decrement-mode transfers,
|
||||||
|
which don't seem to be used in SVR3 */
|
||||||
|
|
||||||
|
switch ((dma_state.mode >> 2) & 0xf) {
|
||||||
|
case DMA_MODE_VERIFY:
|
||||||
|
sim_debug(EXECUTE_MSG, &dmac_dev,
|
||||||
|
"[%08x] [dmac_transfer channel=%d] unhandled VERIFY request.\n",
|
||||||
|
R[NUM_PC], channel);
|
||||||
|
break;
|
||||||
|
case DMA_MODE_WRITE:
|
||||||
|
sim_debug(EXECUTE_MSG, &dmac_dev,
|
||||||
|
"[%08x] [dmac_transfer channel=%d] write: %d bytes from %08x\n",
|
||||||
|
R[NUM_PC], channel,
|
||||||
|
chan->wcount + 1,
|
||||||
|
dma_address(channel, 0, TRUE));
|
||||||
|
offset = 0;
|
||||||
|
for (i = chan->wcount; i >= 0; i--) {
|
||||||
|
addr = dma_address(channel, offset, TRUE);
|
||||||
|
chan->addr_c = dma_state.channels[channel].addr + offset;
|
||||||
|
offset++;
|
||||||
|
data = pread_b(service_address);
|
||||||
|
write_b(addr, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DMA_MODE_READ:
|
||||||
|
sim_debug(EXECUTE_MSG, &dmac_dev,
|
||||||
|
"[%08x] [dmac_transfer channel=%d] read: %d bytes to %08x\n",
|
||||||
|
R[NUM_PC], channel,
|
||||||
|
chan->wcount + 1,
|
||||||
|
dma_address(channel, 0, TRUE));
|
||||||
|
offset = 0;
|
||||||
|
for (i = chan->wcount; i >= 0; i--) {
|
||||||
|
addr = dma_address(channel, offset++, TRUE);
|
||||||
|
chan->addr_c = dma_state.channels[channel].addr + offset;
|
||||||
|
data = pread_b(addr);
|
||||||
|
write_b(service_address, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End of Process must set the IF channel's mask bit */
|
||||||
|
dma_state.mask |= (1 << channel);
|
||||||
|
dma_state.status |= (1 << channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Service pending DRQs
|
||||||
|
*/
|
||||||
|
void dmac_service_drqs()
|
||||||
|
{
|
||||||
|
dmac_drq_handler *h;
|
||||||
|
|
||||||
|
for (h = &dmac_drq_handlers[0]; h->drq != NULL; h++) {
|
||||||
|
/* Only trigger if the channel has a DRQ set and its channel's
|
||||||
|
mask bit is 0 */
|
||||||
|
if (*h->drq && ((dma_state.mask >> h->channel) & 0x1) == 0) {
|
||||||
|
dmac_transfer(h->channel, h->service_address);
|
||||||
|
*h->drq = FALSE; /* Immediately clear DRQ state */
|
||||||
|
if (h->handled_callback != NULL) {
|
||||||
|
h->handled_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
113
3B2/3b2_dmac.h
Normal file
113
3B2/3b2_dmac.h
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/* 3b2_dmac.h: AT&T 3B2 Model 400 AM9517A DMA Controller Header
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _3B2_DMAC_H_
|
||||||
|
#define _3B2_DMAC_H_
|
||||||
|
|
||||||
|
#include "3b2_sysdev.h"
|
||||||
|
#include "3b2_id.h"
|
||||||
|
#include "3b2_if.h"
|
||||||
|
|
||||||
|
/* DMA Controller */
|
||||||
|
#define DMACBASE 0x48000
|
||||||
|
#define DMACSIZE 0x11
|
||||||
|
|
||||||
|
/* DMA integrated disk page buffer */
|
||||||
|
#define DMAIDBASE 0x45000
|
||||||
|
#define DMAIDSIZE 0x5
|
||||||
|
|
||||||
|
/* DMA integrated uart A page buffer */
|
||||||
|
#define DMAIUABASE 0x46000
|
||||||
|
#define DMAIUASIZE 0x5
|
||||||
|
|
||||||
|
/* DMA integrated uart B page buffer */
|
||||||
|
#define DMAIUBBASE 0x47000
|
||||||
|
#define DMAIUBSIZE 0x5
|
||||||
|
|
||||||
|
/* DMA integrated floppy page buffer */
|
||||||
|
#define DMAIFBASE 0x4E000
|
||||||
|
#define DMAIFSIZE 0x5
|
||||||
|
|
||||||
|
#define DMA_ID_CHAN 0
|
||||||
|
#define DMA_IF_CHAN 1
|
||||||
|
#define DMA_IUA_CHAN 2
|
||||||
|
#define DMA_IUB_CHAN 3
|
||||||
|
|
||||||
|
#define DMA_ID 0x45
|
||||||
|
#define DMA_IUA 0x46
|
||||||
|
#define DMA_IUB 0x47
|
||||||
|
#define DMA_C 0x48
|
||||||
|
#define DMA_IF 0x4E
|
||||||
|
|
||||||
|
#define DMA_MODE_VERIFY 0
|
||||||
|
#define DMA_MODE_WRITE 1 /* Write to memory from device */
|
||||||
|
#define DMA_MODE_READ 2 /* Read from memory to device */
|
||||||
|
|
||||||
|
#define DMA_IF_READ (IFBASE + IF_DATA_REG)
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8 page;
|
||||||
|
uint16 addr; /* Original addr */
|
||||||
|
uint16 wcount; /* Original wcount */
|
||||||
|
uint16 addr_c; /* Current addr */
|
||||||
|
uint16 wcount_c; /* Current word-count */
|
||||||
|
} dma_channel;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* Byte (high/low) flip-flop */
|
||||||
|
uint8 bff;
|
||||||
|
|
||||||
|
/* Address and count registers for channels 0-3 */
|
||||||
|
dma_channel channels[4];
|
||||||
|
|
||||||
|
/* DMAC programmable registers */
|
||||||
|
uint8 command;
|
||||||
|
uint8 mode;
|
||||||
|
uint8 request;
|
||||||
|
uint8 mask;
|
||||||
|
uint8 status;
|
||||||
|
} DMA_STATE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8 channel;
|
||||||
|
uint32 service_address;
|
||||||
|
t_bool *drq;
|
||||||
|
void (*handled_callback)();
|
||||||
|
} dmac_drq_handler;
|
||||||
|
|
||||||
|
/* DMAC */
|
||||||
|
t_stat dmac_reset(DEVICE *dptr);
|
||||||
|
uint32 dmac_read(uint32 pa, size_t size);
|
||||||
|
void dmac_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
void dmac_service_drqs();
|
||||||
|
void dmac_transfer(uint8 channel, uint32 service_address);
|
||||||
|
|
||||||
|
#endif
|
844
3B2/3b2_id.c
Normal file
844
3B2/3b2_id.c
Normal file
|
@ -0,0 +1,844 @@
|
||||||
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 Hard Disk (uPD7261) Implementation
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The larger of the two hard drive options shipped with the AT&T 3B2
|
||||||
|
* was a 72MB Wren II ST-506 MFM hard disk. That's what we emulate
|
||||||
|
* here, as a start.
|
||||||
|
*
|
||||||
|
* Formatted Capacity: 76,723,200 Bytes
|
||||||
|
*
|
||||||
|
* Cylinders: 925
|
||||||
|
* Sectors/Track: 18
|
||||||
|
* Heads: 9
|
||||||
|
* Bytes/Sector: 512
|
||||||
|
* Avg. seek: 28 ms
|
||||||
|
* Seek/track: 5 ms
|
||||||
|
*
|
||||||
|
* Drive Information from the 3B2 FAQ:
|
||||||
|
*
|
||||||
|
* Drive type drv id cyls trk/cyl sec/trk byte/cyl abbrev
|
||||||
|
* --------------- ------ ---- ------- ------- -------- ------
|
||||||
|
* Wren II 30MB 3 697 5 18 512 HD30
|
||||||
|
* Wren II 72MB 5 925 9 18 512 HD72
|
||||||
|
* Fujitsu M2243AS 8 754 11 18 512 HD72C
|
||||||
|
* Micropolis 1325 5 1024 8 18 512 HD72
|
||||||
|
* Maxtor 1140 4* 918= 15 18 512 HD120
|
||||||
|
* Maxtor 1190 11 1224+ 15 18 512 HD135
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include "3b2_id.h"
|
||||||
|
|
||||||
|
/* Wait times, in CPU steps, for various actions */
|
||||||
|
|
||||||
|
/* Each step is 50 us in buffered mode */
|
||||||
|
#define ID_SEEK_WAIT 100 /* us */
|
||||||
|
#define ID_SEEK_BASE 700 /* us */
|
||||||
|
#define ID_RECAL_WAIT 6000 /* us */
|
||||||
|
|
||||||
|
/* Reading data takes about 8ms per sector, plus time to seek if not
|
||||||
|
on cylinder */
|
||||||
|
#define ID_RW_WAIT 8000 /* us */
|
||||||
|
|
||||||
|
/* Sense Unit Status completes in about 200 us */
|
||||||
|
#define ID_SUS_WAIT 200 /* us */
|
||||||
|
|
||||||
|
/* Specify takes a bit longer, 1.25 ms */
|
||||||
|
#define ID_SPEC_WAIT 1250 /* us */
|
||||||
|
|
||||||
|
/* Sense Interrupt Status is about 142 us */
|
||||||
|
#define ID_SIS_WAIT 142 /* us */
|
||||||
|
|
||||||
|
/* The catch-all command wait time is about 140 us */
|
||||||
|
#define ID_CMD_WAIT 140 /* us */
|
||||||
|
|
||||||
|
/* State. The DP7261 supports four MFM (winchester) disks connected
|
||||||
|
simultaneously. There is only one set of registers, however, so
|
||||||
|
commands must be completed for one unit before they can begin on
|
||||||
|
another unit. */
|
||||||
|
|
||||||
|
/* Data FIFO pointer - Read */
|
||||||
|
uint8 id_dpr = 0;
|
||||||
|
/* Data FIFO pointer - Write */
|
||||||
|
uint8 id_dpw = 0;
|
||||||
|
/* Selected unit */
|
||||||
|
uint8 id_sel = 0;
|
||||||
|
/* Controller Status Register */
|
||||||
|
uint8 id_status = 0;
|
||||||
|
/* Unit Interrupt Status */
|
||||||
|
uint8 id_int_status;
|
||||||
|
/* Last command received */
|
||||||
|
uint8 id_cmd = 0;
|
||||||
|
/* DMAC request */
|
||||||
|
t_bool id_drq = FALSE;
|
||||||
|
/* 8-byte FIFO */
|
||||||
|
uint8 id_data[ID_FIFO_LEN] = {0};
|
||||||
|
/* INT output pin */
|
||||||
|
t_bool id_irq = FALSE;
|
||||||
|
/* Special flag for seek end SIS */
|
||||||
|
t_bool id_seek_sis = FALSE;
|
||||||
|
|
||||||
|
/* State of each drive */
|
||||||
|
|
||||||
|
/* Cylinder the drive is positioned on */
|
||||||
|
uint16 id_cyl[ID_NUM_UNITS] = {0};
|
||||||
|
|
||||||
|
/* DTLH byte for each drive */
|
||||||
|
uint8 id_dtlh[ID_NUM_UNITS] = {0};
|
||||||
|
|
||||||
|
/* Arguments of last READ, WRITE, VERIFY ID, or READ ID command */
|
||||||
|
|
||||||
|
/* Ending Track Number (from Specify) */
|
||||||
|
uint8 id_etn = 0;
|
||||||
|
/* Ending Sector Number (from Specify) */
|
||||||
|
uint8 id_esn = 0;
|
||||||
|
/* Physical sector number */
|
||||||
|
uint8 id_psn = 0;
|
||||||
|
/* Physical head number */
|
||||||
|
uint8 id_phn = 0;
|
||||||
|
/* Logical cylinder number, high byte */
|
||||||
|
uint8 id_lcnh = 0;
|
||||||
|
/* Logical cylinder number, low byte */
|
||||||
|
uint8 id_lcnl = 0;
|
||||||
|
/* Logical head number */
|
||||||
|
uint8 id_lhn = 0;
|
||||||
|
/* Logical sector number */
|
||||||
|
uint8 id_lsn = 0;
|
||||||
|
/* Number of sectors to transfer, decremented after each sector */
|
||||||
|
uint8 id_scnt = 0;
|
||||||
|
/* Sector buffer */
|
||||||
|
uint8 id_buf[ID_SEC_SIZE];
|
||||||
|
/* Buffer pointer */
|
||||||
|
size_t id_buf_ptr = 0;
|
||||||
|
|
||||||
|
uint8 id_idfield[ID_IDFIELD_LEN];
|
||||||
|
uint8 id_idfield_ptr = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Macros used for debugging timers. Remove when debugging is complete.
|
||||||
|
*/
|
||||||
|
double id_start_time;
|
||||||
|
|
||||||
|
#define ID_START_TIME() { id_start_time = sim_gtime(); }
|
||||||
|
#define ID_DIFF_MS() ((sim_gtime() - id_start_time) / INST_PER_MS)
|
||||||
|
|
||||||
|
UNIT id_unit[] = {
|
||||||
|
{UDATA (&id_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BINK, ID_DSK_SIZE), 0, 0 },
|
||||||
|
{UDATA (&id_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BINK, ID_DSK_SIZE), 0, 1 },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The currently selected drive number */
|
||||||
|
UNIT *id_sel_unit = &id_unit[0];
|
||||||
|
|
||||||
|
REG id_reg[] = {
|
||||||
|
{ HRDATAD(CMD, id_cmd, 8, "Command") },
|
||||||
|
{ HRDATAD(STAT, id_status, 8, "Status") },
|
||||||
|
{ BRDATAD(CYL, id_cyl, 8, 8, ID_NUM_UNITS, "Track") },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE id_dev = {
|
||||||
|
"ID", id_unit, id_reg, NULL,
|
||||||
|
ID_NUM_UNITS, 16, 32, 1, 16, 8,
|
||||||
|
NULL, NULL, &id_reset,
|
||||||
|
NULL, &id_attach, &id_detach, NULL,
|
||||||
|
DEV_DEBUG|DEV_SECTORS, 0, sys_deb_tab,
|
||||||
|
NULL, NULL, &id_help, NULL, NULL,
|
||||||
|
&id_description
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Function implementation */
|
||||||
|
|
||||||
|
static SIM_INLINE void id_activate(uint32 delay)
|
||||||
|
{
|
||||||
|
ID_START_TIME();
|
||||||
|
sim_activate_abs(id_sel_unit, (int32) delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void id_clear_fifo()
|
||||||
|
{
|
||||||
|
id_dpr = 0;
|
||||||
|
id_dpw = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat id_svc(UNIT *uptr)
|
||||||
|
{
|
||||||
|
/* Complete the last command */
|
||||||
|
id_status = ID_STAT_CEH;
|
||||||
|
|
||||||
|
switch (CMD_NUM) {
|
||||||
|
case ID_CMD_SEEK: /* fall-through */
|
||||||
|
case ID_CMD_RECAL:
|
||||||
|
/* SRQ is only set in polling mode (POL bit is 0) */
|
||||||
|
if ((id_dtlh[UNIT_NUM] & ID_DTLH_POLL) == 0) {
|
||||||
|
id_status |= ID_STAT_SRQ;
|
||||||
|
}
|
||||||
|
if (uptr->flags & UNIT_ATT) {
|
||||||
|
id_int_status = ID_IST_SEN|(uint8)uptr->ID_UNIT_NUM;
|
||||||
|
} else {
|
||||||
|
id_int_status = ID_IST_NR|(uint8)uptr->ID_UNIT_NUM;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ID_CMD_SIS:
|
||||||
|
if (!id_seek_sis) {
|
||||||
|
id_status = ID_STAT_CEL;
|
||||||
|
}
|
||||||
|
id_seek_sis = FALSE;
|
||||||
|
id_data[0] = id_int_status;
|
||||||
|
id_status &= ~ID_STAT_SRQ;
|
||||||
|
break;
|
||||||
|
case ID_CMD_SUS:
|
||||||
|
if ((id_sel_unit->flags & UNIT_ATT) == 0) {
|
||||||
|
/* If no HD is attached, SUS puts 0x00 into the data
|
||||||
|
buffer */
|
||||||
|
id_data[0] = 0;
|
||||||
|
} else {
|
||||||
|
/* Put Unit Status into byte 0 */
|
||||||
|
id_data[0] = (ID_UST_DSEL|ID_UST_SCL|ID_UST_RDY);
|
||||||
|
if (id_cyl[UNIT_NUM] == 0) {
|
||||||
|
id_data[0] |= ID_UST_TK0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
"[%08x] \tINTR\t\tDELTA=%f ms\n",
|
||||||
|
R[NUM_PC], ID_DIFF_MS());
|
||||||
|
|
||||||
|
id_irq = TRUE;
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat id_reset(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
id_clear_fifo();
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat id_attach(UNIT *uptr, CONST char *cptr)
|
||||||
|
{
|
||||||
|
return sim_disk_attach(uptr, cptr, 512, 1, TRUE, 0, "HD72", 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat id_detach(UNIT *uptr)
|
||||||
|
{
|
||||||
|
return sim_disk_detach(uptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the logical block address of the given sector */
|
||||||
|
static SIM_INLINE t_lba id_lba(uint16 cyl, uint8 head, uint8 sec)
|
||||||
|
{
|
||||||
|
return((ID_SEC_CNT * ID_HEADS * cyl) +
|
||||||
|
(ID_SEC_CNT * head) +
|
||||||
|
sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* At the end of each sector read or write, we update the FIFO
|
||||||
|
* with the correct return parameters. */
|
||||||
|
static void SIM_INLINE id_end_rw(uint8 est) {
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
">>> ending R/W with status: %02x\n",
|
||||||
|
est);
|
||||||
|
id_dpr = 0;
|
||||||
|
id_dpw = 0;
|
||||||
|
id_data[0] = est;
|
||||||
|
id_data[1] = id_phn;
|
||||||
|
id_data[2] = ~(id_lcnh);
|
||||||
|
id_data[3] = id_lcnl;
|
||||||
|
id_data[4] = id_lhn;
|
||||||
|
id_data[5] = id_lsn;
|
||||||
|
id_data[6] = id_scnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The controller wraps id_lsn, id_lhn, and id_lcnl on each sector
|
||||||
|
* read, so that they point to the next C/H/S */
|
||||||
|
static void SIM_INLINE id_update_chs() {
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
">>> id_update_chs(): id_esn=%02x id_etn=%02x\n",
|
||||||
|
id_esn, id_etn);
|
||||||
|
|
||||||
|
if (id_lsn++ >= id_esn) {
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
">>> id_update_chs(): id_lsn reset to 0. id_lhn is %02x\n",
|
||||||
|
id_lhn);
|
||||||
|
id_lsn = 0;
|
||||||
|
if (id_lhn++ >= id_etn) {
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
">>> id_update_chs(): id_lhn reset to 0. id_lcnl is %02x\n",
|
||||||
|
id_lcnl);
|
||||||
|
id_lhn = 0;
|
||||||
|
if (id_lcnl == 0xff) {
|
||||||
|
id_lcnl = 0;
|
||||||
|
id_lcnh++;
|
||||||
|
} else {
|
||||||
|
id_lcnl++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 id_read(uint32 pa, size_t size) {
|
||||||
|
uint8 reg;
|
||||||
|
uint16 cyl;
|
||||||
|
t_lba lba;
|
||||||
|
uint32 data;
|
||||||
|
t_seccnt sectsread;
|
||||||
|
|
||||||
|
reg = (uint8) (pa - IDBASE);
|
||||||
|
|
||||||
|
switch(reg) {
|
||||||
|
case ID_DATA_REG: /* Data Buffer Register */
|
||||||
|
/* If we're in a DMA transfer, we need to be reading data from
|
||||||
|
* the disk buffer. Otherwise, we're reading from the FIFO. */
|
||||||
|
|
||||||
|
if (id_drq) {
|
||||||
|
/* If the drive isn't attached, there's really nothing we
|
||||||
|
can do. */
|
||||||
|
if ((id_sel_unit->flags & UNIT_ATT) == 0) {
|
||||||
|
id_end_rw(ID_EST_NR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We could be in one of these commands:
|
||||||
|
* - Read Data
|
||||||
|
* - Read ID
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (CMD_NUM == ID_CMD_RDATA) {
|
||||||
|
/* If we're still in DRQ but we've read all our sectors,
|
||||||
|
* that's an error state. */
|
||||||
|
if (id_scnt == 0) {
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x] ERROR\tid_scnt = 0 but still in dma\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
id_end_rw(ID_EST_OVR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the disk buffer is empty, fill it. */
|
||||||
|
if (id_buf_ptr == 0 || id_buf_ptr >= ID_SEC_SIZE) {
|
||||||
|
/* It's time to read a new sector into our sector buf */
|
||||||
|
id_buf_ptr = 0;
|
||||||
|
cyl = (uint16) (((uint16)id_lcnh << 8)|(uint16)id_lcnl);
|
||||||
|
id_cyl[UNIT_NUM] = cyl;
|
||||||
|
lba = id_lba(cyl, id_lhn, id_lsn);
|
||||||
|
if (sim_disk_rdsect(id_sel_unit, lba, id_buf, §sread, 1) == SCPE_OK) {
|
||||||
|
if (sectsread !=1) {
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x]\tERROR: ASKED TO READ ONE SECTOR, READ: %d\n",
|
||||||
|
R[NUM_PC], sectsread);
|
||||||
|
}
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x] \tRDATA\tCYL=%d PHN=%d LCNH=%02x "
|
||||||
|
"LCNL=%02x LHN=%d LSN=%d SCNT=%d LBA=%04x\n",
|
||||||
|
R[NUM_PC], cyl, id_phn, id_lcnh, id_lcnl,
|
||||||
|
id_lhn, id_lsn, id_scnt-1, lba);
|
||||||
|
id_update_chs();
|
||||||
|
} else {
|
||||||
|
/* Uh-oh! */
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x]\tRDATA READ ERROR. Failure from sim_disk_rdsect!\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
id_end_rw(ID_EST_DER);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = id_buf[id_buf_ptr++];
|
||||||
|
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x]\tSECTOR DATA\t%02x\t(%c)\n",
|
||||||
|
R[NUM_PC], data, (data >= 0x20 && data < 0x7f) ? data : '.');
|
||||||
|
|
||||||
|
/* Done with this current sector, update id_scnt */
|
||||||
|
if (id_buf_ptr >= ID_SEC_SIZE) {
|
||||||
|
if (--id_scnt == 0) {
|
||||||
|
id_end_rw(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (CMD_NUM == ID_CMD_RID) {
|
||||||
|
/* We have to return the ID bytes for the current C/H/S */
|
||||||
|
if (id_idfield_ptr == 0 || id_idfield_ptr >= ID_IDFIELD_LEN) {
|
||||||
|
id_idfield[0] = ~(id_lcnh);
|
||||||
|
id_idfield[1] = id_lcnl;
|
||||||
|
id_idfield[2] = id_lhn;
|
||||||
|
id_idfield[3] = id_lsn;
|
||||||
|
id_idfield_ptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = id_idfield[id_idfield_ptr++];
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x]\tID DATA\t%02x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
|
||||||
|
if (id_idfield_ptr >= ID_IDFIELD_LEN) {
|
||||||
|
if (id_scnt-- > 0) {
|
||||||
|
/* Another sector to ID */
|
||||||
|
id_idfield_ptr = 0;
|
||||||
|
} else {
|
||||||
|
/* All done, set return codes */
|
||||||
|
id_dpr = 0;
|
||||||
|
id_dpw = 0;
|
||||||
|
id_data[0] = 0;
|
||||||
|
id_data[1] = id_scnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(0); // cmd not Read Data or Read ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
if (id_dpr < ID_FIFO_LEN) {
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x]\tDATA\t%02x\n",
|
||||||
|
R[NUM_PC], id_data[id_dpr]);
|
||||||
|
return id_data[id_dpr++];
|
||||||
|
} else {
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x] ERROR\tFIFO OVERRUN\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ID_CMD_STAT_REG: /* Status Register */
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x]\tSTATUS\t%02x\n",
|
||||||
|
R[NUM_PC], id_status|id_drq);
|
||||||
|
return id_status|(id_drq ? 1u : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sim_debug(READ_MSG, &id_dev,
|
||||||
|
"[%08x] Read of unsuported register %x\n",
|
||||||
|
R[NUM_PC], id_status);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void id_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
uint8 reg;
|
||||||
|
uint16 cyl;
|
||||||
|
t_lba lba;
|
||||||
|
t_seccnt sectswritten;
|
||||||
|
|
||||||
|
reg = (uint8) (pa - IDBASE);
|
||||||
|
|
||||||
|
switch(reg) {
|
||||||
|
case ID_DATA_REG:
|
||||||
|
/* If we're in a DMA transfer, we need to be writing data to
|
||||||
|
* the disk buffer. Otherwise, we're writing to the FIFO. */
|
||||||
|
|
||||||
|
if (id_drq) {
|
||||||
|
/* If we're still in DRQ but we've written all our sectors,
|
||||||
|
* that's an error state. */
|
||||||
|
if (id_scnt == 0) {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x] ERROR\tid_scnt = 0 but still in dma\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
id_end_rw(ID_EST_OVR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write to the disk buffer */
|
||||||
|
if (id_buf_ptr < ID_SEC_SIZE) {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tSECTOR DATA\t%02x\t(%c)\n",
|
||||||
|
R[NUM_PC], val, (val >= 0x20 && val < 0x7f) ? val : '.');
|
||||||
|
id_buf[id_buf_ptr++] = (uint8)(val & 0xff);
|
||||||
|
} else {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x] ERROR\tWDATA OVERRUN\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
id_end_rw(ID_EST_OVR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we've hit the end of a sector, flush it */
|
||||||
|
if (id_buf_ptr >= ID_SEC_SIZE) {
|
||||||
|
/* It's time to start the next sector, and flush the old. */
|
||||||
|
id_buf_ptr = 0;
|
||||||
|
cyl = (uint16) (((uint16) id_lcnh << 8)|(uint16)id_lcnl);
|
||||||
|
id_cyl[UNIT_NUM] = cyl;
|
||||||
|
lba = id_lba(cyl, id_lhn, id_lsn);
|
||||||
|
if (sim_disk_wrsect(id_sel_unit, lba, id_buf, §swritten, 1) == SCPE_OK) {
|
||||||
|
if (sectswritten !=1) {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tERROR: ASKED TO WRITE ONE SECTOR, WROTE: %d\n",
|
||||||
|
R[NUM_PC], sectswritten);
|
||||||
|
}
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tWDATA\tCYL=%d PHN=%d LCNH=%02x "
|
||||||
|
"LCNL=%02x LHN=%d LSN=%d SCNT=%d LBA=%04x\n",
|
||||||
|
R[NUM_PC], cyl, id_phn, id_lcnh, id_lcnl,
|
||||||
|
id_lhn, id_lsn, id_scnt, lba);
|
||||||
|
id_update_chs();
|
||||||
|
if (--id_scnt == 0) {
|
||||||
|
id_end_rw(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Uh-oh! */
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x] ERROR\tWDATA WRITE ERROR. lba=%04x\n",
|
||||||
|
R[NUM_PC], lba);
|
||||||
|
id_end_rw(ID_EST_DER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tDATA\t%02x\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
|
||||||
|
if (id_dpw < ID_FIFO_LEN) {
|
||||||
|
id_data[id_dpw++] = (uint8) val;
|
||||||
|
} else {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x] ERROR\tFIFO OVERRUN\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case ID_CMD_STAT_REG:
|
||||||
|
id_handle_command((uint8) val);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void id_handle_command(uint8 val)
|
||||||
|
{
|
||||||
|
uint8 cmd, aux_cmd, sec, pattern;
|
||||||
|
uint16 cyl;
|
||||||
|
uint32 time;
|
||||||
|
t_lba lba;
|
||||||
|
|
||||||
|
/* Save the full command byte */
|
||||||
|
id_cmd = val;
|
||||||
|
|
||||||
|
/* Reset the FIFO pointer */
|
||||||
|
id_dpr = 0;
|
||||||
|
id_dpw = 0;
|
||||||
|
|
||||||
|
/* Writing a command always de-asserts INT output, UNLESS
|
||||||
|
the SRQ bit is set. */
|
||||||
|
if ((id_status & ID_STAT_SRQ) != ID_STAT_SRQ) {
|
||||||
|
id_irq = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is this an aux command or a full command? */
|
||||||
|
if ((val & 0xf0) == 0) {
|
||||||
|
aux_cmd = val & 0x0f;
|
||||||
|
id_status &= ~(ID_STAT_CB);
|
||||||
|
|
||||||
|
if (aux_cmd & ID_AUX_CLCE) {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x] \tCOMMAND\t%02x\tAUX:CLCE\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
id_status &= ~(ID_STAT_CEL|ID_STAT_CEH);
|
||||||
|
sim_cancel(id_sel_unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aux_cmd & ID_AUX_HSRQ) {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x] \tCOMMAND\t%02x\tAUX:HSRQ\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
id_status &= ~ID_STAT_SRQ;
|
||||||
|
sim_cancel(id_sel_unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aux_cmd & ID_AUX_CLB) {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tAUX:CLBUF\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
id_clear_fifo();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aux_cmd & ID_AUX_RST) {
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tAUX:RESET\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
sim_cancel(id_sel_unit);
|
||||||
|
id_clear_fifo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Just return early */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now that we know it's not an aux command, get the unit number
|
||||||
|
this command is for */
|
||||||
|
id_sel_unit = &id_unit[UNIT_NUM];
|
||||||
|
|
||||||
|
cmd = (id_cmd >> 4) & 0xf;
|
||||||
|
|
||||||
|
/* If this command is anything BUT a sense interrupt status, set
|
||||||
|
* the seek flag to false.
|
||||||
|
*/
|
||||||
|
if (cmd != ID_CMD_SIS) {
|
||||||
|
id_seek_sis = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
id_status = ID_STAT_CB;
|
||||||
|
|
||||||
|
switch(cmd) {
|
||||||
|
case ID_CMD_SIS:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tSense Int. Status - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_activate(DELAY_US(ID_SIS_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_SPEC:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tSpecify - %d - ETN=%02x ESN=%02x\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM, id_data[3], id_data[4]);
|
||||||
|
id_dtlh[UNIT_NUM] = id_data[1];
|
||||||
|
id_etn = id_data[3];
|
||||||
|
id_esn = id_data[4];
|
||||||
|
id_activate(DELAY_US(ID_SPEC_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_SUS:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tSense Unit Status - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_activate(DELAY_US(ID_SUS_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_DERR:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tDetect Error - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_status |= ID_STAT_CEH;
|
||||||
|
id_activate(DELAY_US(ID_CMD_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_RECAL:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tRecalibrate - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_cyl[UNIT_NUM] = 0;
|
||||||
|
time = id_cyl[UNIT_NUM];
|
||||||
|
id_activate(DELAY_US(ID_RECAL_WAIT + (time * ID_SEEK_WAIT)));
|
||||||
|
id_seek_sis = TRUE;
|
||||||
|
break;
|
||||||
|
case ID_CMD_SEEK:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tSeek - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_lcnh = id_data[0];
|
||||||
|
id_lcnl = id_data[1];
|
||||||
|
cyl = id_lcnh << 8 | id_lcnl;
|
||||||
|
time = (uint32) abs(id_cyl[UNIT_NUM] - cyl);
|
||||||
|
id_activate(DELAY_US(ID_SEEK_BASE + (ID_SEEK_WAIT * time)));
|
||||||
|
id_cyl[UNIT_NUM] = cyl;
|
||||||
|
id_seek_sis = TRUE;
|
||||||
|
break;
|
||||||
|
case ID_CMD_FMT:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tFormat - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
|
||||||
|
id_phn = id_data[0];
|
||||||
|
id_scnt = id_data[1];
|
||||||
|
pattern = id_data[2];
|
||||||
|
|
||||||
|
/* Format scnt sectors with the given pattern, if attached */
|
||||||
|
if (id_sel_unit->flags & UNIT_ATT) {
|
||||||
|
/* Formatting soft-sectored disks always begins at sector 0 */
|
||||||
|
sec = 0;
|
||||||
|
|
||||||
|
while (id_scnt-- > 0) {
|
||||||
|
/* Write one sector of pattern */
|
||||||
|
for (id_buf_ptr = 0; id_buf_ptr < ID_SEC_SIZE; id_buf_ptr++) {
|
||||||
|
id_buf[id_buf_ptr] = pattern;
|
||||||
|
}
|
||||||
|
lba = id_lba(id_cyl[UNIT_NUM], id_phn, sec++);
|
||||||
|
if (sim_disk_wrsect(id_sel_unit, lba, id_buf, NULL, 1) == SCPE_OK) {
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
"[%08x]\tFORMAT: PHN=%d SCNT=%d PAT=%02x LBA=%04x\n",
|
||||||
|
R[NUM_PC], id_phn, id_scnt, pattern, lba);
|
||||||
|
} else {
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
"[%08x]\tFORMAT FAILED! PHN=%d SCNT=%d PAT=%02x LBA=%04x\n",
|
||||||
|
R[NUM_PC], id_phn, id_scnt, pattern, lba);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
id_data[0] = 0;
|
||||||
|
} else {
|
||||||
|
/* Not attached */
|
||||||
|
id_data[0] = ID_EST_NR;
|
||||||
|
}
|
||||||
|
|
||||||
|
id_data[1] = id_scnt;
|
||||||
|
|
||||||
|
id_activate(DELAY_US(ID_CMD_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_VID:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tVerify ID - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_data[0] = 0;
|
||||||
|
id_data[1] = 0x05; /* What do we put here? */
|
||||||
|
id_activate(DELAY_US(ID_CMD_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_RID:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tRead ID - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
if (id_sel_unit->flags & UNIT_ATT) {
|
||||||
|
id_drq = TRUE;
|
||||||
|
|
||||||
|
/* Grab our arguments */
|
||||||
|
id_phn = id_data[0];
|
||||||
|
id_scnt = id_data[1];
|
||||||
|
|
||||||
|
/* Compute logical values used by ID verification */
|
||||||
|
id_lhn = id_phn;
|
||||||
|
id_lsn = 0;
|
||||||
|
} else {
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
"[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ ID.\n",
|
||||||
|
R[NUM_PC], UNIT_NUM);
|
||||||
|
}
|
||||||
|
id_activate(DELAY_US(ID_CMD_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_RDIAG:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tRead Diag - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_activate(DELAY_US(ID_CMD_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_RDATA:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tRead Data - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
if (id_sel_unit->flags & UNIT_ATT) {
|
||||||
|
id_drq = TRUE;
|
||||||
|
id_buf_ptr = 0;
|
||||||
|
|
||||||
|
/* Grab our arguments */
|
||||||
|
id_phn = id_data[0];
|
||||||
|
id_lcnh = ~(id_data[1]);
|
||||||
|
id_lcnl = id_data[2];
|
||||||
|
id_lhn = id_data[3];
|
||||||
|
id_lsn = id_data[4];
|
||||||
|
id_scnt = id_data[5];
|
||||||
|
} else {
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
"[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ DATA.\n",
|
||||||
|
R[NUM_PC], UNIT_NUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
time = (uint32) abs(id_cyl[UNIT_NUM] - ((id_lcnh<<8)|id_lcnl));
|
||||||
|
if (time == 0) {
|
||||||
|
time++;
|
||||||
|
}
|
||||||
|
time = time * ID_SEEK_WAIT;
|
||||||
|
id_activate(DELAY_US(time + ID_RW_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_CHECK:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tCheck - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_activate(DELAY_US(ID_CMD_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_SCAN:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tScan - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_activate(DELAY_US(ID_CMD_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_VDATA:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tVerify Data - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
id_activate(DELAY_US(ID_CMD_WAIT));
|
||||||
|
break;
|
||||||
|
case ID_CMD_WDATA:
|
||||||
|
sim_debug(WRITE_MSG, &id_dev,
|
||||||
|
"[%08x]\tCOMMAND\t%02x\tWrite Data - %d\n",
|
||||||
|
R[NUM_PC], val, UNIT_NUM);
|
||||||
|
if (id_sel_unit->flags & UNIT_ATT) {
|
||||||
|
id_drq = TRUE;
|
||||||
|
id_buf_ptr = 0;
|
||||||
|
|
||||||
|
/* Grab our arguments */
|
||||||
|
id_phn = id_data[0];
|
||||||
|
id_lcnh = ~(id_data[1]);
|
||||||
|
id_lcnl = id_data[2];
|
||||||
|
id_lhn = id_data[3];
|
||||||
|
id_lsn = id_data[4];
|
||||||
|
id_scnt = id_data[5];
|
||||||
|
} else {
|
||||||
|
sim_debug(EXECUTE_MSG, &id_dev,
|
||||||
|
"[%08x]\tUNIT %d NOT ATTACHED, CANNOT WRITE.\n",
|
||||||
|
R[NUM_PC], UNIT_NUM);
|
||||||
|
}
|
||||||
|
time = (uint32) abs(id_cyl[UNIT_NUM] - ((id_lcnh<<8)|id_lcnl));
|
||||||
|
if (time == 0) {
|
||||||
|
time++;
|
||||||
|
}
|
||||||
|
time = time * ID_SEEK_WAIT;
|
||||||
|
id_activate(DELAY_US(time + ID_RW_WAIT));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void id_drq_handled()
|
||||||
|
{
|
||||||
|
id_status &= ~ID_STAT_DRQ;
|
||||||
|
id_drq = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONST char *id_description(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
return "72MB MFM Hard Disk";
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat id_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
||||||
|
{
|
||||||
|
fprintf(st, "71MB MFM Integrated Hard Disk (ID)\n\n");
|
||||||
|
fprintf(st,
|
||||||
|
"The ID controller implements the integrated MFM hard disk controller\n"
|
||||||
|
"of the 3B2/400. Up to four drives are supported on a single controller.\n");
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
146
3B2/3b2_id.h
Normal file
146
3B2/3b2_id.h
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 Hard Disk (uPD7261) Header
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __3B2_ID_H__
|
||||||
|
#define __3B2_ID_H__
|
||||||
|
|
||||||
|
#include "3b2_defs.h"
|
||||||
|
#include "3b2_sysdev.h"
|
||||||
|
#include "sim_disk.h"
|
||||||
|
|
||||||
|
/* Command Codes (bits 3-7 of command byte) */
|
||||||
|
|
||||||
|
#define ID_DATA_REG 0
|
||||||
|
#define ID_CMD_STAT_REG 1
|
||||||
|
|
||||||
|
#define ID_CMD_AUX 0x00 /* Auxiliary Command */
|
||||||
|
#define ID_CMD_SIS 0x01 /* Sense int. status */
|
||||||
|
#define ID_CMD_SPEC 0x02 /* Specify */
|
||||||
|
#define ID_CMD_SUS 0x03 /* Sense unit status */
|
||||||
|
#define ID_CMD_DERR 0x04 /* Detect Error */
|
||||||
|
#define ID_CMD_RECAL 0x05 /* Recalibrate */
|
||||||
|
#define ID_CMD_SEEK 0x06 /* Seek */
|
||||||
|
#define ID_CMD_FMT 0x07 /* Format */
|
||||||
|
#define ID_CMD_VID 0x08 /* Verify ID */
|
||||||
|
#define ID_CMD_RID 0x09 /* Read ID */
|
||||||
|
#define ID_CMD_RDIAG 0x0A /* Read Diagnostic */
|
||||||
|
#define ID_CMD_RDATA 0x0B /* Read Data */
|
||||||
|
#define ID_CMD_CHECK 0x0C /* Check */
|
||||||
|
#define ID_CMD_SCAN 0x0D /* Scan */
|
||||||
|
#define ID_CMD_VDATA 0x0E /* Verify Data */
|
||||||
|
#define ID_CMD_WDATA 0x0F /* Write Data */
|
||||||
|
|
||||||
|
#define ID_AUX_RST 0x01
|
||||||
|
#define ID_AUX_CLB 0x02
|
||||||
|
#define ID_AUX_HSRQ 0x04
|
||||||
|
#define ID_AUX_CLCE 0x08
|
||||||
|
|
||||||
|
#define ID_STAT_DRQ 0x01
|
||||||
|
#define ID_STAT_NCI 0x02
|
||||||
|
#define ID_STAT_IER 0x04
|
||||||
|
#define ID_STAT_RRQ 0x08
|
||||||
|
#define ID_STAT_SRQ 0x10
|
||||||
|
#define ID_STAT_CEL 0x20
|
||||||
|
#define ID_STAT_CEH 0x40
|
||||||
|
#define ID_STAT_CB 0x80
|
||||||
|
|
||||||
|
#define ID_IST_SEN 0x80
|
||||||
|
#define ID_IST_RC 0x40
|
||||||
|
#define ID_IST_SER 0x20
|
||||||
|
#define ID_IST_EQC 0x10
|
||||||
|
#define ID_IST_NR 0x08
|
||||||
|
|
||||||
|
#define ID_UST_DSEL 0x10
|
||||||
|
#define ID_UST_SCL 0x08
|
||||||
|
#define ID_UST_TK0 0x04
|
||||||
|
#define ID_UST_RDY 0x02
|
||||||
|
#define ID_UST_WFL 0x01
|
||||||
|
|
||||||
|
#define ID_EST_ENC 0x80
|
||||||
|
#define ID_EST_OVR 0x40
|
||||||
|
#define ID_EST_DER 0x20
|
||||||
|
#define ID_EST_EQC 0x10
|
||||||
|
#define ID_EST_NR 0x08
|
||||||
|
#define ID_EST_ND 0x04
|
||||||
|
#define ID_EST_NWR 0x02
|
||||||
|
#define ID_EST_MAM 0x01
|
||||||
|
|
||||||
|
#define ID_DTLH_POLL 0x10
|
||||||
|
|
||||||
|
/* Geometry */
|
||||||
|
|
||||||
|
#define ID_CYL 925
|
||||||
|
#define ID_SEC_SIZE 512 /* Bytes per sector */
|
||||||
|
#define ID_SEC_CNT 18 /* Sectors per track */
|
||||||
|
#define ID_HEADS 9
|
||||||
|
#define ID_CYL_SIZE 512 * 18
|
||||||
|
|
||||||
|
/* Unit, Register, Device descriptions */
|
||||||
|
|
||||||
|
#define ID_FIFO_LEN 8
|
||||||
|
#define ID_IDFIELD_LEN 4
|
||||||
|
|
||||||
|
#define ID_NUM_UNITS 2
|
||||||
|
|
||||||
|
/* Unit number field in UNIT structure */
|
||||||
|
#define ID_UNIT_NUM u3
|
||||||
|
|
||||||
|
extern DEVICE id_dev;
|
||||||
|
extern DEBTAB sys_deb_tab[];
|
||||||
|
extern t_bool id_drq;
|
||||||
|
extern t_bool id_irq;
|
||||||
|
|
||||||
|
#define IDBASE 0x4a000
|
||||||
|
#define IDSIZE 0x2
|
||||||
|
|
||||||
|
/* Total disk size, in sectors */
|
||||||
|
#define ID_DSK_SIZE ID_CYL * ID_SEC_CNT * ID_HEADS
|
||||||
|
|
||||||
|
#define CMD_NUM ((id_cmd >> 4) & 0xf)
|
||||||
|
#define UNIT_NUM (id_cmd & 1) /* We intentionally ignore the top unit address bit */
|
||||||
|
|
||||||
|
/* Function prototypes */
|
||||||
|
|
||||||
|
t_stat id_svc(UNIT *uptr);
|
||||||
|
t_stat id_reset(DEVICE *dptr);
|
||||||
|
t_stat id_attach(UNIT *uptr, CONST char *cptr);
|
||||||
|
t_stat id_detach(UNIT *uptr);
|
||||||
|
uint32 id_read(uint32 pa, size_t size);
|
||||||
|
void id_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
CONST char *id_description(DEVICE *dptr);
|
||||||
|
t_stat id_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
|
||||||
|
void id_handle_data(uint8 val);
|
||||||
|
void id_handle_command(uint8 val);
|
||||||
|
|
||||||
|
static SIM_INLINE t_lba id_lba(uint16 cyl, uint8 head, uint8 sec);
|
||||||
|
|
||||||
|
void id_drq_handled();
|
||||||
|
|
||||||
|
#endif
|
526
3B2/3b2_if.c
Normal file
526
3B2/3b2_if.c
Normal file
|
@ -0,0 +1,526 @@
|
||||||
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 Floppy (TMS2797NL) Implementation
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "3b2_if.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Macros used for debugging timers. Remove when debugging is complete.
|
||||||
|
*/
|
||||||
|
double if_start_time;
|
||||||
|
|
||||||
|
#define IF_START_TIME() { if_start_time = sim_gtime(); }
|
||||||
|
#define IF_DIFF_MS() ((sim_gtime() - if_start_time) / INST_PER_MS)
|
||||||
|
#ifndef max
|
||||||
|
#define max(x,y) ((x) > (y) ? (x) : (y))
|
||||||
|
#endif
|
||||||
|
#ifndef min
|
||||||
|
#define min(x,y) ((x) < (y) ? (x) : (y))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disk Format:
|
||||||
|
* ------------
|
||||||
|
*
|
||||||
|
* - 80 Tracks
|
||||||
|
* - 9 Sectors per track
|
||||||
|
* - 2 heads
|
||||||
|
* - 512 bytes per sector
|
||||||
|
*
|
||||||
|
* 80 * 9 * 2 * 512 = 720KB
|
||||||
|
*
|
||||||
|
* The clock on pin 24 runs at 1.000 MHz, meaning that each
|
||||||
|
* step is 6ms and head settling time is 30ms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define IF_STEP_DELAY 6 /* ms */
|
||||||
|
#define IF_R_DELAY 85 /* ms */
|
||||||
|
#define IF_W_DELAY 90 /* ms */
|
||||||
|
#define IF_VERIFY_DELAY 30 /* ms */
|
||||||
|
#define IF_HLD_DELAY 80 /* ms */
|
||||||
|
#define IF_HSW_DELAY 60 /* ms */
|
||||||
|
|
||||||
|
UNIT if_unit = {
|
||||||
|
UDATA (&if_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
|
||||||
|
UNIT_MUSTBUF+UNIT_BINK, IF_DSK_SIZE)
|
||||||
|
};
|
||||||
|
|
||||||
|
REG if_reg[] = {
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE if_dev = {
|
||||||
|
"IF", &if_unit, if_reg, NULL,
|
||||||
|
1, 16, 8, 1, 16, 8,
|
||||||
|
NULL, NULL, &if_reset,
|
||||||
|
NULL, NULL, NULL, NULL,
|
||||||
|
DEV_DEBUG, 0, sys_deb_tab
|
||||||
|
};
|
||||||
|
|
||||||
|
IF_STATE if_state;
|
||||||
|
uint32 if_sec_ptr;
|
||||||
|
t_bool if_irq;
|
||||||
|
|
||||||
|
/* Function implementation */
|
||||||
|
|
||||||
|
static SIM_INLINE void if_set_irq()
|
||||||
|
{
|
||||||
|
if_irq = TRUE;
|
||||||
|
csr_data |= CSRDISK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void if_clear_irq()
|
||||||
|
{
|
||||||
|
if_irq = FALSE;
|
||||||
|
csr_data &= ~CSRDISK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void if_activate(uint32 delay)
|
||||||
|
{
|
||||||
|
IF_START_TIME();
|
||||||
|
sim_activate_abs(&if_unit, (int32) DELAY_MS(delay));
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void if_cancel_pending_irq()
|
||||||
|
{
|
||||||
|
sim_cancel(&if_unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat if_svc(UNIT *uptr)
|
||||||
|
{
|
||||||
|
if_state.status &= ~(IF_BUSY);
|
||||||
|
|
||||||
|
switch(if_state.cmd & 0xf0) {
|
||||||
|
case IF_RESTORE:
|
||||||
|
if_state.status = (IF_TK_0|IF_HEAD_LOADED);
|
||||||
|
break;
|
||||||
|
case IF_SEEK:
|
||||||
|
if_state.status = IF_HEAD_LOADED;
|
||||||
|
if (if_state.track == 0) {
|
||||||
|
if_state.status |= IF_TK_0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if_state.cmd = 0;
|
||||||
|
|
||||||
|
/* Request an interrupt */
|
||||||
|
sim_debug(IRQ_MSG, &if_dev, "\tINTR\t\tDELTA=%f ms\n", IF_DIFF_MS());
|
||||||
|
if_set_irq();
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat if_reset(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
if_state.status = IF_TK_0;
|
||||||
|
if_state.track = 0;
|
||||||
|
if_state.sector = 1;
|
||||||
|
if_sec_ptr = 0;
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 if_read(uint32 pa, size_t size) {
|
||||||
|
uint8 reg, data;
|
||||||
|
uint32 pos, pc;
|
||||||
|
UNIT *uptr;
|
||||||
|
uint8 *fbuf;
|
||||||
|
|
||||||
|
uptr = &(if_dev.units[0]);
|
||||||
|
reg = (uint8)(pa - IFBASE);
|
||||||
|
pc = R[NUM_PC];
|
||||||
|
fbuf = (uint8 *)uptr->filebuf;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case IF_STATUS_REG:
|
||||||
|
data = if_state.status;
|
||||||
|
/* If there's no image attached, we're not ready */
|
||||||
|
if ((uptr->flags & (UNIT_ATT|UNIT_BUF)) == 0) {
|
||||||
|
data |= IF_NRDY;
|
||||||
|
}
|
||||||
|
/* Reading the status register always de-asserts the IRQ line */
|
||||||
|
if_clear_irq();
|
||||||
|
sim_debug(READ_MSG, &if_dev, "\tSTATUS\t%02x\n", data);
|
||||||
|
break;
|
||||||
|
case IF_TRACK_REG:
|
||||||
|
data = if_state.track;
|
||||||
|
sim_debug(READ_MSG, &if_dev, "\tTRACK\t%02x\n", data);
|
||||||
|
break;
|
||||||
|
case IF_SECTOR_REG:
|
||||||
|
data = if_state.sector;
|
||||||
|
sim_debug(READ_MSG, &if_dev, "\tSECTOR\t%02x\n", data);
|
||||||
|
break;
|
||||||
|
case IF_DATA_REG:
|
||||||
|
if_state.status &= ~IF_DRQ;
|
||||||
|
|
||||||
|
if (((uptr->flags & (UNIT_ATT|UNIT_BUF)) == 0) ||
|
||||||
|
((if_state.cmd & 0xf0) != IF_READ_SEC &&
|
||||||
|
(if_state.cmd & 0xf0) != IF_READ_SEC_M)) {
|
||||||
|
/* Not attached, or not a read command */
|
||||||
|
|
||||||
|
switch (if_state.cmd & 0xf0) {
|
||||||
|
case IF_READ_ADDR:
|
||||||
|
/* Special state machine. */
|
||||||
|
switch (if_state.read_addr_ptr++) {
|
||||||
|
case 0:
|
||||||
|
if_state.data = if_state.track;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if_state.data = if_state.side;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if_state.data = if_state.sector;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if_state.data = 2; /* 512 byte */
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
/* TODO: Checksum */
|
||||||
|
if_state.data = 0;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
/* TODO: Checksum */
|
||||||
|
if_state.data = 0;
|
||||||
|
if_state.read_addr_ptr = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sim_debug(READ_MSG, &if_dev, "\tDATA\t%02x\n", if_state.data);
|
||||||
|
return if_state.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = if_buf_offset();
|
||||||
|
data = fbuf[pos + if_sec_ptr++];
|
||||||
|
sim_debug(READ_MSG, &if_dev, "\tDATA\t%02x\n", data);
|
||||||
|
|
||||||
|
if (if_sec_ptr >= IF_SECTOR_SIZE) {
|
||||||
|
if_sec_ptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data = 0xffu; // Compiler warning
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle the most recently received command */
|
||||||
|
void if_handle_command()
|
||||||
|
{
|
||||||
|
uint32 delay_ms = 0;
|
||||||
|
uint32 head_switch_delay = 0;
|
||||||
|
uint32 head_load_delay = 0;
|
||||||
|
|
||||||
|
if_sec_ptr = 0;
|
||||||
|
|
||||||
|
/* We're starting a new command. */
|
||||||
|
if_state.status = IF_BUSY;
|
||||||
|
|
||||||
|
/* Clear read addr state */
|
||||||
|
if_state.read_addr_ptr = 0;
|
||||||
|
|
||||||
|
switch(if_state.cmd & 0xf0) {
|
||||||
|
case IF_RESTORE:
|
||||||
|
case IF_SEEK:
|
||||||
|
case IF_STEP:
|
||||||
|
case IF_STEP_T:
|
||||||
|
case IF_STEP_IN:
|
||||||
|
case IF_STEP_IN_T:
|
||||||
|
case IF_STEP_OUT:
|
||||||
|
case IF_STEP_OUT_T:
|
||||||
|
if_state.cmd_type = 1;
|
||||||
|
if (if_state.cmd & IF_H_FLAG) {
|
||||||
|
head_load_delay = IF_HLD_DELAY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IF_READ_SEC:
|
||||||
|
case IF_READ_SEC_M:
|
||||||
|
case IF_WRITE_SEC:
|
||||||
|
case IF_WRITE_SEC_M:
|
||||||
|
if_state.cmd_type = 2;
|
||||||
|
if (((if_state.cmd & IF_U_FLAG) >> 1) != if_state.side) {
|
||||||
|
head_switch_delay = IF_HSW_DELAY;
|
||||||
|
if_state.side = (if_state.cmd & IF_U_FLAG) >> 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IF_READ_ADDR:
|
||||||
|
case IF_READ_TRACK:
|
||||||
|
case IF_WRITE_TRACK:
|
||||||
|
if_state.cmd_type = 3;
|
||||||
|
if (((if_state.cmd & IF_U_FLAG) >> 1) != if_state.side) {
|
||||||
|
head_switch_delay = IF_HSW_DELAY;
|
||||||
|
if_state.side = (if_state.cmd & IF_U_FLAG) >> 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IF_FORCE_INT:
|
||||||
|
if_state.cmd_type = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(if_state.cmd & 0xf0) {
|
||||||
|
case IF_RESTORE:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRestore\n", if_state.cmd);
|
||||||
|
|
||||||
|
/* Reset HLT */
|
||||||
|
if_state.status &= ~IF_HEAD_LOADED;
|
||||||
|
|
||||||
|
/* If head should be loaded immediately, do so now */
|
||||||
|
if (if_state.cmd & IF_H_FLAG) {
|
||||||
|
if_state.status |= IF_HEAD_LOADED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (if_state.track == 0) {
|
||||||
|
if_state.status |= IF_TK_0;
|
||||||
|
if_state.track = 1; /* Kind of a gross hack */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (if_state.cmd & IF_V_FLAG) {
|
||||||
|
delay_ms = (IF_STEP_DELAY * if_state.track) + IF_VERIFY_DELAY;
|
||||||
|
} else {
|
||||||
|
delay_ms = IF_STEP_DELAY * if_state.track;
|
||||||
|
}
|
||||||
|
|
||||||
|
if_activate(delay_ms);
|
||||||
|
|
||||||
|
if_state.data = 0;
|
||||||
|
if_state.track = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IF_STEP:
|
||||||
|
case IF_STEP_T:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep\n", if_state.cmd);
|
||||||
|
if_activate(IF_STEP_DELAY);
|
||||||
|
if_state.track = (uint8) min(max((int) if_state.track + if_state.step_dir, 0), 0x4f);
|
||||||
|
break;
|
||||||
|
case IF_STEP_IN:
|
||||||
|
case IF_STEP_IN_T:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep In\n", if_state.cmd);
|
||||||
|
if_state.step_dir = IF_STEP_IN_DIR;
|
||||||
|
if_state.track = (uint8) max((int) if_state.track + if_state.step_dir, 0);
|
||||||
|
if_activate(IF_STEP_DELAY);
|
||||||
|
break;
|
||||||
|
case IF_STEP_OUT:
|
||||||
|
case IF_STEP_OUT_T:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep Out\n", if_state.cmd);
|
||||||
|
if_state.step_dir = IF_STEP_OUT_DIR;
|
||||||
|
if_state.track = (uint8) min((int) if_state.track + if_state.step_dir, 0x4f);
|
||||||
|
if_activate(IF_STEP_DELAY);
|
||||||
|
break;
|
||||||
|
case IF_SEEK:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tSeek\n", if_state.cmd);
|
||||||
|
|
||||||
|
/* Reset HLT */
|
||||||
|
if_state.status &= ~IF_HEAD_LOADED;
|
||||||
|
|
||||||
|
/* If head should be loaded immediately, do so now */
|
||||||
|
if (if_state.cmd & IF_H_FLAG) {
|
||||||
|
if_state.status |= IF_HEAD_LOADED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save the direction for stepping */
|
||||||
|
if (if_state.data > if_state.track) {
|
||||||
|
if_state.step_dir = IF_STEP_IN_DIR;
|
||||||
|
} else if (if_state.data < if_state.track) {
|
||||||
|
if_state.step_dir = IF_STEP_OUT_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The new track is in the data register */
|
||||||
|
|
||||||
|
if (if_state.data > IF_TRACK_COUNT-1) {
|
||||||
|
if_state.data = IF_TRACK_COUNT-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (if_state.data == 0) {
|
||||||
|
if_state.status |= IF_TK_0;
|
||||||
|
} else {
|
||||||
|
if_state.status &= ~(IF_TK_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_ms = (uint32) abs(if_state.data - if_state.track);
|
||||||
|
|
||||||
|
if (delay_ms == 0) {
|
||||||
|
delay_ms++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (if_state.cmd & IF_V_FLAG) {
|
||||||
|
if_activate((IF_STEP_DELAY * delay_ms) + IF_VERIFY_DELAY + head_load_delay);
|
||||||
|
} else {
|
||||||
|
if_activate((IF_STEP_DELAY * delay_ms) + head_load_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if_state.track = if_state.data;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IF_READ_SEC:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Sector %d/%d/%d\n",
|
||||||
|
if_state.cmd, if_state.track, if_state.side, if_state.sector);
|
||||||
|
/* We set DRQ right away to request the transfer. */
|
||||||
|
if_state.drq = TRUE;
|
||||||
|
if_state.status |= IF_DRQ;
|
||||||
|
if (if_state.cmd & IF_E_FLAG) {
|
||||||
|
if_activate(IF_R_DELAY + IF_VERIFY_DELAY + head_switch_delay);
|
||||||
|
} else {
|
||||||
|
if_activate(IF_R_DELAY + head_switch_delay);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IF_READ_SEC_M:
|
||||||
|
assert(0);
|
||||||
|
case IF_WRITE_SEC:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Sector %d/%d/%d\n",
|
||||||
|
if_state.cmd, if_state.track, if_state.side, if_state.sector);
|
||||||
|
/* We set DRQ right away to request the transfer. */
|
||||||
|
if_state.drq = TRUE;
|
||||||
|
if_state.status |= IF_DRQ;
|
||||||
|
if (if_state.cmd & IF_E_FLAG) {
|
||||||
|
if_activate(IF_W_DELAY + IF_VERIFY_DELAY + head_switch_delay);
|
||||||
|
} else {
|
||||||
|
if_activate(IF_W_DELAY + head_switch_delay);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IF_WRITE_SEC_M:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
case IF_READ_ADDR:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Address\n", if_state.cmd);
|
||||||
|
if_state.drq = TRUE;
|
||||||
|
if_state.status |= IF_DRQ;
|
||||||
|
if_activate(IF_R_DELAY);
|
||||||
|
break;
|
||||||
|
case IF_READ_TRACK:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Track\n", if_state.cmd);
|
||||||
|
assert(0); /* NOT YET IMPLEMENTED */
|
||||||
|
break;
|
||||||
|
case IF_WRITE_TRACK:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Track\n", if_state.cmd);
|
||||||
|
assert(0); /* NOT YET IMPLEMENTED */
|
||||||
|
break;
|
||||||
|
case IF_FORCE_INT:
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tForce Interrupt\n", if_state.cmd);
|
||||||
|
if_state.status = 0;
|
||||||
|
|
||||||
|
if (if_state.track == 0) {
|
||||||
|
if_state.status |= (IF_TK_0|IF_HEAD_LOADED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((if_state.cmd & 0xf) == 0) {
|
||||||
|
if_cancel_pending_irq();
|
||||||
|
if_clear_irq(); /* TODO: Confirm this is right */
|
||||||
|
} else if ((if_state.cmd & 0x8) == 0x8) {
|
||||||
|
if_state.status |= IF_DRQ;
|
||||||
|
if_set_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void if_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
UNIT *uptr;
|
||||||
|
uint8 reg;
|
||||||
|
uint32 pos;
|
||||||
|
uint8 *fbuf;
|
||||||
|
|
||||||
|
val = val & 0xff;
|
||||||
|
|
||||||
|
uptr = &(if_dev.units[0]);
|
||||||
|
reg = (uint8) (pa - IFBASE);
|
||||||
|
fbuf = (uint8 *)uptr->filebuf;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case IF_CMD_REG:
|
||||||
|
if_state.cmd = (uint8) val;
|
||||||
|
/* Writing to the command register always de-asserts the IRQ line */
|
||||||
|
if_clear_irq();
|
||||||
|
if_handle_command();
|
||||||
|
break;
|
||||||
|
case IF_TRACK_REG:
|
||||||
|
if_state.track = (uint8) val;
|
||||||
|
sim_debug(WRITE_MSG, &if_dev, "\tTRACK\t%02x\n", val);
|
||||||
|
break;
|
||||||
|
case IF_SECTOR_REG:
|
||||||
|
if_state.sector = (uint8) val;
|
||||||
|
sim_debug(WRITE_MSG, &if_dev, "\tSECTOR\t%02x\n", val);
|
||||||
|
break;
|
||||||
|
case IF_DATA_REG:
|
||||||
|
if_state.data = (uint8) val;
|
||||||
|
|
||||||
|
sim_debug(WRITE_MSG, &if_dev, "\tDATA\t%02x\n", val);
|
||||||
|
|
||||||
|
if (uptr->fileref == NULL ||
|
||||||
|
((if_state.cmd & 0xf0) != IF_WRITE_SEC &&
|
||||||
|
(if_state.cmd & 0xf0) != IF_WRITE_SEC_M)) {
|
||||||
|
/* Not attached, or not a write command */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the right offset, and update the value. */
|
||||||
|
pos = if_buf_offset();
|
||||||
|
fbuf[pos + if_sec_ptr++] = (uint8) val;
|
||||||
|
|
||||||
|
if (if_sec_ptr >= IF_SECTOR_SIZE) {
|
||||||
|
if_sec_ptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the offset of the currently selected C/H/S
|
||||||
|
*/
|
||||||
|
SIM_INLINE uint32 if_buf_offset()
|
||||||
|
{
|
||||||
|
uint32 pos;
|
||||||
|
|
||||||
|
pos = IF_TRACK_SIZE * if_state.track * 2;
|
||||||
|
|
||||||
|
if (if_state.side == 1) {
|
||||||
|
pos += IF_TRACK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += IF_SECTOR_SIZE * (if_state.sector - 1);
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void if_drq_handled()
|
||||||
|
{
|
||||||
|
if_state.status &= ~IF_DRQ;
|
||||||
|
}
|
138
3B2/3b2_if.h
Normal file
138
3B2/3b2_if.h
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 Floppy (TMS2797NL) Header
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __3B2_IF_H__
|
||||||
|
#define __3B2_IF_H__
|
||||||
|
|
||||||
|
#include "3b2_defs.h"
|
||||||
|
#include "3b2_sysdev.h"
|
||||||
|
#include "3b2_sys.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8 data;
|
||||||
|
uint8 cmd;
|
||||||
|
uint8 cmd_type;
|
||||||
|
uint8 status;
|
||||||
|
uint8 track;
|
||||||
|
uint8 sector;
|
||||||
|
uint8 side;
|
||||||
|
uint8 read_addr_ptr;
|
||||||
|
int8 step_dir;
|
||||||
|
t_bool drq;
|
||||||
|
} IF_STATE;
|
||||||
|
|
||||||
|
|
||||||
|
extern DEVICE if_dev;
|
||||||
|
extern DEBTAB sys_deb_tab[];
|
||||||
|
extern IF_STATE if_state;
|
||||||
|
extern t_bool if_irq;
|
||||||
|
|
||||||
|
#define IFBASE 0x4d000
|
||||||
|
#define IFSIZE 0x10
|
||||||
|
|
||||||
|
#define IF_STATUS_REG 0
|
||||||
|
#define IF_CMD_REG 0
|
||||||
|
#define IF_TRACK_REG 1
|
||||||
|
#define IF_SECTOR_REG 2
|
||||||
|
#define IF_DATA_REG 3
|
||||||
|
|
||||||
|
/* Status Bits */
|
||||||
|
#define IF_BUSY 0x01
|
||||||
|
#define IF_DRQ 0x02
|
||||||
|
#define IF_INDEX 0x02
|
||||||
|
#define IF_TK_0 0x04
|
||||||
|
#define IF_LOST_DATA 0x04
|
||||||
|
#define IF_CRC_ERR 0x08
|
||||||
|
#define IF_SEEK_ERR 0x10
|
||||||
|
#define IF_RNF 0x10
|
||||||
|
#define IF_HEAD_LOADED 0x20
|
||||||
|
#define IF_RECORD_TYPE 0x20
|
||||||
|
#define IF_WP 0x40
|
||||||
|
#define IF_NRDY 0x80
|
||||||
|
|
||||||
|
/* Type I Commands */
|
||||||
|
#define IF_RESTORE 0x00
|
||||||
|
#define IF_SEEK 0x10
|
||||||
|
#define IF_STEP 0x20
|
||||||
|
#define IF_STEP_T 0x30
|
||||||
|
#define IF_STEP_IN 0x40
|
||||||
|
#define IF_STEP_IN_T 0x50
|
||||||
|
#define IF_STEP_OUT 0x60
|
||||||
|
#define IF_STEP_OUT_T 0x70
|
||||||
|
|
||||||
|
/* Type II Commands */
|
||||||
|
#define IF_READ_SEC 0x80
|
||||||
|
#define IF_READ_SEC_M 0x90
|
||||||
|
#define IF_WRITE_SEC 0xA0
|
||||||
|
#define IF_WRITE_SEC_M 0xB0
|
||||||
|
|
||||||
|
/* Type III Commands */
|
||||||
|
#define IF_READ_ADDR 0xC0
|
||||||
|
#define IF_READ_TRACK 0xE0
|
||||||
|
#define IF_WRITE_TRACK 0xF0
|
||||||
|
|
||||||
|
/* Type IV Command */
|
||||||
|
#define IF_FORCE_INT 0xD0
|
||||||
|
|
||||||
|
/* Command flags */
|
||||||
|
|
||||||
|
#define IF_C_FLAG 0x02
|
||||||
|
#define IF_V_FLAG 0x04
|
||||||
|
#define IF_E_FLAG 0x04
|
||||||
|
#define IF_U_FLAG 0x02
|
||||||
|
#define IF_H_FLAG 0x08
|
||||||
|
#define IF_S_FLAG 0x10
|
||||||
|
|
||||||
|
/* Constants */
|
||||||
|
|
||||||
|
#define IF_SIDES 2
|
||||||
|
#define IF_TRACK_SIZE 4608
|
||||||
|
#define IF_SECTOR_SIZE 512
|
||||||
|
#define IF_TRACK_COUNT 80
|
||||||
|
|
||||||
|
#define IF_STEP_IN_DIR 1
|
||||||
|
#define IF_STEP_OUT_DIR -1
|
||||||
|
|
||||||
|
#define IF_DSK_SIZE (IF_SIDES * IF_TRACK_SIZE * IF_TRACK_COUNT)
|
||||||
|
|
||||||
|
/* Function prototypes */
|
||||||
|
|
||||||
|
static SIM_INLINE void if_set_irq();
|
||||||
|
static SIM_INLINE void if_clear_irq();
|
||||||
|
static SIM_INLINE void if_cancel_pending_irq();
|
||||||
|
t_stat if_svc(UNIT *uptr);
|
||||||
|
t_stat if_reset(DEVICE *dptr);
|
||||||
|
uint32 if_read(uint32 pa, size_t size);
|
||||||
|
void if_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
void if_drq_handled();
|
||||||
|
void if_handle_command();
|
||||||
|
uint32 if_buf_offset();
|
||||||
|
|
||||||
|
#endif
|
135
3B2/3b2_io.c
Normal file
135
3B2/3b2_io.c
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 IO dispatch implemenation
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "3b2_io.h"
|
||||||
|
|
||||||
|
struct iolink iotable[] = {
|
||||||
|
{ MMUBASE, MMUBASE+MMUSIZE, &mmu_read, &mmu_write },
|
||||||
|
{ IFBASE, IFBASE+IFSIZE, &if_read, &if_write },
|
||||||
|
{ IDBASE, IDBASE+IDSIZE, &id_read, &id_write },
|
||||||
|
{ TIMERBASE, TIMERBASE+TIMERSIZE, &timer_read, &timer_write },
|
||||||
|
{ NVRAMBASE, NVRAMBASE+NVRAMSIZE, &nvram_read, &nvram_write },
|
||||||
|
{ CSRBASE, CSRBASE+CSRSIZE, &csr_read, &csr_write },
|
||||||
|
{ IUBASE, IUBASE+IUSIZE, &iu_read, &iu_write },
|
||||||
|
{ DMAIDBASE, DMAIDBASE+DMAIDSIZE, &dmac_read, &dmac_write },
|
||||||
|
{ DMAIUABASE, DMAIUABASE+DMAIUASIZE, &dmac_read, &dmac_write },
|
||||||
|
{ DMAIUBBASE, DMAIUBBASE+DMAIUBSIZE, &dmac_read, &dmac_write },
|
||||||
|
{ DMACBASE, DMACBASE+DMACSIZE, &dmac_read, &dmac_write },
|
||||||
|
{ DMAIFBASE, DMAIFBASE+DMAIFSIZE, &dmac_read, &dmac_write },
|
||||||
|
{ TODBASE, TODBASE+TODSIZE, &tod_read, &tod_write },
|
||||||
|
{ 0, 0, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32 io_read(uint32 pa, size_t size)
|
||||||
|
{
|
||||||
|
struct iolink *p;
|
||||||
|
|
||||||
|
/* Special devices */
|
||||||
|
if (pa == 0x4c003) {
|
||||||
|
/* MEMSIZE register */
|
||||||
|
|
||||||
|
/* It appears that the following values map to memory sizes:
|
||||||
|
0x00: 512KB ( 524,288 B)
|
||||||
|
0x01: 2MB (2,097,152 B)
|
||||||
|
0x02: 1MB (1,048,576 B)
|
||||||
|
0x03: 4MB (4,194,304 B)
|
||||||
|
*/
|
||||||
|
switch(MEM_SIZE) {
|
||||||
|
case 0x80000: /* 512KB */
|
||||||
|
return 0;
|
||||||
|
case 0x100000: /* 1MB */
|
||||||
|
return 2;
|
||||||
|
case 0x200000: /* 2MB */
|
||||||
|
return 1;
|
||||||
|
case 0x400000: /* 4MB */
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IO Board Area - Unimplemented */
|
||||||
|
if (pa >= 0x200000 && pa < 0x2000000) {
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev, "[%08x] [IO BOARD READ] ADDR=%08x\n", R[NUM_PC], pa);
|
||||||
|
/* When we implement boards, register them here
|
||||||
|
N.B.: High byte of board ID is read at 0xnnnnn0,
|
||||||
|
low byte at 0xnnnnn1 */
|
||||||
|
|
||||||
|
/* Since we have no cards in our system, there's nothing
|
||||||
|
to read. We indicate that our bus read timed out with
|
||||||
|
CSRTIMO, then abort.*/
|
||||||
|
csr_data |= CSRTIMO;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (p = &iotable[0]; p->low != 0; p++) {
|
||||||
|
if ((pa >= p->low) && (pa < p->high) && p->read) {
|
||||||
|
return p->read(pa, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not found. */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[%08x] [io_read] ADDR=%08x: No device found.\n",
|
||||||
|
R[NUM_PC], pa);
|
||||||
|
csr_data |= CSRTIMO;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void io_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
struct iolink *p;
|
||||||
|
|
||||||
|
/* IO Board Area - Unimplemented */
|
||||||
|
if (pa >= 0x200000 && pa < 0x2000000) {
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[%08x] ADDR=%08x, DATA=%08x\n",
|
||||||
|
R[NUM_PC], pa, val);
|
||||||
|
csr_data |= CSRTIMO;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (p = &iotable[0]; p->low != 0; p++) {
|
||||||
|
if ((pa >= p->low) && (pa < p->high) && p->write) {
|
||||||
|
p->write(pa, val, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not found. */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[%08x] [io_write] ADDR=%08x: No device found.\n",
|
||||||
|
R[NUM_PC], pa);
|
||||||
|
csr_data |= CSRTIMO;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
}
|
48
3B2/3b2_io.h
Normal file
48
3B2/3b2_io.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 IO dispatch (Header)
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _3B2_IO_H_
|
||||||
|
#define _3B2_IO_H_
|
||||||
|
|
||||||
|
#include "3b2_sysdev.h"
|
||||||
|
#include "3b2_iu.h"
|
||||||
|
#include "3b2_if.h"
|
||||||
|
#include "3b2_id.h"
|
||||||
|
#include "3b2_dmac.h"
|
||||||
|
#include "3b2_mmu.h"
|
||||||
|
|
||||||
|
struct iolink {
|
||||||
|
uint32 low;
|
||||||
|
uint32 high;
|
||||||
|
uint32 (*read)(uint32 pa, size_t size);
|
||||||
|
void (*write)(uint32 pa, uint32 val, size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
546
3B2/3b2_iu.c
Normal file
546
3B2/3b2_iu.c
Normal file
|
@ -0,0 +1,546 @@
|
||||||
|
/* 3b2_iu.c: SCN2681A Dual UART Implementation
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "3b2_iu.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Registers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The IU state */
|
||||||
|
IU_STATE iu_state;
|
||||||
|
|
||||||
|
t_bool iu_increment_a = FALSE;
|
||||||
|
t_bool iu_increment_b = FALSE;
|
||||||
|
|
||||||
|
extern uint16 csr_data;
|
||||||
|
|
||||||
|
UNIT iu_unit[] = {
|
||||||
|
{ UDATA(&iu_svc_tti, UNIT_IDLE, 0), TMLN_SPD_9600_BPS },
|
||||||
|
{ UDATA(&iu_svc_tto, TT_MODE_8B, 0), SERIAL_OUT_WAIT },
|
||||||
|
{ UDATA(&iu_svc_timer, 0, 0) },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
BITFIELD sr_bits[] = {
|
||||||
|
BIT(RXRDY),
|
||||||
|
BIT(FFULL),
|
||||||
|
BIT(TXRDY),
|
||||||
|
BIT(TXEMT),
|
||||||
|
BIT(OVRN_E),
|
||||||
|
BIT(PRTY_E),
|
||||||
|
BIT(FRM_E),
|
||||||
|
BIT(BRK),
|
||||||
|
ENDBITS
|
||||||
|
};
|
||||||
|
|
||||||
|
BITFIELD isr_bits[] = {
|
||||||
|
BIT(TXRDYA),
|
||||||
|
BIT(RXRDY_FFA),
|
||||||
|
BIT(DLTA_BRKA),
|
||||||
|
BIT(CTR_RDY),
|
||||||
|
BIT(TXRDYB),
|
||||||
|
BIT(RXRDY_FFB),
|
||||||
|
BIT(DLTA_BRKB),
|
||||||
|
BIT(IPC),
|
||||||
|
ENDBITS
|
||||||
|
};
|
||||||
|
|
||||||
|
BITFIELD acr_bits[] = {
|
||||||
|
BIT(BRG_SET),
|
||||||
|
BITFFMT(TMR_MODE,3,%d),
|
||||||
|
BIT(DLTA_IP3),
|
||||||
|
BIT(DLTA_IP2),
|
||||||
|
BIT(DLTA_IP1),
|
||||||
|
BIT(DLTA_IP0),
|
||||||
|
ENDBITS
|
||||||
|
};
|
||||||
|
|
||||||
|
BITFIELD conf_bits[] = {
|
||||||
|
BIT(TX_EN),
|
||||||
|
BIT(RX_EN),
|
||||||
|
ENDBITS
|
||||||
|
};
|
||||||
|
|
||||||
|
REG iu_reg[] = {
|
||||||
|
{ HRDATADF(ISTAT, iu_state.istat, 8, "Interrupt Status", isr_bits) },
|
||||||
|
{ HRDATAD(IMR, iu_state.imr, 8, "Interrupt Mask") },
|
||||||
|
{ HRDATADF(ACR, iu_state.acr, 8, "Auxiliary Control Register", acr_bits) },
|
||||||
|
{ HRDATAD(CTR, iu_state.c_set, 16, "Counter Setting") },
|
||||||
|
{ HRDATAD(IP, iu_state.inprt, 8, "Input Port") },
|
||||||
|
{ HRDATADF(STAT_A, iu_state.port[0].stat, 8, "Status (Port A)", sr_bits) },
|
||||||
|
{ HRDATAD(DATA_A, iu_state.port[0].buf, 8, "Data (Port A)") },
|
||||||
|
{ HRDATADF(CONF_A, iu_state.port[0].conf, 8, "Config (Port A)", conf_bits) },
|
||||||
|
{ HRDATADF(STAT_B, iu_state.port[1].stat, 8, "Status (Port B)", sr_bits) },
|
||||||
|
{ HRDATAD(DATA_B, iu_state.port[1].buf, 8, "Data (Port B)") },
|
||||||
|
{ HRDATADF(CONF_B, iu_state.port[1].conf, 8, "Config (Port B)", conf_bits) },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE iu_dev = {
|
||||||
|
"IU", iu_unit, iu_reg, NULL,
|
||||||
|
3, 8, 32, 1, 8, 8,
|
||||||
|
NULL, NULL, &iu_reset,
|
||||||
|
NULL, NULL, NULL, NULL,
|
||||||
|
DEV_DEBUG, 0, sys_deb_tab
|
||||||
|
};
|
||||||
|
|
||||||
|
void increment_modep_a()
|
||||||
|
{
|
||||||
|
iu_increment_a = FALSE;
|
||||||
|
iu_state.port[PORT_A].modep++;
|
||||||
|
|
||||||
|
if (iu_state.port[PORT_A].modep > 1) {
|
||||||
|
iu_state.port[PORT_A].modep = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void increment_modep_b()
|
||||||
|
{
|
||||||
|
iu_increment_b = FALSE;
|
||||||
|
iu_state.port[PORT_B].modep++;
|
||||||
|
|
||||||
|
if (iu_state.port[PORT_B].modep > 1) {
|
||||||
|
iu_state.port[PORT_B].modep = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void iu_txrdy_irq(uint8 portno) {
|
||||||
|
uint8 irq_mask = (uint8) (1u << (portno * 4));
|
||||||
|
|
||||||
|
if ((iu_state.imr & irq_mask) &&
|
||||||
|
(iu_state.port[portno].conf & TX_EN) &&
|
||||||
|
(iu_state.port[portno].stat & STS_TXR)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &iu_dev,
|
||||||
|
"Firing IU TTY IRQ 13 ON TX/State Change\n");
|
||||||
|
csr_data |= CSRUART;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat iu_reset(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
uint8 portno;
|
||||||
|
|
||||||
|
memset(&iu_state, 0, sizeof(struct iu_state));
|
||||||
|
|
||||||
|
iu_state.opcr = 0;
|
||||||
|
|
||||||
|
if (!sim_is_active(&iu_unit[UNIT_CONSOLE_TTI])) {
|
||||||
|
iu_unit[UNIT_CONSOLE_TTI].wait = IU_TTY_DELAY;
|
||||||
|
sim_activate(&iu_unit[UNIT_CONSOLE_TTI],
|
||||||
|
iu_unit[UNIT_CONSOLE_TTI].wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (portno = 0; portno < 2; portno++) {
|
||||||
|
iu_state.port[portno].buf = 0;
|
||||||
|
iu_state.port[portno].modep = 0;
|
||||||
|
iu_state.port[portno].conf = 0;
|
||||||
|
iu_state.port[portno].stat = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat iu_svc_tti(UNIT *uptr)
|
||||||
|
{
|
||||||
|
int32 temp;
|
||||||
|
|
||||||
|
sim_clock_coschedule_tmr_abs(uptr, TMR_CLK, 2);
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
|
||||||
|
- If there has been a change on IP0-IP3, set the corresponding
|
||||||
|
bits in IPCR, if configured to do so. We'll need to figure out
|
||||||
|
how these are wired (DCD pin, etc?)
|
||||||
|
|
||||||
|
- Update the Output Port pins (which are logically inverted)
|
||||||
|
based on the contents of the OPR, OPCR, MR, and CR registers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((temp = sim_poll_kbd()) < SCPE_KFLAG) {
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iu_state.port[PORT_A].conf & RX_EN) {
|
||||||
|
iu_state.port[PORT_A].buf = (temp & 0xff);
|
||||||
|
iu_state.port[PORT_A].stat |= STS_RXR;
|
||||||
|
iu_state.istat |= ISTS_RAI;
|
||||||
|
if (iu_state.imr & 0x02) {
|
||||||
|
sim_debug(EXECUTE_MSG, &iu_dev,
|
||||||
|
"Firing IU TTY IRQ 13 ON RECEIVE (%c)\n",
|
||||||
|
(temp & 0xff));
|
||||||
|
csr_data |= CSRUART;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat iu_svc_tto(UNIT *uptr)
|
||||||
|
{
|
||||||
|
sim_debug(EXECUTE_MSG, &iu_dev,
|
||||||
|
"Calling iu_txrdy_irq on iu_svc_tto\n");
|
||||||
|
|
||||||
|
iu_txrdy_irq(PORT_A);
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat iu_svc_timer(UNIT *uptr)
|
||||||
|
{
|
||||||
|
iu_state.istat |= ISTS_CRI;
|
||||||
|
|
||||||
|
if (iu_state.imr & 0x08) {
|
||||||
|
csr_data |= CSRUART;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reg | Name (Read) | Name (Write)
|
||||||
|
* -----+-------------------------+----------------------------
|
||||||
|
* 0 | Mode Register 1/2 A | Mode Register 1/2 A
|
||||||
|
* 1 | Status Register A | Clock Select Register A
|
||||||
|
* 2 | BRG Test | Command Register A
|
||||||
|
* 3 | Rx Holding Register A | Tx Holding Register A
|
||||||
|
* 4 | Input Port Change Reg. | Aux. Control Register
|
||||||
|
* 5 | Interrupt Status Reg. | Interrupt Mask Register
|
||||||
|
* 6 | Counter/Timer Upper Val | C/T Upper Preset Val.
|
||||||
|
* 7 | Counter/Timer Lower Val | C/T Lower Preset Val.
|
||||||
|
* 8 | Mode Register B | Mode Register B
|
||||||
|
* 9 | Status Register B | Clock Select Register B
|
||||||
|
* 10 | 1X/16X Test | Command Register B
|
||||||
|
* 11 | Rx Holding Register B | Tx Holding Register B
|
||||||
|
* 12 | *Reserved* | *Reserved*
|
||||||
|
* 13 | Input Ports IP0 to IP6 | Output Port Conf. Reg.
|
||||||
|
* 14 | Start Counter Command | Set Output Port Bits Cmd.
|
||||||
|
* 15 | Stop Counter Command | Reset Output Port Bits Cmd.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
uint32 iu_read(uint32 pa, size_t size)
|
||||||
|
{
|
||||||
|
uint8 reg, modep;
|
||||||
|
uint32 data, delay;
|
||||||
|
|
||||||
|
reg = (uint8) (pa - IUBASE);
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case MR12A:
|
||||||
|
modep = iu_state.port[PORT_A].modep;
|
||||||
|
data = iu_state.port[PORT_A].mode[modep];
|
||||||
|
iu_increment_a = TRUE;
|
||||||
|
break;
|
||||||
|
case SRA:
|
||||||
|
data = iu_state.port[PORT_A].stat;
|
||||||
|
break;
|
||||||
|
case RHRA:
|
||||||
|
data = iu_state.port[PORT_A].buf;
|
||||||
|
iu_state.port[PORT_A].stat &= ~STS_RXR;
|
||||||
|
iu_state.istat &= ~ISTS_RAI;
|
||||||
|
csr_data &= ~CSRUART;
|
||||||
|
break;
|
||||||
|
case IPCR:
|
||||||
|
data = iu_state.ipcr;
|
||||||
|
/* Reading the port resets the upper four bits */
|
||||||
|
iu_state.ipcr &= 0x0f;
|
||||||
|
csr_data &= ~CSRUART;
|
||||||
|
break;
|
||||||
|
case ISR:
|
||||||
|
data = iu_state.istat;
|
||||||
|
break;
|
||||||
|
case CTU:
|
||||||
|
data = (iu_state.c_set >> 8) & 0xff;
|
||||||
|
break;
|
||||||
|
case CTL:
|
||||||
|
data = iu_state.c_set & 0xff;
|
||||||
|
break;
|
||||||
|
case MR12B:
|
||||||
|
modep = iu_state.port[PORT_B].modep;
|
||||||
|
data = iu_state.port[PORT_B].mode[modep];
|
||||||
|
iu_increment_b = TRUE;
|
||||||
|
break;
|
||||||
|
case SRB:
|
||||||
|
data = iu_state.port[PORT_B].stat;
|
||||||
|
break;
|
||||||
|
case RHRB:
|
||||||
|
data = iu_state.port[PORT_B].buf;
|
||||||
|
iu_state.port[PORT_B].stat &= ~STS_RXR;
|
||||||
|
iu_state.istat &= ~ISTS_RBI;
|
||||||
|
break;
|
||||||
|
case INPRT:
|
||||||
|
/* TODO: Correct behavior for DCD on contty */
|
||||||
|
/* For now, this enables DCD/DTR on console only */
|
||||||
|
data = 0x8e;
|
||||||
|
break;
|
||||||
|
case START_CTR:
|
||||||
|
data = 0;
|
||||||
|
iu_state.istat &= ~ISTS_CRI;
|
||||||
|
delay = (uint32) (IU_TIMER_STP * iu_state.c_set);
|
||||||
|
sim_activate_abs(&iu_unit[UNIT_IU_TIMER], (int32) DELAY_US(delay));
|
||||||
|
break;
|
||||||
|
case STOP_CTR:
|
||||||
|
data = 0;
|
||||||
|
iu_state.istat &= ~ISTS_CRI;
|
||||||
|
csr_data &= ~CSRUART;
|
||||||
|
sim_cancel(&iu_unit[UNIT_IU_TIMER]);
|
||||||
|
break;
|
||||||
|
case 17: /* Clear DMAC interrupt */
|
||||||
|
data = 0;
|
||||||
|
iu_state.drqa = FALSE;
|
||||||
|
iu_state.drqb = FALSE;
|
||||||
|
csr_data &= ~CSRDMA;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iu_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
uint8 reg;
|
||||||
|
uint8 modep;
|
||||||
|
|
||||||
|
reg = (uint8) (pa - IUBASE);
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case MR12A:
|
||||||
|
modep = iu_state.port[PORT_A].modep;
|
||||||
|
iu_state.port[PORT_A].mode[modep] = val & 0xff;
|
||||||
|
iu_increment_a = TRUE;
|
||||||
|
break;
|
||||||
|
case CSRA:
|
||||||
|
/* Set baud rate - not implemented */
|
||||||
|
break;
|
||||||
|
case CRA: /* Command A */
|
||||||
|
iu_w_cmd(PORT_A, (uint8) val);
|
||||||
|
break;
|
||||||
|
case THRA: /* TX/RX Buf A */
|
||||||
|
/* Loopback mode */
|
||||||
|
if ((iu_state.port[PORT_A].mode[1] & 0xc0) == 0x80) {
|
||||||
|
iu_state.port[PORT_A].buf = (uint8) val;
|
||||||
|
iu_state.port[PORT_A].stat |= STS_RXR;
|
||||||
|
iu_state.istat |= ISTS_RAI;
|
||||||
|
} else {
|
||||||
|
iu_tx(PORT_A, (uint8) val);
|
||||||
|
}
|
||||||
|
csr_data &= ~CSRUART;
|
||||||
|
break;
|
||||||
|
case ACR: /* Auxiliary Control Register */
|
||||||
|
iu_state.acr = (uint8) val;
|
||||||
|
break;
|
||||||
|
case IMR:
|
||||||
|
iu_state.imr = (uint8) val;
|
||||||
|
csr_data &= ~CSRUART;
|
||||||
|
/* Possibly cause an interrupt */
|
||||||
|
sim_debug(EXECUTE_MSG, &iu_dev,
|
||||||
|
">>> calling iu_txrdy_irq() on IMR write.\n");
|
||||||
|
iu_txrdy_irq(PORT_A);
|
||||||
|
iu_txrdy_irq(PORT_B);
|
||||||
|
break;
|
||||||
|
case CTUR: /* Counter/Timer Upper Preset Value */
|
||||||
|
/* Clear out high byte */
|
||||||
|
iu_state.c_set &= 0x00ff;
|
||||||
|
/* Set high byte */
|
||||||
|
iu_state.c_set |= (val & 0xff) << 8;
|
||||||
|
break;
|
||||||
|
case CTLR: /* Counter/Timer Lower Preset Value */
|
||||||
|
/* Clear out low byte */
|
||||||
|
iu_state.c_set &= 0xff00;
|
||||||
|
/* Set low byte */
|
||||||
|
iu_state.c_set |= (val & 0xff);
|
||||||
|
break;
|
||||||
|
case MR12B:
|
||||||
|
modep = iu_state.port[PORT_B].modep;
|
||||||
|
iu_state.port[PORT_B].mode[modep] = val & 0xff;
|
||||||
|
iu_increment_b = TRUE;
|
||||||
|
break;
|
||||||
|
case CRB: /* Command B */
|
||||||
|
iu_w_cmd(PORT_B, (uint8) val);
|
||||||
|
break;
|
||||||
|
case CSRB:
|
||||||
|
break;
|
||||||
|
case THRB: /* TX/RX Buf B */
|
||||||
|
/* Loopback mode */
|
||||||
|
if ((iu_state.port[PORT_B].mode[1] & 0xc0) == 0x80) {
|
||||||
|
iu_state.port[PORT_B].buf = (uint8) val;
|
||||||
|
iu_state.port[PORT_B].stat |= STS_RXR;
|
||||||
|
iu_state.istat |= ISTS_RAI;
|
||||||
|
} else {
|
||||||
|
iu_tx(PORT_B, (uint8) val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OPCR:
|
||||||
|
iu_state.opcr = (uint8) val;
|
||||||
|
break;
|
||||||
|
case SOPR:
|
||||||
|
break;
|
||||||
|
case ROPR:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void iua_drq_handled()
|
||||||
|
{
|
||||||
|
sim_debug(EXECUTE_MSG, &iu_dev,
|
||||||
|
"Firing IU TTY IRQ 13 On DRQ Handled\n");
|
||||||
|
|
||||||
|
csr_data |= CSRDMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iub_drq_handled()
|
||||||
|
{
|
||||||
|
sim_debug(EXECUTE_MSG, &iu_dev,
|
||||||
|
">>> DRQB handled.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void iu_tx(uint8 portno, uint8 val)
|
||||||
|
{
|
||||||
|
struct port *p;
|
||||||
|
|
||||||
|
p = &iu_state.port[portno];
|
||||||
|
|
||||||
|
p->buf = val;
|
||||||
|
|
||||||
|
if (p->conf & TX_EN) {
|
||||||
|
sim_debug(EXECUTE_MSG, &iu_dev,
|
||||||
|
"[%08x] TRANSMIT: %02x (%c)\n",
|
||||||
|
R[NUM_PC], val, val);
|
||||||
|
p->stat &= ~(STS_TXR|STS_TXE);
|
||||||
|
iu_state.istat &= ~(1 << (portno*4));
|
||||||
|
|
||||||
|
/* Write the character to the SIMH console */
|
||||||
|
sim_putchar(p->buf);
|
||||||
|
|
||||||
|
/* The buffer is now empty, we've transmitted, so set TXR */
|
||||||
|
p->stat |= STS_TXR;
|
||||||
|
iu_state.istat |= (1 << (portno*4));
|
||||||
|
|
||||||
|
/* Possibly cause an interrupt */
|
||||||
|
sim_activate_abs(&iu_unit[UNIT_CONSOLE_TTO],
|
||||||
|
iu_unit[UNIT_CONSOLE_TTO].wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 cmd)
|
||||||
|
{
|
||||||
|
/* Enable or disable transmitter */
|
||||||
|
/* Disable always wins, if both are set */
|
||||||
|
if (cmd & CMD_DTX) {
|
||||||
|
iu_state.port[portno].conf &= ~TX_EN;
|
||||||
|
iu_state.port[portno].stat &= ~STS_TXR;
|
||||||
|
iu_state.port[portno].stat &= ~STS_TXE;
|
||||||
|
iu_state.drqa = FALSE;
|
||||||
|
sim_debug(EXECUTE_MSG, &iu_dev,
|
||||||
|
">>> Disabling transmitter.\n");
|
||||||
|
} else if (cmd & CMD_ETX) {
|
||||||
|
iu_state.port[portno].conf |= TX_EN;
|
||||||
|
/* TXE and TXR are always set by an ENABLE */
|
||||||
|
iu_state.port[portno].stat |= STS_TXR;
|
||||||
|
iu_state.port[portno].stat |= STS_TXE;
|
||||||
|
iu_state.istat |= 1 << (portno*4);
|
||||||
|
iu_state.drqa = TRUE;
|
||||||
|
sim_debug(EXECUTE_MSG, &iu_dev,
|
||||||
|
">>> Calling iu_txrdy_irq() on TX Enable\n");
|
||||||
|
iu_txrdy_irq(portno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable or disable receiver. */
|
||||||
|
/* Disable always wins, if both are set */
|
||||||
|
if (cmd & CMD_DRX) {
|
||||||
|
iu_state.port[portno].conf &= ~RX_EN;
|
||||||
|
iu_state.port[portno].stat &= ~STS_RXR;
|
||||||
|
} else if (cmd & CMD_ERX) {
|
||||||
|
iu_state.port[portno].conf |= RX_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Command register bits 6-4 have special meaning */
|
||||||
|
switch ((cmd >> CMD_MISC_SHIFT) & CMD_MISC_MASK) {
|
||||||
|
case 1:
|
||||||
|
/* Causes the Channel A MR pointer to point to MR1. */
|
||||||
|
iu_state.port[portno].modep = 0;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* Reset receiver. Resets the Channel's receiver as if a
|
||||||
|
hardware reset had been applied. The receiver is disabled
|
||||||
|
and the FIFO is flushed. */
|
||||||
|
iu_state.port[portno].stat &= ~STS_RXR;
|
||||||
|
iu_state.port[portno].conf &= ~RX_EN;
|
||||||
|
iu_state.port[portno].buf = 0;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
/* Reset transmitter. Resets the Channel's transmitter as if a
|
||||||
|
hardware reset had been applied. */
|
||||||
|
iu_state.port[portno].stat &= ~STS_TXR;
|
||||||
|
iu_state.port[portno].stat &= ~STS_TXE;
|
||||||
|
iu_state.port[portno].conf &= ~TX_EN;
|
||||||
|
iu_state.port[portno].buf = 0;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
/* Reset error status. Clears the Channel's Received Break,
|
||||||
|
Parity Error, and Overrun Error bits in the status register
|
||||||
|
(SRA[7:4]). Used in character mode to clear OE status
|
||||||
|
(although RB, PE and FE bits will also be cleared) and in
|
||||||
|
block mode to clear all error status after a block of data
|
||||||
|
has been received. */
|
||||||
|
iu_state.port[portno].stat &= ~(STS_FER|STS_PER|STS_OER);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
/* Reset Channel's break change interrupt. Causes the Channel
|
||||||
|
A break detect change bit in the interrupt status register
|
||||||
|
(ISR[2] for Chan. A, ISR[6] for Chan. B) to be cleared to
|
||||||
|
zero. */
|
||||||
|
iu_state.istat &= ~(1 << (2 + portno*4));
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
/* Start break. Forces the TxDA output LOW (spacing). If the
|
||||||
|
transmitter is empty the start of the break condition will
|
||||||
|
be delayed up to two bit times. If the transmitter is
|
||||||
|
active the break begins when transmission of the character
|
||||||
|
is completed. If a character is in the THR, the start of
|
||||||
|
the break will be delayed until that character, or any
|
||||||
|
other loaded subsequently are transmitted. The transmitter
|
||||||
|
must be enabled for this command to be accepted. */
|
||||||
|
/* Not Implemented */
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
/* Stop break. The TxDA line will go HIGH (marking) within two
|
||||||
|
bit times. TxDA will remain HIGH for one bit time before
|
||||||
|
the next character, if any, is transmitted. */
|
||||||
|
/* Not Implemented */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
192
3B2/3b2_iu.h
Normal file
192
3B2/3b2_iu.h
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
/* 3b2_iu.h: SCN2681A Dual UART Header
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __3B2_IU_H__
|
||||||
|
#define __3B2_IU_H__
|
||||||
|
|
||||||
|
#include "3b2_defs.h"
|
||||||
|
#include "3b2_sysdev.h"
|
||||||
|
|
||||||
|
#define CMD_ERX 0x01 /* Enable receiver */
|
||||||
|
#define CMD_DRX 0x02 /* Disable receiver */
|
||||||
|
#define CMD_ETX 0x04 /* Enable transmitter */
|
||||||
|
#define CMD_DTX 0x08 /* Disable transmitter */
|
||||||
|
#define CMD_MISC_SHIFT 4 /* Command */
|
||||||
|
#define CMD_MISC_MASK 0x7
|
||||||
|
|
||||||
|
#define STS_RXR 0x01 /* Receiver ready */
|
||||||
|
#define STS_FFL 0x02 /* FIFO full */
|
||||||
|
#define STS_TXR 0x04 /* Transmitter ready */
|
||||||
|
#define STS_TXE 0x08 /* Transmitter empty */
|
||||||
|
#define STS_OER 0x10 /* Overrun error */
|
||||||
|
#define STS_PER 0x20 /* Parity error */
|
||||||
|
#define STS_FER 0x40 /* Framing error */
|
||||||
|
#define STS_RXB 0x80 /* Received break */
|
||||||
|
|
||||||
|
#define ISTS_TAI 0x01 /* Transmitter ready A */
|
||||||
|
#define ISTS_RAI 0x02 /* Receiver ready A */
|
||||||
|
#define ISTS_CBA 0x04 /* Change in break A */
|
||||||
|
#define ISTS_CRI 0x08 /* Counter ready */
|
||||||
|
#define ISTS_TBI 0x10 /* Transmitter ready B */
|
||||||
|
#define ISTS_RBI 0x20 /* Receiver ready B */
|
||||||
|
#define ISTS_CBB 0x40 /* Change in break B */
|
||||||
|
#define ISTS_IPC 0x80 /* Interrupt port change */
|
||||||
|
|
||||||
|
#define MODE_V_CHM 6 /* Channel mode */
|
||||||
|
#define MODE_M_CHM 0x3
|
||||||
|
|
||||||
|
#define PORT_A 0
|
||||||
|
#define PORT_B 1
|
||||||
|
|
||||||
|
/* Used by the DMAC */
|
||||||
|
#define IUA_DATA_REG 3
|
||||||
|
#define IUB_DATA_REG 11
|
||||||
|
|
||||||
|
/* Registers - Read */
|
||||||
|
#define SRA 1
|
||||||
|
#define RHRA 3
|
||||||
|
#define IPCR 4
|
||||||
|
#define ISR 5
|
||||||
|
#define CTU 6
|
||||||
|
#define CTL 7
|
||||||
|
#define SRB 9
|
||||||
|
#define RHRB 11
|
||||||
|
#define INPRT 13 /* Input port data */
|
||||||
|
#define START_CTR 14
|
||||||
|
#define STOP_CTR 15
|
||||||
|
|
||||||
|
/* Registers - Write */
|
||||||
|
#define CSRA 1
|
||||||
|
#define CRA 2
|
||||||
|
#define THRA 3
|
||||||
|
#define ACR 4
|
||||||
|
#define IMR 5
|
||||||
|
#define CTUR 6
|
||||||
|
#define CTLR 7
|
||||||
|
#define CSRB 9
|
||||||
|
#define CRB 10
|
||||||
|
#define THRB 11
|
||||||
|
#define OPCR 13
|
||||||
|
#define SOPR 14
|
||||||
|
#define ROPR 15
|
||||||
|
|
||||||
|
#define UNIT_CONSOLE_TTI 0
|
||||||
|
#define UNIT_CONSOLE_TTO 1
|
||||||
|
#define UNIT_IU_TIMER 2
|
||||||
|
|
||||||
|
/* Registers - R/W */
|
||||||
|
#define MR12A 0
|
||||||
|
#define MR12B 8
|
||||||
|
|
||||||
|
/* Port configuration */
|
||||||
|
#define TX_EN 1
|
||||||
|
#define RX_EN 2
|
||||||
|
|
||||||
|
#define UM_CTR_EXT 0
|
||||||
|
#define UM_CTR_TXCA 1
|
||||||
|
#define UM_CTR_TXCB 2
|
||||||
|
#define UM_CTR_DIV16 3
|
||||||
|
#define UM_TMR_EXT 4
|
||||||
|
#define UM_TMR_EXT16 5
|
||||||
|
#define UM_TMR_XTL 6
|
||||||
|
#define UM_TMR_XTL16 7
|
||||||
|
#define UM_MASK 0x70
|
||||||
|
#define UM_SHIFT 4
|
||||||
|
|
||||||
|
#define IU_MODE(x) ((x & UM_MASK) >> UM_SHIFT)
|
||||||
|
|
||||||
|
extern DEVICE iu_dev;
|
||||||
|
|
||||||
|
#define IU_TTY_DELAY 25000
|
||||||
|
|
||||||
|
#define IUBASE 0x49000
|
||||||
|
#define IUSIZE 0x100
|
||||||
|
|
||||||
|
/* The UART is driven by a 3.6864 MHz crystal. This is divided by 16
|
||||||
|
to clock the timer. (One peculiarity: 3.6864 MHz /16 is 230400 Hz,
|
||||||
|
but the SVR3 source code claims the /16 clock is actually 230525
|
||||||
|
Hz. So, we'll go with 230525 Hz until proven otherwise.)
|
||||||
|
|
||||||
|
UART clock period = 4338ns
|
||||||
|
System clock period = 100ns
|
||||||
|
|
||||||
|
That means the system ticks 43.3792 times for every one tick of the
|
||||||
|
UART clock.
|
||||||
|
|
||||||
|
But this is a simulated system, where each simulator step is
|
||||||
|
CYCLES_PER_INST long. So we take that into account.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define IU_TIMER_STP 4.33792
|
||||||
|
|
||||||
|
struct port {
|
||||||
|
uint8 stat; /* Port Status */
|
||||||
|
uint8 cmd; /* Command */
|
||||||
|
uint8 mode[2]; /* Two mode buffers */
|
||||||
|
uint8 modep; /* Point to mode[0] or mode[1] */
|
||||||
|
uint8 conf; /* Configuration bits */
|
||||||
|
uint8 buf; /* Character data */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct iu_state {
|
||||||
|
uint8 istat; /* Interrupt Status */
|
||||||
|
uint8 imr; /* Interrupt Mask Register */
|
||||||
|
uint16 c_set; /* Timer / Counter Setting */
|
||||||
|
int32 c_val; /* Timer / Counter Value */
|
||||||
|
t_bool c_en; /* Counter Enabled */
|
||||||
|
t_bool drqa; /* Port A DRQ */
|
||||||
|
t_bool drqb; /* Port B DRQ */
|
||||||
|
uint8 acr;
|
||||||
|
uint8 opcr; /* Output Port Configuration */
|
||||||
|
uint8 inprt; /* Input Port Data */
|
||||||
|
uint8 ipcr; /* Input Port Change Register */
|
||||||
|
struct port port[2]; /* Port A and B */
|
||||||
|
} IU_STATE;
|
||||||
|
|
||||||
|
extern IU_STATE iu_state;
|
||||||
|
|
||||||
|
/* Function prototypes */
|
||||||
|
|
||||||
|
t_stat iu_reset(DEVICE *dptr);
|
||||||
|
t_stat iu_svc_tti(UNIT *uptr);
|
||||||
|
t_stat iu_svc_tto(UNIT *uptr);
|
||||||
|
t_stat iu_svc_timer(UNIT *uptr);
|
||||||
|
uint32 iu_read(uint32 pa, size_t size);
|
||||||
|
void iu_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
void iua_drq_handled();
|
||||||
|
void iub_drq_handled();
|
||||||
|
|
||||||
|
static SIM_INLINE void iu_tx(uint8 portno, uint8 val);
|
||||||
|
static SIM_INLINE void iu_w_buf(uint8 portno, uint8 val);
|
||||||
|
static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 val);
|
||||||
|
static SIM_INLINE void iu_update_rxi(uint8 c);
|
||||||
|
static SIM_INLINE void iu_update_txi();
|
||||||
|
|
||||||
|
#endif
|
882
3B2/3b2_mmu.c
Normal file
882
3B2/3b2_mmu.c
Normal file
|
@ -0,0 +1,882 @@
|
||||||
|
/* 3b2_mmu.c: AT&T 3B2 Model 400 MMU (WE32101) Implementation
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "3b2_mmu.h"
|
||||||
|
|
||||||
|
UNIT mmu_unit = { UDATA(NULL, 0, 0) };
|
||||||
|
|
||||||
|
MMU_STATE mmu_state;
|
||||||
|
|
||||||
|
REG mmu_reg[] = {
|
||||||
|
{ HRDATAD (ENABLE, mmu_state.enabled, 1, "Enabled?") },
|
||||||
|
{ HRDATAD (CONFIG, mmu_state.conf, 32, "Configuration") },
|
||||||
|
{ HRDATAD (VAR, mmu_state.var, 32, "Virtual Address") },
|
||||||
|
{ HRDATAD (FCODE, mmu_state.fcode, 32, "Fault Code") },
|
||||||
|
{ HRDATAD (FADDR, mmu_state.faddr, 32, "Fault Address") },
|
||||||
|
{ BRDATA (SDCL, mmu_state.sdcl, 16, 32, MMU_SDCS) },
|
||||||
|
{ BRDATA (SDCR, mmu_state.sdch, 16, 32, MMU_SDCS) },
|
||||||
|
{ BRDATA (PDCLL, mmu_state.pdcll, 16, 32, MMU_PDCS) },
|
||||||
|
{ BRDATA (PDCLH, mmu_state.pdclh, 16, 32, MMU_PDCS) },
|
||||||
|
{ BRDATA (PDCRL, mmu_state.pdcrl, 16, 32, MMU_PDCS) },
|
||||||
|
{ BRDATA (PDCRH, mmu_state.pdcrh, 16, 32, MMU_PDCS) },
|
||||||
|
{ BRDATA (SRAMA, mmu_state.sra, 16, 32, MMU_SRS) },
|
||||||
|
{ BRDATA (SRAMB, mmu_state.srb, 16, 32, MMU_SRS) },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE mmu_dev = {
|
||||||
|
"MMU", &mmu_unit, mmu_reg, NULL,
|
||||||
|
1, 16, 8, 4, 16, 32,
|
||||||
|
NULL, NULL, &mmu_init,
|
||||||
|
NULL, NULL, NULL, NULL,
|
||||||
|
DEV_DEBUG, 0, sys_deb_tab
|
||||||
|
};
|
||||||
|
|
||||||
|
t_stat mmu_init(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
flush_caches();
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 mmu_read(uint32 pa, size_t size)
|
||||||
|
{
|
||||||
|
uint32 offset;
|
||||||
|
uint32 data = 0;
|
||||||
|
|
||||||
|
offset = (pa >> 2) & 0x1f;
|
||||||
|
|
||||||
|
switch ((pa >> 8) & 0xf) {
|
||||||
|
case MMU_SDCL:
|
||||||
|
data = mmu_state.sdcl[offset];
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"MMU_SDCL[%d] = %08x\n",
|
||||||
|
offset, data);
|
||||||
|
break;
|
||||||
|
case MMU_SDCH:
|
||||||
|
data = mmu_state.sdch[offset];
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"MMU_SDCH[%d] = %08x\n",
|
||||||
|
offset, data);
|
||||||
|
break;
|
||||||
|
case MMU_PDCRL:
|
||||||
|
data = mmu_state.pdcrl[offset];
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"MMU_PDCRL[%d] = %08x\n",
|
||||||
|
offset, data);
|
||||||
|
break;
|
||||||
|
case MMU_PDCRH:
|
||||||
|
data = mmu_state.pdcrh[offset];
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"MMU_PDCRH[%d] = %08x\n",
|
||||||
|
offset, data);
|
||||||
|
break;
|
||||||
|
case MMU_PDCLL:
|
||||||
|
data = mmu_state.pdcll[offset];
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"MMU_PDCLL[%d] = %08x\n",
|
||||||
|
offset, data);
|
||||||
|
break;
|
||||||
|
case MMU_PDCLH:
|
||||||
|
data = mmu_state.pdclh[offset];
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"MMU_PDCLH[%d] = %08x\n",
|
||||||
|
offset, data);
|
||||||
|
break;
|
||||||
|
case MMU_SRAMA:
|
||||||
|
data = mmu_state.sra[offset];
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"[%08x] MMU_SRAMA[%d] = %08x\n",
|
||||||
|
R[NUM_PC], offset, data);
|
||||||
|
break;
|
||||||
|
case MMU_SRAMB:
|
||||||
|
data = mmu_state.srb[offset];
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"[%08x] MMU_SRAMB[%d] = %08x\n",
|
||||||
|
R[NUM_PC], offset, data);
|
||||||
|
break;
|
||||||
|
case MMU_FC:
|
||||||
|
data = mmu_state.fcode;
|
||||||
|
break;
|
||||||
|
case MMU_FA:
|
||||||
|
data = mmu_state.faddr;
|
||||||
|
break;
|
||||||
|
case MMU_CONF:
|
||||||
|
data = mmu_state.conf & 0x7;
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"[%08x] MMU_CONF = %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
break;
|
||||||
|
case MMU_VAR:
|
||||||
|
data = mmu_state.var;
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"[%08x] MMU_VAR = %08x\n",
|
||||||
|
R[NUM_PC], data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mmu_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
uint32 offset;
|
||||||
|
|
||||||
|
offset = (pa >> 2) & 0x1f;
|
||||||
|
|
||||||
|
switch ((pa >> 8) & 0xf) {
|
||||||
|
case MMU_SDCL:
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"MMU_SDCL[%d] = %08x\n",
|
||||||
|
offset, val);
|
||||||
|
mmu_state.sdcl[offset] = val;
|
||||||
|
break;
|
||||||
|
case MMU_SDCH:
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"MMU_SDCH[%d] = %08x\n",
|
||||||
|
offset, val);
|
||||||
|
mmu_state.sdch[offset] = val;
|
||||||
|
break;
|
||||||
|
case MMU_PDCRL:
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"MMU_PDCRL[%d] = %08x\n",
|
||||||
|
offset, val);
|
||||||
|
mmu_state.pdcrl[offset] = val;
|
||||||
|
break;
|
||||||
|
case MMU_PDCRH:
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"MMU_PDCRH[%d] = %08x\n",
|
||||||
|
offset, val);
|
||||||
|
mmu_state.pdcrh[offset] = val;
|
||||||
|
break;
|
||||||
|
case MMU_PDCLL:
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"MMU_PDCLL[%d] = %08x\n",
|
||||||
|
offset, val);
|
||||||
|
mmu_state.pdcll[offset] = val;
|
||||||
|
break;
|
||||||
|
case MMU_PDCLH:
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"MMU_PDCLH[%d] = %08x\n",
|
||||||
|
offset, val);
|
||||||
|
mmu_state.pdclh[offset] = val;
|
||||||
|
break;
|
||||||
|
case MMU_SRAMA:
|
||||||
|
offset = offset & 3;
|
||||||
|
mmu_state.sra[offset] = val;
|
||||||
|
mmu_state.sec[offset].addr = val & 0xffffffe0;
|
||||||
|
/* We flush the entire section on writing SRAMA */
|
||||||
|
flush_cache_sec((uint8) offset);
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"[%08x] MMU_SRAMA[%d] = %08x (addr=%08x)\n",
|
||||||
|
R[NUM_PC], offset, val, mmu_state.sec[offset].addr);
|
||||||
|
break;
|
||||||
|
case MMU_SRAMB:
|
||||||
|
offset = offset & 3;
|
||||||
|
mmu_state.srb[offset] = val;
|
||||||
|
mmu_state.sec[offset].len = (val >> 10) & 0x1fff;
|
||||||
|
/* We do not flush the cache on writing SRAMB */
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"[%08x] MMU_SRAMB[%d] = %08x (len=%06x)\n",
|
||||||
|
R[NUM_PC], offset, val, mmu_state.sec[offset].len);
|
||||||
|
break;
|
||||||
|
case MMU_FC:
|
||||||
|
mmu_state.fcode = val;
|
||||||
|
break;
|
||||||
|
case MMU_FA:
|
||||||
|
mmu_state.faddr = val;
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"[%08x] MMU_FAULT_ADDR = %08x\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
break;
|
||||||
|
case MMU_CONF:
|
||||||
|
mmu_state.conf = val & 0x7;
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"[%08x] MMU_CONF = %08x\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
break;
|
||||||
|
case MMU_VAR:
|
||||||
|
mmu_state.var = val;
|
||||||
|
flush_sdce(val);
|
||||||
|
flush_pdce(val);
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"[%08x] MMU_VAR = %08x\n",
|
||||||
|
R[NUM_PC], val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t_bool addr_is_rom(uint32 pa)
|
||||||
|
{
|
||||||
|
return (pa < BOOT_CODE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
t_bool addr_is_mem(uint32 pa)
|
||||||
|
{
|
||||||
|
return (pa >= PHYS_MEM_BASE &&
|
||||||
|
pa < (PHYS_MEM_BASE + MEM_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
t_bool addr_is_io(uint32 pa)
|
||||||
|
{
|
||||||
|
return ((pa >= IO_BASE && pa < IO_BASE + IO_SIZE) ||
|
||||||
|
(pa >= IOB_BASE && pa < IOB_BASE + IOB_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Raw physical reads and writes.
|
||||||
|
*
|
||||||
|
* The WE32100 is a BIG-endian machine, meaning that words are
|
||||||
|
* arranged in increasing address from most-significant byte to
|
||||||
|
* least-significant byte.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read Word (Physical Address)
|
||||||
|
*/
|
||||||
|
uint32 pread_w(uint32 pa)
|
||||||
|
{
|
||||||
|
uint32 *m;
|
||||||
|
uint32 index;
|
||||||
|
|
||||||
|
if (pa & 3) {
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"[%08x] Cannot read physical address. ALIGNMENT ISSUE: %08x\n",
|
||||||
|
R[NUM_PC], pa);
|
||||||
|
csr_data |= CSRALGN;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_io(pa)) {
|
||||||
|
return io_read(pa, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_rom(pa)) {
|
||||||
|
m = ROM;
|
||||||
|
index = pa >> 2;
|
||||||
|
} else if (addr_is_mem(pa)) {
|
||||||
|
m = RAM;
|
||||||
|
index = (pa - PHYS_MEM_BASE) >> 2;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write Word (Physical Address)
|
||||||
|
*/
|
||||||
|
void pwrite_w(uint32 pa, uint32 val)
|
||||||
|
{
|
||||||
|
if (pa & 3) {
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Cannot write physical address. ALIGNMENT ISSUE: %08x\n",
|
||||||
|
R[NUM_PC], pa);
|
||||||
|
csr_data |= CSRALGN;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_io(pa)) {
|
||||||
|
io_write(pa, val, 32);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_mem(pa)) {
|
||||||
|
RAM[(pa - PHYS_MEM_BASE) >> 2] = val;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read Halfword (Physical Address)
|
||||||
|
*/
|
||||||
|
uint16 pread_h(uint32 pa)
|
||||||
|
{
|
||||||
|
uint32 *m;
|
||||||
|
uint32 index;
|
||||||
|
|
||||||
|
if (pa & 1) {
|
||||||
|
sim_debug(READ_MSG, &mmu_dev,
|
||||||
|
"[%08x] Cannot read physical address. ALIGNMENT ISSUE %08x\n",
|
||||||
|
R[NUM_PC], pa);
|
||||||
|
csr_data |= CSRALGN;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_io(pa)) {
|
||||||
|
return (uint16) io_read(pa, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_rom(pa)) {
|
||||||
|
m = ROM;
|
||||||
|
index = pa >> 2;
|
||||||
|
} else if (addr_is_mem(pa)) {
|
||||||
|
m = RAM;
|
||||||
|
index = (pa - PHYS_MEM_BASE) >> 2;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pa & 2) {
|
||||||
|
return m[index] & HALF_MASK;
|
||||||
|
} else {
|
||||||
|
return (m[index] >> 16) & HALF_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write Halfword (Physical Address)
|
||||||
|
*/
|
||||||
|
void pwrite_h(uint32 pa, uint16 val)
|
||||||
|
{
|
||||||
|
uint32 *m;
|
||||||
|
uint32 index;
|
||||||
|
uint32 wval = (uint32)val;
|
||||||
|
|
||||||
|
if (pa & 1) {
|
||||||
|
sim_debug(WRITE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Cannot write physical address %08x, ALIGNMENT ISSUE\n",
|
||||||
|
R[NUM_PC], pa);
|
||||||
|
csr_data |= CSRALGN;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_io(pa)) {
|
||||||
|
io_write(pa, val, 16);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_mem(pa)) {
|
||||||
|
m = RAM;
|
||||||
|
index = (pa - PHYS_MEM_BASE) >> 2;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pa & 2) {
|
||||||
|
m[index] = (m[index] & ~HALF_MASK) | wval;
|
||||||
|
} else {
|
||||||
|
m[index] = (m[index] & HALF_MASK) | (wval << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read Byte (Physical Address)
|
||||||
|
*/
|
||||||
|
uint8 pread_b(uint32 pa)
|
||||||
|
{
|
||||||
|
uint32 data;
|
||||||
|
int32 sc = (~(pa & 3) << 3) & 0x1f;
|
||||||
|
|
||||||
|
if (addr_is_io(pa)) {
|
||||||
|
return (uint8)(io_read(pa, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_rom(pa)) {
|
||||||
|
data = ROM[pa >> 2];
|
||||||
|
} else if (addr_is_mem(pa)) {
|
||||||
|
data = RAM[(pa - PHYS_MEM_BASE) >> 2];
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (data >> sc) & BYTE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write Byte (Physical Address)
|
||||||
|
*/
|
||||||
|
void pwrite_b(uint32 pa, uint8 val)
|
||||||
|
{
|
||||||
|
uint32 *m;
|
||||||
|
int32 index;
|
||||||
|
int32 sc = (~(pa & 3) << 3) & 0x1f;
|
||||||
|
uint32 mask = 0xffu << sc;
|
||||||
|
|
||||||
|
if (addr_is_io(pa)) {
|
||||||
|
io_write(pa, val, 8);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_is_mem(pa)) {
|
||||||
|
m = RAM;
|
||||||
|
index = (pa - PHYS_MEM_BASE) >> 2;
|
||||||
|
m[index] = (m[index] & ~mask) | (uint32) (val << sc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper functions for MMU decode. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the Segment Descriptor for a virtual address on a cache miss.
|
||||||
|
*
|
||||||
|
* Returns SCPE_OK on success, SCPE_NXM on failure.
|
||||||
|
*
|
||||||
|
* If SCPE_NXM is returned, a failure code and fault address will be
|
||||||
|
* set in the appropriate registers.
|
||||||
|
*
|
||||||
|
* As always, the flag 'fc' may be set to FALSE to avoid certain
|
||||||
|
* typses of fault checking.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
t_stat mmu_get_sd(uint32 va, uint8 r_acc, t_bool fc,
|
||||||
|
uint32 *sd0, uint32 *sd1)
|
||||||
|
{
|
||||||
|
/* We immediately do some bounds checking (fc flag is not checked
|
||||||
|
* because this is a fatal error) */
|
||||||
|
if (SSL(va) > SRAMB_LEN(va)) {
|
||||||
|
MMU_FAULT(MMU_F_SDTLEN);
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] SDT Length Fault. sramb_len=%x ssl=%x va=%08x\n",
|
||||||
|
R[NUM_PC], SRAMB_LEN(va), SSL(va), va);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sd0 contains the segment descriptor, sd1 contains a pointer to
|
||||||
|
the PDT or Segment */
|
||||||
|
*sd0 = pread_w(SD_ADDR(va));
|
||||||
|
*sd1 = pread_w(SD_ADDR(va) + 4);
|
||||||
|
|
||||||
|
if (!SD_VALID(*sd0)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Invalid Segment Descriptor. va=%08x sd0=%08x\n",
|
||||||
|
R[NUM_PC], va, *sd0);
|
||||||
|
MMU_FAULT(MMU_F_INV_SD);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Handle indirect lookups. */
|
||||||
|
if (SD_INDIRECT(*sd0)) {
|
||||||
|
stop_reason = STOP_MMU;
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the segment descriptor isn't present, we need to
|
||||||
|
* fail out */
|
||||||
|
if (!SD_PRESENT(*sd0)) {
|
||||||
|
if (SD_CONTIG(*sd0)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Segment Not Present. va=%08x",
|
||||||
|
R[NUM_PC], va);
|
||||||
|
MMU_FAULT(MMU_F_SEG_NOT_PRES);
|
||||||
|
return SCPE_NXM;
|
||||||
|
} else {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] PDT Not Present. va=%08x",
|
||||||
|
R[NUM_PC], va);
|
||||||
|
MMU_FAULT(MMU_F_PDT_NOT_PRES);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SHOULD_CACHE_SD(*sd0)) {
|
||||||
|
put_sdce(va, *sd0, *sd1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load a page descriptor from memory
|
||||||
|
*/
|
||||||
|
t_stat mmu_get_pd(uint32 va, uint8 r_acc, t_bool fc,
|
||||||
|
uint32 sd0, uint32 sd1,
|
||||||
|
uint32 *pd, uint8 *pd_acc)
|
||||||
|
{
|
||||||
|
uint32 pd_addr;
|
||||||
|
|
||||||
|
/* Where do we find the page descriptor? */
|
||||||
|
pd_addr = SD_SEG_ADDR(sd1) + (PSL(va) * 4);
|
||||||
|
|
||||||
|
/* Bounds checking on length */
|
||||||
|
if ((PSL(va) * 4) > MAX_OFFSET(sd0)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] PDT Length Fault. "
|
||||||
|
"PDT Offset=%08x Max Offset=%08x va=%08x\n",
|
||||||
|
R[NUM_PC], (PSL(va) * 4),
|
||||||
|
MAX_OFFSET(sd0), va);
|
||||||
|
MMU_FAULT(MMU_F_PDTLEN);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pd = pread_w(pd_addr);
|
||||||
|
|
||||||
|
/* Copy the access flags from the SD */
|
||||||
|
*pd_acc = SD_ACC(sd0);
|
||||||
|
|
||||||
|
/* Cache it */
|
||||||
|
if (SHOULD_CACHE_PD(*pd)) {
|
||||||
|
put_pdce(va, sd0, *pd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode an address from a contiguous segment.
|
||||||
|
*/
|
||||||
|
t_stat mmu_decode_contig(uint32 va, uint8 r_acc,
|
||||||
|
uint32 sd0, uint32 sd1,
|
||||||
|
t_bool fc, uint32 *pa)
|
||||||
|
{
|
||||||
|
if (fc) {
|
||||||
|
/* Verify permissions */
|
||||||
|
if (mmu_check_perm(SD_ACC(sd0), r_acc) != SCPE_OK) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] SEGMENT: NO ACCESS TO MEMORY AT %08x.\n"
|
||||||
|
"\t\tcpu_cm=%d acc_req=%x sd_acc=%02x\n",
|
||||||
|
R[NUM_PC], va, CPU_CM, r_acc, SD_ACC(sd0));
|
||||||
|
MMU_FAULT(MMU_F_ACC);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do max segment offset check outside any 'fc' checks because we
|
||||||
|
want this to fail even if fc is false. */
|
||||||
|
|
||||||
|
if (SOT(va) > MAX_OFFSET(sd0)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] CONTIGUOUS: Segment Offset Fault. "
|
||||||
|
"sd0=%08x SOT=%08x len=%08x va=%08x\n",
|
||||||
|
R[NUM_PC], sd0, SOT(va),
|
||||||
|
(SD_MAX_OFF(sd0) * 8), va);
|
||||||
|
MMU_FAULT(MMU_F_SEG_OFFSET);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: It's possible to have BOTH a segment offset violation AND
|
||||||
|
an access violation. We need to cover that instance. */
|
||||||
|
|
||||||
|
|
||||||
|
if (fc) {
|
||||||
|
/* Update R and M bits if configured */
|
||||||
|
if (SHOULD_UPDATE_SD_R_BIT(sd0)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Updating R bit in SD\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
mmu_update_sd(va, SD_R_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SHOULD_UPDATE_SD_M_BIT(sd0)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Updating M bit in SD\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
mmu_update_sd(va, SD_M_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate object trap if needed */
|
||||||
|
if (SD_TRAP(sd0)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Object Trap. va=%08x",
|
||||||
|
R[NUM_PC], va);
|
||||||
|
MMU_FAULT(MMU_F_OTRAP);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pa = SD_SEG_ADDR(sd1) + SOT(va);
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat mmu_decode_paged(uint32 va, uint8 r_acc, t_bool fc,
|
||||||
|
uint32 sd1, uint32 pd,
|
||||||
|
uint8 pd_acc, uint32 *pa)
|
||||||
|
{
|
||||||
|
if (fc && mmu_check_perm(pd_acc, r_acc) != SCPE_OK) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] PAGE: NO ACCESS TO MEMORY AT %08x.\n"
|
||||||
|
"\t\tcpu_cm=%d r_acc=%x pd_acc=%02x\n"
|
||||||
|
"\t\tpd=%08x psw=%08x\n",
|
||||||
|
R[NUM_PC], va, CPU_CM, r_acc, pd_acc,
|
||||||
|
pd, R[NUM_PSW]);
|
||||||
|
MMU_FAULT(MMU_F_ACC);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the PD is not marked present, fail */
|
||||||
|
if (!PD_PRESENT(pd)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Page Not Present. "
|
||||||
|
"pd=%08x r_acc=%x va=%08x\n",
|
||||||
|
R[NUM_PC], pd, r_acc, va);
|
||||||
|
MMU_FAULT(MMU_F_PAGE_NOT_PRES);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fc) {
|
||||||
|
/* If this is a write or interlocked read access, and
|
||||||
|
the 'W' bit is set, trigger a write fault */
|
||||||
|
if ((r_acc == ACC_W || r_acc == ACC_IR) && PD_WFAULT(pd)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Page Write Fault. va=%08x\n",
|
||||||
|
R[NUM_PC], va);
|
||||||
|
MMU_FAULT(MMU_F_PW);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this is a write, modify the M bit */
|
||||||
|
if (SHOULD_UPDATE_PD_M_BIT(pd)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Updating M bit in PD\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
mmu_update_pd(va, PD_LOC(sd1, va), PD_M_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modify the R bit and write it back */
|
||||||
|
if (SHOULD_UPDATE_PD_R_BIT(pd)) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Updating R bit in PD\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
mmu_update_pd(va, PD_LOC(sd1, va), PD_R_MASK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pa = PD_ADDR(pd) + POT(va);
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Translate a virtual address into a physical address.
|
||||||
|
*
|
||||||
|
* If "fc" is false, this function will bypass:
|
||||||
|
*
|
||||||
|
* - Access flag checks
|
||||||
|
* - Cache insertion
|
||||||
|
* - Setting MMU fault registers
|
||||||
|
* - Modifying segment and page descriptor bits
|
||||||
|
*/
|
||||||
|
|
||||||
|
t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa)
|
||||||
|
{
|
||||||
|
uint32 sd0, sd1, pd;
|
||||||
|
uint8 pd_acc;
|
||||||
|
t_stat sd_cached, pd_cached;
|
||||||
|
|
||||||
|
if (!mmu_enabled()) {
|
||||||
|
*pa = va;
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We must check both caches first to determine what kind of miss
|
||||||
|
processing to do. */
|
||||||
|
|
||||||
|
sd_cached = get_sdce(va, &sd0, &sd1);
|
||||||
|
pd_cached = get_pdce(va, &pd, &pd_acc);
|
||||||
|
|
||||||
|
/* Now, potentially, do miss processing */
|
||||||
|
|
||||||
|
if (sd_cached != SCPE_OK && pd_cached != SCPE_OK) {
|
||||||
|
/* Full miss processing. We have to load both the SD and PD
|
||||||
|
* from main memory, and potentially cache them. */
|
||||||
|
if (mmu_get_sd(va, r_acc, fc, &sd0, &sd1) != SCPE_OK) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Could not get SD (full miss). r_acc=%d, fc=%d, va=%08x\n",
|
||||||
|
R[NUM_PC], r_acc, fc, va);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SD_CONTIG(sd0)) {
|
||||||
|
if (mmu_get_pd(va, r_acc, fc, sd0, sd1, &pd, &pd_acc) != SCPE_OK) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Could not get PD (full miss). r_acc=%d, fc=%d, va=%08x\n",
|
||||||
|
R[NUM_PC], r_acc, fc, va);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (sd_cached == SCPE_OK && pd_cached != SCPE_OK && !SD_CONTIG(sd0)) {
|
||||||
|
/* Partial miss processing - SDC hit and PDC miss - but only
|
||||||
|
* if segment is paged. */
|
||||||
|
if (mmu_get_pd(va, r_acc, fc, sd0, sd1, &pd, &pd_acc) != SCPE_OK) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Could not get PD (partial miss). r_acc=%d, fc=%d, va=%08x\n",
|
||||||
|
R[NUM_PC], r_acc, fc, va);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
} else if (sd_cached != SCPE_OK && pd_cached == SCPE_OK) {
|
||||||
|
/* Partial miss processing - SDC miss and PDC hit. This is
|
||||||
|
* always paged translation */
|
||||||
|
|
||||||
|
/* First we must bring the SD into cache so that the SD
|
||||||
|
* R & M bits may be updated, if needed. */
|
||||||
|
|
||||||
|
if (mmu_get_sd(va, r_acc, fc, &sd0, &sd1) != SCPE_OK) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Could not get SD (partial miss). r_acc=%d, fc=%d, va=%08x\n",
|
||||||
|
R[NUM_PC], r_acc, fc, va);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the 'L' bit is set in the page descriptor, we need to
|
||||||
|
* do some bounds checking */
|
||||||
|
if (PD_LAST(pd)) {
|
||||||
|
if ((PD_ADDR(pd) + POT(va)) > (SD_SEG_ADDR(sd1) + MAX_OFFSET(sd0))) {
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] PAGED: Segment Offset Fault.\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
MMU_FAULT(MMU_F_SEG_OFFSET);
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mmu_decode_paged(va, r_acc, fc, sd1, pd, pd_acc, pa);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SD_CONTIG(sd0)) {
|
||||||
|
return mmu_decode_contig(va, r_acc, sd0, sd1, fc, pa);
|
||||||
|
} else {
|
||||||
|
return mmu_decode_paged(va, r_acc, fc, sd1, pd, pd_acc, pa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat examine(uint32 va, uint8 *val) {
|
||||||
|
uint32 pa;
|
||||||
|
t_stat succ;
|
||||||
|
|
||||||
|
succ = mmu_decode_va(va, 0, FALSE, &pa);
|
||||||
|
|
||||||
|
if (succ == SCPE_OK) {
|
||||||
|
if (addr_is_rom(pa) || addr_is_io(pa) || addr_is_mem(pa)) {
|
||||||
|
*val = pread_b(pa);
|
||||||
|
return SCPE_OK;
|
||||||
|
} else {
|
||||||
|
*val = 0;
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*val = 0;
|
||||||
|
return succ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat deposit(uint32 va, uint8 val) {
|
||||||
|
uint32 pa;
|
||||||
|
t_stat succ;
|
||||||
|
|
||||||
|
succ = mmu_decode_va(va, 0, FALSE, &pa);
|
||||||
|
|
||||||
|
if (succ == SCPE_OK) {
|
||||||
|
if (addr_is_mem(pa) || addr_is_io(pa)) {
|
||||||
|
pwrite_b(pa, val);
|
||||||
|
return SCPE_OK;
|
||||||
|
} else {
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return succ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat read_operand(uint32 va, uint8 *val) {
|
||||||
|
uint32 pa;
|
||||||
|
t_stat succ;
|
||||||
|
|
||||||
|
succ = mmu_decode_va(va, ACC_OF, TRUE, &pa);
|
||||||
|
|
||||||
|
if (succ == SCPE_OK) {
|
||||||
|
*val = pread_b(pa);
|
||||||
|
} else {
|
||||||
|
*val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return succ;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 mmu_xlate_addr(uint32 va, uint8 r_acc)
|
||||||
|
{
|
||||||
|
uint32 pa;
|
||||||
|
t_stat succ;
|
||||||
|
|
||||||
|
succ = mmu_decode_va(va, r_acc, TRUE, &pa);
|
||||||
|
|
||||||
|
if (succ == SCPE_OK) {
|
||||||
|
mmu_state.var = va;
|
||||||
|
return pa;
|
||||||
|
} else {
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SIM_INLINE t_bool mmu_enabled()
|
||||||
|
{
|
||||||
|
return mmu_state.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mmu_enable()
|
||||||
|
{
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Enabling MMU.\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
mmu_state.enabled = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mmu_disable()
|
||||||
|
{
|
||||||
|
sim_debug(EXECUTE_MSG, &mmu_dev,
|
||||||
|
"[%08x] Disabling MMU.\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
mmu_state.enabled = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MMU Virtual Read and Write Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8 read_b(uint32 va, uint8 r_acc)
|
||||||
|
{
|
||||||
|
return pread_b(mmu_xlate_addr(va, r_acc));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 read_h(uint32 va, uint8 r_acc)
|
||||||
|
{
|
||||||
|
return pread_h(mmu_xlate_addr(va, r_acc));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 read_w(uint32 va, uint8 r_acc)
|
||||||
|
{
|
||||||
|
return pread_w(mmu_xlate_addr(va, r_acc));
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_b(uint32 va, uint8 val)
|
||||||
|
{
|
||||||
|
pwrite_b(mmu_xlate_addr(va, ACC_W), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_h(uint32 va, uint16 val)
|
||||||
|
{
|
||||||
|
pwrite_h(mmu_xlate_addr(va, ACC_W), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_w(uint32 va, uint32 val)
|
||||||
|
{
|
||||||
|
pwrite_w(mmu_xlate_addr(va, ACC_W), val);
|
||||||
|
}
|
612
3B2/3b2_mmu.h
Normal file
612
3B2/3b2_mmu.h
Normal file
|
@ -0,0 +1,612 @@
|
||||||
|
/* 3b2_mmu.c: AT&T 3B2 Model 400 MMU (WE32101) Header
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _3B2_MMU_H
|
||||||
|
#define _3B2_MMU_H
|
||||||
|
|
||||||
|
#include "3b2_defs.h"
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
*
|
||||||
|
* Vocabulary
|
||||||
|
* ----------
|
||||||
|
*
|
||||||
|
* PD: Page Descriptor (in main memory)
|
||||||
|
* PDT: Page Descriptor Table (in main memory)
|
||||||
|
* POT: Page Offset. Bits 0-10 of a Paged virtual address.
|
||||||
|
* PSL: Page Select. Bits 11-16 of a Paged virtual address.
|
||||||
|
* SD: Segment Descriptor (in main memory)
|
||||||
|
* SDT: Segment Descriptor Table (in main memory)
|
||||||
|
* SID: Section ID. Bits 30-31 of all virtual addresses
|
||||||
|
* SOT: Segment Offset. Bits 0-16 of a Contiguous virtual address.
|
||||||
|
* SSL: Segment Select. Bits 17-29 of all virtual addresses.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The WE32101 MMU divides the virtual address space into four
|
||||||
|
* Sections with 8K Segments per section. Virtual address bits 30 and
|
||||||
|
* 31 determine the section, bits 17-29 determine the Segment within
|
||||||
|
* the section.
|
||||||
|
*
|
||||||
|
* There are two kinds of address translation: Contiguous Translation
|
||||||
|
* and Paged Translation. Contiguous Translation just uses an offset
|
||||||
|
* (bits 0-16 of the virtual address) into each Segment to find an
|
||||||
|
* address, allowing for 128K bytes per Segment. Paged translation
|
||||||
|
* further break Segments down into 64 Pages of 2K each.
|
||||||
|
*
|
||||||
|
* Details about how to do translation are held in main memory in
|
||||||
|
* Segment Descriptors and Page Descriptors. These are located in
|
||||||
|
* Segment Descriptor Tables and Page Descriptor Tables set up by the
|
||||||
|
* computer before enabling the MMU.
|
||||||
|
*
|
||||||
|
* In addition to details in main memory, the MMU has a small cache
|
||||||
|
* of both Segment Descriptors and Page Descriptors. This is NOT just
|
||||||
|
* used for performance reasons! Various features of the cache,
|
||||||
|
* such as updating R and M bits in Segment and Page Descriptors,
|
||||||
|
* are used by various operating system features.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Virtual Address Fields
|
||||||
|
* ----------------------
|
||||||
|
*
|
||||||
|
* 31 30 29 17 16 0
|
||||||
|
* +-----+-------------------+-----------------------------+
|
||||||
|
* Contig: | SID | SSL | SOT |
|
||||||
|
* +-----+-------------------+-----------------------------+
|
||||||
|
*
|
||||||
|
* 31 30 29 17 16 11 10 0
|
||||||
|
* +-----+-------------------+---------+-------------------+
|
||||||
|
* Paged: | SID | SSL | PSL | POT |
|
||||||
|
* +-----+-------------------+---------+-------------------+
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Segment Descriptor Fields
|
||||||
|
* -------------------------
|
||||||
|
*
|
||||||
|
* 31 24 23 10 9 8 7 6 5 4 3 2 1 0
|
||||||
|
* +-------+---------+-----+---+---+---+---+---+---+---+---+
|
||||||
|
* sd0: | Acc | Max Off | Res | I | V | R | T | $ | C | M | P |
|
||||||
|
* +-------+---------+-----+---+---+---+---+---+---+---+---+
|
||||||
|
*
|
||||||
|
* +-----------------------------------------------+-------+
|
||||||
|
* sd1: | Address (high-order 27 or 29 bits) | Soft |
|
||||||
|
* +-----------------------------------------------+-------+
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Segment Descriptor Cache Entry
|
||||||
|
* ------------------------------
|
||||||
|
*
|
||||||
|
* 31 24 23 10 9 0
|
||||||
|
* +-------+-------------------------+---------------------+
|
||||||
|
* Low: | Acc | Max Off | Tag |
|
||||||
|
* +-------+-------------------------+---------------------+
|
||||||
|
*
|
||||||
|
* 31 5 4 3 2 1 0
|
||||||
|
* +-----------------------------------+---+---+---+---+---+
|
||||||
|
* High: | Address | T | $ | C | M | G |
|
||||||
|
* +-----------------------------------+---+---+---+---+---+
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Page Descriptor Fields
|
||||||
|
* ----------------------
|
||||||
|
*
|
||||||
|
* 31 11 10 8 7 6 5 4 3 2 1 0
|
||||||
|
* +----------------+------+-----+---+---+-----+---+---+---+
|
||||||
|
* | Page Address | Soft | Res | R | W | Res | L | M | P |
|
||||||
|
* +----------------+------+-----+---+---+-----+---+---+---+
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Page Descriptor Cache Entry
|
||||||
|
* ---------------------------
|
||||||
|
*
|
||||||
|
* 31 24 23 16 15 0
|
||||||
|
* +-----+------------------+------------------------------+
|
||||||
|
* Low: | Acc | Res | Tag |
|
||||||
|
* +-----+------------------+------------------------------+
|
||||||
|
*
|
||||||
|
* 31 11 10 7 6 5 4 3 2 1 0
|
||||||
|
* +---------------------+-----+---+---+---+---+---+---+---+
|
||||||
|
* High: | Address | Res | U | R | W | $ | L | M | G |
|
||||||
|
* +---------------------+-----+---+---+---+---+---+---+---+
|
||||||
|
*
|
||||||
|
* "U" is only set in the left cache entry, and indicates
|
||||||
|
* which slot (left or right) was most recently updated.
|
||||||
|
*
|
||||||
|
***********************************************************************/
|
||||||
|
|
||||||
|
#define MMUBASE 0x40000
|
||||||
|
#define MMUSIZE 0x1000
|
||||||
|
|
||||||
|
#define MMU_SRS 0x04 /* Section RAM array size (words) */
|
||||||
|
#define MMU_SDCS 0x20 /* Segment Descriptor Cache H/L array size
|
||||||
|
(words) */
|
||||||
|
#define MMU_PDCS 0x20 /* Page Descriptor Cache H/L array size
|
||||||
|
(words) */
|
||||||
|
|
||||||
|
/* Register address offsets */
|
||||||
|
#define MMU_SDCL 0
|
||||||
|
#define MMU_SDCH 1
|
||||||
|
#define MMU_PDCRL 2
|
||||||
|
#define MMU_PDCRH 3
|
||||||
|
#define MMU_PDCLL 4
|
||||||
|
#define MMU_PDCLH 5
|
||||||
|
#define MMU_SRAMA 6
|
||||||
|
#define MMU_SRAMB 7
|
||||||
|
#define MMU_FC 8
|
||||||
|
#define MMU_FA 9
|
||||||
|
#define MMU_CONF 10
|
||||||
|
#define MMU_VAR 11
|
||||||
|
|
||||||
|
#define MMU_CONF_M (mmu_state.conf & 0x1)
|
||||||
|
#define MMU_CONF_R (mmu_state.conf & 0x2)
|
||||||
|
|
||||||
|
/* Caching */
|
||||||
|
#define NUM_SEC 4u /* Number of memory sections */
|
||||||
|
#define NUM_SDCE 8 /* SD cache entries per section */
|
||||||
|
#define NUM_PDCE 8 /* PD cache entries per section per side (l/r) */
|
||||||
|
#define SET_SIZE 2 /* PDs are held in a 2-way associative set */
|
||||||
|
|
||||||
|
/* Cache Tag for SDs */
|
||||||
|
#define SD_TAG(vaddr) ((vaddr >> 20) & 0x3ff)
|
||||||
|
|
||||||
|
/* Cache Tag for PDs */
|
||||||
|
#define PD_TAG(vaddr) (((vaddr >> 13) & 0xf) | ((vaddr >> 14) & 0xfff0))
|
||||||
|
|
||||||
|
/* Index of entry in the SD cache */
|
||||||
|
#define SD_IDX(vaddr) ((vaddr >> 17) & 7)
|
||||||
|
|
||||||
|
/* Index of entry in the PD cache */
|
||||||
|
#define PD_IDX(vaddr) (((vaddr >> 11) & 3) | ((vaddr >> 15) & 4))
|
||||||
|
|
||||||
|
/* Shift and mask the flag bits for the current CPU mode */
|
||||||
|
#define MMU_PERM(f) ((f >> ((3 - CPU_CM) * 2)) & 3)
|
||||||
|
|
||||||
|
#define ROM_SIZE 0x10000
|
||||||
|
#define BOOT_CODE_SIZE 0x8000
|
||||||
|
|
||||||
|
/* Codes set in the MMU Fault register */
|
||||||
|
#define MMU_F_SDTLEN 0x03
|
||||||
|
#define MMU_F_PW 0x04
|
||||||
|
#define MMU_F_PDTLEN 0x05
|
||||||
|
#define MMU_F_INV_SD 0x06
|
||||||
|
#define MMU_F_SEG_NOT_PRES 0x07
|
||||||
|
#define MMU_F_OTRAP 0x08
|
||||||
|
#define MMU_F_PDT_NOT_PRES 0x09
|
||||||
|
#define MMU_F_PAGE_NOT_PRES 0x0a
|
||||||
|
#define MMU_F_ACC 0x0d
|
||||||
|
#define MMU_F_SEG_OFFSET 0x0e
|
||||||
|
|
||||||
|
/* Pluck out Virtual Address fields */
|
||||||
|
#define SID(va) (((va) >> 30) & 3)
|
||||||
|
#define SSL(va) (((va) >> 17) & 0x1fff)
|
||||||
|
#define SOT(va) (va & 0x1ffff)
|
||||||
|
#define PSL(va) (((va) >> 11) & 0x3f)
|
||||||
|
#define POT(va) (va & 0x7ff)
|
||||||
|
|
||||||
|
/* Get the maximum length of an SSL from SRAMB */
|
||||||
|
#define SRAMB_LEN(va) (mmu_state.sec[SID(va)].len + 1)
|
||||||
|
|
||||||
|
/* Pluck out Segment Descriptor fields */
|
||||||
|
#define SD_PRESENT(sd0) ((sd0) & 1)
|
||||||
|
#define SD_MODIFIED(sd0) (((sd0) >> 1) & 1)
|
||||||
|
#define SD_CONTIG(sd0) (((sd0) >> 2) & 1)
|
||||||
|
#define SD_CACHE(sd0) (((sd0) >> 3) & 1)
|
||||||
|
#define SD_TRAP(sd0) (((sd0) >> 4) & 1)
|
||||||
|
#define SD_REF(sd0) (((sd0) >> 5) & 1)
|
||||||
|
#define SD_VALID(sd0) (((sd0) >> 6) & 1)
|
||||||
|
#define SD_INDIRECT(sd0) (((sd0) >> 7) & 1)
|
||||||
|
#define SD_SEG_ADDR(sd1) ((sd1) & 0xffffffe0)
|
||||||
|
#define SD_MAX_OFF(sd0) (((sd0) >> 10) & 0x3fff)
|
||||||
|
#define SD_ACC(sd0) (((sd0) >> 24) & 0xff)
|
||||||
|
#define SD_R_MASK 0x20
|
||||||
|
#define SD_M_MASK 0x2
|
||||||
|
#define SD_GOOD_MASK 0x1u
|
||||||
|
#define SDCE_TAG(sdcl) ((sdcl) & 0x3ff)
|
||||||
|
|
||||||
|
#define SD_ADDR(va) (mmu_state.sec[SID(va)].addr + (SSL(va) * 8))
|
||||||
|
|
||||||
|
|
||||||
|
/* Convert from sd to sd cache entry */
|
||||||
|
#define SD_TO_SDCL(va,sd0) ((sd0 & 0xfffffc00)|SD_TAG(va))
|
||||||
|
#define SD_TO_SDCH(sd0,sd1) (SD_SEG_ADDR(sd1)|(sd0 & 0x1e)|1)
|
||||||
|
|
||||||
|
/* Note that this is a lossy transform. We will lose the state of the
|
||||||
|
I and R flags, as well as the software flags. We don't need them.
|
||||||
|
The V and P flags can be inferred as set. */
|
||||||
|
|
||||||
|
#define SDCE_TO_SD0(sdch,sdcl) ((sdcl & 0xfffffc00)|0x40|(sdch & 0x1e)|1)
|
||||||
|
#define SDCE_TO_SD1(sdch) (sdch & 0xffffffe0)
|
||||||
|
|
||||||
|
/* Maximum size (in bytes) of a segment */
|
||||||
|
#define MAX_OFFSET(sd0) ((SD_MAX_OFF(sd0) + 1) << 3)
|
||||||
|
|
||||||
|
#define PD_PRESENT(pd) (pd & 1)
|
||||||
|
#define PD_MODIFIED(pd) ((pd >> 1) & 1)
|
||||||
|
#define PD_LAST(pd) ((pd >> 2) & 1)
|
||||||
|
#define PD_WFAULT(pd) ((pd >> 4) & 1)
|
||||||
|
#define PD_REF(pd) ((pd >> 5) & 1)
|
||||||
|
#define PD_ADDR(pd) (pd & 0xfffff800) /* Address portion of PD */
|
||||||
|
#define PD_USED_MASK 0x40
|
||||||
|
#define PD_R_MASK 0x20
|
||||||
|
#define PD_M_MASK 0x2
|
||||||
|
#define PD_GOOD_MASK 0x1u
|
||||||
|
#define PDCXL_TAG(pdcxl) (pdcxl & 0xffff)
|
||||||
|
|
||||||
|
#define PD_LOC(sd1,va) SD_SEG_ADDR(sd1) + (PSL(va) * 4)
|
||||||
|
|
||||||
|
/* Page Descriptor Cache Entry
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Convert from pd to pd cache entry. Alwasy sets "Good" bit. */
|
||||||
|
#define SD_TO_PDCXL(va,sd0) ((sd0 & 0xff000000)|PD_TAG(va))
|
||||||
|
#define PD_TO_PDCXH(pd,sd0) ((pd & 0xfffff836)|(sd0 & 0x8)|1)
|
||||||
|
|
||||||
|
/* Always set 'present' to true on conversion */
|
||||||
|
#define PDCXH_TO_PD(pdch) ((pdch & 0xfffff836)|1)
|
||||||
|
#define PDCXL_TO_ACC(pdcl) (((pdcl & 0xff000000) >> 24) & 0xff)
|
||||||
|
|
||||||
|
#define PDCLH_USED_MASK 0x40u
|
||||||
|
|
||||||
|
/* Fault codes */
|
||||||
|
#define MMU_FAULT(f) { \
|
||||||
|
if (fc) { \
|
||||||
|
mmu_state.fcode = ((((uint32)r_acc)<<7)|(((uint32)(CPU_CM))<<5)|f); \
|
||||||
|
mmu_state.faddr = va; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _mmu_sec {
|
||||||
|
uint32 addr;
|
||||||
|
uint32 len;
|
||||||
|
} mmu_sec;
|
||||||
|
|
||||||
|
typedef struct _mmu_state {
|
||||||
|
t_bool enabled; /* Global enabled/disabled flag */
|
||||||
|
|
||||||
|
uint32 sdcl[MMU_SDCS]; /* SDC low bits (0-31) */
|
||||||
|
uint32 sdch[MMU_SDCS]; /* SDC high bits (32-63) */
|
||||||
|
|
||||||
|
uint32 pdcll[MMU_PDCS]; /* PDC low bits (left) (0-31) */
|
||||||
|
uint32 pdclh[MMU_PDCS]; /* PDC high bits (left) (32-63) */
|
||||||
|
|
||||||
|
uint32 pdcrl[MMU_PDCS]; /* PDC low bits (right) (0-31) */
|
||||||
|
uint32 pdcrh[MMU_PDCS]; /* PDC high bits (right) (32-63) */
|
||||||
|
|
||||||
|
uint32 sra[MMU_SRS]; /* Section RAM A */
|
||||||
|
uint32 srb[MMU_SRS]; /* Section RAM B */
|
||||||
|
|
||||||
|
mmu_sec sec[MMU_SRS]; /* Section descriptors decoded from
|
||||||
|
Section RAM A and B */
|
||||||
|
|
||||||
|
uint32 fcode; /* Fault Code Register */
|
||||||
|
uint32 faddr; /* Fault Address Register */
|
||||||
|
uint32 conf; /* Configuration Register */
|
||||||
|
uint32 var; /* Virtual Address Register */
|
||||||
|
|
||||||
|
} MMU_STATE;
|
||||||
|
|
||||||
|
extern MMU_STATE mmu_state;
|
||||||
|
|
||||||
|
extern volatile int32 stop_reason;
|
||||||
|
extern DEVICE mmu_dev;
|
||||||
|
|
||||||
|
t_stat mmu_init(DEVICE *dptr);
|
||||||
|
uint32 mmu_read(uint32 pa, size_t size);
|
||||||
|
void mmu_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
|
||||||
|
/* Physical memory read/write */
|
||||||
|
uint8 pread_b(uint32 pa);
|
||||||
|
uint16 pread_h(uint32 pa);
|
||||||
|
uint32 pread_w(uint32 pa);
|
||||||
|
uint32 pread_w_u(uint32 pa);
|
||||||
|
void pwrite_b(uint32 pa, uint8 val);
|
||||||
|
void pwrite_h(uint32 pa, uint16 val);
|
||||||
|
void pwrite_w(uint32 pa, uint32 val);
|
||||||
|
|
||||||
|
/* Virtual memory translation */
|
||||||
|
uint32 mmu_xlate_addr(uint32 va, uint8 r_acc);
|
||||||
|
t_stat mmu_decode_vaddr(uint32 vaddr, uint8 r_acc,
|
||||||
|
t_bool fc, uint32 *pa);
|
||||||
|
|
||||||
|
#define SHOULD_CACHE_PD(pd) \
|
||||||
|
(fc && PD_PRESENT(pd))
|
||||||
|
|
||||||
|
#define SHOULD_CACHE_SD(sd) \
|
||||||
|
(fc && SD_VALID(sd) && SD_PRESENT(sd))
|
||||||
|
|
||||||
|
#define SHOULD_UPDATE_SD_R_BIT(sd) \
|
||||||
|
(MMU_CONF_R && !((sd) & SD_R_MASK))
|
||||||
|
|
||||||
|
#define SHOULD_UPDATE_SD_M_BIT(sd) \
|
||||||
|
(MMU_CONF_M && r_acc == ACC_W && !((sd) & SD_M_MASK))
|
||||||
|
|
||||||
|
#define SHOULD_UPDATE_PD_R_BIT(pd) \
|
||||||
|
(!((pd) & PD_R_MASK))
|
||||||
|
|
||||||
|
#define SHOULD_UPDATE_PD_M_BIT(pd) \
|
||||||
|
(r_acc == ACC_W && !((pd) & PD_M_MASK))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find an SD in the cache.
|
||||||
|
*/
|
||||||
|
static SIM_INLINE t_stat get_sdce(uint32 va, uint32 *sd0, uint32 *sd1)
|
||||||
|
{
|
||||||
|
uint32 tag, sdch, sdcl;
|
||||||
|
uint8 ci;
|
||||||
|
|
||||||
|
ci = (SID(va) * NUM_SDCE) + SD_IDX(va);
|
||||||
|
tag = SD_TAG(va);
|
||||||
|
|
||||||
|
sdch = mmu_state.sdch[ci];
|
||||||
|
sdcl = mmu_state.sdcl[ci];
|
||||||
|
|
||||||
|
if ((sdch & SD_GOOD_MASK) && SDCE_TAG(sdcl) == tag) {
|
||||||
|
*sd0 = SDCE_TO_SD0(sdch, sdcl);
|
||||||
|
*sd1 = SDCE_TO_SD1(sdch);
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find a PD in the cache. Sets both the PD and the cached access
|
||||||
|
* permissions.
|
||||||
|
*/
|
||||||
|
static SIM_INLINE t_stat get_pdce(uint32 va, uint32 *pd, uint8 *pd_acc)
|
||||||
|
{
|
||||||
|
uint32 tag, pdcll, pdclh, pdcrl, pdcrh;
|
||||||
|
uint8 ci;
|
||||||
|
|
||||||
|
ci = (SID(va) * NUM_PDCE) + PD_IDX(va);
|
||||||
|
tag = PD_TAG(va);
|
||||||
|
|
||||||
|
/* Left side */
|
||||||
|
pdcll = mmu_state.pdcll[ci];
|
||||||
|
pdclh = mmu_state.pdclh[ci];
|
||||||
|
|
||||||
|
/* Right side */
|
||||||
|
pdcrl = mmu_state.pdcrl[ci];
|
||||||
|
pdcrh = mmu_state.pdcrh[ci];
|
||||||
|
|
||||||
|
/* Search L and R to find a good entry with a matching tag. */
|
||||||
|
if ((pdclh & PD_GOOD_MASK) && PDCXL_TAG(pdcll) == tag) {
|
||||||
|
*pd = PDCXH_TO_PD(pdclh);
|
||||||
|
*pd_acc = PDCXL_TO_ACC(pdcll);
|
||||||
|
return SCPE_OK;
|
||||||
|
} else if ((pdcrh & PD_GOOD_MASK) && PDCXL_TAG(pdcrl) == tag) {
|
||||||
|
*pd = PDCXH_TO_PD(pdcrh);
|
||||||
|
*pd_acc = PDCXL_TO_ACC(pdcrl);
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void put_sdce(uint32 va, uint32 sd0, uint32 sd1)
|
||||||
|
{
|
||||||
|
uint32 tag;
|
||||||
|
uint8 ci;
|
||||||
|
|
||||||
|
tag = SD_TAG(va);
|
||||||
|
ci = (SID(va) * NUM_SDCE) + SD_IDX(va);
|
||||||
|
|
||||||
|
mmu_state.sdcl[ci] = SD_TO_SDCL(va, sd0);
|
||||||
|
mmu_state.sdch[ci] = SD_TO_SDCH(sd0, sd1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static SIM_INLINE void put_pdce(uint32 va, uint32 sd0, uint32 pd)
|
||||||
|
{
|
||||||
|
uint32 tag, pdclh, pdcrh;
|
||||||
|
uint8 ci;
|
||||||
|
|
||||||
|
tag = PD_TAG(va);
|
||||||
|
ci = (SID(va) * NUM_PDCE) + PD_IDX(va);
|
||||||
|
|
||||||
|
/* Left side contains the 'U' bit we care about */
|
||||||
|
pdclh = mmu_state.pdclh[ci];
|
||||||
|
|
||||||
|
/* Right side */
|
||||||
|
pdcrh = mmu_state.pdcrh[ci];
|
||||||
|
|
||||||
|
/* Pick the least-recently-replaced side */
|
||||||
|
if (pdclh & PDCLH_USED_MASK) { /* Right side replaced more recently */
|
||||||
|
/* Add to left side of cache without the U bit set */
|
||||||
|
mmu_state.pdcll[ci] = SD_TO_PDCXL(va, sd0);
|
||||||
|
mmu_state.pdclh[ci] = PD_TO_PDCXH(pd, sd0);
|
||||||
|
mmu_state.pdcll[ci] &= ~PDCLH_USED_MASK;
|
||||||
|
} else { /* Left side replaced more recently */
|
||||||
|
/* Add to right side of cache and set the U bit on the left side */
|
||||||
|
mmu_state.pdcrl[ci] = SD_TO_PDCXL(va, sd0);
|
||||||
|
mmu_state.pdcrh[ci] = PD_TO_PDCXH(pd, sd0);
|
||||||
|
mmu_state.pdcll[ci] |= PDCLH_USED_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void flush_sdce(uint32 va)
|
||||||
|
{
|
||||||
|
uint32 tag;
|
||||||
|
uint8 ci;
|
||||||
|
|
||||||
|
ci = (SID(va) * NUM_SDCE) + SD_IDX(va);
|
||||||
|
tag = SD_TAG(va);
|
||||||
|
|
||||||
|
if (mmu_state.sdch[ci] & SD_GOOD_MASK) {
|
||||||
|
mmu_state.sdch[ci] &= ~SD_GOOD_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void flush_pdce(uint32 va)
|
||||||
|
{
|
||||||
|
uint32 tag, pdcll, pdclh, pdcrl, pdcrh;
|
||||||
|
uint8 ci;
|
||||||
|
|
||||||
|
ci = (SID(va) * NUM_PDCE) + PD_IDX(va);
|
||||||
|
tag = PD_TAG(va);
|
||||||
|
|
||||||
|
/* Left side */
|
||||||
|
pdcll = mmu_state.pdcll[ci];
|
||||||
|
pdclh = mmu_state.pdclh[ci];
|
||||||
|
/* Right side */
|
||||||
|
pdcrl = mmu_state.pdcrl[ci];
|
||||||
|
pdcrh = mmu_state.pdcrh[ci];
|
||||||
|
|
||||||
|
/* Search L and R to find a good entry with a matching tag. */
|
||||||
|
if ((pdclh & PD_GOOD_MASK) && PDCXL_TAG(pdcll) == tag) {
|
||||||
|
mmu_state.pdclh[ci] &= ~PD_GOOD_MASK;
|
||||||
|
} else if ((pdcrh & PD_GOOD_MASK) && PDCXL_TAG(pdcrl) == tag) {
|
||||||
|
mmu_state.pdcrh[ci] &= ~PD_GOOD_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void flush_cache_sec(uint8 sec)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MMU_SDCS; i++) {
|
||||||
|
mmu_state.sdch[(sec * NUM_SDCE) + i] &= ~SD_GOOD_MASK;
|
||||||
|
}
|
||||||
|
for (i = 0; i < MMU_PDCS; i++) {
|
||||||
|
mmu_state.pdclh[(sec * NUM_PDCE) + i] |= PD_USED_MASK;
|
||||||
|
mmu_state.pdclh[(sec * NUM_PDCE) + i] &= ~PD_GOOD_MASK;
|
||||||
|
mmu_state.pdcrh[(sec * NUM_PDCE) + i] &= ~PD_GOOD_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE void flush_caches()
|
||||||
|
{
|
||||||
|
uint8 i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_SEC; i++) {
|
||||||
|
flush_cache_sec(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIM_INLINE t_stat mmu_check_perm(uint8 flags, uint8 r_acc)
|
||||||
|
{
|
||||||
|
switch(MMU_PERM(flags)) {
|
||||||
|
case 0: /* No Access */
|
||||||
|
return SCPE_NXM;
|
||||||
|
case 1: /* Exec Only */
|
||||||
|
if (r_acc != ACC_IF && r_acc != ACC_IFAD) {
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
return SCPE_OK;
|
||||||
|
case 2: /* Read / Execute */
|
||||||
|
if (r_acc != ACC_AF && r_acc != ACC_OF &&
|
||||||
|
r_acc != ACC_IF && r_acc != ACC_IFAD &&
|
||||||
|
r_acc != ACC_MT) {
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
return SCPE_OK;
|
||||||
|
default:
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the M (modified) or R (referenced) bit the SD and cache
|
||||||
|
*/
|
||||||
|
static SIM_INLINE void mmu_update_sd(uint32 va, uint32 mask)
|
||||||
|
{
|
||||||
|
uint32 sd0, tag;
|
||||||
|
uint8 ci;
|
||||||
|
|
||||||
|
tag = SD_TAG(va);
|
||||||
|
ci = (SID(va) * NUM_SDCE) + SD_IDX(va);
|
||||||
|
|
||||||
|
/* We go back to main memory to find the SD because the SD may
|
||||||
|
have been loaded from cache, which is lossy. */
|
||||||
|
sd0 = pread_w(SD_ADDR(va));
|
||||||
|
pwrite_w(SD_ADDR(va), sd0|mask);
|
||||||
|
|
||||||
|
/* There is no 'R' bit in the SD cache, only an 'M' bit. */
|
||||||
|
if (mask == SD_M_MASK) {
|
||||||
|
mmu_state.sdch[ci] |= mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the M (modified) or R (referenced) bit the PD and cache
|
||||||
|
*/
|
||||||
|
static SIM_INLINE void mmu_update_pd(uint32 va, uint32 pd_addr, uint32 mask)
|
||||||
|
{
|
||||||
|
uint32 pd, tag, pdcll, pdclh, pdcrl, pdcrh;
|
||||||
|
uint8 ci;
|
||||||
|
|
||||||
|
tag = PD_TAG(va);
|
||||||
|
ci = (SID(va) * NUM_PDCE) + PD_IDX(va);
|
||||||
|
|
||||||
|
/* We go back to main memory to find the PD because the PD may
|
||||||
|
have been loaded from cache, which is lossy. */
|
||||||
|
pd = pread_w(pd_addr);
|
||||||
|
pwrite_w(pd_addr, pd|mask);
|
||||||
|
|
||||||
|
/* Update in the cache */
|
||||||
|
|
||||||
|
/* Left side */
|
||||||
|
pdcll = mmu_state.pdcll[ci];
|
||||||
|
pdclh = mmu_state.pdclh[ci];
|
||||||
|
|
||||||
|
/* Right side */
|
||||||
|
pdcrl = mmu_state.pdcrl[ci];
|
||||||
|
pdcrh = mmu_state.pdcrh[ci];
|
||||||
|
|
||||||
|
/* Search L and R to find a good entry with a matching tag, then
|
||||||
|
update the appropriate bit */
|
||||||
|
if ((pdclh & PD_GOOD_MASK) && PDCXL_TAG(pdcll) == tag) {
|
||||||
|
mmu_state.pdclh[ci] |= mask;
|
||||||
|
} else if ((pdcrh & PD_GOOD_MASK) && PDCXL_TAG(pdcrl) == tag) {
|
||||||
|
mmu_state.pdcrh[ci] |= mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special functions for reading operands and examining memory
|
||||||
|
safely */
|
||||||
|
t_stat read_operand(uint32 va, uint8 *val);
|
||||||
|
t_stat examine(uint32 va, uint8 *val);
|
||||||
|
t_stat deposit(uint32 va, uint8 val);
|
||||||
|
|
||||||
|
/* Dispatch to the MMU when enabled, or to physical RW when
|
||||||
|
disabled */
|
||||||
|
uint8 read_b(uint32 va, uint8 r_acc);
|
||||||
|
uint16 read_h(uint32 va, uint8 r_acc);
|
||||||
|
uint32 read_w(uint32 va, uint8 r_acc);
|
||||||
|
void write_b(uint32 va, uint8 val);
|
||||||
|
void write_h(uint32 va, uint16 val);
|
||||||
|
void write_w(uint32 va, uint32 val);
|
||||||
|
|
||||||
|
t_bool addr_is_rom(uint32 pa);
|
||||||
|
t_bool addr_is_mem(uint32 pa);
|
||||||
|
t_bool addr_is_io(uint32 pa);
|
||||||
|
|
||||||
|
#endif
|
180
3B2/3b2_sys.c
Normal file
180
3B2/3b2_sys.c
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
/* 3b2_defs.h: AT&T 3B2 Model 400 system-specific logic implementation
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "3b2_sys.h"
|
||||||
|
|
||||||
|
#include "3b2_cpu.h"
|
||||||
|
#include "3b2_iu.h"
|
||||||
|
#include "3b2_if.h"
|
||||||
|
#include "3b2_id.h"
|
||||||
|
#include "3b2_mmu.h"
|
||||||
|
#include "3b2_sysdev.h"
|
||||||
|
|
||||||
|
char sim_name[] = "AT&T 3B2 Model 400";
|
||||||
|
|
||||||
|
REG *sim_PC = &cpu_reg[0];
|
||||||
|
|
||||||
|
/* All opcodes are 1 or 2 bytes. Operands may be up to 6 bytes, and
|
||||||
|
there may be up to 3 operands, for a maximum of 20 bytes */
|
||||||
|
int32 sim_emax = 20;
|
||||||
|
|
||||||
|
extern instr cpu_instr;
|
||||||
|
|
||||||
|
DEVICE *sim_devices[] = {
|
||||||
|
&cpu_dev,
|
||||||
|
&mmu_dev,
|
||||||
|
&timer_dev,
|
||||||
|
&tod_dev,
|
||||||
|
&nvram_dev,
|
||||||
|
&csr_dev,
|
||||||
|
&iu_dev,
|
||||||
|
&dmac_dev,
|
||||||
|
&if_dev,
|
||||||
|
&id_dev,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *sim_stop_messages[] = {
|
||||||
|
"Unknown error",
|
||||||
|
"Reserved Instruction",
|
||||||
|
"Breakpoint",
|
||||||
|
"Invalid Opcode",
|
||||||
|
"IRQ",
|
||||||
|
"Exception/Trap",
|
||||||
|
"Exception Stack Too Deep",
|
||||||
|
"Unimplemented MMU Feature"
|
||||||
|
};
|
||||||
|
|
||||||
|
t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
|
||||||
|
{
|
||||||
|
int32 i;
|
||||||
|
uint32 addr = 0;
|
||||||
|
int32 cnt = 0;
|
||||||
|
|
||||||
|
if ((*cptr != 0) || (flag != 0)) {
|
||||||
|
return SCPE_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = R[NUM_PC];
|
||||||
|
|
||||||
|
while ((i = getc (fileref)) != EOF) {
|
||||||
|
pwrite_b(addr, (uint8)i);
|
||||||
|
addr++;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf ("%d Bytes loaded.\n", cnt);
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat parse_sym(CONST char *cptr, t_addr exta, UNIT *uptr, t_value *val, int32 sw)
|
||||||
|
{
|
||||||
|
DEVICE *dptr;
|
||||||
|
t_stat r;
|
||||||
|
int32 k, num, vp;
|
||||||
|
int32 len = 4;
|
||||||
|
|
||||||
|
if (sw & (int32) SWMASK ('B')) {
|
||||||
|
len = 1;
|
||||||
|
} else if (sw & (int32) SWMASK ('H')) {
|
||||||
|
len = 2;
|
||||||
|
} else if (sw & (int32) SWMASK ('W')) {
|
||||||
|
len = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse cptr
|
||||||
|
num = (int32) get_uint(cptr, 16, WORD_MASK, &r);
|
||||||
|
|
||||||
|
if (r != SCPE_OK) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uptr == NULL) {
|
||||||
|
uptr = &cpu_unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
dptr = find_dev_from_unit(uptr);
|
||||||
|
|
||||||
|
if (dptr == NULL) {
|
||||||
|
return SCPE_IERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
vp = 0;
|
||||||
|
for (k = len - 1; k >= 0; k--) {
|
||||||
|
val[vp++] = (num >> (k * 8)) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -(vp - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw)
|
||||||
|
{
|
||||||
|
uint32 len = 4;
|
||||||
|
int32 k, vp, num;
|
||||||
|
unsigned int c;
|
||||||
|
|
||||||
|
num = 0;
|
||||||
|
vp = 0;
|
||||||
|
|
||||||
|
if (sw & (int32) SWMASK ('B')) {
|
||||||
|
len = 1;
|
||||||
|
} else if (sw & (int32) SWMASK ('H')) {
|
||||||
|
len = 2;
|
||||||
|
} else if (sw & (int32) SWMASK ('W')) {
|
||||||
|
len = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sw & (int32) SWMASK('M')) {
|
||||||
|
fprint_sym_m(of, &cpu_instr);
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sw & (int32) SWMASK('C')) {
|
||||||
|
len = 16;
|
||||||
|
for (k = (int32) len - 1; k >= 0; k--) {
|
||||||
|
c = (unsigned int)val[vp++];
|
||||||
|
if (c >= 0x20 && c < 0x7f) {
|
||||||
|
fprintf(of, "%c", c);
|
||||||
|
} else {
|
||||||
|
fprintf(of, ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -(vp - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k = len - 1; k >= 0; k--) {
|
||||||
|
num = num | (((int32) val[vp++]) << (k * 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
fprint_val(of, (uint32) num, 16, len * 8, PV_RZRO);
|
||||||
|
|
||||||
|
return -(vp - 1);
|
||||||
|
}
|
47
3B2/3b2_sys.h
Normal file
47
3B2/3b2_sys.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/* 3b2_sys.h: AT&T 3B2 Model 400 system-specific logic headers
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _3B2_SYS_H
|
||||||
|
#define _3B2_SYS_H
|
||||||
|
|
||||||
|
#include "3b2_defs.h"
|
||||||
|
|
||||||
|
extern uint32 R[16];
|
||||||
|
extern char sim_name[];
|
||||||
|
extern REG *sim_PC;
|
||||||
|
extern int32 sim_emax;
|
||||||
|
extern DEVICE *sim_devices[];
|
||||||
|
|
||||||
|
t_stat sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag);
|
||||||
|
t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val,
|
||||||
|
int32 sw);
|
||||||
|
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw);
|
||||||
|
|
||||||
|
#endif
|
711
3B2/3b2_sysdev.c
Normal file
711
3B2/3b2_sysdev.c
Normal file
|
@ -0,0 +1,711 @@
|
||||||
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 System Devices implementation
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file contains system-specific registers and devices for the
|
||||||
|
following 3B2 devices:
|
||||||
|
|
||||||
|
- timer 8253 interval timer
|
||||||
|
- nvram Non-Volatile RAM
|
||||||
|
- csr Control Status Registers
|
||||||
|
- tod MM58174A Real-Time-Clock
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "3b2_sysdev.h"
|
||||||
|
#include "3b2_iu.h"
|
||||||
|
|
||||||
|
DEBTAB sys_deb_tab[] = {
|
||||||
|
{ "INIT", INIT_MSG, "Init" },
|
||||||
|
{ "READ", READ_MSG, "Read activity" },
|
||||||
|
{ "WRITE", WRITE_MSG, "Write activity" },
|
||||||
|
{ "EXECUTE", EXECUTE_MSG, "Execute activity" },
|
||||||
|
{ "IRQ", IRQ_MSG, "Interrupt activity"},
|
||||||
|
{ "TRACE", TRACE_MSG, "Detailed activity" },
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32 *NVRAM = NULL;
|
||||||
|
|
||||||
|
extern DEVICE cpu_dev;
|
||||||
|
|
||||||
|
/* CSR */
|
||||||
|
|
||||||
|
uint16 csr_data;
|
||||||
|
|
||||||
|
BITFIELD csr_bits[] = {
|
||||||
|
BIT(IOF),
|
||||||
|
BIT(DMA),
|
||||||
|
BIT(DISK),
|
||||||
|
BIT(UART),
|
||||||
|
BIT(PIR9),
|
||||||
|
BIT(PIR8),
|
||||||
|
BIT(CLK),
|
||||||
|
BIT(IFLT),
|
||||||
|
BIT(ITIM),
|
||||||
|
BIT(FLOP),
|
||||||
|
BIT(NA),
|
||||||
|
BIT(LED),
|
||||||
|
BIT(ALGN),
|
||||||
|
BIT(RRST),
|
||||||
|
BIT(PARE),
|
||||||
|
BIT(TIMO),
|
||||||
|
ENDBITS
|
||||||
|
};
|
||||||
|
|
||||||
|
UNIT csr_unit = {
|
||||||
|
UDATA(&csr_svc, UNIT_FIX, CSRSIZE)
|
||||||
|
};
|
||||||
|
|
||||||
|
REG csr_reg[] = {
|
||||||
|
{ HRDATADF(DATA, csr_data, 16, "CSR Data", csr_bits) }
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE csr_dev = {
|
||||||
|
"CSR", &csr_unit, csr_reg, NULL,
|
||||||
|
1, 16, 8, 4, 16, 32,
|
||||||
|
&csr_ex, &csr_dep, &csr_reset,
|
||||||
|
NULL, NULL, NULL, NULL,
|
||||||
|
DEV_DEBUG, 0, sys_deb_tab
|
||||||
|
};
|
||||||
|
|
||||||
|
t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw)
|
||||||
|
{
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw)
|
||||||
|
{
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat csr_reset(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
csr_data = 0;
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 csr_read(uint32 pa, size_t size)
|
||||||
|
{
|
||||||
|
uint32 reg = pa - CSRBASE;
|
||||||
|
|
||||||
|
sim_debug(READ_MSG, &csr_dev,
|
||||||
|
"[%08x] CSR=%04x\n",
|
||||||
|
R[NUM_PC], csr_data);
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case 0x2:
|
||||||
|
if (size == 8) {
|
||||||
|
return (csr_data >> 8) & 0xff;
|
||||||
|
} else {
|
||||||
|
return csr_data;
|
||||||
|
}
|
||||||
|
case 0x3:
|
||||||
|
return csr_data & 0xff;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Remove once we confirm we don't need it */
|
||||||
|
t_stat csr_svc(UNIT *uptr)
|
||||||
|
{
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void csr_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
uint32 reg = pa - CSRBASE;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case 0x03: /* Clear Bus Timeout Error */
|
||||||
|
csr_data &= ~CSRTIMO;
|
||||||
|
break;
|
||||||
|
case 0x07: /* Clear Memory Parity Error */
|
||||||
|
csr_data &= ~CSRPARE;
|
||||||
|
break;
|
||||||
|
case 0x0b: /* Set System Reset Request */
|
||||||
|
iu_reset(&iu_dev);
|
||||||
|
cpu_reset(&cpu_dev);
|
||||||
|
cpu_boot(0, &cpu_dev);
|
||||||
|
break;
|
||||||
|
case 0x0f: /* Clear Memory Alignment Fault */
|
||||||
|
csr_data &= ~CSRALGN;
|
||||||
|
break;
|
||||||
|
case 0x13: /* Set Failure LED */
|
||||||
|
csr_data |= CSRLED;
|
||||||
|
break;
|
||||||
|
case 0x17: /* Clear Failure LED */
|
||||||
|
csr_data &= ~CSRLED;
|
||||||
|
break;
|
||||||
|
case 0x1b: /* Set Floppy Motor On */
|
||||||
|
csr_data |= CSRFLOP;
|
||||||
|
break;
|
||||||
|
case 0x1f: /* Clear Floppy Motor On */
|
||||||
|
csr_data &= ~CSRFLOP;
|
||||||
|
break;
|
||||||
|
case 0x23: /* Set Inhibit Timers */
|
||||||
|
csr_data |= CSRITIM;
|
||||||
|
break;
|
||||||
|
case 0x27: /* Clear Inhibit Timers */
|
||||||
|
csr_data &= ~CSRITIM;
|
||||||
|
break;
|
||||||
|
case 0x2b: /* Set Inhibit Faults */
|
||||||
|
csr_data |= CSRIFLT;
|
||||||
|
break;
|
||||||
|
case 0x2f: /* Clear Inhibit Faults */
|
||||||
|
csr_data &= ~CSRIFLT;
|
||||||
|
break;
|
||||||
|
case 0x33: /* Set PIR9 */
|
||||||
|
csr_data |= CSRPIR9;
|
||||||
|
break;
|
||||||
|
case 0x37: /* Clear PIR9 */
|
||||||
|
csr_data &= ~CSRPIR9;
|
||||||
|
break;
|
||||||
|
case 0x3b: /* Set PIR8 */
|
||||||
|
csr_data |= CSRPIR8;
|
||||||
|
break;
|
||||||
|
case 0x3f: /* Clear PIR8 */
|
||||||
|
csr_data &= ~CSRPIR8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NVRAM */
|
||||||
|
|
||||||
|
UNIT nvram_unit = {
|
||||||
|
UDATA(NULL, UNIT_FIX+UNIT_BINK, NVRAMSIZE)
|
||||||
|
};
|
||||||
|
|
||||||
|
REG nvram_reg[] = {
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE nvram_dev = {
|
||||||
|
"NVRAM", &nvram_unit, nvram_reg, NULL,
|
||||||
|
1, 16, 8, 4, 16, 32,
|
||||||
|
&nvram_ex, &nvram_dep, &nvram_reset,
|
||||||
|
NULL, &nvram_attach, &nvram_detach,
|
||||||
|
NULL, DEV_DEBUG, 0, sys_deb_tab, NULL, NULL,
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
&nvram_description
|
||||||
|
};
|
||||||
|
|
||||||
|
t_stat nvram_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw)
|
||||||
|
{
|
||||||
|
uint32 addr = (uint32) exta;
|
||||||
|
|
||||||
|
if ((vptr == NULL) || (addr & 03)) {
|
||||||
|
return SCPE_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr >= NVRAMSIZE) {
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*vptr = NVRAM[addr >> 2];
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat nvram_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw)
|
||||||
|
{
|
||||||
|
uint32 addr = (uint32) exta;
|
||||||
|
|
||||||
|
if (addr & 03) {
|
||||||
|
return SCPE_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr >= NVRAMSIZE) {
|
||||||
|
return SCPE_NXM;
|
||||||
|
}
|
||||||
|
|
||||||
|
NVRAM[addr >> 2] = (uint32) val;
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat nvram_reset(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
if (NVRAM == NULL) {
|
||||||
|
NVRAM = (uint32 *)calloc(NVRAMSIZE >> 2, sizeof(uint32));
|
||||||
|
memset(NVRAM, 0, sizeof(uint32) * NVRAMSIZE >> 2);
|
||||||
|
nvram_unit.filebuf = NVRAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NVRAM == NULL) {
|
||||||
|
return SCPE_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *nvram_description(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
return "Non-volatile memory";
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat nvram_attach(UNIT *uptr, CONST char *cptr)
|
||||||
|
{
|
||||||
|
t_stat r;
|
||||||
|
|
||||||
|
/* If we've been asked to attach, make sure the ATTABLE
|
||||||
|
and BUFABLE flags are set on the unit */
|
||||||
|
uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE);
|
||||||
|
|
||||||
|
r = attach_unit(uptr, cptr);
|
||||||
|
|
||||||
|
if (r != SCPE_OK) {
|
||||||
|
/* Unset the ATTABLE and BUFABLE flags if we failed. */
|
||||||
|
uptr->flags = uptr->flags & (uint32) ~(UNIT_ATTABLE | UNIT_BUFABLE);
|
||||||
|
} else {
|
||||||
|
uptr->hwmark = (uint32) uptr->capac;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat nvram_detach(UNIT *uptr)
|
||||||
|
{
|
||||||
|
t_stat r;
|
||||||
|
|
||||||
|
r = detach_unit(uptr);
|
||||||
|
|
||||||
|
if ((uptr->flags & UNIT_ATT) == 0) {
|
||||||
|
uptr->flags = uptr->flags & (uint32) ~(UNIT_ATTABLE | UNIT_BUFABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32 nvram_read(uint32 pa, size_t size)
|
||||||
|
{
|
||||||
|
uint32 offset = pa - NVRAMBASE;
|
||||||
|
uint32 data;
|
||||||
|
uint32 sc = (~(offset & 3) << 3) & 0x1f;
|
||||||
|
|
||||||
|
switch(size) {
|
||||||
|
case 8:
|
||||||
|
data = (NVRAM[offset >> 2] >> sc) & BYTE_MASK;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
if (offset & 2) {
|
||||||
|
data = NVRAM[offset >> 2] & HALF_MASK;
|
||||||
|
} else {
|
||||||
|
data = (NVRAM[offset >> 2] >> 16) & HALF_MASK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
data = NVRAM[offset >> 2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nvram_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
uint32 offset = pa - NVRAMBASE;
|
||||||
|
uint32 index = offset >> 2;
|
||||||
|
uint32 sc, mask;
|
||||||
|
|
||||||
|
switch(size) {
|
||||||
|
case 8:
|
||||||
|
sc = (~(pa & 3) << 3) & 0x1f;
|
||||||
|
mask = (uint32) (0xff << sc);
|
||||||
|
NVRAM[index] = (NVRAM[index] & ~mask) | (val << sc);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
if (offset & 2) {
|
||||||
|
NVRAM[index] = (NVRAM[index] & ~HALF_MASK) | val;
|
||||||
|
} else {
|
||||||
|
NVRAM[index] = (NVRAM[index] & HALF_MASK) | (val << 16);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
NVRAM[index] = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 8253 Timer.
|
||||||
|
*
|
||||||
|
* The 8253 Timer IC has three interval timers, which we treat here as
|
||||||
|
* three units.
|
||||||
|
*
|
||||||
|
* Note that this simulation is very specific to the 3B2, and not
|
||||||
|
* usable as a general purpose 8253 simulator.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct timer_ctr TIMERS[3];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The three timers, (A, B, C) run at different
|
||||||
|
* programmatially controlled frequencies, so each must be
|
||||||
|
* handled through a different service routine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
UNIT timer_unit[] = {
|
||||||
|
{ UDATA(&timer0_svc, 0, 0) },
|
||||||
|
{ UDATA(&timer1_svc, UNIT_IDLE, 0) },
|
||||||
|
{ UDATA(&timer2_svc, 0, 0) },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
REG timer_reg[] = {
|
||||||
|
{ HRDATAD(DIVA, TIMERS[0].divider, 16, "Divider A") },
|
||||||
|
{ HRDATAD(STA, TIMERS[0].mode, 16, "Mode A") },
|
||||||
|
{ HRDATAD(DIVB, TIMERS[1].divider, 16, "Divider B") },
|
||||||
|
{ HRDATAD(STB, TIMERS[1].mode, 16, "Mode B") },
|
||||||
|
{ HRDATAD(DIVC, TIMERS[2].divider, 16, "Divider C") },
|
||||||
|
{ HRDATAD(STC, TIMERS[2].mode, 16, "Mode C") },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE timer_dev = {
|
||||||
|
"TIMER", timer_unit, timer_reg, NULL,
|
||||||
|
1, 16, 8, 4, 16, 32,
|
||||||
|
NULL, NULL, &timer_reset,
|
||||||
|
NULL, NULL, NULL, NULL,
|
||||||
|
DEV_DEBUG, 0, sys_deb_tab
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TIMER_STP_US 10 /* 10 us delay per timer step */
|
||||||
|
|
||||||
|
#define tmrnum u3
|
||||||
|
#define tmr up7
|
||||||
|
#define DECR_STEPS 400
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a hack to make diagnostics pass. If read immediately after
|
||||||
|
* being set, a counter should always return the initial value. If a
|
||||||
|
* certain number of steps have passed, it should have decremented a
|
||||||
|
* little bit, so we return a value one less than the initial value.
|
||||||
|
* This is not 100% accurate, but it makes SVR3 and DGMON tests happy.
|
||||||
|
*/
|
||||||
|
static SIM_INLINE uint16 timer_current_val(struct timer_ctr *ctr)
|
||||||
|
{
|
||||||
|
if ((sim_gtime() - ctr->stime) > DECR_STEPS) {
|
||||||
|
return ctr->divider - 1;
|
||||||
|
} else {
|
||||||
|
return ctr->divider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat timer_reset(DEVICE *dptr) {
|
||||||
|
int32 i, t;
|
||||||
|
|
||||||
|
memset(&TIMERS, 0, sizeof(struct timer_ctr) * 3);
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
timer_unit[i].tmrnum = i;
|
||||||
|
timer_unit[i].tmr = &TIMERS[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Timer 1 gate is always active */
|
||||||
|
TIMERS[1].gate = 1;
|
||||||
|
|
||||||
|
if (!sim_is_running) {
|
||||||
|
t = sim_rtcn_init_unit(&timer_unit[1], TPS_CLK, TMR_CLK);
|
||||||
|
sim_activate_after(&timer_unit[1], 1000000 / t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat timer0_svc(UNIT *uptr)
|
||||||
|
{
|
||||||
|
struct timer_ctr *ctr;
|
||||||
|
int32 time;
|
||||||
|
|
||||||
|
ctr = (struct timer_ctr *)uptr->tmr;
|
||||||
|
|
||||||
|
time = ctr->divider * TIMER_STP_US;
|
||||||
|
|
||||||
|
if (time == 0) {
|
||||||
|
time = TIMER_STP_US;
|
||||||
|
}
|
||||||
|
|
||||||
|
sim_activate_abs(uptr, (int32) DELAY_US(time));
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat timer1_svc(UNIT *uptr)
|
||||||
|
{
|
||||||
|
struct timer_ctr *ctr;
|
||||||
|
int32 t, ticks;
|
||||||
|
|
||||||
|
ctr = (struct timer_ctr *)uptr->tmr;
|
||||||
|
|
||||||
|
if (ctr->enabled && !(csr_data & CSRITIM)) {
|
||||||
|
/* Fire the IPL 15 clock interrupt */
|
||||||
|
csr_data |= CSRCLK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ticks = ctr->divider / TIMER_STP_US;
|
||||||
|
if (ticks == 0) {
|
||||||
|
ticks = TPS_CLK;
|
||||||
|
}
|
||||||
|
t = sim_rtcn_calb(ticks, TMR_CLK);
|
||||||
|
sim_activate_after(uptr, (uint32) (1000000 / ticks));
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat timer2_svc(UNIT *uptr)
|
||||||
|
{
|
||||||
|
struct timer_ctr *ctr;
|
||||||
|
int32 time;
|
||||||
|
|
||||||
|
ctr = (struct timer_ctr *)uptr->tmr;
|
||||||
|
|
||||||
|
time = ctr->divider * TIMER_STP_US;
|
||||||
|
|
||||||
|
if (time == 0) {
|
||||||
|
time = TIMER_STP_US;
|
||||||
|
}
|
||||||
|
|
||||||
|
sim_activate_abs(uptr, (int32) DELAY_US(time));
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 timer_read(uint32 pa, size_t size)
|
||||||
|
{
|
||||||
|
uint32 reg;
|
||||||
|
uint16 ctr_val;
|
||||||
|
uint8 ctrnum;
|
||||||
|
struct timer_ctr *ctr;
|
||||||
|
|
||||||
|
reg = pa - TIMERBASE;
|
||||||
|
ctrnum = (reg >> 2) & 0x3;
|
||||||
|
ctr = &TIMERS[ctrnum];
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case TIMER_REG_DIVA:
|
||||||
|
case TIMER_REG_DIVB:
|
||||||
|
case TIMER_REG_DIVC:
|
||||||
|
if (ctr->enabled && ctr->gate) {
|
||||||
|
ctr_val = timer_current_val(ctr);
|
||||||
|
} else {
|
||||||
|
ctr_val = ctr->divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ctr->mode & CLK_RW) {
|
||||||
|
case CLK_LSB:
|
||||||
|
return ctr_val & 0xff;
|
||||||
|
case CLK_MSB:
|
||||||
|
return (ctr_val & 0xff00) >> 8;
|
||||||
|
case CLK_LMB:
|
||||||
|
if (ctr->lmb) {
|
||||||
|
ctr->lmb = FALSE;
|
||||||
|
return (ctr_val & 0xff00) >> 8;
|
||||||
|
} else {
|
||||||
|
ctr->lmb = TRUE;
|
||||||
|
return ctr_val & 0xff;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_REG_CTRL:
|
||||||
|
return ctr->mode;
|
||||||
|
case TIMER_CLR_LATCH:
|
||||||
|
/* Clearing the timer latch has a side-effect
|
||||||
|
of also clearing pending interrupts */
|
||||||
|
csr_data &= ~CSRCLK;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
/* Unhandled */
|
||||||
|
sim_debug(READ_MSG, &timer_dev,
|
||||||
|
"[%08x] UNHANDLED TIMER READ. ADDR=%08x\n",
|
||||||
|
R[NUM_PC], pa);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_timer_write(uint8 ctrnum, uint32 val)
|
||||||
|
{
|
||||||
|
struct timer_ctr *ctr;
|
||||||
|
|
||||||
|
ctr = &TIMERS[ctrnum];
|
||||||
|
switch(ctr->mode & 0x30) {
|
||||||
|
case 0x10:
|
||||||
|
ctr->divider &= 0xff00;
|
||||||
|
ctr->divider |= val & 0xff;
|
||||||
|
ctr->enabled = TRUE;
|
||||||
|
ctr->stime = sim_gtime();
|
||||||
|
break;
|
||||||
|
case 0x20:
|
||||||
|
ctr->divider &= 0x00ff;
|
||||||
|
ctr->divider |= (val & 0xff) << 8;
|
||||||
|
ctr->enabled = TRUE;
|
||||||
|
ctr->stime = sim_gtime();
|
||||||
|
break;
|
||||||
|
case 0x30:
|
||||||
|
if (ctr->lmb) {
|
||||||
|
ctr->lmb = FALSE;
|
||||||
|
ctr->divider = (uint16) ((ctr->divider & 0x00ff) | ((val & 0xff) << 8));
|
||||||
|
ctr->enabled = TRUE;
|
||||||
|
ctr->stime = sim_gtime();
|
||||||
|
} else {
|
||||||
|
ctr->lmb = TRUE;
|
||||||
|
ctr->divider = (ctr->divider & 0xff00) | (val & 0xff);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
uint8 reg, ctrnum;
|
||||||
|
struct timer_ctr *ctr;
|
||||||
|
|
||||||
|
reg = (uint8) (pa - TIMERBASE);
|
||||||
|
|
||||||
|
switch(reg) {
|
||||||
|
case TIMER_REG_DIVA:
|
||||||
|
handle_timer_write(0, val);
|
||||||
|
break;
|
||||||
|
case TIMER_REG_DIVB:
|
||||||
|
handle_timer_write(1, val);
|
||||||
|
break;
|
||||||
|
case TIMER_REG_DIVC:
|
||||||
|
handle_timer_write(2, val);
|
||||||
|
break;
|
||||||
|
case TIMER_REG_CTRL:
|
||||||
|
/* The counter number is in bits 6 and 7 */
|
||||||
|
ctrnum = (val >> 6) & 3;
|
||||||
|
if (ctrnum > 2) {
|
||||||
|
sim_debug(WRITE_MSG, &timer_dev,
|
||||||
|
"[%08x] WARNING: Write to invalid counter: %d\n",
|
||||||
|
R[NUM_PC], ctrnum);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctr = &TIMERS[ctrnum];
|
||||||
|
ctr->mode = (uint8) val;
|
||||||
|
ctr->enabled = FALSE;
|
||||||
|
ctr->lmb = FALSE;
|
||||||
|
break;
|
||||||
|
case TIMER_CLR_LATCH:
|
||||||
|
sim_debug(WRITE_MSG, &timer_dev,
|
||||||
|
"[%08x] unexpected write to clear timer latch\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MM58174A Real-Time-Clock
|
||||||
|
*/
|
||||||
|
|
||||||
|
UNIT tod_unit = { UDATA(&tod_svc, UNIT_IDLE+UNIT_FIX, 0) };
|
||||||
|
|
||||||
|
uint32 tod_reg = 0;
|
||||||
|
|
||||||
|
DEVICE tod_dev = {
|
||||||
|
"TOD", &tod_unit, NULL, NULL,
|
||||||
|
1, 16, 8, 4, 16, 32,
|
||||||
|
NULL, NULL, &tod_reset,
|
||||||
|
NULL, NULL, NULL, NULL,
|
||||||
|
DEV_DEBUG, 0, sys_deb_tab
|
||||||
|
};
|
||||||
|
|
||||||
|
t_stat tod_reset(DEVICE *dptr)
|
||||||
|
{
|
||||||
|
int32 t;
|
||||||
|
|
||||||
|
if (!sim_is_running) {
|
||||||
|
t = sim_rtcn_init_unit(&tod_unit, TPS_TOD, TMR_TOD);
|
||||||
|
sim_activate_after(&tod_unit, 1000000 / TPS_TOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat tod_svc(UNIT *uptr)
|
||||||
|
{
|
||||||
|
int32 t;
|
||||||
|
|
||||||
|
t = sim_rtcn_calb(TPS_TOD, TMR_TOD);
|
||||||
|
sim_activate_after(&tod_unit, 1000000 / TPS_TOD);
|
||||||
|
|
||||||
|
tod_reg++;
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 tod_read(uint32 pa, size_t size)
|
||||||
|
{
|
||||||
|
uint32 reg;
|
||||||
|
|
||||||
|
reg = pa - TODBASE;
|
||||||
|
|
||||||
|
sim_debug(READ_MSG, &tod_dev,
|
||||||
|
"[%08x] READ TOD: reg=%02x\n",
|
||||||
|
R[NUM_PC], reg);
|
||||||
|
|
||||||
|
switch(reg) {
|
||||||
|
case 0x04: /* 1/10 Sec */
|
||||||
|
case 0x08: /* 1 Sec */
|
||||||
|
case 0x0c: /* 10 Sec */
|
||||||
|
case 0x10: /* 1 Min */
|
||||||
|
case 0x14: /* 10 Min */
|
||||||
|
case 0x18: /* 1 Hour */
|
||||||
|
case 0x1c: /* 10 Hour */
|
||||||
|
case 0x20: /* 1 Day */
|
||||||
|
case 0x24: /* 10 Day */
|
||||||
|
case 0x28: /* Day of Week */
|
||||||
|
case 0x2c: /* 1 Month */
|
||||||
|
case 0x30: /* 10 Month */
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tod_write(uint32 pa, uint32 val, size_t size)
|
||||||
|
{
|
||||||
|
uint32 reg;
|
||||||
|
|
||||||
|
reg = pa - TODBASE;
|
||||||
|
|
||||||
|
sim_debug(WRITE_MSG, &tod_dev,
|
||||||
|
"[%08x] WRITE TOD: reg=%02x val=%d\n",
|
||||||
|
R[NUM_PC], reg, val);
|
||||||
|
}
|
84
3B2/3b2_sysdev.h
Normal file
84
3B2/3b2_sysdev.h
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 System Devices (Header)
|
||||||
|
|
||||||
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall
|
||||||
|
not be used in advertising or otherwise to promote the sale, use or
|
||||||
|
other dealings in this Software without prior written authorization
|
||||||
|
from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _3B2_SYSDEV_H_
|
||||||
|
#define _3B2_SYSDEV_H_
|
||||||
|
|
||||||
|
#include "3b2_defs.h"
|
||||||
|
#include "3b2_sys.h"
|
||||||
|
#include "3b2_cpu.h"
|
||||||
|
|
||||||
|
extern DEVICE nvram_dev;
|
||||||
|
extern DEVICE timer_dev;
|
||||||
|
extern DEVICE csr_dev;
|
||||||
|
extern DEVICE tod_dev;
|
||||||
|
extern DEBTAB sys_deb_tab[];
|
||||||
|
|
||||||
|
struct timer_ctr {
|
||||||
|
uint16 divider;
|
||||||
|
uint8 mode;
|
||||||
|
t_bool lmb;
|
||||||
|
t_bool enabled;
|
||||||
|
t_bool gate;
|
||||||
|
double stime; /* Most recent start time of counter */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* NVRAM */
|
||||||
|
t_stat nvram_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw);
|
||||||
|
t_stat nvram_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw);
|
||||||
|
t_stat nvram_reset(DEVICE *dptr);
|
||||||
|
uint32 nvram_read(uint32 pa, size_t size);
|
||||||
|
t_stat nvram_attach(UNIT *uptr, CONST char *cptr);
|
||||||
|
t_stat nvram_detach(UNIT *uptr);
|
||||||
|
const char *nvram_description(DEVICE *dptr);
|
||||||
|
void nvram_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
|
||||||
|
/* 8253 Timer */
|
||||||
|
t_stat timer_reset(DEVICE *dptr);
|
||||||
|
uint32 timer_read(uint32 pa, size_t size);
|
||||||
|
void timer_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
t_stat timer0_svc(UNIT *uptr);
|
||||||
|
t_stat timer1_svc(UNIT *uptr);
|
||||||
|
t_stat timer2_svc(UNIT *uptr);
|
||||||
|
|
||||||
|
/* CSR */
|
||||||
|
t_stat csr_svc(UNIT *uptr);
|
||||||
|
t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw);
|
||||||
|
t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw);
|
||||||
|
t_stat csr_reset(DEVICE *dptr);
|
||||||
|
uint32 csr_read(uint32 pa, size_t size);
|
||||||
|
void csr_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
|
||||||
|
/* TOD */
|
||||||
|
t_stat tod_svc(UNIT *uptr);
|
||||||
|
t_stat tod_reset(DEVICE *dptr);
|
||||||
|
uint32 tod_read(uint32 pa, size_t size);
|
||||||
|
void tod_write(uint32, uint32 val, size_t size);
|
||||||
|
#endif
|
112
3B2/README.md
Normal file
112
3B2/README.md
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
AT&T 3B2 Simulator
|
||||||
|
==================
|
||||||
|
|
||||||
|
This module contains a simulator for the AT&T 3B2 Model 400 microcomputer.
|
||||||
|
|
||||||
|
*CAUTION*: The simulator is under active and heavy development. It is
|
||||||
|
usable today, but please consider this emulator to be a beta.
|
||||||
|
|
||||||
|
Devices
|
||||||
|
-------
|
||||||
|
|
||||||
|
The following devices are simulated. The SIMH names for the simulated
|
||||||
|
devices are given in parentheses:
|
||||||
|
|
||||||
|
- 3B2 Model 400 System Board with 1MB, 2MB, or 4MB RAM (CSR, NVRAM)
|
||||||
|
- WE32100 CPU (CPU)
|
||||||
|
- WE32101 MMU (MMU)
|
||||||
|
- PD8253 Interval Timer (TIMER)
|
||||||
|
- AM9517 DMA controller (DMAC)
|
||||||
|
- SCN2681A Integrated DUART (IU)
|
||||||
|
- TMS2793 Integrated Floppy Controller (IF)
|
||||||
|
- uPD7261A Integrated MFM Fixed Disk Controller (ID)
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
To boot the 3B2 simulator into firmware mode, simply type:
|
||||||
|
|
||||||
|
sim> BOOT CPU
|
||||||
|
|
||||||
|
You will be greeted with the message:
|
||||||
|
|
||||||
|
FW ERROR 1-01: NVRAM SANITY FAILURE
|
||||||
|
DEFAULT VALUES ASSUMED
|
||||||
|
IF REPEATED, CHECK THE BATTERY
|
||||||
|
|
||||||
|
FW ERROR 1-02: DISK SANITY FAILURE
|
||||||
|
EXECUTION HALTED
|
||||||
|
|
||||||
|
SYSTEM FAILURE: CONSULT YOUR SYSTEM ADMINISTRATION UTILITIES GUIDE
|
||||||
|
|
||||||
|
NVRAM can be saved between boots by attaching it to a file.
|
||||||
|
|
||||||
|
sim> ATTACH NVRAM <file>
|
||||||
|
|
||||||
|
On subsequent boots, you will instead see the message
|
||||||
|
|
||||||
|
SELF-CHECK
|
||||||
|
|
||||||
|
|
||||||
|
FW ERROR 1-02: DISK SANITY FAILURE
|
||||||
|
EXECUTION HALTED
|
||||||
|
|
||||||
|
SYSTEM FAILURE: CONSULT YOUR SYSTEM ADMINISTRATION UTILITIES GUIDE
|
||||||
|
|
||||||
|
|
||||||
|
Once you see the `SYSTEM FAILURE` message, this is actually an
|
||||||
|
invisible prompt. To access firmware mode, type the default 3B2
|
||||||
|
firmware password `mcp`, then press Enter or carriage return.
|
||||||
|
|
||||||
|
You should then be prompted with:
|
||||||
|
|
||||||
|
Enter name of program to execute [ ]:
|
||||||
|
|
||||||
|
Here, you may type a question mark (?) and press Enter to see a list
|
||||||
|
of available firmware programs.
|
||||||
|
|
||||||
|
Booting UNIX SVR3
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
UNIX SVR3 for the 3B2 partially boots. To boot UNIX, attach the first
|
||||||
|
disk image from the 3B2 "Essential Utilities" distribution.
|
||||||
|
|
||||||
|
sim> ATTACH IF <disk-image>
|
||||||
|
sim> BOOT CPU
|
||||||
|
|
||||||
|
Once you reach the `SYSTEM FAILURE` message, type `mcp` to enter
|
||||||
|
firmware mode. When prompted for the name of a program to boot, enter
|
||||||
|
`unix`, and confirm the boot device is `FD5` by pressing Enter or
|
||||||
|
carriage return.
|
||||||
|
|
||||||
|
Enter name of program to execute [ ]: unix
|
||||||
|
Possible load devices are:
|
||||||
|
|
||||||
|
Option Number Slot Name
|
||||||
|
---------------------------------------
|
||||||
|
0 0 FD5
|
||||||
|
|
||||||
|
Enter Load Device Option Number [0 (FD5)]:
|
||||||
|
|
||||||
|
Installing SVR3
|
||||||
|
---------------
|
||||||
|
|
||||||
|
To install SVR3 to the first hard disk, first, attach a new image
|
||||||
|
|
||||||
|
sim> ATTACH ID0 <disk-image>
|
||||||
|
|
||||||
|
Then, boot the file `idtools` from the "3B2 Maintenance Utilities -
|
||||||
|
Issue 4.0" floppy diskette.
|
||||||
|
|
||||||
|
From `idtools`, select the `formhard` option and low-level format
|
||||||
|
integrated disk 0. Parameters are:
|
||||||
|
|
||||||
|
Drive Id: 5
|
||||||
|
Number cylinders: 925
|
||||||
|
Number tracks/cyl: 9
|
||||||
|
Number sectors/track: 18
|
||||||
|
Number bytes/sector: 512
|
||||||
|
|
||||||
|
After low-level formatting integrated disk 0, boot the file `unix`
|
||||||
|
from the first diskette of the 3B2 "Essential Utilities" distribution,
|
||||||
|
and follow the prompts.
|
BIN
3B2/rom_400.bin
Normal file
BIN
3B2/rom_400.bin
Normal file
Binary file not shown.
2061
3B2/rom_400_bin.h
Normal file
2061
3B2/rom_400_bin.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
### New Simulators
|
### New Simulators
|
||||||
|
|
||||||
|
#### Seth Morabito has implemented a AT&T 3B2 simulator.
|
||||||
|
|
||||||
#### Leonid Broukhis and Serge Vakulenko have implemented a simulator for the Soviet mainframe BESM-6 computer.
|
#### Leonid Broukhis and Serge Vakulenko have implemented a simulator for the Soviet mainframe BESM-6 computer.
|
||||||
|
|
||||||
#### Matt Burke has implemented new VAX model simulators:
|
#### Matt Burke has implemented new VAX model simulators:
|
||||||
|
|
377
Visual Studio Projects/3B2.vcproj
Normal file
377
Visual Studio Projects/3B2.vcproj
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
<?xml version="1.0" encoding="Windows-1252"?>
|
||||||
|
<VisualStudioProject
|
||||||
|
ProjectType="Visual C++"
|
||||||
|
Version="9.00"
|
||||||
|
Name="3B2"
|
||||||
|
ProjectGUID="{56178F08-8783-4ADA-820C-20C06412678E}"
|
||||||
|
RootNamespace="3B2"
|
||||||
|
Keyword="Win32Proj"
|
||||||
|
TargetFrameworkVersion="131072"
|
||||||
|
>
|
||||||
|
<Platforms>
|
||||||
|
<Platform
|
||||||
|
Name="Win32"
|
||||||
|
/>
|
||||||
|
</Platforms>
|
||||||
|
<ToolFiles>
|
||||||
|
</ToolFiles>
|
||||||
|
<Configurations>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug|Win32"
|
||||||
|
OutputDirectory="..\BIN\NT\$(PlatformName)-$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="..\BIN\NT\Project\simh\$(ProjectName)\$(PlatformName)-$(ConfigurationName)"
|
||||||
|
ConfigurationType="1"
|
||||||
|
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||||
|
CharacterSet="0"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
Description="Check for required build dependencies & git commit id"
|
||||||
|
CommandLine="Pre-Build-Event.cmd LIBPCRE ROM"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="0"
|
||||||
|
AdditionalIncludeDirectories="./;../;../3B2/;"../../windows-build/PCRE/include/""
|
||||||
|
PreprocessorDefinitions="_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;SIM_NEED_GIT_COMMIT_ID;HAVE_PCREPOSIX_H;PCRE_STATIC;USE_INT64;USE_ADDR64"
|
||||||
|
KeepComments="false"
|
||||||
|
MinimalRebuild="true"
|
||||||
|
BasicRuntimeChecks="0"
|
||||||
|
RuntimeLibrary="1"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
CompileAs="1"
|
||||||
|
ShowIncludes="false"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="wsock32.lib winmm.lib pcrestaticd.lib pcreposixstaticd.lib"
|
||||||
|
LinkIncremental="2"
|
||||||
|
AdditionalLibraryDirectories="../../windows-build/PCRE/lib/"
|
||||||
|
GenerateDebugInformation="true"
|
||||||
|
SubSystem="1"
|
||||||
|
StackReserveSize="10485760"
|
||||||
|
StackCommitSize="10485760"
|
||||||
|
RandomizedBaseAddress="1"
|
||||||
|
DataExecutionPrevention="0"
|
||||||
|
TargetMachine="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Release|Win32"
|
||||||
|
OutputDirectory="..\BIN\NT\$(PlatformName)-$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="..\BIN\NT\Project\simh\$(ProjectName)\$(PlatformName)-$(ConfigurationName)"
|
||||||
|
ConfigurationType="1"
|
||||||
|
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||||
|
CharacterSet="0"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
Description="Check for required build dependencies & git commit id"
|
||||||
|
CommandLine="Pre-Build-Event.cmd LIBPCRE ROM"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="2"
|
||||||
|
InlineFunctionExpansion="1"
|
||||||
|
OmitFramePointers="true"
|
||||||
|
AdditionalIncludeDirectories="./;../;../3B2/;"../../windows-build/PCRE/include/""
|
||||||
|
PreprocessorDefinitions="_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;SIM_NEED_GIT_COMMIT_ID;HAVE_PCREPOSIX_H;PCRE_STATIC;USE_INT64;USE_ADDR64"
|
||||||
|
StringPooling="true"
|
||||||
|
RuntimeLibrary="0"
|
||||||
|
EnableFunctionLevelLinking="true"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
CompileAs="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="wsock32.lib winmm.lib pcrestatic.lib pcreposixstatic.lib"
|
||||||
|
LinkIncremental="1"
|
||||||
|
AdditionalLibraryDirectories="../../windows-build/PCRE/lib/"
|
||||||
|
GenerateDebugInformation="false"
|
||||||
|
SubSystem="1"
|
||||||
|
StackReserveSize="10485760"
|
||||||
|
StackCommitSize="10485760"
|
||||||
|
OptimizeReferences="2"
|
||||||
|
EnableCOMDATFolding="2"
|
||||||
|
RandomizedBaseAddress="1"
|
||||||
|
DataExecutionPrevention="0"
|
||||||
|
TargetMachine="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
</Configurations>
|
||||||
|
<References>
|
||||||
|
</References>
|
||||||
|
<Files>
|
||||||
|
<Filter
|
||||||
|
Name="Source Files"
|
||||||
|
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_cpu.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_dmac.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_id.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_if.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_io.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_iu.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_mmu.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_sys.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_sysdev.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\scp.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_console.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_disk.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_ether.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_fio.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_serial.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_sock.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_tape.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_timer.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_tmxr.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_video.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="Header Files"
|
||||||
|
Filter="h;hpp;hxx;hm;inl;inc"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_cpu.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_defs.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_dmac.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_id.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_if.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_io.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_iu.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_mmu.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_sys.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\3B2\3b2_sysdev.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\scp.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_console.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_defs.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_disk.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_ether.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_fio.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_rev.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_serial.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_sock.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_tape.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_timer.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_tmxr.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\sim_video.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="Resource Files"
|
||||||
|
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||||
|
>
|
||||||
|
</Filter>
|
||||||
|
</Files>
|
||||||
|
<Globals>
|
||||||
|
</Globals>
|
||||||
|
</VisualStudioProject>
|
|
@ -254,6 +254,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scelbi", "scelbi.vcproj", "
|
||||||
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
|
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2", "3B2.vcproj", "{56178F08-8783-4ADA-820C-20C06412678E}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Win32 = Debug|Win32
|
Debug|Win32 = Debug|Win32
|
||||||
|
@ -472,6 +477,10 @@ Global
|
||||||
{1E92CC4B-9ED5-4CD4-BD35-061F25126523}.Debug|Win32.Build.0 = Debug|Win32
|
{1E92CC4B-9ED5-4CD4-BD35-061F25126523}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
{1E92CC4B-9ED5-4CD4-BD35-061F25126523}.Release|Win32.ActiveCfg = Release|Win32
|
{1E92CC4B-9ED5-4CD4-BD35-061F25126523}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
{1E92CC4B-9ED5-4CD4-BD35-061F25126523}.Release|Win32.Build.0 = Release|Win32
|
{1E92CC4B-9ED5-4CD4-BD35-061F25126523}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{56178F08-8783-4ADA-820C-20C06412678E}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{56178F08-8783-4ADA-820C-20C06412678E}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{56178F08-8783-4ADA-820C-20C06412678E}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{56178F08-8783-4ADA-820C-20C06412678E}.Release|Win32.Build.0 = Release|Win32
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
50
descrip.mms
50
descrip.mms
|
@ -16,6 +16,7 @@
|
||||||
# This build script will accept the following build options.
|
# This build script will accept the following build options.
|
||||||
#
|
#
|
||||||
# ALL Just Build "Everything".
|
# ALL Just Build "Everything".
|
||||||
|
# 3B2 Just Build The AT&T 3B2.
|
||||||
# ALTAIR Just Build The MITS Altair.
|
# ALTAIR Just Build The MITS Altair.
|
||||||
# ALTAIRZ80 Just Build The MITS Altair Z80.
|
# ALTAIRZ80 Just Build The MITS Altair Z80.
|
||||||
# BESM6 Just Build The BESM-6.
|
# BESM6 Just Build The BESM-6.
|
||||||
|
@ -306,6 +307,17 @@ PCAP_SIMH_INC = /INCL=($(PCAP_DIR))
|
||||||
@ IF (("$(BUILDING_ROMS)".EQS."").AND.(F$SEARCH("$(BIN_DIR)BuildROMs-$(ARCH).EXE").EQS."")) THEN $(MMS) BUILDROMS/MACRO=(BUILDING_ROMS=1$(NEST_DEBUG))
|
@ IF (("$(BUILDING_ROMS)".EQS."").AND.(F$SEARCH("$(BIN_DIR)BuildROMs-$(ARCH).EXE").EQS."")) THEN $(MMS) BUILDROMS/MACRO=(BUILDING_ROMS=1$(NEST_DEBUG))
|
||||||
|
|
||||||
|
|
||||||
|
# AT&T 3B2 Simulator Definitions.
|
||||||
|
#
|
||||||
|
ATT3B2_DIR = SYS$DISK:[.3B2]
|
||||||
|
ATT3B2_LIB = $(LIB_DIR)ATT3B2-$(ARCH).OLB
|
||||||
|
ATT3B2_SOURCE = $(ATT3B2_DIR)3B2_CPU.C,$(ATT3B2_DIR)3B2_DMAC.C,\
|
||||||
|
$(ATT3B2_DIR)3B2_ID.C,$(ATT3B2_DIR)3B2_IF.C,\
|
||||||
|
$(ATT3B2_DIR)3B2_IO.C,$(ATT3B2_DIR)3B2_IU.C,\
|
||||||
|
$(ATT3B2_DIR)3B2_MMU.C,$(ATT3B2_DIR)3B2_SYS.C,\
|
||||||
|
$(ATT3B2_DIR)3B2_SYSDEV.C
|
||||||
|
ALTAIR_OPTIONS = /INCL=($(SIMH_DIR),$(ATT3B2_DIR))/DEF=($(CC_DEFS))
|
||||||
|
|
||||||
# MITS Altair Simulator Definitions.
|
# MITS Altair Simulator Definitions.
|
||||||
#
|
#
|
||||||
ALTAIR_DIR = SYS$DISK:[.ALTAIR]
|
ALTAIR_DIR = SYS$DISK:[.ALTAIR]
|
||||||
|
@ -1033,6 +1045,17 @@ $(SIMH_LIB64) : $(SIMH_SOURCE)
|
||||||
$ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;*
|
$ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;*
|
||||||
.ENDIF
|
.ENDIF
|
||||||
|
|
||||||
|
$(ATT3B2_LIB) : $(ATT3B2_SOURCE)
|
||||||
|
$!
|
||||||
|
$! Building The $(ATT3B2_LIB) Library.
|
||||||
|
$!
|
||||||
|
$ $(CC)$(ATT3B2_OPTIONS) -
|
||||||
|
/OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST)
|
||||||
|
$ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN -
|
||||||
|
LIBRARY/CREATE $(MMS$TARGET)
|
||||||
|
$ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ
|
||||||
|
$ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;*
|
||||||
|
|
||||||
$(ALTAIR_LIB) : $(ALTAIR_SOURCE)
|
$(ALTAIR_LIB) : $(ALTAIR_SOURCE)
|
||||||
$!
|
$!
|
||||||
$! Building The $(ALTAIR_LIB) Library.
|
$! Building The $(ALTAIR_LIB) Library.
|
||||||
|
@ -1698,6 +1721,33 @@ $(I7094_LIB) :
|
||||||
#
|
#
|
||||||
# Individual Simulator Builds.
|
# Individual Simulator Builds.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# If Not On VAX, Build The AT&T 3B2 Simulator.
|
||||||
|
#
|
||||||
|
.IFDEF ALPHA_OR_IA64
|
||||||
|
ATT3B2 : $(BIN_DIR)ATT3B2-$(ARCH).EXE
|
||||||
|
$! ATT3B2 aka 3B2 done
|
||||||
|
.ELSE
|
||||||
|
#
|
||||||
|
# Else We Are On VAX And Tell The User We Can't Build On VAX
|
||||||
|
# Due To The Use Of INT64.
|
||||||
|
#
|
||||||
|
ATT3B2 :
|
||||||
|
$! Sorry, Can't Build $(BIN_DIR)ATT3B2-$(ARCH).EXE Simulator
|
||||||
|
$! Because It Requires The Use Of INT64.
|
||||||
|
.ENDIF
|
||||||
|
|
||||||
|
$(BIN_DIR)ATT3B2-$(ARCH).EXE : $(SIMH_MAIN) $(SIMH_NONET_LIB) $(ATT3B2_LIB)
|
||||||
|
$!
|
||||||
|
$! Building The $(BIN_DIR)ATT3B2-$(ARCH).EXE Simulator.
|
||||||
|
$!
|
||||||
|
$ $(CC)$(ATT3B2_OPTIONS)/OBJ=$(BLD_DIR) SCP.C
|
||||||
|
$ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ATT3B2-$(ARCH).EXE -
|
||||||
|
$(BLD_DIR)SCP.OBJ,$(ATT3B2_LIB)/LIBRARY,$(SIMH_NONET_LIB)/LIBRARY
|
||||||
|
$ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;*
|
||||||
|
$ COPY $(BIN_DIR)ATT3B2-$(ARCH).EXE $(BIN_DIR)3B2-$(ARCH).EXE
|
||||||
|
|
||||||
ALTAIR : $(BIN_DIR)ALTAIR-$(ARCH).EXE
|
ALTAIR : $(BIN_DIR)ALTAIR-$(ARCH).EXE
|
||||||
$! ALTAIR done
|
$! ALTAIR done
|
||||||
|
|
||||||
|
|
16
makefile
16
makefile
|
@ -1547,6 +1547,14 @@ PDQ3D = PDQ-3
|
||||||
PDQ3 = ${PDQ3D}/pdq3_cpu.c ${PDQ3D}/pdq3_sys.c ${PDQ3D}/pdq3_stddev.c \
|
PDQ3 = ${PDQ3D}/pdq3_cpu.c ${PDQ3D}/pdq3_sys.c ${PDQ3D}/pdq3_stddev.c \
|
||||||
${PDQ3D}/pdq3_mem.c ${PDQ3D}/pdq3_debug.c ${PDQ3D}/pdq3_fdc.c
|
${PDQ3D}/pdq3_mem.c ${PDQ3D}/pdq3_debug.c ${PDQ3D}/pdq3_fdc.c
|
||||||
PDQ3_OPT = -I ${PDQ3D} -DUSE_SIM_IMD
|
PDQ3_OPT = -I ${PDQ3D} -DUSE_SIM_IMD
|
||||||
|
|
||||||
|
ATT3B2D = 3B2
|
||||||
|
ATT3B2 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_mmu.c \
|
||||||
|
${ATT3B2D}/3b2_iu.c ${ATT3B2D}/3b2_if.c \
|
||||||
|
${ATT3B2D}/3b2_id.c ${ATT3B2D}/3b2_dmac.c \
|
||||||
|
${ATT3B2D}/3b2_sys.c ${ATT3B2D}/3b2_io.c \
|
||||||
|
${ATT3B2D}/3b2_sysdev.c
|
||||||
|
ATT3B2_OPT = -I ${ATT3B2D} -DUSE_INT64 -DUSE_ADDR64
|
||||||
#
|
#
|
||||||
# Build everything (not the unsupported/incomplete or experimental simulators)
|
# Build everything (not the unsupported/incomplete or experimental simulators)
|
||||||
#
|
#
|
||||||
|
@ -1555,7 +1563,7 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \
|
||||||
nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri \
|
nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri \
|
||||||
i7094 ibm1130 id16 id32 sds lgp h316 cdc1700 \
|
i7094 ibm1130 id16 id32 sds lgp h316 cdc1700 \
|
||||||
swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 isys8010 isys8020 \
|
swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 isys8010 isys8020 \
|
||||||
isys8030 isys8024 imds-225 scelbi
|
isys8030 isys8024 imds-225 scelbi 3b2
|
||||||
|
|
||||||
all : ${ALL}
|
all : ${ALL}
|
||||||
|
|
||||||
|
@ -1933,6 +1941,12 @@ ${BIN}b5500${EXE} : ${B5500} ${SIM}
|
||||||
${MKDIRBIN}
|
${MKDIRBIN}
|
||||||
${CC} ${B5500} ${SIM} ${B5500_OPT} $(CC_OUTSPEC) ${LDFLAGS}
|
${CC} ${B5500} ${SIM} ${B5500_OPT} $(CC_OUTSPEC) ${LDFLAGS}
|
||||||
|
|
||||||
|
3b2 : $(BIN)3b2$(EXE)
|
||||||
|
|
||||||
|
${BIN}3b2${EXE} : ${ATT3B2} ${SIM} ${BUILD_ROMS}
|
||||||
|
${MKDIRBIN}
|
||||||
|
${CC} ${ATT3B2} ${SIM} ${ATT3B2_OPT} $(CC_OUTSPEC) ${LDFLAGS}
|
||||||
|
|
||||||
# Front Panel API Demo/Test program
|
# Front Panel API Demo/Test program
|
||||||
|
|
||||||
frontpaneltest : ${BIN}frontpaneltest${EXE}
|
frontpaneltest : ${BIN}frontpaneltest${EXE}
|
||||||
|
|
|
@ -48,6 +48,7 @@ struct ROM_File_Descriptor {
|
||||||
{"VAX/vmb.exe", "VAX/vax_vmb_exe.h", 44544, 0xFFC014BB, "vax_vmb_exe"},
|
{"VAX/vmb.exe", "VAX/vax_vmb_exe.h", 44544, 0xFFC014BB, "vax_vmb_exe"},
|
||||||
{"PDP11/lunar11/lunar.lda", "PDP11/pdp11_vt_lunar_rom.h", 13824 , 0xFFF15D00, "lunar_lda"},
|
{"PDP11/lunar11/lunar.lda", "PDP11/pdp11_vt_lunar_rom.h", 13824 , 0xFFF15D00, "lunar_lda"},
|
||||||
{"swtp6800/swtp6800/swtbug.bin", "swtp6800/swtp6800/swtp_swtbug_bin.h", 1024, 0xFFFE4FBC, "swtp_swtbug_bin"},
|
{"swtp6800/swtp6800/swtbug.bin", "swtp6800/swtp6800/swtp_swtbug_bin.h", 1024, 0xFFFE4FBC, "swtp_swtbug_bin"},
|
||||||
|
{"3B2/rom_400.bin", "3B2/rom_400_bin.h", 32768, 0xFFD55762, "rom_400_bin"},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue