-
Notifications
You must be signed in to change notification settings - Fork 0
/
acia.c
122 lines (103 loc) · 2.85 KB
/
acia.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
#include <pty.h>
#include "acia.h"
#include "log.h"
uint8_t mm6502_acia_read_callback(fake6502_context *context, uint16_t address, void *data)
{
mm6551_acia *acia = (mm6551_acia *)data;
uint16_t reg = address - acia->address;
switch(reg) {
case ACIA_REG_BASE:
acia->status ^= ACIA_STATUS_RXF;
return acia->rx;
case ACIA_REG_STATUS:
return acia->status;
case ACIA_REG_COMMAND:
return acia->command;
case ACIA_REG_CONTROL:
return acia->control;
}
return 0;
}
void mm6502_acia_write_callback(fake6502_context *context, uint16_t address, uint8_t value, void *data)
{
mm6551_acia *acia = (mm6551_acia *)data;
uint16_t reg = address - acia->address;
switch(reg) {
case ACIA_REG_BASE:
acia->tx = value;
acia->status ^= ACIA_STATUS_TXE;
return;
case ACIA_REG_COMMAND:
acia->command = value;
return;
case ACIA_REG_CONTROL:
acia->control = value;
return;
default:
return;
}
}
void mm6551_init(mm6551_acia *acia)
{
// create pty
int amaster, aslave;
char *name = malloc(sizeof(char) * 256);
openpty(&amaster, &aslave, name, NULL, NULL);
// set non blocking
int flags = fcntl(amaster, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(amaster, F_SETFL, flags);
// set up polling for step callback
acia->fd.fd = amaster;
acia->fd.events = POLLOUT | POLLIN;
info("acia at %x on %s", acia->address, name);
}
void mm6551_step_callback(fake6502_context *context, void *data)
{
mm6551_acia *acia = (mm6551_acia *)data;
char buf;
int echo = 0;
ssize_t n;
// todo recover from terminal disconnect
if(poll(&acia->fd, 1, 100) > 0) {
// can we read?
if(acia->fd.revents & POLLIN) {
if(read(acia->fd.fd, &buf, 1) > 0) {
// have we overrun?
if(acia->status & ACIA_STATUS_RXF) {
acia->status |= ACIA_STATUS_OVR;
}
acia->rx = buf;
echo = 1;
acia->status |= ACIA_STATUS_RXF;
// interrupt?
if(acia->command & ~ACIA_COMMAND_IRD) {
acia->status |= ACIA_STATUS_IRQ;
fake6502_irq(context);
}
}
}
// can we write?
if(acia->fd.revents & POLLOUT) {
// echo previously rxd char if we have one and echo is enabled
if(echo && (acia->command & ACIA_COMMAND_REM)) {
// We'll write back directly rather than through buffer
n = write(acia->fd.fd, &acia->rx, 1);
echo = 0; // what about if we want to echo a null char?
}
if((acia->status & ACIA_STATUS_TXE) == 0) {
// only if the tx buffer is full though
n = write(acia->fd.fd, &acia->tx, 1);
acia->status |= ACIA_STATUS_TXE;
if(acia->command & ~ACIA_COMMAND_TICH & ACIA_COMMAND_TICL) {
acia->status |= ACIA_STATUS_IRQ;
fake6502_irq(context);
}
}
}
}
}