OpenCores
URL https://opencores.org/ocsvn/s80186/s80186/trunk

Subversion Repositories s80186

[/] [s80186/] [trunk/] [bios/] [disk.c] - Rev 2

Compare with Previous | Blame | View Log

// Copyright Jamie Iles, 2017
//
// This file is part of s80x86.
//
// s80x86 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// s80x86 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with s80x86.  If not, see <http://www.gnu.org/licenses/>.
 
#include "bios.h"
#include "sd.h"
#include "io.h"
#include "leds.h"
#include "serial.h"
#include "utils.h"
 
#define SECTORS_PER_TRACK 63
#define NUM_HEADS 16
#define NUM_CYLINDERS 4
#define SECTOR_SIZE 512
 
#define FLOPPY_TIMEOUT 0x80
 
static void set_disk_status(struct callregs *regs, unsigned char err)
{
    if (err)
        regs->flags |= CF;
    else
        regs->flags &= ~CF;
    bda_write(diskette_status, err);
    regs->ax.h = err;
}
 
static void disk_read(struct callregs *regs)
{
    if (regs->dx.l != 0x80) {
        set_disk_status(regs, FLOPPY_TIMEOUT);
        return;
    }
 
    unsigned short cylinder =
        regs->cx.h | (((unsigned short)regs->cx.l & 0xc0) << 2);
    unsigned short head = regs->dx.h;
    unsigned short sector = regs->cx.l & 0x3f;
    unsigned short lba =
        (cylinder * NUM_HEADS + head) * SECTORS_PER_TRACK + (sector - 1);
    unsigned short i;
    unsigned short dst = regs->bx.x;
    unsigned short count = regs->ax.l;
 
    regs->ax.l = 0;
    regs->flags &= ~CF;
 
    for (i = 0; i < count; ++i) {
        read_sector(lba, get_es(), dst);
        ++lba;
        dst += SECTOR_SIZE;
        ++regs->ax.l;
    }
 
    set_disk_status(regs, regs->ax.l != count ? 0xff : 0x00);
}
 
static void disk_write(struct callregs *regs)
{
    if (regs->dx.l != 0x80) {
        set_disk_status(regs, FLOPPY_TIMEOUT);
        return;
    }
 
    unsigned short cylinder =
        regs->cx.h | (((unsigned short)regs->cx.l & 0xc0) << 2);
    unsigned short head = regs->dx.h;
    unsigned short sector = regs->cx.l & 0x3f;
    unsigned short lba =
        (cylinder * NUM_HEADS + head) * SECTORS_PER_TRACK + (sector - 1);
    unsigned short i;
    unsigned short dst = regs->bx.x;
    unsigned short count = regs->ax.l;
 
    regs->ax.l = 0;
    regs->flags &= ~CF;
 
    for (i = 0; i < count; ++i) {
        if (write_sector(lba, get_es(), dst)) {
            regs->flags |= CF;
            break;
        }
        ++lba;
        dst += SECTOR_SIZE;
        ++regs->ax.l;
    }
 
    set_disk_status(regs, regs->ax.l != count ? 0xff : 0x00);
}
 
static void disk_status(struct callregs *regs)
{
    if (regs->dx.l != 0x80) {
        set_disk_status(regs, FLOPPY_TIMEOUT);
        return;
    }
 
    unsigned char v = bda_read(diskette_status);
    bda_write(diskette_status, v);
}
 
static void disk_reset(struct callregs *regs)
{
    regs->flags &= ~CF;
    if (regs->dx.l != 0x80) {
        set_disk_status(regs, FLOPPY_TIMEOUT);
        return;
    }
    set_disk_status(regs, 0);
}
 
static void disk_parameters(struct callregs *regs)
{
    regs->flags &= ~CF;
    if (regs->dx.l != 0x80) {
        regs->flags |= CF;
        set_disk_status(regs, FLOPPY_TIMEOUT);
        return;
    }
 
    regs->dx.l = 1; // 1 drive
    regs->dx.h = NUM_HEADS - 1;
    regs->cx.x = SECTORS_PER_TRACK | (NUM_CYLINDERS << 8);
    regs->bx.l = 0;
 
    set_disk_status(regs, 0);
}
 
static void disk_get_type(struct callregs *regs)
{
    regs->flags &= ~CF;
    if (regs->dx.l != 0x80) {
        set_disk_status(regs, FLOPPY_TIMEOUT);
        return;
    }
 
    regs->ax.h = 3;
    regs->cx.x = regs->dx.x = 0xffff;
}
 
static void disk_services(struct callregs *regs)
{
    regs->flags |= CF;
 
    led_set(LED_DISK);
    switch (regs->ax.h) {
    case 0x00: disk_reset(regs); break;
    case 0x01: disk_status(regs); break;
    case 0x02: disk_read(regs); break;
    case 0x03: disk_write(regs); break;
    case 0x08: disk_parameters(regs); break;
    case 0x15: disk_get_type(regs); break;
    }
    led_clear(LED_DISK);
}
VECTOR(0x13, disk_services);
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.