#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "../tools/pdp6common.h"
#include "threading.h"

#define nelem(a) (sizeof(a)/sizeof(a[0]))
#define nil NULL
#define print printf
#define fprint fprintf

typedef uint32_t u32;
typedef uint16_t u16;
typedef int8_t   i8;
typedef uint8_t  u8;
typedef unsigned char uchar;
typedef uchar bool;

extern FILE *debugfp;
extern int dotrace;
void trace(char *fmt, ...);
void debug(char *fmt, ...);
void err(char *fmt, ...);

void strtolower(char *s);
int hasinput(int fd);
int writen(int fd, void *data, int n);
int readn(int fd, void *data, int n);
int dial(const char *host, int port);

void quit(int code);
void commandline(char *line);
void cli(FILE *f, Channel **c);
void readcmdchan(void *p);
void *cmdthread(void *);
void *simthread(void *p);
void *rtcthread(void *p);
int dofile(const char *path);
void defaultconfig(void);

extern Lock initlock;
extern volatile int awaitinit;

enum {
	MAXPULSE = 20
};

enum Mask {
	FW   = 0777777777777,
	RT   = 0000000777777,
	LT   = 0777777000000,
	SGN  = 0400000000000,
	RSGN = 0000000400000,
};

enum HalfwordBits {
	H0  = 0400000, H1  = 0200000, H2  = 0100000,
	H3  = 0040000, H4  = 0020000, H5  = 0010000,
	H6  = 0004000, H7  = 0002000, H8  = 0001000,
	H9  = 0000400, H10 = 0000200, H11 = 0000100,
	H12 = 0000040, H13 = 0000020, H14 = 0000010,
	H15 = 0000004, H16 = 0000002, H17 = 0000001
};

enum FullwordBits {
	FCRY = 01000000000000,
	F0  = 0400000000000, F1  = 0200000000000, F2  = 0100000000000,
	F3  = 0040000000000, F4  = 0020000000000, F5  = 0010000000000,
	F6  = 0004000000000, F7  = 0002000000000, F8  = 0001000000000,
	F9  = 0000400000000, F10 = 0000200000000, F11 = 0000100000000,
	F12 = 0000040000000, F13 = 0000020000000, F14 = 0000010000000,
	F15 = 0000004000000, F16 = 0000002000000, F17 = 0000001000000,
	F18 = 0000000400000, F19 = 0000000200000, F20 = 0000000100000,
	F21 = 0000000040000, F22 = 0000000020000, F23 = 0000000010000,
	F24 = 0000000004000, F25 = 0000000002000, F26 = 0000000001000,
	F27 = 0000000000400, F28 = 0000000000200, F29 = 0000000000100,
	F30 = 0000000000040, F31 = 0000000000020, F32 = 0000000000010,
	F33 = 0000000000004, F34 = 0000000000002, F35 = 0000000000001
};

typedef struct Mem Mem;
typedef struct Membus Membus;
typedef struct FMem FMem;
typedef struct CMem CMem;
typedef struct MobyMem MobyMem;
typedef struct Busdev Busdev;
typedef struct IOBus IOBus;
typedef struct Apr Apr;


typedef struct Task Task;
struct Task
{
	Task *next;	/* link to next task */
	void (*f)(void*);
	void *arg;
	int freq;	/* how often the task is serviced */
	int cnt;
};

extern Task *tasks;
void addtask(Task t);

typedef struct RtcMsg RtcMsg;
struct RtcMsg
{
	int msg;	// 1 - start; 0 - stop
	int repeat;
	Channel *c;
	word interval;
};
extern Channel *rtcchan;

typedef struct Device Device;
struct Device
{
	Device *next;
	char *type;
	char *name;
	/* mount file on device */
	void (*mount)(Device *dev, const char *path);
	void (*unmount)(Device *dev);
	/* connect device to iobus */
	void (*ioconnect)(Device *dev, IOBus *bus);
	/* manipulate device registers */
	word (*examine)(Device *dev, const char *reg);
	int  (*deposit)(Device *dev, const char *reg, word data);
};

extern Device *devlist;
void adddevice(Device *dev);
Device *getdevice(const char *name);
void showdevices(void);


/*
 * Memory
 */

// 7-2, 7-10
enum {
	MEMBUS_MA21         = 0000000000001,
	MEMBUS_WR_RQ        = 0000000000004,
	MEMBUS_RD_RQ        = 0000000000010,
	MEMBUS_MA_FMC_SEL0  = 0000001000000,
	MEMBUS_MA_FMC_SEL1  = 0000002000000,
	MEMBUS_MA35_0       = 0000004000000,
	MEMBUS_MA35_1       = 0000010000000,
	MEMBUS_MA21_0       = 0000020000000,
	MEMBUS_MA21_1       = 0000040000000,
	MEMBUS_MA20_0       = 0000100000000,
	MEMBUS_MA20_1       = 0000200000000,
	MEMBUS_MA19_0       = 0000400000000,
	MEMBUS_MA19_1       = 0001000000000,
	MEMBUS_MA18_0       = 0002000000000,
	MEMBUS_MA18_1       = 0004000000000,
	MEMBUS_RQ_CYC       = 0020000000000,
	MEMBUS_WR_RS        = 0100000000000,
	MEMBUS_MAI_RD_RS    = 0200000000000,
	MEMBUS_MAI_ADDR_ACK = 0400000000000,
};

/* A memory module connected to up to 4 Membuses */
struct Mem
{
	Device dev;
	void *module;			/* CMem, FMem */
	Membus *bus[4];
	int (*wake)(Mem *mem, int sel, Membus *bus);
	void (*poweron)(Mem *mem);
	void (*sync)(Mem *mem);		/* sync file to disk */
};

struct Membus
{
	/* c12 are cables 1 and 2, c34 3 and 4.
	 * c??_prev records the previous state of the bus
	 * and c??_pulse is used to detect leading edges. */
	word c12, c12_prev, c12_pulse;
	word c34, c34_prev, c34_pulse;

	Mem *fmem;	/* fast memory */
	Mem *cmem[16];	/* 16 16k core modules */
};
extern Membus memterm;	/* terminator */
void wakemem(Membus *bus);

/* Fast memory 162, 16 words */
struct FMem
{
	word ff[16];
	i8 fmc_p_sel;
	bool fmc_act;
	bool fmc_wr;
};
#define FMEM_IDENT "fmem162"
extern char *fmem_ident;
Mem *makefastmem(int p);
Device *makefmem(int argc, char *argv[]);

/* Core memory 161C, normally 16k words.
 * Can be used for other sizes too. */
struct CMem
{
	char *filename;
	int size;
	int mask;

	word *core;
	word cmb;
	hword cma;
	bool cma_rd_rq, cma_wr_rq;
	bool cmc_aw_rq;
	i8 cmc_p_act;
	u8 cmc_last_proc;
	bool cmc_pse_sync;
};
#define CMEM_IDENT "cmem161C"
#define MMEM_IDENT "moby"
extern char *cmem_ident;
extern char *mmem_ident;
Mem *makecoremem(const char *file, int size);
Device *make16kmem(int argc, char *argv[]);
Device *make256kmem(int argc, char *argv[]);

void attachmem(Mem *mem, int p, Membus *bus, int n);
void readmem(const char *file, word *mem, word size);
void showmem(Membus *bus);
word *getmemref(Membus *bus, hword addr, int fastmem, int *busy);

/*
 * IO bus
 */

// 7-10
enum {
	IOBUS_PI_REQ_7    = F35,
	IOBUS_PI_REQ_6    = F34,
	IOBUS_PI_REQ_5    = F33,
	IOBUS_PI_REQ_4    = F32,
	IOBUS_PI_REQ_3    = F31,
	IOBUS_PI_REQ_2    = F30,
	IOBUS_PI_REQ_1    = F29,
	IOBUS_IOB_STATUS  = F23,
	IOBUS_IOB_DATAI   = F22,
	IOBUS_CONO_SET    = F21,
	IOBUS_CONO_CLEAR  = F20,
	IOBUS_DATAO_SET   = F19,
	IOBUS_DATAO_CLEAR = F18,
	IOBUS_IOS9_1      = F17,
	IOBUS_IOS9_0      = F16,
	IOBUS_IOS8_1      = F15,
	IOBUS_IOS8_0      = F14,
	IOBUS_IOS7_1      = F13,
	IOBUS_IOS7_0      = F12,
	IOBUS_IOS6_1      = F11,
	IOBUS_IOS6_0      = F10,
	IOBUS_IOS5_1      = F9,
	IOBUS_IOS5_0      = F8,
	IOBUS_IOS4_1      = F7,
	IOBUS_IOS4_0      = F6,
	IOBUS_IOS3_1      = F5,
	IOBUS_IOS3_0      = F4,
	IOBUS_MC_DR_SPLIT = F3,
	IOBUS_POWER_ON    = F1,
	IOBUS_IOB_RESET   = F0,
	IOBUS_PULSES = IOBUS_CONO_SET | IOBUS_CONO_CLEAR |
	               IOBUS_DATAO_SET | IOBUS_DATAO_CLEAR
};

#define IOB_RESET       (bus->c34 & IOBUS_IOB_RESET)
#define IOB_DATAO_CLEAR (bus->c34 & IOBUS_DATAO_CLEAR)
#define IOB_DATAO_SET   (bus->c34 & IOBUS_DATAO_SET)
#define IOB_CONO_CLEAR  (bus->c34 & IOBUS_CONO_CLEAR)
#define IOB_CONO_SET    (bus->c34 & IOBUS_CONO_SET)
#define IOB_STATUS      (bus->c34 & IOBUS_IOB_STATUS)
#define IOB_DATAI       (bus->c34 & IOBUS_IOB_DATAI)

/* A peripheral device connected to an IO bus */
struct Busdev
{
	void *dev;
	void (*wake)(void *dev);
	u8 req;
};

struct IOBus
{
	/* c12 are cables 1 and 2, c34 3 and 4.
	 * c??_prev records the previous state of the bus
	 * and c??_pulse is used to detect leading edges. */
	word c12, c12_prev, c12_pulse;
	word c34, c34_prev, c34_pulse;
	/* device code decoded from the cables */
	int devcode;
	/* All IO devices connected to this bus */
	Busdev dev[128];
};
void setreq(IOBus *bus, int dev, u8 pia);
void setreq2(IOBus *bus, int dev, u8 req);


/* A pulse modeled as a function, and a name for debugging */
typedef struct Pulse Pulse;
struct Pulse
{
	void (*f)(void *arg);
	char *n;
};

/* A timed pulse in a linked list.
 * t is time difference since last pulse */
typedef struct TPulse TPulse;
struct TPulse
{
	TPulse *next;
	Pulse *p;
	int t;
};

/*
 * Devices
 */

/* Arithmetic Processor 166 */
#define CPA (0000>>2)
#define PI (0004>>2)

/* external pulses, bits of Apr.extpulse */
enum Extpulse {
	EXT_KEY_MANUAL       = 1,
	EXT_KEY_INST_STOP    = 2,
};

/* everything that is read AND written at the same time */
struct AprState
{
	word ar;
	word mb;
	word mq;
};

struct Apr
{
	Device dev;
	IOBus iobus;
	Membus membus;
	int powered;

	struct AprState c, n;
	hword ir;
	word mi;
	word data;
	hword pc;
	hword ma;
	hword mas;
	bool mq36;
	u16 sc, fe;
	u8 pr, rlr, rla;
	bool run;
	bool sw_addr_stop, sw_repeat, sw_mem_disable, sw_power;
	/* maint switches */
	bool sw_rim_maint, sw_rpt_bypass, sw_art3_maint, sw_sct_maint, sw_spltcyc_override;
	/* keys */
	bool key_start, key_readin;
	bool key_mem_cont, key_inst_cont;
	bool key_mem_stop, key_inst_stop;
	bool key_io_reset, key_exec;
	bool key_dep, key_dep_nxt;
	bool key_ex, key_ex_nxt;
	bool key_rd_off, key_rd_on;
	bool key_pt_rd, key_pt_wr;
	/* knobs */
	int speed_range;	// 0-5
	int speed_set;		// 0-100

	/* PI */
	u8 pio, pir, pih, pi_req;
	bool pi_active;
	bool pi_ov, pi_cyc;

	/* flip-flops */
	bool ex_mode_sync, ex_uuo_sync, ex_pi_sync, ex_ill_op, ex_user;
	bool ar_pc_chg_flag, ar_ov_flag, ar_cry0_flag, ar_cry1_flag;
	bool ar_cry0, ar_cry1, ar_com_cont;
	bool ar_cry0_xor_cry1;

	bool key_ex_st, key_ex_sync;
	bool key_dep_st, key_dep_sync;
	bool key_rd_wr, key_rim_sbr;

	bool mc_rd, mc_wr, mc_rq, mc_stop, mc_stop_sync, mc_split_cyc_sync;

	bool cpa_iot_user, cpa_illeg_op, cpa_non_exist_mem,
	     cpa_clock_enable, cpa_clock_flag, cpa_pc_chg_enable, cpa_pdl_ov,
	     cpa_arov_enable;
	int cpa_pia;

	bool iot_go;
	bool iot_init_setup, iot_final_setup;	// not used
	bool iot_reset;
	bool iot_go_pulse;	// for edge detection

	/* ?? */
	bool a_long;

	/* sbr flip-flops */
	bool if1a;
	bool af0, af3, af3a;
	bool f1a, f4a, f6a;
	bool et4_ar_pse;
	bool chf1, chf2, chf3, chf4, chf5, chf6, chf7;
	bool lcf1, dcf1;
	bool sf3, sf5a, sf7;
	bool shf1;
	bool mpf1, mpf2;
	bool msf1;
	bool dsf1, dsf2, dsf3, dsf4, dsf5, dsf6, dsf7, dsf8, dsf9;
	bool fsf1;
	bool fmf1, fmf2;
	bool fdf1, fdf2;
	bool faf1, faf2, faf3, faf4;
	bool fpf1, fpf2;
	bool nrf1, nrf2, nrf3;
	bool iot_f0a;
	bool blt_f0a, blt_f3a, blt_f5a;
	bool uuo_f1;

	/* temporaries */
	bool ex_inh_rel;

	/* decoded instructions */
	hword inst_ma;		/* for debug, memory address of instruction */
	int inst, io_inst;
	bool ir_fp;
	bool ir_fwt;
	bool fwt_00, fwt_01, fwt_10, fwt_11;
	bool shift_op, ir_md, ir_jp, ir_as;
	bool ir_boole;
	bool boole_as_00, boole_as_01, boole_as_10, boole_as_11;
	int ir_boole_op;
	bool ir_hwt;
	bool hwt_00, hwt_01, hwt_10, hwt_11;
	bool ir_acbm;
	bool ex_ir_uuo, ir_iot, ir_jrst;

	bool fc_e_pse;
	bool pc_set;

	Channel *clkchan, *rptchan;
	int extpulse;
	bool ia_inh;	// this is asserted for some time

	/* This could be abstracted away */
	TPulse pulses[MAXPULSE];
	TPulse *pfree;
	TPulse *pulse;
};
#define APR_IDENT "apr166"
extern char *apr_ident;
Device *makeapr(int argc, char *argv[]);


/* Paper tape reader 760 */
#define PTR (0104>>2)
typedef struct Ptr Ptr;
struct Ptr
{
	Device dev;
	IOBus *bus;
	int motor_on;
	word sr;
	word ptr;
	bool busy, flag, b;
	int pia;

	int fd;
};
#define PTR_IDENT "ptr760"
extern char *ptr_ident;
Device *makeptr(int argc, char *argv[]);
void ptr_setmotor(Ptr *ptr, int m);


/* Paper tape punch 761 */
#define PTP (0100>>2)
typedef struct Ptp Ptp;
struct Ptp
{
	Device dev;
	IOBus *bus;
	uchar ptp;
	bool busy, flag, b;
	int pia;

	int fd;
	int waitdatao;
};
#define PTP_IDENT "ptp761"
extern char *ptp_ident;
Device *makeptp(int argc, char *argv[]);


/* Teletype 626 */
#define TTY (0120>>2)
typedef struct Tty Tty;
struct Tty
{
	Device dev;
	IOBus *bus;
	uchar tto, tti;
	bool tto_busy, tto_flag;
	bool tti_busy, tti_flag;
	int pia;
	int fd;
};
#define TTY_IDENT "tty626"
extern char *tty_ident;
Device *maketty(int argc, char *argv[]);

/* Data Control 136 */
#define DC (0200>>2)
typedef struct Dc136 Dc136;
struct Dc136
{
	Device dev;
	IOBus *bus;
	int pia;
	int device;
	int ch_mode;
	void *devp[8];

	word da;
	word db;
	int cct;
	int sct;

	int data_clbd;	/* data clobbered */
	int dbda_move;
	int da_rq;
	int db_rq;
	int inout;

	/* lines between dc and devices 1/2:
	 * tk/gv rt
	 * tk/gv lt
	 * clr cct 1
	 * darq(1)
	 * sel 1
	 */
};
#define DC_IDENT "dc136"
extern char *dc_ident;
Device *makedc(int argc, char *argv[]);
void dcgv(Dc136 *dc, int n, word c, int rev);
word dctk(Dc136 *dc, int n, int rev);

/* Microtape/DECtape */

/* 555 has:
 *  move tape fwd, rev, auto
 *  unit selection (1-8)
 *  motor off, write lock on/off
*/

typedef struct Dx555 Dx555;
typedef struct Dt551 Dt551;

/* Transport 555,
 * a half of it really */
struct Dx555
{
	Device dev;

	Dt551 *dt;

	int unit;
	int wrlock;
	int motion;	// -1 rev, 0 stop, 1 fwd

	int fd;
	uchar *start;
	uchar *cur;
	uchar *end;
	uint size;
};
#define DX_IDENT "dx555"
extern char *dx_ident;
Device *makedx(int argc, char *argv[]);
void dxconn(Dt551 *dt, Dx555 *dx, int n);
/*
void dxatt(Dx555 *dx, uchar *buf, int size);
void dxff(Dx555 *dx);
void dxrew(Dx555 *dx);
*/

/* Control 551 */
#define UTC (0210>>2)
#define UTS (0214>>2)
struct Dt551
{
	Device dev;
	IOBus *bus;

	Dc136 *dc;
	Dx555 *dx[8];
	Dx555 *seldx;

	/* enable time and mark track writing */
	int ut_btm_switch;

	int ut_pia;
	int ut_units;
	int ut_units_select;
	int ut_fcn;
	int ut_time;
	int ut_wren;
	int ut_go;
	int ut_rev;
	int ut_tape_end_flag;
	int ut_tape_end_enable;
	int ut_jb_done_flag;
	int ut_jb_done_enable;
	int time_enable;
	int time_flag;
	int ut_illegal_op;
	int ut_info_error;
	int ut_incomp_block;

	int tmk;	/* mark track window, 9b */
	int rwb;	/* read write buffer, 6b */
	int lb;		/* longitudinal buffer, computes check sum, 6b */
	int tbm;	/* block mark timing, 4b */
	int tdata;	/* data timing, 8b */
	int tct;	/* selects half of 6 bit rwb, 1b */
	/* error check */
	int utek;	/* 6 bits */
	int uteck;

	int rw_state;	/* null, rq, active */

	int delay;
	int sense;
	int wb;

//	int req;
};
#define DT_IDENT "dt551"
extern char *dt_ident;
Device *makedt(int argc, char *argv[]);
void dtconn(Dc136 *dc, Dt551 *dt);


#define DIS (0130>>2)
#define DIS_IDENT "dis340"
Device *makedis(int argc, char *argv[]);
extern char *dis_ident;


#define JOY (0420>>2)
#define JOY_IDENT "joy420"
Device *makejoy(int argc, char *argv[]);
extern char *joy_ident;


#define OJOY (0724>>2)
#define OJOY_IDENT "joy724"
Device *makeojoy(int argc, char *argv[]);
extern char *ojoy_ident;


typedef struct Netmem Netmem;
struct Netmem
{
	Device dev;
	int fd;
	u8 buf[9];
	int waiting;

	// tmp?
	Apr *apr;
};
#define NETMEM_IDENT "netmem"
extern char *netmem_ident;
Device *makenetmem(int argc, char *argv[]);


typedef struct Netcons Netcons;
struct Netcons
{
	Device dev;
	int fd;
	u8 buf[9];

	// tmp?
	Apr *apr;
};
#define NETCONS_IDENT "netcons"
extern char *netcons_ident;
Device *makenetcons(int argc, char *argv[]);

void writeconsreg(Apr *apr, u32 addr, u32 data);
u32 readconsreg(Apr *apr, u32 addr);
