diff --git a/Makefile b/Makefile index 1374f9c..ca94a50 100644 --- a/Makefile +++ b/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 diff --git a/a2p.c b/a2p.c index 06859e0..0af5374 100644 --- a/a2p.c +++ b/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); +} diff --git a/a2p.h b/a2p.h index ed36716..50e663b 100644 --- a/a2p.h +++ b/a2p.h @@ -23,12 +23,16 @@ #ifndef EWM_A2P_H #define EWM_A2P_H +#include + 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 diff --git a/dsk.c b/dsk.c new file mode 100644 index 0000000..f92273d --- /dev/null +++ b/dsk.c @@ -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 +#include +#include +#include +#include +#include + +#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; +} diff --git a/dsk.h b/dsk.h new file mode 100644 index 0000000..5a3d187 --- /dev/null +++ b/dsk.h @@ -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 +#include + +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 diff --git a/ewm.c b/ewm.c index 2378682..6f848b3 100644 --- a/ewm.c +++ b/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);