├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── Ethernet UNAPI specification 1.1.md ├── Introduction to MSX-UNAPI.md ├── MSX UNAPI specification 1.1.md └── TCP-IP UNAPI specification.md ├── examples ├── .gitignore ├── README.md ├── unapi-nextor-id.asm ├── unapi-nextor.asm ├── unapi-ram.asm ├── unapi-rom.asm └── unapi-specless.asm ├── tools ├── .gitignore ├── README.md ├── apilist.asm ├── eth.c ├── ramhelpr.asm └── tcpip.c └── txt ├── ethunapi11.txt ├── unapi11.txt └── unapiintro.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: konamiman 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Néstor Soriano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MSX-UNAPI specification 2 | 3 | MSX-UNAPI is a standard procedure for defining, discovering and using new APIs (Application Program Interfaces) for MSX computers, intended primarly (but not exclusively) to allow hardware manufacturers to provide consistent and standarized APIs. UNAPI is short for "UNified API definition and discovery procedure". 4 | 5 | This repository contains the following documents: 6 | 7 | * [Introduction to MSX-UNAPI](docs/Introduction%20to%20MSX-UNAPI.md): You might want to start here to get a high-level overview of what UNAPI is and how it works. 8 | 9 | * [MSX-UNAPI specification](docs/MSX%20UNAPI%20specification%201.1.md): The full core specification document, covering all the details on how to design an API, how to implement it, and how to discover and consume it from a client application. 10 | 11 | * [Ethernet UNAPI specification](docs/Ethernet%20UNAPI%20specification%201.1.md): The specification of an API for Ethernet cards. 12 | 13 | * [TCP/IP UNAPI specification](docs/TCP-IP%20UNAPI%20specification.md): The specification of an API for TCP/IP stacks. 14 | 15 | Also there are a couple of directories with code: [examples](/examples) and [tools](/tools). For the later, binaries are available in the [releases](https://github.com/Konamiman/MSX-UNAPI-specification/releases) section. 16 | 17 | If you like MSX-UNAPI **[please consider donating](http://www.konamiman.com/msx/msx-e.html#donate)**. My kids need moar shoes! 18 | -------------------------------------------------------------------------------- /docs/Ethernet UNAPI specification 1.1.md: -------------------------------------------------------------------------------- 1 | # The Ethernet UNAPI specification 2 | 3 | This document describes an UNAPI compliant API specification for Ethernet hardware for MSX computers. 4 | 5 | ## Index 6 | 7 | [1. Introduction](#1-introduction) 8 | 9 | [2. API identifier and version](#2-api-identifier-and-version) 10 | 11 | [3. API routines](#3-api-routines) 12 | 13 | [3.1. ETH_GETINFO: Obtain the implementation name and version](#31-eth_getinfo-obtain-the-implementation-name-and-version) 14 | 15 | [3.2. ETH_RESET: Reset hardware](#32-eth_reset-reset-hardware) 16 | 17 | [3.3. ETH_GET_HWADD: Obtain the Ethernet address](#33-eth_get_hwadd-obtain-the-ethernet-address) 18 | 19 | [3.5. ETH_NET_ONOFF: Enable or disable networking](#35-eth_net_onoff-enable-or-disable-networking) 20 | 21 | [3.6. ETH_DUPLEX: Configure duplex mode](#36-eth_duplex-configure-duplex-mode) 22 | 23 | [3.7. ETH_FILTERS: Configure frame reception filters](#37-eth_filters-configure-frame-reception-filters) 24 | 25 | [3.8. ETH_IN_STATUS: Check for received frames availability](#38-eth_in_status-check-for-received-frames-availability) 26 | 27 | [3.9. ETH_GET_FRAME: Retrieve the oldest received frame](#39-eth_get_frame-retrieve-the-oldest-received-frame) 28 | 29 | [3.10. ETH_SEND_FRAME: Send a frame](#310-eth_send_frame-send-a-frame) 30 | 31 | [3.11. ETH_OUT_STATUS: Check frame transmission status](#311-eth_out_status-check-frame-transmission-status) 32 | 33 | [3.12. ETH_SET_HWADD: Set the Ethernet address](#312-eth_set_hwadd-set-the-ethernet-address) 34 | 35 | [Appendix A. Ethernet frame formats](#appendix-a-ethernet-frame-formats) 36 | 37 | [Appendix B. Acknowledgements](#appendix-b-acknowledgements) 38 | 39 | [Appendix C. Version history](#appendix-c-version-history) 40 | 41 | ## 1. Introduction 42 | 43 | MSX-UNAPI is a standard procedure for defining, discovering and using new APIs (Application Program Interfaces) for MSX computers. The MSX-UNAPI specification is described [in a separate document](MSX%20UNAPI%20specification%201.1.md). 44 | 45 | This document describes an UNAPI compliant API intended for hardware that implements Ethernet networking capabilities. The functionality provided by this API covers the link layer of a network communications stack; as such, it mainly provides routines to send and receiving raw Ethernet frames to and from an Ethernet network. There are also auxiliary routines to check for network availability, to obtain the local Ethernet address, or to configure reception filters. 46 | 47 | The intended client software applications for this API are implementations of the higher level layers of the communications stack, typically, TCP/IP stacks. Anyway it can be useful for other types of software as well, for example network traffic monitors. 48 | 49 | Note that it is not mandatory to have actual underlying Ethernet hardware in order to implement this API. As long as the routine signatures and behaviors are preserved, the implementation will ve valid, even if it acts on a completely different type of hardware, or on no harwdare at all (for example, an Ethernet emulation API over a serial or JoyNet cable could be developed). 50 | 51 | ## 2. API identifier and version 52 | 53 | The API identifier for the specification described in this document is: "ETHERNET" (without the quotes). Remember that per the UNAPI specification, API identifiers are case-insensitive. 54 | 55 | The Ethernet API version described in this document is 1.1. This is the API specification version that the mandatory implementation information routine must return in DE (see [Section 3.1](#31-eth_getinfo-obtain-the-implementation-name-and-version)). 56 | 57 | ## 3. API routines 58 | 59 | This version of the Ethernet API consists of 11 mandatory routines, which are described below. API implementations may define their own additional implementation-specific routines, as described in the MSX- UNAPI specification. 60 | 61 | ### 3.1. ETH_GETINFO: Obtain the implementation name and version 62 | 63 | * Input: 64 | * A = 0 65 | 66 | * Output: 67 | * HL = Address of the implementation name string 68 | * DE = API specification version supported. D=primary, E=secondary. 69 | * BC = API implementation version. B=primary, C=secondary. 70 | 71 | This routine is mandatory for all implementations of all UNAPI compliant APIs. It returns basic information about the implementation itself: the implementation version, the supported API version, and a pointer to the implementation description string. 72 | 73 | The implementation name string must be placed in the same slot or segment of the implementation code (or in page 3), must be zero terminated, must consist of printable characters, and must be at most 63 characters long (not including the terminating zero). Refer to the MSX-UNAPI specification for more details. 74 | 75 | ### 3.2. ETH_RESET: Reset hardware 76 | 77 | * Input: 78 | * A = 1 79 | 80 | This routine resets the underlying hardware and/or the API implementation state variables to its initial state. After executing this routine, the hardware (if present) and the implementation must remain in the same state as after a computer reset or after the implementation is installed (whatever applies). More precisely, the state after the reset must be as follows: 81 | 82 | * The Ethernet address is appropriately set to its default value (it is optional to allow changing this address, see [Section 3.3](#33-eth_get_hwadd-obtain-the-ethernet-address)). 83 | * Networking is enabled. (See [Section 3.5](#35-eth_net_onoff-enable-or-disable-networking)). 84 | * Received frames, if any, are discarded. Any frame transmission in process when the routine was called is aborted. 85 | * Duplex mode is set to half-duplex, when it is possible (see [Section 3.6](#36-eth_duplex-configure-duplex-mode)). 86 | * Reception filters are set to accept broadcast and small frames. Promiscuous mode is disabled. (See [Section 3.7](#37-eth_filters-configure-frame-reception-filters)). 87 | 88 | ### 3.3. ETH_GET_HWADD: Obtain the Ethernet address 89 | 90 | * Input: 91 | * A = 2 92 | 93 | * Output: 94 | * L-H-E-D-C-B = Current Ethernet address 95 | 96 | This routine obtains the current Ethernet address of the implementation. The address is mapped to registers HL, DE and BC in a way that makes it easy to store and retrieve it for Z80, which stores 16 bit values int little-endian format. For example, the address returned by this routine can be stored at address X with these instructions: LD (X),HL - LD (X+2),DE - LD (X+4),BC. 97 | 98 | Ethernet addresses (also called MAC addresses) are unique for each physical card and are intended to never be changed after the card is manufactured. See [Appendix A](#appendix-a-ethernet-frame-formats). 99 | 100 | If the implementation supports it, the hardware address can be changed by using the ETH_SET_HWADD routine (see [Section 3.12](#312-eth_set_hwadd-set-the-ethernet-address)). 101 | 102 | ### 3.4. ETH_GET_NETSTAT: Obtain network connection status 103 | 104 | * Input: 105 | * A = 3 106 | 107 | * Output: 108 | * A = current network status 109 | * 0 NOT connected to an active network 110 | * 1 connected to an active network 111 | 112 | This routine checks whether the underlying hardware is connected to an active network or not (in other words, if the Ethernet cable is appropriately plugged and there is carrier). 113 | 114 | It is allowed to use loopback methods (that is, to send a frame and check if it is received back) to check the network connection status. Therefore, it may take a while to execute, so it is not advisable to invoke it too often. 115 | 116 | ### 3.5. ETH_NET_ONOFF: Enable or disable networking 117 | 118 | * Input: 119 | * A = 4 120 | * B = Action to perform: 121 | * 0: Obtain current state only 122 | * 1: Enable networking 123 | * 2: Disable networking 124 | 125 | * Output: 126 | * A = State after routine execution: 127 | * 1: Networking is enabled 128 | * 2: Networking is disabled 129 | 130 | This routine logically enables or disables networking activity. When disabled, no frames will be recevied (or received frames will be automatically discarded); the behavior when trying to send a frame with networking disabled is undefined. 131 | 132 | When the underlying hardware offers ways to phisically disable networking (such as entering in low power mode or similar), these must be used. Otherwise, disabling must be done by software. 133 | 134 | ### 3.6. ETH_DUPLEX: Configure duplex mode 135 | 136 | * Input: 137 | * A = 5 138 | * B = action to perform: 139 | * 0: Obtain current mode only 140 | * 1: Set half-duplex mode 141 | * 2: Set full-duplex mode 142 | 143 | * Output: 144 | * A = Mode after routine execution: 145 | * 1: Currently half-duplex mode set 146 | * 2: Currently full-duplex mode set 147 | * 3: Current mode unknown or duplex mode does not apply 148 | 149 | Offering the ability to change the duplex mode is optional. When not possible (because only one mode is supported, or because it is not possible to change the current mode by software), the routine must simply return the current mode, as if it were called with B=0. 150 | 151 | When it is possible to change the duplex mode by software, the default mode (after a reset, see [Section 3.2](#32-eth_reset-reset-hardware)) should be the half- duplex mode. 152 | 153 | The "Unknown or does not apply" mode is primarily intended for implementations that do not act on real Ethernet hardware. For these implementations, the concept of "duplex mode" may not make sense. 154 | 155 | ### 3.7. ETH_FILTERS: Configure frame reception filters 156 | 157 | * Input: 158 | * A = 6 159 | * B = Filter bitmask: 160 | * Bit 7: Set to return current configuration only 161 | * Bit 6: Reserved 162 | * Bit 5: Reserved 163 | * Bit 4: Set to enable promiscuous mode, reset do disable it 164 | * Bit 3: Reserved 165 | * Bit 2: Set to accept broadcast frames, reset to reject them 166 | * Bit 1: Set to accept small frames (smaller than 64 bytes), reset to reject them 167 | * Bit 0: Reserved 168 | 169 | * Output: 170 | * A = Filter configuration after execution (bitmask with same format as B at input) 171 | 172 | This routine allows to set or to check the current frame reception filters. Frames whose destination Ethernet address matches the current address of the underlying hardware must always be accepted; these filters decide what to do with special frames. 173 | 174 | When bit 7 of B is set, all other bits will be ignored, and the routine will simply return a bitmask with the current configuration, without modifying anything. 175 | 176 | When bit 4 is set, promiscuous mode is enabled. In this mode, all received frames are enabled, regardless of their destination address. This mode is usually never enabled, except for special purposes such as network analysis. 177 | 178 | When bit 2 is set, broadcast frames are accepted. These frames have a destination address of FF-FF-FF-FF-FF-FF, and are intended to be received by all hosts in the network. Broadcats frames are usually always accepted. 179 | 180 | When bit 1 is set, small frames (frames smaller than 64 bytes) are accepted. These frames violate the Ethernet specification but may anyway contain useful information. 181 | 182 | Reserved bits must be set to zero at input, and are always returned as zero at output. These bits may be used in future versions of the Ethernet API specification. 183 | 184 | ### 3.8. ETH_IN_STATUS: Check for received frames availability 185 | 186 | * Input: 187 | * A = 7 188 | 189 | * Output: 190 | * A = availability of incoming frames: 191 | * 0: No received frames available 192 | * 1: At least one received frame is available 193 | * BC = Size of the oldest available frame (only if A=1) 194 | * HL = Bytes 12 and 13 of the oldest available frame (only if A=1) 195 | 196 | This routine checks whether there are received frames available to be retrieved or not. When A=1 is returned, it means that there is at least one received frame available to be retrieved; there is no way to know how many frames are available. 197 | 198 | When at least one frame is available, BC returns the total size of the oldest frame. This size includes the Ethernet header and data; it corresponds to the frame format outlined in Appendix A, which does not include the frame preamble nor the checksum. 199 | 200 | When at least one frame is available, BC returns bytes 12 and 13 of the oldest frame. These bytes contain ether the frame size or the ether-type, depending of the frame type (Ethernet II or IEEE802.3). Since the frame data starts at a different boundary on each frame type, client software may use this information to decide where to retrieve the frame, so that the data boundary is always at the same memory address. This may ease frame data manipulation. 201 | 202 | The oldest received frame can be retrieved using ETH_GET_FRAME, see [Section 3.9](#39-eth_get_frame-retrieve-the-oldest-received-frame). 203 | 204 | ### 3.9. ETH_GET_FRAME: Retrieve the oldest received frame 205 | 206 | * Input: 207 | * A = 8 208 | * HL = Destination address for the frame, or 0 to discard the frame 209 | 210 | * Output: 211 | * A = operation result: 212 | * 0: frame has been retrieved or discarded 213 | * 1: no received frames are available 214 | * BC = Size of the retrieved frame (if A = 0) 215 | 216 | This routine retrieves the oldest received frame (copies it to the specified memory address), and removes it from the implementation internal buffer (usually belonging to the underlying harwdare), so that when the routine is called again, the next received frame is obtained. Successive calls to this routine must return the received frames in the same order as they are received from the network. 217 | 218 | The received frame will include the Ethernet header and data, it will not include the frame preamble nor checksum. The frame format will be one of the two formats outlined in Appendix A. 219 | 220 | Implementations may not allow the destination address to be a page 1 address (in the range 4000h-7FFFh). Client software should not use this range as destination address when invoking this routine, in order to correctly interoperate with such implementations. 221 | 222 | When the specified destination address is zero, the frame is not retrieved but just discarded. That is, it is removed from the implementation internal buffer but not copied to memory. 223 | 224 | This specification does not impose a minimum size for the internal reception buffer, other than being big enough to hold one complete frame. But of course, the bigger the better, so that it can hold enough frames, thus minimizing the probability of losing frames. When this buffer is full, new frames must be discarded so that the oldest ones are preserved. 225 | 226 | ### 3.10. ETH_SEND_FRAME: Send a frame 227 | 228 | * Input: 229 | * A = 9 230 | * HL = Frame address in memory 231 | * BC = Frame length 232 | * D = Routine execution mode: 233 | * 0: Synchronous 234 | * 1: Asynchronous 235 | 236 | * Output: 237 | * A = operation result: 238 | * 0: Frame sent, or transmission started 239 | * 1: Invalid frame length 240 | * 3: Carrier lost 241 | * 4: Excessive collisions 242 | * 5: Asyncrhonous mode not supported 243 | 244 | This routine sends to the network the frame that the client software has composed in memory, at the address specified in HL. The frame format must be one of the two outlined in Appendix A; that is, the frame must be composed of Ethernet header (including the local Ethernet address, see [Section 3.3](#33-eth_get_hwadd-obtain-the-ethernet-address)) and data only, with no preamble nor checksum. (When there is real underlying Ethernet hardware, it is expected that the hardware itself will generate the checksum; otherwise, the implementation must generate it prior to sending the frame.) 245 | 246 | Implementations may not allow the frame source address to be a page 1 address (in the range 4000h-7FFFh). Client software should not use this range as source address when invoking this routine, in order to correctly interoperate with such implementations. 247 | 248 | Allowed frame lengths are 16 to 1514. If the frame is smaller than 64 bytes, the implementation must pad it with zeros up to 64 bytes (usually the underlying hardware will do it automatically). 249 | 250 | All implementations must support the syncrhonous execution mode. In this mode, the routine will start the frame transmission; then will wait until transmission has finished (successfully or not) and will return the appropriate success or error code (in this mode, error code zero means "frame successfully sent"). 251 | 252 | Implementations may optionally support the asyncrhonous execution mode, and should indeed support it when there is real Ethernet hardware that handles the transmission independently of the Z80 code execution. In this mode, the routine initiates the transmission (unless frame length is invalid, in which case A=1 must be returned) and returns immediately with A=0 (meaning "frame transmission started"). Frame transmission status can then be ckeched by using ETH_OUT_STATUS (see [Section 3.11](#311-eth_out_status-check-frame-transmission-status)). 253 | 254 | To check whether asynchronous mode is supported or not, try to send a dummy 16 byte frame (12 zero bytes for source and detination address and then FFFFh for the ether-type) in asyncrhonous mode, and check the return code: it will be 0 if asyncrhonous execution is supported, 5 otherwise. 255 | 256 | ### 3.11. ETH_OUT_STATUS: Check frame transmission status 257 | 258 | * Input: A = 10 259 | 260 | * Output: 261 | * A = operation result: 262 | * 0: No frames were sent since last reset 263 | * 1: Now transmitting 264 | * 2: Transmission finished successfully 265 | * 3: Carrier lost 266 | * 4: Excessive collisions 267 | 268 | This routine returns the state of the last frame send operation initiated by ETH_SEND_FRAME (see [Section 3.10](#310-eth_send_frame-send-a-frame)). It is intended for being used after an asyncronous execution of that routine, but will return valid information for syncrhonous executions as well. 269 | 270 | In case of syncrhonous execution, this routine will return the same error code that ETH_SEND_FRAME returned, except that code 0 is converted to 2, and code 1 (invalid length) is not considered a failed transmission (since the routine returns without attempting the transmission). 271 | 272 | The obtained information is persistent: successive executions of this routine will always return the same value until another frame is sent (or attempted to be sent) or the reset routine (see [Section 3.2](#32-eth_reset-reset-hardware)) is invoked. 273 | 274 | ### 3.12. ETH_SET_HWADD: Set the Ethernet address 275 | 276 | * Input: 277 | * A = 11 278 | * L-H-E-D-C-B = Ethernet address to set 279 | 280 | * Output: 281 | * L-H-E-D-C-B = Ethernet address after the routine execution 282 | 283 | This routine sets the hardware address if the implementation supports it. When changing the address is not supported, the routine must do nothing and return the current address as if the ETH_GET_ADDRESS routine were called (see [Section 3.3](#33-eth_get_hwadd-obtain-the-ethernet-address)). Setting the address should not be allowed unless there is a good reason for it (for example, when it is not possible to store the address in non-volatile memory). 284 | 285 | Ethernet addresses (also called MAC addresses) are unique for each physical card and are intended to never be changed after the card is manufactured. See [Appendix A](#appendix-a-ethernet-frame-formats). 286 | 287 | ## Appendix A. Ethernet frame formats 288 | 289 | In this appendix the Ethernet frame formats are described. This information is provided for reference only, and it is not exhaustive. More detailed information can be found on Internet. 290 | 291 | There are two types of Ethernet frame: Ethernet II, and IEEE802.3 with SNAP extension (IEEE in short). 292 | 293 | The Ethernet II frame format is as follows (one line is 32 bits): 294 | 295 | ``` 296 | 0 1 2 3 297 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 298 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 299 | | Ethernet address of receiver | 300 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 301 | | E. Add. of receiver (cont.) | E. Add. of sender | 302 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 303 | | Ethernet address of sender (continues) | 304 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 305 | | Ether-Type | | 306 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 307 | . . 308 | . Ethernet frame data . 309 | . . 310 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 311 | ``` 312 | 313 | The IEEE frame format is as follows: 314 | 315 | ``` 316 | 0 1 2 3 317 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 318 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 319 | | Ethernet address of receiver | 320 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 321 | | E. Add. of receiver (cont.) | E. Add. of sender | 322 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 323 | | Ethernet address of sender (continues) | 324 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 325 | | Frame length | 170 | 170 | 326 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 327 | | 3 | 0 | 0 | 0 | 328 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 329 | | Ether-Type | | 330 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 331 | . . 332 | . Ethernet frame data . 333 | . . 334 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 335 | ``` 336 | 337 | The Ethernet addresses (also called MAC address), six bytes long, are unique for each card and are assigned in the factory. The address composed by all ones (six FFh bytes) is special: it is the broadcast address, and when it appears on the destination address field, it indicates that the frame is addressed to all the machines on the network, rather than to a given machine only. 338 | 339 | Normal Ethernet addresses have the lowest bit of the first byte set to zero. When it is one, it is a multicast address (the frame is addressed to a group of computers). 340 | 341 | The minimum size of an Ethernet frame (including all the headers) is 64 bytes. The maximum size is 1514 bytes. 342 | 343 | The "Frame length" of IEEE frames counts from the first byte with value 170. That is, it equals the frame data part plus eight. 344 | 345 | The "Ether-Type" field indicates the type of the frame being transported on the frame data part. The two most commonly used types are: 346 | 347 | * 0800h: IP datagram 348 | * 0806h: ARP frame 349 | 350 | Both frame types, Ethernet II and IEEE, can exist mixed in the same network. To know whether an incoming frame is Ehternet II type or IEEE type, the 16 byte number placed at positions 12 and 13 of the frame must be checked: 351 | 352 | * If the number is less than or equal to 1500, it is an IEEE frame (the number is the frame length). 353 | 354 | * If the number is greater than 1500, it is an IEEE frame (the number is the frame Ether-Type). All the Ether-Type codes are greater than 1500. 355 | 356 | When sending frames, they must be sent in Ethernet II type frames unless we know for sure that the machines on the network work exclusively with the IEEE format. 357 | 358 | Note that the frame length and Ether-Type fields are stored in Big- Endian format, that is with the high byte first; contrarywise to MSX, that stores the 16 bit numbers in Little-Endian format, with the low byte first. 359 | 360 | ## Appendix B. Acknowledgements 361 | 362 | The original text version of this document was produced using xml2rfc v1.34 (of http://xml.resource.org/) from a source in RFC-2629 XML format. 363 | 364 | ## Appendix C. Version history 365 | 366 | * Version 1.1: 367 | 368 | * The ETH_GET_HWADD routine now only gets the hardware address (see [Section 3.3](#33-eth_get_hwadd-obtain-the-ethernet-address)) 369 | * Added the ETH_SET_HWADD routine (see [Section 3.12](#312-eth_set_hwadd-set-the-ethernet-address)) 370 | -------------------------------------------------------------------------------- /docs/Introduction to MSX-UNAPI.md: -------------------------------------------------------------------------------- 1 | # Introduction to MSX-UNAPI 2 | 3 | This document introduces MSX-UNAPI, a standard procedure for defining, discovering and using new APIs (Application Program Interfaces) for MSX computers. For the detailed specification, please read [the MSX-UNAPI specification document](MSX%20UNAPI%20specification%201.1.md). 4 | 5 | ## 1. Motivation 6 | 7 | In the last years, several MSX hobbyists have developed various kinds of amateur hardware for MSX computers. Usually, each of these pieces of hardware comes with a ROM containing an API (Application Program Interface), which consists of a series of routines that allow developers to interact with the hardware. 8 | 9 | As each device has its own API, devices are not interchangeable from the software point of view. For example, InterNestor Lite works only with the ethernet card ObsoNET, and it will not work with any other card developed in the future. 10 | 11 | The MSX-UNAPI specification aims to solve this problem, by defining a set of rules for creating interchangeable API implementations. 12 | 13 | ## 2. Key concepts 14 | 15 | The complete MSX-UNAPI specification may seem complicated at first glance, but it relies on just a few key concepts, which are enumerated below. 16 | 17 | **Note:** In the following text, the terms "API specification" and "API implementation" refer to API specifications and implementations that obey the rules of the MSX-UNAPI specification. 18 | 19 | * An **API specification** is a set of routines for performing a set of concrete tasks. Each specification has a short alphanumeric identifier that serves as a signature to uniquely distinguish it from other specifications. 20 | 21 | For example, an API specification for ethernet cards could have the identifier ETHERNET and be composed of three routines: send packet, receive packet and check network state. 22 | 23 | * An **API implementation** is the realization in code of an API specification. There may be several implementation of the same API specification, and since all of them implement the same set of routines, they are interchangeable. Each implementation has a short name that serves to distinguish it from other implementations. 24 | 25 | For example, "ObsoNET BIOS" and "DenyoNet BIOS" could be the names of two implementations of the API whose identifier is ETHERNET. A TCP/IP stack prepared to deal with the ETHERNET API could work with both implementations. 26 | 27 | * The MSX-UNAPI specification provides a set of minimal rules that must be followed by all compliant API specifications and implementations. This is done to ease the development of software that make use of API implementations. 28 | 29 | The main rules are: API implementation code must be placed in ROM, in mapped RAM or in page 3 RAM; there must be one single call point for all the API routines (routine number is passed in register A); and there must be one routine that informs about the API name and version. All of this is detailed in the MSX-UNAPI specification document. 30 | 31 | * More than one implementation of a given API specification may be installed at the same time. The MSX extended BIOS mechanism is used to discover and locate the available implementations. 32 | 33 | Usually, when more than one implementation is found, it does not matter which one is used to perform the tasks offered by the API specification. However, if necessary, implementations can be distinguished thanks to their names. 34 | 35 | ## 3. Example 36 | 37 | This example provides pseudo-code for an hypothetical TCP/IP stack that relies on the ETHERNET API to send and receive data. In the code, the names A, B, C, HL and DE refer to Z80 registers; other names refer to routines or variables. Semicolons (;) indicate that the rest of the line is a comment. 38 | 39 | Please refer to [the MSX-UNAPI specification document](MSX%20UNAPI%20specification%201.1.md) for details about how to call API routines and extended BIOS, as well as Z80 registers usage. 40 | 41 | PRINT "Welcome to this wonderful ETHERNET API aware TCP/IP stack!" 42 | PRINT "Now I'm going to search for ETHERNET API implementations... 43 | 44 | POKE &HF847,"ETHERNET"+0 45 | A=0 46 | B=0 47 | DE=&H2222 48 | CALL &HFFCA ; The EXTBIO hook 49 | 50 | IF B=0 THEN 51 | PRINT "Ooops!" 52 | PRINT "I haven't found any ETHERNET API implementation!" 53 | END 54 | ENDIF 55 | 56 | PRINT "I've found "+B+" implementations of the ETHERNET API!" 57 | PRINT "I'll use implementation with index 1" 58 | 59 | ; Obtain implementation location (address, slot and/or segment) 60 | ; and as the first task, obtain its name and version 61 | 62 | POKE &HF847,"ETHERNET"+0 ; Not necessary if memory not modified 63 | A=1 ; The implementation index 64 | DE=&H2222 65 | CALL &HFFCA ; The EXTBIO hook 66 | ApiSlot=A 67 | ApiSegment=B 68 | ApiEntry=HL 69 | 70 | A=0 ; 0 is the index for the API information routine 71 | CALL EXE_UNAPI 72 | PRINT "The API name is: "+READ_UNAPI(HL) 73 | PRINT "The API version is: "+B+"."+C 74 | 75 | ; Now assume that per the ETHERNET API specification, 76 | ; routine 3 returns A=1 if network is available or 0 otherwise 77 | 78 | A=3 79 | CALL EXE_UNAPI 80 | IF A=0 THEN 81 | PRINT "Ooops! No network!" 82 | END 83 | ENDIF 84 | 85 | PRINT "Network OK! Let's internetwork!" 86 | ; etc etc... 87 | 88 | 89 | ;--- This routine calls the API routine whose index is passed in A 90 | 91 | EXE_UNAPI: 92 | IF ApiEntry>=&HC000 THEN 93 | CALL ApiEntry 94 | ELSE IF ApiSegment=&HFF THEN 95 | CALL ApiEntry AT SLOT ApiSlot 96 | ELSE 97 | CALL ApiEntry AT SEGMENT ApiSegment AT SLOT ApiSlot 98 | RETURN 99 | 100 | 101 | ;--- This routine reads the API memory whose address 102 | ;--- is passed as parameter, until a zero is found 103 | 104 | READ_UNAPI(Address): 105 | HL=Address 106 | String="" 107 | LOOP: 108 | IF Address>=&HC000 THEN 109 | A=PEEK(HL) 110 | ELSE IF ApiSegment=&HFF THEN 111 | A=READ (HL) AT SLOT ApiSlot 112 | ELSE 113 | A=READ (HL) AT SEGMENT ApiSegment AT SLOT ApiSlot 114 | ENDIF 115 | IF A<>0 THEN 116 | String=String+A 117 | HL=HL+1 118 | GOTO LOOP 119 | RETURN String 120 | 121 | ## Appendix A. Acknowledgements 122 | 123 | The original text version of this document was produced using xml2rfc v1.34 (of http://xml.resource.org/) from a source in RFC-2629 XML format. 124 | 125 | ## Appendix B. Document version history 126 | 127 | * Version 0.2: Done some minor changes proposed by Tanni, in order to clarify the text. 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.lst 3 | *.sym 4 | *.rom 5 | *.com 6 | *.dat 7 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # MSX-UNAPI examples 2 | 3 | This directory contains some examples on how to implement an MSX-UNAPI specification in various scenarios: 4 | 5 | * [unapi-ram](unapi-ram.asm): Implementation that installs in a mapped RAM segment. 6 | 7 | * [unapi-rom](unapi-rom.asm): Implementation inside a plain ROM. 8 | 9 | * [unapi-specless](unapi-specless.asm): Application that uses the concept of "specificationless implementation": it uses the UNAPI infrastructure to install as a TSR, but doesn't actually implement any API specification. 10 | 11 | * [unapi-nextor](unapi-nextor.asm): Implementation that lives inside a [Nextor](https://github.com/Konamiman/Nextor) driver. 12 | 13 | Please see the code of each example for details on how to build them and how to customize them for your own API specification. 14 | 15 | You may want to take a look at [InterNestor Lite](https://github.com/Konamiman/MSX/tree/master/SRC/INL), [DenyoNet BIOS](https://github.com/Konamiman/MSX/tree/master/SRC/DENYONET) and [ObsoNet BIOS 1.2](https://github.com/Konamiman/MSX/tree/master/SRC/ONET) as well as examples of implementations of UNAPI specifications. 16 | 17 | To see examples of API client applications see the [tools](../tools) directory. 18 | -------------------------------------------------------------------------------- /examples/unapi-nextor-id.asm: -------------------------------------------------------------------------------- 1 | db "SIMPLE_MATH",0 2 | -------------------------------------------------------------------------------- /examples/unapi-nextor.asm: -------------------------------------------------------------------------------- 1 | ; Example of Nextor driver containing a UNAPI implementation + a BASIC CALL statement 2 | ; By Konamiman, 5/2019 3 | ; 4 | ; This driver does not implement any actual Nextor device, and 5 | ; implements a sample UNAPI mathematical specification, "SIMPLE_MATH", 6 | ; which has just two functions: 7 | ; Function 1: Returns HL = L + E 8 | ; Function 2: Returns HL = L * E 9 | ; 10 | ; Search for "TODO" comments for what to change/extend when creating your own implementation. 11 | ; 12 | ; Also, it implements a very simple BASIC CALL statement command: 13 | ; CALL SAY("whatever"), which will just print whatever string is supplied 14 | ; as a parameter. 15 | ; 16 | ; Additionally to this driver, a string with the UNAPI implementation identifier 17 | ; must be placed in the ROM so that it is visible at address APIINFO 18 | ; when either the Nextor kernel ROM bank 0 or 3 is switched 19 | ; (the MKNEXROM tool does that when using the /e option). 20 | ; 21 | ; The Nextor related code is just succintly commented, please see 22 | ; the Nextor Driver Development Guide or the example dummy driver 23 | ; in https://github.com/Konamiman/Nextor for more details. 24 | ; 25 | ; How to build: 26 | ; 27 | ; 1. Get sjasm (https://github.com/Konamiman/Sjasm/releases) 28 | ; 29 | ; 2. Get MKNEXROM.EXE and the Nextor kernel base file (Nextor-.base.dat) 30 | ; from the latest release of Nextor (https://github.com/Konamiman/Nextor/releases) 31 | ; 32 | ; 3. Build the driver and the identifier string: 33 | ; sjasm unapi-nextor.asm unapi-nextor.drv 34 | ; sjasm unapi-nextor-id.asm unapi-nextor-id.dat 35 | ; 36 | ; 4. Build the Nextor kernel ROM: 37 | ; mknexrom Nextor-.base.dat nextor.rom /d:unapi-nextor.dat /e:unapi-nextor-id.dat 38 | ; 39 | ; 5. Done! nextor.rom is a full Nextor kernel with ASCII16 mapper and a dummy driver. 40 | 41 | 42 | org 4000h 43 | 44 | ds 256 ;Required for MKNEXROM.EXE 45 | 46 | DRV_START: 47 | 48 | 49 | ;======================= 50 | ;=== MSX CONSTANTS === 51 | ;======================= 52 | 53 | ;--- BIOS 54 | 55 | CALSLT: equ 001Ch 56 | CHPUT: equ 00A2h 57 | CALBAS: equ 0159H ;Call a routine in the BASIC interpreter, IX=routine 58 | 59 | ;--- BASIC interpreter 60 | ; See MSX2 Technical Handbook chapter 2 for details on these 61 | 62 | SYNERR: equ 4055h ;Throw "Syntax Error" 63 | TYPEM: equ 406Dh ;Throw "Type Mismatch" 64 | CHRGTR: equ 4666h 65 | FRMEVL: equ 4C64h 66 | FRESTR: equ 67D0h 67 | 68 | ;--- Page 3 work area 69 | 70 | VALTYP: equ 0F663h 71 | DAC: equ 0F7F6h 72 | ARG: equ 0F847h 73 | PROCNM: equ 0FD89h 74 | EXPTBL: equ 0FCC1h 75 | 76 | 77 | ;================================= 78 | ;=== NEXTOR DRIVER CONSTANTS === 79 | ;================================= 80 | 81 | ;Nextor driver version 82 | 83 | VER_MAIN equ 1 84 | VER_SEC equ 0 85 | VER_REV equ 0 86 | 87 | ;Invoke the routine at CODE_ADD with kernel main bank swithced on 88 | CALLB0: equ 403Fh 89 | 90 | ;Call a routine in another bank 91 | CALBNK equ 4042h 92 | 93 | ;Get the current slot for page 1 94 | GSLOT1 equ 402Dh 95 | 96 | ;First direct driver call entry point 97 | DRV_DIRECT0: equ 7850h 98 | 99 | ;Start of unused space in Nextor kernel, used here for the UNAPI implementation name 100 | APIINFO: equ 7BD0h 101 | 102 | ;Address of the routine to be executed by CALLB0 103 | CODE_ADD: equ 0F1D0h 104 | 105 | 106 | ;========================= 107 | ;=== UNAPI CONSTANTS === 108 | ;========================= 109 | 110 | ;--- API version and implementation version 111 | 112 | ;TODO: Adjust for your implementation 113 | 114 | API_V_P: equ 1 115 | API_V_S: equ 0 116 | ROM_V_P: equ 1 117 | ROM_V_S: equ 0 118 | 119 | ;--- Maximum number of available standard and implementation-specific function numbers 120 | 121 | ;TODO: Adjust for your implementation 122 | 123 | ;Maximum function number 124 | ;Must be 0 to 127 125 | MAX_FN: equ 2 126 | 127 | ;Maximum implementation-specific function number 128 | ;Must be either zero (if no implementation-specific functions available), or 128 to 254 129 | MAX_IMPFN: equ 0 130 | 131 | 132 | ;============================== 133 | ;=== NEXTOR DRIVER HEADER === 134 | ;============================== 135 | 136 | ; See the Nextor Driver Development Guide in https://github.com/Konamiman/Nextor for more details. 137 | 138 | db "NEXTOR_DRIVER",0 ;Driver signature 139 | db 1 ;Device-based driver 140 | db 0 ;Reserved 141 | 142 | DRV_NAME: 143 | ;TODO: Adjust for your implementation 144 | db "Driver with UNAPI example" 145 | ds 32-($-DRV_NAME)," " 146 | 147 | 148 | ; Jump table for the driver public routines 149 | 150 | jp DRV_TIMI 151 | jp DRV_VERSION 152 | jp DRV_INIT 153 | jp DRV_BASSTAT 154 | jp DRV_BASDEV 155 | ;;; Relevant for UNAPI 156 | jp UNAPI_EXTBIO 157 | jp UNAPI_ENTRY ;Calling DRV_DIRECT0 in bank 0 or 3 redirects here 158 | ;;; END of relevant for UNAPI 159 | jp GO_DRV_DIRECT1 ;This one would be used if a second UNAPI specification is implemented 160 | jp GO_DRV_DIRECT2 161 | jp GO_DRV_DIRECT3 162 | jp GO_DRV_DIRECT4 163 | 164 | ds 15 165 | 166 | jp DEV_RW 167 | jp DEV_INFO 168 | jp DEV_STATUS 169 | jp LUN_INFO 170 | 171 | 172 | ;================================ 173 | ;=== NEXTOR DRIVER ROUTINES === 174 | ;================================ 175 | 176 | ; See the Nextor Driver Development Guide in https://github.com/Konamiman/Nextor for more details. 177 | 178 | ; Timer interrupt routine, it will be called on each timer interrupt 179 | DRV_TIMI: 180 | ret 181 | 182 | ; Driver initialization routine 183 | DRV_INIT: 184 | xor a 185 | ld hl,0 186 | ret 187 | 188 | ; Obtain driver version 189 | DRV_VERSION: 190 | ld a,VER_MAIN 191 | ld b,VER_SEC 192 | ld c,VER_REV 193 | ret 194 | 195 | ; BASIC expanded device handler 196 | DRV_BASDEV: 197 | scf 198 | ret 199 | 200 | ; Direct calls entry points 201 | ; (we only use DRV_DIRECT0, which jumps to UNAPI_ENTRY) 202 | GO_DRV_DIRECT1: 203 | GO_DRV_DIRECT2: 204 | GO_DRV_DIRECT3: 205 | GO_DRV_DIRECT4: 206 | ret 207 | 208 | ; Read or write logical sectors from/to a logical unit 209 | DEV_RW: 210 | ld a,0FCh ;.NRDY 211 | ld b,0 212 | ret 213 | 214 | ; Device information gathering 215 | DEV_INFO: 216 | ld a,1 217 | ret 218 | 219 | ; Obtain device status 220 | DEV_STATUS: 221 | xor a 222 | ret 223 | 224 | ; Obtain logical unit information 225 | LUN_INFO: 226 | ld a,1 227 | ret 228 | 229 | 230 | ;============================== 231 | ;=== UNAPI EXTBIO HANDLER === 232 | ;============================== 233 | 234 | ; Works the expected way, except that it must return 235 | ; D'=1 if the old hook must be called, D'=0 otherwise. 236 | ; It is entered with D'=1. 237 | 238 | UNAPI_EXTBIO: 239 | push hl 240 | push bc 241 | push af 242 | ld a,d 243 | cp 22h 244 | jr nz,JUMP_OLD 245 | cp e 246 | jr nz,JUMP_OLD 247 | 248 | ;Check API ID 249 | 250 | ld hl,UNAPI_ID 251 | ld de,ARG 252 | LOOP: ld a,(de) 253 | call TOUPPER 254 | cp (hl) 255 | jr nz,JUMP_OLD2 256 | inc hl 257 | inc de 258 | or a 259 | jr nz,LOOP 260 | 261 | ;A=255: Jump to old hook 262 | 263 | pop af 264 | push af 265 | inc a 266 | jr z,JUMP_OLD2 267 | 268 | ;A=0: B=B+1 and jump to old hook 269 | 270 | pop af 271 | pop bc 272 | or a 273 | jr nz,DO_EXTBIO2 274 | inc b 275 | pop hl 276 | ld de,2222h 277 | ret 278 | DO_EXTBIO2: 279 | 280 | ;A=1: Return A=Slot, B=Segment, HL=UNAPI entry address 281 | 282 | dec a 283 | jr nz,DO_EXTBIO3 284 | pop hl 285 | xor a 286 | ld ix,GSLOT1 287 | call CALBNK 288 | ld b,0FFh 289 | ld hl,DRV_DIRECT0 290 | ld de,2222h 291 | exx 292 | ld d,0 ;D'=0 --> don't execute old hook 293 | exx 294 | ret 295 | 296 | ;A>1: A=A-1, and jump to old hook 297 | 298 | DO_EXTBIO3: ;A=A-1 already done 299 | pop hl 300 | ld de,2222h 301 | ret 302 | 303 | ;--- Jump here to execute old EXTBIO code 304 | 305 | JUMP_OLD2: 306 | ld de,2222h 307 | JUMP_OLD: ;Assumes "push hl,bc,af" done 308 | pop af 309 | pop bc 310 | pop hl 311 | ret 312 | 313 | UNAPI_ID: 314 | db "SIMPLE_MATH",0 315 | 316 | TOUPPER: 317 | cp "a" 318 | ret c 319 | cp "z"+1 320 | ret nc 321 | and 0DFh 322 | ret 323 | 324 | 325 | ;================================ 326 | ;=== UNAPI ENTRY POINT CODE === 327 | ;================================ 328 | 329 | UNAPI_ENTRY: 330 | push hl 331 | push af 332 | ld hl,FN_TABLE 333 | bit 7,a 334 | 335 | if MAX_IMPFN >= 128 336 | 337 | jr z,IS_STANDARD 338 | ld hl,IMPFN_TABLE 339 | and 01111111b 340 | cp MAX_IMPFN-128 341 | jr z,OK_FNUM 342 | jr nc,UNDEFINED 343 | IS_STANDARD: 344 | 345 | else 346 | 347 | jr nz,UNDEFINED 348 | 349 | endif 350 | 351 | cp MAX_FN 352 | jr z,OK_FNUM 353 | jr nc,UNDEFINED 354 | 355 | OK_FNUM: 356 | add a,a 357 | push de 358 | ld e,a 359 | ld d,0 360 | add hl,de 361 | pop de 362 | 363 | ld a,(hl) 364 | inc hl 365 | ld h,(hl) 366 | ld l,a 367 | 368 | pop af 369 | ex (sp),hl 370 | ret 371 | 372 | ;--- Undefined function: return with registers unmodified 373 | 374 | UNDEFINED: 375 | pop af 376 | pop hl 377 | ret 378 | 379 | 380 | ;========================================= 381 | ;=== UNAPI FUNCTIONS ADDRESSES TABLE === 382 | ;========================================= 383 | 384 | ;TODO: Adjust for the routines of your implementation 385 | 386 | ;--- Standard routines addresses table 387 | 388 | FN_TABLE: 389 | FN_0: dw FN_INFO 390 | FN_1: dw FN_ADD 391 | FN_2: dw FN_MULT 392 | 393 | 394 | ;--- Implementation-specific routines addresses table 395 | 396 | if MAX_IMPFN >= 128 397 | 398 | IMPFN_TABLE: 399 | FN_128: dw FN_DUMMY 400 | 401 | endif 402 | 403 | 404 | ;============================== 405 | ;=== UNAPI FUNCTIONS CODE === 406 | ;============================== 407 | 408 | ;--- Mandatory routine 0: return API information 409 | ; Input: A = 0 410 | ; Output: HL = Descriptive string for this implementation, on this slot, zero terminated 411 | ; DE = API version supported, D.E 412 | ; BC = This implementation version, B.C. 413 | ; A = 0 and Cy = 0 414 | ; 415 | ; REMEMBER: The API identifier string must be visible at APIINFO address in bank 0 416 | 417 | FN_INFO: 418 | ld bc,256*ROM_V_P+ROM_V_S 419 | ld de,256*API_V_P+API_V_S 420 | ld hl,APIINFO 421 | xor a 422 | ret 423 | 424 | ;TODO: Replace the FN_* routines below with the appropriate routines for your implementation 425 | 426 | ;--- Sample routine 1: adds two 8-bit numbers 427 | ; Input: E, L = Numbers to add 428 | ; Output: HL = Result 429 | 430 | FN_ADD: 431 | ld h,0 432 | ld d,0 433 | add hl,de 434 | ret 435 | 436 | 437 | ;--- Sample routine 2: multiplies two 8-bit numbers 438 | ; Input: E, L = Numbers to multiply 439 | ; Output: HL = Result 440 | 441 | FN_MULT: 442 | ld b,e 443 | ld e,l 444 | ld d,0 445 | ld hl,0 446 | MULT_LOOP: 447 | add hl,de 448 | djnz MULT_LOOP 449 | ret 450 | 451 | 452 | ;======================================= 453 | ;=== BASIC CALL STATEMENTS HANDLER === 454 | ;======================================= 455 | 456 | ;Implements CALL SAY("whatever"), which just prints back whatever string is supplied. 457 | 458 | DRV_BASSTAT: 459 | 460 | push hl ;BASIC program pointer, must be kept up to date! 461 | 462 | ;--- Check if it's our command 463 | 464 | ld hl,PROCNM 465 | ld de,SAY_S 466 | call COMP_STR 467 | 468 | pop hl 469 | scf 470 | ret nz ;Not our command 471 | 472 | ;--- Extract next char, throw "Syntax error" if it's not '(' 473 | 474 | call GETCHAR 475 | cp "(" 476 | ld ix,SYNERR 477 | jp nz,DO_CALBAS 478 | 479 | ;--- Extract argument and check if it's a string, throw "Type mismatch" if not 480 | 481 | ld ix,FRMEVL 482 | call DO_CALBAS ;Will throw "Syntax error" if parameter is missing 483 | 484 | ld a,(VALTYP) 485 | cp 3 ;Is it a string? 486 | ld ix,TYPEM 487 | jp nz,DO_CALBAS 488 | 489 | ;--- Print "You said..." 490 | 491 | ld de,YOUSAID_S 492 | call PRINTZ 493 | 494 | ;--- Get the string pointer to DE and the string length to B, it's a bit tricky: 495 | ; DAC+2 contains a pointer to a 3 byte area which in turn 496 | ; contains the string length (1 byte) and a pointer to the string (2 bytes). 497 | 498 | ex de,hl 499 | 500 | ld hl,DAC+2 501 | ld a,(hl) 502 | inc hl 503 | ld h,(hl) 504 | ld l,a ;HL=pinter to (length, string pointer) 505 | 506 | ld b,(hl) 507 | inc hl 508 | ld a,(hl) 509 | inc hl 510 | ld h,(hl) 511 | ld l,a ;B=length, HL=string pointer 512 | 513 | ex de,hl 514 | 515 | ;--- Print the string passed as parameter 516 | 517 | call PRINTB 518 | 519 | ;--- If there were more parameters, we would extract the ',' with GETCHAR, 520 | ; then we would extract the parameter with FRMEVL, and so on. 521 | ; Since there aren't, we just extract the final ')'. 522 | 523 | call GETCHAR 524 | cp ")" 525 | ld ix,SYNERR 526 | jp nz,DO_CALBAS 527 | 528 | ;--- The end, note that HL points to the BASIC program after our CALL statement 529 | 530 | or a ;Cy must be 0 531 | ret 532 | 533 | 534 | ;======================================================= 535 | ;=== SUBROUTINES FOR BASIC CALL STATEMENTS HANDLER === 536 | ;======================================================= 537 | 538 | 539 | ;--- Extract the next character from the BASIC program 540 | 541 | ;Note that CHRGTR requires HL to point BEFORE the character to be extracted, 542 | ;but FRMEVL requires HL to point exactly to the expression to evaluate. 543 | ;That's why we 'dec hl' before and 'inc hl' after doing the call to CHRGTR. 544 | 545 | GETCHAR: 546 | dec hl 547 | ld ix,CHRGTR 548 | call DO_CALBAS 549 | inc hl 550 | ret 551 | 552 | ;--- Invoke routine in the BASIC interpreter, IX = routine address 553 | 554 | DO_CALBAS: 555 | push ix 556 | ld ix,CALBAS 557 | ld (CODE_ADD),ix 558 | pop ix 559 | jp CALLB0 560 | 561 | ;--- Compare zero terminated strings at HL and DE 562 | ; Returns Z if they are equal, NZ otherwise 563 | 564 | COMP_STR: 565 | ld a,(de) 566 | cp (hl) 567 | ret nz 568 | inc hl 569 | inc de 570 | or a 571 | jr nz,COMP_STR 572 | ret 573 | 574 | ;--- Print zero-terminated string pointed by DE 575 | 576 | PRINTZ: 577 | ld a,(de) 578 | or a 579 | ret z 580 | call CHPUT 581 | inc de 582 | jr PRINTZ 583 | 584 | ;--- Print string with length B pointed by DE 585 | 586 | PRINTB: 587 | ld a,(de) 588 | call CHPUT 589 | inc de 590 | djnz PRINTB 591 | ret 592 | 593 | SAY_S: db "SAY",0 594 | YOUSAID_S: db "You said: ",0 595 | 596 | ;----------------------------------------------------------------------------- 597 | ; 598 | ; End of the driver code 599 | 600 | DRV_END: 601 | 602 | ;ds 3FD0h-(DRV_END-DRV_START) 603 | 604 | end 605 | -------------------------------------------------------------------------------- /examples/unapi-ram.asm: -------------------------------------------------------------------------------- 1 | ;--- Sample implementation of a MSX-UNAPI specification that installs in mapped RAM 2 | ; By Konamiman, 5-2019 3 | ; 4 | ; This code implements a sample mathematical specification, "SIMPLE_MATH", 5 | ; which has just two functions: 6 | ; Function 1: Returns HL = L + E 7 | ; Function 2: Returns HL = L * E 8 | ; 9 | ; The code installs on a mapped RAM segment. 10 | ; The RAM helper must have been previously installed. 11 | ; 12 | ; You can compile it with sjasm (https://github.com/Konamiman/Sjasm/releases): 13 | ; sjasm unapi-ram.asm math.com 14 | ; The resulting file is a MSX-DOS .COM program that installs the implementation. 15 | ; 16 | ; Search for "TODO" comments for what to change/extend when creating your own implementation. 17 | ; 18 | ; Optional improvements (up to you): 19 | ; - Install a RAM helper if none is detected. 20 | ; - Let the user choose the install segment in DOS 1. 21 | ; - Add code for uninstallation. 22 | 23 | 24 | ;******************* 25 | ;*** CONSTANTS *** 26 | ;******************* 27 | 28 | ;--- System variables and routines 29 | 30 | _TERM0: equ 00h 31 | _STROUT: equ 09h 32 | 33 | ENASLT: equ 0024h 34 | EXTBIO: equ 0FFCAh 35 | ARG: equ 0F847h 36 | 37 | ;--- API version and implementation version 38 | 39 | ;TODO: Adjust for your implementation 40 | 41 | API_V_P: equ 1 42 | API_V_S: equ 0 43 | ROM_V_P: equ 1 44 | ROM_V_S: equ 1 45 | 46 | ;--- Maximum number of available standard and implementation-specific function numbers 47 | 48 | ;TODO: Adjust for your implementation 49 | 50 | ;Must be 0 to 127 51 | MAX_FN: equ 2 52 | 53 | ;Must be either zero (if no implementation-specific functions available), or 128 to 254 54 | MAX_IMPFN: equ 0 55 | 56 | 57 | ;*************************** 58 | ;*** INSTALLATION CODE *** 59 | ;*************************** 60 | 61 | org 100h 62 | 63 | ;--- Show welcome message 64 | 65 | ld de,WELCOME_S 66 | ld c,_STROUT 67 | call 5 68 | 69 | ;--- Locate the RAM helper, terminate with error if not installed 70 | 71 | ld de,2222h 72 | ld hl,0 73 | ld a,0FFh 74 | call EXTBIO 75 | ld a,h 76 | or l 77 | jr nz,HELPER_OK 78 | 79 | ld de,NOHELPER_S 80 | ld c,_STROUT 81 | call 5 82 | ld c,_TERM0 83 | jp 5 84 | HELPER_OK: 85 | ld (HELPER_ADD),hl 86 | ld (MAPTAB_ADD),bc 87 | 88 | ;--- Check if we are already installed. 89 | ; Do this by searching all the SIMPLE_MATH 90 | ; implementations installed, and comparing 91 | ; the implementation name of each one with 92 | ; our implementation name. 93 | 94 | ;* Copy the implementation identifier to ARG 95 | 96 | ld hl,UNAPI_ID-SEG_CODE_START+SEG_CODE 97 | ld de,ARG 98 | ld bc,UNAPI_ID_END-UNAPI_ID 99 | ldir 100 | 101 | ;* Obtain the number of installed implementations 102 | 103 | ld de,2222h 104 | xor a 105 | ld b,0 106 | call EXTBIO 107 | ld a,b 108 | or a 109 | jr z,NOT_INST 110 | 111 | ;>>> The loop for each installed implementations 112 | ; starts here, with A=implementation index 113 | 114 | IMPL_LOOP: push af 115 | 116 | ;* Obtain the slot, segment and entry point 117 | ; for the implementation 118 | 119 | ld de,2222h 120 | call EXTBIO 121 | ld (ALLOC_SLOT),a 122 | ld a,b 123 | ld (ALLOC_SEG),a 124 | ld (IMPLEM_ENTRY),hl 125 | 126 | ;* If the implementation is in page 3 127 | ; or in ROM, skip it 128 | 129 | ld a,h 130 | and 10000000b 131 | jr nz,NEXT_IMP 132 | ld a,b 133 | cp 0FFh 134 | jr z,NEXT_IMP 135 | 136 | ;* Call the routine for obtaining 137 | ; the implementation information 138 | 139 | ld a,(ALLOC_SLOT) 140 | ld iyh,a 141 | ld a,(ALLOC_SEG) 142 | ld iyl,a 143 | ld ix,(IMPLEM_ENTRY) 144 | ld hl,(HELPER_ADD) 145 | xor a 146 | call CALL_HL ;Returns HL=name address 147 | 148 | ;* Compare the name of the implementation 149 | ; against our own name 150 | 151 | ld a,(ALLOC_SEG) 152 | ld b,a 153 | ld de,APIINFO-SEG_CODE_START+SEG_CODE 154 | ld ix,(HELPER_ADD) 155 | inc ix 156 | inc ix 157 | inc ix ;Now IX=helper routine to read from segment 158 | NAME_LOOP: ld a,(ALLOC_SLOT) 159 | push bc 160 | push de 161 | push hl 162 | push ix 163 | call CALL_IX 164 | pop ix 165 | pop hl 166 | pop de 167 | pop bc 168 | ld c,a 169 | ld a,(de) 170 | cp c 171 | jr nz,NEXT_IMP 172 | or a 173 | inc hl 174 | inc de 175 | jr nz,NAME_LOOP 176 | 177 | ;* The names match: already installed 178 | 179 | ld de,ALINST_S 180 | ld c,_STROUT 181 | call 5 182 | ld c,_TERM0 183 | jp 5 184 | 185 | ;* Names don't match: go to the next implementation 186 | 187 | NEXT_IMP: pop af 188 | dec a 189 | jr nz,IMPL_LOOP 190 | 191 | ;* No more implementations: 192 | ; continue installation process 193 | 194 | NOT_INST: 195 | 196 | ;--- Obtain the mapper support routines table, if available 197 | 198 | xor a 199 | ld de,0402h 200 | call EXTBIO 201 | or a 202 | jr nz,ALLOC_DOS2 203 | 204 | ;--- DOS 1: Use the last segment on the primary mapper 205 | 206 | ld a,2 207 | ld (MAPTAB_ENTRY_SIZE),a 208 | 209 | ld hl,(MAPTAB_ADD) 210 | ld b,(hl) 211 | inc hl 212 | ld a,(hl) 213 | jr ALLOC_OK 214 | 215 | ;--- DOS 2: Allocate a segment using mapper support routines 216 | 217 | ALLOC_DOS2: 218 | ld a,b 219 | ld (PRIM_SLOT),a 220 | ld de,ALL_SEG 221 | ld bc,15*3 222 | ldir 223 | 224 | ld de,0401h 225 | call EXTBIO 226 | ld (MAPTAB_ADD),hl 227 | 228 | ld a,8 229 | ld (MAPTAB_ENTRY_SIZE),a 230 | 231 | ld a,(PRIM_SLOT) 232 | or 00100000b ;Try primary mapper, then try others 233 | ld b,a 234 | ld a,1 ;System segment 235 | call ALL_SEG 236 | jr nc,ALLOC_OK 237 | 238 | ld de,NOFREE_S ;Terminate if no free segments available 239 | ld c,_STROUT 240 | call 5 241 | ld c,_TERM0 242 | jp 5 243 | 244 | ALLOC_OK: 245 | ld (ALLOC_SEG),a 246 | ld a,b 247 | ld (ALLOC_SLOT),a 248 | 249 | ;--- Switch segment, copy code, and setup data 250 | 251 | call GET_P1 ;Backup current segment 252 | ld (P1_SEG),a 253 | 254 | ld a,(ALLOC_SLOT) ;Switch slot and segment 255 | ld h,40h 256 | call ENASLT 257 | ld a,(ALLOC_SEG) 258 | call PUT_P1 259 | 260 | ld hl,4000h ;Clear the segment first 261 | ld de,4001h 262 | ld bc,4000h-1 263 | ld (hl),0 264 | ldir 265 | 266 | ld hl,SEG_CODE ;Copy the code to the segment 267 | ld de,4000h 268 | ld bc,SEG_CODE_END-SEG_CODE_START 269 | ldir 270 | 271 | ld hl,(ALLOC_SLOT) ;Setup slot and segment information 272 | ld (MY_SLOT),hl 273 | 274 | ;* Now backup and patch the EXTBIO hook 275 | ; so that it calls address 4000h of the allocated segment 276 | 277 | ld hl,EXTBIO 278 | ld de,OLD_EXTBIO 279 | ld bc,5 280 | ldir 281 | 282 | di 283 | ld a,0CDh ;Code for "CALL" 284 | ld (EXTBIO),a 285 | ld hl,(HELPER_ADD) 286 | ld bc,6 287 | add hl,bc ;Now HL points to segment call routine 288 | ld (EXTBIO+1),hl 289 | 290 | ld hl,(MAPTAB_ADD) 291 | ld a,(ALLOC_SLOT) 292 | ld bc,(MAPTAB_ENTRY_SIZE) 293 | ld b,0 294 | ld d,a 295 | ld e,0 ;Index on mappers table 296 | SRCHMAP: 297 | ld a,(hl) 298 | cp d 299 | jr z,MAPFND 300 | add hl,bc ;Next table entry 301 | inc e 302 | jr SRCHMAP 303 | MAPFND: 304 | ld a,e ;A = Index of slot on mappers table 305 | rrca 306 | rrca 307 | and 11000000b ;Entry point 4010h = index 0 308 | ld (EXTBIO+3),a 309 | 310 | ld a,(ALLOC_SEG) 311 | ld (EXTBIO+4),a 312 | ei 313 | 314 | ;--- Restore slot and segment, and terminate 315 | 316 | ld a,(PRIM_SLOT) 317 | ld h,40h 318 | call ENASLT 319 | ld a,(P1_SEG) 320 | call PUT_P1 321 | 322 | ld de,OK_S 323 | ld c,_STROUT 324 | call 5 325 | 326 | ld c,_TERM0 327 | jp 5 328 | 329 | ;>>> Other auxiliary code 330 | 331 | CALL_IX: jp (ix) 332 | CALL_HL: jp (hl) 333 | 334 | 335 | ;**************************************************** 336 | ;*** DATA AND STRINGS FOR THE INSTALLATION CODE *** 337 | ;**************************************************** 338 | 339 | ;--- Variables 340 | 341 | PRIM_SLOT: db 0 ;Primary mapper slot number 342 | P1_SEG: db 0 ;Segment number for TPA on page 1 343 | ALLOC_SLOT: db 0 ;Slot for the allocated segment 344 | ALLOC_SEG: db 0 ;Allocated segment 345 | HELPER_ADD: dw 0 ;Address of the RAM helper jump table 346 | MAPTAB_ADD: dw 0 ;Address of the mappers table supplied by either DOS 2 or the RAM helper 347 | MAPTAB_ENTRY_SIZE: db 0 ;Size of an entry in the mappers table: 348 | ;- 8 in DOS 2 (mappers table provided by standard mapper support routines), 349 | ;- 2 in DOS 1 (mappers table provided by the RAM helper) 350 | IMPLEM_ENTRY: dw 0 ;Entry point for implementations 351 | 352 | ;--- Mapper support routines, used at install time only 353 | 354 | ALL_SEG: ds 3 355 | FRE_SEG: ds 3 356 | RD_SEG: ds 3 357 | WR_SEG: ds 3 358 | CAL_SEG: ds 3 359 | CALLS: ds 3 360 | PUT_PH: ds 3 361 | GET_PH: ds 3 362 | PUT_P0: ds 3 363 | GET_P0: ds 3 364 | PUT_P1: jp _PUT_P1 365 | GET_P1: ld a,2 366 | ret 367 | PUT_P2: ds 3 368 | GET_P2: ds 3 369 | PUT_P3: ds 3 370 | _PUT_P1: 371 | ld (GET_P1+1),a 372 | out (0FDh),a 373 | ret 374 | 375 | ;--- Strings 376 | 377 | ;TODO: Adjust welcome text for your implementation 378 | 379 | WELCOME_S: 380 | db "UNAPI Sample RAM implementation 1.0 (SIMPLE_MATH)",13,10 381 | db "(c) 2019 by Konamiman",13,10 382 | db 13,10 383 | db "$" 384 | 385 | NOHELPER_S: 386 | db "*** ERROR: No UNAPI RAM helper is installed",13,10,"$" 387 | 388 | NOMAPPER_S: 389 | db "*** ERROR: No mapped RAM found",13,10,"$" 390 | 391 | NOFREE_S: 392 | db "*** ERROR: Could not allocate any RAM segment",13,10,"$" 393 | 394 | OK_S: db "Installed. Have fun!",13,10,"$" 395 | 396 | ALINST_S: 397 | db "*** Already installed.",13,10,"$" 398 | 399 | 400 | ;********************************************* 401 | ;*** CODE TO BE INSTALLED ON RAM SEGMENT *** 402 | ;********************************************* 403 | 404 | SEG_CODE: 405 | org 4000h 406 | SEG_CODE_START: 407 | 408 | 409 | ;=============================== 410 | ;=== EXTBIO hook execution === 411 | ;=============================== 412 | 413 | ;>>> Note that this code starts exactly at address 4000h 414 | 415 | DO_EXTBIO: 416 | push hl 417 | push bc 418 | push af 419 | ld a,d 420 | cp 22h 421 | jr nz,JUMP_OLD 422 | cp e 423 | jr nz,JUMP_OLD 424 | 425 | ;Check API ID 426 | 427 | ld hl,UNAPI_ID 428 | ld de,ARG 429 | LOOP: ld a,(de) 430 | call TOUPPER 431 | cp (hl) 432 | jr nz,JUMP_OLD2 433 | inc hl 434 | inc de 435 | or a 436 | jr nz,LOOP 437 | 438 | ;A=255: Jump to old hook 439 | 440 | pop af 441 | push af 442 | inc a 443 | jr z,JUMP_OLD2 444 | 445 | ;A=0: B=B+1 and jump to old hook 446 | 447 | pop af 448 | pop bc 449 | or a 450 | jr nz,DO_EXTBIO2 451 | inc b 452 | pop hl 453 | ld de,2222h 454 | jp OLD_EXTBIO 455 | DO_EXTBIO2: 456 | 457 | ;A=1: Return A=Slot, B=Segment, HL=UNAPI entry address 458 | 459 | dec a 460 | jr nz,DO_EXTBIO3 461 | pop hl 462 | ld a,(MY_SEG) 463 | ld b,a 464 | ld a,(MY_SLOT) 465 | ld hl,UNAPI_ENTRY 466 | ld de,2222h 467 | ret 468 | 469 | ;A>1: A=A-1, and jump to old hook 470 | 471 | DO_EXTBIO3: ;A=A-1 already done 472 | pop hl 473 | ld de,2222h 474 | jp OLD_EXTBIO 475 | 476 | 477 | ;--- Jump here to execute old EXTBIO code 478 | 479 | JUMP_OLD2: 480 | ld de,2222h 481 | JUMP_OLD: ;Assumes "push hl,bc,af" done 482 | pop af 483 | pop bc 484 | pop hl 485 | 486 | ;Old EXTBIO hook contents is here 487 | ;(it is setup at installation time) 488 | 489 | OLD_EXTBIO: 490 | ds 5 491 | 492 | 493 | ;==================================== 494 | ;=== Functions entry point code === 495 | ;==================================== 496 | 497 | UNAPI_ENTRY: 498 | push hl 499 | push af 500 | ld hl,FN_TABLE 501 | bit 7,a 502 | 503 | if MAX_IMPFN >= 128 504 | 505 | jr z,IS_STANDARD 506 | ld hl,IMPFN_TABLE 507 | and 01111111b 508 | cp MAX_IMPFN-128 509 | jr z,OK_FNUM 510 | jr nc,UNDEFINED 511 | IS_STANDARD: 512 | 513 | else 514 | 515 | jr nz,UNDEFINED 516 | 517 | endif 518 | 519 | cp MAX_FN 520 | jr z,OK_FNUM 521 | jr nc,UNDEFINED 522 | 523 | OK_FNUM: 524 | add a,a 525 | push de 526 | ld e,a 527 | ld d,0 528 | add hl,de 529 | pop de 530 | 531 | ld a,(hl) 532 | inc hl 533 | ld h,(hl) 534 | ld l,a 535 | 536 | pop af 537 | ex (sp),hl 538 | ret 539 | 540 | ;--- Undefined function: return with registers unmodified 541 | 542 | UNDEFINED: 543 | pop af 544 | pop hl 545 | ret 546 | 547 | 548 | ;=================================== 549 | ;=== Functions addresses table === 550 | ;=================================== 551 | 552 | ;TODO: Adjust for the routines of your implementation 553 | 554 | ;--- Standard routines addresses table 555 | 556 | FN_TABLE: 557 | FN_0: dw FN_INFO 558 | FN_1: dw FN_ADD 559 | FN_2: dw FN_MULT 560 | 561 | 562 | ;--- Implementation-specific routines addresses table 563 | 564 | if MAX_IMPFN >= 128 565 | 566 | IMPFN_TABLE: 567 | FN_128: dw FN_DUMMY 568 | 569 | endif 570 | 571 | 572 | ;======================== 573 | ;=== Functions code === 574 | ;======================== 575 | 576 | ;--- Mandatory routine 0: return API information 577 | ; Input: A = 0 578 | ; Output: HL = Descriptive string for this implementation, on this slot, zero terminated 579 | ; DE = API version supported, D.E 580 | ; BC = This implementation version, B.C. 581 | ; A = 0 and Cy = 0 582 | 583 | FN_INFO: 584 | ld bc,256*ROM_V_P+ROM_V_S 585 | ld de,256*API_V_P+API_V_S 586 | ld hl,APIINFO 587 | xor a 588 | ret 589 | 590 | ;TODO: Replace the FN_* routines below with the appropriate routines for your implementation 591 | 592 | ;--- Sample routine 1: adds two 8-bit numbers 593 | ; Input: E, L = Numbers to add 594 | ; Output: HL = Result 595 | 596 | FN_ADD: 597 | ld h,0 598 | ld d,0 599 | add hl,de 600 | ret 601 | 602 | 603 | ;--- Sample routine 2: multiplies two 8-bit numbers 604 | ; Input: E, L = Numbers to multiply 605 | ; Output: HL = Result 606 | 607 | FN_MULT: 608 | ld b,e 609 | ld e,l 610 | ld d,0 611 | ld hl,0 612 | MULT_LOOP: 613 | add hl,de 614 | djnz MULT_LOOP 615 | ret 616 | 617 | 618 | ;============================ 619 | ;=== Auxiliary routines === 620 | ;============================ 621 | 622 | ;--- Convert a character to upper-case if it is a lower-case letter 623 | 624 | TOUPPER: 625 | cp "a" 626 | ret c 627 | cp "z"+1 628 | ret nc 629 | and 0DFh 630 | ret 631 | 632 | 633 | ;============================ 634 | ;=== UNAPI related data === 635 | ;============================ 636 | 637 | ;This data is setup at installation time 638 | 639 | MY_SLOT: db 0 640 | MY_SEG: db 0 641 | 642 | ;TODO: Adjust this data for your implementation 643 | 644 | ;--- Specification identifier (up to 15 chars) 645 | 646 | UNAPI_ID: 647 | db "SIMPLE_MATH",0 648 | UNAPI_ID_END: 649 | 650 | ;--- Implementation name (up to 63 chars and zero terminated) 651 | 652 | APIINFO: 653 | db "Konamiman's RAM implementation of SIMPLE_MATH UNAPI",0 654 | 655 | 656 | SEG_CODE_END: 657 | -------------------------------------------------------------------------------- /examples/unapi-rom.asm: -------------------------------------------------------------------------------- 1 | ;--- Sample implementation of a MSX-UNAPI specification in ROM 2 | ; By Konamiman, 5-2019 3 | ; 4 | ; This code implements a sample mathematical specification, "SIMPLE_MATH", 5 | ; which has just two functions: 6 | ; Function 1: Returns HL = L + E 7 | ; Function 2: Returns HL = L * E 8 | ; 9 | ; You can compile it with sjasm (https://github.com/Konamiman/Sjasm/releases): 10 | ; sjasm unapi-rom.asm math.rom 11 | ; 12 | ; Search for "TODO" comments for what to change/extend when creating your own implementation. 13 | 14 | ;******************* 15 | ;*** CONSTANTS *** 16 | ;******************* 17 | 18 | ;--- System variables and routines 19 | 20 | CHPUT: equ 00A2h 21 | HOKVLD: equ 0FB20h 22 | EXPTBL: equ 0FCC1h 23 | EXTBIO: equ 0FFCAh 24 | SLTWRK: equ 0FD09h 25 | ARG: equ 0F847h 26 | 27 | ;--- API version and implementation version 28 | 29 | ;TODO: Adjust for your implementation 30 | 31 | API_V_P: equ 1 32 | API_V_S: equ 0 33 | ROM_V_P: equ 1 34 | ROM_V_S: equ 0 35 | 36 | ;--- Maximum number of available standard and implementation-specific function numbers 37 | 38 | ;TODO: Adjust for your implementation 39 | 40 | ;Must be 0 to 127 41 | MAX_FN: equ 2 42 | 43 | ;Must be either zero (if no implementation-specific functions available), or 128 to 254 44 | MAX_IMPFN: equ 0 45 | 46 | 47 | ;******************************************** 48 | ;*** ROM HEADER AND INITIALIZATION CODE *** 49 | ;******************************************** 50 | 51 | org 4000h 52 | 53 | ;--- ROM header 54 | 55 | db "AB" 56 | dw INIT 57 | ds 12 58 | 59 | INIT: 60 | ;--- Initialize EXTBIO hook if necessary 61 | 62 | ld a,(HOKVLD) 63 | bit 0,a 64 | jr nz,OK_INIEXTB 65 | 66 | ld hl,EXTBIO 67 | ld de,EXTBIO+1 68 | ld bc,5-1 69 | ld (hl),0C9h ;code for RET 70 | ldir 71 | 72 | or 1 73 | ld (HOKVLD),a 74 | OK_INIEXTB: 75 | 76 | ;--- Save previous EXTBIO hook 77 | 78 | call GETSLT 79 | call GETWRK 80 | ex de,hl 81 | ld hl,EXTBIO 82 | ld bc,5 83 | ldir 84 | 85 | ;--- Patch EXTBIO hook 86 | 87 | di 88 | ld a,0F7h ;code for "RST 30h" 89 | ld (EXTBIO),a 90 | call GETSLT 91 | ld (EXTBIO+1),a 92 | ld hl,DO_EXTBIO 93 | ld (EXTBIO+2),hl 94 | ei 95 | 96 | ;>>> UNAPI initialization finished, now perform 97 | ; other ROM initialization tasks. 98 | 99 | ROM_INIT: 100 | 101 | ;TODO: extend (or replace) with other initialization code as needed by your implementation 102 | 103 | ;--- Show informative message 104 | 105 | ld hl,INITMSG 106 | PRINT_LOOP: 107 | ld a,(hl) 108 | or a 109 | jp z,INIT2 110 | call CHPUT 111 | inc hl 112 | jr PRINT_LOOP 113 | INIT2: 114 | 115 | ret 116 | 117 | 118 | ;******************************* 119 | ;*** EXTBIO HOOK EXECUTION *** 120 | ;******************************* 121 | 122 | DO_EXTBIO: 123 | push hl 124 | push bc 125 | push af 126 | ld a,d 127 | cp 22h 128 | jr nz,JUMP_OLD 129 | cp e 130 | jr nz,JUMP_OLD 131 | 132 | ;Check API ID 133 | 134 | ld hl,UNAPI_ID 135 | ld de,ARG 136 | LOOP: ld a,(de) 137 | call TOUPPER 138 | cp (hl) 139 | jr nz,JUMP_OLD2 140 | inc hl 141 | inc de 142 | or a 143 | jr nz,LOOP 144 | 145 | ;A=255: Jump to old hook 146 | 147 | pop af 148 | push af 149 | inc a 150 | jr z,JUMP_OLD2 151 | 152 | ;A=0: B=B+1 and jump to old hook 153 | 154 | call GETSLT 155 | call GETWRK 156 | pop af 157 | pop bc 158 | or a 159 | jr nz,DO_EXTBIO2 160 | inc b 161 | ex (sp),hl 162 | ld de,2222h 163 | ret 164 | DO_EXTBIO2: 165 | 166 | ;A=1: Return A=Slot, B=Segment, HL=UNAPI entry address 167 | 168 | dec a 169 | jr nz,DO_EXTBIO3 170 | pop hl 171 | call GETSLT 172 | ld b,0FFh 173 | ld hl,UNAPI_ENTRY 174 | ld de,2222h 175 | ret 176 | 177 | ;A>1: A=A-1, and jump to old hook 178 | 179 | DO_EXTBIO3: ;A=A-1 already done 180 | ex (sp),hl 181 | ld de,2222h 182 | ret 183 | 184 | ;--- Jump here to execute old EXTBIO code 185 | 186 | JUMP_OLD2: 187 | ld de,2222h 188 | JUMP_OLD: ;Assumes "push hl,bc,af" done 189 | push de 190 | call GETSLT 191 | call GETWRK 192 | pop de 193 | pop af 194 | pop bc 195 | ex (sp),hl 196 | ret 197 | 198 | 199 | ;************************************ 200 | ;*** FUNCTIONS ENTRY POINT CODE *** 201 | ;************************************ 202 | 203 | UNAPI_ENTRY: 204 | push hl 205 | push af 206 | ld hl,FN_TABLE 207 | bit 7,a 208 | 209 | if MAX_IMPFN >= 128 210 | 211 | jr z,IS_STANDARD 212 | ld hl,IMPFN_TABLE 213 | and 01111111b 214 | cp MAX_IMPFN-128 215 | jr z,OK_FNUM 216 | jr nc,UNDEFINED 217 | IS_STANDARD: 218 | 219 | else 220 | 221 | jr nz,UNDEFINED 222 | 223 | endif 224 | 225 | cp MAX_FN 226 | jr z,OK_FNUM 227 | jr nc,UNDEFINED 228 | 229 | OK_FNUM: 230 | add a,a 231 | push de 232 | ld e,a 233 | ld d,0 234 | add hl,de 235 | pop de 236 | 237 | ld a,(hl) 238 | inc hl 239 | ld h,(hl) 240 | ld l,a 241 | 242 | pop af 243 | ex (sp),hl 244 | ret 245 | 246 | ;--- Undefined function: return with registers unmodified 247 | 248 | UNDEFINED: 249 | pop af 250 | pop hl 251 | ret 252 | 253 | 254 | ;*********************************** 255 | ;*** FUNCTIONS ADDRESSES TABLE *** 256 | ;*********************************** 257 | 258 | ;TODO: Adjust for the routines of your implementation 259 | 260 | ;--- Standard routines addresses table 261 | 262 | FN_TABLE: 263 | FN_0: dw FN_INFO 264 | FN_1: dw FN_ADD 265 | FN_2: dw FN_MULT 266 | 267 | 268 | ;--- Implementation-specific routines addresses table 269 | 270 | if MAX_IMPFN >= 128 271 | 272 | IMPFN_TABLE: 273 | FN_128: dw FN_DUMMY 274 | 275 | endif 276 | 277 | 278 | ;************************ 279 | ;*** FUNCTIONS CODE *** 280 | ;************************ 281 | 282 | ;--- Mandatory routine 0: return API information 283 | ; Input: A = 0 284 | ; Output: HL = Descriptive string for this implementation, on this slot, zero terminated 285 | ; DE = API version supported, D.E 286 | ; BC = This implementation version, B.C. 287 | ; A = 0 and Cy = 0 288 | 289 | FN_INFO: 290 | ld bc,256*ROM_V_P+ROM_V_S 291 | ld de,256*API_V_P+API_V_S 292 | ld hl,APIINFO 293 | xor a 294 | ret 295 | 296 | ;TODO: Replace the FN_* routines below with the appropriate routines for your implementation 297 | 298 | ;--- Sample routine 1: adds two 8-bit numbers 299 | ; Input: E, L = Numbers to add 300 | ; Output: HL = Result 301 | 302 | FN_ADD: 303 | ld h,0 304 | ld d,0 305 | add hl,de 306 | ret 307 | 308 | 309 | ;--- Sample routine 2: multiplies two 8-bit numbers 310 | ; Input: E, L = Numbers to multiply 311 | ; Output: HL = Result 312 | 313 | FN_MULT: 314 | ld b,e 315 | ld e,l 316 | ld d,0 317 | ld hl,0 318 | MULT_LOOP: 319 | add hl,de 320 | djnz MULT_LOOP 321 | ret 322 | 323 | 324 | ;**************************** 325 | ;*** AUXILIARY ROUTINES *** 326 | ;**************************** 327 | 328 | ;--- Get slot connected on page 1 329 | ; Input: - 330 | ; Output: A = Slot number 331 | ; Modifies: AF, HL, E, BC 332 | 333 | GETSLT: 334 | di 335 | exx 336 | in a,(0A8h) 337 | ld e,a 338 | and 00001100b 339 | sra a 340 | sra a 341 | ld c,a ;C = Slot 342 | ld b,0 343 | ld hl,EXPTBL 344 | add hl,bc 345 | bit 7,(hl) 346 | jr z,NOEXP1 347 | EXP1: inc hl 348 | inc hl 349 | inc hl 350 | inc hl 351 | ld a,(hl) 352 | and 00001100b 353 | or c 354 | or 80h 355 | ld c,a 356 | NOEXP1: ld a,c 357 | exx 358 | ei 359 | ret 360 | 361 | 362 | ;--- Obtain slot work area (8 bytes) on SLTWRK 363 | ; Input: A = Slot number 364 | ; Output: HL = Work area address 365 | ; Modifies: AF, BC 366 | 367 | GETWRK: 368 | ld b,a 369 | rrca 370 | rrca 371 | rrca 372 | and 01100000b 373 | ld c,a ;C = Slot * 32 374 | ld a,b 375 | rlca 376 | and 00011000b ;A = Subslot * 8 377 | or c 378 | ld c,a 379 | ld b,0 380 | ld hl,SLTWRK 381 | add hl,bc 382 | ret 383 | 384 | 385 | ;--- Convert a character to upper-case if it is a lower-case letter 386 | 387 | TOUPPER: 388 | cp "a" 389 | ret c 390 | cp "z"+1 391 | ret nc 392 | and 0DFh 393 | ret 394 | 395 | 396 | ;************** 397 | ;*** DATA *** 398 | ;************** 399 | 400 | ;TODO: Adjust this data for your implementation 401 | 402 | ;--- Specification identifier (up to 15 chars) 403 | 404 | UNAPI_ID: 405 | db "SIMPLE_MATH",0 406 | 407 | ;--- Implementation identifier (up to 63 chars and zero terminated) 408 | 409 | APIINFO: 410 | db "Konamiman's ROM implementation of SIMPLE_MATH UNAPI",0 411 | 412 | ;--- Other data 413 | 414 | INITMSG: 415 | db 13,10,"UNAPI Sample ROM 1.0 (SIMPLE_MATH)",13,10 416 | db "(c) 2019 by Konamiman",13,10 417 | db 13,10 418 | db 0 419 | 420 | ds 0C000h-$ ;Padding to make a 32K ROM 421 | -------------------------------------------------------------------------------- /examples/unapi-specless.asm: -------------------------------------------------------------------------------- 1 | ;--- Sample MSX-UNAPI specificationless application 2 | ; By Konami Man, 5-2019 3 | ; 4 | ; This code implements a simple TSR that makes the CAPS led to blink, 5 | ; by hooking on the timer interrupt. It offers just one functions: 6 | ; Function 1: Set the blink rate 7 | ; 8 | ; The code installs on a mapped RAM segment. 9 | ; The RAM helper must have been previously installed. 10 | ; You can compile it with sjasm (https://github.com/Konamiman/Sjasm/releases): 11 | ; sjasm unapi-specless.asm blink.com 12 | ; The resulting file is a MSX-DOS .COM program that installs the tsr. 13 | ; 14 | ; Search for "TODO" comments for what to change/extend when creating your own implementation. 15 | ; 16 | ; Optional improvements (up to you): 17 | ; - Install a RAM helper if none is detected. 18 | ; - Provide a command line option to change the blink rate. 19 | ; - Let the user choose the install segment in DOS 1. 20 | ; - Add code for uninstallation. 21 | 22 | 23 | ;******************* 24 | ;*** CONSTANTS *** 25 | ;******************* 26 | 27 | ;--- System variables and routines 28 | 29 | _TERM0: equ 00h 30 | _STROUT: equ 09h 31 | 32 | ENDTPA: equ 0006h 33 | ENASLT: equ 0024h 34 | EXTBIO: equ 0FFCAh 35 | ARG: equ 0F847h 36 | H_TIMI: equ 0FD9Fh 37 | 38 | ;--- Application version 39 | 40 | ;TODO: Adjust for your program 41 | 42 | ROM_V_P: equ 1 43 | ROM_V_S: equ 0 44 | 45 | ;--- Maximum function number available 46 | 47 | ;TODO: Adjust for your program 48 | 49 | ;Must be 1 to 254, 50 | ;or 0 if no extra functions are provided 51 | MAX_FN: equ 1 52 | 53 | 54 | ;*************************** 55 | ;*** INSTALLATION CODE *** 56 | ;*************************** 57 | 58 | org 100h 59 | 60 | ;--- Show welcome message 61 | 62 | ld de,WELCOME_S 63 | ld c,_STROUT 64 | call 5 65 | 66 | ;--- Locate the RAM helper, terminate with error if not installed 67 | 68 | ld de,2222h 69 | ld hl,0 70 | ld a,0FFh 71 | call EXTBIO 72 | ld a,h 73 | or l 74 | jr nz,HELPER_OK 75 | 76 | ld de,NOHELPER_S 77 | ld c,_STROUT 78 | call 5 79 | ld c,_TERM0 80 | jp 5 81 | HELPER_OK: 82 | ld (HELPER_ADD),hl 83 | ld (MAPTAB_ADD),bc 84 | 85 | ;--- Check if we are already installed. 86 | ; Do this by searching all the specificationless 87 | ; applications installed, and comparing 88 | ; the implementation name of each one with 89 | ; our implementation name. 90 | 91 | ;* Copy an empty implementation identifier to ARG 92 | 93 | xor a 94 | ld (ARG),a 95 | 96 | ;* Obtain the number of installed applications 97 | 98 | ld de,2222h 99 | xor a 100 | ld b,0 101 | call EXTBIO 102 | ld a,b 103 | or a 104 | jr z,NOT_INST 105 | 106 | ;>>> The loop for each installed application 107 | ; starts here, with A=implementation index 108 | 109 | IMPL_LOOP: push af 110 | 111 | ;* Obtain the slot, segment and entry point 112 | ; for the application 113 | 114 | ld de,2222h 115 | call EXTBIO 116 | ld (ALLOC_SLOT),a 117 | ld a,b 118 | ld (ALLOC_SEG),a 119 | ld (IMPLEM_ENTRY),hl 120 | 121 | ;* If the implementation is in page 3 122 | ; or in ROM, skip it 123 | 124 | ld a,h 125 | and 10000000b 126 | jr nz,NEXT_IMP 127 | ld a,b 128 | cp 0FFh 129 | jr z,NEXT_IMP 130 | 131 | ;* Call the routine for obtaining 132 | ; the application information 133 | 134 | ld a,(ALLOC_SLOT) 135 | ld iyh,a 136 | ld a,(ALLOC_SEG) 137 | ld iyl,a 138 | ld ix,(IMPLEM_ENTRY) 139 | ld hl,(HELPER_ADD) 140 | xor a 141 | call CALL_HL ;Returns HL=name address 142 | 143 | ;* Compare the name of the application 144 | ; against our own name 145 | 146 | ld a,(ALLOC_SEG) 147 | ld b,a 148 | ld de,APIINFO-SEG_CODE_START+SEG_CODE 149 | ld ix,(HELPER_ADD) 150 | inc ix 151 | inc ix 152 | inc ix ;Now IX=helper routine to read from segment 153 | NAME_LOOP: ld a,(ALLOC_SLOT) 154 | push bc 155 | push de 156 | push hl 157 | push ix 158 | call CALL_IX 159 | pop ix 160 | pop hl 161 | pop de 162 | pop bc 163 | ld c,a 164 | ld a,(de) 165 | cp c 166 | jr nz,NEXT_IMP 167 | or a 168 | inc hl 169 | inc de 170 | jr nz,NAME_LOOP 171 | 172 | ;* The names match: already installed 173 | 174 | ld de,ALINST_S 175 | ld c,_STROUT 176 | call 5 177 | ld c,_TERM0 178 | jp 5 179 | 180 | ;* Names don't match: go to the next application 181 | 182 | NEXT_IMP: pop af 183 | dec a 184 | jr nz,IMPL_LOOP 185 | 186 | ;* No more application: 187 | ; continue installation process 188 | 189 | NOT_INST: 190 | 191 | ;--- Obtain the mapper support routines table, if available 192 | 193 | xor a 194 | ld de,0402h 195 | call EXTBIO 196 | or a 197 | jr nz,ALLOC_DOS2 198 | 199 | ;--- DOS 1: Use the last segment on the primary mapper 200 | 201 | ld a,2 202 | ld (MAPTAB_ENTRY_SIZE),a 203 | 204 | ld hl,(MAPTAB_ADD) 205 | ld b,(hl) 206 | inc hl 207 | ld a,(hl) 208 | jr ALLOC_OK 209 | 210 | ;--- DOS 2: Allocate a segment using mapper support routines 211 | 212 | ALLOC_DOS2: 213 | ld a,b 214 | ld (PRIM_SLOT),a 215 | ld de,ALL_SEG 216 | ld bc,15*3 217 | ldir 218 | 219 | ld de,0401h 220 | call EXTBIO 221 | ld (MAPTAB_ADD),hl 222 | 223 | ld a,8 224 | ld (MAPTAB_ENTRY_SIZE),a 225 | 226 | ld a,(PRIM_SLOT) 227 | or 00100000b ;Try primary mapper, then try others 228 | ld b,a 229 | ld a,1 ;System segment 230 | call ALL_SEG 231 | jr nc,ALLOC_OK 232 | 233 | ld de,NOFREE_S ;Terminate if no free segments available 234 | ld c,_STROUT 235 | call 5 236 | ld c,_TERM0 237 | jp 5 238 | 239 | ALLOC_OK: 240 | ld (ALLOC_SEG),a 241 | ld a,b 242 | ld (ALLOC_SLOT),a 243 | 244 | ;--- Switch segment, copy code, and setup data 245 | 246 | call GET_P1 ;Backup current segment 247 | ld (P1_SEG),a 248 | 249 | ld a,(ALLOC_SLOT) ;Switch slot and segment 250 | ld h,40h 251 | call ENASLT 252 | ld a,(ALLOC_SEG) 253 | call PUT_P1 254 | 255 | ld hl,4000h ;Clear the segment first 256 | ld de,4001h 257 | ld bc,4000h-1 258 | ld (hl),0 259 | ldir 260 | 261 | ld hl,SEG_CODE ;Copy the code to the segment 262 | ld de,4000h 263 | ld bc,SEG_CODE_END-SEG_CODE_START 264 | ldir 265 | 266 | ld hl,(ALLOC_SLOT) ;Setup slot and segment information 267 | ld (MY_SLOT),hl 268 | 269 | ;* Now backup and patch the EXTBIO and H_TIMI hooks 270 | ; so that it calls the appropriate jump table entry 271 | ; on the allocated segment 272 | 273 | di 274 | 275 | ld hl,EXTBIO 276 | ld de,OLD_EXTBIO 277 | ld bc,5 278 | ldir 279 | 280 | xor a 281 | ld ix,EXTBIO 282 | call PATCH_HOOK 283 | 284 | ;TODO: Adjust for your program by patching the required hooks (EXTBIO is always required) 285 | 286 | ld hl,H_TIMI 287 | ld de,OLD_HTIMI 288 | ld bc,5 289 | ldir 290 | 291 | ld a,1 292 | ld ix,H_TIMI 293 | call PATCH_HOOK 294 | 295 | ei 296 | 297 | ;--- Restore slot and segment, and terminate 298 | 299 | ld a,(PRIM_SLOT) 300 | ld h,40h 301 | call ENASLT 302 | ld a,(P1_SEG) 303 | call PUT_P1 304 | 305 | ld de,OK_S 306 | ld c,_STROUT 307 | call 5 308 | 309 | ld c,_TERM0 310 | jp 5 311 | 312 | ;>>> Other auxiliary code 313 | 314 | CALL_IX: jp (ix) 315 | CALL_HL: jp (hl) 316 | 317 | 318 | ;--- This routine patches a hook so that 319 | ; it calls the routine with the specified index 320 | ; in the allocated segment. 321 | ; Input: A = Routine index, 0 to 63 322 | ; IX = Hook address 323 | ; ALLOC_SEG and ALLOC_SLOT set 324 | 325 | PATCH_HOOK: 326 | push af 327 | ld a,0CDh ;Code for "CALL" 328 | ld (ix),a 329 | ld hl,(HELPER_ADD) 330 | ld bc,6 331 | add hl,bc ;Now HL points to segment call routine 332 | ld (ix+1),l 333 | ld (ix+2),h 334 | 335 | ld hl,(MAPTAB_ADD) 336 | ld a,(ALLOC_SLOT) 337 | ld bc,(MAPTAB_ENTRY_SIZE) 338 | ld b,0 339 | ld d,a 340 | ld e,0 ;Index on mappers table 341 | SRCHMAP: 342 | ld a,(hl) 343 | cp d 344 | jr z,MAPFND 345 | add hl,bc ;Next table entry 346 | inc e 347 | jr SRCHMAP 348 | MAPFND: 349 | ld a,e ;A = Index of slot on mappers table 350 | rrca 351 | rrca 352 | and 11000000b 353 | pop de ;Retrieve routine index 354 | or d 355 | ld (ix+3),a 356 | 357 | ld a,(ALLOC_SEG) 358 | ld (ix+4),a 359 | ret 360 | 361 | 362 | ;**************************************************** 363 | ;*** DATA AND STRINGS FOR THE INSTALLATION CODE *** 364 | ;**************************************************** 365 | 366 | ;--- Variables 367 | 368 | PRIM_SLOT: db 0 ;Primary mapper slot number 369 | P1_SEG: db 0 ;Segment number for TPA on page 1 370 | ALLOC_SLOT: db 0 ;Slot for the allocated segment 371 | ALLOC_SEG: db 0 ;Allocated segment 372 | HELPER_ADD: dw 0 ;Address of the RAM helper jump table 373 | MAPTAB_ADD: dw 0 ;Address of the mappers table supplied by either DOS 2 or the RAM helper 374 | MAPTAB_ENTRY_SIZE: db 0 ;Size of an entry in the mappers table: 375 | ;- 8 in DOS 2 (mappers table provided by standard mapper support routines), 376 | ;- 2 in DOS 1 (mappers table provided by the RAM helper) 377 | IMPLEM_ENTRY: dw 0 ;Entry point for implementations 378 | 379 | ;--- Mapper support routines, used at install time only 380 | 381 | ALL_SEG: ds 3 382 | FRE_SEG: ds 3 383 | RD_SEG: ds 3 384 | WR_SEG: ds 3 385 | CAL_SEG: ds 3 386 | CALLS: ds 3 387 | PUT_PH: ds 3 388 | GET_PH: ds 3 389 | PUT_P0: ds 3 390 | GET_P0: ds 3 391 | PUT_P1: jp _PUT_P1 392 | GET_P1: ld a,2 393 | ret 394 | PUT_P2: ds 3 395 | GET_P2: ds 3 396 | PUT_P3: ds 3 397 | _PUT_P1: 398 | ld (GET_P1+1),a 399 | out (0FDh),a 400 | ret 401 | 402 | ;--- Strings 403 | 404 | WELCOME_S: 405 | db "CAPS blinker - an UNAPI sample specificationless application 1.0",13,10 406 | db "(c) 2019 by Konamiman",13,10 407 | db 13,10 408 | db "$" 409 | 410 | NOHELPER_S: 411 | db "*** ERROR: No UNAPI RAM helper is installed",13,10,"$" 412 | 413 | NOMAPPER_S: 414 | db "*** ERROR: No mapped RAM found",13,10,"$" 415 | 416 | NOFREE_S: 417 | db "*** ERROR: Could not allocate any RAM segment",13,10,"$" 418 | 419 | OK_S: db "Installed. Have fun!",13,10,"$" 420 | 421 | ALINST_S: 422 | db "*** Already installed.",13,10,"$" 423 | 424 | 425 | ;********************************************* 426 | ;*** CODE TO BE INSTALLED ON RAM SEGMENT *** 427 | ;********************************************* 428 | 429 | SEG_CODE: 430 | org 4000h 431 | SEG_CODE_START: 432 | 433 | ;=================== 434 | ;=== Jump table === 435 | ;=================== 436 | 437 | ;TODO: Adjust for your program according to the hooks that you have patched 438 | 439 | jp DO_EXTBIO 440 | jp DO_HTIMI 441 | 442 | 443 | ;=============================== 444 | ;=== EXTBIO hook execution === 445 | ;=============================== 446 | 447 | DO_EXTBIO: 448 | push hl 449 | push bc 450 | push af 451 | ld a,d 452 | cp 22h 453 | jr nz,JUMP_OLD 454 | cp e 455 | jr nz,JUMP_OLD 456 | 457 | ;Check API ID 458 | 459 | ld a,(ARG) 460 | or a 461 | jr nz,JUMP_OLD 462 | 463 | ;A=255: Jump to old hook 464 | 465 | pop af 466 | push af 467 | inc a 468 | jr z,JUMP_OLD 469 | 470 | ;A=0: B=B+1 and jump to old hook 471 | 472 | pop af 473 | pop bc 474 | or a 475 | jr nz,DO_EXTBIO2 476 | inc b 477 | pop hl 478 | jp OLD_EXTBIO 479 | DO_EXTBIO2: 480 | 481 | ;A=1: Return A=Slot, B=Segment, HL=UNAPI entry address 482 | 483 | dec a 484 | jr nz,DO_EXTBIO3 485 | pop hl 486 | ld a,(MY_SEG) 487 | ld b,a 488 | ld a,(MY_SLOT) 489 | ld hl,UNAPI_ENTRY 490 | ret 491 | 492 | ;A>1: A=A-1, and jump to old hook 493 | 494 | DO_EXTBIO3: ;A=A-1 already done 495 | pop hl 496 | jr OLD_EXTBIO 497 | 498 | 499 | ;--- Jump here to execute old EXTBIO code 500 | 501 | JUMP_OLD: ;Assumes "push hl,bc,af" done 502 | pop af 503 | pop bc 504 | pop hl 505 | 506 | ;Old EXTBIO hook contents is here 507 | ;(it is setup at installation time) 508 | 509 | OLD_EXTBIO: 510 | ds 5 511 | 512 | 513 | ;TODO: Adjust the DO_H* routines for your program according to the hooks that you have patched 514 | 515 | ;=============================== 516 | ;=== H_TIMI hook execution === 517 | ;=============================== 518 | 519 | DO_HTIMI: 520 | ld a,(CAPS_CNT) 521 | dec a 522 | ld (CAPS_CNT),a 523 | or a 524 | ret nz 525 | 526 | ld a,(BLINK_RATE) 527 | ld (CAPS_CNT),a 528 | 529 | ld a,(CAPS_STATE) 530 | cpl 531 | ld (CAPS_STATE),a 532 | or a 533 | jr nz,DO_CAPS_ON 534 | 535 | DO_CAPS_OFF: 536 | in a,(0AAh) 537 | or 01000000b 538 | jr DO_CAPS_END 539 | 540 | DO_CAPS_ON: 541 | in a,(0AAh) 542 | and 10111111b 543 | 544 | DO_CAPS_END: 545 | out (0AAh),a 546 | 547 | ;Old H_TIMI hook contents is here 548 | ;(it is setup at installation time) 549 | OLD_HTIMI: 550 | ds 5 551 | 552 | 553 | CAPS_STATE: ;Current state, 0=off, 255=on 554 | db 0 555 | 556 | CAPS_CNT: ;Timer interrupts counter, current value 557 | db 0 558 | 559 | BLINK_RATE: ;Blink rate, less is faster 560 | db 50 561 | 562 | 563 | ;==================================== 564 | ;=== Functions entry point code === 565 | ;==================================== 566 | 567 | UNAPI_ENTRY: 568 | push hl 569 | push af 570 | ld hl,FN_TABLE 571 | cp MAX_FN 572 | jr z,OK_FNUM 573 | jr nc,UNDEFINED 574 | 575 | OK_FNUM: 576 | add a,a 577 | push de 578 | ld e,a 579 | ld d,0 580 | add hl,de 581 | pop de 582 | 583 | ld a,(hl) 584 | inc hl 585 | ld h,(hl) 586 | ld l,a 587 | 588 | pop af 589 | ex (sp),hl 590 | ret 591 | 592 | ;--- Undefined function: return with registers unmodified 593 | 594 | UNDEFINED: 595 | pop af 596 | pop hl 597 | ret 598 | 599 | 600 | ;=================================== 601 | ;=== Functions addresses table === 602 | ;=================================== 603 | 604 | ;--- Routines addresses table 605 | 606 | ;TODO: Adjust for your program 607 | 608 | FN_TABLE: 609 | FN_0: dw FN_INFO 610 | FN_1: dw FN_SETRATE 611 | 612 | 613 | ;======================== 614 | ;=== Functions code === 615 | ;======================== 616 | 617 | ;--- Mandatory routine 0: return API information 618 | ; Input: A = 0 619 | ; Output: HL = Descriptive string for this implementation, on this slot, zero terminated 620 | ; DE = API version supported, D.E 621 | ; (always zero for specificationless applications) 622 | ; BC = This implementation version, B.C. 623 | ; A = 0 and Cy = 0 624 | 625 | FN_INFO: 626 | ld bc,256*ROM_V_P+ROM_V_S 627 | ld de,0 628 | ld hl,APIINFO 629 | xor a 630 | ret 631 | 632 | ;TODO: Adjust for the routines of your program 633 | 634 | ;--- Routine 1: set CAPS led blink rate 635 | ; Input: B = New blink rate, less is faster 636 | ; Output: - 637 | 638 | FN_SETRATE: 639 | ld a,b 640 | ld (BLINK_RATE),a 641 | ld a,1 642 | ld (CAPS_CNT),a ;Force the new rate to apply right now 643 | ret 644 | 645 | 646 | ;============================ 647 | ;=== UNAPI related data === 648 | ;============================ 649 | 650 | ;This data is setup at installation time 651 | 652 | MY_SLOT: db 0 653 | MY_SEG: db 0 654 | 655 | ;--- Implementation name (up to 63 chars and zero terminated) 656 | 657 | ;TODO: Adjust for your program 658 | 659 | APIINFO: 660 | db "Konamiman's UNAPI CAPS led blinker",0 661 | 662 | 663 | SEG_CODE_END: 664 | -------------------------------------------------------------------------------- /tools/.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.lst 3 | *.sym 4 | *.rom 5 | *.com 6 | *.dat 7 | *.rel 8 | *.lib 9 | *.map 10 | *.noi 11 | *.ihx 12 | *.lk 13 | asm.h 14 | eth.asm 15 | tcpip.asm 16 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # MSX-UNAPI tools 2 | 3 | This directory contains the source of various UNAPI related tools. Looking at these may be useful for you if you want to develop an application that uses the routines provided by an UNAPI compliant API implementation; if you just want the binaries, head to the [releases](https://github.com/Konamiman/MSX-UNAPI-specification/releases) section. 4 | 5 | * [apilist.asm](apilist.asm): This tool lists all the installed UNAPI implementations for a given specification identifier. 6 | 7 | * [ramhelpr.asm](ramhelpr.asm): This is the source for two tools: 8 | * RAMHELPR.COM: Installs an UNAPI compatible RAM helper. To be used in MSX-DOS 2 and Nextor. 9 | * MSR.COM: Installs MSX-DOS 2 compatible mapper support routines and an UNAPI compatible RAM helper. To be used in MSX-DOS 1. 10 | 11 | * [eth.c](eth.c): Control program for Ethernet UNAPI implementations. 12 | 13 | * [tcpip.c](eth.c): Control program for TCP/IP UNAPI implementations. 14 | 15 | Note that the tools written in C use [ASMLIB](https://github.com/Konamiman/MSX/tree/master/SRC/SDCC/asmlib) and also `crt0msx_msxdos_advanced`, `printf_simple` and `putchar_msxdos` from [Konamiman's MSX page](https://www.konamiman.com/msx/msx-e.html#sdcc). 16 | -------------------------------------------------------------------------------- /tools/apilist.asm: -------------------------------------------------------------------------------- 1 | ;--- UNAPI implementations lister v1.1 2 | ; By Konamiman, 2/2010 3 | ; 4 | ; You can compile it with sjasm (https://github.com/Konamiman/Sjasm/releases): 5 | ; sjasm apilist.asm apilist.com 6 | ; The resulting file is a MSX-DOS .COM program. 7 | ; 8 | ; Usage: apilist |* 9 | 10 | ;******************* 11 | ;*** CONSTANTS *** 12 | ;******************* 13 | 14 | ;--- System variables and routines 15 | 16 | _TERM0: equ 00h 17 | _CONOUT: equ 02h 18 | _STROUT: equ 09h 19 | 20 | RDSLT: equ 000Ch ;A=PEEK(Slot A, Addr HL) 21 | CALSLT: equ 001Ch ;CALL Slot IYh, Addr IX 22 | ARG: equ 0F847h 23 | EXTBIO: equ 0FFCAh 24 | 25 | 26 | ;********************** 27 | ;*** MAIN PROGRAM *** 28 | ;********************** 29 | 30 | org 100h 31 | 32 | ;--- Show welcome message 33 | 34 | ld de,WELCOME_S 35 | ld c,_STROUT 36 | call 5 37 | 38 | ;--- Put a 0 at the end of the command line 39 | ; (needed when running DOS 1) 40 | 41 | ld hl,(0080h) 42 | ld h,0 43 | ld bc,0081h 44 | add hl,bc 45 | ld (hl),0 46 | 47 | ;--- Search the parameter (skip spaces in command line) 48 | 49 | ld hl,80h 50 | SRCHPAR: 51 | inc hl 52 | ld a,(hl) 53 | cp " " 54 | jr z,SRCHPAR 55 | or a 56 | jr nz,GETPAR 57 | 58 | ;--- No parameters: show usage information 59 | ; and terminate 60 | 61 | ld de,USAGE_S 62 | ld c,_STROUT 63 | call 5 64 | ld c,_TERM0 65 | jp 5 66 | 67 | ;--- Copy the parameter to ARG 68 | 69 | GETPAR: ld de,ARG 70 | ld a,(hl) 71 | cp "*" 72 | jr z,SPECLESS 73 | ld bc,15 74 | ldir 75 | SPECLESS: 76 | ex de,hl 77 | ld (hl),0 ;To prevent paramater >15 chars 78 | 79 | ;--- Get number of installed implementations 80 | 81 | ld de,2222h 82 | xor a 83 | ld b,0 84 | call EXTBIO 85 | ld a,b 86 | ld (NUM_IMP),a 87 | 88 | ;--- Print the number of installed implementations 89 | 90 | ld de,FOUND_S ;Print "Found" 91 | ld c,_STROUT 92 | call 5 93 | 94 | ld a,(NUM_IMP) 95 | ld ix,TEMP ;Print the number 96 | call BYTE2ASC 97 | ld (ix),"$" 98 | ld de,TEMP 99 | ld c,_STROUT 100 | call 5 101 | 102 | ld a,(ARG) 103 | or a 104 | jr z,SPECLESS2 105 | 106 | ;> No specificationless application 107 | 108 | ld de,IMPLEMS_S ;Print "implementations of the" 109 | ld c,_STROUT 110 | call 5 111 | 112 | ld hl,ARG ;Print the API identifier 113 | PRINTID: 114 | ld a,(hl) 115 | or a 116 | jr z,OKPRINTID 117 | call TOUPPER 118 | ld e,a 119 | ld c,_CONOUT 120 | push hl 121 | call 5 122 | pop hl 123 | inc hl 124 | jr PRINTID 125 | OKPRINTID: 126 | 127 | ld de,API_S ;Print "API" 128 | ld c,_STROUT 129 | call 5 130 | jr OK_PRNUM 131 | 132 | ;> Specificationless application 133 | 134 | SPECLESS2: 135 | ld de,SPECLESS_S 136 | ld c,_STROUT 137 | call 5 138 | OK_PRNUM: 139 | 140 | ;--- If no implementations found, 141 | ; print a dot and terminate; otherwise 142 | ; print a colon and continue 143 | 144 | ld a,(NUM_IMP) 145 | or a 146 | jr nz,NOZEROIMP 147 | 148 | ld de,DOT_S 149 | ld c,_STROUT 150 | call 5 151 | ld c,_TERM0 152 | jp 5 153 | 154 | NOZEROIMP: 155 | ld de,COLON_S 156 | ld c,_STROUT 157 | call 5 158 | 159 | ;--- Obtain the address of the RAM helper. 160 | ; If not installed, the address will be 0, 161 | ; but we will not call it anyway since 162 | ; there will be no RAM implementations. 163 | 164 | ld de,2222h 165 | ld hl,0 166 | ld a,0FFh 167 | call EXTBIO 168 | ld (RAM_HELPER),hl 169 | 170 | ;>>> Here begins a loop for all implementations 171 | 172 | MAIN_LOOP: 173 | 174 | ;--- Obtain location of the current implementation 175 | 176 | ld a,(CUR_IMP) 177 | ld de,2222h 178 | call EXTBIO 179 | ld (IMP_SLOT),a 180 | ld a,b 181 | ld (IMP_SEG),a 182 | ld (IMP_ENTRY),hl 183 | 184 | ;--- Build code to read the implementation name 185 | 186 | ld a,(IMP_ENTRY+1) 187 | and 10000000b 188 | jr z,BLDCODE2 ;Page 3 implemenation: 189 | ld a,7Eh ;code for "ld a,(hl)" 190 | ld (READ_CODE),a 191 | jr OKBLDCODE 192 | 193 | BLDCODE2: 194 | ld a,0CDh ;code for "call" 195 | ld (READ_CODE),a 196 | ld a,(IMP_SEG) 197 | ld hl,RDSLT ;ROM implem: call to RDSLT 198 | cp 0FFh 199 | jr z,BLDCODE3 200 | ld hl,(RAM_HELPER) 201 | inc hl 202 | inc hl 203 | inc hl ;RAM implem: call to RAM helper 204 | BLDCODE3: 205 | ld (READ_CODE+1),hl 206 | OKBLDCODE: 207 | 208 | ;--- Call function 0 to obtain name and version 209 | 210 | xor a 211 | call EXE_UNAPI 212 | push bc ;Save version number 213 | 214 | ;--- Print the implementation name 215 | 216 | ld a,(IMP_SEG) 217 | ld b,a 218 | ld a,(IMP_SLOT) 219 | NAME_LOOP: 220 | push af 221 | push bc 222 | push hl 223 | READ_CODE: 224 | nop ;Will be "ld a,hl", "call RDLT" or "call HELPER+3" 225 | nop 226 | nop 227 | or a 228 | jr z,NAME_END 229 | ld e,a 230 | ld c,_CONOUT 231 | call 5 232 | pop hl 233 | pop bc 234 | pop af 235 | inc hl 236 | jr NAME_LOOP 237 | 238 | NAME_END: 239 | pop hl 240 | pop bc 241 | pop af 242 | 243 | ;--- Now print the implementation version 244 | 245 | ld ix,TEMP+2 246 | ld (ix-2)," " 247 | ld (ix-1),"v" 248 | 249 | pop bc 250 | push bc 251 | ld a,b 252 | call BYTE2ASC ;Main version number 253 | 254 | ld (ix),"." 255 | inc ix 256 | 257 | pop bc 258 | ld a,c 259 | call BYTE2ASC ;Secondary version number 260 | 261 | ld (ix),"$" 262 | ld de,TEMP 263 | ld c,_STROUT 264 | call 5 265 | 266 | ;--- Print the implementation location 267 | 268 | ld de,ON_S 269 | ld c,_STROUT 270 | call 5 271 | 272 | ;* Location is page 3: simply print "page 3" 273 | 274 | ld a,(IMP_ENTRY+1) 275 | and 10000000b 276 | jr z,NOPAGE3 277 | 278 | ld de,PAGE3_S 279 | ld c,_STROUT 280 | call 5 281 | jr PRNAME_END 282 | NOPAGE3: 283 | ;* RAM or ROM: print slot number 284 | 285 | ld de,SLOT_S 286 | ld c,_STROUT 287 | call 5 288 | 289 | ld ix,TEMP 290 | ld a,(IMP_SLOT) ;Build main slot number 291 | and 00000011b 292 | add "0" 293 | ld (ix),a 294 | inc ix 295 | 296 | ld a,(IMP_SLOT) 297 | bit 7,a 298 | jr z,BLDSLOT_END 299 | 300 | ld (ix),"-" ;If expanded, build subslot number 301 | inc ix 302 | rrca 303 | rrca 304 | and 00000011b 305 | add "0" 306 | ld (ix),a 307 | inc ix 308 | 309 | BLDSLOT_END: ld (ix),"$" 310 | ld de,TEMP 311 | ld c,_STROUT 312 | call 5 313 | 314 | ;* RAM: print segment number 315 | 316 | ld a,(IMP_SEG) 317 | cp 0FFh 318 | jr z,PRNAME_END 319 | 320 | ld ix,TEMP 321 | call BYTE2ASC 322 | ld (ix),"$" 323 | 324 | ld de,SEG_S 325 | ld c,_STROUT 326 | call 5 327 | ld de,TEMP 328 | ld c,_STROUT 329 | call 5 330 | PRNAME_END: 331 | 332 | ;--- Go for the next implementation, if any 333 | 334 | ld de,CRLF_S 335 | ld c,_STROUT 336 | call 5 337 | 338 | ld a,(NUM_IMP) 339 | ld b,a 340 | ld a,(CUR_IMP) 341 | cp b 342 | ld c,_TERM0 ;Terminate if no more implementations 343 | jp z,5 344 | 345 | inc a 346 | ld (CUR_IMP),a 347 | jp MAIN_LOOP 348 | 349 | 350 | ;********************* 351 | ;*** SUBROUTINES *** 352 | ;********************* 353 | 354 | ;--- Convert a 1-byte number to an unterminated ASCII string 355 | ; Input: A = Number to convert 356 | ; IX = Destination address for the string 357 | ; Output: IX points after the string 358 | ; Modifies: AF, C 359 | 360 | BYTE2ASC: cp 10 361 | jr c,B2A_1D 362 | cp 100 363 | jr c,B2A_2D 364 | cp 200 365 | jr c,B2A_1XX 366 | jr B2A_2XX 367 | 368 | ;--- One digit 369 | 370 | B2A_1D: add "0" 371 | ld (ix),a 372 | inc ix 373 | ret 374 | 375 | ;--- Two digits 376 | 377 | B2A_2D: ld c,"0" 378 | B2A_2D2: inc c 379 | sub 10 380 | cp 10 381 | jr nc,B2A_2D2 382 | 383 | ld (ix),c 384 | inc ix 385 | jr B2A_1D 386 | 387 | ;--- Between 100 and 199 388 | 389 | B2A_1XX: ld (ix),"1" 390 | sub 100 391 | B2A_XXX: inc ix 392 | cp 10 393 | jr nc,B2A_2D ;If 1XY with X>0 394 | ld (ix),"0" ;If 10Y 395 | inc ix 396 | jr B2A_1D 397 | 398 | ;--- Between 200 and 255 399 | 400 | B2A_2XX: ld (ix),"2" 401 | sub 200 402 | jr B2A_XXX 403 | 404 | 405 | ;--- Execute a function on an UNAPI implementation 406 | 407 | EXE_UNAPI: 408 | push af 409 | ld ix,(IMP_ENTRY) 410 | ld a,ixh 411 | and 10000000b 412 | jr z,EXEUNAP2 413 | 414 | pop af 415 | jp (ix) ;Direct call to page 3 416 | 417 | EXEUNAP2: 418 | ld a,(IMP_SEG) 419 | cp 0FFh 420 | jr nz,EXEUNAP3 421 | 422 | ld iy,(IMP_SLOT-1) 423 | ld ix,(IMP_ENTRY) 424 | pop af 425 | jp CALSLT ;Call to ROM 426 | 427 | EXEUNAP3: ld a,(IMP_SLOT) 428 | ld iyh,a 429 | ld a,(IMP_SEG) 430 | ld iyl,a 431 | pop af 432 | ld ix,(RAM_HELPER) 433 | push ix 434 | ld ix,(IMP_ENTRY) ;Call to segment 435 | ret 436 | 437 | 438 | ;--- Convert a character to upper-case if it is a lower-case letter 439 | 440 | TOUPPER: 441 | cp "a" 442 | ret c 443 | cp "z"+1 444 | ret nc 445 | and 0DFh 446 | ret 447 | 448 | 449 | ;************************** 450 | ;*** DATA AND STRINGS *** 451 | ;************************** 452 | 453 | NUM_IMP: db 0 ;Number of implementations found 454 | CUR_IMP: db 1 ;Index of the current implementation 455 | IMP_SLOT: db 0 ;Slot of the current implementation 456 | IMP_SEG: db 0 ;Segment of the current implementation 457 | IMP_ENTRY: dw 0 ;Entry point of the current imp. 458 | RAM_HELPER: dw 0 ;Address of the RAM helper 459 | 460 | 461 | WELCOME_S: 462 | db "UNAPI implementations lister 1.1",13,10 463 | db "(c) 2010 by Konamiman",13,10 464 | db 13,10 465 | db "$" 466 | 467 | USAGE_S: 468 | db "Usage: APILIST |*",13,10 469 | db " use * to list specificationless applications.",13,10,"$" 470 | 471 | FOUND_S: 472 | db "Found $" 473 | IMPLEMS_S: 474 | db " implementation(s) of the $" 475 | API_S: db " API$" 476 | SPECLESS_S: 477 | db " specificationless applications$" 478 | COLON_S: 479 | db ":",13,10,13,10,"$" 480 | DOT_S: db "." 481 | CRLF_S: db 13,10,"$" 482 | 483 | ON_S: db " on $" 484 | PAGE3_S: 485 | db "page 3$" 486 | SLOT_S: db "slot $" 487 | SEG_S: db ", segment $" 488 | 489 | TEMP: ;For temporary data 490 | -------------------------------------------------------------------------------- /tools/eth.c: -------------------------------------------------------------------------------- 1 | /* Ethernet UNAPI implementation controller 2 | By Konamiman 2/2010 3 | 4 | Compilation command line: 5 | 6 | sdcc --code-loc 0x180 --data-loc 0 -mz80 --disable-warning 196 --no-std-crt0 crt0msx_msxdos_advanced.rel printf_simple.rel putchar_msxdos.rel asm.lib eth.c 7 | hex2bin -e com eth.ihx 8 | 9 | The resulting file, eth.com, is a MSX-DOS executable. 10 | 11 | Get the required extra files (asm.h, asm.lib, crt0msx_msxdos_advanced.rel, printf_simple.rel, putchar_msxdos.rel, asm.lib) here: 12 | https://www.konamiman.com/msx/msx-e.html#sdcc 13 | */ 14 | 15 | #include 16 | #include "asm.h" 17 | 18 | #define LowerCase(c) ((c) | 32) 19 | 20 | // Just in case you want to define a faster print function 21 | // for printing plain strings (without formatting). 22 | #define print(x) printf(x) 23 | 24 | enum EthUnapiFunctions { 25 | ETH_GETINFO = 0, 26 | ETH_RESET, 27 | ETH_GET_HWADD, 28 | ETH_GET_NETSTAT, 29 | ETH_NET_ONOFF, 30 | ETH_DUPLEX, 31 | ETH_FILTERS, 32 | ETH_IN_STATUS, 33 | ETH_GET_FRAME, 34 | ETH_SEND_FRAME, 35 | ETH_OUT_STATUS, 36 | ETH_SET_HWADD 37 | }; 38 | 39 | enum EthUnapiFilterFlags { 40 | FILTER_PROMISCUOUS = 16, 41 | FILTER_BROADCAST = 4, 42 | FILTER_SMALLFRAMES = 2 43 | }; 44 | 45 | const char* strPresentation= 46 | "Ethernet UNAPI control program 1.0\r\n" 47 | "By Konamiman, 2/2010\r\n" 48 | "\r\n"; 49 | 50 | const char* strUsage= 51 | "Usage: eth r|i|[h
] [e0|1] [d0|1] [p0|1] [b0|1] [s0|1]\r\n" 52 | "\r\n" 53 | "r: Reset Ethernet hardware\r\n" 54 | "i: Show current configuration information\r\n" 55 | "h: Change the MAC address,
=XX-XX-XX-XX-XX-XX\r\n" 56 | "e: Enable (1) or disable (0) the Ethernet hardware\r\n" 57 | "d: Set Half-duplex (0) or Full-duplex (1) mode\r\n" 58 | "p: Enable (1) or disable (0) promiscuous mode\r\n" 59 | "b: Accept (1) or reject (0) broadcast frames\r\n" 60 | "s: Accept (1) or reject (0) small frames (<64 bytes)\r\n"; 61 | 62 | const char* strYES="YES"; 63 | const char* strNO="NO"; 64 | const char* strON="ON"; 65 | const char* strOFF="OFF"; 66 | 67 | Z80_registers regs; 68 | int i; 69 | int param; 70 | unapi_code_block codeBlock; 71 | 72 | byte filterFlags; 73 | byte mustChangeMac=0; 74 | byte newMac[6]; 75 | byte newEnableConfig=0; 76 | byte newDuplexConfig=0; 77 | byte newPromiscuousConfig=0; //0=don't change, 1=disable, 2=enable 78 | byte newBroadcastConfig=0; //same as above 79 | byte newSmallFrameConfig=0; //same as above 80 | uint specVersion; 81 | 82 | 83 | // I know, these should be on an eth.h 84 | 85 | void PrintUsageAndEnd(); 86 | void PrintImplementationName(); 87 | void DoRegisterFilterChange(byte* flagBufferAddress, char newState); 88 | void DoRegisterNewMac(char* newMacString); 89 | byte ParseHexDigit(char digit); 90 | void DoResetHardware(); 91 | void DoShowInfo(); 92 | void MakeMacString(Z80_registers* regs, char* buffer); 93 | void ByteToHexPlusColon(byte value, char* buffer); 94 | char NibbleToHex(byte value); 95 | void DoChangeMac(); 96 | void DoEnableDisableNetworking(); 97 | void DoChangeDuplexMode(); 98 | void DoGetFilterFlags(); 99 | void DoUpdateFilterFlags(); 100 | void DoUpdateOneFilterFlag(byte newConfigValue, byte filterMask); 101 | void DoWriteFilterFlags(); 102 | 103 | 104 | /********************** 105 | *** MAIN is here *** 106 | **********************/ 107 | 108 | int main(char** argv, int argc) 109 | { 110 | char paramLetter; 111 | 112 | print(strPresentation); 113 | if(argc==0) { 114 | PrintUsageAndEnd(); 115 | } 116 | 117 | i = UnapiGetCount("ETHERNET"); 118 | if(i==0) { 119 | print("*** No Ethernet UNAPI implementations found"); 120 | return 0; 121 | } 122 | UnapiBuildCodeBlock(NULL, 1, &codeBlock); 123 | 124 | for(param=0; param='0' && digit<='9') { 261 | return digit-'0'; 262 | } 263 | digit = LowerCase(digit); 264 | if(digit>='a' && digit<='f') { 265 | return digit-'a'+10; 266 | } 267 | PrintUsageAndEnd(); 268 | return 0; //Never reached, needed to avoid warning 269 | } 270 | 271 | 272 | void DoResetHardware() 273 | { 274 | UnapiCall(&codeBlock, ETH_RESET, ®s, REGS_NONE, REGS_NONE); 275 | print("Ethernet hardware has been reset."); 276 | } 277 | 278 | 279 | void DoShowInfo() 280 | { 281 | char macString[18]; 282 | 283 | UnapiCall(&codeBlock, ETH_GET_HWADD, ®s, REGS_NONE, REGS_MAIN); 284 | MakeMacString(®s, macString); 285 | printf("MAC address: %s\r\n", macString); 286 | 287 | UnapiCall(&codeBlock, ETH_GET_NETSTAT, ®s, REGS_NONE, REGS_AF); 288 | printf("Network link is: %s\r\n", regs.Bytes.A ? strON : strOFF); 289 | 290 | regs.Bytes.B = 0; 291 | UnapiCall(&codeBlock, ETH_NET_ONOFF, ®s, REGS_MAIN, REGS_AF); 292 | printf("Network hardware is: %s\r\n", regs.Bytes.A==1 ? strON : strOFF); 293 | 294 | regs.Bytes.B = 0; 295 | UnapiCall(&codeBlock, ETH_DUPLEX, ®s, REGS_MAIN, REGS_AF); 296 | print("Current duplex mode is: "); 297 | if(regs.Bytes.A == 1) { 298 | print("Half-duplex\r\n"); 299 | } else if(regs.Bytes.A == 2) { 300 | print("Full-duplex\r\n"); 301 | } else 302 | print("Unknown / does not apply\r\n"); 303 | 304 | DoGetFilterFlags(); 305 | printf("Promiscuous mode is: %s\r\n", (filterFlags & FILTER_PROMISCUOUS) ? strON : strOFF); 306 | printf("Boradcast frames are accepted: %s\r\n", (filterFlags & FILTER_BROADCAST) ? strYES : strNO); 307 | printf("Small frames are accepted: %s\r\n", (filterFlags & FILTER_SMALLFRAMES) ? strYES : strNO); 308 | } 309 | 310 | 311 | //This is needed because the printf implementation that I found 312 | //does not allow padding numbers with zeros. 313 | //Of course printf("MAC address: %02x:%02x...) would have been much nicer. 314 | void MakeMacString(Z80_registers* regs, char* buffer) 315 | { 316 | ByteToHexPlusColon(regs->Bytes.L, buffer); 317 | ByteToHexPlusColon(regs->Bytes.H, buffer+3); 318 | ByteToHexPlusColon(regs->Bytes.E, buffer+6); 319 | ByteToHexPlusColon(regs->Bytes.D, buffer+9); 320 | ByteToHexPlusColon(regs->Bytes.C, buffer+12); 321 | ByteToHexPlusColon(regs->Bytes.B, buffer+15); 322 | buffer[17] = 0; 323 | } 324 | 325 | 326 | void ByteToHexPlusColon(byte value, char* buffer) 327 | { 328 | byte lowNibble; 329 | byte highNibble; 330 | 331 | lowNibble = value & 0x0F; 332 | highNibble = (value >> 4) & 0x0F; 333 | 334 | buffer[0] = NibbleToHex(highNibble); 335 | buffer[1] = NibbleToHex(lowNibble); 336 | buffer[2] = ':'; 337 | } 338 | 339 | 340 | char NibbleToHex(byte value) 341 | { 342 | if(value<10) { 343 | return value+'0'; 344 | } else { 345 | return value-10+'A'; 346 | } 347 | } 348 | 349 | 350 | void DoChangeMac() 351 | { 352 | regs.UWords.HL = *(uint*)&(newMac[0]); 353 | regs.UWords.DE = *(uint*)&(newMac[2]); 354 | regs.UWords.BC = *(uint*)&(newMac[4]); 355 | UnapiCall(&codeBlock, ETH_SET_HWADD, ®s, REGS_MAIN, REGS_NONE); 356 | print("MAC address has been changed.\r\n"); 357 | } 358 | 359 | 360 | void DoEnableDisableNetworking() 361 | { 362 | regs.Bytes.B = newEnableConfig ^ 3; //1->2, 2->1 363 | UnapiCall(&codeBlock, ETH_NET_ONOFF, ®s, REGS_MAIN, REGS_NONE); 364 | printf("Networking has been %s.\r\n", newEnableConfig==1 ? "disabled" : "enabled"); 365 | } 366 | 367 | 368 | void DoChangeDuplexMode() 369 | { 370 | regs.Bytes.B = newDuplexConfig; 371 | UnapiCall(&codeBlock, ETH_DUPLEX, ®s, REGS_MAIN, REGS_NONE); 372 | printf("%s-duplex mode has been set.\r\n", newDuplexConfig==1 ? "Half" : "Full"); 373 | } 374 | 375 | 376 | void DoGetFilterFlags() 377 | { 378 | regs.Bytes.B = 128; 379 | UnapiCall(&codeBlock, ETH_FILTERS, ®s, REGS_MAIN, REGS_AF); 380 | filterFlags = regs.Bytes.A; 381 | } 382 | 383 | 384 | void DoUpdateFilterFlags() 385 | { 386 | DoUpdateOneFilterFlag(newPromiscuousConfig, FILTER_PROMISCUOUS); 387 | DoUpdateOneFilterFlag(newBroadcastConfig, FILTER_BROADCAST); 388 | DoUpdateOneFilterFlag(newSmallFrameConfig, FILTER_SMALLFRAMES); 389 | } 390 | 391 | 392 | void DoUpdateOneFilterFlag(byte newConfigValue, byte filterMask) 393 | { 394 | if(newConfigValue == 1) { 395 | filterFlags &= (~filterMask); 396 | } else if(newConfigValue == 2) { 397 | filterFlags |= filterMask; 398 | } 399 | } 400 | 401 | 402 | void DoWriteFilterFlags() 403 | { 404 | regs.Bytes.B = filterFlags; 405 | UnapiCall(&codeBlock, ETH_FILTERS, ®s, REGS_MAIN, REGS_AF); 406 | print("Filter flags have been modified.\r\n"); 407 | } 408 | -------------------------------------------------------------------------------- /tools/tcpip.c: -------------------------------------------------------------------------------- 1 | /* TCP/IP UNAPI implementations control program 2 | By Konamiman 6/2019 3 | 4 | Compilation command line: 5 | 6 | sdcc --code-loc 0x180 --data-loc 0 -mz80 --disable-warning 196 --no-std-crt0 crt0msx_msxdos_advanced.rel printf_simple.rel putchar_msxdos.rel asm.lib tcpip.c 7 | hex2bin -e com tcpip.ihx 8 | 9 | The resulting file, eth.com, is a MSX-DOS executable. 10 | 11 | Get the required extra files (asm.h, asm.lib, crt0msx_msxdos_advanced.rel, printf_simple.rel, putchar_msxdos.rel) here: 12 | https://www.konamiman.com/msx/msx-e.html#sdcc 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "asm.h" 20 | 21 | #define _TERM0 0 22 | 23 | enum TcpipUnapiFunctions { 24 | UNAPI_GET_INFO = 0, 25 | TCPIP_GET_CAPAB = 1, 26 | TCPIP_GET_IPINFO = 2, 27 | TCPIP_NET_STATE = 3, 28 | TCPIP_DNS_Q = 6, 29 | TCPIP_CONFIG_AUTOIP = 25, 30 | TCPIP_CONFIG_IP = 26, 31 | TCPIP_CONFIG_TTL = 27, 32 | TCPIP_CONFIG_PING = 28, 33 | }; 34 | 35 | enum TcpipErrorCodes { 36 | ERR_OK, 37 | ERR_NOT_IMP, 38 | ERR_NO_NETWORK, 39 | ERR_NO_DATA, 40 | ERR_INV_PARAM, 41 | ERR_QUERY_EXISTS, 42 | ERR_INV_IP, 43 | ERR_NO_DNS, 44 | ERR_DNS, 45 | ERR_NO_FREE_CONN, 46 | ERR_CONN_EXISTS, 47 | ERR_NO_CONN, 48 | ERR_CONN_STATE, 49 | ERR_BUFFER, 50 | ERR_LARGE_DGRAM, 51 | ERR_INV_OPER 52 | }; 53 | 54 | enum IpAddresses { 55 | IP_LOCAL = 1, 56 | IP_REMOTE = 2, 57 | IP_MASK = 3, 58 | IP_GATEWAY = 4, 59 | IP_DNS1 = 5, 60 | IP_DNS2 = 6 61 | }; 62 | 63 | const char* strPresentation= 64 | "TCP/IP UNAPI control program 1.1\r\n" 65 | "By Konamiman, 6/2019\r\n" 66 | "\r\n"; 67 | 68 | const char* strUsage= 69 | "Usage: tcpip f | s\r\n" 70 | " tcpip ip [l
] [r
] [m
] [g
]\r\n" 71 | " [p
] [s
] [a(0|1|2|3)]]\r\n" 72 | " tcpip set [p(0|1)] [ttl] [tos]\r\n" 73 | "\r\n" 74 | "f: Show implementation capabilities and features\r\n" 75 | "s: Show current configuration and status information\r\n" 76 | "ip l/r/m/g/p/s: Change the IP address (local, remote, subnet mask,\r\n" 77 | " primary DNS server, secondary DNS server)\r\n" 78 | "ip a: Configure IP addresses to retrieve automatically\r\n" 79 | " (0=none, 1=local+subnet+gateway, 2=DNS servers, 3=all)\r\n" 80 | "set p: Enable (1) or disable (0) reply to incoming PINGs\r\n" 81 | "set ttl: Set TTL for outgoing deatagrams (0-255)\r\n" 82 | "set tos: Set ToS for outgoing deatagrams (0-255)\r\n"; 83 | 84 | const char* strMissingParams = "Missing parameters"; 85 | const char* strInvParam = "Invalid parameter"; 86 | 87 | const char* strYES="YES"; 88 | const char* strNO="NO"; 89 | const char* strON="ON"; 90 | const char* strOFF="OFF"; 91 | 92 | Z80_registers regs; 93 | int i; 94 | int param; 95 | unapi_code_block codeBlock; 96 | 97 | byte mustChangeIP[6]; 98 | byte newIP[6*4]; 99 | byte mustChangeAutoIP = 0; 100 | byte newAutoIP; 101 | byte mustChangeTTL = 0; 102 | byte newTTL; 103 | byte mustChangeTOS = 0; 104 | byte newTOS; 105 | byte mustChangePingReply = 0; 106 | byte newPingReply; 107 | byte isSpec11; 108 | 109 | 110 | // I know, these should be on a tcpip.h 111 | 112 | void PrintUsageAndEnd(); 113 | void PrintImplementationName(); 114 | void DoShowInfo(); 115 | void Terminate(char* errorMessage); 116 | int strcmpi(const char *a1, const char *a2); 117 | int strncmpi(const char *a1, const char *a2, unsigned size); 118 | void DoShowFeatures(); 119 | void DoShowStatus(); 120 | void ParseFlagParam(char* param, byte* flagAddress, byte* flagValue); 121 | void ParseIP(char* IP, byte* flagAddress, byte* ipAddress); 122 | void ParseAutoIP(char* param, byte* flagAddress, byte* flagValue); 123 | void ParseByteParam(char* param, byte* flagAddress, byte* valueAddress); 124 | void DoChangeIP(byte IPindex, byte* IPvalue); 125 | void DoChangeAutoIP(byte autoIP); 126 | void DoChangePingReply(byte newPingReply); 127 | void DoChangeTtlTos(byte mustChangeTTL, byte newTTL, byte mustChangeTOS, byte newTOS); 128 | void print(char* str); 129 | 130 | #define print(x) printf(x) 131 | 132 | 133 | /********************** 134 | *** MAIN is here *** 135 | **********************/ 136 | 137 | int main(char** argv, int argc) 138 | { 139 | memset(mustChangeIP, 0, 6); 140 | print(strPresentation); 141 | if(argc==0) { 142 | PrintUsageAndEnd(); 143 | } 144 | 145 | i = UnapiGetCount("TCP/IP"); 146 | if(i==0) { 147 | print("*** No TCP/IP UNAPI implementations found"); 148 | return 0; 149 | } 150 | UnapiBuildCodeBlock(NULL, 1, &codeBlock); 151 | 152 | if(strcmpi(argv[0], "f")==0) { 153 | PrintImplementationName(); 154 | DoShowFeatures(); 155 | Terminate(NULL); 156 | } 157 | else if(strcmpi(argv[0], "s")==0) { 158 | PrintImplementationName(); 159 | DoShowStatus(); 160 | Terminate(NULL); 161 | } 162 | else if(strcmpi(argv[0], "ip")==0) { 163 | if(argc == 1) { 164 | Terminate(strMissingParams); 165 | } 166 | for(param=1; param= 0x0101; 259 | 260 | while(1) { 261 | readChar = UnapiRead(&codeBlock, nameAddress); 262 | if(readChar == 0) { 263 | break; 264 | } 265 | putchar(readChar); 266 | nameAddress++; 267 | } 268 | 269 | printf(" v%u.%u\r\nTCP/IP UNAPI specification version: %u.%u\r\n\r\n", versionMain, versionSec, specVersionMain, specVersionSec); 270 | } 271 | 272 | 273 | void Terminate(const char* errorMessage) 274 | { 275 | if(errorMessage != NULL) { 276 | printf("\r\n*** %s\r\n", errorMessage); 277 | } 278 | 279 | DosCall(_TERM0, ®s, REGS_NONE, REGS_NONE); 280 | } 281 | 282 | 283 | int strcmpi(const char *a1, const char *a2) { 284 | char c1, c2; 285 | /* Want both assignments to happen but a 0 in both to quit, so it's | not || */ 286 | while((c1=*a1) | (c2=*a2)) { 287 | if (!c1 || !c2 || /* Unneccesary? */ 288 | (islower(c1) ? toupper(c1) : c1) != (islower(c2) ? toupper(c2) : c2)) 289 | return (c1 - c2); 290 | a1++; 291 | a2++; 292 | } 293 | return 0; 294 | } 295 | 296 | 297 | int strncmpi(const char *a1, const char *a2, unsigned size) { 298 | char c1, c2; 299 | /* Want both assignments to happen but a 0 in both to quit, so it's | not || */ 300 | while((size > 0) && (c1=*a1) | (c2=*a2)) { 301 | if (!c1 || !c2 || /* Unneccesary? */ 302 | (islower(c1) ? toupper(c1) : c1) != (islower(c2) ? toupper(c2) : c2)) 303 | return (c1 - c2); 304 | a1++; 305 | a2++; 306 | size--; 307 | } 308 | return 0; 309 | } 310 | 311 | 312 | void PrintYesNoBit(char* message, uint flags, int bitIndex) 313 | { 314 | printf("%s: %s\r\n", message, (flags & (1<= '0' && param[0] <= '3') { 527 | *flagValue = (byte)param[0] - (byte)'0'; 528 | } else { 529 | Terminate(strInvParam); 530 | } 531 | } 532 | 533 | 534 | void DoChangeIP(byte IPindex, byte* IPvalue) 535 | { 536 | regs.Bytes.B = IPindex; 537 | regs.Words.HL = *((int*)IPvalue); 538 | regs.Words.DE = *(((int*)IPvalue)+1); 539 | UnapiCall(&codeBlock, TCPIP_CONFIG_IP, ®s, REGS_MAIN, REGS_MAIN); 540 | } 541 | 542 | 543 | void DoChangeAutoIP(byte autoIP) 544 | { 545 | regs.Bytes.B = 1; 546 | regs.Bytes.C = autoIP; 547 | UnapiCall(&codeBlock, TCPIP_CONFIG_AUTOIP, ®s, REGS_MAIN, REGS_MAIN); 548 | } 549 | 550 | 551 | void ParseByteParam(char* param, byte* flagAddress, byte* valueAddress) 552 | { 553 | int value; 554 | 555 | *flagAddress = 1; 556 | value = atoi(param); 557 | if(value>255 || (*valueAddress == 0 && (param[0]!='0' || param[1]!='\0'))) { 558 | Terminate(strInvParam); 559 | } 560 | *valueAddress = (byte)value; 561 | } 562 | 563 | 564 | void DoChangePingReply(byte newPingReply) 565 | { 566 | regs.Bytes.B = 1; 567 | regs.Bytes.C = newPingReply; 568 | UnapiCall(&codeBlock, TCPIP_CONFIG_PING, ®s, REGS_MAIN, REGS_MAIN); 569 | } 570 | 571 | 572 | void DoChangeTtlTos(byte mustChangeTTL, byte newTTL, byte mustChangeTOS, byte newTOS) 573 | { 574 | regs.Bytes.B = 0; 575 | UnapiCall(&codeBlock, TCPIP_CONFIG_TTL, ®s, REGS_MAIN, REGS_MAIN); 576 | 577 | if(mustChangeTTL) { 578 | regs.Bytes.D = newTTL; 579 | } 580 | if(mustChangeTOS) { 581 | regs.Bytes.E = newTOS; 582 | } 583 | 584 | regs.Bytes.B = 1; 585 | UnapiCall(&codeBlock, TCPIP_CONFIG_TTL, ®s, REGS_MAIN, REGS_MAIN); 586 | } 587 | 588 | -------------------------------------------------------------------------------- /txt/ethunapi11.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Version 1.1 N. Soriano 5 | MSX community 6 | February 11, 2010 7 | 8 | 9 | The Ethernet UNAPI specification 10 | 11 | Abstract 12 | 13 | This document describes an UNAPI compliant API specification for 14 | Ethernet hardware for MSX computers. 15 | 16 | 17 | Table of Contents 18 | 19 | 1. Introduction 20 | 2. API identifier and version 21 | 3. API routines 22 | 3.1. ETH_GETINFO: Obtain the implementation name and version 23 | 3.2. ETH_RESET: Reset hardware 24 | 3.3. ETH_GET_HWADD: Obtain the Ethernet address 25 | 3.4. ETH_GET_NETSTAT: Obtain network connection status 26 | 3.5. ETH_NET_ONOFF: Enable or disable networking 27 | 3.6. ETH_DUPLEX: Configure duplex mode 28 | 3.7. ETH_FILTERS: Configure frame reception filters 29 | 3.8. ETH_IN_STATUS: Check for received frames availability 30 | 3.9. ETH_GET_FRAME: Retrieve the oldest received frame 31 | 3.10. ETH_SEND_FRAME: Send a frame 32 | 3.11. ETH_OUT_STATUS: Check frame transmission status 33 | 3.12. ETH_SET_HWADD: Set the Ethernet address 34 | Appendix A. Ethernet frame formats 35 | Appendix B. Acknowledgements 36 | Appendix C. Document version history 37 | Author's Address 38 | 39 | 40 | 1. Introduction 41 | 42 | MSX-UNAPI is a standard procedure for defining, discovering and using 43 | new APIs (Application Program Interfaces) for MSX computers. The 44 | MSX-UNAPI specification is described in a separate document. 45 | 46 | This document describes an UNAPI compliant API intended for hardware 47 | that implements Ethernet networking capabilities. The functionality 48 | provided by this API covers the link layer of a network 49 | communications stack; as such, it mainly provides routines to send 50 | and receiving raw Ethernet frames to and from an Ethernet network. 51 | There are also auxiliary routines to check for network availability, 52 | to obtain the local Ethernet address, or to configure reception 53 | filters. 54 | 55 | The intended client software applications for this API are 56 | implementations of the higher level layers of the communications 57 | stack, typically, TCP/IP stacks. Anyway it can be useful for other 58 | types of software as well, for example network traffic monitors. 59 | 60 | Note that it is not mandatory to have actual underlying Ethernet 61 | hardware in order to implement this API. As long as the routine 62 | signatures and behaviors are preserved, the implementation will ve 63 | valid, even if it acts on a completely different type of hardware, or 64 | on no harwdare at all (for example, an Ethernet emulation API over a 65 | serial or JoyNet cable could be developed). 66 | 67 | 68 | 2. API identifier and version 69 | 70 | The API identifier for the specification described in this document 71 | is: "ETHERNET" (without the quotes). Remember that per the UNAPI 72 | specification, API identifiers are case-insensitive. 73 | 74 | The Ethernet API version described in this document is 1.1. This is 75 | the API specification version that the mandatory implementation 76 | information routine must return in DE (se Section 3.1). 77 | 78 | 79 | 3. API routines 80 | 81 | This version of the Ethernet API consists of 11 mandatory routines, 82 | which are described below. API implementations may define their own 83 | additional implementation-specific routines, as described in the MSX- 84 | UNAPI specification. 85 | 86 | 3.1. ETH_GETINFO: Obtain the implementation name and version 87 | 88 | Input: A = 0 89 | Output: HL = Address of the implementation name string 90 | DE = API specification version supported. D=primary, E=secondary. 91 | BC = API implementation version. B=primary, C=secondary. 92 | 93 | This routine is mandatory for all implementations of all UNAPI 94 | compliant APIs. It returns basic information about the 95 | implementation itself: the implementation version, the supported API 96 | version, and a pointer to the implementation description string. 97 | 98 | The implementation name string must be placed in the same slot or 99 | segment of the implementation code (or in page 3), must be zero 100 | terminated, must consist of printable characters, and must be at most 101 | 63 characters long (not including the terminating zero). Refer to 102 | the MSX-UNAPI specification for more details. 103 | 104 | 3.2. ETH_RESET: Reset hardware 105 | 106 | Input: A = 1 107 | Output: - 108 | 109 | This routine resets the underlying hardware and/or the API 110 | implementation state variables to its initial state. After executing 111 | this routine, the hardware (if present) and the implementation must 112 | remain in the same state as after a computer reset or after the 113 | implementation is installed (whatever applies). More precisely, the 114 | state after the reset must be as follows: 115 | 116 | o The Ethernet address is appropriately set to its default value (it 117 | is optional to allow changing this address, see Section 3.3). 118 | o Networking is enabled. (See Section 3.5). 119 | o Received frames, if any, are discarded. Any frame transmission in 120 | process when the routine was called is aborted. 121 | o Duplex mode is set to half-duplex, when it is possible (see 122 | Section 3.6). 123 | o Reception filters are set to accept broadcast and small frames. 124 | Promiscuous mode is disabled. (See Section 3.7). 125 | 126 | 3.3. ETH_GET_HWADD: Obtain the Ethernet address 127 | 128 | Input: A = 2 129 | Output: L-H-E-D-C-B = Current Ethernet address 130 | 131 | This routine obtains the current Ethernet address of the 132 | implementation. The address is mapped to registers HL, DE and BC in 133 | a way that makes it easy to store and retrieve it for Z80, which 134 | stores 16 bit values int little-endian format. For example, the 135 | address returned by this routine can be stored at address X with 136 | these instructions: LD (X),HL - LD (X+2),DE - LD (X+4),BC. 137 | 138 | Ethernet addresses (also called MAC addresses) are unique for each 139 | physical card and are intended to never be changed after the card is 140 | manufactured. See Appendix A. 141 | 142 | If the implementation supports it, the hardware address can be 143 | changed by using the ETH_SET_HWADD routine (see Section 3.12). 144 | 145 | 3.4. ETH_GET_NETSTAT: Obtain network connection status 146 | 147 | Input: A = 3 148 | Output: A = 0 if NOT connected to an active network 149 | 1 if connected to an active network 150 | 151 | This routine checks whether the underlying hardware is connected to 152 | an active network or not (in other words, if the Ethernet cable is 153 | appropriately plugged and there is carrier). 154 | 155 | It is allowed to use loopback methods (that is, to send a frame and 156 | check if it is received back) to check the network connection status. 157 | Therefore, it may take a while to execute, so it is not advisable to 158 | invoke it too often. 159 | 160 | 3.5. ETH_NET_ONOFF: Enable or disable networking 161 | 162 | Input: A = 4 163 | B = 0: Obtain current state only 164 | 1: Enable networking 165 | 2: Disable networking 166 | Output: A = State after routine execution: 167 | 1: Networking is enabled 168 | 2: Networking is disabled 169 | 170 | This routine logically enables or disables networking activity. When 171 | disabled, no frames will be recevied (or received frames will be 172 | automatically discarded); the behavior when trying to send a frame 173 | with networking disabled is undefined. 174 | 175 | When the underlying hardware offers ways to phisically disable 176 | networking (such as entering in low power mode or similar), these 177 | must be used. Otherwise, disabling must be done by software. 178 | 179 | 3.6. ETH_DUPLEX: Configure duplex mode 180 | 181 | Input: A = 5 182 | B = 0: Obtain current mode only 183 | 1: Set half-duplex mode 184 | 2: Set full-duplex mode 185 | Output: A = Mode after routine execution: 186 | 1: Currently half-duplex mode set 187 | 2: Currently full-duplex mode set 188 | 3: Current mode unknown or duplex mode does not apply 189 | 190 | Offering the ability to change the duplex mode is optional. When not 191 | possible (because only one mode is supported, or because it is not 192 | possible to change the current mode by software), the routine must 193 | simply return the current mode, as if it were called with B=0. 194 | 195 | When it is possible to change the duplex mode by software, the 196 | default mode (after a reset, see Section 3.2) should be the half- 197 | duplex mode. 198 | 199 | The "Unknown or does not apply" mode is primarily intended for 200 | implementations that do not act on real Ethernet hardware. For these 201 | implementations, the concept of "duplex mode" may not make sense. 202 | 203 | 3.7. ETH_FILTERS: Configure frame reception filters 204 | 205 | Input: A = 6 206 | B = Filter bitmask: 207 | Bit 7: Set to return current configuration only 208 | Bit 6: Reserved 209 | Bit 5: Reserved 210 | Bit 4: Set to enable promiscuous mode, reset do disable it 211 | Bit 3: Reserved 212 | Bit 2: Set to accept broadcast frames, 213 | reset to reject them 214 | Bit 1: Set to accept small frames (smaller than 64 bytes), 215 | reset to reject them 216 | Bit 0: Reserved 217 | Output: A = Filter configuration after execution 218 | (bitmask with same format as B at input) 219 | 220 | This routine allows to set or to check the current frame reception 221 | filters. Frames whose destination Ethernet address matches the 222 | current address of the underlying hardware must always be accepted; 223 | these filters decide what to do with special frames. 224 | 225 | When bit 7 of B is set, all other bits will be ignored, and the 226 | routine will simply return a bitmask with the current configuration, 227 | without modifying anything. 228 | 229 | When bit 4 is set, promiscuous mode is enabled. In this mode, all 230 | received frames are enabled, regardless of their destination address. 231 | This mode is usually never enabled, except for special purposes such 232 | as network analysis. 233 | 234 | When bit 2 is set, broadcast frames are accepted. These frames have 235 | a destination address of FF-FF-FF-FF-FF-FF, and are intended to be 236 | received by all hosts in the network. Broadcats frames are usually 237 | always accepted. 238 | 239 | When bit 1 is set, small frames (frames smaller than 64 bytes) are 240 | accepted. These frames violate the Ethernet specification but may 241 | anyway contain useful information. 242 | 243 | Reserved bits must be set to zero at input, and are always returned 244 | as zero at output. These bits may be used in future versions of the 245 | Ethernet API specification. 246 | 247 | 3.8. ETH_IN_STATUS: Check for received frames availability 248 | 249 | Input: A = 7 250 | Output: A = 0: No received frames available 251 | 1: At least one received frame is available 252 | When A=1: 253 | BC = Size of the oldest available frame 254 | HL = Bytes 12 and 13 of the oldest available frame 255 | 256 | This routine checks whether there are received frames available to be 257 | retrieved or not. When A=1 is returned, it means that there is at 258 | least one received frame available to be retrieved; there is no way 259 | to know how many frames are available. 260 | 261 | When at least one frame is available, BC returns the total size of 262 | the oldest frame. This size includes the Ethernet header and data; 263 | it corresponds to the frame format outlined in Appendix A, which does 264 | not include the frame preamble nor the checksum. 265 | 266 | When at least one frame is available, BC returns bytes 12 and 13 of 267 | the oldest frame. These bytes contain ether the frame size or the 268 | ether-type, depending of the frame type (Ethernet II or IEEE802.3). 269 | Since the frame data starts at a different boundary on each frame 270 | type, client software may use this information to decide where to 271 | retrieve the frame, so that the data boundary is always at the same 272 | memory address. This may ease frame data manipulation. 273 | 274 | The oldest received frame can be retrieved using routine 8, see 275 | Section 3.9. 276 | 277 | 3.9. ETH_GET_FRAME: Retrieve the oldest received frame 278 | 279 | Input: A = 8 280 | HL = Destination address for the frame, or 281 | 0 to discard the frame 282 | Output: A = 0 if frame has been retrieved or discarded 283 | 1 if no received frames are available 284 | BC = Size of the retrieved frame 285 | 286 | This routine retrieves the oldest received frame (copies it to the 287 | specified memory address), and removes it from the implementation 288 | internal buffer (usually belonging to the underlying harwdare), so 289 | that when the routine is called again, the next received frame is 290 | obtained. Successive calls to this routine must return the received 291 | frames in the same order as they are received from the network. 292 | 293 | The received frame will include the Ethernet header and data, it will 294 | not include the frame preamble nor checksum. The frame format will 295 | be one of the two formats outlined in Appendix A. 296 | 297 | Implementations may not allow the destination address to be a page 1 298 | address (in the range #4000-#7FFF). Client software should not use 299 | this range as destination address when invoking this routine, in 300 | order to correctly interoperate with such implementations. 301 | 302 | When the specified destination address is zero, the frame is not 303 | retrieved but just discarded. That is, it is removed from the 304 | implementation internal buffer but not copied to memory. 305 | 306 | This specification does not impose a minimum size for the internal 307 | reception buffer, other than being big enough to hold one complete 308 | frame. But of course, the bigger the better, so that it can hold 309 | enough frames, thus minimizing the probability of losing frames. 310 | When this buffer is full, new frames must be discarded so that the 311 | oldest ones are preserved. 312 | 313 | 3.10. ETH_SEND_FRAME: Send a frame 314 | 315 | Input: A = 9 316 | HL = Frame address in memory 317 | BC = Frame length 318 | D = Routine execution mode: 319 | 0: Synchronous 320 | 1: Asynchronous 321 | Output: A = 0: Frame sent, or transmission started 322 | 1: Invalid frame length 323 | 3: Carrier lost 324 | 4: Excessive collisions 325 | 5: Asyncrhonous mode not supported 326 | 327 | This routine sends to the network the frame that the client software 328 | has composed in memory, at the address specified in HL. The frame 329 | format must be one of the two outlined in Appendix A; that is, the 330 | frame must be composed of Ethernet header (including the local 331 | Ethernet address, see Section 3.3) and data only, with no preamble 332 | nor checksum. (When there is real underlying Ethernet hardware, it 333 | is expected that the hardware itself will generate the checksum; 334 | otherwise, the implementation must generate it prior to sending the 335 | frame.) 336 | 337 | Implementations may not allow the frame source address to be a page 1 338 | address (in the range #4000-#7FFF). Client software should not use 339 | this range as source address when invoking this routine, in order to 340 | correctly interoperate with such implementations. 341 | 342 | Allowed frame lengths are 16 to 1514. If the frame is smaller than 343 | 64 bytes, the implementation must pad it with zeros up to 64 bytes 344 | (usually the underlying hardware will do it automatically). 345 | 346 | All implementations must support the syncrhonous execution mode. In 347 | this mode, the routine will start the frame transmission; then will 348 | wait until transmission has finished (successfully or not) and will 349 | return the appropriate success or error code (in this mode, error 350 | code zero means "frame successfully sent"). 351 | 352 | Implementations may optionally support the asyncrhonous execution 353 | mode, and should indeed support it when there is real Ethernet 354 | hardware that handles the transmission independently of the Z80 code 355 | execution. In this mode, the routine initiates the transmission 356 | (unless frame length is invalid, in which case A=1 must be returned) 357 | and returns immediately with A=0 (meaning "frame transmission 358 | started"). Frame transmission status can then be ckeched by using 359 | routine 10 (see Section 3.11). 360 | 361 | To check whether asynchronous mode is supported or not, try to send a 362 | dummy 16 byte frame (12 zero bytes for source and detination address 363 | and then #FFFF for the ether-type) in asyncrhonous mode, and check 364 | the return code: it will be 0 if asyncrhonous execution is supported, 365 | 5 otherwise. 366 | 367 | 3.11. ETH_OUT_STATUS: Check frame transmission status 368 | 369 | Input: A = 10 370 | Output: A = 0: No frames were sent since last reset 371 | 1: Now transmitting 372 | 2: Transmission finished successfully 373 | 3: Carrier lost 374 | 4: Excessive collisions 375 | 376 | This routine returns the state of the last frame send operation 377 | initiated by routine 9 (see Section 3.10). It is intended for being 378 | used after an asyncronous execution of that routine, but will return 379 | valid information for syncrhonous executions as well. 380 | 381 | In case of syncrhonous execution, this routine will return the same 382 | error code that routine 9 returned, except that code 0 is converted 383 | to 2, and code 1 (invalid length) is not considered a failed 384 | transmission (since the routine returns without attempting the 385 | transmission). 386 | 387 | The obtained information is persistent: successive executions of this 388 | routine will always return the same value until another frame is sent 389 | (or attempted to be sent) or the reset routine (see Section 3.2) is 390 | invoked. 391 | 392 | 3.12. ETH_SET_HWADD: Set the Ethernet address 393 | 394 | Input: A = 11 395 | L-H-E-D-C-B = Ethernet address to set 396 | Output: L-H-E-D-C-B = Ethernet address after the routine execution 397 | 398 | This routine sets the hardware address if the implementation supports 399 | it. When changing the address is not supported, the routine must do 400 | nothing and return the current address as if the ETH_GET_ADDRESS 401 | routine were called (see Section 3.3). Setting the address should 402 | not be allowed unless there is a good reason for it (for example, 403 | when it is not possible to store the address in non-volatile memory). 404 | 405 | Ethernet addresses (also called MAC addresses) are unique for each 406 | physical card and are intended to never be changed after the card is 407 | manufactured. See Appendix A. 408 | 409 | 410 | Appendix A. Ethernet frame formats 411 | 412 | In this appendix the Ethernet frame formats are described. This 413 | information is provided for reference only, and it is not exhaustive. 414 | More detailed information can be found on Internet. 415 | 416 | There are two types of Ethernet frame: Ethernet II, and IEEE802.3 417 | with SNAP extension (IEEE in short). 418 | 419 | The Ethernet II frame format is as follows (one line is 32 bits): 420 | 421 | 0 1 2 3 422 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 423 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 424 | | Ethernet address of receiver | 425 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 426 | | E. Add. of receiver (cont.) | E. Add. of sender | 427 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 428 | | Ethernet address of sender (continues) | 429 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 430 | | Ether-Type | | 431 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 432 | . . 433 | . Ethernet frame data . 434 | . . 435 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 436 | 437 | The IEEE frame format is as follows: 438 | 439 | 0 1 2 3 440 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 441 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 442 | | Ethernet address of receiver | 443 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 444 | | E. Add. of receiver (cont.) | E. Add. of sender | 445 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 446 | | Ethernet address of sender (continues) | 447 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 448 | | Frame length | 170 | 170 | 449 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 450 | | 3 | 0 | 0 | 0 | 451 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 452 | | Ether-Type | | 453 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 454 | . . 455 | . Ethernet frame data . 456 | . . 457 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 458 | 459 | The Ethernet addresses (also called MAC address), six bytes long, are 460 | unique for each card and are assigned in the factory. The address 461 | composed by all ones (six #FF bytes) is special: it is the broadcast 462 | address, and when it appears on the destination address field, it 463 | indicates that the frame is addressed to all the machines on the 464 | network, rather than to a given machine only. 465 | 466 | Normal Ethernet addresses have the lowest bit of the first byte set 467 | to zero. When it is one, it is a multicast address (the frame is 468 | addressed to a group of computers). 469 | 470 | The minimum size of an Ethernet frame (including all the headers) is 471 | 64 bytes. The maximum size is 1514 bytes. 472 | 473 | The "Frame length" of IEEE frames counts from the first byte with 474 | value 170. That is, it equals the frame data part plus eight. 475 | 476 | The "Ether-Type" field indicates the type of the frame being 477 | transported on the frame data part. The two most commonly used types 478 | are: 479 | 480 | #0800: IP datagram 481 | #0806: ARP frame 482 | 483 | Both frame types, Ethernet II and IEEE, can exist mixed in the same 484 | network. To know whether an incoming frame is Ehternet II type or 485 | IEEE type, the 16 byte number placed at positions 12 and 13 of the 486 | frame must be checked: 487 | 488 | o If the number is less than or equal to 1500, it is an IEEE frame 489 | (the number is the frame length). 490 | 491 | o If the number is greater than 1500, it is an IEEE frame (the 492 | number is the frame Ether-Type). All the Ether-Type codes are 493 | greater than 1500. 494 | 495 | When sending frames, they must be sent in Ethernet II type frames 496 | unless we know for sure that the machines on the network work 497 | exclusively with the IEEE format. 498 | 499 | Note that the frame length and Ether-Type fields are stored in Big- 500 | Endian format, that is with the high byte first; contrarywise to MSX, 501 | that stores the 16 bit numbers in Little-Endian format, with the low 502 | byte first. 503 | 504 | 505 | Appendix B. Acknowledgements 506 | 507 | This document was produced using xml2rfc v1.34 (of 508 | http://xml.resource.org/) from a source in RFC-2629 XML format. 509 | 510 | 511 | Appendix C. Document version history 512 | 513 | o Version 1.1 514 | * The ETH_GET_HWADD routine now only gets the hardware address 515 | (see Section 3.3) 516 | * Added the ETH_SET_HWADD routine (see Section 3.12) 517 | 518 | 519 | Author's Address 520 | 521 | Nestor Soriano 522 | MSX community 523 | 524 | Email: konamiman@konamiman.com 525 | URI: http://www.konamiman.com 526 | 527 | 528 | -------------------------------------------------------------------------------- /txt/unapi11.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Version 1.1 N. Soriano 5 | MSX community 6 | February 3, 2010 7 | 8 | 9 | MSX-UNAPI: Unified procedure for API definition and discovery on MSX 10 | computers 11 | 12 | Abstract 13 | 14 | This document describes MSX-UNAPI, a standard procedure for defining, 15 | discovering and using new APIs (Application Program Interfaces) for 16 | MSX computers. The goal is to provide a unified way to design and 17 | use BIOS-like software for the various kinds of new hardware that is 18 | being developed for these computers, altough it can be used for 19 | software-only solutions as well. 20 | 21 | 22 | Table of Contents 23 | 24 | 1. Introduction 25 | 1.1. Motivation 26 | 1.2. Goals 27 | 1.3. Non-goals 28 | 1.4. Glossary 29 | 1.5. Sample scenario 30 | 2. API specification rules 31 | 2.1. Rule 1: The API identifier and version number 32 | 2.2. Rule 2: One single entry point on Z80 page 1 or 3 33 | 2.3. Rule 3: Z80 registers usage 34 | 2.4. Rule 4: Routine number ranges 35 | 2.5. Rule 5: The API information routine + the 36 | implementation name and version 37 | 2.6. Rule 6: Implement code for the API discovery procedure 38 | 2.7. Rule 7: Install the RAM helper 39 | 2.8. Rule 8: Avoid segment number #FF 40 | 2.9. Optional rule 9: Give your routines a meaningful name 41 | 3. API implementations discovery procedure 42 | 3.1. The MSX extended BIOS 43 | 3.2. Steps of the discovery procedure 44 | 3.3. How to implement support for the discovery procedure 45 | 4. The RAM helper 46 | 4.1. Notes on the routine for calling a routine with inline 47 | routine identification 48 | 5. Specificationless UNAPI applications 49 | Appendix A. Acknowledgements 50 | Appendix B. Document version history 51 | Author's Address 52 | 53 | 54 | 1. Introduction 55 | 56 | 1.1. Motivation 57 | 58 | MSX was presented in 1983 as a standard for home computers, in the 59 | form of a set of specifications to be followed by manufacturers. Any 60 | brand was able to make its own model of computer; as long as the 61 | machine met the MSX standard, it was considered an MSX computer. 62 | This allowed for flawless interoperation between computers of 63 | different makers, something not as common at the time as it is 64 | nowadays. 65 | 66 | An important part of the MSX specification was the BIOS, a set of 67 | routines that -amongst other things- provided a standarized way to 68 | access the harwdare resources of the machine. This provided certain 69 | degree of freedom to manufacturers, which could -to some extent- 70 | freely choose the hardware parts that would build up their given 71 | model of MSX computer. 72 | 73 | Time passed and the MSX standard was discontinued by all 74 | manufacturers long time ago. However, many people still using their 75 | old MSX computers, and some of them even develop new hardware for 76 | these machines. Hard disk interfaces, sound cards and network cards 77 | are examples of hardware that has been developed for MSX computers 78 | past the commercial live of the standard. 79 | 80 | Altough the appearance of new hardware is of course a good thing, 81 | this causes a problem for software developers. Not having any 82 | authority dictating specifications for the MSX standard anymore, each 83 | hardware developer freely choses the way their hardware will have to 84 | be accessed from software; in other words, there are no 85 | specifications to follow at the time of designing the API 86 | (Application Program Interface) for the new harwdare. As a result, 87 | hardware extensions performing the same functions but being developed 88 | by different persons are software-incompatible (unless developers 89 | privately agree on using compatible APIs, which is not always 90 | possible). 91 | 92 | The intent of this document is to propose a solution for this 93 | problem: a standard, unified procedure for defining, discovering and 94 | using new APIs for MSX computers, aimed especially (but not 95 | exclusively) to the hardware development field. 96 | 97 | 1.2. Goals 98 | 99 | The main goals of this specification are: 100 | 101 | o To provide a minimum set of rules to be followed by APIs that 102 | adhere to this specification. These rules are to be taken in 103 | account when designing (and later implementing) the various APIs. 104 | They are very simple, impose only a few restrictions on compliant 105 | code, and in practice allow any kind of API software to be 106 | written. 107 | 108 | o To provide a standard procedure for discovering and using APIs 109 | that adhere to this standard and are installed on the MSX computer 110 | running the UNAPI aware software. This procedure is based on the 111 | MSX extended BIOS mechanism, and is very easy to implement and 112 | even easier to use. 113 | 114 | 1.3. Non-goals 115 | 116 | It is NOT the intent of this specification, and/or is outside the 117 | scope of this document: 118 | 119 | o To impose the described specification to developers or users. 120 | Altough the authors hope that the MSX-UNAPI specification will be 121 | useful for the MSX community, this document is merely a proposal, 122 | and adhering to it is of course optional. 123 | 124 | o To arbiter who should desing the APIs. Common sense suggests that 125 | APIs should be designed by the first person or team that develops 126 | a given type of hardware (or software), but the decision of such 127 | thing is outside the scope of this document. 128 | 129 | o To describe any API other than for illustration purposes. This 130 | documents just tells a procedure for designing APIs; these must be 131 | described in sepparate documents. 132 | 133 | 1.4. Glossary 134 | 135 | Before proceeding any further, a glossary of terms used along this 136 | document will be introduced. It will be useful to understand the 137 | rules and procedures described later. 138 | 139 | MSX-UNAPI 140 | Short for "MSX unified API definition and discovery standard". A 141 | set of rules to be followed when designing and implementing APIs, 142 | aimed mainly (but not exclusively) to hardware developers; and a 143 | procedure for discovering any such APIs available to application 144 | software. 145 | 146 | API specification 147 | API stands for "Application Program Interface". The description 148 | of a set of routines that perform some action (which usually, but 149 | not necessarily, involve accessing some kind of hardware), 150 | including the precise description of input and output parameters, 151 | as well as the side effects caused by the execution of each 152 | routine. 153 | 154 | UNAPI compliant API specification 155 | An API specification that conforms to the rules for API 156 | specifications described in this document. 157 | 158 | API specification identifier 159 | An alphanumeric, case-insensitive string made of up to 15 160 | characters, which uniquely identifies an UNAPI compliant API 161 | specification. For example the identifier of an API specification 162 | for Ethernet cards could be "ETHERNET". See Section 2.1 for more 163 | details. 164 | 165 | The XXXXX UNAPI specification 166 | The specification for an UNAPI compliant API whose specification 167 | identifier is XXXXX. For example, "the Ethernet UNAPI 168 | specification". 169 | 170 | XXXXX UNAPI compliant API implementation 171 | Real code that builds up a set of routines which conforms to an 172 | UNAPI compliant API specification (and more precisely, to the 173 | XXXXX UNAPI specification). There may be several implementations 174 | of one single specification, each made by a different person or 175 | team; but if they strictly conform to the API specification, they 176 | are indistinguishable with respect to usage and behavior. 177 | 178 | API implementation name 179 | An alphanumeric string made of up to 63 characters, which uniquely 180 | identifies an API implementation. For example the name of an 181 | implementation of the Ethernet UNAPI could be "ObsoNET", while the 182 | name of other implementation could be "UltraNET Supercard". See 183 | Section 2.5 for more details. 184 | 185 | XXXXX UNAPI client software 186 | Application software that invokes the routines of an XXXXX UNAPI 187 | compliant API implementation. For example, a TCP/IP stack could 188 | act as client software of the Ethernet UNAPI. The client software 189 | must use the discovery procedure (see Section 3) to locate the 190 | available API implementations, and usually it does not matter 191 | which one is used if more than one is found. 192 | 193 | Specificationless UNAPI application 194 | Application software (typically, a resident application) that does 195 | not conform to any concrete UNAPI specification, but that make use 196 | of the remaining of the UNAPI infrastructure, namely, the 197 | implementation and discovery rules. See Section 5 for more 198 | details. 199 | 200 | #FF 201 | Character # is used in this document as a prefix for hexadecimal 202 | numbers, so for example #FF is 255. 203 | 204 | Through this document, and unless otherwise stated, the term "API 205 | specification" will actually have the meaning of "UNAPI compliant API 206 | specification". The same applies to references to API 207 | implementations. 208 | 209 | 1.5. Sample scenario 210 | 211 | This section provides an fictional scenary that briefly shows the 212 | steps that are needed in order to design and implement UNAPI 213 | compliant APIs. The goal is to provide the reader a basic 214 | understanding about how all the whole thing works, so that it is 215 | easier to read the detailed rules and procedures that are provided in 216 | the remaining of the document. 217 | 218 | Suppose that a user named H.G. Wells builds a time machine for MSX 219 | computers. He wants to make the software to control it to be UNAPI 220 | compliant. To achieve this, since it is the first time machine ever 221 | developed for MSX, he first must design the API specification. These 222 | are the steps he follows for it: 223 | 224 | 1. Chooses a short name for the API specification. He chooses 225 | "TIME_MACHINE". (see Section 2.1) 226 | 227 | 2. Designs the routines that will compose the API. He designs three 228 | routines: travel backwards, travel forwards, and return to 229 | original time. These routines will have routine numbers 1 230 | through 3. (see Section 2.2, Section 2.3 and Section 2.4) 231 | 232 | Now he must write a real implementation of the API, to be included in 233 | ROM together with the time-travel hardware. He does then the 234 | following: 235 | 236 | 1. Chooses a name for the API implementation: "Well's Time Machine 237 | BIOS" (see Section 2.5) 238 | 239 | 2. Implements the actual code for the three API routines (again, see 240 | Section 2.2, Section 2.3 and Section 2.4) 241 | 242 | 3. Implements the API discovery procedure, so that when the time 243 | machine is present in a MSX machine, it can be discovered by 244 | using extended BIOS (see Section 3) 245 | 246 | The device turns out to sell quite well in MSX users meetings, and 247 | some users develop software that uses it, from multi-epoch games to 248 | virtual history books. All of this software uses the UNAPI discovery 249 | procedure (see Section 3) in order to find out at run time whether 250 | the time machine is present or not. 251 | 252 | Soon afterwards, another user with hardware design knowledge, Dr. 253 | Brown, develops another kind of time machine for MSX. Since he wants 254 | the software that already exists for Well's hardware to be compatible 255 | with his own device, he develops the software so that it is 256 | compatible with the TIME_MACHINE API specification, by following the 257 | same steps that Wells did. There are, however, some important 258 | differences with the Well's machine: 259 | 260 | 1. The API implementation name is now "Brown's flux-capacited time 261 | machine". 262 | 263 | 2. The device is connected to MSX via serial port, so it has no ROM 264 | and the API implementation is therefore installed in mapped RAM. 265 | When this API is installed, it installs the RAM helper. (see 266 | Section 4) 267 | 268 | 3. There is an extra routine which is needed to calibrate the flux 269 | capacitor. This routine is implemented as an implementation- 270 | specific routine (see Section 2.4), and has the routine number 271 | 128. 272 | 273 | Now that there are two different time travel devices around, an user 274 | named M. McFly wants to develop a windowed graphic interface to 275 | control the time machines. It develops the software to discover, and 276 | interact with, TIME_MACHINE APIs; therefore it works with both kinds 277 | of devices. Moreover, as an extra feature, it checks the 278 | implementation name of the API implementation it uses (see 279 | Section 2.1), and when it is the Brown's implementation, the 280 | application offers extra functionality to calibrate the flux 281 | capacitor. 282 | 283 | 284 | 2. API specification rules 285 | 286 | This section describes the rules that must follow any UNAPI compliant 287 | API specification, as well as their implementations. 288 | 289 | 2.1. Rule 1: The API identifier and version number 290 | 291 | An API specification must have an alphanumeric identifier composed of 292 | up to 15 characters. Allowed characters are letters (having an ASCII 293 | code below 128), digits, and the following ones: - _ / . ( ) 294 | 295 | The API identifier is case-insensitive, so for example, "ETHERNET" is 296 | the same as "Ethernet". 297 | 298 | An API specification must have a version number composed of main 299 | version number in the range 1-255, and secondary version number in 300 | the range 0-255. Specifications with higher version numbers must be 301 | backwards compatible with older versions. The first released 302 | specification document should have version 1.0. Versions 0.x are 303 | allowed as pre-releases, with no guarantee of backwards compatibility 304 | between them. 305 | 306 | Digits may be used in the identifier when they are meaningful to 307 | describe the API (for example "WIFI_IEEE802.3"), but they must NOT be 308 | used to indicate the specification version. 309 | 310 | 2.2. Rule 2: One single entry point on Z80 page 1 or 3 311 | 312 | There must be one single entry point for the API routines. This 313 | entry point must be accessible in any address on one of these places: 314 | 315 | o A ROM or non-mapped RAM slot, on Z80 page 1 (address between #4000 316 | and #7FFF). 317 | 318 | o A mapped RAM segment, provided that it will be connected to Z80 319 | page 1 (address between #4000 and #7FFF) when called. 320 | 321 | o System RAM on page 3 (address above #C000). 322 | 323 | It is outside the scope of this specification to define how RAM 324 | memory (mapped RAM segments and space in system RAM on page 3) is 325 | allocated. 326 | 327 | If necessary, API routines may return a pointer to hard-coded 328 | information to be read by the client software (as the mandatory API 329 | information routine does with the API implementation name, see 330 | Section 2.5). In this case, the information must be in the same 331 | place as the API implementation itself. That is, in the same ROM 332 | slot (in Z80 page 1), in the same RAM segment (if this is the case, a 333 | page 1 address is returned), or in system RAM on page 3. This way it 334 | is easy to obtain the name by using inter-slot or inter-segment (see 335 | Section 4) reads. 336 | 337 | As an exception to the above rule, API implementations residing in 338 | page 1 (ROM or a mapped RAM segment) may return an address in system 339 | RAM on page 3 (this is useful when ROM implementations need to return 340 | dynamic data, for example). Therefore the client software must be 341 | always prepared to either perform inter-slot or inter-segment reads, 342 | or direct memory reads, in order to retrieve the data. 343 | 344 | If necessary, API routines may require the client software to specify 345 | a buffer where dynamic data will be read from, or written to (for 346 | example, packets to be sent to or retrieved from the network in the 347 | case of an Ethernet API). In this case, the API specification may 348 | impose a restriction to the client software, so that it must NOT 349 | specify any page 1 address as the buffer address. 350 | 351 | 2.3. Rule 3: Z80 registers usage 352 | 353 | Z80 registers are used by the API routines in the following way: 354 | 355 | o Register A is used at input to specify the routine to be executed. 356 | Each routine has associated a number in the range 0-254; how these 357 | numbers are allocated is described in Section 2.4. It can be used 358 | also as an output parameter, otherwise it is corrupted after the 359 | routine is executed. 360 | 361 | o Registers F, BC, DE and HL may be used freely as input and/our 362 | output parameters. Registers not used to hold output parameters 363 | are corrupted after the routine is executed, unless otherwise 364 | stated in the routine definition. 365 | 366 | o Registers IX, IY must not be used for input parameters, to allow 367 | for inter-slot and inter-segment calls. They can be used as 368 | output parameters, otherwise they are corrupted after the routine 369 | is executed. 370 | 371 | o Alternate registers (AF', BC', DE' and HL') are corrupted after 372 | the routine is executed. 373 | 374 | "The registers are corrupted after the routine is executed" means 375 | that the routine code may freely make internal use of the registers, 376 | and it does not need to restore their original values before 377 | finishing. 378 | 379 | 2.4. Rule 4: Routine number ranges 380 | 381 | As stated before, each API routine has associated a number which must 382 | be passed in register A when calling the API entry point. The 383 | numeric range 0-255 is divided in four ranges of routines: 384 | 385 | o 0: This value corresponds to the API information routine. It is a 386 | mandatory routine that must be present on all implementations of 387 | any UNAPI compliant API. The parameters and behavior of this 388 | routine are described in Section 2.5. 389 | 390 | o 1-127: Specification routines. The behavior of these routines is 391 | defined in the appropriate API specification document. 392 | 393 | o 128-254: Implementation-specific routines. API implementation 394 | developers may freely use this range of routines to offer 395 | additional capabilities not present in the original API 396 | specification; if this is done, these routines must be described 397 | in the documentation of the implementation. For example, one 398 | implementation of the Ethernet UNAPI could use this range of 399 | routines to offer WiFi connectivity, while other could offer basic 400 | internetworking capability. Note that different implementations 401 | may use the same numbers for different routines, always inside 402 | this range. 403 | 404 | o 255: This value is reserved for a possible future extension 405 | mechanism. 406 | 407 | Numbers must be assigned to routines in increasing order, starting 408 | with 1 for specification routines and starting with 128 for 409 | implementation-specific routines; it is not allowed to leave holes in 410 | the function number ranges. If client software invokes a non- 411 | existing routine (that is, calls the API implementation entry point 412 | passing in A a number not assigned to any routine), nothing must 413 | happen and the code must return with AF, BC, DE and HL unmodified. 414 | 415 | 2.5. Rule 5: The API information routine + the implementation name and 416 | version 417 | 418 | Every API implementation, no matter which API specification is 419 | implementing, must mandatorily implement the API information routine. 420 | This routine has always the routine number 0 and has the following 421 | input and output parameters: 422 | 423 | Input: A = 0 424 | Output: HL = Address of the implementation name string 425 | DE = API specification version supported. D=primary, E=secondary. 426 | BC = API implementation version. B=primary, C=secondary. 427 | 428 | The implementation name string must be composed of up to 63 printable 429 | characters and must have a zero byte as termination. The name may be 430 | any descriptive text, but must NOT contain any version information, 431 | and is case-sensitive. 432 | 433 | The implementation name must be placed in the same place as the API 434 | implementation itself (see Section 2.2). 435 | 436 | Usually, client software will use the first suitable API 437 | implementation it founds, and will use the implementation name merely 438 | to show information to the user. However, if the client software is 439 | able to use the implementation-specific features of any given 440 | implementation, it must check the implementation name to know wether 441 | the desired implementation-specific routines are available or not. 442 | 443 | Client software should check the API specification version supported 444 | by the implementation, since newer implementation versions may have 445 | routines not available in older versions. 446 | 447 | Rules for the implementation version are similar to rules for the 448 | specification version (see Section 2.1): first release version should 449 | be 1.0, and 0.x versions are allowed for pre-releases. However, the 450 | backwards compatibility rule now applies to implementation-specific 451 | routines only, since the compatibility with the specification 452 | routines is indicated by the "API specification version supported" 453 | parameter. 454 | 455 | It is not allowed for an API implementation to go backwards in the 456 | API specification version supported when the API implementation 457 | version is increased. For example, if implementation version 2.0 458 | supports specification version 1.5, it is illegal for implementation 459 | version 2.1 to claim compliance with the specification version 1.4; 460 | it must support specification version 1.5 or higher. Otherwise, 461 | backwards compatibility between API implementations could not be 462 | assured. 463 | 464 | 2.6. Rule 6: Implement code for the API discovery procedure 465 | 466 | Every API implementation, no matter which API specification is 467 | implementing, must mandatorily support the API discovery procedure. 468 | See Section 3. 469 | 470 | 2.7. Rule 7: Install the RAM helper 471 | 472 | Every API implementation that is installed on a mapped RAM segment 473 | must, at installation time, check if the RAM helper is installed. If 474 | not, it must either install one, or refuse to install. See 475 | Section 4. 476 | 477 | 2.8. Rule 8: Avoid segment number #FF 478 | 479 | An API implementation that installs on a mapped RAM segment must NOT 480 | be installed on a segment whose number is #FF (this is the last 481 | existing segment number on 4MByte RAM slots). This is necessary 482 | because the discovery procedure will use #FF as a fictitious segment 483 | number when the API implementation resides in ROM (see Section 3.2). 484 | 485 | When using the mapper support routines provided by MSX-DOS 2 to 486 | allocate memory, this is not an issue, since these routines will 487 | never allocate segment #FF even when it is available. However, when 488 | running under DOS 1 and manually selecting the segment for 489 | installation, care must be taken to not use segment #FF. 490 | 491 | 2.9. Optional rule 9: Give your routines a meaningful name 492 | 493 | In your API specification, you can refer to your ruotines simply by 494 | their numbers. However, to make the client software developers life 495 | easier, it is advisable to give your routines meaningful names. 496 | Tipically, these names will be directly used as constants in source 497 | code; therefore, the following guidelines should be followed when 498 | choosing the names: 499 | 500 | o Use characters that are legal for all assemblers and compilers. 501 | Ideally, you should use only letters and the underscore symbol 502 | ("_"). Use uppercase letters to improve readability. 503 | 504 | o Limit the length of the names to a reasonable maximum, for example 505 | 16 characters. 506 | 507 | o Do not use names already in use for standard MSX BIOS routines, 508 | system work area variables or MSX-DOS function calls. 509 | 510 | o It may be useful to prepend the routine names with the first 511 | characters of the implementation identifier. 512 | 513 | o And of course, choose names that make sense for the routines being 514 | named. 515 | 516 | For example, an Ethernet UNAPI specification could have the following 517 | routine names: ETH_RESET (reset hardware), ETH_GET_FRAME (retrieve 518 | incoming frame from the network), or ETH_GET_NETSTAT (check the 519 | network status). 520 | 521 | Note that this rule is optional, and so are the guidelines listed; 522 | you can break them if necessary, simply be wise and think on the 523 | other developers. 524 | 525 | 526 | 3. API implementations discovery procedure 527 | 528 | Section 2 described the rules for designing UNAPI compliant APIs. 529 | These API specifications are used by developers to make API 530 | implementations, which are composed of real code that once installed 531 | on an MSX computer (whatever the installation method is), becomes 532 | available to client software. 533 | 534 | This section describes the API implementations discovery procedure, 535 | that is, the steps that client software must follow to find out how 536 | many implementations of a given API specification are available at 537 | run time, and to gather information about each implementation (in 538 | which slot/segment are placed, where the entry point is, and the name 539 | and version information), as well as how API implementations can 540 | include support for this procedure. 541 | 542 | 3.1. The MSX extended BIOS 543 | 544 | The discovery procedure is based on the MSX extended BIOS, which is a 545 | mechanism available on MSX computers that allows standard BIOS to be 546 | extended with new routines. This section explains the general rules 547 | of the extended BIOS mechanism, and later sections explain how this 548 | is used for the UNAPI discovery protocol. 549 | 550 | Extended BIOS provides a five byte hook named EXTBIO at address 551 | #FFCA. To check whether this hook has been initialized with any 552 | value or is uninitialized, look at bit 0 of the byte at address 553 | HOKVLD (#FB20). A value of one for this bit means that the hook 554 | contains a valid jump instruction, previously set by system code or 555 | user code. 556 | 557 | The EXTBIO hook contents may be replaced with a jump instruction to 558 | custom code, provided that the old hook contents are saved somewhere. 559 | If the hook is not initialized, it should first be filled with five 560 | RET instructions, and bit 0 of HOKVLD should be set; from this point, 561 | consider it as being initialized. 562 | 563 | The EXTBIO hook is called with a code named "device identifier" in 564 | register D, a "function identifier" in register E, and input 565 | parameters in AF, BC and HL. The code called in this way must look 566 | at the device identifier to check if the request is for itself. If 567 | not, it must jump to the old hook (the one saved at installation 568 | time) with AF, BC, DE and HL unmodified. 569 | 570 | When the device identifier matches the one expected by the code, it 571 | performs the action requested (as per the function identifier) and 572 | either returns parameters in, or corrupts, AF, BC and HL; DE is 573 | always preserved. 574 | 575 | IX, IY and the alternate registers are always corrupted, no matter 576 | whether any suitable code for the specified device identifier is 577 | executed or not. 578 | 579 | This procedure allows to chain together multiple BIOS extensions, 580 | each having its own device identifier. These identifiers were once 581 | assigned by MSX manufacurers, and later on, users have developed 582 | software that patch extended BIOS, freely choosing their own 583 | identifiers. 584 | 585 | 3.2. Steps of the discovery procedure 586 | 587 | Client software willing to discover how many implementations of a 588 | certain API are available at run time must perform these steps: 589 | 590 | 1. Copy the API specification identifier to address #F847. Append a 591 | zero byte as string terminator. 592 | 593 | 2. Set Z80 registers as follows: A=0, B=0, DE=#2222. 594 | 595 | 3. Call the EXTBIO hook. 596 | 597 | 4. When EXTBIO returns, B contains the number of installed 598 | implementations of the specified API. DE is preserved and AF, C, 599 | HL are corrupted. 600 | 601 | Address #F847 is a 16 byte buffer named ARG, used normally by Math- 602 | Pack (mathematic routines package of MSX BIOS). 603 | 604 | After the previous steps have been performed, and provided that at 605 | least one implementation is installed (B>0), do the following to 606 | gather information about a given implementation: 607 | 608 | 1. Make sure that the API specification identifier is still at ARG. 609 | 610 | 2. Set Z80 registers as follows: A=implementation index (from 1 to 611 | the number of available implementations), DE=#2222. 612 | 613 | 3. Call the EXTBIO hook. 614 | 615 | 4. When EXTBIO returns, Z80 registers contain the following 616 | information about the specified implementation: 617 | 618 | A = Slot where the implementation code is placed 619 | B = RAM segment where the implementation code is placed 620 | (#FF if not in mapped RAM) 621 | HL = Routines entry point address 622 | (if a page 3 address, A and B are meaningless) 623 | DE is preserved. F and C are corrupted. 624 | 625 | With this information, now you know how to call the implementation 626 | entry point in order to invoke the API routines: 627 | 628 | o If the entry point address is a page 3 address, ignore A and B and 629 | perform direct calls to that address. 630 | 631 | o Otherwise, if B=#FF, use inter-slot calls (for example by using 632 | the BIOS routine CALSLT) to invoke the routines. 633 | 634 | o Otherwise, use inter-segment calls (you can use the RAM helper for 635 | this, see Section 4) to invoke the routines. 636 | 637 | Usually, the first thing to do at this point is to call the API 638 | implementation information routine (see Section 2.5) to obtain the 639 | implementation name and version (this is not mandatory, though). 640 | From here, client software can invoke the API routines as needed. 641 | 642 | 3.3. How to implement support for the discovery procedure 643 | 644 | Section 3.2 provided a functional description of the API 645 | implementations discovery procedure. For this procedure to work, all 646 | implementations of an UNAPI compliant API must include code to 647 | support this procedure. This section explains how this code should 648 | be implemented. 649 | 650 | First, when the API implementation is installed (or at system boot, 651 | for ROM based code), the existing EXTBIO hook must be backed up (if 652 | EXTBIO hook is not initialized, initialize it first as explained in 653 | Section 3.1). Then, put a jump instruction or an inter-slot or 654 | inter-segment call pointing to your EXTBIO manager code. 655 | 656 | Second, include in the API implementation code to manage EXTBIO calls 657 | (that's where the new EXTBIO hook points to). This code must perform 658 | the following steps: 659 | 660 | 1. Check that register pair DE holds the value #2222. If not, jump 661 | to the old EXTBIO hook with AF, BC, DE, HL unmodified. 662 | 663 | 2. If A=#FF, jump to the old EXTBIO hook with AF, BC, DE, HL 664 | unmodified (this is to allow the RAM helper to be installed, see 665 | Section 4). 666 | 667 | 3. Check that the string placed in ARG (remember that it is zero- 668 | terminated there) matches the identifier of the API specification 669 | that your code implements. If not, jump to the old EXTBIO hook 670 | with AF, BC, DE, HL unmodified. Remember that the strings 671 | comparison must be done in a case-insensitive way. 672 | 673 | 4. If A=0, increase the value of register B by one, and jump to the 674 | old EXTBIO hook with A and DE unmodified. 675 | 676 | 5. Otherwise, if A=1, put the appropriate information about the 677 | implementation in registers A, B, and HL, as described in 678 | Section 3.2, and return (do NOT call the old EXTBIO hook) with DE 679 | unmodified. 680 | 681 | 6. Otherwise, decrease A by one, and jump to the old EXTBIO hook 682 | with DE unmodified. 683 | 684 | Note that given the way the discovery procedure is designed, the 685 | implementations installed in the first place have assigned the 686 | highest implementation index numbers. Usually this has no impact on 687 | the design on client software but it is a good thing to know it. 688 | 689 | 690 | 4. The RAM helper 691 | 692 | Once an API implementation has been discovered, it is easy for client 693 | software to invoke the API routines if the API entry point is located 694 | at page 3 (using direct calls) or in a ROM slot (using inter-slot 695 | calls, for example via the BIOS function CALSLT). However, for API 696 | implementations installed in a mapped RAM segment, it is not so easy 697 | to invoke the API entry point. MSX-DOS 2 provides system routines to 698 | execute code placed on an arbitrary segment, but the client code must 699 | still ensure that the appropriate RAM slot is switched on page 1. 700 | For MSX-DOS 1 it is even worse, since no mapper support routines are 701 | provided. 702 | 703 | To solve this issue, and in order to ease the development of client 704 | software, this specification includes the concept of the RAM helper. 705 | The RAM helper consists of a set of routines and a mappers table that 706 | are placed at system RAM on page 3, each having an entry point in the 707 | same area. Two of the routines allow to easily perform inter-segment 708 | calls, while the other allows to perform inter-segment data reads. 709 | 710 | To check for the presence of the RAM helper, and to obtain the 711 | address of its routines, EXTBIO must be called with DE=#2222, HL=0, 712 | and A=#FF. If the RAM helper is not installed, then HL=0 at output; 713 | otherwise the following register values will be returned: 714 | 715 | HL = Address of a jump table in page 3 716 | BC = Address of a mappers table in page 3 717 | A = Number of entries in the jump table 718 | 719 | The value returned in A is always 3 as per this specification. This 720 | value is provided because future specification versions may define 721 | additional routines, and so client applications may detect which 722 | specification version the installed RAM helper conforms to. 723 | 724 | The jump table routines are as follow: 725 | 726 | +0: Call a routine in a mapped RAM segment 727 | Input: 728 | IYh = Slot number 729 | IYl = Segment number 730 | IX = Target routine address (must be a page 1 address) 731 | AF, BC, DE, HL = Parameters for the target routine 732 | Output: 733 | AF, BC, DE, HL, IX, IY = Parameters returned from the target 734 | routine 735 | 736 | +3: Read a byte from a RAM segment 737 | Input: 738 | A = Slot number 739 | B = Segment number 740 | HL = Address to be read from 741 | (higher two bits will be ignored) 742 | Output: 743 | A = Data readed from the specified address 744 | F, BC, DE, HL, IX, IY preserved 745 | 746 | +6: Call a routine in a mapped RAM segment, with inline routine 747 | identification 748 | Input: 749 | AF, BC, DE, HL = Parameters for the target routine 750 | Output: 751 | AF, BC, DE, HL, IX, IY = Parameters returned from the target 752 | routine 753 | The routine is to be called as follows: 754 | CALL CALLSEG 755 | 756 | CALLSEG: 757 | CALL 758 | DB &Bmmeeeeee 759 | DB 760 | ;no RET is needed here 761 | 762 | where 763 | 764 | mm is the mapper slot, as an index (0 to 3) in the mappers 765 | table 766 | 767 | eeeeee is the routine to be called, as an index (0 to 63) of 768 | a jump table that starts at address #4000 of the segment. 769 | That is, 0 means #4000, 1 means #4003, 2 means #4006, etc. 770 | 771 | The mappers table is as follows: 772 | 773 | +0: Slot number of the primary mapper 774 | +1: Maximum segment number of the primary mapper 775 | +2: Slot number of second mapper 776 | +3: Maximum segment number of the secondary mapper 777 | ... 778 | 779 | The table contains from one to four entries depending on how many 780 | mappers are present in the system. For each mapper, an entry exists 781 | containing the slot number and the maximum available segment number 782 | (this will be #FE for 4MB mappers, since segment #FF can't be used, 783 | see Section 2.8). The end of the table is always indicated by a zero 784 | byte (even if all four entries are filled). 785 | 786 | This table is needed when using the routine that calls a routine in a 787 | mapped RAM segment with inline routine identification: the slot 788 | number is specified as an index in this table (from 0 for the first 789 | entry, up to 3 for the fourth entry). The maximum available segment 790 | number is provided to help RAM based implementations to decide in 791 | which segment should they be installed; normally they should provide 792 | the user the option to decide which segment to use, and default to 793 | use the maximum segment number available of one of the available 794 | mappers if the user does not provide any segment number. (Note that 795 | this applies when running under MSX-DOS 1 only, since MSX-DOS 2 796 | provides its own set of mapper support routines) 797 | 798 | The installation code for every API implementation that is to be 799 | installed on a mapped RAM segment must check if the RAM helper is 800 | installed in the system (by using the procedure based on a EXTBIO 801 | call described above). If not, the implementation installer has two 802 | options: either install a RAM helper by itslef, appropriately 803 | patching the EXTBIO hook so that the RAM helper can be discovered by 804 | using the explained procedure; or refuse to install, thus requiring 805 | the user to previously install a RAM helper in order to install that 806 | implementation. This way, client applications can always safely 807 | assume that the RAM helper is installed if implementations that 808 | reside in mapped RAM are installed. 809 | 810 | How page 3 RAM is allocated in order to install the RAM helper is 811 | outside the scope of this specification. 812 | 813 | Having a RAM helper installed is mandatory when installing API 814 | implementations which reside in mapped RAM, but using it is optional; 815 | client software may choose to use its own code for calling the API 816 | routines and reading the API data. However, it is recommended to use 817 | the RAM helper since it significantly reduces the complexity of 818 | client software. 819 | 820 | 4.1. Notes on the routine for calling a routine with inline routine 821 | identification 822 | 823 | The routine for calling a routine with inline routine identification 824 | is intended for helping in the process of patching hooks (mainly the 825 | EXTBIO hook) to implementations that do not otherwise need to 826 | allocate or modify RAM on page 3. If the implementation has the code 827 | for the discovery procedure available at an entry that belongs to a 828 | jump table that starts at address #4000 (the start of the segment 829 | where it resides), and saves the previous state of the hook in the 830 | segment itself, then it is enough to set up the contents of the hook 831 | with the five bytes of code needed to call the routine (one CALL and 832 | two DB). No further action is required on page 3 at install time. 833 | 834 | Note that a double indirection is needed here: a CALL is made to a 835 | memory location which in turn contains a CALL to the real routine. 836 | The implementation of this routine must increase the stack pointer by 837 | two, so that execution resumes at the address placed after the first 838 | call, ignoring whatever is placed after the two DB directives. It is 839 | necessary to implement it this way, in order to be able to place a 840 | call to this routine in the EXTBIO hook, which is just five bytes 841 | long. 842 | 843 | 844 | 5. Specificationless UNAPI applications 845 | 846 | The UNAPI specification is primarily intended for the development of 847 | software that conforms to a given UNAPI compliant API specification. 848 | However, the infrastructure offered by UNAPI for the implementation 849 | and discovery of API implementations may be useful as well to develop 850 | normal applications that do not follow any standard, but still need 851 | to offer services to and be discoverable by the user (typically, 852 | TSRs, Terminate and Stay Resident programs). 853 | 854 | For this reason, the concept of specificationless UNAPI applications 855 | is introduced. Such applications are identical to regular UNAPI 856 | implementations, except for the following: 857 | 858 | 1. The API identifier is an empty string. That is, when performing 859 | the discovery procedure (see Section 3), a single zero byte must 860 | be placed by client software in ARG, and this is what the 861 | specificationless application discovery code must search for. 862 | The application is then identified exclusively by the 863 | implementation name, which becomes the application name. 864 | 865 | 2. There is no concept of Specification routines vs Implementation- 866 | specific routines as defined in Section 2.4. Instead, the whole 867 | range of function numbers 1-254 are implementation-specific 868 | routines, that is, the specificationless application may define 869 | these routines as desired (or define no routines at all). Note 870 | however that the API information routine (see Section 2.5) is 871 | still mandatory, and that function number 255 is still reserved. 872 | 873 | 3. Since specificationless application do not conform to any API 874 | specification, the API specification version number provided by 875 | the API information routine (see Section 2.5) does not make 876 | sense. This routine should return this value as Version 0.0 877 | (that is, DE=0). 878 | 879 | Apart from these differences, the whole set of rules enumerated in 880 | this document still apply, including the discovery procdedure and the 881 | rules that refer to routine registers usage. 882 | 883 | Specificationless applications will tipically take the form of TSRs 884 | that install on RAM, but this is not mandatory and ROM 885 | specificationless applications are allowed as well. 886 | 887 | 888 | Appendix A. Acknowledgements 889 | 890 | This document was produced using xml2rfc v1.34 (of 891 | http://xml.resource.org/) from a source in RFC-2629 XML format. 892 | 893 | 894 | Appendix B. Document version history 895 | 896 | o Version 0.2 897 | * Added the "The big picture" section (Section 1.5) 898 | 899 | o Version 0.3 900 | * "The big picture" section (Section 1.5) has been renamed to 901 | "Sample scenario" 902 | * Added the routine to call address #4010 in the "The RAM helper" 903 | section (Section 4) 904 | 905 | o Version 0.4 906 | * The maximum length for implementation names has been changed 907 | from 64 to 63 characters (Section 1.4 and Section 2.5), so a 908 | full length name will be 64 bytes long including the zero 909 | termination byte. 910 | * Slightly modified the explanation of the discovery procedure to 911 | make more clear (sort of) the fact that the API identifier must 912 | be zero-terminated only when it is copied to ARG (that is, the 913 | zero byte is NOT part of the identifier itself). See 914 | Section 3.2 and Section 3.3. 915 | * Added information about the alternate registers usage in 916 | Section 2.3. 917 | * Added the "avoid segment #FF" rule (see Section 2.8). 918 | * The usage of "#" as hexadecimal prefix is now noted in the 919 | glossary (see Section 1.4). 920 | 921 | o Version 1.0 922 | * Added the rule about routine names (see Section 2.9). 923 | 924 | o Version 1.1 925 | * Added the concept of specificationless applications (see 926 | Section 5). 927 | * Modified the RAM helper specification (see Section 4) so that: 928 | + Installing the RAM helper when it is not installed is now 929 | optional (RAM based implementation installers may refuse to 930 | install instead). 931 | + When invoking the EXTBIO hook with A=#FF, additional 932 | information is returned in registers A and DE. 933 | + The routine for invoking a routine in the segment address 934 | #4010 has been replaced by a routine for invoking a routine 935 | in a jump table of up to 64 entries. 936 | + A mappers table is now generated in addition to the jump 937 | table. 938 | 939 | 940 | Author's Address 941 | 942 | Nestor Soriano 943 | MSX community 944 | 945 | Email: konamiman@konamiman.com 946 | URI: http://www.konamiman.com 947 | 948 | 949 | -------------------------------------------------------------------------------- /txt/unapiintro.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Version 0.2 N. Soriano 5 | MSX community 6 | July 25, 2007 7 | 8 | 9 | Introduction to MSX-UNAPI 10 | 11 | Abstract 12 | 13 | This document introduces MSX-UNAPI, a standard procedure for 14 | defining, discovering and using new APIs (Application Program 15 | Interfaces) for MSX computers. For the detailed specification, 16 | please read the MSX-UNAPI specification document. 17 | 18 | 19 | Table of Contents 20 | 21 | 1. Motivation 22 | 2. Key concepts 23 | 3. Example 24 | Appendix A. Acknowledgements 25 | Appendix B. Document version history 26 | Author's Address 27 | 28 | 29 | 1. Motivation 30 | 31 | In the last years, several MSX hobbyists have developed various kinds 32 | of amateur hardware for MSX computers. Usually, each of these pieces 33 | of hardware comes with a ROM containing an API (Application Program 34 | Interface), which consists of a series of routines that allow 35 | developers to interact with the hardware. 36 | 37 | As each device has its own API, devices are not interchangeable from 38 | the software point of view. For example, InterNestor Lite works only 39 | with the ethernet card ObsoNET, and it will not work with any other 40 | card developed in the future. 41 | 42 | The MSX-UNAPI specification aims to solve this problem, by defining a 43 | set of rules for creating interchangeable API implementations. 44 | 45 | 46 | 2. Key concepts 47 | 48 | The complete MSX-UNAPI specification may seem complicated at first 49 | glance, but it relies on just a few key concepts, which are 50 | enumerated below. 51 | 52 | Note: In the following text, the terms "API specification" and "API 53 | implementation" refer to API specifications and implementations that 54 | obey the rules of the MSX-UNAPI specification. 55 | 56 | o An "API specification" is a set of routines for performing a set 57 | of concrete tasks. Each specification has a short alphanumeric 58 | identifier that serves as a signature to uniquely distinguish it 59 | from other specifications. 60 | 61 | For example, an API specification for ethernet cards could have 62 | the identifier ETHERNET and be composed of three routines: send 63 | packet, receive packet and check network state. 64 | 65 | o An "API implementation" is the realisation in code of an API 66 | specification. There may be several implementation of the same 67 | API specification, and since all of them implement the same set of 68 | routines, they are interchangeable. Each implementation has a 69 | short name that serves to distinguish it from other 70 | implementations. 71 | 72 | For example, "ObsoNET BIOS" and "Dumas BIOS" could be the names 73 | of two implementations of the API whose identifier is ETHERNET. 74 | A TCP/IP stack prepared to deal with the ETHERNET API could 75 | work with both implementations. 76 | 77 | o The MSX-UNAPI specification provides a set of minimal rules that 78 | must be followed by all compliant API specifications and 79 | implementations. This is done to ease the development of software 80 | that make use of API implementations. 81 | 82 | The main rules are: API implementation code must be placed in 83 | ROM, in mapped RAM or in page 3 RAM; there must be one single 84 | call point for all the API routines (routine number is passed 85 | in register A); and there must be one routine that informs 86 | about the API name and version. All of this is detailed in the 87 | MSX-UNAPI specification document. 88 | 89 | o More than one implementation of a given API specification may be 90 | installed at the same time. The MSX extended BIOS mechanism is 91 | used to discover and locate the available implementations. 92 | 93 | Usually, when more than one implementation is found, it does 94 | not matter which one is used to perform the tasks offered by 95 | the API specification. However, if necessary, implementations 96 | can be distinguished thanks to their names. 97 | 98 | 99 | 3. Example 100 | 101 | This example provides pseudo-code for an hypothetical TCP/IP stack 102 | that relies on the ETHERNET API to send and receive data. In the 103 | code, the names A, B, C, HL and DE refer to Z80 registers; other 104 | names refer to routines or variables. Semicolons (;) indicate that 105 | the rest of the line is a comment. 106 | 107 | Please refer to the MSX-UNAPI specification document for details 108 | about how to call API routines and extended BIOS, as well as Z80 109 | registers usage. 110 | 111 | PRINT "Welcome to this wonderful ETHERNET API aware TCP/IP stack!" 112 | PRINT "Now I'm going to search for ETHERNET API implementations... 113 | 114 | POKE &HF847,"ETHERNET"+0 115 | A=0 116 | B=0 117 | DE=&H2222 118 | CALL &HFFCA ; The EXTBIO hook 119 | 120 | IF B=0 THEN 121 | PRINT "Ooops!" 122 | PRINT "I haven't found any ETHERNET API implementation!" 123 | END 124 | ENDIF 125 | 126 | PRINT "I've found "+B+" implementations of the ETHERNET API!" 127 | PRINT "I'll use implementation with index 1" 128 | 129 | ; Obtain implementation location (address, slot and/or segment) 130 | ; and as the first task, obtain its name and version 131 | 132 | POKE &HF847,"ETHERNET"+0 ; Not necessary if memory not modified 133 | A=1 ; The implementation index 134 | DE=&H2222 135 | CALL &HFFCA ; The EXTBIO hook 136 | ApiSlot=A 137 | ApiSegment=B 138 | ApiEntry=HL 139 | 140 | A=0 ; 0 is the index for the API information routine 141 | CALL EXE_UNAPI 142 | PRINT "The API name is: "+READ_UNAPI(HL) 143 | PRINT "The API version is: "+B+"."+C 144 | 145 | ; Now assume that per the ETHERNET API specification, 146 | ; routine 3 returns A=1 if network is available or 0 otherwise 147 | 148 | A=3 149 | CALL EXE_UNAPI 150 | IF A=0 THEN 151 | PRINT "Ooops! No network!" 152 | END 153 | ENDIF 154 | 155 | PRINT "Network OK! Let's internetwork!" 156 | ; etc etc... 157 | 158 | 159 | ;--- This routine calls the API routine whose index is passed in A 160 | 161 | EXE_UNAPI: 162 | IF ApiEntry>=&HC000 THEN 163 | CALL ApiEntry 164 | ELSE IF ApiSegment=&HFF THEN 165 | CALL ApiEntry AT SLOT ApiSlot 166 | ELSE 167 | CALL ApiEntry AT SEGMENT ApiSegment AT SLOT ApiSlot 168 | RETURN 169 | 170 | 171 | ;--- This routine reads the API memory whose address 172 | ;--- is passed as parameter, until a zero is found 173 | 174 | READ_UNAPI(Address): 175 | HL=Address 176 | String="" 177 | LOOP: 178 | IF Address>=&HC000 THEN 179 | A=PEEK(HL) 180 | ELSE IF ApiSegment=&HFF THEN 181 | A=READ (HL) AT SLOT ApiSlot 182 | ELSE 183 | A=READ (HL) AT SEGMENT ApiSegment AT SLOT ApiSlot 184 | ENDIF 185 | IF A<>0 THEN 186 | String=String+A 187 | HL=HL+1 188 | GOTO LOOP 189 | RETURN String 190 | 191 | 192 | Appendix A. Acknowledgements 193 | 194 | This document was produced using xml2rfc v1.32 (of 195 | http://xml.resource.org/) from a source in RFC-2629 XML format. 196 | 197 | 198 | Appendix B. Document version history 199 | 200 | o Version 0.2 201 | * Done some minor changes proposed by Tanni, in order 202 | to clarify the text. 203 | 204 | 205 | Author's Address 206 | 207 | Nestor Soriano 208 | MSX community 209 | 210 | Email: konamiman@konamiman.com 211 | URI: http://www.konamiman.com 212 | 213 | 214 | --------------------------------------------------------------------------------