mirror of
https://github.com/cmosher01/Epple-II.git
synced 2025-01-23 08:30:01 +00:00
read WOZ files instead of NIB files; implement Logic State Sequencer
This commit is contained in:
parent
51085f6802
commit
8a393af531
@ -17,7 +17,7 @@ import motherboard rom 2800 $(PREFIX)lib/apple2/system/a2/monitor.a65
|
||||
|
||||
|
||||
# Disk ][ card with 13-sector ROMs
|
||||
slot 6 disk
|
||||
slot 6 disk13
|
||||
import slot 6 rom 0 $(PREFIX)lib/apple2/dos/13sector/disk2.a65
|
||||
|
||||
# Insert DOS 3.1 System Master disk into drive 1
|
||||
|
@ -45,7 +45,7 @@ import slot 4 rom 0 $(PREFIX)lib/epple2/cards/clock.a65
|
||||
# Disk ][ controller card in slot 5, with 13-sector ROMs.
|
||||
# This will read (DOS 3.1, 3.2, and 3.2.1) disks, which
|
||||
# have 13 sectors per track.
|
||||
slot 5 disk
|
||||
slot 5 disk13
|
||||
import slot 5 rom 0 $(PREFIX)lib/apple2/dos/13sector/disk2.a65
|
||||
# Insert the DOS 3.1 System Master disk into drive 1 of slot 5
|
||||
load slot 5 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_rawdos.nib
|
||||
|
@ -44,7 +44,7 @@ import slot 4 rom 0 $(PREFIX)lib/epple2/cards/clock.a65
|
||||
# Disk ][ controller card in slot 5, with 13-sector ROMs.
|
||||
# This will read (DOS 3.1, 3.2, and 3.2.1) disks, which
|
||||
# have 13 sectors per track.
|
||||
slot 5 disk
|
||||
slot 5 disk13
|
||||
import slot 5 rom 0 $(PREFIX)lib/apple2/dos/13sector/disk2.a65
|
||||
# Insert the DOS 3.1 System Master disk into drive 1 of slot 5
|
||||
load slot 5 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_rawdos.nib
|
||||
|
@ -14,7 +14,7 @@
|
||||
# Demo system ROM for the emulator. This is only to allow the
|
||||
# emulator to do something useful when there are no real Apple ROM
|
||||
# images provided.
|
||||
#
|
||||
#
|
||||
import motherboard rom 2C00 $(PREFIX)lib/epple2/system/epple2sys.a65
|
||||
|
||||
# These are how to load the real (proprietary) Apple ROMs.
|
||||
@ -38,7 +38,7 @@ import slot 2 rom 0 $(PREFIX)lib/epple2/cards/stdin.a65
|
||||
slot 4 clock
|
||||
import slot 4 rom 0 $(PREFIX)lib/epple2/cards/clock.a65
|
||||
|
||||
#slot 5 disk
|
||||
#slot 5 disk13
|
||||
#import slot 5 rom 0 $(PREFIX)lib/apple2/dos/13sector/disk2.a65
|
||||
#load slot 5 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_rawdos.nib
|
||||
|
||||
|
@ -20,7 +20,10 @@ import slot 6 rom 0 /usr/lib/apple2/dos3x/16sector/controller/disk2.ex65
|
||||
|
||||
The first line uses the <<slot>> command to
|
||||
insert a disk contoller card into slot 6, which is the standard
|
||||
slot used for disk cards. The next line uses the <<import>>
|
||||
slot used for disk cards.
|
||||
The +disk+ keyword loads the 16-sector P6 ROM (Logic State Sequencer).
|
||||
Alternatively, use +disk13+ to load the 13-sector P6 ROM (for DOS 3.2 or earlier).
|
||||
The next line uses the <<import>>
|
||||
command to load the card's ROM with the disk controller
|
||||
firmware. This firmware is known as the ``bootstrap'' or ``P5'' ROM code.
|
||||
It is seen by the Apple ][ at memory addresses $Cs00-$CsFF, where s is the
|
||||
@ -28,7 +31,7 @@ slot number (so in the common case of the card being in slot 6, the ROM is
|
||||
at $C600-$C6FF). The firmware is copyright by Apple, and is available from
|
||||
the http://mosher.mine.nu/apple2/[Apple II Library].
|
||||
|
||||
You can also load a floppy disk image (nibble format) into the drive,either by putting
|
||||
You can also load a floppy disk image (nibble format) into the drive, either by putting
|
||||
the <<load>> command into the +epple2.conf+ file, or by using the command prompt
|
||||
in the emulator (+F5+ key). For example, you could load the DOS 3.3 system master into
|
||||
slot 6, drive 1, with this command
|
||||
|
@ -65,7 +65,8 @@ Apple2::~Apple2()
|
||||
|
||||
void Apple2::tick()
|
||||
{
|
||||
this->cpu.tick();
|
||||
this->slts.tick();
|
||||
this->cpu.tick();
|
||||
this->video.tick();
|
||||
this->paddles.tick();
|
||||
this->speaker.tick();
|
||||
|
@ -34,6 +34,9 @@ void Card::reset()
|
||||
{
|
||||
}
|
||||
|
||||
void Card::tick()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
unsigned char Card::io(const unsigned short /*address*/, const unsigned char data, const bool /*writing*/)
|
||||
|
@ -34,7 +34,8 @@ protected:
|
||||
public:
|
||||
Card();
|
||||
virtual ~Card();
|
||||
virtual void reset();
|
||||
virtual void tick();
|
||||
virtual void reset();
|
||||
virtual unsigned char io(const unsigned short address, const unsigned char data, const bool writing);
|
||||
virtual unsigned char readRom(const unsigned short address, const unsigned char data);
|
||||
virtual bool hasSeventhRom() { return false; }
|
||||
|
@ -392,12 +392,17 @@ void Config::insertCard(const std::string& cardType, int slot, Slots& slts, Scre
|
||||
{
|
||||
card = new FirmwareCard(gui,slot);
|
||||
}
|
||||
else if (cardType == "disk")
|
||||
else if (cardType == "disk") // 16-sector LSS ROM
|
||||
{
|
||||
card = new DiskController(gui,slot);
|
||||
card = new DiskController(gui,slot,false);
|
||||
disk_mask |= (1 << slot);
|
||||
}
|
||||
else if (cardType == "clock")
|
||||
else if (cardType == "disk13") // 13-sector LSS ROM
|
||||
{
|
||||
card = new DiskController(gui,slot,true);
|
||||
disk_mask |= (1 << slot);
|
||||
}
|
||||
else if (cardType == "clock")
|
||||
{
|
||||
card = new ClockCard();
|
||||
}
|
||||
|
@ -23,7 +23,8 @@
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
|
||||
DiskBytes::DiskBytes()
|
||||
DiskBytes::DiskBytes(bool lss13):
|
||||
lss13(lss13)
|
||||
{
|
||||
unload();
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ private:
|
||||
enum { TRACKS_PER_DISK = 0x23 };
|
||||
enum { BYTES_PER_TRACK = 0x1A00 };
|
||||
|
||||
std::vector<unsigned char> bytes[TRACKS_PER_DISK];
|
||||
bool lss13;
|
||||
|
||||
std::vector<unsigned char> bytes[TRACKS_PER_DISK];
|
||||
|
||||
std::string fileName;
|
||||
std::string filePath;
|
||||
@ -36,11 +38,11 @@ private:
|
||||
unsigned int byt; // represents rotational position of disk
|
||||
bool modified;
|
||||
|
||||
void nextByte();
|
||||
void nextByte();
|
||||
void checkForWriteProtection();
|
||||
|
||||
public:
|
||||
DiskBytes();
|
||||
DiskBytes(bool lss13);
|
||||
~DiskBytes();
|
||||
|
||||
bool load(const std::string& filePath);
|
||||
|
@ -17,66 +17,131 @@
|
||||
*/
|
||||
#include "diskcontroller.h"
|
||||
|
||||
DiskController::DiskController(ScreenImage& gui, int slot):
|
||||
gui(gui),
|
||||
slot(slot),
|
||||
drive1(diskBytes1,arm1),
|
||||
drive2(diskBytes2,arm2),
|
||||
currentDrive(&this->drive1)
|
||||
{
|
||||
DiskController::DiskController(ScreenImage& gui, int slot, bool lss13):
|
||||
gui(gui),
|
||||
slot(slot),
|
||||
drive1(diskBytes1,arm1),
|
||||
drive2(diskBytes2,arm2),
|
||||
currentDrive(&this->drive1),
|
||||
load(false),
|
||||
write(false),
|
||||
lssp6rom(lss13),
|
||||
seq(0x20), // gotta start somewhere
|
||||
t(0) {
|
||||
}
|
||||
|
||||
DiskController::~DiskController()
|
||||
{
|
||||
DiskController::~DiskController() {
|
||||
}
|
||||
|
||||
unsigned char DiskController::io(const unsigned short addr, const unsigned char d, const bool writing)
|
||||
{
|
||||
unsigned char data(d);
|
||||
const unsigned char q = (addr & 0x000E) >> 1;
|
||||
const bool on = (addr & 0x0001);
|
||||
unsigned char DiskController::io(const unsigned short addr, const unsigned char d, const bool writing) {
|
||||
this->dataBusReadOnlyCopy = d;
|
||||
|
||||
switch (q)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
this->currentDrive->setMagnet(q,on);
|
||||
this->gui.setTrack(this->slot,getCurrentDriveNumber(),getTrack());
|
||||
break;
|
||||
case 4:
|
||||
this->motorOn = on;
|
||||
this->gui.setIO(this->slot,getCurrentDriveNumber(),on);
|
||||
break;
|
||||
case 5:
|
||||
this->gui.clearCurrentDrive(this->slot,getCurrentDriveNumber());
|
||||
this->currentDrive = (on ? &this->drive2 : &this->drive1);
|
||||
this->gui.setCurrentDrive(this->slot,getCurrentDriveNumber(),getTrack(),this->motorOn);
|
||||
break;
|
||||
case 6:
|
||||
if (on && this->write && writing)
|
||||
{
|
||||
set(data);
|
||||
this->gui.setIO(this->slot,getCurrentDriveNumber(),this->motorOn);
|
||||
this->gui.setDirty(this->slot,getCurrentDriveNumber(),true);
|
||||
}
|
||||
else if (!(on || this->write))
|
||||
{
|
||||
data = get();
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
this->write = on;
|
||||
if (this->currentDrive->isWriteProtected())
|
||||
{
|
||||
data |= 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
data &= 0x7F;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
const unsigned char q = (addr & 0x000E) >> 1;
|
||||
const bool on = (addr & 0x0001);
|
||||
|
||||
switch (q) {
|
||||
case 0:
|
||||
case 1: // TODO if phase-1 is on, it also acts as write-protect (UA2, 9-8)
|
||||
case 2:
|
||||
case 3:
|
||||
this->currentDrive->setMagnet(q,on);
|
||||
this->gui.setTrack(this->slot,getCurrentDriveNumber(),getTrack());
|
||||
break;
|
||||
case 4:
|
||||
this->motorOn = on;
|
||||
this->gui.setIO(this->slot,getCurrentDriveNumber(),on);
|
||||
break;
|
||||
case 5:
|
||||
this->gui.clearCurrentDrive(this->slot,getCurrentDriveNumber());
|
||||
this->currentDrive = (on ? &this->drive2 : &this->drive1);
|
||||
this->gui.setCurrentDrive(this->slot,getCurrentDriveNumber(),getTrack(),this->motorOn);
|
||||
break;
|
||||
case 6:
|
||||
this->load = on;
|
||||
// TODO use LSS
|
||||
// if (on && this->write && writing) {
|
||||
// set(data);
|
||||
// this->gui.setIO(this->slot,getCurrentDriveNumber(),this->motorOn);
|
||||
// this->gui.setDirty(this->slot,getCurrentDriveNumber(),true);
|
||||
// } else if (!(on || this->write)) {
|
||||
// data = get();
|
||||
// }
|
||||
break;
|
||||
case 7:
|
||||
this->write = on;
|
||||
// TODO use LSS
|
||||
// if (this->currentDrive->isWriteProtected()) {
|
||||
// data |= 0x80;
|
||||
// } else {
|
||||
// data &= 0x7F;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
// if (this->motorOn) {
|
||||
// if (this->dataRegister == 0xD5) {
|
||||
// printf("\ndata register --> ");
|
||||
// }
|
||||
// if (this->dataRegister & 0x80) {
|
||||
// printf("%02x", this->dataRegister);
|
||||
// }
|
||||
// }
|
||||
return on ? d : this->dataRegister;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a timing cycle here, based on the MPU clock (1 MHz).
|
||||
* In the real Apple we don't get really get this here. But...
|
||||
* We need a 2MHz clock for the LSS; and
|
||||
* we need to rotate the floppy @ 1 bit per 4 microseconds.
|
||||
* (When the motor is on, that is.)
|
||||
*/
|
||||
void DiskController::tick() {
|
||||
if (!this->motorOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
rotateCurrentDisk();
|
||||
|
||||
// run two LSS cycles = 2MHz
|
||||
stepLss();
|
||||
stepLss();
|
||||
}
|
||||
|
||||
void DiskController::rotateCurrentDisk() {
|
||||
++t;
|
||||
if (t < 0 || 4 <= t) { // 4us interval between bits
|
||||
this->currentDrive->rotateDiskOneBit(); // (will also generate a read-pulse when it reads a 1-bit)
|
||||
t = 0;
|
||||
} else {
|
||||
// clear the read pulse (to make it last only 1us)
|
||||
this->currentDrive->clearPulse();
|
||||
}
|
||||
}
|
||||
|
||||
void DiskController::stepLss() {
|
||||
std::uint8_t adr = this->write<<3 | this->load<<2 | (this->dataRegister>>7)<<1 | this->currentDrive->readPulse();
|
||||
std::uint8_t cmd = this->lssp6rom.read(this->seq|adr);
|
||||
// if (cmd & 3) printf("LSS ROM command byte: 0x%2x\n", cmd);
|
||||
this->seq = cmd & 0xF0u;
|
||||
|
||||
// LSS command functions (UA2, 9-15, Table 9.3)
|
||||
if (cmd & 8u) {
|
||||
switch (cmd & 3u) {
|
||||
case 3:
|
||||
this->dataRegister = this->dataBusReadOnlyCopy;
|
||||
break;
|
||||
case 2:
|
||||
this->dataRegister >>= 1;
|
||||
this->dataRegister |= (isWriteProtected() << 7);
|
||||
// TODO how to handle writing?
|
||||
break;
|
||||
case 1:
|
||||
this->dataRegister <<= 1;
|
||||
this->dataRegister |= ((cmd & 4u) >> 2);
|
||||
// TODO how to handle writing?
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this->dataRegister = 0;
|
||||
}
|
||||
}
|
||||
|
@ -17,48 +17,60 @@
|
||||
*/
|
||||
#include "card.h"
|
||||
#include "drive.h"
|
||||
#include "wozfile.h"
|
||||
#include "lss.h"
|
||||
#include "screenimage.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
class DiskController : public Card
|
||||
{
|
||||
private:
|
||||
ScreenImage& gui;
|
||||
int slot;
|
||||
DiskBytes diskBytes1;
|
||||
WozFile diskBytes1;
|
||||
StepperMotor arm1;
|
||||
Drive drive1;
|
||||
|
||||
DiskBytes diskBytes2;
|
||||
WozFile diskBytes2;
|
||||
StepperMotor arm2;
|
||||
Drive drive2;
|
||||
|
||||
Drive* currentDrive;
|
||||
|
||||
bool write;
|
||||
bool motorOn;
|
||||
bool load; // Q6
|
||||
bool write; // Q7
|
||||
bool motorOn; // TODO WOZ make it delay power-off by about 1 second.
|
||||
|
||||
// Maintain a copy of the last thing on the data bus, so it can
|
||||
// be read by the LSS algorithm when needed.
|
||||
std::uint8_t dataBusReadOnlyCopy;
|
||||
LSS lssp6rom; // the LSS PROM P6 chip (one command per sequence/state combination)
|
||||
std::uint8_t dataRegister; // C3 the controller's LS323 data register
|
||||
std::uint8_t seq; // A3 sequence control LS174 (current sequence number, 0-F)
|
||||
// For ease of use, we store the 4-bit seq number in the _high order_ nibble here.
|
||||
// On the real Apple the read pulse goes thru this LS174 too, but we don't emulate that here.
|
||||
|
||||
// TODO for a rev. 0 motherboard, the disk controller will auto reset the CPU
|
||||
std::uint8_t t; // used to keep track of 4 MPU cycles
|
||||
|
||||
void set(unsigned char data)
|
||||
{
|
||||
if (!this->motorOn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this->currentDrive->set(data);
|
||||
}
|
||||
// TODO for a rev. 0 motherboard, the disk controller will auto reset the CPU (see UA2, 9-13)
|
||||
|
||||
unsigned char get() const
|
||||
{
|
||||
if (!this->motorOn)
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
return this->currentDrive->get();
|
||||
}
|
||||
void writeBit(bool on) {
|
||||
if (!this->motorOn) {
|
||||
return;
|
||||
}
|
||||
this->currentDrive->writeBit(on);
|
||||
}
|
||||
|
||||
// unsigned char get() const
|
||||
// {
|
||||
// if (!this->motorOn)
|
||||
// {
|
||||
// return 0xFF;
|
||||
// }
|
||||
// return this->currentDrive->get();
|
||||
// }
|
||||
|
||||
Drive& getDrive(const unsigned char drive)
|
||||
{
|
||||
@ -70,13 +82,15 @@ private:
|
||||
return (this->currentDrive == &this->drive1) ? this->drive2 : this->drive1;
|
||||
}
|
||||
|
||||
|
||||
void rotateCurrentDisk();
|
||||
void stepLss();
|
||||
|
||||
public:
|
||||
DiskController(ScreenImage& gui, int slot);
|
||||
DiskController(ScreenImage& gui, int slot, bool lss13);
|
||||
~DiskController();
|
||||
|
||||
virtual unsigned char io(const unsigned short address, const unsigned char data, const bool writing);
|
||||
void tick();
|
||||
virtual unsigned char io(const unsigned short address, const unsigned char data, const bool writing);
|
||||
|
||||
void reset()
|
||||
{
|
||||
@ -117,10 +131,10 @@ public:
|
||||
return this->motorOn;
|
||||
}
|
||||
|
||||
const DiskBytes& getDiskBytes(unsigned char disk)
|
||||
{
|
||||
return this->getDrive(disk).getDiskBytes();
|
||||
}
|
||||
// const WozFile& getDiskBytes(unsigned char disk)
|
||||
// {
|
||||
// return this->getDrive(disk).getDiskBytes();
|
||||
// }
|
||||
|
||||
unsigned char getTrack()
|
||||
{
|
||||
|
157
src/drive.h
157
src/drive.h
@ -18,87 +18,122 @@
|
||||
#ifndef DRIVE_H
|
||||
#define DRIVE_H
|
||||
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include "diskbytes.h"
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include "wozfile.h"
|
||||
#include "steppermotor.h"
|
||||
|
||||
class Drive
|
||||
{
|
||||
class Drive {
|
||||
private:
|
||||
enum { TRACKS_PER_DISK = 0x23 };
|
||||
WozFile& disk;
|
||||
StepperMotor& arm;
|
||||
|
||||
DiskBytes& disk;
|
||||
StepperMotor& arm;
|
||||
bool pulse;
|
||||
std::uint8_t cContiguousZeroBits;
|
||||
|
||||
|
||||
|
||||
std::default_random_engine generator;
|
||||
std::uniform_int_distribution<int> distribution;
|
||||
|
||||
bool randomBit() {
|
||||
return !distribution(generator);
|
||||
}
|
||||
|
||||
public:
|
||||
Drive(DiskBytes& disk, StepperMotor& arm):
|
||||
disk(disk),
|
||||
arm(arm)
|
||||
{
|
||||
}
|
||||
Drive(WozFile& disk, StepperMotor& arm):
|
||||
disk(disk),
|
||||
arm(arm),
|
||||
pulse(false),
|
||||
cContiguousZeroBits(0),
|
||||
generator(std::chrono::system_clock::now().time_since_epoch().count()),
|
||||
distribution(0,1) {
|
||||
}
|
||||
|
||||
~Drive() {}
|
||||
~Drive() {
|
||||
}
|
||||
|
||||
bool loadDisk(const std::string& fnib)
|
||||
{
|
||||
return this->disk.load(fnib);
|
||||
}
|
||||
bool loadDisk(const std::string& fnib) {
|
||||
return this->disk.load(fnib);
|
||||
}
|
||||
|
||||
void unloadDisk()
|
||||
{
|
||||
this->disk.unload();
|
||||
}
|
||||
bool isLoaded()
|
||||
{
|
||||
return this->disk.isLoaded();
|
||||
}
|
||||
void unloadDisk() {
|
||||
this->disk.unload();
|
||||
}
|
||||
bool isLoaded() {
|
||||
return this->disk.isLoaded();
|
||||
}
|
||||
|
||||
void saveDisk()
|
||||
{
|
||||
this->disk.save();
|
||||
}
|
||||
void saveDisk() {
|
||||
this->disk.save();
|
||||
}
|
||||
|
||||
bool isWriteProtected() const
|
||||
{
|
||||
return this->disk.isWriteProtected();
|
||||
}
|
||||
bool isWriteProtected() const {
|
||||
return this->disk.isWriteProtected();
|
||||
}
|
||||
|
||||
bool isModified() const
|
||||
{
|
||||
return this->disk.isModified();
|
||||
}
|
||||
bool isModified() const {
|
||||
return this->disk.isModified();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setMagnet(unsigned char q, bool on)
|
||||
{
|
||||
this->arm.setMagnet(q,on);
|
||||
}
|
||||
void setMagnet(unsigned char q, bool on) {
|
||||
this->arm.setMagnet(q,on);
|
||||
}
|
||||
|
||||
int getTrack() const
|
||||
{
|
||||
return this->arm.getTrack();
|
||||
}
|
||||
int getTrack() const {
|
||||
return this->arm.getTrack();
|
||||
}
|
||||
|
||||
|
||||
void rotateDiskOneBit() {
|
||||
this->disk.rotateOneBit(this->arm.getQuarterTrack());
|
||||
|
||||
if (this->disk.getBit(this->arm.getQuarterTrack())) {
|
||||
this->pulse = true;
|
||||
cContiguousZeroBits = 0;
|
||||
} else {
|
||||
// keep a count of contiguous zero-bits and generate random bits when
|
||||
// we see more than three (emulating the MC3470, see UA2, 9-11)
|
||||
++cContiguousZeroBits;
|
||||
if (3 < cContiguousZeroBits) {
|
||||
// if (cContiguousZeroBits == 4) printf("\n<GENERATING RANDOM BIT(S).....>\n");
|
||||
if (randomBit()) {
|
||||
this->pulse = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool readPulse() {
|
||||
return this->pulse;
|
||||
}
|
||||
void clearPulse() {
|
||||
this->pulse = false;
|
||||
}
|
||||
|
||||
void writeBit(bool on) {
|
||||
this->disk.setBit(this->arm.getQuarterTrack());
|
||||
}
|
||||
// unsigned char get() const
|
||||
// {
|
||||
// return this->disk.get(this->arm.getTrack());
|
||||
// }
|
||||
|
||||
// void set(unsigned char value)
|
||||
// {
|
||||
// this->disk.put(this->arm.getTrack(),value);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
unsigned char get() const
|
||||
{
|
||||
return this->disk.get(this->arm.getTrack());
|
||||
}
|
||||
|
||||
void set(unsigned char value)
|
||||
{
|
||||
this->disk.put(this->arm.getTrack(),value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const DiskBytes& getDiskBytes()
|
||||
{
|
||||
return this->disk;
|
||||
}
|
||||
// const WozFile& getDiskBytes() {
|
||||
// return this->disk;
|
||||
// }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
13
src/lss.cpp
13
src/lss.cpp
@ -175,11 +175,14 @@ LSS::LSS(bool use13SectorDos32LSS):
|
||||
setseq(lss13rom,0x23u,0x30u);
|
||||
setseq(lss13rom,0x33u,0xD0u);
|
||||
|
||||
for (unsigned int seq = 0; seq < 0x100u; seq += 0x10u) {
|
||||
showua2seq(lssrom,seq);
|
||||
}
|
||||
for (unsigned int seq = 0; seq < 0x100u; seq += 0x10u) {
|
||||
showua2seq(lss13rom,seq);
|
||||
if (use13SectorDos32LSS) {
|
||||
for (unsigned int seq = 0; seq < 0x100u; seq += 0x10u) {
|
||||
showua2seq(lss13rom,seq);
|
||||
}
|
||||
} else {
|
||||
for (unsigned int seq = 0; seq < 0x100u; seq += 0x10u) {
|
||||
showua2seq(lssrom,seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
75
src/main.cpp
75
src/main.cpp
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
@ -30,12 +30,12 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
static int run(const std::string& config_file)
|
||||
{
|
||||
static int run(const std::string& config_file) {
|
||||
GUI gui;
|
||||
|
||||
std::auto_ptr<Emulator> emu(new Emulator());
|
||||
std::unique_ptr<Emulator> emu(new Emulator());
|
||||
|
||||
Config cfg(config_file);
|
||||
emu->config(cfg);
|
||||
@ -45,26 +45,77 @@ static int run(const std::string& config_file)
|
||||
return emu->run();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//std::uint8_t dataBus;
|
||||
//std::uint8_t lssrom[0x100];
|
||||
//std::uint8_t lss13rom[0x100];
|
||||
//std::uint8_t wp(0);
|
||||
//std::uint8_t q7(0);
|
||||
//std::uint8_t q6(0);
|
||||
//std::uint8_t dreg(0);
|
||||
//std::uint8_t seq(0x20);
|
||||
//std::uint8_t cmd;
|
||||
//std::uint8_t adr;
|
||||
//std::uint8_t cprint(0);
|
||||
|
||||
//void debugLog(const std::uint8_t x) {
|
||||
// if (++cprint == 128 || x==0xD5u) {
|
||||
// cprint = 0;
|
||||
// printf("\n");
|
||||
// }
|
||||
// printf("%02x", x);
|
||||
//}
|
||||
|
||||
//void lss(const std::uint8_t cmd) {
|
||||
// if (cmd & 8) {
|
||||
// switch (cmd & 3) {
|
||||
// case 3: dreg = dataBus; break;
|
||||
// case 2: dreg >>= 1; dreg |= (wp << 7); break;
|
||||
// case 1: dreg <<= 1; dreg |= ((cmd & 4) >> 2); break;
|
||||
// }
|
||||
// } else {
|
||||
// debugLog(dreg);
|
||||
// dreg = 0;
|
||||
// }
|
||||
//}
|
||||
|
||||
//void doTrack(std::uint8_t *ptrk, const std::uint16_t ctrk) {
|
||||
// std::uint8_t *pend(ptrk+ctrk);
|
||||
// while (ptrk < pend) {
|
||||
// for (std::uint8_t m(0x80); m; m >>= 1) {
|
||||
// std::uint8_t pls = (*ptrk & m) ? 1 : 0;
|
||||
// for (int i(0); i < 8; ++i) {
|
||||
// adr = q7<<3 | q6<<2 | (dreg>>7)<<1 | pls;
|
||||
// pls = 0;
|
||||
// cmd = lssrom[seq|adr];
|
||||
// seq = cmd & 0xF0;
|
||||
// lss(cmd);
|
||||
// }
|
||||
// }
|
||||
// ++ptrk;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc > 2)
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc > 2) {
|
||||
throw std::runtime_error("usage: epple2 [config-file]" );
|
||||
}
|
||||
|
||||
int x = E2Const::test();
|
||||
if (x != -1)
|
||||
{
|
||||
if (x != -1) {
|
||||
std::cerr << x << std::endl;
|
||||
throw std::runtime_error("bad constant in e2const.h" );
|
||||
}
|
||||
|
||||
std::string config_file;
|
||||
if (argc > 1)
|
||||
{
|
||||
if (argc > 1) {
|
||||
config_file = argv[1];
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,16 @@ void Slots::reset()
|
||||
std::for_each(this->cards.begin(),this->cards.end(),Slots_Card_reset());
|
||||
}
|
||||
|
||||
struct Slots_Card_tick
|
||||
{
|
||||
void operator() (Card* p) { p->tick(); }
|
||||
};
|
||||
|
||||
void Slots::tick()
|
||||
{
|
||||
std::for_each(this->cards.begin(),this->cards.end(),Slots_Card_tick());
|
||||
}
|
||||
|
||||
unsigned char Slots::readRom(const int islot, const unsigned short addr, const unsigned char data)
|
||||
{
|
||||
return this->cards[islot]->readRom(addr,data);
|
||||
|
@ -35,6 +35,7 @@ public:
|
||||
Slots(ScreenImage& gui);
|
||||
~Slots();
|
||||
|
||||
void tick();
|
||||
unsigned char io(const int islot, const int iswch, const unsigned char b, const bool writing);
|
||||
void reset();
|
||||
unsigned char readRom(const int islot, const unsigned short addr, const unsigned char data);
|
||||
|
@ -17,9 +17,7 @@
|
||||
*/
|
||||
/**
|
||||
* Emulates the arm stepper motor in the Disk ][.
|
||||
* This implementation differs from the actual Disk ][ in
|
||||
* that it rounds half- and quarter-tracks down to the
|
||||
* next whole track. Also, this emulator moves the arm
|
||||
* This emulator moves the arm
|
||||
* instantaneously, whereas the Disk ][ arm would actually
|
||||
* take some time to reach its new position (this would
|
||||
* cause a difference if the state of the magnets changed
|
||||
@ -48,70 +46,60 @@ mags ps magval
|
||||
|
||||
(undefined, i.e., no movement)
|
||||
0000 ? 0
|
||||
0101 ? 5
|
||||
1010 ? A
|
||||
0101 ? 5 // <-- TODO pick one at random?
|
||||
1010 ? A // <-- TODO pick one at random?
|
||||
1111 ? F
|
||||
*/
|
||||
#include "steppermotor.h"
|
||||
#include "util.h"
|
||||
#include <iostream>
|
||||
|
||||
StepperMotor::StepperMotor():
|
||||
quarterTrack(QTRACKS >> 1), // start in the middle of the disk... just for fun
|
||||
quarterTrack(QTRACK_MAX >> 1), // start in the middle of the disk... just for fun
|
||||
// TODO if we want to be extremely accurate, we should save each arm's position on shutdown and restore on startup
|
||||
// (because in the real-life Apple ][, the arm stays in the same position when powered off).
|
||||
pos(0),
|
||||
mags(0)
|
||||
{
|
||||
mags(0) {
|
||||
}
|
||||
|
||||
StepperMotor::~StepperMotor()
|
||||
{
|
||||
StepperMotor::~StepperMotor() {
|
||||
}
|
||||
|
||||
signed char StepperMotor::mapMagPos[] = {-1,0,2,1,4,-1,3,2,6,7,-1,0,5,6,4,-1};
|
||||
|
||||
void StepperMotor::setMagnet(const unsigned char magnet, const bool on)
|
||||
{
|
||||
const unsigned char mask = 1 << magnet;
|
||||
if (on)
|
||||
{
|
||||
this->mags |= mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->mags &= ~mask;
|
||||
}
|
||||
void StepperMotor::setMagnet(const unsigned char magnet, const bool on) {
|
||||
const unsigned char mask = 1 << magnet;
|
||||
if (on) {
|
||||
this->mags |= mask;
|
||||
} else {
|
||||
this->mags &= ~mask;
|
||||
}
|
||||
|
||||
const char newPos = mapMagPos[this->mags];
|
||||
char d;
|
||||
if (newPos >= 0)
|
||||
{
|
||||
d = calcDeltaPos(this->pos,newPos);
|
||||
this->pos = newPos;
|
||||
const char newPos = mapMagPos[this->mags];
|
||||
char d;
|
||||
if (newPos >= 0) {
|
||||
d = calcDeltaPos(this->pos,newPos);
|
||||
this->pos = newPos;
|
||||
|
||||
this->quarterTrack += d;
|
||||
if (this->quarterTrack < 0)
|
||||
this->quarterTrack = 0;
|
||||
else if (this->quarterTrack > QTRACKS)
|
||||
this->quarterTrack = QTRACKS;
|
||||
}
|
||||
/*
|
||||
std::cout << " ARM: magnet " << (unsigned int)magnet << " " << (on ? "on " : "off" );
|
||||
std::cout << " [" <<
|
||||
((mags&1)?"*":".") <<
|
||||
((mags&2)?"*":".") <<
|
||||
((mags&4)?"*":".") <<
|
||||
((mags&8)?"*":".") <<
|
||||
"]";
|
||||
if (d != 0)
|
||||
{
|
||||
std::cout << " track " << std::hex << (unsigned int)(this->quarterTrack >> 2);
|
||||
int fract = this->quarterTrack & 3;
|
||||
if (fract != 0)
|
||||
{
|
||||
std::cout << (fract == 1 ? " +.25" : fract == 2 ? " +.5" : " +.75");
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
*/
|
||||
this->quarterTrack += d;
|
||||
if (this->quarterTrack < 0)
|
||||
this->quarterTrack = 0;
|
||||
else if (this->quarterTrack > QTRACK_MAX)
|
||||
this->quarterTrack = QTRACK_MAX;
|
||||
}
|
||||
std::cout << " ARM: magnet " << (unsigned int)magnet << " " << (on ? "on " : "off" );
|
||||
std::cout << " [" <<
|
||||
((mags&1)?"*":".") <<
|
||||
((mags&2)?"*":".") <<
|
||||
((mags&4)?"*":".") <<
|
||||
((mags&8)?"*":".") <<
|
||||
"]";
|
||||
if (d != 0) {
|
||||
std::cout << " track " << std::hex << (unsigned int)(this->quarterTrack >> 2);
|
||||
int fract = this->quarterTrack & 3;
|
||||
if (fract != 0) {
|
||||
std::cout << (fract == 1 ? " +.25" : fract == 2 ? " +.5" : " +.75");
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
@ -18,47 +18,46 @@
|
||||
#ifndef STEPPERMOTOR_H
|
||||
#define STEPPERMOTOR_H
|
||||
|
||||
class StepperMotor
|
||||
{
|
||||
#include <cstdint>
|
||||
|
||||
class StepperMotor {
|
||||
private:
|
||||
enum { TRACKS_PER_DISK = 0x23 };
|
||||
enum { QTRACKS = TRACKS_PER_DISK << 2 };
|
||||
enum { TRACKS_PER_DISK = 0x23 };
|
||||
enum { QTRACK_MAX = TRACKS_PER_DISK << 2 };
|
||||
|
||||
signed short quarterTrack;
|
||||
signed char pos; // 0 - 7
|
||||
unsigned char mags;
|
||||
// quarter track: 0=t0, 1=t0.25, 2=t0.5, 3=t0.75, 4=t1, ... 140=t35.00
|
||||
// (see TMAP in WOZ file format)
|
||||
signed short quarterTrack;
|
||||
signed char pos; // 0 - 7
|
||||
unsigned char mags;
|
||||
|
||||
static signed char mapMagPos[];
|
||||
static signed char mapMagPos[];
|
||||
|
||||
static signed char calcDeltaPos(const unsigned char cur, const signed char next)
|
||||
{
|
||||
signed char d = next-cur; // -7 to +7
|
||||
static signed char calcDeltaPos(const unsigned char cur, const signed char next) {
|
||||
signed char d = next-cur; // -7 to +7
|
||||
|
||||
if (d==4 || d==-4)
|
||||
{
|
||||
d = 0;
|
||||
}
|
||||
else if (d>4)
|
||||
{
|
||||
d -= 8;
|
||||
}
|
||||
else if (d<-4)
|
||||
{
|
||||
d += 8;
|
||||
}
|
||||
if (d == 4 || d == -4) {
|
||||
d = 0; // <--- TODO pick random direction?
|
||||
} else if (4 < d) {
|
||||
d -= 8;
|
||||
} else if (d < -4) {
|
||||
d += 8;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public:
|
||||
StepperMotor();
|
||||
~StepperMotor();
|
||||
StepperMotor();
|
||||
~StepperMotor();
|
||||
|
||||
void setMagnet(const unsigned char magnet, const bool on);
|
||||
unsigned char getTrack()
|
||||
{
|
||||
return ((unsigned short)(this->quarterTrack)) >> 2;
|
||||
}
|
||||
void setMagnet(const unsigned char magnet, const bool on);
|
||||
unsigned char getTrack() {
|
||||
return ((unsigned short)(this->quarterTrack)) >> 2;
|
||||
}
|
||||
std::uint8_t getQuarterTrack() {
|
||||
return this->quarterTrack;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
158
src/wozfile.cpp
158
src/wozfile.cpp
@ -23,7 +23,7 @@
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
|
||||
WozFile::WozFile() {
|
||||
WozFile::WozFile() : lastQuarterTrack(0) {
|
||||
unload();
|
||||
}
|
||||
|
||||
@ -201,10 +201,8 @@ void WozFile::checkForWriteProtection() {
|
||||
outf.close();
|
||||
}
|
||||
|
||||
void WozFile::save()
|
||||
{
|
||||
if (isWriteProtected() || !isLoaded())
|
||||
{
|
||||
void WozFile::save() {
|
||||
if (isWriteProtected() || !isLoaded()) {
|
||||
return;
|
||||
}
|
||||
// std::ofstream out(filePath.c_str(),std::ios::binary);
|
||||
@ -215,11 +213,155 @@ void WozFile::save()
|
||||
// this->modified = false;
|
||||
}
|
||||
|
||||
void WozFile::unload()
|
||||
{
|
||||
this->byt = 0;
|
||||
void WozFile::unload() {
|
||||
this->bit = 0x80u;
|
||||
this->byt = 0x00u;
|
||||
this->writable = true;
|
||||
this->loaded = false;
|
||||
this->filePath = "";
|
||||
this->modified = false;
|
||||
}
|
||||
|
||||
|
||||
static std::uint8_t bc(std::uint8_t bit) {
|
||||
switch (bit) {
|
||||
case 0x80u: return 0u;
|
||||
case 0x40u: return 1u;
|
||||
case 0x20u: return 2u;
|
||||
case 0x10u: return 3u;
|
||||
case 0x08u: return 4u;
|
||||
case 0x04u: return 5u;
|
||||
case 0x02u: return 6u;
|
||||
case 0x01u: return 7u;
|
||||
}
|
||||
return 255u; // should never happen
|
||||
}
|
||||
|
||||
static std::uint8_t cb(std::uint8_t bit) {
|
||||
switch (bit) {
|
||||
case 0u: return 0x80u;
|
||||
case 1u: return 0x40u;
|
||||
case 2u: return 0x20u;
|
||||
case 3u: return 0x10u;
|
||||
case 4u: return 0x08u;
|
||||
case 5u: return 0x04u;
|
||||
case 6u: return 0x02u;
|
||||
case 7u: return 0x01u;
|
||||
}
|
||||
return 255u; // should never happen
|
||||
}
|
||||
|
||||
static void dumpQTrack(std::uint8_t currentQuarterTrack) {
|
||||
if (currentQuarterTrack % 4) {
|
||||
const std::uint8_t hundredths((currentQuarterTrack%4) * 25);
|
||||
printf(" Reading from <---------- track 0x%02X +.%02d\n", currentQuarterTrack/4, hundredths);
|
||||
} else {
|
||||
printf(" Reading from <---------- track 0x%02X\n", currentQuarterTrack/4);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Rotate the floppy disk by one bit.
|
||||
* In real life we don't care what track we're one, but for the
|
||||
* emulator we need to know. This is because the tracks within the
|
||||
* WOZ file could be different lengths. So in order to know when
|
||||
* we need to loop back to the beginning of the track (circular
|
||||
* track on the floppy), we need to know the actual bit length
|
||||
* of that track in our WOZ file.
|
||||
*/
|
||||
void WozFile::rotateOneBit(std::uint8_t currentQuarterTrack) {
|
||||
if (!isLoaded()) {
|
||||
return; // there's no disk to rotate
|
||||
}
|
||||
|
||||
// In WOZ track image, bits (i.e., magnetic field reversal on disk,
|
||||
// or lack thereof) are packed into bytes, high bit to low bit.
|
||||
// Really, it's the stream of bits returned by the MC3470 (but also
|
||||
// possibly with random bits zeroed out).
|
||||
// std::uint16_t before = (this->byt*8+bc(this->bit));
|
||||
// printf("disk at bit: %d\n", this->byt*8+bc(this->bit));
|
||||
|
||||
// Move to next bit:
|
||||
this->bit >>= 1;
|
||||
|
||||
// If we hit end of this byte, move on to beginning of next byte
|
||||
if (this->bit == 0) {
|
||||
++this->byt;
|
||||
this->bit = 0x80u;
|
||||
}
|
||||
|
||||
if (this->tmap[currentQuarterTrack] == 0xFFu) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentQuarterTrack != this->lastQuarterTrack) {
|
||||
double oldLen = this->trk_bits[this->tmap[this->lastQuarterTrack]];
|
||||
double newLen = this->trk_bits[this->tmap[currentQuarterTrack]];
|
||||
double dif = newLen/oldLen;
|
||||
if (dif < -0.000001 || 0.000001 < dif) {
|
||||
// dumpQTrack(currentQuarterTrack);
|
||||
// printf(" new track: bit pos: %d ", this->byt*8+bc(this->bit));
|
||||
std::uint16_t newBit = (this->byt*8+bc(this->bit)) * dif;
|
||||
this->byt = newBit / 8;
|
||||
this->bit = cb(newBit % 8);
|
||||
// printf("--> %d\n\n", this->byt*8+bc(this->bit));
|
||||
}
|
||||
this->lastQuarterTrack = currentQuarterTrack;
|
||||
}
|
||||
|
||||
// Check for hitting the end of our track image,
|
||||
// and if so, move back to the beginning.
|
||||
// This is how we emulate a circular track on the floppy.
|
||||
if (this->trk_bits[this->tmap[currentQuarterTrack]] <= this->byt*8+bc(this->bit)) {
|
||||
// printf("\n<rewinding track here>\n");
|
||||
this->byt = 0;
|
||||
this->bit = 0x80u;
|
||||
}
|
||||
|
||||
// std::uint16_t after = (this->byt*8+bc(this->bit));
|
||||
// if (!(after % 0x100u)) {
|
||||
// printf("\nnow at bit %04x\n", after);
|
||||
// }
|
||||
// if (after != before+1) {
|
||||
// printf("\nbit changing from %04x to %04x\n", before, after);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
bool WozFile::getBit(std::uint8_t currentQuarterTrack) {
|
||||
if (!isLoaded()) {
|
||||
printf("\nNO DISK TO READ FROM (will generate random data)\n");
|
||||
return false; // there's no disk, so no pulse
|
||||
}
|
||||
if (this->tmap[currentQuarterTrack] == 0xFFu) {
|
||||
// printf("\nreading (random) from empty q-track: %d\n", currentQuarterTrack);
|
||||
return false; // track doesn't exist
|
||||
}
|
||||
if (this->c_trks <= this->tmap[currentQuarterTrack]) { // shouldn't happen
|
||||
printf("\nBAD TRACK quarterTrack %d mapped to TRKS index %d (count of tracks: %d)\n", currentQuarterTrack, this->tmap[currentQuarterTrack], this->c_trks);
|
||||
return false; // track doesn't exist
|
||||
}
|
||||
|
||||
// if (!(this->byt % 128) && this->bit == 0x01) {
|
||||
// printf("\ngetBit--> ");
|
||||
// }
|
||||
// printf("%02x", this->byt*8+bc(this->bit));
|
||||
return this->trks[this->tmap[currentQuarterTrack]][this->byt] & this->bit;
|
||||
}
|
||||
|
||||
void WozFile::setBit(std::uint8_t currentQuarterTrack) {
|
||||
if (!isLoaded()) {
|
||||
return; // there's no disk to write data to
|
||||
}
|
||||
if (!this->writable) {
|
||||
return; // write-protected
|
||||
}
|
||||
if (this->c_trks <= this->tmap[currentQuarterTrack]) { // shouldn't happen
|
||||
return; // TODO track doesn't exist: create a new one
|
||||
}
|
||||
if (this->tmap[currentQuarterTrack] == 0xFFu) {
|
||||
// track does not exist, create new one
|
||||
}
|
||||
// TODO extend track length if needed
|
||||
this->trks[this->tmap[currentQuarterTrack]][this->byt] |= this->bit;
|
||||
}
|
||||
|
@ -21,22 +21,46 @@
|
||||
#define WOZFILE_H
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief WOZ file (floppy disk image)
|
||||
* Represents a floppy disk. We only handle 5.25" disks.
|
||||
* The disk has 141 quarter-track possible positions, but
|
||||
* typically will have 35 tracks.
|
||||
* The floppy disk "knows" it's rotational position.
|
||||
*
|
||||
* Note, the floppy has no notion of the current track;
|
||||
* rather, that information is known by the stepper motor and arm.
|
||||
*/
|
||||
class WozFile {
|
||||
std::string fileName;
|
||||
std::string filePath;
|
||||
bool writable;
|
||||
bool loaded;
|
||||
// TODO add bit position:
|
||||
unsigned int byt; // represents rotational position of disk
|
||||
bool modified;
|
||||
|
||||
std::uint8_t tmap[141]; // quarter-tracks from 0 through 35, values are indexes into trks
|
||||
// represents (negation of) write-protect tab
|
||||
// We consider the disk to be write-protected if the user does
|
||||
// not have write access to the file OR the WOZ file INFO chuck
|
||||
// indicates that the floppy was write-protected.
|
||||
bool writable;
|
||||
|
||||
std::uint8_t tmap[141]; // quarter-tracks from T0 through T35, values are indexes into trks
|
||||
std::uint8_t c_trks; // count of actual tracks:
|
||||
std::uint8_t trks[141][6646]; // 141 is theoretical max; will always be less
|
||||
std::uint16_t trk_bits[141]; // count of bits in each track
|
||||
|
||||
void nextByte();
|
||||
// bit and byt together represent the rotational position
|
||||
// of the floppy disk.
|
||||
// bit is a mask indicating current bit within current byte (byt).
|
||||
// WOZ file bits are packed into bytes starting at bit 7
|
||||
// through bit 0 of the first byte, then bits 7-0 of the
|
||||
// next byte, etc., from bits 0 through trk_bits-1
|
||||
std::uint8_t bit;
|
||||
std::uint16_t byt;
|
||||
|
||||
std::uint8_t lastQuarterTrack;
|
||||
|
||||
void checkForWriteProtection();
|
||||
|
||||
public:
|
||||
@ -62,6 +86,10 @@ public:
|
||||
bool isModified() {
|
||||
return this->modified;
|
||||
}
|
||||
|
||||
void rotateOneBit(std::uint8_t currentQuarterTrack);
|
||||
bool getBit(std::uint8_t currentQuarterTrack);
|
||||
void setBit(std::uint8_t currentQuarterTrack);
|
||||
};
|
||||
|
||||
#endif // WOZFILE_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user