gigatron/rom/Contrib/dhkolf/libgtemu/gtloader.c
2025-01-28 19:17:01 +03:00

323 lines
7.1 KiB
C

/*
This file is part of libgtemu, a library for Gigatron emulation.
Copyright (C) 2019 David Heiko Kolf
Published under the BSD-2-Clause license.
https://opensource.org/licenses/BSD-2-Clause
*/
#include "gtemu.h"
const char loaderframe = 'L';
const unsigned char initchecksum = (unsigned char) 'g';
const char maxframesize = 60;
enum LoaderState {
L_IDLE = 0,
L1_INIT_FV,
L2_INIT_RH,
L3_HEADER_RH,
L4_PAYLOAD_RH,
L5_FILLER_RH,
L6_CHECKSUM_RH,
L7_FINISH_RH,
LT1_KEYUP_FV,
LT2_KEYDOWN_FV,
LT3_KEYIDLE_FV,
};
static void sendbit (struct GTState *gt, struct GTPeriph *ph,
unsigned char value, int n)
{
gt->in = gt->in << 1 | (1 & (value >> (7 - n)));
ph->loader.bitcount++;
}
static void loadnextsegment (struct GTPeriph *ph)
{
ph->loader.addr = (ph->loader.data[0] << 8) | ph->loader.data[1];
if (ph->loader.data[2] != 0) {
ph->loader.remainingsegment = ph->loader.data[2];
} else {
ph->loader.remainingsegment = 256;
}
ph->loader.remainingdata -= 3;
ph->loader.data += 3;
if (ph->loader.remainingsegment > ph->loader.remainingdata) {
ph->loader.remainingsegment = ph->loader.remainingdata;
}
if (ph->loader.remainingsegment > maxframesize) {
ph->loader.remainingframe = maxframesize;
} else {
ph->loader.remainingframe = ph->loader.remainingsegment;
}
ph->loader.state = L1_INIT_FV;
}
static void sendheaderbit (struct GTState *gt, struct GTPeriph *ph)
{
int bc = ph->loader.bitcount;
if (bc < 8) {
sendbit(gt, ph, loaderframe, bc);
} else if (bc < 14) {
sendbit(gt, ph, ph->loader.remainingframe, bc - 6);
} else if (bc < 22) {
sendbit(gt, ph, (unsigned char) ph->loader.addr, bc - 14);
} else {
sendbit(gt, ph, (unsigned char) (ph->loader.addr >> 8), bc - 22);
if (bc + 1 >= 30) {
ph->loader.checksum += loaderframe +
(loaderframe << 6) +
ph->loader.remainingframe +
ph->loader.addr +
(ph->loader.addr >> 8);
ph->loader.bitcount = 0;
ph->loader.remainingfiller = maxframesize - ph->loader.remainingframe;
if (ph->loader.remainingframe > 0) {
ph->loader.addr += ph->loader.remainingframe;
ph->loader.state = L4_PAYLOAD_RH;
} else {
ph->loader.state = L5_FILLER_RH;
}
}
}
}
static void sendpayloadbit (struct GTState *gt, struct GTPeriph *ph)
{
int bc = ph->loader.bitcount;
sendbit(gt, ph, ph->loader.data[0], bc);
if (bc + 1 >= 8) {
ph->loader.checksum += ph->loader.data[0];
ph->loader.bitcount = 0;
ph->loader.remainingframe--;
ph->loader.remainingsegment--;
ph->loader.remainingdata--;
ph->loader.data++;
if (ph->loader.remainingframe == 0) {
if (ph->loader.remainingfiller > 0) {
ph->loader.state = L5_FILLER_RH;
} else {
ph->loader.checksum = -ph->loader.checksum;
ph->loader.state = L6_CHECKSUM_RH;
}
}
}
}
static void sendfillerbit (struct GTState *gt, struct GTPeriph *ph)
{
int bc = ph->loader.bitcount;
sendbit(gt, ph, 0, bc);
if (bc + 1 >= 8) {
ph->loader.bitcount = 0;
ph->loader.remainingfiller--;
if (ph->loader.remainingfiller == 0) {
ph->loader.checksum = -ph->loader.checksum;
ph->loader.state = L6_CHECKSUM_RH;
}
}
}
static void sendchecksumbit (struct GTState *gt, struct GTPeriph *ph)
{
int bc = ph->loader.bitcount;
sendbit(gt, ph, ph->loader.checksum, bc);
if (bc + 1 >= 8) {
if (ph->loader.remainingsegment > 0) {
if (ph->loader.remainingsegment > maxframesize) {
ph->loader.remainingframe = maxframesize;
} else {
ph->loader.remainingframe = (unsigned char) ph->loader.remainingsegment;
}
ph->loader.state = L1_INIT_FV;
} else if (ph->loader.remainingdata > 2) {
if (ph->loader.data[0] == 0) {
ph->loader.addr = (ph->loader.data[1] << 8) | ph->loader.data[2];
ph->loader.remainingdata = 0;
ph->loader.state = L1_INIT_FV;
} else {
loadnextsegment(ph);
}
} else {
ph->loader.state = L7_FINISH_RH;
}
}
}
static void sendtextrelease (struct GTState *gt, struct GTPeriph *ph)
{
ph->loader.remainingframe--;
if (ph->loader.remainingframe == 0) {
ph->loader.remainingframe = 3;
gt->in = 0xff;
if (ph->loader.remainingdata > 0) {
ph->loader.state = LT2_KEYDOWN_FV;
} else {
ph->loader.state = LT3_KEYIDLE_FV;
}
}
}
static void sendtextbyte (struct GTState *gt, struct GTPeriph *ph)
{
ph->loader.remainingframe--;
if (ph->loader.remainingframe == 0) {
gt->in = ph->loader.data[0];
if (gt->in == '\n') {
ph->loader.remainingframe = 9;
} else {
ph->loader.remainingframe = 3;
}
ph->loader.data++;
ph->loader.remainingdata--;
ph->loader.state = LT1_KEYUP_FV;
}
}
void gtloader_onfallingvsync (struct GTState *gt, struct GTPeriph *ph)
{
switch (ph->loader.state) {
case L1_INIT_FV:
ph->loader.state = L2_INIT_RH;
return;
case LT1_KEYUP_FV:
sendtextrelease(gt, ph);
return;
case LT2_KEYDOWN_FV:
sendtextbyte(gt, ph);
return;
case LT3_KEYIDLE_FV:
ph->loader.remainingframe--;
if (ph->loader.remainingframe == 0) {
ph->loader.state = L_IDLE;
}
return;
default:
return;
}
}
void gtloader_onrisinghsync (struct GTState *gt, struct GTPeriph *ph)
{
switch (ph->loader.state) {
case L2_INIT_RH:
ph->loader.bitcount = 0;
ph->loader.state = L3_HEADER_RH;
return;
case L3_HEADER_RH:
sendheaderbit(gt, ph);
return;
case L4_PAYLOAD_RH:
sendpayloadbit(gt, ph);
return;
case L5_FILLER_RH:
sendfillerbit(gt, ph);
return;
case L6_CHECKSUM_RH:
sendchecksumbit(gt, ph);
return;
case L7_FINISH_RH:
gt->in = 0xff;
ph->loader.state = L_IDLE;
return;
}
}
int gtloader_sendgt1 (struct GTPeriph *ph,
const char *data, size_t datasize)
{
if (ph->loader.state != L_IDLE) {
return 0;
}
if (datasize < 3) {
return -1;
}
ph->loader.data = (const unsigned char *) data;
ph->loader.remainingdata = datasize;
ph->loader.checksum = initchecksum;
loadnextsegment(ph);
return 1;
}
int gtloader_isactive (struct GTPeriph *ph)
{
return ph->loader.state != L_IDLE;
}
int gtloader_validategt1 (const char *data, size_t datasize)
{
const unsigned char *ud = (const unsigned char *) data;
unsigned char adhi;
if (datasize < 3) {
return 0;
}
adhi = ud[0];
do {
unsigned char adlo = ud[1];
int size = ud[2];
if (size == 0) {
size = 256;
}
ud += 3;
datasize -= 3;
if (size + 3 > datasize) {
return 0;
}
if (size + adlo > 256) {
return 0;
}
ud += size;
datasize -= size;
adhi = ud[0];
} while (adhi != 0 && datasize > 3);
if (datasize < 3) {
return 0;
}
return 1;
}
int gtloader_sendtext (struct GTPeriph *ph,
const char *data, size_t datasize)
{
if (ph->loader.state != L_IDLE) {
return 0;
}
ph->loader.data = (const unsigned char *) data;
ph->loader.remainingdata = datasize;
ph->loader.remainingframe = 3;
ph->loader.state = LT1_KEYUP_FV;
return 1;
}
int gtloader_sendkey (struct GTState *gt, struct GTPeriph *ph, char key)
{
if (ph->loader.state != L_IDLE &&
(ph->loader.state != LT1_KEYUP_FV ||
ph->loader.remainingdata > 0) &&
ph->loader.state != LT3_KEYIDLE_FV) {
return 0;
}
if (ph->loader.state == L_IDLE || ph->loader.prevkey != key) {
ph->loader.prevkey = key;
gt->in = (unsigned char) key;
ph->loader.remainingdata = 0;
ph->loader.remainingframe = 3;
ph->loader.state = LT1_KEYUP_FV;
}
return 1;
}