mirror of
https://github.com/st3fan/ewm.git
synced 2025-01-14 06:30:51 +00:00
Fixes #44 - Implement the Disk ][ Controller
This commit is contained in:
parent
5a445f1d9b
commit
aa66588a47
4
Makefile
4
Makefile
@ -21,11 +21,11 @@
|
||||
# SOFTWARE.
|
||||
|
||||
CC=cc
|
||||
CFLAGS=-std=c11 -O3 -Wpedantic -Wall -Wshadow -Werror -Wshadow -Wno-gnu-binary-literal -g
|
||||
CFLAGS=-std=c11 -O0 -Wpedantic -Wall -Wshadow -Werror -Wshadow -Wno-gnu-binary-literal -g
|
||||
LDFLAGS=-g -L/usr/local/lib
|
||||
|
||||
EWM_EXECUTABLE=ewm
|
||||
EWM_SOURCES=cpu.c ins.c pia.c mem.c ewm.c fmt.c a2p.c scr.c
|
||||
EWM_SOURCES=cpu.c ins.c pia.c mem.c ewm.c fmt.c a2p.c scr.c dsk.c
|
||||
EWM_OBJECTS=$(EWM_SOURCES:.c=.o)
|
||||
EWM_LIBS=-lcurses -lSDL2 -lSDL2_ttf
|
||||
|
||||
|
7
a2p.c
7
a2p.c
@ -26,6 +26,7 @@
|
||||
#include "cpu.h"
|
||||
#include "mem.h"
|
||||
|
||||
#include "dsk.h"
|
||||
#include "a2p.h"
|
||||
|
||||
#define EWM_A2P_SS_KBD 0xc000
|
||||
@ -108,9 +109,15 @@ void a2p_init(struct a2p_t *a2p, struct cpu_t *cpu) {
|
||||
a2p->rom = cpu_add_rom_file(cpu, 0xd000, "roms/a2p.rom");
|
||||
a2p->iom = cpu_add_iom(cpu, 0xc000, 0xc0ff, a2p, a2p_iom_read, a2p_iom_write);
|
||||
|
||||
a2p->dsk = ewm_dsk_create(cpu);
|
||||
|
||||
a2p->screen1_data = malloc(1 * 1024);
|
||||
a2p->screen1 = cpu_add_iom(cpu, 0x0400, 0x07ff, a2p, a2p_screen1_read, a2p_screen1_write);
|
||||
|
||||
a2p->screen2_data = malloc(1 * 1024);
|
||||
a2p->screen2 = cpu_add_iom(cpu, 0x0800, 0x0bff, a2p, a2p_screen2_read, a2p_screen2_write);
|
||||
}
|
||||
|
||||
int a2p_load_disk(struct a2p_t *a2p, int drive, char *path) {
|
||||
return ewm_dsk_set_disk_file(a2p->dsk, drive, false, path);
|
||||
}
|
||||
|
5
a2p.h
5
a2p.h
@ -23,12 +23,16 @@
|
||||
#ifndef EWM_A2P_H
|
||||
#define EWM_A2P_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct mem_t;
|
||||
struct ewm_dsk_t;
|
||||
|
||||
struct a2p_t {
|
||||
struct mem_t *ram;
|
||||
struct mem_t *rom;
|
||||
struct mem_t *iom;
|
||||
struct ewm_dsk_t *dsk;
|
||||
|
||||
struct mem_t *screen1;
|
||||
uint8_t *screen1_data;
|
||||
@ -44,5 +48,6 @@ struct a2p_t {
|
||||
};
|
||||
|
||||
void a2p_init(struct a2p_t *a2p, struct cpu_t *cpu);
|
||||
int a2p_load_disk(struct a2p_t *a2p, int drive, char *path);
|
||||
|
||||
#endif
|
||||
|
479
dsk.c
Normal file
479
dsk.c
Normal file
@ -0,0 +1,479 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "mem.h"
|
||||
#include "cpu.h"
|
||||
#include "dsk.h"
|
||||
|
||||
//
|
||||
// This implements a 16-sector Disk ][ controller with two drives
|
||||
// attached. It is currently fixed to slot 6, which is pretty
|
||||
// normal. That will be changed at a later stage when we introduce
|
||||
// slots and cards in a more generic way.
|
||||
//
|
||||
// Most of this code is based on Beneath Apple DOS and another open
|
||||
// source emulator at https://github.com/whscullin/apple2js
|
||||
//
|
||||
|
||||
// Private
|
||||
|
||||
#define EWM_DISKII_PHASE0OFF 0xc0e0
|
||||
#define EWM_DISKII_PHASE0ON 0xc0e1
|
||||
#define EWM_DISKII_PHASE1OFF 0xc0e2
|
||||
#define EWM_DISKII_PHASE1ON 0xc0e3
|
||||
#define EWM_DISKII_PHASE2OFF 0xc0e4
|
||||
#define EWM_DISKII_PHASE2ON 0xc0e5
|
||||
#define EWM_DISKII_PHASE3OFF 0xc0e6
|
||||
#define EWM_DISKII_PHASE3ON 0xc0e7
|
||||
|
||||
#define EWM_DISKII_DRIVEOFF 0xc0e8
|
||||
#define EWM_DISKII_DRIVEON 0xc0e9
|
||||
#define EWM_DISKII_DRIVE1 0xc0ea
|
||||
#define EWM_DISKII_DRIVE2 0xc0eb
|
||||
#define EWM_DISKII_READ 0xc0ec
|
||||
#define EWM_DISKII_WRITE 0xc0ed
|
||||
#define EWM_DISKII_READMODE 0xc0ee
|
||||
#define EWM_DISKII_WRITEMODE 0xc0ef
|
||||
|
||||
#define EWM_DSK_MODE_READ 0
|
||||
#define EWM_DSK_MODE_WRITE 1
|
||||
|
||||
static uint8_t dsk_rom[] = {
|
||||
0xa2,0x20,0xa0,0x00,0xa2,0x03,0x86,0x3c,0x8a,0x0a,0x24,0x3c,0xf0,0x10,0x05,0x3c,
|
||||
0x49,0xff,0x29,0x7e,0xb0,0x08,0x4a,0xd0,0xfb,0x98,0x9d,0x56,0x03,0xc8,0xe8,0x10,
|
||||
0xe5,0x20,0x58,0xff,0xba,0xbd,0x00,0x01,0x0a,0x0a,0x0a,0x0a,0x85,0x2b,0xaa,0xbd,
|
||||
0x8e,0xc0,0xbd,0x8c,0xc0,0xbd,0x8a,0xc0,0xbd,0x89,0xc0,0xa0,0x50,0xbd,0x80,0xc0,
|
||||
0x98,0x29,0x03,0x0a,0x05,0x2b,0xaa,0xbd,0x81,0xc0,0xa9,0x56,0x20,0xa8,0xfc,0x88,
|
||||
0x10,0xeb,0x85,0x26,0x85,0x3d,0x85,0x41,0xa9,0x08,0x85,0x27,0x18,0x08,0xbd,0x8c,
|
||||
0xc0,0x10,0xfb,0x49,0xd5,0xd0,0xf7,0xbd,0x8c,0xc0,0x10,0xfb,0xc9,0xaa,0xd0,0xf3,
|
||||
0xea,0xbd,0x8c,0xc0,0x10,0xfb,0xc9,0x96,0xf0,0x09,0x28,0x90,0xdf,0x49,0xad,0xf0,
|
||||
0x25,0xd0,0xd9,0xa0,0x03,0x85,0x40,0xbd,0x8c,0xc0,0x10,0xfb,0x2a,0x85,0x3c,0xbd,
|
||||
0x8c,0xc0,0x10,0xfb,0x25,0x3c,0x88,0xd0,0xec,0x28,0xc5,0x3d,0xd0,0xbe,0xa5,0x40,
|
||||
0xc5,0x41,0xd0,0xb8,0xb0,0xb7,0xa0,0x56,0x84,0x3c,0xbc,0x8c,0xc0,0x10,0xfb,0x59,
|
||||
0xd6,0x02,0xa4,0x3c,0x88,0x99,0x00,0x03,0xd0,0xee,0x84,0x3c,0xbc,0x8c,0xc0,0x10,
|
||||
0xfb,0x59,0xd6,0x02,0xa4,0x3c,0x91,0x26,0xc8,0xd0,0xef,0xbc,0x8c,0xc0,0x10,0xfb,
|
||||
0x59,0xd6,0x02,0xd0,0x87,0xa0,0x00,0xa2,0x56,0xca,0x30,0xfb,0xb1,0x26,0x5e,0x00,
|
||||
0x03,0x2a,0x5e,0x00,0x03,0x2a,0x91,0x26,0xc8,0xd0,0xee,0xe6,0x27,0xe6,0x3d,0xa5,
|
||||
0x3d,0xcd,0x00,0x08,0xa6,0x2b,0x90,0xdb,0x4c,0x01,0x08,0x00,0x00,0x00,0x00,0x00
|
||||
};
|
||||
|
||||
// See Beneath Apple DOS 3-21
|
||||
static uint8_t dsk_wr_table[] = {
|
||||
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
|
||||
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
|
||||
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
|
||||
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
|
||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
||||
};
|
||||
|
||||
static int dsk_phase_delta[4][4] = {
|
||||
{ 0, 1, 2,-1},
|
||||
{-1, 0, 1, 2},
|
||||
{-2,-1, 0, 1},
|
||||
{ 1,-2,-1, 0}
|
||||
};
|
||||
|
||||
static struct ewm_dsk_drive_t *dsk_drive(struct ewm_dsk_t *dsk) {
|
||||
return &dsk->drives[dsk->drive];
|
||||
}
|
||||
|
||||
static void dsk_phase(struct ewm_dsk_t *dsk, int phase, bool on) {
|
||||
if (on) {
|
||||
//printf("[DSK] Disk #%d phase %d on\n", dsk->drive, phase);
|
||||
struct ewm_dsk_drive_t *drive = dsk_drive(dsk);
|
||||
|
||||
drive->track += dsk_phase_delta[drive->phase][phase];
|
||||
drive->phase = phase;
|
||||
|
||||
if (drive->track > EWM_DSK_TRACKS * 2 - 1) {
|
||||
drive->track = EWM_DSK_TRACKS * 2 - 1;
|
||||
}
|
||||
|
||||
if (drive->track < 0) {
|
||||
drive->track = 0;
|
||||
}
|
||||
|
||||
//printf("[DSK] Disk #%d track = %d\n", dsk->drive, drive->track);
|
||||
} else {
|
||||
//printf("[DSK] Disk #%d phase %d off\n", dsk->drive, phase);
|
||||
}
|
||||
}
|
||||
|
||||
static void dsk_write_next(struct ewm_dsk_t *dsk, uint8_t v) {
|
||||
if (dsk->mode == EWM_DSK_MODE_WRITE) {
|
||||
dsk->latch = v;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t dsk_read_next(struct ewm_dsk_t *dsk) {
|
||||
uint8_t result = 0;
|
||||
if (dsk->skip || dsk->mode == EWM_DSK_MODE_WRITE) {
|
||||
struct ewm_dsk_drive_t *drive = dsk_drive(dsk);
|
||||
struct ewm_dsk_track_t track = drive->tracks[drive->track >> 1]; // TODO Because drv->track actually goes to 70?
|
||||
|
||||
//printf("Reading track.data[%d] (track.length = %zu): %.2X\n", drive->head, track.length, track.data[drive->head]);
|
||||
|
||||
if (drive->head >= track.length) {
|
||||
drive->head = 0;
|
||||
}
|
||||
|
||||
if (dsk->mode == EWM_DSK_MODE_WRITE) {
|
||||
track.data[drive->head] = dsk->latch; // TODO Implement write support
|
||||
} else {
|
||||
result = track.data[drive->head];
|
||||
}
|
||||
|
||||
drive->head += 1;
|
||||
}
|
||||
|
||||
dsk->skip = (++dsk->skip % 4); // TODO Why is this done? To simulate a slow disk?
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t dsk_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) {
|
||||
//printf("[DSK] dsk_read at $%.4X\n", addr);
|
||||
struct ewm_dsk_t *dsk = (struct ewm_dsk_t*) mem->obj;
|
||||
uint8_t result = 0x00;
|
||||
|
||||
switch (addr) {
|
||||
case EWM_DISKII_PHASE0OFF:
|
||||
dsk_phase(dsk, 0, false);
|
||||
break;
|
||||
case EWM_DISKII_PHASE0ON:
|
||||
dsk_phase(dsk, 0, true);
|
||||
break;
|
||||
case EWM_DISKII_PHASE1OFF:
|
||||
dsk_phase(dsk, 1, false);
|
||||
break;
|
||||
case EWM_DISKII_PHASE1ON:
|
||||
dsk_phase(dsk, 1, true);
|
||||
break;
|
||||
case EWM_DISKII_PHASE2OFF:
|
||||
dsk_phase(dsk, 2, false);
|
||||
break;
|
||||
case EWM_DISKII_PHASE2ON:
|
||||
dsk_phase(dsk, 2, true);
|
||||
break;
|
||||
case EWM_DISKII_PHASE3OFF:
|
||||
dsk_phase(dsk, 3, false);
|
||||
break;
|
||||
case EWM_DISKII_PHASE3ON:
|
||||
dsk_phase(dsk, 3, true);
|
||||
break;
|
||||
|
||||
case EWM_DISKII_DRIVEOFF:
|
||||
//printf("[DSK] Drive #%d off\n", dsk->drive);
|
||||
dsk->on = false;
|
||||
// TODO Drive light
|
||||
break;
|
||||
case EWM_DISKII_DRIVEON:
|
||||
//printf("[DSK] Drive #%d on\n", dsk->drive);
|
||||
dsk->on = true;
|
||||
// TODO Drive light
|
||||
break;
|
||||
|
||||
case EWM_DISKII_DRIVE1:
|
||||
//printf("[DSK] Select drive #%d\n", dsk->drive);
|
||||
dsk->drive = EWM_DSK_DRIVE1;
|
||||
// TODO Drive light
|
||||
break;
|
||||
case EWM_DISKII_DRIVE2:
|
||||
//printf("[DSK] Select drive #%d\n", dsk->drive);
|
||||
dsk->drive = EWM_DSK_DRIVE2;
|
||||
// TODO Drive light
|
||||
break;
|
||||
|
||||
case EWM_DISKII_READMODE:
|
||||
dsk->mode = EWM_DSK_MODE_READ;
|
||||
if (dsk_drive(dsk)->loaded) {
|
||||
result = (dsk_read_next(dsk) & 0x7f) | (dsk_drive(dsk)->readonly ? 0x80 : 0x00);
|
||||
}
|
||||
break;
|
||||
case EWM_DISKII_WRITEMODE:
|
||||
dsk->mode = EWM_DSK_MODE_WRITE;
|
||||
break;
|
||||
|
||||
case EWM_DISKII_READ:
|
||||
if (dsk_drive(dsk)->loaded) {
|
||||
result = dsk_read_next(dsk);
|
||||
}
|
||||
break;
|
||||
case EWM_DISKII_WRITE:
|
||||
// Called by code, but doesn't do anything?
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "[DSK] Got an unhandled read from $%.4X\n", addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void dsk_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) {
|
||||
//printf("[DSK] dsk_write at $%.4X\n", addr);
|
||||
|
||||
// TODO It is entirely possible that we need to handle to the exact same soft switches as in read
|
||||
struct ewm_dsk_t *dsk = (struct ewm_dsk_t*) mem->obj;
|
||||
switch (addr) {
|
||||
case EWM_DISKII_WRITE:
|
||||
dsk_write_next(dsk, b);
|
||||
break;
|
||||
case EWM_DISKII_WRITEMODE:
|
||||
dsk->mode = EWM_DSK_MODE_WRITE;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "[DSK] Got an unhandled write to $%.4X\n", addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int dsk_native_track_length(int track_idx) {
|
||||
int length = 0;
|
||||
for (int sector_idx = 0; sector_idx < EWM_DSK_SECTORS; sector_idx++) {
|
||||
// Gap 1
|
||||
if (sector_idx == 0) {
|
||||
length += 0x80;
|
||||
} else {
|
||||
if (track_idx == 0) {
|
||||
length += 0x28;
|
||||
} else {
|
||||
length += 0x26;
|
||||
}
|
||||
}
|
||||
// Address field
|
||||
length += 14;
|
||||
// Gap 2
|
||||
length += 5;
|
||||
// Data field
|
||||
length += 3 + 342 + 1 + 3;
|
||||
// Gap 3
|
||||
length += 1;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static uint8_t dsk_fourxfour_hi(uint8_t v) {
|
||||
return ((v & 0b10101010) >> 1) | 0b10101010;
|
||||
}
|
||||
|
||||
static uint8_t dsk_fourxfour_lo(uint8_t v) {
|
||||
return (v & 0b01010101) | 0b10101010;
|
||||
}
|
||||
|
||||
static uint8_t dsk_sector_ordering[EWM_DSK_SECTORS] = {
|
||||
0x0,0xD,0xB,0x9,0x7,0x5,0x3,0x1,0xE,0xC,0xA,0x8,0x6,0x4,0x2,0xF
|
||||
};
|
||||
|
||||
static uint8_t *dsk_convert_sector(struct ewm_dsk_t *dsk, struct ewm_dsk_drive_t *drive, int track_idx, int sector_idx, uint8_t *src, uint8_t *dst) {
|
||||
// Gap 1
|
||||
if (sector_idx == 0) {
|
||||
for (int i = 0; i < 0x80; i++) {
|
||||
*dst++ = 0xff;
|
||||
}
|
||||
} else {
|
||||
if (track_idx == 0) {
|
||||
for (int i = 0; i < 0x28; i++) {
|
||||
*dst++ = 0xff;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 0x26; i++) {
|
||||
*dst++ = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Address Field
|
||||
uint8_t checksum = drive->volume ^ track_idx ^ sector_idx;
|
||||
*dst++ = 0xd5;
|
||||
*dst++ = 0xaa;
|
||||
*dst++ = 0x96;
|
||||
*dst++ = dsk_fourxfour_hi(drive->volume);
|
||||
*dst++ = dsk_fourxfour_lo(drive->volume);
|
||||
*dst++ = dsk_fourxfour_hi(track_idx);
|
||||
*dst++ = dsk_fourxfour_lo(track_idx);
|
||||
*dst++ = dsk_fourxfour_hi(sector_idx);
|
||||
*dst++ = dsk_fourxfour_lo(sector_idx);
|
||||
*dst++ = dsk_fourxfour_hi(checksum);
|
||||
*dst++ = dsk_fourxfour_lo(checksum);
|
||||
*dst++ = 0xde;
|
||||
*dst++ = 0xaa;
|
||||
*dst++ = 0xeb;
|
||||
|
||||
// Gap 2
|
||||
for (int i = 0; i < 5; i++) {
|
||||
*dst++ = 0xff;
|
||||
}
|
||||
|
||||
// Data Field
|
||||
*dst++ = 0xd5;
|
||||
*dst++ = 0xaa;
|
||||
*dst++ = 0xad;
|
||||
|
||||
uint8_t nibbles[0x156];
|
||||
uint8_t ptr2 = 0;
|
||||
uint8_t ptr6 = 0x56;
|
||||
|
||||
for (int i = 0; i < 0x156; i++) {
|
||||
nibbles[i] = 0;
|
||||
}
|
||||
|
||||
int idx2 = 0x55;
|
||||
for (int idx6 = 0x101; idx6 >= 0; idx6--) {
|
||||
uint8_t val6 = src[idx6 % 0x100]; // TODO % 0x100 makes no sense on an uint8_t
|
||||
uint8_t val2 = nibbles[ptr2 + idx2];
|
||||
|
||||
val2 = (val2 << 1) | (val6 & 1);
|
||||
val6 >>= 1;
|
||||
val2 = (val2 << 1) | (val6 & 1);
|
||||
val6 >>= 1;
|
||||
|
||||
nibbles[ptr6 + idx6] = val6;
|
||||
nibbles[ptr2 + idx2] = val2;
|
||||
|
||||
if (--idx2 < 0) {
|
||||
idx2 = 0x55;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t last = 0;
|
||||
for (int i = 0; i < 0x156; i++) {
|
||||
uint8_t val = nibbles[i];
|
||||
*dst++ = dsk_wr_table[last ^ val];
|
||||
last = val;
|
||||
}
|
||||
*dst++ = dsk_wr_table[last];
|
||||
|
||||
*dst++ = 0xde;
|
||||
*dst++ = 0xaa;
|
||||
*dst++ = 0xeb;
|
||||
|
||||
// Gap 3
|
||||
*dst++ = 0xff;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
static struct ewm_dsk_track_t dsk_convert_track(struct ewm_dsk_t *disk, struct ewm_dsk_drive_t *drive, uint8_t *data, int track_idx) {
|
||||
struct ewm_dsk_track_t track;
|
||||
track.length = dsk_native_track_length(track_idx);
|
||||
track.data = malloc(track.length);
|
||||
|
||||
uint8_t *dst = track.data;
|
||||
for (int sector_idx = 0; sector_idx < EWM_DSK_SECTORS; sector_idx++) {
|
||||
int _s = 15 - sector_idx;
|
||||
uint8_t *src = data
|
||||
+ (track_idx * EWM_DSK_SECTORS * EWM_DSK_SECTOR_SIZE) // Start of track_idx
|
||||
+ (_s * EWM_DSK_SECTOR_SIZE); // Start of sector_idx
|
||||
dst = dsk_convert_sector(disk, drive, track_idx, dsk_sector_ordering[_s], src, dst);
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
// Disk file parsing
|
||||
|
||||
// Public
|
||||
|
||||
int ewm_dsk_init(struct ewm_dsk_t *dsk, struct cpu_t *cpu) {
|
||||
memset(dsk, 0x00, sizeof(struct ewm_dsk_t));
|
||||
dsk->rom = cpu_add_rom_data(cpu, 0xc600, 0xc6ff, dsk_rom);
|
||||
dsk->iom = cpu_add_iom(cpu, 0xc0e0, 0xc0ef, dsk, dsk_read, dsk_write);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ewm_dsk_t *ewm_dsk_create(struct cpu_t *cpu) {
|
||||
struct ewm_dsk_t *dsk = (struct ewm_dsk_t*) malloc(sizeof(struct ewm_dsk_t));
|
||||
ewm_dsk_init(dsk, cpu);
|
||||
return dsk;
|
||||
}
|
||||
|
||||
int ewm_dsk_set_disk_data(struct ewm_dsk_t *dsk, uint8_t index, bool readonly, void *data, size_t length) {
|
||||
assert(index < 2);
|
||||
assert(length == (EWM_DSK_TRACKS * EWM_DSK_SECTORS * EWM_DSK_SECTOR_SIZE));
|
||||
|
||||
struct ewm_dsk_drive_t *drive = &dsk->drives[index];
|
||||
|
||||
for (int t = 0; t < EWM_DSK_TRACKS; t++) {
|
||||
if (drive->tracks[t].data != NULL) {
|
||||
free(drive->tracks[t].data);
|
||||
drive->tracks[t].data = NULL;
|
||||
drive->tracks[t].length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
drive->loaded = true;
|
||||
drive->volume = 254; // TODO Find Volume from disk image. Or does this not matter? I guess this gets lost in .dsk files.
|
||||
drive->track = 0;
|
||||
drive->head = 0;
|
||||
drive->phase = 0;
|
||||
drive->readonly = readonly;
|
||||
drive->dirty = false;
|
||||
|
||||
for (int t = 0; t < EWM_DSK_TRACKS; t++) {
|
||||
drive->tracks[t] = dsk_convert_track(dsk, drive, data, t);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ewm_dsk_set_disk_file(struct ewm_dsk_t *dsk, uint8_t drive, bool readonly, char *path) {
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct stat file_info;
|
||||
if (fstat(fd, &file_info) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file_info.st_size != (EWM_DSK_TRACKS * EWM_DSK_SECTORS * 256)) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *data = calloc(file_info.st_size, 1);
|
||||
if (read(fd, data, file_info.st_size) != file_info.st_size) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
int result = ewm_dsk_set_disk_data(dsk, drive, readonly, data, file_info.st_size);
|
||||
free(data);
|
||||
|
||||
return result;
|
||||
}
|
70
dsk.h
Normal file
70
dsk.h
Normal file
@ -0,0 +1,70 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef EWM_DSK_H
|
||||
#define EWM_DSK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct cpu_t;
|
||||
struct mem_t;
|
||||
|
||||
#define EWM_DSK_DRIVE1 0
|
||||
#define EWM_DSK_DRIVE2 1
|
||||
|
||||
#define EWM_DSK_TRACKS 35
|
||||
#define EWM_DSK_SECTORS 16
|
||||
#define EWM_DSK_SECTOR_SIZE 256
|
||||
|
||||
struct ewm_dsk_track_t {
|
||||
size_t length;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct ewm_dsk_drive_t {
|
||||
bool loaded;
|
||||
uint8_t volume;
|
||||
int track, head, phase;
|
||||
bool readonly;
|
||||
bool dirty;
|
||||
struct ewm_dsk_track_t tracks[EWM_DSK_TRACKS];
|
||||
};
|
||||
|
||||
struct ewm_dsk_t {
|
||||
struct mem_t *rom;
|
||||
struct mem_t *iom;
|
||||
bool on;
|
||||
int active_drive;
|
||||
int mode;
|
||||
uint8_t latch;
|
||||
struct ewm_dsk_drive_t drives[2];
|
||||
uint8_t drive; // 0 based
|
||||
int skip;
|
||||
};
|
||||
|
||||
int ewm_dsk_init(struct ewm_dsk_t *dsk, struct cpu_t *cpu);
|
||||
struct ewm_dsk_t *ewm_dsk_create(struct cpu_t *cpu);
|
||||
int ewm_dsk_set_disk_data(struct ewm_dsk_t *dsk, uint8_t index, bool readonly, void *data, size_t length);
|
||||
int ewm_dsk_set_disk_file(struct ewm_dsk_t *dsk, uint8_t index, bool readonly, char *path);
|
||||
|
||||
#endif
|
31
ewm.c
31
ewm.c
@ -33,6 +33,7 @@
|
||||
|
||||
#include "a2p.h"
|
||||
#include "scr.h"
|
||||
#include "dsk.h"
|
||||
|
||||
// Apple 1 / 6502 / 8K RAM / WOZ Monitor
|
||||
|
||||
@ -130,6 +131,8 @@ static struct option options[] = {
|
||||
{ "strict", no_argument, NULL, 's' },
|
||||
{ "trace", optional_argument, NULL, 't' },
|
||||
{ "memory", required_argument, NULL, 'x' },
|
||||
{ "drive1", required_argument, NULL, 'a' },
|
||||
{ "drive2", required_argument, NULL, 'b' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
@ -147,6 +150,8 @@ int main(int argc, char **argv) {
|
||||
bool strict = false;
|
||||
char *trace_path = NULL;
|
||||
struct ewm_memory_t *memory;
|
||||
char *drive1 = NULL;
|
||||
char *drive2 = NULL;
|
||||
|
||||
char ch;
|
||||
while ((ch = getopt_long(argc, argv, "m:", options, NULL)) != -1) {
|
||||
@ -172,6 +177,12 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'a':
|
||||
drive1 = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
drive2 = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,6 +202,26 @@ int main(int argc, char **argv) {
|
||||
|
||||
machine->setup(&cpu);
|
||||
|
||||
// TODO This really does not belong here. it is probably better to
|
||||
// pass arguments to setup_apple2plus so that it can handle its
|
||||
// specific arguments like --drive1/drive2 and probably some more
|
||||
// in the future.
|
||||
|
||||
if (a2p != NULL) {
|
||||
if (drive1 != NULL) {
|
||||
if (a2p_load_disk(a2p, EWM_DSK_DRIVE1, drive1) != 0) {
|
||||
fprintf(stderr, "[A2P] Cannot load Drive 1 with %s\n", drive1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (drive2 != NULL) {
|
||||
if (a2p_load_disk(a2p, EWM_DSK_DRIVE2, drive2) != 0) {
|
||||
fprintf(stderr, "[A2P] Cannot load Drive 2 with %s\n", drive2);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ewm_memory_t *m = memory;
|
||||
while (m != NULL) {
|
||||
fprintf(stderr, "[EWM] Adding %s $%.4X %s\n", m->type == EWM_MEMORY_TYPE_RAM ? "RAM" : "ROM", m->address, m->path);
|
||||
|
Loading…
x
Reference in New Issue
Block a user