Initial push of pre-existing code.

This commit is contained in:
Mark Long 2015-12-01 10:24:51 -06:00
parent 2d532c386b
commit 1bf321072d
31 changed files with 1520 additions and 0 deletions

42
src/AppleSAWS.pro Normal file
View File

@ -0,0 +1,42 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = AppleSAWS
TEMPLATE = app
SOURCES += main.cpp \
diskfile.cxx \
sector.cxx \
vtoc.cxx \
catalogsector.cxx \
applestring.cxx \
tracksectorlist.cxx \
filedescriptiveentry.cxx \
applesoftfile.cxx \
genericfile.cxx \
disassembler.cxx \
binaryfile.cxx \
catalogwidget.cxx \
mainwindow.cxx
HEADERS += \
diskfile.h \
sector.h \
vtoc.h \
util.h \
catalogsector.h \
applestring.h \
tracksectorlist.h \
filedescriptiveentry.h \
applesoftfile.h \
genericfile.h \
disassembler.h \
binaryfile.h \
catalogwidget.h \
mainwindow.h
FORMS += \
catalogwidget.ui \
mainwindow.ui

195
src/applesoftfile.cxx Normal file
View File

@ -0,0 +1,195 @@
#include "applesoftfile.h"
#include <QDebug>
ApplesoftFile::ApplesoftFile(QByteArray data) : GenericFile(data)
{
if (m_tokens.size() == 0)
{
makeTokens();
}
if (!data.isEmpty())
{
setData(data);
}
}
void ApplesoftFile::setData(QByteArray data)
{
GenericFile::setData(data);
quint8 addlo = m_data.at(0);
quint8 addhi = m_data.at(1);
m_length = addlo + (addhi * 256);
m_data.remove(0,2);
m_detokenized = detokenize();
}
QList<ApplesoftLine> ApplesoftFile::detokenize()
{
QList<ApplesoftLine> retval;
/*
qDebug() << "Length: " << m_length;
for (int idx = 0; idx < m_length; idx += 8)
{
qDebug()
<< QString("%1 (%2): %3 %4 %5 %6 %7 %8 %9 %10")
.arg((quint16) idx + 0x801,4,16,QChar('0')).toUpper()
.arg((quint16) idx + 0x801,4,10,QChar('0'))
.arg((quint8) m_data[idx],2,16,QChar('0')).toUpper()
.arg((quint8) m_data[idx+1],2,16,QChar('0')).toUpper()
.arg((quint8) m_data[idx+2],2,16,QChar('0')).toUpper()
.arg((quint8) m_data[idx+3],2,16,QChar('0')).toUpper()
.arg((quint8) m_data[idx+4],2,16,QChar('0')).toUpper()
.arg((quint8) m_data[idx+5],2,16,QChar('0')).toUpper()
.arg((quint8) m_data[idx+6],2,16,QChar('0')).toUpper()
.arg((quint8) m_data[idx+7],2,16,QChar('0')).toUpper();
}
*/
int idx = 0;
quint8 val = 0;
while (idx < m_data.length()) {
ApplesoftLine line;
line.next_address = (quint8) m_data[idx] + (((quint8) m_data[idx+1]) *256);
line.tokens.append(m_data[idx]);
line.tokens.append(m_data[idx+1]);
idx++; idx++;
line.linenum = (quint8) m_data[idx] + (((quint8) m_data[idx+1])*256);
line.tokens.append(m_data[idx]);
line.tokens.append(m_data[idx+1]);
idx++; idx++;
if (line.next_address == 0x00) { break; }
do {
val = m_data[idx++];
line.tokens.append(val);
if (val >= 0x80) {
line.detokenized_line.append(" " + m_tokens[val]);
} else {
if (val >= 0x20) {
line.detokenized_line.append(val);
} else if (val == 0x7F) {
QChar ch = QChar(0x2401);
line.detokenized_line.append(ch);
}else if (val > 0x00) {
QChar ch = QChar(0x00 + val);
line.detokenized_line.append(ch);
}
}
} while (val != 0x00);
retval.append(line);
}
m_data_end = idx;
if (idx < m_data.length()) {
qDebug() << QString("%1 byte(s) unaccounted for.").arg(m_data.length() - idx);
}
return retval;
}
void ApplesoftFile::list() {
foreach (ApplesoftLine line,detokenized()) {
QString debugline = QString("%1 %2 %3").arg(line.next_address,4,16,QChar('0')).arg(line.linenum).arg(line.detokenized_line);
qDebug() << debugline;
/*
debugline = "";
foreach (quint8 val, line.tokens) {
debugline.append(QString("%1 ").arg(val,2,16,QChar('0')));
}
qDebug() << " " << debugline;
qDebug() << "\n";
*/
}
qDebug() << extraDataHexValues().join("\n");
}
QStringList ApplesoftFile::extraDataHexValues() {
QStringList retval;
QString debugline = "";
int count = 0;
foreach (quint8 val, extraData()) {
debugline.append(QString("%1").arg(val,2,16,QChar('0')).toUpper());
count++;
if (count == 16) {
retval.append(debugline);
debugline = "";
count = 0;
} else {
debugline.append(" ");
}
}
if (!debugline.simplified().isEmpty()) {
retval.append(debugline);
}
return retval;
}
QByteArray ApplesoftFile::extraData()
{
return m_data.mid(m_data_end);
}
void ApplesoftFile::makeTokens()
{
m_tokens[0x80] = "END "; m_tokens[0x81] = "FOR "; m_tokens[0x82] = "NEXT ";
m_tokens[0x83] = "DATA "; m_tokens[0x84] = "INPUT "; m_tokens[0x85] = " DEL ";
m_tokens[0x86] = "DIM "; m_tokens[0x87] = "READ "; m_tokens[0x88] = "GR ";
m_tokens[0x89] = "TEXT "; m_tokens[0x8A] = "PR# "; m_tokens[0x8B] = "IN# ";
m_tokens[0x8C] = "CALL "; m_tokens[0x8D] = "PLOT "; m_tokens[0x8E] = "HLIN ";
m_tokens[0x8F] = "VLIN "; m_tokens[0x90] = "HGR2 "; m_tokens[0x91] = "HGR ";
m_tokens[0x92] = "HCOLOR= "; m_tokens[0x93] = "HPLOT "; m_tokens[0x94] = "DRAW ";
m_tokens[0x95] = "XDRAW "; m_tokens[0x96] = "HTAB "; m_tokens[0x97] = "HOME ";
m_tokens[0x98] = "ROT= "; m_tokens[0x99] = "SCALE= "; m_tokens[0x9A] = "SHLOAD ";
m_tokens[0x9B] = "TRACE "; m_tokens[0x9C] = "NOTRACE "; m_tokens[0x9D] = "NORMAL ";
m_tokens[0x9E] = "INVERSE "; m_tokens[0x9F] = "FLASH "; m_tokens[0xA0] = "COLOR= ";
m_tokens[0xA1] = "POP "; m_tokens[0xA2] = "VTAB "; m_tokens[0xA3] = "HIMEM: ";
m_tokens[0xA4] = "LOMEM: "; m_tokens[0xA5] = "ONERR "; m_tokens[0xA6] = "RESUME ";
m_tokens[0xA7] = "RECALL "; m_tokens[0xA8] = "STORE "; m_tokens[0xA9] = "SPEED= ";
m_tokens[0xAA] = "LET "; m_tokens[0xAB] = "GOTO "; m_tokens[0xAC] = "RUN ";
m_tokens[0xAD] = "IF "; m_tokens[0xAE] = "RESTORE "; m_tokens[0xAF] = "& ";
m_tokens[0xB0] = "GOSUB "; m_tokens[0xB1] = "RETURN "; m_tokens[0xB2] = "REM ";
m_tokens[0xB3] = "STOP "; m_tokens[0xB4] = "ON "; m_tokens[0xB5] = "WAIT ";
m_tokens[0xB6] = "LOAD "; m_tokens[0xB7] = "SAVE "; m_tokens[0xB8] = "DEF ";
m_tokens[0xB9] = "POKE "; m_tokens[0xBA] = "PRINT "; m_tokens[0xBB] = "CONT ";
m_tokens[0xBC] = "LIST "; m_tokens[0xBD] = "CLEAR "; m_tokens[0xBE] = "GET ";
m_tokens[0xBF] = "NEW "; m_tokens[0xC0] = "TAB "; m_tokens[0xC1] = " TO ";
m_tokens[0xC2] = "FN "; m_tokens[0xC3] = "SPC( "; m_tokens[0xC4] = "THEN ";
m_tokens[0xC5] = " AT "; m_tokens[0xC6] = "NOT "; m_tokens[0xC7] = " STEP ";
m_tokens[0xC8] = "+ "; m_tokens[0xC9] = "- "; m_tokens[0xCA] = "* ";
m_tokens[0xCB] = "/ "; m_tokens[0xCC] = "^ "; m_tokens[0xCD] = " AND";
m_tokens[0xCE] = " OR"; m_tokens[0xCF] = "> "; m_tokens[0xD0] = "= ";
m_tokens[0xD1] = "< "; m_tokens[0xD2] = "SGN "; m_tokens[0xD3] = "INT ";
m_tokens[0xD4] = "ABS "; m_tokens[0xD5] = "USR "; m_tokens[0xD6] = "FRE ";
m_tokens[0xD7] = "SCRN ( "; m_tokens[0xD8] = "PDL "; m_tokens[0xD9] = "POS ";
m_tokens[0xDA] = "SQR "; m_tokens[0xDB] = "RND "; m_tokens[0xDC] = "LOG ";
m_tokens[0xDD] = "EXP "; m_tokens[0xDE] = "COS "; m_tokens[0xDF] = "SIN ";
m_tokens[0xE0] = "TAN "; m_tokens[0xE1] = "ATN "; m_tokens[0xE2] = "PEEK ";
m_tokens[0xE3] = "LEN "; m_tokens[0xE4] = "STR$ "; m_tokens[0xE5] = "VAL ";
m_tokens[0xE6] = "ASC "; m_tokens[0xE7] = "CHR$ "; m_tokens[0xE8] = "LEFT$ ";
m_tokens[0xE9] = "RIGHT$ "; m_tokens[0xEA] = "MID$ "; m_tokens[0xEB] = "{Token 0xEB} ";
m_tokens[0xEC] = "{Token 0xEC} ";
m_tokens[0xED] = "{Token 0xED} ";
m_tokens[0xEE] = "{Token 0xEE} ";
m_tokens[0xEF] = "{Token 0xEF} ";
m_tokens[0xF0] = "{Token 0xF0} ";
m_tokens[0xF1] = "{Token 0xF1} ";
m_tokens[0xF2] = "{Token 0xF2} ";
m_tokens[0xF3] = "{Token 0xF3} ";
m_tokens[0xF4] = "{Token 0xF4} ";
m_tokens[0xF5] = "{Token 0xF5} ";
m_tokens[0xF6] = "{Token 0xF6} ";
m_tokens[0xF7] = "{Token 0xF7} ";
m_tokens[0xF8] = "{Token 0xF8} ";
m_tokens[0xF9] = "{Token 0xF9} ";
m_tokens[0xFA] = "{Token 0xFA} ";
m_tokens[0xFB] = "{Token 0xFB} ";
m_tokens[0xFC] = "{Token 0xFC} ";
m_tokens[0xFD] = "{Token 0xFD} ";
m_tokens[0xFE] = "{Token 0xFE} ";
m_tokens[0xFF] = "{Token 0xFF} ";
}

38
src/applesoftfile.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef APPLESOFTFILE_H
#define APPLESOFTFILE_H
#include <QByteArray>
#include <QStringList>
#include <QMap>
#include "genericfile.h"
struct ApplesoftLine {
quint16 next_address;
quint16 linenum;
QByteArray tokens;
QString detokenized_line;
};
class ApplesoftFile : public GenericFile
{
public:
ApplesoftFile(QByteArray data = QByteArray());
void setData(QByteArray data);
QByteArray extraData();
QList<ApplesoftLine> detokenized() { return m_detokenized; }
void list();
QStringList extraDataHexValues();
private:
void makeTokens();
QList<ApplesoftLine> detokenize();
int m_data_end;
QMap<quint8,QString> m_tokens;
quint16 m_length;
QList<ApplesoftLine> m_detokenized;
};
#endif // APPLESOFTFILE_H

78
src/applestring.cxx Normal file
View File

@ -0,0 +1,78 @@
#include "applestring.h"
QString AppleString::printable() const
{
QString retval;
foreach (quint8 ch, *this) {
retval.append(AppleChar(ch).printable());
}
return retval;
}
QVector<TextAttribute> AppleString::attributes() const
{
QVector<TextAttribute> retval(this->length());
int idx = 0;
foreach (quint8 ch, *this) {
retval[idx++] = AppleChar(ch).getAttribute();
}
return retval;
}
QChar AppleChar::printable() const
{
quint8 newval;
switch (getTextSet()) {
case SetInvUC:
newval = m_val+0x40;
break;
case SetInvSp:
newval = m_val;
break;
case SetFlUC:
newval = m_val;
break;
case SetFlSp:
newval = m_val-0x40;
break;
case SetNormUC:
newval = m_val-0x40;
break;
case SetNormSp:
newval = m_val-0x80;
break;
case SetNormAltUC:
newval = m_val-0x80;
break;
case SetNormLC:
default:
newval = m_val-0x80;
break;
}
return QChar(newval);
}
TextAttribute AppleChar::getAttribute() const
{
if (m_val <= 0x3f) { return Inverse; }
if (m_val <= 0x7f) { return Flash; }
if (m_val <= 0xbf) { return Normal; }
if (m_val <= 0xdf) { return NormalHigh; }
return Normal;
}
TextSet AppleChar::getTextSet() const {
if (m_val < 0x20) { return SetInvUC; }
if (m_val < 0x40) { return SetInvSp; }
if (m_val < 0x60) { return SetFlUC; }
if (m_val < 0x80) { return SetFlSp; }
if (m_val < 0xA0) { return SetNormUC; }
if (m_val < 0xC0) { return SetNormSp; }
if (m_val < 0xE0) { return SetNormAltUC; }
return SetNormLC;
}

32
src/applestring.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef APPLESTRING_H
#define APPLESTRING_H
#include <QVector>
#include <QChar>
#include <QString>
#include <QByteArray>
#include "util.h"
class AppleChar {
public:
AppleChar() { m_val = 0; }
AppleChar(quint8 ch) { m_val = ch; }
QChar printable() const;
TextAttribute getAttribute() const;
TextSet getTextSet() const;
private:
quint8 m_val;
};
class AppleString : public QByteArray {
public:
QString printable() const;
QVector<TextAttribute> attributes() const;
};
#endif // APPLESTRING_H

28
src/binaryfile.cxx Normal file
View File

@ -0,0 +1,28 @@
#include <QDebug>
#include "binaryfile.h"
BinaryFile::BinaryFile(QByteArray data) : GenericFile(data)
{
m_length = 0;
m_address = 0;
if (!data.isEmpty()) {
setData(data);
}
}
void BinaryFile::setData(QByteArray data)
{
if (data.length() >= 4) {
QByteArray metadata = data.left(4);
m_data = data.mid(4);
m_address = (quint8) metadata[0] + ((quint8) metadata[1]*256);
m_length = (quint8) metadata[2] + ((quint8) metadata[3]*256);
}
}
void BinaryFile::dump()
{
qDebug() << QString("Address: %1 Length: %2").arg(m_address,4,16,QChar('0')).arg(m_length,4,16,QChar('0')).toUpper();
qDebug() << QString("Data Length Recorded: %1").arg(m_data.length(),4,16,QChar('0')).toUpper();
}

23
src/binaryfile.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef BINARYFILE_H
#define BINARYFILE_H
#include "genericfile.h"
class BinaryFile : public GenericFile
{
public:
BinaryFile(QByteArray data = QByteArray());
void setData(QByteArray data);
quint16 address() { return m_address; }
quint16 length() { return m_length; }
QByteArray data() { return m_data; }
void dump();
protected:
quint16 m_address;
quint16 m_length;
};
#endif // BINARYFILE_H

44
src/catalogsector.cxx Normal file
View File

@ -0,0 +1,44 @@
#include "catalogsector.h"
#include "sector.h"
CatalogSector::CatalogSector(Sector *data)
{
m_data = data;
m_next.track = m_data->rawData()[0x01];
m_next.sector = m_data->rawData()[0x02];
for (int idx = 0; idx<7; idx++)
{
FileDescriptiveEntry fde = makeFDE(idx*0x23+0x0B);
if (fde.firstTSListSector != TSPair(0,0)) {
m_fdes.append(fde);
// qDebug() << "FDE #"<<idx;
// fde.dump();
}
}
}
void CatalogSector::dumpFDEs() {
for (int idx = 0; idx<7; idx++)
{
FileDescriptiveEntry fde = m_fdes[idx];
if (fde.firstTSListSector != TSPair(0,0)) {
qDebug() << "FDE #"<<idx;
fde.dump();
}
}
}
FileDescriptiveEntry CatalogSector::makeFDE(int offset)
{
FileDescriptiveEntry fde;
fde.firstTSListSector.track = m_data->rawData()[offset + 0x00];
fde.firstTSListSector.sector = m_data->rawData()[offset + 0x01];
fde.fileTypeAndFlags = m_data->rawData()[offset + 0x02];
fde.lengthInSectors = m_data->rawData()[offset + 0x21] + (m_data->rawData()[offset + 0x22] * 256);
for (int idx = 0x03; idx <= 0x20; idx++) {
fde.filename.append(m_data->rawData()[idx+offset]);
}
return fde;
}

43
src/catalogsector.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef CATALOGSECTOR_H
#define CATALOGSECTOR_H
#include <Qt>
#include <QByteArray>
#include <QDebug>
#include "util.h"
#include "applestring.h"
#include "filedescriptiveentry.h"
class Sector;
class CatalogSector
{
public:
CatalogSector(Sector *sector);
FileDescriptiveEntry &getFDE(quint8 number) {
if (number >= m_fdes.length()) {
number = m_fdes.length() - 1;
}
return m_fdes[number];
}
QList<FileDescriptiveEntry> getFDEs() { return m_fdes; }
TSPair nextCatalogSector() { return m_next; }
void dumpFDEs();
private:
FileDescriptiveEntry makeFDE(int offset);
private:
Sector *m_data;
QList<FileDescriptiveEntry> m_fdes;
TSPair m_next;
};
#endif // CATALOGSECTOR_H

61
src/catalogwidget.cxx Normal file
View File

@ -0,0 +1,61 @@
#include "catalogwidget.h"
#include "ui_catalogwidget.h"
#include "filedescriptiveentry.h"
#include <QUrl>
CatalogWidget::CatalogWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::CatalogWidget)
{
ui->setupUi(this);
ui->catalog_list->setFont(QFont("monospace"));
}
CatalogWidget::~CatalogWidget()
{
delete ui;
}
void CatalogWidget::prepForNewDisk(QString filename, DiskFile *disk)
{
m_disk = disk;
m_diskname = filename;
}
void CatalogWidget::processNewlyLoadedDisk(QString diskfilename, DiskFile *disk)
{
if (m_disk == disk) {
QUrl url = QUrl::fromLocalFile(diskfilename);
QString shortfilename = url.fileName();
QFontMetrics *fm = new QFontMetrics(ui->catalog_list->font());
QRect maxrect;
ui->volume_label->setText(shortfilename);
foreach(FileDescriptiveEntry fde, m_disk->getAllFDEs()) {
QString filetype = fde.fileType();
QString filename = AppleString(fde.filename).printable().simplified();
int size = fde.lengthInSectors;
bool locked = fde.isLocked();
QString sizeStr = QString("%1").arg(size,5,10,QChar(' ')).toUpper();
QString text = QString("%1 %2 %3 %4").arg(locked?"*":" ").arg(sizeStr).arg(filetype).arg(filename);
ui->catalog_list->addItem(new QListWidgetItem(text));
QRect rect = fm->boundingRect(text);
if (rect.width() > maxrect.width()) {
maxrect = rect;
}
}
ui->catalog_list->resize(maxrect.width(),ui->catalog_list->size().height());
}
}
void CatalogWidget::unloadDisk(DiskFile *disk)
{
if (m_disk == disk) {
m_disk = 0;
}
ui->catalog_list->clear();
ui->volume_label->clear();
}

37
src/catalogwidget.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef CATALOGWIDGET_H
#define CATALOGWIDGET_H
#include <QWidget>
#include "diskfile.h"
namespace Ui {
class CatalogWidget;
}
class CatalogWidget : public QWidget
{
Q_OBJECT
public:
explicit CatalogWidget(QWidget *parent = 0);
~CatalogWidget();
public slots:
void prepForNewDisk(QString filename, DiskFile *disk);
void processNewlyLoadedDisk(QString filename, DiskFile *disk);
void unloadDisk(DiskFile *disk);
signals:
void newFileSelected(FileDescriptiveEntry *entry);
private:
Ui::CatalogWidget *ui;
DiskFile *m_disk;
QString m_diskname;
};
#endif // CATALOGWIDGET_H

42
src/catalogwidget.ui Normal file
View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CatalogWidget</class>
<widget class="QWidget" name="CatalogWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>328</width>
<height>560</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="volume_label">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="catalog_list"/>
</item>
<item>
<widget class="QLabel" name="file_label">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

7
src/disassembler.cxx Normal file
View File

@ -0,0 +1,7 @@
#include "disassembler.h"
QStringList Disassembler::disassemble(quint16 address, QByteArray values)
{
return QStringList() << "TODO";
}

13
src/disassembler.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef DISASSEMBLER_H
#define DISASSEMBLER_H
#include <QByteArray>
#include <QStringList>
class Disassembler
{
public:
static QStringList disassemble(quint16 address, QByteArray values);
};
#endif // DISASSEMBLER_H

109
src/diskfile.cxx Normal file
View File

@ -0,0 +1,109 @@
#include "diskfile.h"
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include "tracksectorlist.h"
DiskFile::DiskFile(QString filename)
{
if (!filename.isEmpty())
{
read(filename);
}
}
bool DiskFile::read(QString filename)
{
QFile infile(filename);
if (infile.open(QIODevice::ReadOnly))
{
QDataStream qds(&infile);
for (int track = 0; track < 35; track++)
{
for (int sector = 0; sector < 16; sector++)
{
char buffer[256];
if (qds.readRawData(buffer,256) == 256)
{
Sector sec;
sec.setTrackSector(track,sector);
sec.setData(QByteArray(buffer,256));
m_contents[track][sector] = sec;
}
else
{
qDebug() << "Invalid sector read!";
return false;
}
}
}
return true;
}
else
qDebug() << "Could not open file " << filename;
return false;
}
VTOC DiskFile::getVTOC()
{
return getSector(17,0).promoteToVTOC();
}
QList<CatalogSector> DiskFile::getCatalogSectors()
{
QList<CatalogSector> retval;
VTOC vtoc = getVTOC();
TSPair ts = vtoc.firstCatalogSector();
CatalogSector cs = getSector(ts).promoteToCatalogSector();
retval.append(cs);
while (cs.nextCatalogSector() != TSPair(0,0)) {
ts = cs.nextCatalogSector();
cs = getSector(ts).promoteToCatalogSector();
retval.append(cs);
}
return retval;
}
QByteArray DiskFile::getFile(FileDescriptiveEntry fde)
{
QByteArray retval;
TrackSectorList tsl = getSector(fde.firstTSListSector).promoteToTrackSectorList();
return getDataFromTrackSectorList(tsl);
}
QByteArray DiskFile::getDataFromTrackSectorList(TrackSectorList tsl)
{
QByteArray retval;
foreach(TSPair pair, tsl.getDataTSPairs())
{
Sector sec = getSector(pair);
retval.append(sec.rawData());
}
if (tsl.getNextTSList() != TSPair(0,0)) {
TrackSectorList nextTsl = getSector(tsl.getNextTSList()).promoteToTrackSectorList();
retval.append(getDataFromTrackSectorList(nextTsl));
}
return retval;
}
QList<FileDescriptiveEntry> DiskFile::getAllFDEs() {
QList<FileDescriptiveEntry> retval;
QList<CatalogSector> sectors = getCatalogSectors();
foreach (CatalogSector cs, sectors)
{
QList<FileDescriptiveEntry> fdes = cs.getFDEs();
retval.append(fdes);
}
return retval;
}

45
src/diskfile.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef DISKFILE_H
#define DISKFILE_H
#include <Qt>
#include <QMap>
#include <QString>
#include <QDebug>
#include "util.h"
#include "sector.h"
#include "vtoc.h"
class DiskFile
{
public:
DiskFile(QString filename = "");
bool read(QString filename);
Sector &getSector(TSPair ts) { return getSector(ts.track, ts.sector); }
Sector &getSector(int track, int sector) {
return m_contents[track][sector];
}
VTOC getVTOC();
QList<CatalogSector> getCatalogSectors();
QByteArray getFile(FileDescriptiveEntry fde);
QByteArray getDataFromTrackSectorList(TrackSectorList tsl);
QList<FileDescriptiveEntry> getAllFDEs();
private:
QMap< int, QMap< int, Sector> > m_contents;
};
#endif // DISKFILE_H

View File

@ -0,0 +1,2 @@
#include "filedescriptiveentry.h"

View File

@ -0,0 +1,44 @@
#ifndef FILEDESCRIPTIVEENTRY_H
#define FILEDESCRIPTIVEENTRY_H
#include "util.h"
#include <QDebug>
#include "applestring.h"
struct FileDescriptiveEntry {
TSPair firstTSListSector;
int fileTypeAndFlags;
AppleString filename;
quint16 lengthInSectors;
QString fileType() {
if (fileTypeAndFlags & IntegerBasicFile) { return "I"; }
if (fileTypeAndFlags & ApplesoftBasicFile) { return "A"; }
if (fileTypeAndFlags & RelocatableFile) { return "R"; }
if (fileTypeAndFlags & RawBinaryFile) { return "B"; }
if (fileTypeAndFlags & TypeSFile) { return "S"; }
if (fileTypeAndFlags & TypeAFile) { return "a"; }
if (fileTypeAndFlags & TypeBFile) { return "b"; }
return "T";
}
bool isLocked() { return (fileTypeAndFlags & IsLocked); }
void dump() {
qDebug() << "First TS List Sector: Track: " << QString("%1").arg(firstTSListSector.track,2,16,QChar('0')).toUpper()
<< " Sector: " << QString("%1").arg(firstTSListSector.sector,2,16,QChar('0')).toUpper();
qDebug() << "File Type and Flags: " << QString::number((quint8)fileTypeAndFlags) << "(" << fileType() << "," << (isLocked()?"Locked":"Unlocked") << ")";
qDebug() << "Filename: " << filename.printable();
qDebug() << "Length in Sectors: " << lengthInSectors;
}
void catalog() {
QString output = QString("%1 %2 %3 %4").arg(QString(isLocked()?"*":" "))
.arg(lengthInSectors,3,10,QChar('0'))
.arg(fileType())
.arg(filename.printable().trimmed());
qDebug() << output;
}
};
#endif // FILEDESCRIPTIVEENTRY_H

13
src/genericfile.cxx Normal file
View File

@ -0,0 +1,13 @@
#include "genericfile.h"
GenericFile::GenericFile(QByteArray data)
{
if (!data.isEmpty()) {
setData(data);
}
}
void GenericFile::setData(QByteArray data)
{
m_data = data;
}

16
src/genericfile.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef GENERICFILE_H
#define GENERICFILE_H
#include <QByteArray>
class GenericFile
{
public:
GenericFile(QByteArray data = QByteArray());
virtual void setData(QByteArray data);
protected:
QByteArray m_data;
};
#endif // GENERICFILE_H

48
src/main.cpp Normal file
View File

@ -0,0 +1,48 @@
#include <QApplication>
#include "mainwindow.h"
#include "binaryfile.h"
#include "genericfile.h"
#include "diskfile.h"
#include "catalogsector.h"
#include "applesoftfile.h"
int main(int argc, char** argv)
{
/*
// DiskFile df("/home/mlong/Desktop/dos.3.3.system.master.dsk");
DiskFile df("/home/mlong/Desktop/missing_ring_good.dsk");
df.getVTOC().dump();
QList<CatalogSector> sectors = df.getCatalogSectors();
FileDescriptiveEntry toLoad;
foreach (FileDescriptiveEntry fde, df.getAllFDEs()) {
fde.catalog();
if (fde.filename.printable().contains("ASM.DATA")) {
toLoad = fde;
}
}
QByteArray file = df.getFile(toLoad);
BinaryFile myfile;
myfile.setData(file);
myfile.dump();
//ApplesoftFile af(file);
//af.list();
*/
QApplication a(argc, argv);
MainWindow w;
w.loadDiskFile("/home/mlong/Desktop/missing_ring_good.dsk");
w.show();
return a.exec();
}

63
src/mainwindow.cxx Normal file
View File

@ -0,0 +1,63 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_disk = 0;
connect(ui->action_Quit, SIGNAL(triggered()), qApp, SLOT(quit()));
connect(ui->action_Load_Disk_Image, SIGNAL(triggered()), SLOT(showLoadDialog()));
connect(this, SIGNAL(diskFileLoading(QString, DiskFile*)),
ui->catalogWidget, SLOT(prepForNewDisk(QString,DiskFile*)));
connect(this, SIGNAL(diskFileLoaded(QString,DiskFile*)),
ui->catalogWidget, SLOT(processNewlyLoadedDisk(QString,DiskFile*)));
connect(this, SIGNAL(diskFileUnloading(DiskFile*)),
ui->catalogWidget, SLOT(unloadDisk(DiskFile*)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::loadDiskFile(QString filename)
{
if (m_disk) {
unloadDiskFile();
}
m_disk = new DiskFile();
emit diskFileLoading(filename,m_disk);
if (m_disk->read(filename)) {
emit diskFileLoaded(filename,m_disk);
} else {
emit diskFileLoadFailed(filename,m_disk);
delete m_disk;
m_disk = 0;
}
}
void MainWindow::unloadDiskFile()
{
emit diskFileUnloading(m_disk);
delete m_disk;
m_disk = 0;
emit diskFileUnloaded();
}
void MainWindow::showLoadDialog()
{
QString filename = QFileDialog::getOpenFileName(this,
tr("Open Disk Image"),
"/home/mlong/Desktop",
"Disk Images (*.do *.dsk)");
if (!filename.isEmpty()) {
loadDiskFile(filename);
}
}

39
src/mainwindow.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "diskfile.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void loadDiskFile(QString filename);
void unloadDiskFile();
void showLoadDialog();
signals:
void diskFileLoading(QString filename, DiskFile *file);
void diskFileLoaded(QString filename, DiskFile *file);
void diskFileLoadFailed(QString filename, DiskFile *file);
void diskFileUnloading(DiskFile *file);
void diskFileUnloaded();
private:
Ui::MainWindow *ui;
DiskFile *m_disk;
};
#endif // MAINWINDOW_H

116
src/mainwindow.ui Normal file
View File

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>AppleSAWS</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="CatalogWidget" name="catalogWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>400</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
<widget class="QMdiArea" name="mdiArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Expanding">
<horstretch>3</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>20</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
<property name="title">
<string>&amp;File</string>
</property>
<addaction name="action_Load_Disk_Image"/>
<addaction name="separator"/>
<addaction name="action_Quit"/>
</widget>
<addaction name="menu_File"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<action name="action_Load_Disk_Image">
<property name="text">
<string>&amp;Load Disk Image</string>
</property>
</action>
<action name="action_Quit">
<property name="text">
<string>&amp;Quit</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>CatalogWidget</class>
<extends>QWidget</extends>
<header>catalogwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

47
src/sector.cxx Normal file
View File

@ -0,0 +1,47 @@
#include "sector.h"
#include <QString>
#include <QDebug>
QByteRef Sector::operator[](uint offset) {
if (offset > 255) {
offset = 255;
}
return m_data[offset];
}
bool Sector::setData(QByteArray data) {
if (data.length() != 256) return false;
m_data = data;
return true;
}
void Sector::dump() {
qDebug() << "Dumping Track " << track() << "Sector " << sector() << " ...";
for (int jdx = 0; jdx < 16; jdx++)
{
QString line;
line += QString("%1 (%2): ").arg(jdx*16,2,16,QChar('0')).arg(jdx*16,3,10,QChar(' '));
for (int idx = 0; idx < 16; idx++)
{
int offset = (jdx*16) + idx;
quint8 val = m_data[offset];
line += QString("%1 ").arg(val,2,16,QChar('0'));
if (idx == 7) line += " ";
}
line = line.toUpper();
line += " ";
for (int idx = 0; idx < 16; idx++)
{
int offset = (jdx*16) + idx;
quint8 val = m_data[offset];
if (val > 127) { val -= 128; }
QChar ch(val);
line += QString("%1").arg(ch.isPrint()?ch:'.');
}
qDebug() << line;
}
}

57
src/sector.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef SECTOR_H
#define SECTOR_H
#include <QByteArray>
#include "vtoc.h"
#include "catalogsector.h"
#include "tracksectorlist.h"
class Sector
{
public:
Sector() {
m_data.resize(256);
m_track = 255;
m_sector = 255;
}
VTOC promoteToVTOC() {
return VTOC(this);
}
CatalogSector promoteToCatalogSector() {
return CatalogSector(this);
}
TrackSectorList promoteToTrackSectorList() {
return TrackSectorList(this);
}
int sector() { return m_sector; }
int track() { return m_track; }
void setTrackSector(int track, int sector) {
setTrack(track);
setSector(sector);
}
void setTrack(int track) { m_track = track; }
void setSector(int sector) { m_sector = sector; }
QByteRef operator[](uint offset);
bool setData(QByteArray data);
void dump();
QByteArray rawData() { return m_data; }
private:
QByteArray m_data;
int m_track;
int m_sector;
};
#endif // SECTOR_H

24
src/tracksectorlist.cxx Normal file
View File

@ -0,0 +1,24 @@
#include "tracksectorlist.h"
#include "sector.h"
TrackSectorList::TrackSectorList(Sector *data)
{
m_data = data;
m_next_tslist.track = m_data->rawData()[0x01];
m_next_tslist.sector = m_data->rawData()[0x02];
m_sector_offset.track = m_data->rawData()[0x05];
m_sector_offset.sector = m_data->rawData()[0x06];
for (int idx = 0x0C; idx < 0xff; idx+=2)
{
TSPair ts(m_data->rawData()[idx],m_data->rawData()[idx+1]);
if (ts == TSPair(0,0)) {
break;
} else {
m_ts_pairs_for_data.append(ts);
}
}
}

27
src/tracksectorlist.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef TRACKSECTORLIST_H
#define TRACKSECTORLIST_H
#include "util.h"
class Sector;
class TrackSectorList
{
public:
TrackSectorList(Sector *data);
TSPair getNextTSList() const { return m_next_tslist; }
TSPair getSectorOffset() const { return m_sector_offset; }
QList<TSPair> getDataTSPairs() { return m_ts_pairs_for_data; }
private:
TSPair m_next_tslist;
TSPair m_sector_offset;
QList<TSPair> m_ts_pairs_for_data;
Sector *m_data;
};
#endif // TRACKSECTORLIST_H

58
src/util.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef UTIL_H
#define UTIL_H
#include <QPair>
#include <QByteArray>
#include <QDebug>
typedef enum {
TextFile = 0x00,
IntegerBasicFile = 0x01,
ApplesoftBasicFile = 0x02,
RawBinaryFile = 0x04,
TypeSFile = 0x08,
RelocatableFile = 0x10,
TypeAFile = 0x20,
TypeBFile= 0x40,
IsLocked = 0x80
} FileTypeFlag;
typedef enum {
Inverse = 0x00, // 0x00 -- 0x3F
Flash = 0x01, // 0x40 -- 0x7F
Normal = 0x02, // 0x80 -- 0xBF
NormalHigh = 0x04 // 0xC0 -- 0xFF
} TextAttribute;
typedef enum {
SetInvUC = 0x00,
SetInvSp = 0x20,
SetFlUC = 0x40,
SetFlSp = 0x60,
SetNormUC = 0x80,
SetNormSp = 0xA0,
SetNormAltUC = 0xC0,
SetNormLC = 0xE0
} TextSet;
struct TSPair {
TSPair() { track = sector = 0; }
TSPair(quint8 trackval, quint8 secval) { track=trackval; sector = secval; }
quint8 track;
quint8 sector;
bool operator==(const TSPair &other) {
if (other.track == track && other.sector == sector) return true;
return false;
}
bool operator!=(const TSPair &other) {
return !(operator==(other));
}
void dump() { qDebug() << "TSPair: track: " << track << " sector: " << sector; }
};
#endif // UTIL_H

96
src/vtoc.cxx Normal file
View File

@ -0,0 +1,96 @@
#include "vtoc.h"
#include <QDebug>
#include <QString>
#include "sector.h"
VTOC::VTOC(Sector *data)
{
m_data = data;
}
TSPair VTOC::firstCatalogSector() {
return TSPair(m_data->rawData()[0x01], m_data->rawData()[0x02]);
}
quint8 VTOC::dosVersion() {
return m_data->rawData()[0x03];
}
quint8 VTOC::volumeNumber() {
return m_data->rawData()[0x06];
}
quint8 VTOC::maxTSPairs() {
return m_data->rawData()[0x27];
}
quint8 VTOC::lastTrackAllocated() {
return m_data->rawData()[0x30];
}
qint8 VTOC::directionOfAllocation() {
return m_data->rawData()[0x31];
}
quint8 VTOC::tracksPerDisk() {
return m_data->rawData()[0x34];
}
quint8 VTOC::sectorsPerDisk() {
return m_data->rawData()[0x35];
}
qint16 VTOC::bytesPerSector() {
return m_data->rawData()[0x36] + (256 * m_data->rawData()[0x37]);
}
bool VTOC::isSectorInUse(TSPair ts) {
quint8 track = ts.track;
quint8 sec = ts.sector;
quint8 baseaddr = (track * 4) + 0x38;
quint16 word = (((quint16) m_data->rawData()[baseaddr]) *256) + (quint8) m_data->rawData()[baseaddr+1];
quint16 bitpos = (quint16) 0x01 << (quint16) sec;
return !(word & bitpos);
}
void VTOC::dump()
{
/*
for (qint8 idx = 0x0f; idx >= 0; idx--) {
quint8 shift;
if (idx < 0x08) { shift = idx; } else { shift = idx-0x08; }
qDebug() << "Idx: " << idx << "Shift: " << (quint8) shift << "Bitpos: " << (quint8) (0x01 << shift);
}
*/
qDebug() << "Dumping VTOC Track " << m_data->track() << "Sector " << m_data->sector() << " ...";
qDebug() << " Track number of first catalog sector: " << QString::number(firstCatalogSector().track);
qDebug() << " Sector number of first catalog sector: " << QString::number(firstCatalogSector().sector);
qDebug() << " Release number of DOS used to INIT disk: " << QString::number(dosVersion());
qDebug() << " Disk Volume Number: " << QString::number(volumeNumber());
qDebug() << " Max track/sector pairs that fit in t/s list sector (122=256): " << QString::number(maxTSPairs());
qDebug() << " Last track where sectors were allocated: " << QString::number(lastTrackAllocated());
qDebug() << " Direction of track allocations (+/- 1): " << QString::number(directionOfAllocation());
qDebug() << " Number tracks per disk: " << QString::number(tracksPerDisk());
qDebug() << " Number sectors per disk: " << QString::number(sectorsPerDisk());
qDebug() << " Number bytes/sector: " << QString::number(bytesPerSector());
qDebug() << " Track Usage (.=free, 0-F=used):";
for (quint8 track = 0; track < m_data->rawData()[0x34];track++)
{
qDebug() << " " << QString("Track %1:").arg((int) track,2,10,QChar('0')) << buildUseString(track);
}
}
QString VTOC::buildUseString(quint8 track) {
QString usestr;
for (qint8 sec = 0x0f; sec >= 0; sec--)
{
usestr.append(isSectorInUse(TSPair(track,sec))?QString::number(sec,16).toUpper():"-");
}
return usestr;
}

33
src/vtoc.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef VTOC_H
#define VTOC_H
#include <Qt>
#include "util.h"
class Sector;
class QString;
class VTOC
{
public:
VTOC(Sector *data);
void dump();
TSPair firstCatalogSector();
quint8 dosVersion();
quint8 volumeNumber();
quint8 maxTSPairs();
quint8 lastTrackAllocated();
qint8 directionOfAllocation();
quint8 tracksPerDisk();
quint8 sectorsPerDisk();
qint16 bytesPerSector();
bool isSectorInUse(TSPair ts);
private:
QString buildUseString(quint8 track);
Sector *m_data;
};
#endif // VTOC_H