1
0
mirror of https://github.com/rzzzwilson/pymlac.git synced 2025-06-10 09:32:41 +00:00

Combining PTR and PTP device

This commit is contained in:
Ross Wilson
2015-11-01 20:03:43 +07:00
parent d222eec8a7
commit 388d582c00
4 changed files with 360 additions and 11 deletions

View File

@@ -1,4 +1,4 @@
DEVFILES = cpu.o dcpu.o ptr.o ptp.o memory.o kb.o ttyin.o ttyout.o trace.o error.o log.o plist.o
DEVFILES = cpu.o dcpu.o ptrptp.o memory.o kb.o ttyin.o ttyout.o trace.o error.o log.o plist.o
OFILES = vimlac.o $(DEVFILES)
#CFLAGS=-fPIC -O2 -Wall -ansi -pedantic -std=c99 -g

250
vimlac/ptrptp.c Executable file
View File

@@ -0,0 +1,250 @@
/*
* Implementation for the vimlac PTR/PTP (papertape reader/punch).
*/
#include "vimlac.h"
#include "ptrptp.h"
/*****
* constants for the PTR device
*
* The device reads at 300 chars/second, so we work out how many
* machine cycles data is ready/notready at a 30%/70% ready cycle.
******/
#define PTR_CHARS_PER_SECOND 300
#define PTR_CYCLES_PER_CHAR (CPU_HERZ / PTR_CHARS_PER_SECOND)
#define PTR_READY_CYCLES (int)((3 * PTR_CYCLES_PER_CHAR) / 10)
#define PTR_NOT_PTR_READY_CYCLES (int)((7 * PTR_CYCLES_PER_CHAR) / 10)
#define PTR_EOF 0377
/*****
* constants for the PTP device
*
* The device punches at 30 chars/second, so we work out how many
* machine cycles device is not ready after punch starts.
******/
#define PTP_CHARS_PER_SECOND 30
#define PTP_NOT_READY_CYCLES (int) (CPU_HERZ / PTP_CHARS_PER_SECOND)
// define some use values
#define InUsePTR "PTR"
#define InUsePTP "PTP"
#define STREQ(a, b) (strcmp((a), (b)) == 0)
/*****
* State variables for the general device
******/
static char *device_use = NULL; // NULL, InUsePTR or InUsePTP
static bool device_motor_on = false;
static FILE *device_open_file = NULL;
static char *device_filename = NULL;
static long device_cycle_count = 0;
static bool device_ready = false;
/*****
* Specific state variables for the PTR device
******/
static bool ptr_at_eof = true;
static BYTE ptr_value = PTR_EOF;
int ptr_mount(char *fname)
{
if (device_use && STREQ(device_use, InUsePTP))
verror("ptr_mount: Can't mount PTR file, being used as PTP");
;
device_filename = fname;
device_open_file = fopen(fname, "rb");
if (device_open_file == NULL)
{
ptr_dismount();
return errno;
}
device_motor_on = false;
device_ready = false;
ptr_at_eof = false;
ptr_value = PTR_EOF;
device_cycle_count = PTR_NOT_PTR_READY_CYCLES;
return 0;
}
void ptr_dismount(void)
{
if (device_use && STREQ(device_use, InUsePTP))
verror("ptr_mount: Can't dismount PTR file, being used as PTP");
if (device_open_file)
fclose(device_open_file);
device_filename = NULL;
device_open_file = NULL;
device_motor_on = false;
device_ready = true;
ptr_at_eof = true;
ptr_value = PTR_EOF;
}
void ptr_start(void)
{
if (device_use && STREQ(device_use, InUsePTP))
verror("ptrptp_start: Can't start PTR motor, being used as PTP");
device_motor_on = true;
device_ready = false;
device_cycle_count = PTR_NOT_PTR_READY_CYCLES;
}
void ptr_stop(void)
{
if (device_use && STREQ(device_use, InUsePTP))
verror("ptr_stop: Can't stop PTR motor, being used as PTP");
device_motor_on = false;
device_cycle_count = PTR_NOT_PTR_READY_CYCLES;
}
int ptr_read(void)
{
if (device_use && STREQ(device_use, InUsePTP))
verror("ptr_read: Can't read PTR, device being used as PTP");
return ptr_value;
}
bool ptr_ready(void)
{
return device_ready;
}
void ptr_tick(long cycles)
{
// if not being used as PTR, do nothing
if (device_use && !STREQ(device_use, InUsePTR))
return;
/* if no state change */
if (!device_motor_on || ptr_at_eof || device_open_file == NULL)
return;
/* tape in, motor on */
device_cycle_count -= cycles;
if (device_cycle_count <= 0L)
{
if (device_ready)
{
device_ready = false;
device_cycle_count += PTR_NOT_PTR_READY_CYCLES;
ptr_value = 0;
}
else
{
device_ready = true;
device_cycle_count += PTR_READY_CYCLES;
if (fread(&ptr_value, sizeof(BYTE), 1, device_open_file) != 1)
{ /* assume EOF on file, dismount tape */
fclose(device_open_file);
device_open_file = NULL;
ptr_at_eof = true;
ptr_value = PTR_EOF;
}
}
}
}
// MID
int ptp_mount(char *fname)
{
if (device_use && STREQ(device_use, InUsePTR))
verror("ptp_mount: Can't mount PTP, device being used as PTR");
device_use = InUsePTP;
device_filename = fname;
device_open_file = fopen(fname, "wb");
if (device_open_file == NULL)
{
ptp_dismount();
return errno;
}
device_motor_on = false;
device_ready = false;
device_cycle_count = PTP_NOT_READY_CYCLES;
return 0;
}
void ptp_dismount(void)
{
if (device_use && STREQ(device_use, InUsePTR))
verror("ptp_dismount: Can't dismount PTP, device being used as PTR");
if (device_open_file)
if (fclose(device_open_file) != 0)
device_filename = NULL;
device_open_file = NULL;
device_motor_on = false;
device_ready = true;
}
void ptp_start(void)
{
device_motor_on = true;
device_ready = false;
device_cycle_count = PTP_NOT_READY_CYCLES;
}
void ptp_stop(void)
{
device_motor_on = false;
device_cycle_count = PTP_NOT_READY_CYCLES;
}
void ptp_punch(BYTE value)
{
if (device_motor_on && device_open_file != NULL)
{
putc(value, device_open_file);
device_cycle_count = PTP_NOT_READY_CYCLES;
}
}
bool ptp_ready(void)
{
return device_ready;
}
void ptp_tick(long cycles)
{
/* if no state change */
if (!device_motor_on || device_open_file == NULL)
return;
/* tape in, motor on */
device_cycle_count -= cycles;
if (device_cycle_count <= 0L)
{
if (!device_ready)
device_ready = true;
device_cycle_count = 0;
}
}

24
vimlac/ptrptp.h Executable file
View File

@@ -0,0 +1,24 @@
/*
* Interface for the vimlac PTR/PTP (papertape reader/punch).
*/
#ifndef PTRPTP_H
#define PTRPTP_H
int ptr_mount(char *fname);
void ptr_dismount(void);
void ptr_start(void);
void ptr_stop(void);
int ptr_read(void);
void ptr_tick(long cycles);
bool ptr_ready(void);
int ptp_mount(char *fname);
void ptp_dismount(void);
void ptp_start(void);
void ptp_stop(void);
void ptp_punch(BYTE byte);
void ptp_tick(long cycles);
bool ptp_ready(void);
#endif

View File

@@ -60,6 +60,7 @@
#include "cpu.h"
#include "dcpu.h"
#include "memory.h"
#include "ptrptp.h"
#include "error.h"
#include "log.h"
#include "plist.h"
@@ -469,22 +470,60 @@ allmem(char *value, char *var2)
/******************************************************************************
Description : Run one instruction on the CPU.
Parameters : addr - address of memory cell (string, may be NULL)
Description : Run one or more instructions on the CPU.
Parameters : num - address of memory cell (string, may be NULL)
: fld2 - unused
Returns : The number of errors encountered (0 or 1).
Comments :
******************************************************************************/
int
run_one(char *addr, char *fld2)
run_one(char *num, char *fld2)
{
int run_num = 0;
// if given address run from that address, else use PC contents
if (addr)
cpu_set_PC(str2word(addr));
if (!num)
run_num = 1;
else
run_num = str2word(num);
cpu_start();
UsedCycles = cpu_execute_one();
while (run_num--)
{
int cycles = cpu_execute_one();
ptr_tick(cycles);
ptp_tick(cycles);
UsedCycles += cycles;
}
return 0;
}
/******************************************************************************
Description : Run the CPU until PC is the same as 'addr'.
Parameters : addr - PC contents to stop at
: fld2 - unused
Returns : The number of errors encountered (0 or 1).
Comments :
******************************************************************************/
int
rununtil(char *addr, char *fld2)
{
WORD stop_pc = str2word(addr);
cpu_start();
do
{
int cycles = cpu_execute_one();
ptr_tick(cycles);
ptp_tick(cycles);
UsedCycles += cycles;
} while (cpu_get_PC() != stop_pc);
return 0;
}
@@ -547,6 +586,38 @@ checkreg(char *reg, char *expected)
}
/******************************************************************************
Description : Check that the main CPU is in the correct state.
Parameters : state - string holding expected display state ('on' or 'off)
: unused - unused
Returns : The number of errors encountered (0 or 1).
Comments :
******************************************************************************/
int
checkcpu(char *state, char *unused)
{
if ((STREQ(state, "on")) && !cpu_get_state())
{
vlog("Main CPU run state is %s, should be 'ON'",
(DisplayOn ? "ON": "OFF"));
return 1;
}
else if ((STREQ(state, "off")) && cpu_get_state())
{
vlog("Main CPU run state is %s, should be 'OFF'",
(DisplayOn ? "ON": "OFF"));
return 1;
}
else
{
vlog("checkcpu: state should be 'on' or 'OFF', got %s", state);
return 1;
}
return 0;
}
/******************************************************************************
Description : Check that the display is in the correct state.
Parameters : state - string holding expected display state ('on' or 'off)
@@ -555,7 +626,7 @@ Description : Check that the display is in the correct state.
Comments :
******************************************************************************/
int
checkd(char *state, char *unused)
checkdcpu(char *state, char *unused)
{
if ((STREQ(state, "on")) && !DisplayOn)
{
@@ -565,7 +636,7 @@ checkd(char *state, char *unused)
}
else if ((STREQ(state, "off")) && DisplayOn)
{
vlog("DCPU run state is %s, should be 'OFF'",
vlog("Display CPU run state is %s, should be 'OFF'",
(DisplayOn ? "ON": "OFF"));
return 1;
}
@@ -1210,6 +1281,8 @@ run_one_test(Test *test)
error += setmem(fld1, fld2);
else if (STREQ(opcode, "RUN"))
error += run_one(fld1, fld2);
else if (STREQ(opcode, "RUNUNTIL"))
error += rununtil(fld1, fld2);
else if (STREQ(opcode, "CHECKCYCLES"))
error += checkcycles(fld1, fld2);
else if (STREQ(opcode, "CHECKREG"))
@@ -1224,8 +1297,10 @@ run_one_test(Test *test)
error += checkrun(fld1, fld2);
else if (STREQ(opcode, "SETD"))
error += setd(fld1, fld2);
else if (STREQ(opcode, "CHECKD"))
error += checkd(fld1, fld2);
else if (STREQ(opcode, "CHECKDCPU"))
error += checkdcpu(fld1, fld2);
else if (STREQ(opcode, "CHECKCPU"))
error += checkcpu(fld1, fld2);
else
{
printf("Unrecognized operation '%s' at line %d\n",