RAM config; remove obsolete nib paths from config files

This commit is contained in:
Christopher Mosher 2019-02-02 16:08:32 -05:00
parent 0183c5304f
commit e8cdb8975e
28 changed files with 848 additions and 81 deletions

View File

@ -9,3 +9,9 @@
import motherboard rom 1000 $(PREFIX)lib/apple2/system/a2/intbasic.a65
import motherboard rom 2425 $(PREFIX)lib/apple2/system/a2/other.a65
import motherboard rom 2800 $(PREFIX)lib/apple2/system/a2/monitor.a65
# 4K RAM
motherboard ram C 4K 4K 4K 4K 4K 4K 4K 4K
motherboard strap C 4K 0000

View File

@ -9,6 +9,16 @@
# 48K RAM
motherboard ram E 16K
motherboard ram D 16K
motherboard ram C 16K
motherboard strap E 16K 8000
motherboard strap D 16K 4000
motherboard strap C 16K 0000
# Integer BASIC and old Monitor ROMs
import motherboard rom 1000 $(PREFIX)lib/apple2/system/a2/intbasic.a65
import motherboard rom 2425 $(PREFIX)lib/apple2/system/a2/other.a65
@ -21,4 +31,4 @@ 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
load slot 6 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_rawdos.nib
#load slot 6 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_rawdos.dsk.woz

View File

@ -9,6 +9,17 @@
# indicator on the language card line turning on or off.
# 48K RAM
motherboard ram E 16K
motherboard ram D 16K
motherboard ram C 16K
motherboard strap E 16K 8000
motherboard strap D 16K 4000
motherboard strap C 16K 0000
# Integer BASIC and old Monitor ROMs
import motherboard rom 1000 $(PREFIX)lib/apple2/system/a2/intbasic.a65
import motherboard rom 2425 $(PREFIX)lib/apple2/system/a2/other.a65
@ -26,4 +37,4 @@ slot 6 disk
import slot 6 rom 0 $(PREFIX)lib/apple2/dos/16sector/disk2.a65
# Insert DOS 3.3 System Master disk (original version) in drive 1
load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/dos330/clean330sysmas.nib
#load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/dos330/clean330sysmas.dsk.woz

View File

@ -9,6 +9,16 @@
# 48K RAM
motherboard ram E 16K
motherboard ram D 16K
motherboard ram C 16K
motherboard strap E 16K 8000
motherboard strap D 16K 4000
motherboard strap C 16K 0000
# Integer BASIC and old Monitor ROMs
import motherboard rom 1000 $(PREFIX)lib/apple2/system/a2/intbasic.a65
import motherboard rom 2425 $(PREFIX)lib/apple2/system/a2/other.a65
@ -48,7 +58,7 @@ import slot 4 rom 0 $(PREFIX)lib/epple2/cards/clock.a65
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
#load slot 5 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_rawdos.dsk.woz
@ -57,7 +67,7 @@ load slot 5 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_
slot 6 disk
import slot 6 rom 0 $(PREFIX)lib/apple2/dos/16sector/disk2.a65
# Insert the DOS 3.3 System Master disk (original version) into slot 6
load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/dos330/clean330sysmas.nib
#load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/dos330/clean330sysmas.dsk.woz

View File

@ -8,3 +8,9 @@
import motherboard rom 0000 $(PREFIX)lib/apple2/system/a2p/applesoft.a65
import motherboard rom 2800 $(PREFIX)lib/apple2/system/a2p/monitor.a65
# 4K RAM
motherboard ram C 4K 4K 4K 4K 4K 4K 4K 4K
motherboard strap C 4K 0000

View File

@ -9,6 +9,16 @@
# indicator on the language card line turning on or off.
# 48K RAM
motherboard ram E 16K
motherboard ram D 16K
motherboard ram C 16K
motherboard strap E 16K 8000
motherboard strap D 16K 4000
motherboard strap C 16K 0000
# Applesoft BASIC and Autostart Monitor ROMs
import motherboard rom 0000 $(PREFIX)lib/apple2/system/a2p/applesoft.a65
import motherboard rom 2800 $(PREFIX)lib/apple2/system/a2p/monitor.a65
@ -25,4 +35,4 @@ slot 6 disk
import slot 6 rom 0 $(PREFIX)lib/apple2/dos/16sector/disk2.a65
# Insert DOS 3.3 System Master disk (original version) in drive 1
load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/disks/dos330/clean330sysmas.nib
#load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/disks/dos330/clean330sysmas.dsk.woz

View File

@ -9,6 +9,16 @@
# 48K RAM
motherboard ram E 16K
motherboard ram D 16K
motherboard ram C 16K
motherboard strap E 16K 8000
motherboard strap D 16K 4000
motherboard strap C 16K 0000
# Applesoft BASIC and Autostart Monitor ROMs
import motherboard rom 0000 $(PREFIX)lib/apple2/system/a2p/applesoft.a65
import motherboard rom 2800 $(PREFIX)lib/apple2/system/a2p/monitor.a65
@ -47,7 +57,7 @@ import slot 4 rom 0 $(PREFIX)lib/epple2/cards/clock.a65
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
#load slot 5 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_rawdos.dsk.woz
@ -56,7 +66,7 @@ load slot 5 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_
slot 6 disk
import slot 6 rom 0 $(PREFIX)lib/apple2/dos/16sector/disk2.a65
# Insert the DOS 3.3 System Master disk (original version) into slot 6
load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/dos330/clean330sysmas.nib
#load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/dos330/clean330sysmas.dsk.woz

View File

@ -11,6 +11,14 @@
# standard 48K RAM
motherboard ram E 16K
motherboard ram D 16K
motherboard ram C 16K
motherboard strap E 16K 8000
motherboard strap D 16K 4000
motherboard strap C 16K 0000
# 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.
@ -40,11 +48,11 @@ import slot 4 rom 0 $(PREFIX)lib/epple2/cards/clock.a65
#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
#load slot 5 drive 1 $(PREFIX)lib/apple2/dos/13sector/dos310/clean31sysmas_stock_rawdos.dsk.woz
#slot 6 disk
#import slot 6 rom 0 $(PREFIX)lib/apple2/dos/16sector/disk2.a65
#load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/dos330/clean330sysmas.nib
#load slot 6 drive 1 $(PREFIX)lib/apple2/dos/16sector/dos330/clean330sysmas.dsk.woz
#slot 7 firmware
#import slot 7 rombank 1000 $(PREFIX)lib/apple2/system/a2/intbasic.a65

View File

@ -10,6 +10,11 @@
# Use an original, revision zero, motherboard
revision 0
# Load Integer BASIC and old Monitor ROMs
import motherboard rom 1000 $(PREFIX)lib/apple2/system/a2/intbasic.a65
import motherboard rom 2425 $(PREFIX)lib/apple2/system/a2/other.a65
@ -17,5 +22,6 @@ import motherboard rom 2800 $(PREFIX)lib/apple2/system/a2/monitor.a65
# Use an original, revision zero, motherboard
revision 0
# 4K RAM
motherboard ram C 4K 4K 4K 4K 4K 4K 4K 4K
motherboard strap C 4K 0000

View File

@ -2,7 +2,7 @@ anchor:commands[]
=== Commands
+<<slot>> <<import>> <<load>> <<unload>> <<save>> <<cassette>> <<revision>>+
+<<slot>> <<motherboard>> <<import>> <<load>> <<unload>> <<save>> <<cassette>> <<revision>>+
@ -45,6 +45,58 @@ The emulated Apple should be _powered off_ before inserting or removing cards.
anchor:motherboard[]
==== motherboard
The +motherboard+ command configures the emulated Apple's motherboard RAM chips and strapping block.
--------
motherboard ram {C|D|E} { 4K | 4096 | 16K | 4116 | - } [...up to 8]
motherboard strap {C|D|E} { 4K | 16K } <base>
--------
The RAM configuration lines represent the rows of chips on the motherboard. The motherboard labels the
rows as C, D, and E. Each row has 8 chips, one per bit in a byte. The Apple ][ accepts 4K or 16K chips.
You use the +ram+ command to insert (or remove) chips from the sockets.
You configure each row's address range by using a "strapping block" on the original Apple. In the
emulator, use the +strap+ command to perform this function. You should strap 4K rows to a 4K range
of RAM. You should always assign some RAM to the zero address.
For more information about RAM configuration, see
https://archive.org/details/Apple_II_Reference_Manual_1979_Apple/page/n79[Christopher Espinosa, Apple II Reference Manual
(Cupertino, Calif.: Apple Computer, 1978), pp. 70-72].
Example of normal 48K RAM configuration:
--------
ram e 16K
strap e 16K 8000
ram d 16K
strap d 16K 4000
ram c 16K
strap c 16K 0000
--------
Example of 4K, showing how you could specify each chip:
--------
ram e - - - - - - - -
ram d - - - - - - - -
ram c 4K 4K 4K 4K 4K 4K 4K 4K
strap c 4K 0000
--------
Example of 4K at zero address, and 8K at HI-RES page one:
--------
ram e 4K
strap e 4K 3000
ram d 4K
strap d 4K 2000
ram c 4K
strap c 4K 0000
--------
anchor:import[]
==== import
@ -53,7 +105,7 @@ The +import+ command imports a binary image file into the emulated Apple's memor
--------
import slot <slot> { rom | rom7 | rombank } <base> <file-path>
import motherboard { rom | ram } <base> <file-path>
import motherboard rom <base> <file-path>
--------
+<slot>+ Slot number, 0 through 7, of peripheral card to import the binary image into.
@ -63,17 +115,16 @@ import motherboard { rom | ram } <base> <file-path>
+<file-path>+ Path of the binary image to import.
The +import+ command reads the binary image byte-for-byte from the given file-path
into an area of memory in the emulated Apple. You can load into either the motherboard or
a card in one of the slots. For the motherboard, you choose the RAM or ROM area. For a card
in a slot, you can choose either the normal ROM, the bank-switched ROM, or the so-called
``seventh ROM'' area.
into an area of ROM in the emulated Apple. You can load into either the motherboard or
a card in one of the slots. For a card in a slot, you can choose either the normal ROM,
the bank-switched ROM, or the so-called ``seventh ROM'' area.
You also have to specify the base address within the specific memory
area at which the image file will be loaded. Note that the base address is specified as the offset
within the specific memory area, and not necessarily as the actual memory address as seen
by the Apple. So for motherboard ROM, for example, specifying a base as 2DED will cause the
image to be loaded at offset 2DED in the ROM, which will be addressed by the Apple at
memory address $FDED (because motherboard ROM ``starts'' at address $D000).
image to be loaded at offset $2DED in the ROM, which will be addressed by the Apple at
memory address $FDED, because motherboard ROM ``starts'' at address $D000, and $D000 + $2DED = $FDED.
For peripheral cards, the ROM will be seen at locations $Cs00-$CsFF, where s is the slot
number (1 through 7). The ``seventh ROM'' can be seen as locations $C800-$CFFF; Jim Sather

View File

@ -16,7 +16,9 @@ clipboardhandler.cpp clockcard.cpp \
configep2.cpp cpu.cpp diskcontroller.cpp drive.cpp drivemotor.cpp \
emptyslot.cpp emulator.cpp firmwarecard.cpp gui.cpp hypermode.cpp \
keyboard.cpp keyboardbuffermode.cpp languagecard.cpp filterchroma.cpp \
filterluma.cpp lss.cpp main.cpp memory.cpp paddlebuttonstates.cpp \
filterluma.cpp lss.cpp main.cpp memory.cpp \
memorychip.cpp memoryrow.cpp memorystrapping.cpp memoryrandomaccess.cpp \
paddlebuttonstates.cpp \
paddles.cpp picturegenerator.cpp powerupreset.cpp raminitializer.cpp \
screenimage.cpp slots.cpp speakerclicker.cpp standardin.cpp \
standardinproducer.cpp standardout.cpp steppermotor.cpp textcharacters.cpp \
@ -31,7 +33,9 @@ card.h cassette.h cassettein.h cassetteout.h \
clipboardhandler.h clockcard.h configep2.h cpu.h \
diskcontroller.h drive.h drivemotor.h e2const.h emptyslot.h emulator.h firmwarecard.h font3x5.h gui.h \
hypermode.h keyboardbuffermode.h keyboard.h languagecard.h filterchroma.h \
filterluma.h lss.h memory.h paddlebuttonstates.h paddles.h picturegenerator.h \
filterluma.h lss.h memory.h \
memorychip.h memoryrow.h memorystrapping.h memoryrandomaccess.h \
paddlebuttonstates.h paddles.h picturegenerator.h \
powerupreset.h raminitializer.h screenimage.h slots.h speakerclicker.h \
standardin.h standardinproducer.h standardout.h steppermotor.h \
textcharacterimages.h textcharacters.h timable.h util.h \

View File

@ -17,6 +17,7 @@
*/
#include "addressbus.h"
#include "memory.h"
#include "memoryrandomaccess.h"
#include "keyboard.h"
#include "videomode.h"
#include "paddles.h"
@ -26,7 +27,7 @@
#include "cassetteout.h"
#include "slots.h"
AddressBus::AddressBus(ScreenImage& gui, int& revision, Memory& ram, Memory& rom, Keyboard& kbd, VideoMode& vid, Paddles& paddles, PaddleButtonStates& paddleButtonStates, SpeakerClicker& speaker, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Slots& slts):
AddressBus::AddressBus(ScreenImage& gui, int& revision, MemoryRandomAccess& ram, Memory& rom, Keyboard& kbd, VideoMode& vid, Paddles& paddles, PaddleButtonStates& paddleButtonStates, SpeakerClicker& speaker, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Slots& slts):
gui(gui), revision(revision), ram(ram), rom(rom), kbd(kbd), vid(vid), paddles(paddles), paddleButtonStates(paddleButtonStates), speaker(speaker), cassetteIn(cassetteIn), cassetteOut(cassetteOut), slts(slts)
{
}

View File

@ -20,6 +20,7 @@
class ScreenImage;
class Memory;
class MemoryRandomAccess;
class Keyboard;
class VideoMode;
class Paddles;
@ -33,7 +34,7 @@ class AddressBus {
private:
ScreenImage& gui;
int& revision;
Memory& ram;
MemoryRandomAccess& ram;
Memory& rom;
Keyboard& kbd;
VideoMode& vid;
@ -47,7 +48,7 @@ class AddressBus {
unsigned char data; // this emulates the (floating) data bus
public:
AddressBus(ScreenImage& gui, int& revision, Memory& ram, Memory& rom, Keyboard& kbd, VideoMode& vid, Paddles& paddles, PaddleButtonStates& paddleButtonStates, SpeakerClicker& speaker, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Slots& slts);
AddressBus(ScreenImage& gui, int& revision, MemoryRandomAccess& ram, Memory& rom, Keyboard& kbd, VideoMode& vid, Paddles& paddles, PaddleButtonStates& paddleButtonStates, SpeakerClicker& speaker, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Slots& slts);
~AddressBus();
unsigned char read(const unsigned short address);

View File

@ -42,7 +42,7 @@ Apple2::Apple2(KeypressQueue& keypresses, PaddleButtonStates& paddleButtonStates
slts(gui),
kbd(keypresses,fhyper,buffered),
rom(AddressBus::MOTHERBOARD_ROM_SIZ),
ram(AddressBus::MOTHERBOARD_RAM_SIZ),
ram(revision),
cassetteIn(gui),
cassetteOut(gui),
addressBus(gui,revision,ram,rom,kbd,videoMode,paddles,paddleButtonStates,speaker,cassetteIn,cassetteOut,slts),

View File

@ -24,6 +24,7 @@
#include "keyboard.h"
#include "addressbus.h"
#include "memory.h"
#include "memoryrandomaccess.h"
#include "picturegenerator.h"
#include "textcharacters.h"
#include "video.h"
@ -51,7 +52,7 @@ class Apple2 : public Timable
Paddles paddles;
SpeakerClicker speaker;
Memory rom;
Memory ram;
MemoryRandomAccess ram;
CassetteIn cassetteIn;
CassetteOut cassetteOut;
AddressBus addressBus;

View File

@ -18,6 +18,7 @@
#include "configep2.h"
#include "memory.h"
#include "memoryrandomaccess.h"
#include "slots.h"
#include "diskcontroller.h"
#include "languagecard.h"
@ -36,6 +37,34 @@
#include <string>
#include <stdexcept>
#define K 1024u
static std::uint16_t chip_size(const std::string &chip_model) {
if (chip_model == "4K") {
return 4u*K;
}
if (chip_model == "16K") {
return 16u*K;
}
if (chip_model == "-") {
return 0u;
}
throw ConfigException("unrecognized RAM chip model");
}
static std::uint16_t memory_block_size(const std::string &block_size) {
if (block_size == "4K") {
return 4u*K;
}
if (block_size == "16K") {
return 16u*K;
}
throw ConfigException("invalid RAM strapping block size (must be 4K or 16K)");
}
unsigned char Config::disk_mask(0);
Config::Config(const std::string& file_path):
@ -75,7 +104,7 @@ static void trim(std::string& str)
}
}
void Config::parse(Memory& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut)
void Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut)
{
std::ifstream* pConfig;
@ -154,7 +183,7 @@ void Config::parse(Memory& ram, Memory& rom, Slots& slts, int& revision, ScreenI
// TODO: make sure there is no more than ONE stdin and/or ONE stdout card
}
void Config::parseLine(const std::string& line, Memory& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut)
void Config::parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut)
{
try
{
@ -166,7 +195,7 @@ void Config::parseLine(const std::string& line, Memory& ram, Memory& rom, Slots&
}
}
void Config::tryParseLine(const std::string& line, Memory& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut)
void Config::tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut)
{
std::istringstream tok(line);
@ -180,7 +209,62 @@ void Config::tryParseLine(const std::string& line, Memory& ram, Memory& rom, Slo
insertCard(sCardType,slot,slts,gui);
}
else if (cmd == "import")
else if (cmd == "motherboard") {
std::string op;
tok >> op;
if (op == "ram") {
/* ram ROW BIT0 [BIT1 [... [BIT7]]]
* ram e -
* ram d 4096 MK4096 4K
* ram c 16K 4116 MK4116 MM5290 16K 16K 16K 16K
*/
std::string row;
tok >> row;
std::transform(row.begin(), row.end(), row.begin(), ::toupper);
if (row != "C" && row != "D" && row != "E") {
throw ConfigException("expected row to be C, D, or E");
}
std::string chip_model;
tok >> chip_model;
std::uint16_t siz = chip_size(chip_model);
for (std::uint_fast8_t bit = 0u; bit < 8u; ++bit) {
if (siz) {
ram.insert_chip(row, MemoryChip(siz,chip_model), bit);
} else {
ram.remove_chip(row, bit);
}
std::string chip_model_optional;
tok >> chip_model_optional;
if (chip_model_optional.length()) {
chip_model = chip_model_optional;
}
siz = chip_size(chip_model);
}
} else if (op == "strap") {
/* strap ROM K start-addr
* strap c 4K 0000
* strap d 4K 1000
* strap e 4K 2000
*/
std::string row;
tok >> row;
std::transform(row.begin(), row.end(), row.begin(), ::toupper);
if (row != "C" && row != "D" && row != "E") {
throw ConfigException("expected row to be C, D, or E");
}
std::string block_size;
tok >> block_size;
std::uint16_t siz = memory_block_size(block_size);
unsigned short base(0);
tok >> std::hex >> base;
// TODO validate siz/base combination
ram.strap_to(row, base, siz);
} else {
throw ConfigException("error at \"motherboard\"; expected \"ram\" or \"strap\"");
}
}
else if (cmd == "import")
{
std::string sm;
tok >> sm;
@ -216,10 +300,6 @@ void Config::tryParseLine(const std::string& line, Memory& ram, Memory& rom, Slo
{
rom.load(base,memfile);
}
else if (romtype == "ram")
{
ram.load(base,memfile);
}
else
{
throw ConfigException("error at \""+romtype+"\"; expected rom or ram");

View File

@ -20,6 +20,7 @@
#include <string>
class Memory;
class MemoryRandomAccess;
class Slots;
class ScreenImage;
class CassetteIn;
@ -42,14 +43,14 @@ private:
static void unloadDisk(Slots& slts, int slot, int drive);
static void saveDisk(Slots& slts, int slot, int drive);
static void insertCard(const std::string& cardType, int slot, Slots& slts, ScreenImage& gui);
static void tryParseLine(const std::string& line, Memory& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut);
static void tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut);
public:
Config(const std::string& file_path);
~Config();
void parse(Memory& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut);
static void parseLine(const std::string& line, Memory& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut);
void parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut);
static void parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut);
};
#endif

View File

@ -1,6 +1,7 @@
/*
epple2
Copyright (C) 2008 by Christopher A. Mosher <cmosher01@gmail.com>
Copyright © 2008, 2019, Christopher Alan Mosher, Shelton, CT, USA. <cmosher01@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,32 +20,76 @@
#include <vector>
#include <algorithm>
#include <istream>
#include <cstdlib>
#include "raminitializer.h"
const int Memory::CLEAR_VALUE(0);
/*
* If any RAM IC sockets are empty, set the corresponding bits to 1 most of the time.
* But set to 0 instead, with probability 1 in 137 (a rough estimate obtained empirically)
*/
static std::uint8_t randomize_missing_bits(std::uint8_t v, const std::uint8_t bits) {
std::uint8_t bit = 1u;
for (std::uint_fast8_t i = 0; i < 8; ++i) {
if (bits & bit) {
double r = static_cast<double>(std::rand())/RAND_MAX;
if (r < 1.0/137.0) {
v &= ~bit;
} else {
v |= bit;
}
}
bit <<= 1;
}
return v;
}
Memory::Memory(const size_t n):
bytes(n)
{
bytes(n),
clear_value(0u),
missing_bits(0u) {
}
void Memory::clear()
{
std::fill(this->bytes.begin(),this->bytes.end(),CLEAR_VALUE);
void Memory::clear() {
std::fill(this->bytes.begin(), this->bytes.end(), this->clear_value);
}
void Memory::powerOn()
{
RAMInitializer initRam(*this);
initRam.init();
void Memory::init() {
RAMInitializer initRam(*this);
initRam.init();
}
void Memory::powerOff()
{
clear();
void Memory::load(const std::uint16_t base, std::istream& in) {
in.read(reinterpret_cast<char*>(&this->bytes[base]), static_cast<ptrdiff_t>(this->bytes.size()-base));
}
void Memory::load(const unsigned short base, std::istream& in)
{
in.read((char*)&this->bytes[base],this->bytes.size()-base);
void Memory::powerOn() {
init();
}
void Memory::powerOff() {
clear();
}
size_t Memory::size() const {
return this->bytes.size();
}
std::uint8_t Memory::read(const std::uint16_t address) const {
std::uint8_t v = this->bytes[address];
if (this->missing_bits) {
v = randomize_missing_bits(v, this->missing_bits);
}
return v;
}
void Memory::write(const std::uint16_t address, const std::uint8_t data) {
this->bytes[address] = data;
}

View File

@ -20,34 +20,27 @@
#include <vector>
#include <istream>
#include <cstdint>
class Memory
{
private:
std::vector<unsigned char> bytes;
static const int CLEAR_VALUE;
class Memory {
private:
std::vector<std::uint8_t> bytes;
const std::uint8_t clear_value;
const std::uint8_t missing_bits;
public:
Memory(const size_t n);
size_t size() const
{
return this->bytes.size();
}
unsigned char read(const unsigned short address) const
{
return this->bytes[address];
}
void write(const unsigned short address, const unsigned char data)
{
this->bytes[address] = data;
}
void clear();
void powerOn();
void powerOff();
void load(const unsigned short base, std::istream& in);
public:
Memory(const size_t n);
virtual ~Memory() { }
size_t size() const;
std::uint8_t read(const std::uint16_t address) const;
void write(const std::uint16_t address, const std::uint8_t data);
void powerOn();
void powerOff();
void clear();
void init();
void load(const std::uint16_t base, std::istream& in);
};
#endif

74
src/memorychip.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "memorychip.h"
#include <stdexcept>
#include <algorithm>
#include <vector>
#define K 1024
MemoryChip::MemoryChip(const std::uint16_t size, const std::string &id_model):
size_bits(size),
id_model(id_model) {
if (size_bits != 4*K && size_bits != 16*K) {
throw std::out_of_range("MemoryChip must be 4K or 16K");
}
}
MemoryChip::MemoryChip(const MemoryChip &that):
size_bits(that.size_bits),
id_model(that.id_model) {
}
MemoryChip &MemoryChip::operator=(const MemoryChip &that) {
this->size_bits = that.size_bits;
this->id_model = that.id_model;
return *this;
}
MemoryChip::~MemoryChip() {
}
std::uint16_t MemoryChip::size() const {
return this->size_bits;
}
std::string MemoryChip::id() const {
return this->id_model;
}
bool MemoryChip::exists() const {
return true;
}
static void bitflag(const bool on, const std::uint8_t mask, std::uint8_t &byte) {
if (on) {
byte |= mask;
} else {
byte &= ~mask;
}
}
//#define CYCLE 128u
#define CYCLE 2u
void MemoryChip::init(const std::uint8_t mask, std::vector<std::uint8_t> &bytes, const std::uint16_t size) const {
bool on = false;
std::uint8_t c = 0u;
for (std::uint16_t i = 0u; i < std::min(size, this->size_bits); ++i) {
bitflag(on, mask, bytes[i]);
if (CYCLE <= ++c) {
c = 0u;
on = !on;
}
}
}
MemoryChipEmptySocket::MemoryChipEmptySocket():
MemoryChip(4*K,"[empty]") {
}
MemoryChipEmptySocket::~MemoryChipEmptySocket() {
}
bool MemoryChipEmptySocket::exists() const {
return false;
}

29
src/memorychip.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef MEMORYCHIP_H
#define MEMORYCHIP_H
#include <cstdint>
#include <string>
class MemoryChip {
private:
std::uint16_t size_bits;
std::string id_model;
public:
MemoryChip(const std::uint16_t size, const std::string &id_model);
MemoryChip(const MemoryChip &that);
MemoryChip &operator=(const MemoryChip &that);
virtual ~MemoryChip();
std::uint16_t size() const;
std::string id() const;
virtual void init(const std::uint8_t mask, std::vector<std::uint8_t> &bytes, const std::uint16_t size) const;
virtual bool exists() const;
};
class MemoryChipEmptySocket : public MemoryChip {
public:
MemoryChipEmptySocket();
virtual ~MemoryChipEmptySocket();
virtual bool exists() const;
};
#endif // MEMORYCHIP_H

149
src/memoryrandomaccess.cpp Normal file
View File

@ -0,0 +1,149 @@
#include "memoryrandomaccess.h"
#include <cstdint>
#include <exception>
#define K 1024u
MemoryRandomAccess::MemoryRandomAccess(int &revision):
revision(revision),
rowE('E'),
strapE(rowE),
rowD('D'),
strapD(rowD),
rowC('C'),
strapC(rowC) {
}
MemoryRow &MemoryRandomAccess::row_of(const std::string &row) {
if (row == "E") {
return this->rowE;
}
if (row == "D") {
return this->rowD;
}
if (row == "C") {
return this->rowC;
}
throw std::logic_error("expected C/D/E");
}
MemoryStrapping &MemoryRandomAccess::strapping_of(const std::string &row) {
if (row == "E") {
return this->strapE;
}
if (row == "D") {
return this->strapD;
}
if (row == "C") {
return this->strapC;
}
throw std::logic_error("expected C/D/E");
}
void MemoryRandomAccess::insert_chip(const std::string &row, MemoryChip chip, const std::uint_fast8_t socket) {
row_of(row).insert_chip(chip, socket);
}
void MemoryRandomAccess::remove_chip(const std::string &row, const std::uint_fast8_t socket) {
row_of(row).remove_chip(socket);
}
void MemoryRandomAccess::strap_to(const std::string &row, std::uint16_t addr_base, std::uint16_t addr_size) {
strapping_of(row).strap_to(addr_base, addr_size);
}
bool MemoryRandomAccess::k20or24() const {
const std::uint32_t k = this->rowC.size() + this->rowD.size() + this->rowD.size();
return (k==20*K) || (k==24*K);
}
/* for 20K or 24K on rev. 0: pages 40-5F are dup w/ 60-7F */
std::uint8_t MemoryRandomAccess::buggyRamRead(const std::uint16_t address) const {
std::uint16_t ax = address & ~0x2000u;
if (this->strapE.contains(ax)) {
return this->strapE.read(ax);
}
if (this->strapD.contains(ax)) {
return this->strapD.read(ax);
}
if (this->strapC.contains(ax)) {
return this->strapC.read(ax);
}
ax = address | 0x2000u;
if (this->strapE.contains(ax)) {
return this->strapE.read(ax);
}
if (this->strapD.contains(ax)) {
return this->strapD.read(ax);
}
if (this->strapC.contains(ax)) {
return this->strapC.read(ax);
}
return MemoryRow::missing_memory_byte_value();
}
std::uint8_t MemoryRandomAccess::read(const std::uint16_t address) const {
if (this->revision == 0 && k20or24() && ((address & 0xC000u) == 0x4000u)) {
return buggyRamRead(address);
}
if (this->strapE.contains(address)) {
return this->strapE.read(address);
}
if (this->strapD.contains(address)) {
return this->strapD.read(address);
}
if (this->strapC.contains(address)) {
return this->strapC.read(address);
}
return MemoryRow::missing_memory_byte_value();
}
void MemoryRandomAccess::buggyRamWrite(std::uint16_t address, const std::uint8_t data) {
std::uint16_t ax = address & ~0x2000u;
if (this->strapE.contains(ax)) {
this->strapE.write(ax, data);
}
if (this->strapD.contains(ax)) {
this->strapD.write(ax, data);
}
if (this->strapC.contains(ax)) {
this->strapC.write(ax, data);
}
ax = address | 0x2000u;
if (this->strapE.contains(ax)) {
this->strapE.write(ax, data);
}
if (this->strapD.contains(ax)) {
this->strapD.write(ax, data);
}
if (this->strapC.contains(ax)) {
this->strapC.write(ax, data);
}
}
void MemoryRandomAccess::write(const std::uint16_t address, const std::uint8_t data) {
if (this->revision == 0 && k20or24() && ((address & 0xC000u) == 0x4000u)) {
buggyRamWrite(address, data);
}
if (this->strapE.contains(address)) {
this->strapE.write(address, data);
}
if (this->strapD.contains(address)) {
this->strapD.write(address, data);
}
if (this->strapC.contains(address)) {
this->strapC.write(address, data);
}
}
void MemoryRandomAccess::powerOn() {
this->rowE.powerOn();
this->rowD.powerOn();
this->rowC.powerOn();
}
void MemoryRandomAccess::powerOff() {
this->rowE.powerOff();
this->rowD.powerOff();
this->rowC.powerOff();
}

38
src/memoryrandomaccess.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef MEMORYRANDOMACCESS_H
#define MEMORYRANDOMACCESS_H
#include "memorystrapping.h"
#include "memoryrow.h"
#include <cstdint>
#include <string>
class MemoryRandomAccess {
private:
int &revision;
MemoryRow rowE;
MemoryStrapping strapE;
MemoryRow rowD;
MemoryStrapping strapD;
MemoryRow rowC;
MemoryStrapping strapC;
MemoryRow &row_of(const std::string &row);
MemoryStrapping &strapping_of(const std::string &row);
std::uint8_t buggyRamRead(std::uint16_t address) const;
void buggyRamWrite(std::uint16_t address, const std::uint8_t data);
bool k20or24() const;
public:
MemoryRandomAccess(int &revision);
void insert_chip(const std::string &row, MemoryChip chip, const std::uint_fast8_t socket);
void remove_chip(const std::string &row, const std::uint_fast8_t socket);
void strap_to(const std::string &row, std::uint16_t addr_base, std::uint16_t addr_size);
std::uint8_t read(std::uint16_t address) const;
void write(std::uint16_t address, const std::uint8_t data);
void powerOn();
void powerOff();
};
#endif

131
src/memoryrow.cpp Normal file
View File

@ -0,0 +1,131 @@
#include "memoryrow.h"
#include <exception>
#include <cstdlib>
/*
* If any RAM IC sockets are empty, set the corresponding bits to 1 most of the time.
* For some addresses it seems they are always 1, but for other addresses they can return
* 0 sometimes, empirically I've seen anywhere from 8% to 15% of the time.
*/
static std::uint8_t randomize_missing_bits(std::uint8_t v, const std::uint8_t bits) {
std::uint8_t bit = 1u;
for (std::uint_fast8_t i = 0; i < 8; ++i) {
if (bits & bit) {
double r = static_cast<double>(std::rand())/RAND_MAX;
if (r < 0.11) {
v &= ~bit;
} else {
v |= bit;
}
}
bit <<= 1;
}
return v;
}
MemoryRow::MemoryRow(const char label):
label(label) {
}
MemoryRow::~MemoryRow() {
}
void MemoryRow::insert_chip(MemoryChip chip, const std::uint_fast8_t socket) {
if (socket < 8u) {
remove_chip(socket);
if (chip.exists()) {
this->chips[socket] = chip;
this->missing_bits &= ~(1u << socket);
this->values_stored.resize(calculate_size());
}
} else {
throw std::out_of_range("socket must be < 8");
}
}
void MemoryRow::remove_chip(const std::uint_fast8_t socket) {
if (socket < 8u) {
this->chips[socket] = MemoryChipEmptySocket();
this->missing_bits |= (1u << socket);
this->values_stored.resize(calculate_size());
} else {
throw std::out_of_range("socket must be < 8");
}
}
/*
* If no chips, return 0.
* Otherwise, return minimum of existing chips (4K or 16K)
*/
std::uint16_t MemoryRow::calculate_size() const {
std::uint16_t size_new = 0u;
for (std::uint_fast8_t i_chip = 0; i_chip < 8; ++i_chip) {
const MemoryChip &chip = this->chips[i_chip];
if (chip.exists()) {
const std::uint16_t s = chip.size();
if (size_new == 0u || s < size_new) {
size_new = s;
}
}
}
return size_new;
}
void MemoryRow::powerOff() {
this->power = false;
}
/*
* Calls init of each chip, and uses a bit mask to assemble into bytes.
*/
void MemoryRow::powerOn() {
if (!this->power) {
this->power = true;
std::uint8_t mask_bit = 1u;
for (std::uint_fast8_t i_bit = 0; i_bit < 8; ++i_bit) {
const MemoryChip &chip = this->chips[i_bit];
if (chip.exists()) {
chip.init(mask_bit, this->values_stored, size());
}
mask_bit <<= 1;
}
}
}
std::uint16_t MemoryRow::size() const {
return static_cast<std::uint16_t>(this->values_stored.size());
}
std::uint8_t MemoryRow::missing_memory_byte_value() {
return randomize_missing_bits(0xFFu, 0xFFu);
}
std::uint8_t MemoryRow::read(const std::uint16_t address_offset) const {
if (this->power) {
std::uint8_t v;
if (address_offset < this->values_stored.size()) {
v = this->values_stored[address_offset];
if (this->missing_bits) {
v = randomize_missing_bits(v, this->missing_bits);
}
} else {
v = missing_memory_byte_value();
}
return v;
} else {
throw std::logic_error("cannot read memory when power is off");
}
}
void MemoryRow::write(const std::uint16_t address, const std::uint8_t data) {
if (this->power) {
this->values_stored[address] = data;
} else {
throw std::logic_error("cannot write memory when power is off");
}
}

49
src/memoryrow.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef MEMORYROW_H
#define MEMORYROW_H
#include "memorychip.h"
#include <map>
#include <vector>
#include <array>
#include <cstdint>
class MemoryRow {
private:
/* C, D, E */
const char label;
bool power = false;
/* 8 sockets for memory chips (empty socket represented by MemoryChipEmptySocket) */
std::array<MemoryChip,8> chips = {MemoryChipEmptySocket(),MemoryChipEmptySocket(),MemoryChipEmptySocket(),MemoryChipEmptySocket(),MemoryChipEmptySocket(),MemoryChipEmptySocket(),MemoryChipEmptySocket(),MemoryChipEmptySocket()};
/* bit mask of empty chip sockets */
std::uint8_t missing_bits = 0xFFu;
/*
* Instead of storing each bit in a MemoryChip (too slow), we store the data here as bytes,
* representing one bit for each chip.
*/
std::vector<std::uint8_t> values_stored;
std::uint16_t calculate_size() const;
public:
MemoryRow(const char label);
virtual ~MemoryRow();
void insert_chip(MemoryChip chip, const std::uint_fast8_t socket);
void remove_chip(const std::uint_fast8_t socket);
void powerOff();
void powerOn();
/* 4K or 16K, size of each chip (or minimum in corner case of mixed sizes) */
std::uint16_t size() const;
std::uint8_t read(const std::uint16_t address) const;
void write(const std::uint16_t address, const std::uint8_t data);
static std::uint8_t missing_memory_byte_value();
};
#endif // MEMORYROW_H

23
src/memorystrapping.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "memorystrapping.h"
#include <algorithm>
MemoryStrapping::MemoryStrapping(MemoryRow &row):
row(row) {
}
void MemoryStrapping::strap_to(std::uint16_t addr_base, std::uint16_t addr_size) {
this->addr_base = addr_base;
this->addr_size = addr_size;
}
bool MemoryStrapping::contains(std::uint16_t address) const {
return this->addr_base <= address && address < this->addr_base + std::min(this->row.size(), this->addr_size);
}
std::uint8_t MemoryStrapping::read(const std::uint16_t address) const {
return this->row.read(address - this->addr_base);
}
void MemoryStrapping::write(const std::uint16_t address, const std::uint8_t data) {
this->row.write(address - this->addr_base, data);
}

20
src/memorystrapping.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef MEMORYSTRAPPING_H
#define MEMORYSTRAPPING_H
#include "memoryrow.h"
#include <cstdint>
class MemoryStrapping {
private:
MemoryRow &row;
std::uint16_t addr_base;
std::uint16_t addr_size;
public:
MemoryStrapping(MemoryRow &row);
void strap_to(std::uint16_t addr_base, std::uint16_t addr_size);
bool contains(std::uint16_t address) const;
std::uint8_t read(const std::uint16_t address) const;
void write(const std::uint16_t address, const std::uint8_t data);
};
#endif

View File

@ -40,7 +40,7 @@ void RAMInitializer::init()
putBytesUntilFull(b++,2);
putBytesUntilFull(b++,2);
putBytesUntilFull(b++,1);
};
}
void RAMInitializer::putBytesUntilFull(int bit, int pat)