mirror of
https://github.com/aap/pdp6.git
synced 2026-02-27 01:10:14 +00:00
emu: implemented a better clock; implemented repeat functionality, no knobs on panel yet
This commit is contained in:
@@ -37,7 +37,8 @@ Otherwise you need SDL and pthread.
|
||||
|
||||
The cpu (apr), console tty, paper tape and punch,
|
||||
the data control and DECtape are implemented.
|
||||
The only things missing from the cpu is the repeat key mechanism.
|
||||
The panel is missing the repeat delay knobs,
|
||||
but the functionality is implemented.
|
||||
|
||||
## Verilog Simulation
|
||||
|
||||
@@ -125,7 +126,7 @@ misc nothing important
|
||||
|
||||
## To do
|
||||
|
||||
- repeat and maint. switches
|
||||
- repeat and maint. switches on panel
|
||||
- improve timing
|
||||
- implement 340 display
|
||||
- do more tests
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SRC=emu.c util.c threading.c cmd.c apr.c mem.c tty.c pt.c dc.c dt.c netmem.c ../tools/pdp6common.c
|
||||
SRC=emu.c apr.c mem.c tty.c pt.c dc.c dt.c netmem.c cmd.c util.c threading.c rtc.c ../tools/pdp6common.c
|
||||
H=pdp6.h ../tools/pdp6common.h threading.h
|
||||
# clang
|
||||
#CFLAGS= -Wno-shift-op-parentheses -Wno-logical-op-parentheses \
|
||||
|
||||
49
emu/apr.c
49
emu/apr.c
@@ -240,6 +240,9 @@ makeapr(int argc, char *argv[])
|
||||
t = (Task){ nil, aprcycle, apr, 1, 0 };
|
||||
addtask(t);
|
||||
|
||||
apr->clkchan = chancreate(sizeof(int), 1);
|
||||
apr->rptchan = chancreate(sizeof(int), 1);
|
||||
|
||||
return &apr->dev;
|
||||
}
|
||||
|
||||
@@ -3290,9 +3293,25 @@ defpulse(key_go)
|
||||
|
||||
defpulse_(kt4)
|
||||
{
|
||||
/* low end of ranges. unsure, but not totally off */
|
||||
static word ranges[6] = {
|
||||
3400,
|
||||
41718, // *12.27
|
||||
602407, // *14.44
|
||||
6024079, // *10
|
||||
60240792, // *10
|
||||
269878748, // *4.48
|
||||
};
|
||||
static RtcMsg rpt = { 1, 0, nil, 0 };
|
||||
|
||||
if(apr->run && (apr->key_ex_st || apr->key_dep_st))
|
||||
pulse(apr, &key_go, 0); // 5-2
|
||||
// TODO check repeat switch
|
||||
|
||||
rpt.c = apr->rptchan;
|
||||
rpt.interval = ranges[apr->speed_range];
|
||||
/* TODO: is this just a multiplier */
|
||||
rpt.interval *= 1 + apr->speed_set/100.0f*14.0f;
|
||||
chansend(rtcchan, &rpt);
|
||||
}
|
||||
|
||||
defpulse(kt3)
|
||||
@@ -3381,11 +3400,11 @@ updatebus(void *bus)
|
||||
b->c34_pulse = (b->c34_prev ^ b->c34) & b->c34;
|
||||
}
|
||||
|
||||
#define TIMESTEP (1000.0/60.0)
|
||||
|
||||
void
|
||||
aprstart(Apr *apr)
|
||||
{
|
||||
static RtcMsg lineclk = { 1, 1, nil, 1000000000/60 };
|
||||
|
||||
int i;
|
||||
printf("[aprstart]\n");
|
||||
|
||||
@@ -3404,8 +3423,10 @@ aprstart(Apr *apr)
|
||||
apr->iobus.c34 = 0;
|
||||
|
||||
pulse(apr, &mr_pwr_clr, 100); // random value
|
||||
apr->lasttick = getms();
|
||||
apr->powered = 1;
|
||||
|
||||
lineclk.c = apr->clkchan;
|
||||
chansend(rtcchan, &lineclk);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -3423,21 +3444,17 @@ aprcycle(void *p)
|
||||
}else if(!apr->powered)
|
||||
aprstart(apr);
|
||||
|
||||
/*
|
||||
if(apr->pulsestepping){
|
||||
int c;
|
||||
while(c = getchar(), c != EOF && c != '\n')
|
||||
if(c == 'x')
|
||||
apr->pulsestepping = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
apr->tick = getms();
|
||||
if(apr->tick-apr->lasttick >= TIMESTEP){
|
||||
apr->lasttick = apr->lasttick+TIMESTEP;
|
||||
int foo;
|
||||
if(channbrecv(apr->clkchan, &foo) == 1){
|
||||
// printf("tick\n");
|
||||
apr->cpa_clock_flag = 1;
|
||||
recalc_cpa_req(apr);
|
||||
}
|
||||
if(channbrecv(apr->rptchan, &foo) == 1){
|
||||
// printf("rpt\n");
|
||||
if(KEY_MANUAL)
|
||||
pulse(apr, &kt0a, 1);
|
||||
}
|
||||
|
||||
apr->iobus.c12_prev = apr->iobus.c12;
|
||||
apr->iobus.c34_prev = apr->iobus.c34;
|
||||
|
||||
@@ -35,12 +35,6 @@ struct Element
|
||||
int active;
|
||||
};
|
||||
|
||||
u32
|
||||
getms(void)
|
||||
{
|
||||
return SDL_GetTicks();
|
||||
}
|
||||
|
||||
SDL_Surface*
|
||||
mustloadimg(const char *path)
|
||||
{
|
||||
@@ -544,12 +538,12 @@ threadmain(int argc, char *argv[])
|
||||
default:
|
||||
usage();
|
||||
}ARGEND;
|
||||
|
||||
if(debugfp = fopen(outfile, "w"), debugfp == nil){
|
||||
fprintf(stderr, "Can't open %s\n", outfile);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
if(SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
err("%s", SDL_GetError());
|
||||
|
||||
@@ -638,6 +632,8 @@ threadmain(int argc, char *argv[])
|
||||
|
||||
extra_l = e; e += 1;
|
||||
|
||||
rtcchan = chancreate(sizeof(RtcMsg), 20);
|
||||
|
||||
dofile("init.ini");
|
||||
apr = (Apr*)getdevice("apr");
|
||||
tty = (Tty*)getdevice("tty");
|
||||
@@ -650,6 +646,7 @@ threadmain(int argc, char *argv[])
|
||||
addtask(t);
|
||||
threadcreate(simthread, nil);
|
||||
threadcreate(cmdthread, cmdchans);
|
||||
threadcreate(rtcthread, nil);
|
||||
|
||||
for(;;){
|
||||
while(SDL_PollEvent(&ev))
|
||||
|
||||
@@ -9,13 +9,6 @@
|
||||
// TODO: get rid of this
|
||||
void updatepanel(Apr *apr) {}
|
||||
|
||||
u32
|
||||
getms(void)
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define KEYPULSE(k) (apr->k && !oldapr.k)
|
||||
|
||||
void
|
||||
@@ -179,12 +172,12 @@ threadmain(int argc, char *argv[])
|
||||
default:
|
||||
usage();
|
||||
}ARGEND;
|
||||
|
||||
if(debugfp = fopen(outfile, "w"), debugfp == nil){
|
||||
fprintf(stderr, "Can't open %s\n", outfile);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
dofile("init.ini");
|
||||
apr = (Apr*)getdevice("apr");
|
||||
ptr = (Ptr*)getdevice("ptr");
|
||||
|
||||
19
emu/pdp6.h
19
emu/pdp6.h
@@ -25,7 +25,6 @@ extern int dotrace;
|
||||
void trace(char *fmt, ...);
|
||||
void debug(char *fmt, ...);
|
||||
void err(char *fmt, ...);
|
||||
u32 getms(void);
|
||||
|
||||
void strtolower(char *s);
|
||||
int hasinput(int fd);
|
||||
@@ -39,6 +38,7 @@ void cli(FILE *f, Channel **c);
|
||||
void readcmdchan(void *p);
|
||||
void *cmdthread(void *);
|
||||
void *simthread(void *p);
|
||||
void *rtcthread(void *p);
|
||||
void dofile(const char *path);
|
||||
|
||||
enum {
|
||||
@@ -100,6 +100,16 @@ struct Task
|
||||
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
|
||||
{
|
||||
@@ -352,6 +362,9 @@ struct Apr
|
||||
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; // 1-100
|
||||
|
||||
/* PI */
|
||||
u8 pio, pir, pih, pi_req;
|
||||
@@ -426,11 +439,9 @@ struct Apr
|
||||
bool fc_e_pse;
|
||||
bool pc_set;
|
||||
|
||||
/* needed for the emulation */
|
||||
double lasttick, tick;
|
||||
Channel *clkchan, *rptchan;
|
||||
int extpulse;
|
||||
bool ia_inh; // this is asserted for some time
|
||||
int pulsestepping;
|
||||
|
||||
/* This could be abstracted away */
|
||||
TPulse pulses[MAXPULSE];
|
||||
|
||||
163
emu/rtc.c
Normal file
163
emu/rtc.c
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "pdp6.h"
|
||||
#include <time.h>
|
||||
|
||||
struct timespec
|
||||
timesub(struct timespec t1, struct timespec t2)
|
||||
{
|
||||
struct timespec d;
|
||||
d.tv_sec = t2.tv_sec - t1.tv_sec;
|
||||
d.tv_nsec = t2.tv_nsec - t1.tv_nsec;
|
||||
if(d.tv_nsec < 0){
|
||||
d.tv_nsec += 1000000000;
|
||||
d.tv_sec -= 1;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
struct timespec
|
||||
timeadd(struct timespec t1, struct timespec t2)
|
||||
{
|
||||
struct timespec s;
|
||||
s.tv_sec = t1.tv_sec + t2.tv_sec;
|
||||
s.tv_nsec = t1.tv_nsec + t2.tv_nsec;
|
||||
if(s.tv_nsec >= 1000000000){
|
||||
s.tv_nsec -= 1000000000;
|
||||
s.tv_sec += 1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/* is t2 after t1 */
|
||||
int
|
||||
timeafter(struct timespec t1, struct timespec t2)
|
||||
{
|
||||
if(t1.tv_sec == t2.tv_sec)
|
||||
return t1.tv_nsec < t2.tv_nsec;
|
||||
return t1.tv_sec < t2.tv_sec;
|
||||
}
|
||||
|
||||
/* is t2 before t1 */
|
||||
int
|
||||
timebefore(struct timespec t1, struct timespec t2)
|
||||
{
|
||||
if(t1.tv_sec == t2.tv_sec)
|
||||
return t1.tv_nsec > t2.tv_nsec;
|
||||
return t1.tv_sec > t2.tv_sec;
|
||||
}
|
||||
|
||||
typedef struct Clockchan Clockchan;
|
||||
struct Clockchan
|
||||
{
|
||||
int active;
|
||||
Channel *c;
|
||||
struct timespec start;
|
||||
struct timespec timeout;
|
||||
struct timespec interval;
|
||||
int repeat;
|
||||
Clockchan *next;
|
||||
};
|
||||
static Clockchan *channels;
|
||||
Channel *rtcchan;
|
||||
|
||||
static Clockchan*
|
||||
getchan(Channel *c)
|
||||
{
|
||||
Clockchan *cc;
|
||||
for(cc = channels; cc; cc = cc->next)
|
||||
if(cc->c == c)
|
||||
return cc;
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
rtcstart(Channel *c, word interval, int repeat)
|
||||
{
|
||||
Clockchan *cc;
|
||||
|
||||
cc = getchan(c);
|
||||
if(cc == nil){
|
||||
cc = malloc(sizeof(Clockchan));
|
||||
memset(cc, 0, sizeof(Clockchan));
|
||||
cc->next = channels;
|
||||
channels = cc;
|
||||
}
|
||||
|
||||
cc->active = 1;
|
||||
cc->c = c;
|
||||
clock_gettime(CLOCK_REALTIME, &cc->start);
|
||||
cc->interval.tv_sec = interval / 1000000000;
|
||||
cc->interval.tv_nsec = interval % 1000000000;
|
||||
cc->timeout = timeadd(cc->start, cc->interval);
|
||||
cc->repeat = repeat;
|
||||
}
|
||||
|
||||
static void
|
||||
rtcstop(Channel *c)
|
||||
{
|
||||
Clockchan *cc;
|
||||
|
||||
cc = getchan(c);
|
||||
if(cc)
|
||||
cc->active = 0;
|
||||
}
|
||||
|
||||
void*
|
||||
rtcthread(void *p)
|
||||
{
|
||||
struct timespec now;
|
||||
Clockchan *cc;
|
||||
RtcMsg msg;
|
||||
int loss;
|
||||
|
||||
loss = 0;
|
||||
for(;;){
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
/* Check all channels for a timeout */
|
||||
for(cc = channels; cc; cc = cc->next){
|
||||
if(!cc->active || timebefore(cc->timeout, now))
|
||||
continue;
|
||||
|
||||
/* I *hope* this is ok */
|
||||
if(channbsend(cc->c, &loss) == 0)
|
||||
;//printf("missed clock\n");
|
||||
if(cc->repeat)
|
||||
cc->timeout = timeadd(cc->timeout, cc->interval);
|
||||
else
|
||||
cc->active = 0;
|
||||
}
|
||||
|
||||
if(channbrecv(rtcchan, &msg) == 1){
|
||||
if(msg.msg == 1)
|
||||
rtcstart(msg.c, msg.interval, msg.repeat);
|
||||
else
|
||||
rtcstop(msg.c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
int
|
||||
main()
|
||||
{
|
||||
long max = 0;
|
||||
int n = 0x7FFFFFFF;
|
||||
|
||||
struct timespec interval = { 0, 500000000 };
|
||||
struct timespec timeout, now;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &timeout);
|
||||
timeout = timeadd(timeout, interval);
|
||||
|
||||
while(n--){
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
if(timeafter(timeout, now)){
|
||||
timeout = timeadd(timeout, interval);
|
||||
printf("tick\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
Reference in New Issue
Block a user