Added LICENSE and README
This commit is contained in:
parent
2f77975f65
commit
8dd29cfa95
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright 2018, Seth Morabito <web@loomcom.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,20 @@
|
||||||
|
# GTK DMD 5620 Emulator
|
||||||
|
|
||||||
|
This is a GTK+ 3.0 implementation of an AT&T / Teletype DMD 5620 emulator.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Version: 0.1
|
||||||
|
|
||||||
|
This is an actively developed project, and is not ready for use yet.
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
* [dmd_core](https://github.com/sethm/dmd_core): DMD 5620 core
|
||||||
|
implementation library, used by this project.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT license. See the file [LICENSE.md](LICENSE.md)
|
||||||
|
|
||||||
|
Copyright (c) 2018, Seth Morabito <web@loomcom.com>
|
92
src/dmd.c
92
src/dmd.c
|
@ -1,10 +1,17 @@
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
static cairo_surface_t *surface = NULL;
|
static cairo_surface_t *surface = NULL;
|
||||||
|
static pthread_t dmd_thread;
|
||||||
|
|
||||||
|
uint8_t last_kb_char;
|
||||||
|
uint8_t kb_pending = 0;
|
||||||
|
volatile int dmd_thread_run = 1;
|
||||||
|
|
||||||
extern int dmd_init();
|
extern int dmd_init();
|
||||||
extern int dmd_reset();
|
extern int dmd_reset();
|
||||||
|
@ -53,10 +60,7 @@ static void
|
||||||
close_window(void)
|
close_window(void)
|
||||||
{
|
{
|
||||||
printf("[close_window]\n");
|
printf("[close_window]\n");
|
||||||
gtk_main_quit();
|
dmd_thread_run = 0;
|
||||||
if (surface) {
|
|
||||||
cairo_surface_destroy(surface);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -82,9 +86,15 @@ long get_current_time_ms()
|
||||||
return (s * 1000) + ms;
|
return (s * 1000) + ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
refresh_display(GtkWidget *widget)
|
refresh_display(gpointer data)
|
||||||
{
|
{
|
||||||
|
GtkWidget *widget = (GtkWidget *)data;
|
||||||
|
|
||||||
|
if (widget == NULL || !GTK_IS_WIDGET(widget)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
|
GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
|
||||||
TRUE,
|
TRUE,
|
||||||
8,
|
8,
|
||||||
|
@ -132,6 +142,8 @@ refresh_display(GtkWidget *widget)
|
||||||
cairo_destroy(cr);
|
cairo_destroy(cr);
|
||||||
|
|
||||||
gtk_widget_queue_draw(widget);
|
gtk_widget_queue_draw(widget);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -148,24 +160,41 @@ button_press_event_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t last_kb_char;
|
void *dmd_run(void *threadid)
|
||||||
uint8_t kb_pending = 0;
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
run_dmd(gpointer user_data)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 40000; i++) {
|
long double steps = 0;
|
||||||
|
int rs;
|
||||||
|
struct timespec sleep_time_req, sleep_time_rem;
|
||||||
|
|
||||||
|
sleep_time_req.tv_sec = 0;
|
||||||
|
sleep_time_req.tv_nsec = 10000000;
|
||||||
|
|
||||||
|
printf("[DMD thread starting]");
|
||||||
|
dmd_init();
|
||||||
|
dmd_reset();
|
||||||
|
|
||||||
|
while (dmd_thread_run) {
|
||||||
dmd_step();
|
dmd_step();
|
||||||
|
|
||||||
|
// Stop every once in a while to poll for I/O and idle.
|
||||||
|
if (steps++ == 250000) {
|
||||||
|
if (kb_pending && dmd_rx_keyboard(last_kb_char)) {
|
||||||
|
printf("[DMD thread] Sent char 0x%02x\n", last_kb_char);
|
||||||
|
kb_pending = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
steps = 0;
|
||||||
|
rs = nanosleep(&sleep_time_req, &sleep_time_rem);
|
||||||
|
if (rs) {
|
||||||
|
printf("[DMD thread] SLEEP FAILED. Result=%d\n", rs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll.
|
printf("[DMD thread exiting]\n");
|
||||||
if (kb_pending && dmd_rx_keyboard(last_kb_char)) {
|
|
||||||
printf("[run_dmd] Just sent char 0x%02x\n", last_kb_char);
|
|
||||||
kb_pending = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_display((GtkWidget *)user_data);
|
pthread_exit(NULL);
|
||||||
return G_SOURCE_CONTINUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -204,7 +233,7 @@ activate(GtkApplication *app, gpointer user_data)
|
||||||
|
|
||||||
gtk_container_add(GTK_CONTAINER(frame), drawing_area);
|
gtk_container_add(GTK_CONTAINER(frame), drawing_area);
|
||||||
|
|
||||||
g_timeout_add(33, run_dmd, drawing_area);
|
g_timeout_add(20, refresh_display, drawing_area);
|
||||||
|
|
||||||
/* Signals used to handle the backing surface */
|
/* Signals used to handle the backing surface */
|
||||||
g_signal_connect(drawing_area, "draw",
|
g_signal_connect(drawing_area, "draw",
|
||||||
|
@ -241,11 +270,13 @@ main(int argc, char *argv[])
|
||||||
|
|
||||||
GtkApplication *app;
|
GtkApplication *app;
|
||||||
int status;
|
int status;
|
||||||
|
long thread_id;
|
||||||
|
int rc;
|
||||||
|
|
||||||
dmd_init();
|
rc = pthread_create(&dmd_thread, NULL, dmd_run, (void *)thread_id);
|
||||||
dmd_reset();
|
if (rc) {
|
||||||
for (int i = 0; i < 1000000; i++) {
|
printf("ERROR: Could not create main DMD cpu thread. Status=%d\n", rc);
|
||||||
dmd_step();
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
app = gtk_application_new("com.loomcom.dmd", G_APPLICATION_FLAGS_NONE);
|
app = gtk_application_new("com.loomcom.dmd", G_APPLICATION_FLAGS_NONE);
|
||||||
|
@ -253,6 +284,19 @@ main(int argc, char *argv[])
|
||||||
|
|
||||||
status = g_application_run(G_APPLICATION(app), argc, argv);
|
status = g_application_run(G_APPLICATION(app), argc, argv);
|
||||||
|
|
||||||
|
void *join_status;
|
||||||
|
rc = pthread_join(dmd_thread, &join_status);
|
||||||
|
if (rc) {
|
||||||
|
printf("ERROR: Could not join thread. Status=%d\n", rc);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Main: DMD thread is done.\n");
|
||||||
|
|
||||||
|
/* if (surface) { */
|
||||||
|
/* cairo_surface_destroy(surface); */
|
||||||
|
/* } */
|
||||||
|
|
||||||
g_object_unref(app);
|
g_object_unref(app);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
Loading…
Reference in New Issue