Class: Zemu::Config::BlockDrive

Inherits:
BusDevice show all
Defined in:
lib/zemu/config.rb

Overview

Block drive object

Represents a device with a sequence of sectors of a fixed size, which can be accessed via IO instructions as an IDE drive.

Constant Summary collapse

DRIVE_MODE_READ =

Mode for reading drive.

0x01
DRIVE_MODE_WRITE =

Mode for writing drive.

0x02
DRIVE_MODE_UNINIT =

Uninitialised drive mode.

0x00

Instance Method Summary collapse

Methods inherited from BusDevice

#clock, #interrupt, #interrupt?, #mem_read, #mem_write, #memory, #nmi, #nmi?, #when_setup

Methods inherited from Zemu::ConfigObject

#method_missing

Constructor Details

#initializeBlockDrive

Constructor.

Takes a block in which the parameters of the block drive can be initialized.

All parameters can be set within this block. They become readonly as soon as the block completes.

Constructor raises RangeError if a file is provided for initialization and it is of the wrong size.

Examples:


Zemu::Config::BlockDrive.new do
    name "drive"
    base_port 0x0c
    sector_size 512
    num_sectors 64
end


490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# File 'lib/zemu/config.rb', line 490

def initialize
    @initialize_from = nil

    super
    
    # Initialize from provided file if applicable.
    unless @initialize_from.nil?
        # Check file size.
        file_size = File.size(@initialize_from)
        if (file_size != num_sectors * sector_size)
            raise RangeError, "Initialization file for Zemu::Config::BlockDrive '#{name}' is of wrong size."
        end
    end

    @lba_0 = 0
    @lba_1 = 0
    @lba_2 = 0
    @lba_3 = 0

    @drive_mode = DRIVE_MODE_UNINIT
    @drive_status = 0b01000000
    @sector_offset = 0
    @sector_data = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Zemu::ConfigObject

Instance Method Details

#blocksObject

Array of sectors of this drive.



612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# File 'lib/zemu/config.rb', line 612

def blocks
    b = []
    
    if @initialize_from.nil?
        num_sectors.times do
            this_block = []
            sector_size.times do
                this_block << 0
            end
            b << this_block
        end
        return b
    end
    
    File.open(@initialize_from, "rb") do |f|
        num_sectors.times do
            this_block = f.read(sector_size)
            b << this_block.unpack("C" * sector_size)
        end
    end

    b
end

#functionsObject

Defines FFI API which will be available to the instance wrapper if this IO device is used.



657
658
659
660
# File 'lib/zemu/config.rb', line 657

def functions
    [
    ]
end

#get_sectorObject

Get a sector number from the four LBA registers.



540
541
542
543
544
545
546
547
548
# File 'lib/zemu/config.rb', line 540

def get_sector()
    sector = 0
    sector |= @lba_0
    sector |= @lba_1 << 8
    sector |= @lba_2 << 16
    sector |= @lba_3 << 24

    sector
end

#initialize_from(file) ⇒ Object

Set file to initialize from.



637
638
639
# File 'lib/zemu/config.rb', line 637

def initialize_from(file)
    @initialize_from = file
end

#io_read(port) ⇒ Object

IO bus read handler.

Handles read access via the IO bus to this device.

Returns the value read, or nil if the port does not correspond to this device.

Parameters:

  • port

    The IO port being accessed.



523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
# File 'lib/zemu/config.rb', line 523

def io_read(port)
    if port == base_port
        b = @sector_data.shift()

        if @sector_data.empty?
            @drive_status = 0b01000000
        end

        return b
    elsif port == (base_port + 7)
        return @drive_status
    end

    nil
end

#io_write(port, value) ⇒ Object

IO bus write handler.

Handles write access via the IO bus to this device.

Parameters:

  • port

    The IO port being accessed.

  • value

    The value being written.



576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
# File 'lib/zemu/config.rb', line 576

def io_write(port, value)
    if port == base_port
        if @drive_mode == DRIVE_MODE_WRITE
            @sector_data << value
            if @sector_data.size >= sector_size
                write_current_sector()
                @drive_status = 0b01000000
            end
        end
    elsif port == (base_port + 3)
        @lba_0 = (value & 0xff)
    elsif port == (base_port + 4)
        @lba_1 = (value & 0xff)
    elsif port == (base_port + 5)
        @lba_2 = (value & 0xff)
    elsif port == (base_port + 6)
        @lba_3 = (value & 0x1f)
    elsif port == (base_port + 7)
        # Read command.
        if value == 0x20
            load_sector()

            @drive_mode = DRIVE_MODE_READ
            @drive_status = 0b00001000

        # Write command.
        elsif value == 0x30
            @sector_data = []

            @drive_mode = DRIVE_MODE_WRITE
            @drive_status = 0b00001000
        end
    end
end

#load_sectorObject

Load sector data pointed to by LBA registers.



561
562
563
564
565
566
567
568
# File 'lib/zemu/config.rb', line 561

def load_sector()
    file_offset = get_sector() * sector_size
    File.open(@initialize_from, "rb") do |f|
        f.seek(file_offset)
        s = f.read(sector_size)
        @sector_data = s.unpack("C" * sector_size)
    end
end

#paramsObject

Valid parameters for a BlockDrive, along with those defined in [Zemu::Config::BusDevice].



664
665
666
# File 'lib/zemu/config.rb', line 664

def params
    super + %w(base_port sector_size num_sectors)
end

#read_byte(sector, offset) ⇒ Object

Read a byte at the given offset in a sector.

Returns the byte read from the file.

Parameters:

  • sector

    The sector to read from.

  • offset

    Offset in that sector to read.



647
648
649
650
651
652
653
654
# File 'lib/zemu/config.rb', line 647

def read_byte(sector, offset)
    file_offset = (sector * sector_size) + offset
    File.open(@initialize_from, "rb") do |f|
        f.seek(file_offset)
        s = f.read(1)
        return s.unpack("C")[0]
    end
end

#write_current_sectorObject

Write sector data to the sector currently pointed to by the LBA registers.



552
553
554
555
556
557
558
# File 'lib/zemu/config.rb', line 552

def write_current_sector()
    file_offset = get_sector() * sector_size
    File.open(@initialize_from, "r+b") do |f|
        f.seek(file_offset)
        f.write(@sector_data.pack("C" * sector_size))
    end
end