Skip to content

Instantly share code, notes, and snippets.

@Konamiman
Last active August 5, 2020 20:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Konamiman/d9231c9f1e9f00a300c4e166479695ae to your computer and use it in GitHub Desktop.
Save Konamiman/d9231c9f1e9f00a300c4e166479695ae to your computer and use it in GitHub Desktop.
Nextor v3 driver structure proposal

Nextor v3 driver migration guide

This guide explains how to migrate a Nextor driver from v2 to v3. The reader is expected to have experience with Nextor v2 driver development.

Only the changes relative to the structure of the driver for v2 are mentioned here. You can see the skeleton for a full v3 driver in the Nextor-v3-driver.asm in this same gist, and the skeleton for a full v2 driver in the Nextor repository.

1. Change the hardcoded flags (DRV_FLAGS, 410Eh)

The driver flags byte needs to be changed to the fixed value 83h. Bit 7 indicates that the driver is a v3 driver, bits 0 and 1 need to be set for consistency with the meaning they had in Nextor v2.

2. Change the hardcoded driver name (DRV_NAME, 4110h)

This field is not used anymore in Nextor v3 (the DRV_QUERY routine is used instead to get driver information). It should have the fixed value "Nextor v3 driver", padded at the right with spaces up to 32 characters.

3. Change the old driver version routine (DRV_VERSION, 4133h)

This routine is not used anymore in Nextor v3 (the DRV_QUERY routine is used instead to get driver information). It should be changed to return A=0 and BC=0.

4. Adjust the initialization routine (DRV_INIT, 4136h) if necessary

This routine works the same as in v2, with two minor changes:

  1. Since drive-based drivers are not supported anymore, the routine will never return the number of required drives in its first execution (or if it does, Nextor will ignore it).
  2. The second execution will occur even when there's not enough space in page 3 to allocate the requested amount of work area, but a flag will be passed (bit 7 of register C) indicating so.

5. Adjust the routine to read and write sectors (DEV_RW, 4160h) if necessary

This routine works as in v2, but since there is no concept of LUN anymore, Nextor will not pass any meaningful LUN number in register C; instead, it will always pass the value 1, which is the value expected by v2 drivers that don't implement the concept of LUNs (or put another way, implement one single LUN).

If your driver doesn't use LUNs, you don't need to change anything in this routine. Otherwise you need to come up with some mechanism to map devices+LUNs to single device numbers. For example you could support only device numbers 1-15 (that's more than the 7 devices that Nextor v2 supported anyway) and map higher values to LUNs, e.g. you can use something like "device+16*LUN".

6. Replace DEV_INFO (4163h), DEV_STATUS (4166h), LUN_INFO (4169h) with error stubs

These routines are not used anymore in Nextor v3, for compatibility with tools that might still call them they should be kept but just returning an error. DEV_INFO and LUN_INFO should return A=1, DEV_STATUS should return A=0.

The functionality of these routines has actually been moved to the DEV_QUERY routine, and you will probably be able to reuse most of their code, as we'll see later in this document.

7. Implement the DRV_QUERY routine (4151h)

This routine existed already since Nextor 2.0.5 (with the name DRV_CONFIG and only with queries 1 and 2) but it was optional; it will be mandatory in Nextor v3.

Note however that although the routine itself is mandatory, all the queries are optional, so you can just return an error for all of them (although it's recommended to implement at least the queries to return driver names and versions).

7.1 "Get number of drives at boot time" query

This query already existed in v2 and is identical in v3, so if you had already implemented it you don't need to change anything.

7.2 "Get default configuration for drive" query

This query already existed in v2 and is identical in v3, except that it doesn't return a LUN number in register C anymore so if you had already implemented it and your driver doesn't support LUNs you don't need to change anything; otherwise adjust as needed, see the explanation about DEV_RW for a suggestion for remapping LUNs to devices.

7.3 "Get driver capabilities" query

For now, you need to implement this query only if your driver supports hot-plug devices.

A driver is said to support hot-plug devices if after the system boots new devices can begin to exist and existing devices can cease to exist, or be replaced with another devices. This is the case of a USB bus, for example.

Note that a hot-plug device changing is not the same as a medium change in a removable device, e.g. a SD card reader. See the descriprion of the "Get device or medium change status" query for the DEV_QUERY routine in Nextor-v3-driver.asm for more details on this.

7.4 "Get driver identification information" query

This query replaces the hardcoded driver name defined in v2 drivers (DRV_NAME), and provides more options for returning information. While all the subqueries (information ids) are optional to implement, it's recommended to supply at least either the hardware name (for drivers that are specific to a given controller) or the driver name (for drivers made for generic hardware, or that don't depend on any hardware at all). Nextor doesn't require this information for normal operation.

Nextor will always call this query with a buffer size of at least 64 bytes, but the routine must be prepared to receive a smaller buffer size and return a truncated string if necessary. Note also that there's a special error code to indicate that the output is truncated.

7.5 "Get driver version number" query

This query replaces the DRV_VERSION routine in v2 drivers (and extends version number components to 16 bit values). While it's optional to implement, it's recommended to at least supply the driver version number. Nextor doesn't require this information for normal operation.

8. Implement the DEV_QUERY routine (4154h)

This routine is new in Nextor v3 drivers, and replaces the DEV_INFO, DEV_STATUS and LUN_INFO routines defined in v2, while providing also new capabilities.

8.1. "Get basic device information" query

This query replaces the "Medium type" and "Logical unit features flags" fields in the block of information returned by LUN_INFO from v2. So here's how you could do the conversion from the old routine to the new query:

  1. Adjust input parameters, from A=device id and B=LUN id to C=device id.
  2. Return the old "Logical unit features flags" in register C. Set B, DE, HL to zero.

Note that this query defines two new flags: bit 4 (device is hot-pluggable) and bit 5 (device is NOT a block device), the later is the replacement for the "Medium type" field returned by LUN_INFO.

8.2 "Get device or medium change status" query

This query replaces the DEV_STATUS routine from v2. However, other than for the input parameters (same as previous query), there are two important differences:

  1. Bit 7 in the returned status now indicates if the device itself has changed. This applies to hot-plug devices only, and replaces the "return the status of the device itself" input parameter in DEV_STATUS.
  2. DEV_STATUS returned a status of 0 in two cases: when the device doesn't exist, and when the device is removable but there is no medium inserted. This query on the other hand has two different outputs for these two cases: device not existing returns an error (A=1), device existing but medium not inserted returns a status of 0 (A=0, B=0).

See the descriprion of this query for the DEV_QUERY routine in Nextor-v3-driver.asm for more details on hot-plug devices vs removable devices.

8.3 "Get device or medium identification" query

This query replaces the DEV_INFO routine while providing additional types of information to return. Note that while in DEV_INFO the supplied buffer to copy information was defined as having a fixed size of 64 bytes, in this query the buffer size is passed along with the address of the buffer.

Nextor will always call this query with a buffer size of at least 64 bytes, but the routine must be prepared to receive a smaller buffer size and return a truncated string if necessary. Note also that there's a special error code to indicate that the output is truncated.

8.4 "Get device size" query

This query replaces the "Total number of available sectors" field in the block of information returned by LUN_INFO from v2. The difference is that if this information is not available LUN_INFO returned a value of 0, while this query returns an error instead.

Nextor doesn't require this information for normal operation, but devices not providing this information won't be handled by the built-in FDISK tool.

8.5 "Get device geometry" query

This query replaces the "Sector size", "Number of cylinders", "Number of heads" and "Number of sectors per track" fields in the block of information returned by LUN_INFO from v2.

As it was the case in v2, the last three are applicable only to physical hard disks and are useful only to disk partitioning tools (but Nextor's built-in FDISK doesn't use them). Regarding the sector size, Nextor will assume 512 bytes if this query is not implemented; so in most cases it won't be necessary to implement it.

8.6 "Get format choice string" and "Format a floppy disk" queries

Nextor will only call these queries in response to a FORMAT/CALL FORMAT command and only for devices that return the "it's a floppy disk drive" flag set in the "Get basic device information" query. Implementing these is optional, but of course not implementing them means that Nextor won't be able to format floppy disks in drives controlled by that driver.

; Proposal for a new driver structure for Nextor v3
; By Konamiman, 8/2020
;
; Based on the driver template for Nextor v2.
; For simplicity all the constants have been removed.
;
; The final structure migh have some small differences.
;
;org 4100h
DRV_START:
;-----------------------------------------------------------------------------
;
; Driver signature
db "NEXTOR_DRIVER",0
;-----------------------------------------------------------------------------
;
; Driver flags, this byte needs to be hardcoded to 83h:
; bit 0: set, meaning device-based driver
; (the only type of driver supported in Nextor v3)
; bit 1: set, meaning that the driver implements the DRV_QUERY routine
; (it was optional, and named DRV_CONFIG, in Nextor v2)
; bit 7: set, meaning it's a Nextor v3 driver
db 83h
;-----------------------------------------------------------------------------
;
; Reserved byte, must be zero
db 0
;-----------------------------------------------------------------------------
;
; Driver name in Nextor v2, DEPRECATED in Nextor v3, don't modify
; (use DRV_QUERY instead)
DRV_NAME:
db "Nextor v3 driver"
ds 32-($-DRV_NAME)," "
;-----------------------------------------------------------------------------
;
; Jump table for the driver public routines
jp DRV_TIMI
jp DRV_VERSION
jp DRV_INIT
jp DRV_BASSTAT
jp DRV_BASDEV
jp DRV_EXTBIO
jp DRV_DIRECT0
jp DRV_DIRECT1
jp DRV_DIRECT2
jp DRV_DIRECT3
jp DRV_DIRECT4
jp DRV_QUERY
jp DEV_QUERY
ds 9
jp DEV_RW
ld a,1 ;Was DEV_INFO in Nextor v2, DEPRECATED in Nextor v3
ret
ld a,0 ;Was DEV_STATUS in Nextor v2, DEPRECATED in Nextor v3
ret
ld a,1 ;Was LUN_INFO in Nextor v2, DEPRECATED in Nextor v3
ret
;=====
;===== END of data that must be at fixed addresses
;=====
;-----------------------------------------------------------------------------
;
; Timer interrupt routine, it will be called on each timer interrupt
; (at 50 or 60Hz), but only if DRV_INIT returns Cy=1 on its first execution
; and enough work area was allocated for the driver.
DRV_TIMI:
ret
;-----------------------------------------------------------------------------
;
; Driver initialization routine, it is called twice:
;
; 1) First execution, for information gathering.
; Input:
; A = 0
; B = number of available drives
; HL = maximum size of allocatable work area in page 3
; C: bit 5 set if user is requesting reduced drive count
; (by pressing the 5 key)
; other bits: 0
; DE = 0 (could have information in future versions)
; Output:
; HL = size of required work area in page 3
; Cy = 1 if DRV_TIMI must be hooked to the timer interrupt, 0 otherwise
;
; 2) Second execution, for work area and hardware initialization.
; Input:
; A = 1
; B = number of allocated drives for this controller
; C: bit 5 set if user is requesting reduced drive count
; (by pressing the 5 key)
; bit 7 set on error (not enough page 3 work area to allocate)
; other bits: 0
; DE, HL = 0 (could have information in future versions)
;
; The work area address can be obtained by using GWORK.
;
; If first execution requests more work area than available,
; second execution will be done with with bit 7 of C set,
; and DRV_TIMI will not be hooked to the timer interrupt.
DRV_INIT:
xor a
ld hl,0
ret
;-----------------------------------------------------------------------------
;
; Obtain driver version
;
; DEPRECATED in Nextor v3 (DRV_QUERY is used instead),
; should return v0.0.0
;
; Input: -
; Output: A = Main version number
; B = Secondary version number
; C = Revision number
DRV_VERSION:
ld a,0
ld bc,0
ret
;-----------------------------------------------------------------------------
;
; BASIC expanded statement ("CALL") handler.
; Works the expected way, except that if invoking CALBAS is needed,
; it must be done via the CALLB0 routine in kernel page 0.
DRV_BASSTAT:
scf
ret
;-----------------------------------------------------------------------------
;
; BASIC expanded device handler.
; Works the expected way, except that if invoking CALBAS is needed,
; it must be done via the CALLB0 routine in kernel page 0.
DRV_BASDEV:
scf
ret
;-----------------------------------------------------------------------------
;
; Extended BIOS hook.
; Works the expected way, except that it must return
; D'=1 if the old hook must be called, D'=0 otherwise.
; It is entered with D'=1.
DRV_EXTBIO:
ret
;-----------------------------------------------------------------------------
;
; Direct calls entry points.
; Calls to addresses 7850h, 7853h, 7856h, 7859h and 785Ch
; in kernel banks 0 and 3 will be redirected
; to DIRECT0/1/2/3/4 respectively.
; Receives all register data from the caller except IX and AF'.
DRV_DIRECT0:
DRV_DIRECT1:
DRV_DIRECT2:
DRV_DIRECT3:
DRV_DIRECT4:
ret
;-----------------------------------------------------------------------------
;
; Driver query: get information about the driver itself
;
; For all queries:
; Input:
; A = Query index
; BC, DE, HL = Depends on the query
; Output:
; A = 0: Ok
; 1: Query or subquery not implemented
; BC, DE, HL = Depends on the query
;
; The routine must be prepared to receive unknown query
; (and subquery in register B, if applicable) indexes and return A=1,
; to allow for the extension of driver queries in future versions.
;
; ----------
;
; * Get number of drives at boot time
; Input:
; A = 1
; B = 0 for DOS 2 mode, 1 for DOS 1 mode
; C: bit 5 set if user is requesting reduced drive count
; (by pressing the 5 key)
; Output:
; B = number of drives
;
; ----------
;
; * Get default configuration for drive
; Input:
; A = 2
; B = 0 for DOS 2 mode, 1 for DOS 1 mode
; C = Relative drive number at boot time
; Output:
; B = Device index
;
; ----------
;
; * Get driver capabilities
; Input:
; A = 3
; B = Index of capabilities to get:
; 1: Basic
; Output when B=1:
; C = Flags:
; bit 0: set if the driver supports hot-plug devices
; bits 1-7: always zero
; B = 0 (could return further information in future versions)
;
; "Not implemented" error must be returned if the supplied
; capabilities index is not recognized or if the function
; is not implemented (flags = 0 is assumed in that case).
;
; "Supports hot-plug devices" means that new devices could exist
; after boot, and existing devices could disappear (e.g. USB)
;
; ----------
;
; * Get driver identification information
; Input: A = 4
; HL = Pointer to buffer in RAM
; DE = Buffer size in bytes
; (will always be at least 64 when called from Nextor kernel)
; B = Information id:
; 1: Hardware name
; 2: Hardware manufacturer name
; 3: Hardware designer name
; 4: Hardware serial number
; 5: Driver name
; 6: Driver developer name
; 255: Additional information
; Output: A = 0: Ok
; 1: Information not available
; 80h: Ok, but the output is truncated (buffer too small)
;
; The driver may choose to implement only some of the information ids,
; or to not implement any; but at least one of either “hardware name”
; or “driver name” should be provided.
;
; ----------
;
; * Get driver version number
; Input: A = 5
; B = What to get the version for:
; 0: Hardware
; 1: Driver
; Output: A = 0: Ok
; 1: Unknown value of B or no version number available
; HL = Main version number
; DE = Secondary version number
; BC = Revision number
;
; Both versions are optional but at least the driver version number
; should be provided.
;
; ----------
;
; * Get maximum supported device number
; Input: A = 6
; Output: B = Maximum supported device number
;
; Devices don't need to exist consecutively. E.g. maximum could be 5
; but only devices 2 and 4 actually exist.
;
; If not implemented, Nextor assumes a value of 7
; (the maximum legal device number in Nextor v2)
;
; If the driver handles no devices at all, 0 should be returned.
DRV_QUERY:
ld a,1
ret
;-----------------------------------------------------------------------------
;
; Device query: get information or perform an action on a device
;
; For all queries:
; Input: A = Query index
; B = Subquery index (for some queries)
; C = Device number
; HL, DE = Depends on the query
; Output: A = 0: Ok
; 1: Query or subquery not implemented
; (or device doesn’t exist, for queries after 2)
; 80h: Ok, but output is truncated
; (for queries that return information but there's
; not enough buffer space)
;
; The routine must be prepared to receive unknown query
; (and subquery in register B, if applicable) indexes and return A=1,
; to allow for the extension of device queries in future versions.
;
; ----------
;
; * Get basic device information
; Input: A = 1
; C = Device number
; Output: A = 0: Ok, 1 if device doesn’t exist
; C = Flags:
; bit 0: set if removable device
; bit 1: set if read-only device
; bit 2: set if it's a floppy disk drive
; bit 3: set if shouldn't be used for automapping
; bit 4: set if hot-pluggable device (it can cease to exist)
; bit 5: set if NOT a block device
; bits 5-7: always zero
; B, DE, HL: always zero
;
; This is the query that Nextor will use to decide if a device exists or not.
;
; ----------
;
; * Get device or medium change status
; Input: A = 2
; C = Device number
; Output: A = 0 if ok, 1 if device doesn’t exist
; B = Bits 0-1: Status of the medium:
; 0: The device exists but no medium is inserted
; (for removable devices only)
; 1: The medium has not changed since the last status request
; (non-removable devices should always return this)
; 2: The medium has changed since the last status request
; 3: The medium is available, but it is not
; possible to determine whether it has been changed
; or not since the last status request.
; B:2-6 = Always zero
; B:7 = Set if the device itself has changed (if it's hot-pluggable)
;
; * Non-hot-plug, non-removable (e.g. IDE hard disk):
; Nextor will never call this routine for such a device, but the routine
; must be prepared to receive such a call and return B=1.
; * Non-hot-plug, removable (e.g. SD card reader built-in in cartridge):
; Return B:7 always zero, B:0-1 as appropriate.
; * Hot-plug, non-removable (e.g. USB pendrive):
; Return B:7 as appropriate, B:0-1 = 1.
; * Hot-plug, removable (e.g. USB SD card reader):
; If device changed:
; Return B:7 = 1, B:0-1 = 2 (if medium present) or 0 (if not)
; If device unchanged:
; Return B:7 = 0, B:0-1 as appropriate.
;
; ----------
;
; * Get device or medium identification
; Input: A = 2
; C = Device number
; B = Identification information to return:
; 1: Device name string
; 2: Device manufacturer name string
; 3: Device serial number string
; 4: Medium name string
; 5: Medium manufacturer name string
; 6: Medium serial number string
; 255: Additional information
; HL = Pointer to a buffer in RAM
; DE = Size of buffer (at least 64 when called from Nextor kernel)
; Output: A = 0: Ok
; 1: Device doesn't exist, or information not available
; 80h: Ok, but output is truncated (not enough buffer space)
;
; All the information is optional and not used in normal Nextor operation,
; only in FDISK and CALL commands or tools that list devices.
;
; ----------
;
; * Get device size
; Input: A = 3
; C = Device number
; Output: A = 0: Ok
; 1: Device doesn't exist, or information not available
; HLDE = Device size in sectors
;
; If this information is not returned Nextor will be able to use the device
; normally, but FDISK won't work for the device.
;
; ----------
;
; * Get device geometry
; Input: A = 4
; C = Device number
; Output: A = 0: Ok
; 1: Device doesn't exist, or information not available
; HL = Cylinders (0 if not available)
; D = Heads (0 if not available)
; E = Sectors per track (0 if not available)
; BC = Sector size (0 if not available)
;
; This information is intended for FDISK-type tools, but Nextor's built-in
; FDISK doesn't use it. The Nextor kernel uses this query only to check
; if the sector size is 512 bytes.
;
; In most cases it's safe to leave this query unimplemented, Nextor will
; assume a sector size of 512 bytes when that happens.
;
; ----------
;
; * Get format choice string
; Input: A = 5
; C = Device number
; HL = Buffer address
; DE = Buffer size (at least 64 when called from Nextor kernel)
; Output: A = 0: Ok
; 1: Device doesn't exist, or is not formattable
; 80h: Ok, but output is truncated (not enough buffer space)
;
; Nextor will use this query only for devices with the "floppy" flag set.
;
; If there's only one choicea single 0 byte should be written in the buffer.
;
; ----------
;
; * Format a floppy disk
; Input: A = 6
; B = Format choice, 1 to 9
; C = Device number
; Output: A = 0: Ok
; 1: Device doesn't exist, or is not formattable,
; or bad choice string
;
; Nextor will use this query only for devices with the "floppy" flag set,
; and with one of the choices listed by "Get format choice string".
DEV_QUERY:
ld a,1
ret
;-----------------------------------------------------------------------------
;
; Read or write logical sectors from/to a logical unit
;
;Input: Cy=0 to read, 1 to write
; A = Device number, 1 to 255
; B = Number of sectors to read or write
; C = 1 (was logical unit number in Nextor v2)
; HL = Source or destination memory address for the transfer
; DE = Address where the 4 byte sector number is stored.
;Output: A = Error code (the same codes of MSX-DOS are used):
; 0: Ok
; .IDEVL: Invalid device or LUN
; .NRDY: Not ready
; .DISK: General unknown disk error
; .DATA: CRC error when reading
; .RNF: Sector not found
; .UFORM: Unformatted disk
; .WPROT: Write protected media, or read-only logical unit
; .WRERR: Write error
; .NCOMP: Incompatible disk.
; .SEEK: Seek error.
B = Number of sectors actually read (in case of error only)
DEV_RW:
ld a,.NRDY
ld b,0
ret
;-----------------------------------------------------------------------------
;
; End of the driver code
DRV_END:
;ds 3FD0h-(DRV_END-DRV_START)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment