Skip to content

Commit dcc6f5e

Browse files
author
azertyfun
committed
Console is now remote and more functional
1 parent f9dd7d8 commit dcc6f5e

File tree

14 files changed

+572
-512
lines changed

14 files changed

+572
-512
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*.log
2+
*.o
23
out/

.idea/workspace.xml

Lines changed: 199 additions & 152 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Options:
4545
--edc Adds an EDC device.
4646
--M35FD=path/to/file Adds an M35FD device with a floppy stored in path/to/file.
4747
--M525HD=path/to/file Adds an M525HD device with a hard disk stored in path/to/file.
48-
--console Makes the LEM1802s and keyboard work with stdin/stdout. Only works with consoles that support ANSI escape codes (i.e. not cmd, but cygwin does). Flags that would otherwise spawn a window are disabled.
48+
--console Disables debugger and EDC, and creates a server on port 25570 that can be used by remoteConsole.py to control the LEM1802 and keyboard via a remote console. Useful if you want to run the emulator headless.
4949
```
5050

5151
Screenshot

remoteConsole/remoteConsole.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import curses
5+
import socket
6+
7+
colors = [curses.COLOR_BLACK, curses.COLOR_BLUE, curses.COLOR_GREEN, curses.COLOR_CYAN, curses.COLOR_RED, curses.COLOR_MAGENTA, curses.COLOR_RED, curses.COLOR_CYAN, curses.COLOR_WHITE, curses.COLOR_RED, curses.COLOR_GREEN, curses.COLOR_CYAN, curses.COLOR_RED, curses.COLOR_RED, curses.COLOR_YELLOW, curses.COLOR_WHITE]
8+
9+
def init_curses():
10+
stdscr = curses.initscr()
11+
curses.noecho()
12+
curses.raw()
13+
curses.start_color()
14+
stdscr.keypad(True)
15+
16+
stdscr.timeout(0)
17+
18+
return stdscr
19+
20+
def free_curses():
21+
curses.nocbreak()
22+
stdscr.keypad(False)
23+
curses.echo()
24+
curses.endwin()
25+
26+
def init_networking():
27+
mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
28+
mySocket.connect((sys.argv[1], int(sys.argv[2])))
29+
return mySocket
30+
31+
if len(sys.argv) != 3:
32+
print("Error: two arguments needed.")
33+
print("Usage:\n./remoteConsole.py <address> <port>")
34+
sys.exit()
35+
36+
stdscr = init_curses()
37+
try:
38+
socket = init_networking()
39+
while True:
40+
41+
# We can't make a receive of 12*32*2 bytes at once because a bytearray doesn't work after 512 items for some reason.
42+
data = []
43+
for i in range(0, 12*32*2):
44+
b = socket.recv(1)
45+
data.append(b[0])
46+
47+
stdscr.clear()
48+
49+
pairs = []
50+
pairPointer = 1
51+
52+
for i in range(0, 12*32):
53+
try:
54+
d = (data[i * 2] << 8) | data[i * 2 + 1]
55+
except IndexError:
56+
stdscr.addstr("error at index " + str(i))
57+
58+
if i % 32 == 0 and i != 0:
59+
stdscr.addstr("\n");
60+
61+
c = d & 0x7F
62+
blink = (d >> 7) & 1
63+
color = (d >> 8) & 0xFF
64+
#stdscr.addstr("Color: " + str(colors[color >> 4]) + ", " + str(colors[color & 0xF]));
65+
#stdscr.getch()
66+
pair = [pairPointer, colors[color >> 4], colors[color & 0xF]]
67+
foundPair = False
68+
for p in pairs:
69+
if p[1] == pair[1] and p[2] == pair[2]:
70+
pair[0] = p[0]
71+
foundPair = True
72+
break
73+
74+
if not foundPair:
75+
pairs.append(pair)
76+
pairPointer = pairPointer + 1
77+
try:
78+
curses.init_pair(pair[0], pair[1], pair[2])
79+
except curses.error:
80+
stdscr.addstr("pairPointer: " + str(pairPointer))
81+
stdscr.getch()
82+
83+
stdscr.addstr(chr(c), curses.color_pair(pair[0]))
84+
#stdscr.getch()
85+
86+
c = stdscr.getch()
87+
if c != curses.ERR:
88+
b = bytearray()
89+
b.append(c)
90+
b.append(0)
91+
socket.send(b)
92+
if c == 3:
93+
free_curses()
94+
break
95+
96+
stdscr.refresh()
97+
98+
finally:
99+
free_curses()

src/tk/azertyfun/dcputoolchain/DCPUToolChain.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public static void usage() {
4646
" --edc Adds an EDC device.\n" +
4747
" --M35FD=path/to/file Adds an M35FD device with a floppy stored in path/to/file.\n" +
4848
" --M525HD=path/to/file Adds an M525HD device with a hard disk stored in path/to/file.\n" +
49-
" --console Makes the LEM1802s and keyboard work with stdin/stdout. Only works with consoles that support ANSI escape codes (i.e. not cmd, but cygwin does). Flags that would otherwise spawn a window are disabled.");
49+
" --console Disables debugger and EDC, and creates a server on port 25570 that can be used by remoteConsole.py to control the LEM1802 and keyboard via a remote console. Useful if you want to run the emulator headless.");
5050

5151
System.exit(-1);
5252
}

src/tk/azertyfun/dcputoolchain/Emulator.java

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ public class Emulator implements CallbackStop {
1616

1717
private LinkedList<DCPUHardware> hardware = new LinkedList<>();
1818
private LinkedList<LemDisplay> lemDisplays = new LinkedList<>();
19-
private LinkedList<KeyboardInterface> keyboardInterfaces = new LinkedList<>();
19+
private LinkedList<KeyboardDisplay> keyboardDisplays = new LinkedList<>();
2020
private LinkedList<EdcDisplay> edcDisplays = new LinkedList<>();
2121

2222
private TickingThread ticking;
2323

2424
private DebuggerInterface debuggerInterface;
2525

26+
private boolean console = false;
27+
private ConsoleServer consoleServer;
28+
2629
public Emulator(String[] args) {
2730
String input_file = args[1];
2831

@@ -41,11 +44,8 @@ public Emulator(String[] args) {
4144
boolean assemble = false;
4245
boolean debugger = false;
4346
boolean optimize_shortLiterals = true;
44-
boolean console = false;
4547
boolean addLem = false; //If we have to add a LEM, it must be initialized last because creating a JFrame after having initialized glfw will make java (at least with OpenJDK 8) crash.
4648

47-
int console_offset = 0;
48-
4949
if(args.length > 2) {
5050

5151
for(int i = 2; i < args.length; ++i) {
@@ -78,21 +78,21 @@ public Emulator(String[] args) {
7878
hardware.getLast().connectTo(dcpu);
7979
hardware.getLast().powerOn();
8080

81-
KeyboardInterface keyboardInterface;
82-
if(console) {
83-
keyboardInterface = new KeyboardConsole((GenericKeyboard) hardware.getLast(), cpuControl, this);
84-
(new Thread((KeyboardConsole) keyboardInterface)).start();
85-
} else {
86-
keyboardInterface = new KeyboardDisplay((GenericKeyboard) hardware.getLast(), cpuControl);
81+
if(!console) {
82+
KeyboardDisplay keyboardDisplay = new KeyboardDisplay((GenericKeyboard) hardware.getLast(), cpuControl);
83+
keyboardDisplays.add(keyboardDisplay);
8784
}
88-
keyboardInterfaces.add(keyboardInterface);
8985
} else if(args[i].equalsIgnoreCase("--EDC")) {
90-
hardware.add(hardwareTracker.requestEDC());
91-
hardware.getLast().connectTo(dcpu);
92-
hardware.getLast().powerOn();
86+
if(!console) {
87+
hardware.add(hardwareTracker.requestEDC());
88+
hardware.getLast().connectTo(dcpu);
89+
hardware.getLast().powerOn();
9390

94-
EdcDisplay edcDisplay = new EdcDisplay((EDC) hardware.getLast());
95-
edcDisplays.add(edcDisplay);
91+
EdcDisplay edcDisplay = new EdcDisplay((EDC) hardware.getLast());
92+
edcDisplays.add(edcDisplay);
93+
} else {
94+
System.out.println("WARNING: Ignored edc flag, console flag specified.");
95+
}
9696
} else {
9797
if (!args[i].equalsIgnoreCase("--console")) {
9898
String[] splitted = args[i].split("=");
@@ -170,15 +170,11 @@ public Emulator(String[] args) {
170170
hardware.getLast().connectTo(dcpu);
171171
hardware.getLast().powerOn();
172172

173-
LemDisplay lemDisplay;
174-
if(console) {
175-
lemDisplay = new LemDisplayConsole((LEM1802) hardware.getLast(), console_offset);
176-
console_offset += 12;
177-
} else {
178-
lemDisplay = new LemDisplayGlfw((LEM1802) hardware.getLast());
173+
if(!console) {
174+
LemDisplay lemDisplay = new LemDisplay((LEM1802) hardware.getLast());
175+
lemDisplay.start();
176+
lemDisplays.add(lemDisplay);
179177
}
180-
lemDisplay.start();
181-
lemDisplays.add(lemDisplay);
182178
}
183179
} else {
184180
LEM1802 lem1802 = hardwareTracker.requestLem();
@@ -200,6 +196,19 @@ public Emulator(String[] args) {
200196
debuggerInterface = new DebuggerInterface(dcpu, ticking, this);
201197
}
202198

199+
if(console) {
200+
consoleServer = new ConsoleServer(25570);
201+
for(GenericKeyboard genericKeyboard : hardwareTracker.getKeyboards()) {
202+
consoleServer.addKeyboard(genericKeyboard);
203+
}
204+
205+
for(LEM1802 lem1802 : hardwareTracker.getLems()) {
206+
consoleServer.addLemDisplayConsole(lem1802);
207+
}
208+
209+
consoleServer.start();
210+
}
211+
203212
try {
204213
if(assemble) {
205214
File tmpFile = File.createTempFile("DCPUToolchain", Long.toString(System.currentTimeMillis()));
@@ -255,12 +264,15 @@ public void stopCallback() {
255264
debuggerInterface.close();
256265
for(LemDisplay lemDisplay : lemDisplays)
257266
lemDisplay.close();
258-
for(KeyboardInterface keyboardInterface : keyboardInterfaces)
267+
for(KeyboardDisplay keyboardInterface : keyboardDisplays)
259268
keyboardInterface.close();
260269
for(EdcDisplay edcDisplay : edcDisplays)
261270
edcDisplay.close();
262271
ticking.setStopped();
263272

273+
if(console)
274+
consoleServer.setStopped();
275+
264276
try {
265277
Thread.sleep(1000);
266278
System.exit(0);

src/tk/azertyfun/dcputoolchain/emulator/HardwareTracker.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,12 @@ public M525HD getM525HD(String id) {
109109
}
110110
return null;
111111
}
112+
113+
public LinkedList<GenericKeyboard> getKeyboards() {
114+
return keyboards;
115+
}
116+
117+
public LinkedList<LEM1802> getLems() {
118+
return lems;
119+
}
112120
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package tk.azertyfun.dcputoolchain.interfaces;
2+
3+
import tk.azertyfun.dcputoolchain.emulator.GenericKeyboard;
4+
import tk.azertyfun.dcputoolchain.emulator.LEM1802;
5+
6+
import java.io.BufferedReader;
7+
import java.io.IOException;
8+
import java.io.InputStreamReader;
9+
import java.io.OutputStream;
10+
import java.net.ServerSocket;
11+
import java.net.Socket;
12+
import java.net.SocketException;
13+
import java.util.LinkedList;
14+
15+
public class ConsoleServer extends Thread {
16+
17+
private boolean stopped = false;
18+
private int port;
19+
private StoppableThread readThread, writeThread;
20+
private LinkedList<GenericKeyboard> keyboards = new LinkedList<>();
21+
private LinkedList<LEM1802> lem1802s = new LinkedList<>();
22+
23+
public ConsoleServer(int port) {
24+
this.port = port;
25+
}
26+
27+
@Override
28+
public void run() {
29+
try {
30+
ServerSocket serverSocket = new ServerSocket(port);
31+
32+
while(!stopped) {
33+
System.out.println("Waiting for connection from console...");
34+
Socket clientSocket = serverSocket.accept();
35+
System.out.println("Client console connected!");
36+
OutputStream out = clientSocket.getOutputStream();
37+
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
38+
39+
(readThread = new StoppableThread() {
40+
public void run() {
41+
while(!stopped) {
42+
try {
43+
if(in.ready()) {
44+
for (GenericKeyboard genericKeyboard : keyboards) {
45+
int key = in.read();
46+
47+
switch(key) { // /!\ IMPORTANT NOTICE: It is impossible in console mode to capture insert, delete, arrow keys, shift or control.
48+
case 0:
49+
break;
50+
case 0x8:
51+
case 0x7F: //BACKSPACE-DEL (ncurses doesn't seem to differenciate)
52+
genericKeyboard.pressedKeyCode(0x10);
53+
break;
54+
case 0xa: //LF
55+
genericKeyboard.pressedKeyCode(0x11);
56+
break;
57+
default:
58+
genericKeyboard.pressedKey((char) key);
59+
}
60+
}
61+
} else {
62+
Thread.sleep(10);
63+
}
64+
} catch (InterruptedException | IOException e) {
65+
e.printStackTrace();
66+
}
67+
}
68+
}
69+
}).start();
70+
71+
(writeThread = new StoppableThread() {
72+
public void run() {
73+
while(!stopped) {
74+
try {
75+
char[] ram = lem1802s.getFirst().getVideoRam();
76+
for (char c : ram) {
77+
out.write((byte) ((c & 0xFF00) >> 8));
78+
out.write((byte) (c & 0xFF));
79+
}
80+
Thread.sleep(100);
81+
} catch (SocketException e) {
82+
readThread.stopped = true;
83+
stopped = true;
84+
} catch (InterruptedException | IOException e) {
85+
e.printStackTrace();
86+
}
87+
}
88+
}
89+
}).start();
90+
}
91+
} catch (IOException e) {
92+
e.printStackTrace();
93+
}
94+
}
95+
96+
public void setStopped() {
97+
stopped = true;
98+
readThread.stopped = true;
99+
writeThread.stopped = true;
100+
}
101+
102+
public void addLemDisplayConsole(LEM1802 lem1802) {
103+
lem1802s.add(lem1802);
104+
}
105+
106+
public void addKeyboard(GenericKeyboard genericKeyboard) {
107+
keyboards.add(genericKeyboard);
108+
}
109+
110+
private abstract class StoppableThread extends Thread {
111+
public boolean stopped = false;
112+
}
113+
}

0 commit comments

Comments
 (0)