├── .gitignore ├── Makefile ├── README.md ├── bin └── .empty ├── common.mk ├── doc ├── README ├── bdos_calls.txt ├── bdosfunc_summary.txt ├── bios_func.txt ├── fcb.txt └── iobyte.txt ├── esrc ├── empty │ ├── empty.c │ └── empty.mk ├── fileop │ ├── fileop.c │ └── fileop.mk └── modprnex01 │ ├── modprnex01.c │ └── modprnex01.mk ├── examples.mk ├── lbin └── .empty ├── libraries.mk ├── lsrc ├── binpak.c └── load.c ├── src ├── cpm │ ├── cpm0.lst │ ├── cpm0.rel │ ├── cpm0.s │ └── cpmbdos.c ├── hw │ ├── common │ │ ├── hw_common.c │ │ └── hw_common.h │ └── modprn02 │ │ ├── hw_modprn02.c │ │ └── hw_modprn02.h ├── include │ ├── common_datatypes.h │ ├── cpmbdos.h │ └── cprintf.h └── syslib │ ├── ansi_term.c │ ├── ansi_term.h │ ├── cpm_sysfunc.c │ ├── cpm_sysfunc.h │ └── cprintf.c └── tools.mk /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | # Libraries 6 | *.lib 7 | *.a 8 | 9 | # Shared objects (inc. Windows DLLs) 10 | *.dll 11 | *.so 12 | *.so.* 13 | *.dylib 14 | 15 | # Executables 16 | *.exe 17 | *.out 18 | *.app 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include common.mk 2 | 3 | all: tools libraries examples \ 4 | example-empty fileop-empty example-modprnex01 5 | 6 | clean: tools-clean libraries-clean examples-clean 7 | rm -f $(BIN_DIR)/* 8 | 9 | include tools.mk # Local tools 10 | include libraries.mk # Libraries 11 | include examples.mk # Examples 12 | 13 | include esrc/empty/empty.mk 14 | include esrc/fileop/fileop.mk 15 | include esrc/modprnex01/modprnex01.mk 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sdcc-cpm-example 2 | ================ 3 | 4 | HELLO WORLD / experimentation program running under CP/M-80, crosscompiled with SDCC (Small Devices C Compiler). 5 | It's based on sources from http://n8vem-sbc.pbworks.com/ (maily found in the unified-bios package). 6 | 7 | This comes without any warranty: It's mostly a experimentation code for my N8VEM SBC V2. 8 | -------------------------------------------------------------------------------- /bin/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzlab/sdcc-cpm-example/169e48842a7bf96d3baeb16a06e9ef1aafb5c271/bin/.empty -------------------------------------------------------------------------------- /common.mk: -------------------------------------------------------------------------------- 1 | # Prefixes 2 | #COMPILER_PREFIX = /opt/local/ 3 | COMPILER_PREFIX = $(SDCC_PREFIX) 4 | COMPILER_LIBS = $(COMPILER_PREFIX)/share/sdcc/lib/z80/ 5 | 6 | # Options 7 | QUIET = @ 8 | 9 | # SDCC commands 10 | CCC = $(COMPILER_PREFIX)/bin/sdcc 11 | CAS = $(COMPILER_PREFIX)/bin/sdasz80 12 | CLD = $(COMPILER_PREFIX)/bin/sdldz80 13 | 14 | # Local CC 15 | CC = gcc 16 | 17 | # Misc local commands 18 | ECHO = echo 19 | COPY = cp 20 | MOVE = mv 21 | SED = sed 22 | 23 | # Project directories 24 | SRC_DIR = src/ 25 | CPM_SRC_DIR = $(SRC_DIR)/cpm 26 | SYSLIB_SRC_DIR = $(SRC_DIR)/syslib 27 | HWLIB_SRC_DIR = $(SRC_DIR)/hw 28 | BIN_DIR = bin/ 29 | 30 | LSRC_DIR = lsrc/ 31 | LBIN_DIR = lbin/ 32 | 33 | ESRC_DIR = esrc/ 34 | 35 | INCLUDE_DIR = -I$(SRC_DIR)/include -I$(SRC_DIR) 36 | 37 | # Compilation / Assembly / Linking flags 38 | CCC_FLAGS = -c -mz80 -D__SDCC__=1 $(INCLUDE_DIR) 39 | CAS_FLAGS = -plosff 40 | CLD_FLAGS = 41 | 42 | -------------------------------------------------------------------------------- /doc/README: -------------------------------------------------------------------------------- 1 | Most of the documentation in this directory is a text rendering of the following sources: 2 | * http://www.seasip.demon.co.uk/Cpm/index.html 3 | 4 | Please, refer to these for up-to-date reference, the text contained here is just for 5 | quick reference. 6 | -------------------------------------------------------------------------------- /doc/bdos_calls.txt: -------------------------------------------------------------------------------- 1 | ****** BDOS system calls ****** 2 | So far, this list covers CP/M 1, 2 and 3, with partial MP/M and DOSPLUS 3 | details. Some groupings I use are: 4 | CP/M 1.3 5 | The 1975 version of CP/M available as source from the Unofficial CP/ 6 | M Website. The version number of 1.3 isn't given in the source. 7 | CP/M 2.2 8 | In addition to CP/M-80 version 2.2, this also includes CP/M-86 1.x, CP/M- 9 | 68000 1.x and CP/M-Z8000 1.x. 10 | MP/M II and above 11 | * MP/M II 12 | * CP/M 3 (CP/M Plus) 13 | * multiuser 16-bit systems such as DR-MDOS and REAL/32 14 | * single-user 16-bit systems after CP/M-86 1.1 and before DRDOS 3.41. 15 | This covers DOSPLUS and "Personal CP/M-86 v2.0/2". 16 | CP/M 3 and above 17 | All the above systems except MP/M II 18 | CP/Net redirector 19 | Any CP/Net client that allows access to drives on an MP/M server. 20 | The system calls for REAL/32 (a descendent of Concurrent CP/M) used to be 21 | online at http://development.imsltd.com/calls/allcalls.htm. This site appears 22 | to be offline but some parts of its content can be retrieved by entering the 23 | above URL into the archive.org wayback machine. 24 | ***** How to make a call ***** 25 | To make a BDOS call in an 8-bit CP/M, use: 26 | LD DE,parameter 27 | LD C,function 28 | CALL 5 29 | and in a 16-bit CP/M, use: 30 | MOV DX,parameter 31 | MOV CL,function 32 | INT 0E0h 33 | In CP/M 1.x, 8-bit values are returned in A and 16-bit values in BA. In later 34 | 8-bit versions, they are also returned in L and HL respectively. 35 | 16-bit systems return 8-bit values in AL = BL and 16-bit in AX = BX. 36 | The information in this list overlaps with the Interrupt List section on INT 37 | E0h to some extent. 38 | ***** DOSPLUS and DOS+ ***** 39 | Note that DOS + is a CP/M extended BDOS. For information on "DOSPLUS 1.2" and 40 | "Personal CP/M-86 v2.0/2" see sections applicable to CP/M-86 v4; the underlying 41 | BDOS is version 4.1 in both cases. 42 | =============================================================================== 43 | ***** The calls ***** 44 | **** BDOS function 0 (P_TERMCPM) - System Reset **** 45 | * Supported by: CP/M 1, 2, 3; MP/M; Concurrent CP/M. * 46 | Entered with C=0. Does not return. 47 | Quit the current program, return to command prompt. This call is hardly ever 48 | used in 8-bit CP/M since the RST 0 instruction does the same thing and saves 49 | four bytes. 50 | **** BDOS function 0 (P_TERMCPM) - System Reset **** 51 | * Supported by: Single-user CP/M-86. * 52 | Entered with CL=0, DL=0 or 1. Does not return. 53 | Quit the current program, return to command prompt. If DL is 0, the memory used 54 | by the program is deallocated; if DL is 1, it remains resident. 55 | =============================================================================== 56 | **** BDOS function 1 (C_READ) - Console input **** 57 | * Supported by: All versions * 58 | Entered with C=1. Returns A=L=character. 59 | Wait for a character from the keyboard; then echo it to the screen and return 60 | it. 61 | =============================================================================== 62 | **** BDOS function 2 (C_WRITE) - Console output **** 63 | * Supported by: All versions * 64 | Entered with C=2, E=ASCII character. 65 | Send the character in E to the screen. Tabs are expanded to spaces. Output can 66 | be paused with ^S and restarted with ^Q (or any key under versions prior to CP/ 67 | M 3). While the output is paused, the program can be terminated with ^C. 68 | =============================================================================== 69 | **** BDOS function 3 (A_READ) - Auxiliary (Reader) input **** 70 | * Supported by: All CP/M versions except MP/M and Concurrent CP/M * 71 | Entered with C=3. Returns A=L=ASCII character 72 | Note that this call can hang if the auxiliary input never sends data. 73 | **** BDOS function 3 - Raw console input **** 74 | * Supported by: MP/M. * 75 | Entered with C=3. Returns A=L=ASCII character 76 | Reads a character from the console without checking for ^S / ^Q. 77 | =============================================================================== 78 | **** BDOS function 4 (A_WRITE) - Auxiliary (Punch) output **** 79 | * Supported by: All versions except MP/M and Concurrent CP/M. * 80 | Entered with C=4, E=ASCII character. 81 | If the device is permanently not ready, this call can hang. 82 | **** BDOS function 4 - Raw console output **** 83 | * Supported by: MP/M. * 84 | Entered with C=4, E=ASCII character. 85 | Outputs the character, without expanding tabs or checking for ^S / ^Q. 86 | =============================================================================== 87 | **** BDOS function 5 (L_WRITE) - Printer output **** 88 | * Supported by: All versions * 89 | Entered with C=2, E=ASCII character. 90 | If the printer is permanently offline or busy, this call can hang. 91 | =============================================================================== 92 | **** BDOS function 6 - Detect memory size **** 93 | * Supported by: CP/M 1.3 only * 94 | Entered with C=6. Returns the base address of the CCP. 95 | **** BDOS function 6 (C_RAWIO) - Direct console I/O **** 96 | * Supported by: CP/M 1.4 and later, with variations * 97 | Entered with C=6, E=code. Returned values (in A) vary. 98 | E=0FFh 99 | Return a character without echoing if one is waiting; zero if none is 100 | available. In MP/M 1, this works like E=0FDh below and waits for a 101 | character. 102 | E=0FEh 103 | [CP/M3, NovaDOS, Z80DOS, DOS+] Return console input status. Zero if no 104 | character is waiting, nonzero otherwise. 105 | E=0FDh 106 | [CP/M3, DOS+] Wait until a character is ready, return it without echoing. 107 | E=0FCh 108 | [DOS+] One-character lookahead - return the next character waiting but 109 | leave it in the buffer. 110 | Values of E not supported on a particular system will output the character. 111 | Under CP/M 2 and lower, direct console functions may interact undesirably with 112 | non-direct ones, since certain buffers may be bypassed. Do not mix them. 113 | =============================================================================== 114 | **** BDOS function 7 - Get I/O byte **** 115 | * Supported by: CP/M 2 and lookalikes. Not supported in MP/M. * 116 | Entered with C=7. Returns I/O byte. 117 | Here's a description of how the IOBYTE works. 118 | **** BDOS function 7 (A_STATIN) - Auxiliary Input status **** 119 | * Supported by: CP/M 3 and above. Not supported in MP/M. * 120 | Entered with C=7. Returns A=0 or 0FFh. 121 | 0FFh is returned if the Auxiliary Input device has a character ready; otherwise 122 | 0 is returned. 123 | =============================================================================== 124 | **** BDOS function 8 - Set I/O byte **** 125 | * Supported by: CP/M 2 and lookalikes. Not supported in MP/M. * 126 | Entered with C=8, E=I/O byte. 127 | Here's a description of how the IOBYTE works. 128 | **** BDOS function 8 (A_STATOUT) - Auxiliary Output status **** 129 | * Supported by: CP/M 3 and above. Not supported in MP/M. * 130 | Entered with C=8. Returns A=0 or 0FFh. 131 | 0FFh is returned if the Auxiliary Output device is ready for characters; 132 | otherwise 0 is returned. 133 | =============================================================================== 134 | **** BDOS function 9 (C_WRITESTR) - Output string **** 135 | * Supported by: All versions * 136 | Entered with C=9, DE=address of string. 137 | Display a string of ASCII characters, terminated with the $ character. Thus the 138 | string may not contain $ characters - so, for example, the VT52 cursor 139 | positioning command ESC Y y+32 x+32 will not be able to use row 4. 140 | Under CP/M 3 and above, the terminating character can be changed using BDOS 141 | function 110. 142 | =============================================================================== 143 | **** BDOS function 10 (C_READSTR) - Buffered console input **** 144 | * Supported by: All versions, with variations * 145 | Entered with C=0Ah, DE=address or zero. 146 | This function reads characters from the keyboard into a memory buffer until 147 | RETURN is pressed. The Delete key is handled correctly. In later versions, more 148 | features can be used at this point; ZPM3 includes a full line editor with 149 | recall of previous lines typed. 150 | On entry, DE is the address of a buffer. If DE=0 (in CP/M-86 versions 151 | DX=0FFFFh), the DMA_address is used (CP/M 3 and later) and the buffer already 152 | contains data: 153 | DE=address: DE=0 / DX=0FFFFh: 154 | buffer: DEFB size buffer: DEFB size 155 | DEFB ? DEFB len 156 | DEFB bytes DEFB bytes 157 | The value at buffer+0 is the amount of bytes available in the buffer. Once the 158 | limit has been reached, no more can be added, although the line editor can 159 | still be used. 160 | If DE=0 (in 16-bit versions, DX=0FFFFh) the next byte contains the number of 161 | bytes already in the buffer; otherwise this is ignored. On return from the 162 | function, it contains the number of bytes present in the buffer. 163 | The bytes typed then follow. There is no end marker. 164 | =============================================================================== 165 | **** BDOS function 11 (C_STAT) - Console status **** 166 | * Supported by: All versions * 167 | Entered with C=0Bh. Returns A=L=status 168 | Returns A=0 if no characters are waiting, nonzero if a character is waiting. 169 | =============================================================================== 170 | **** BDOS function 12 - Lift head **** 171 | * Supported by: Version 1 * 172 | Entered with C=0Ch. Returns HL=0 173 | Has no effect except to return HL=0. 174 | **** BDOS function 12 (S_BDOSVER) - Return version number **** 175 | * Supported by: Versions 2.0 and later * 176 | Entered with C=0Ch. Returns B=H=system type, A=L=version number. 177 | The system type is subdivided into a machine type and a CP/M type. The machine 178 | type occupies the high nibble of the byte; the CP/M type is a bitmapped field 179 | stored in the low nibble. 180 | Machine types: CP/M types: Version numbers: 181 | 0 - 8080 Bit 0 set for MP/M 00h - Version 1 (see Lift Head 182 | above) 183 | 1 - 8086 Bit 1 set for CP/Net 20h - Version 2.0 184 | 2 - 68000/Z8000 (ie:network present) 21h - Version 2.1 185 | Bit 2 set in 16-bit 22h - Version 2.2 186 | multi-user OSes 25h - Version 2.5 (DOS +) 187 | 28h - Version 2.8 (Personal CP/M-80) 188 | For plain CP/M, 30h - Version 3.0 (MP/M II, MP/M-86) 189 | the CP/M type is 0. 31h - Version 3.1 (CP/M Plus) 190 | 33h - Version 3.3 (Apricot PCP/M-86) 191 | 41h - Version 4.1 (DOSPLUS 1) 192 | 50h - Version 5.0 (DOSPLUS 2) 193 | Sources differ on the value returned by DOSPLUS 2. Most sources follow the 194 | Interrupt List and say 60h; my experiments on the real thing say 50h. 195 | Confusingly, CP/M-86 v1.1 returns 0022h (ie, "8080 CP/M v2.2"). 196 | It is interesting to note that the version numbers returned by DRDOS and Novell 197 | DOS follow this system; DRDOS 3, 5 and 6 are version 6.x, Novell DOS 7 is 198 | version 7.2 and DR-OpenDOS is version 7.3. However these systems rather 199 | unsportingly fail to provide an INT 0E0h call to get the version number; you 200 | have to use INT 21h with AX=4452h. 201 | =============================================================================== 202 | **** BDOS function 13 (DRV_ALLRESET) - Reset discs **** 203 | * Supported by: All versions. * 204 | Entered with C=0Dh. Returned values vary. 205 | Resets disc drives. Logs out all discs and empties disc buffers. Sets the 206 | currently selected drive to A:. Any drives set to Read-Only in software become 207 | Read-Write; replacement BDOSses tend to leave them Read-Only. 208 | In versions 1 and 2, logs in drive A: and returns 0FFh if there is a file 209 | present whose name begins with a $, otherwise 0. Replacement BDOSses may modify 210 | this behaviour. 211 | In multitasking versions, returns 0 if succeeded, or 0FFh if other processes 212 | have files open on removable or read-only drives. 213 | =============================================================================== 214 | **** BDOS function 14 (DRV_SET) - Select disc **** 215 | * Supported by: All versions * 216 | Entered with C=0Eh, E=drive number. Returns L=A=0 or 0FFh. 217 | The drive number passed to this routine is 0 for A:, 1 for B: up to 15 for P:. 218 | Sets the currently selected drive to the drive in A; logs in the disc. Returns 219 | 0 if successful or 0FFh if error. Under MP/M II and later versions, H can 220 | contain a physical error number. 221 | =============================================================================== 222 | **** BDOS function 15 (F_OPEN) - Open file **** 223 | * Supported by: All versions * 224 | Entered with C=0Fh, DE=FCB address. Returns error codes in BA and HL. 225 | This function opens a file to read or read/write. The FCB is a 36-byte data 226 | structure, most of which is maintained by CP/M. Look_here for details. 227 | The FCB should have its DR, Fn and Tn fields filled in, and the four fields EX, 228 | S1, S2 and RC set to zero. Under CP/M 3 and later, if CR is set to 0FFh then on 229 | return CR will contain the last_record_byte_count. Note that CR should normally 230 | be reset to zero if sequential access is to be used. 231 | Under MP/M II, the file is normally opened exclusively - no other process can 232 | access it. Two bits in the FCB control the mode the file is opened in: 233 | * F5' - set to 1 for "unlocked" mode - other programs can use the file. 234 | * F6' - set to 1 to open the file in read-only mode - other programs can 235 | use the file read-only. If both F6' and F5' are set, F6' wins. 236 | If the file is opened in "unlocked" mode, the file's identifier (used for 237 | record locking) will be returned at FCB+21h. 238 | Under MP/M II and later versions, a password can be supplied to this function 239 | by pointing the DMA_address at the password. 240 | On return from this function, A is 0FFh for error, or 0-3 for success. Some 241 | versions (including CP/M 3) always return zero; others return 0-3 to indicate 242 | that an image of the directory entry is to be found at (80h+20h*A). 243 | If A=0FFh, CP/M 3 returns a hardware_error in H and B. It also sets some bits 244 | in the FCB: 245 | * F7' is set if the file is read-only because writing is password protected 246 | and no password was supplied; 247 | * F8' is set if the file is read-only because it is a User 0 system file 248 | opened from another user area. 249 | **** BDOS function 15 - Open directory **** 250 | * Supported by: CP/M-86 v4. * 251 | Entered with C=0Fh, DE=FCB address. Returns error codes in BA and HL. 252 | This function is called with bit 7 of the FCB drive byte set, to distinguish it 253 | from a normal Open File call. 254 | Byte 0Ch (Extent) of the FCB holds a subfunction code, 0-3: 255 | * 0 - Change current directory 256 | * 1 - Assign floating drive N: to specified directory 257 | * 2 - Assign floating drive O: to specified directory 258 | * 3 - Assign floating drive P: to specified directory 259 | Each floating drive maps to a directory on an existing drive. I refer to the 260 | existing drive as the 'host' drive. 261 | =============================================================================== 262 | **** BDOS function 16 (F_CLOSE) - Close file **** 263 | * Supported by: All versions * 264 | Entered with C=10h, DE=FCB address. Returns error codes in BA and HL. 265 | This function closes a file, and writes any pending data. This function should 266 | always be used when a file has been written to. 267 | On return from this function, A is 0FFh for error, or 0-3 for success. Some 268 | versions always return zero; others return 0-3 to indicate that an image of the 269 | directory entry is to be found at (80h+20h*A). 270 | Under CP/M 3, if F5' is set to 1 then the pending data are written and the file 271 | is made consistent, but it remains open. 272 | If A=0FFh, CP/M 3 returns a hardware_error in H and B. 273 | **** BDOS function 16 - Close directory **** 274 | * Supported by: CP/M-86 v4. * 275 | Entered with C=10h, DE=FCB address. Returns error codes in BA and HL. 276 | This function is called with bit 7 of the FCB drive byte set, to distinguish it 277 | from a normal Close File call. 278 | This sets the current directory on the specified drive to the root directory. 279 | =============================================================================== 280 | **** BDOS function 17 (F_SFIRST) - search for first **** 281 | * Supported by: All versions * 282 | Entered with C=11h, DE=address of FCB. Returns error codes in BA and HL. 283 | Search for the first occurrence of the specified file; the filename should be 284 | stored in the supplied FCB. The filename can include ? marks, which match 285 | anything on disc. If the first byte of the FCB is ?, then any directory entry 286 | (including disc labels, date stamps etc.) will match. The EX byte is also 287 | checked; normally it should be set to zero, but if it is set to ? then all 288 | suitable extents are matched. 289 | Returns A=0FFh if error (CP/M 3 returns a hardware_error in H and B), or A=0- 290 | 3 if successful. The value returned can be used to calculate the address of a 291 | memory image of the directory entry; it is to be found at DMA+A*32. 292 | Under CP/M-86 v4, if the first byte of the FCB is '?' or bit 7 of the byte is 293 | set, subdirectories as well as files will be returned by this search. 294 | =============================================================================== 295 | **** BDOS function 18 (F_SNEXT) - search for next **** 296 | * Supported by: All versions * 297 | Entered with C=12h, (DE=address of FCB)?. Returns error codes in BA and HL. 298 | This function should only be executed immediately after function 17 or another 299 | invocation of function 18. No other disc access functions should have been 300 | used. 301 | Function 18 behaves exactly as number 17, but finds the next occurrence of the 302 | specified file after the one returned last time. The FCB parameter is not 303 | documented, but Jim Lopushinsky states in LD301.DOC: 304 | In none of the official Programmer's Guides for any version of CP/ 305 | M does it say that an FCB is required for Search Next (function 18). 306 | However, if the FCB passed to Search First contains an unambiguous 307 | file reference (i.e. no question marks), then the Search Next 308 | function requires an FCB passed in reg DE (for CP/M-80) or DX (for 309 | CP/M-86). 310 | =============================================================================== 311 | **** BDOS function 19 (F_DELETE) - delete file **** 312 | * Supported by: All versions * 313 | Entered with C=13h, DE=address of FCB. Returns error codes in BA and HL. 314 | Deletes all directory entries matching the specified filename. The name can 315 | contain ? marks. Returns A=0FFh if error, otherwise 0-3 (CP/M 3 returns a 316 | hardware_error in H and B). 317 | Under CP/M 3, if bit F5' is set to 1, the file remains but any password 318 | protection is removed. If the file has any password protection at all, the DMA 319 | address must be pointing at the password when this function is called. 320 | **** BDOS function 19 - remove directory **** 321 | * Supported by: CP/M-86 v4. * 322 | Entered with C=13h, DE=address of FCB. Returns error codes in BA and HL. 323 | This function is called with bit 7 of the FCB drive byte set, to distinguish it 324 | from a normal Delete File call. 325 | If the specified subdirectory exists and is empty, removes it. 326 | =============================================================================== 327 | **** BDOS function 20 (F_READ) - read next record **** 328 | Entered with C=14h, DE=address of FCB. Returns error codes in BA and HL. 329 | * Supported by: All versions * 330 | Load a record (normally 128 bytes, but under CP/M 3 this can be a multiple of 331 | 128 bytes) at the previously specified DMA_address. Values returned in A are: 332 | 0 333 | OK, 334 | 1 335 | end of file, 336 | 9 337 | invalid FCB, 338 | 10 339 | (CP/M) media changed; (MP/M) FCB checksum error, 340 | 11 341 | (MP/M) unlocked file verification error, 342 | 0FFh 343 | hardware error. 344 | If on return A is not 0FFh, H contains the number of 128-byte records read 345 | before the error (MP/M II and later). 346 | =============================================================================== 347 | **** BDOS function 21 (F_WRITE) - write next record **** 348 | * Supported by: All versions * 349 | Entered with C=15h, DE=address of FCB. Returns error codes in BA and HL. 350 | Write a record (normally 128 bytes, but can be a multiple of 128 bytes) to be 351 | found at previously specified DMA_address. Values returned in A are: 352 | 0 353 | OK, 354 | 1 355 | directory full, 356 | 2 357 | disc full, 358 | 8 359 | (MP/M) record locked by another process, 360 | 9 361 | invalid FCB, 362 | 10 363 | (CP/M) media changed; (MP/M) FCB checksum error, 364 | 11 365 | (MP/M) unlocked file verification error, 366 | 0FFh 367 | hardware error. 368 | If on return A is not 0FFh, H contains the number of 128-byte records written 369 | before the error (CP/M 3 only). 370 | =============================================================================== 371 | **** BDOS function 22 (F_MAKE) - create file **** 372 | * Supported by: All versions * 373 | Entered with C=16h, DE=address of FCB. Returns error codes in BA and HL. 374 | Creates the file specified. Returns A=0FFh if the directory is full. 375 | If the file exists already, then the default action is to return to the command 376 | prompt, but CP/M 3 may return a hardware_error instead. 377 | Under MP/M II, set F5' to open the file in "unlocked"_mode. 378 | Under MP/M II and later versions, set F6' to create the file with a password; 379 | the DMA_address should point at a 9-byte buffer: 380 | DEFS 8 ;Password 381 | DEFB 1 ;Password_mode 382 | **** BDOS function 22 - create directory **** 383 | * Supported by: CP/M-86 v4. * 384 | Entered with C=16h, DE=address of FCB. Returns error codes in BA and HL. 385 | This function is called with bit 7 of the FCB drive byte set, to distinguish it 386 | from a normal Create File call. 387 | Creates a new subdirectory in the current directory, with the given name. 388 | =============================================================================== 389 | **** BDOS function 23 (F_RENAME) - Rename file **** 390 | * Supported by: All versions * 391 | Entered with C=17h, DE=address of FCB. Returns error codes in BA and HL. 392 | Renames the file specified to the new name, stored at FCB+16. This function 393 | cannot rename across drives so the "drive" bytes of both filenames should be 394 | identical. Returns A=0-3 if successful; A=0FFh if error. Under CP/M 3, if H is 395 | zero then the file could not be found; if it is nonzero it contains a hardware 396 | error number. 397 | Under Concurrent CP/M, set F5' if an extended lock on the file should be held 398 | through the rename. Otherwise the lock will be released. 399 | =============================================================================== 400 | **** BDOS function 24 (DRV_LOGINVEC) - Return bitmap of logged-in drives **** 401 | * Supported by: All versions * 402 | Entered with C=18h. Returns bitmap in HL. 403 | Bit 7 of H corresponds to P: while bit 0 of L corresponds to A:. A bit is set 404 | if the corresponding drive is logged in. 405 | In DOSPLUS v2.1, the three top bits (for the floating drives) will mirror the 406 | status of the corresponding host drives). This does not happen in earlier 407 | DOSPLUS / Personal CP/M-86 systems. 408 | =============================================================================== 409 | **** BDOS function 25 (DRV_GET) - Return current drive **** 410 | * Supported by: All versions * 411 | Entered with C=19h. Returns drive in A. Returns currently selected drive. 0 => 412 | A:, 1 => B: etc. 413 | =============================================================================== 414 | **** BDOS function 26 (F_DMAOFF) - Set DMA address **** 415 | * Supported by: All versions * 416 | Entered with C=1Ah, DE=address. 417 | Set the Direct Memory Access address; a pointer to where CP/M should read or 418 | write data. Initially used for the transfer of 128-byte records between memory 419 | and disc, but over the years has gained many more functions. 420 | =============================================================================== 421 | **** BDOS function 27 (DRV_ALLOCVEC) - Return address of allocation map **** 422 | * Supported by: All versions, but differs in banked versions. * 423 | Entered with C=1Bh. Returns address in HL (16-bit versions use ES:BX). 424 | Return the address of the allocation bitmap (which blocks are used and which 425 | are free) in HL. Under banked CP/M 3 and MP/M, this will be an address in bank 426 | 0 (the system bank) and not easily accessible. 427 | Under previous versions, the format of the bitmap is a sequence of bytes, with 428 | bit 7 of the byte representing the lowest-numbered block on disc, and counting 429 | starting at block 0 (the directory). A bit is set if the corresponding block is 430 | in use. 431 | Under CP/M 3, the allocation vector may be of this form (single-bit) or 432 | allocate two bits to each block (double-bit). This information is stored in the 433 | SCB. 434 | =============================================================================== 435 | **** BDOS function 28 (DRV_SETRO) - Software write-protect current disc **** 436 | * Supported by: All versions, with differences * 437 | Entered with C=1Ch. 438 | Temporarily set current drive to be read-only; attempts to write to it will 439 | fail. Under genuine CP/M systems, this continues until either call 13_(disc 440 | reset) or call 37_(selective_disc_reset) is called; in practice, this means 441 | that whenever a program returns to the command prompt, all drives are reset to 442 | read/write. Newer BDOS replacements only reset the drive when function 37 is 443 | called. 444 | Under multitasking CP/Ms, this can fail (returning A=0FFh) if another process 445 | has a file open on the drive. 446 | =============================================================================== 447 | **** BDOS function 29 (DRV_ROVEC) - Return bitmap of read-only drives **** 448 | * Supported by: All versions * 449 | Entered with C=1Dh. Returns bitmap in HL. 450 | Bit 7 of H corresponds to P: while bit 0 of L corresponds to A:. A bit is set 451 | if the corresponding drive is set to read-only in software. 452 | =============================================================================== 453 | **** BDOS function 30 - set echo mode for function 1 **** 454 | * Supported by: CP/M 1.3 only * 455 | Entered with C=1Eh, E=echo mode (0=no echo, else echo). 456 | **** BDOS function 30 (F_ATTRIB) - set file attributes **** 457 | * Supported by: All versions * 458 | Entered with C=1Eh, DE=address of FCB. Returns error codes in BA and HL. 459 | Set and reset the bits required. Standard CP/M versions allow the bits F1', 460 | F2', F3', F4', T1' (read-only), T2' (system) and T3' (archive) to be changed. 461 | Some alternative BDOSses allow F5', F6', F7' and F8' to be set, but this is not 462 | to be encouraged since setting these bits can cause CP/M 3 to behave 463 | differently. 464 | Under Concurrent CP/M, if the F5' bit is not set and the file has an extended 465 | file lock, the lock will be released when the attributes are set. If F5' is set 466 | the lock stays. 467 | Under CP/M 3, the Last_Record_Byte_Count is set by storing the required value 468 | at FCB+32 (FCB+20h) and setting the F6' bit. 469 | The code returned in A is 0-3 if the operation was successful, or 0FFh if there 470 | was an error. Under CP/M 3, if A is 0FFh and H is nonzero, H contains a 471 | hardware_error. 472 | =============================================================================== 473 | **** BDOS function 31 (DRV_DPB) - get DPB address **** 474 | * Supported by: CP/M 2 and later. * 475 | Entered with C=1Fh. Returns address in HL. 476 | Returns the address of the Disc Parameter Block for the current drive. See the 477 | formats_listing for details of the DPBs under various CP/M versions. 478 | =============================================================================== 479 | **** BDOS function 32 (F_USERNUM) - get/set user number **** 480 | * Supported by: CP/M 2 and later. * 481 | Entered with C=20h, E=number. If E=0FFh, returns number in A. 482 | Set current user number. E should be 0-15, or 255 to retrieve the current user 483 | number into A. Some versions can use user areas 16-31, but these should be 484 | avoided for compatibility reasons. 485 | DOS+ returns the number set in A. 486 | =============================================================================== 487 | **** BDOS function 33 (F_READRAND) - Random access read record **** 488 | * Supported by: CP/M 2 and later. * 489 | Entered with C=21h, DE=FCB address. Returns error codes in BA and HL. 490 | Read the record specified in the random record count area of the FCB, at the 491 | DMA_address. The pointers in the FCB will be updated so that the next record to 492 | read using the sequential I/O calls will be the record just read. Error numbers 493 | returned are: 494 | 0 495 | OK 496 | 1 497 | Reading unwritten data 498 | 4 499 | Reading unwritten extent (a 16k portion of file does not exist) 500 | 6 501 | Record number out of range 502 | 9 503 | Invalid FCB 504 | 10 505 | Media changed (CP/M); FCB checksum error (MP/M), 506 | 11 507 | Unlocked file verification error (MP/M), 508 | 0FFh 509 | [MP/M II, CP/M 3] hardware_error in H. 510 | =============================================================================== 511 | **** BDOS function 34 (F_WRITERAND) - Random access write record **** 512 | * Supported by: CP/M 2 and later. * 513 | Entered with C=22h, DE=FCB address. Returns error codes in BA and HL. 514 | Write the record specified in the random record count area of the FCB, at the 515 | DMA_address. The pointers in the FCB will be updated so that the next record to 516 | write using the sequential I/O calls will be the record just written. Error 517 | numbers returned are: 518 | 0 519 | OK 520 | 2 521 | Disc full 522 | 3 523 | Cannot close extent 524 | 5 525 | Directory full 526 | 6 527 | Record number out of range 528 | 8 529 | Record is locked by another process (MP/M) 530 | 9 531 | Invalid FCB 532 | 10 533 | Media changed (CP/M); FCB checksum error (MP/M) 534 | 11 535 | Unlocked file verification error (MP/M) 536 | 0FFh 537 | [MP/M II, CP/M 3] hardware_error in H. 538 | If the record indicated is beyond the end of the file, the record will be 539 | written and the file may contain a gap; attempting to read this gap may give 540 | "reading unwritten data" errors, or nonsense. 541 | =============================================================================== 542 | **** BDOS function 35 (F_SIZE) - Compute file size **** 543 | * Supported by: CP/M 2 and later. * 544 | Entered with C=23h, DE=FCB address. Returns error codes in BA and HL. 545 | Set the random record count bytes of the FCB to the number of 128-byte records 546 | in the file. Returns A=0FFh if error (file not found, or CP/M 3 hardware 547 | error); otherwise A=0. 548 | =============================================================================== 549 | **** BDOS function 36 (F_RANDREC) - Update random access pointer **** 550 | * Supported by: CP/M 2 and later. * 551 | Entered with C=24h, DE=FCB address. 552 | Set the random record count bytes of the FCB to the number of the last record 553 | read/written by the sequential I/O calls. 554 | =============================================================================== 555 | **** BDOS function 37 (DRV_RESET) - Selectively reset disc drives **** 556 | * Supported by: CP/M 2 and later. * 557 | Entered with C=25h, DE=bitmap of drives to reset. Returns A=0 if OK, 0FFh if 558 | error. 559 | Bit 7 of D corresponds to P: while bit 0 of E corresponds to A:. A bit is set 560 | if the corresponding drive should be reset. Resetting a drive removes its 561 | software read-only status. 562 | =============================================================================== 563 | **** BDOS function 38 (DRV_ACCESS) - Access drives **** 564 | * Supported by: MP/M, Concurrent CP/M, CP/Net redirector. * 565 | Entered with C=26h, DE=drive bitmap. 566 | Locks one or more disc drives. Processes will not be able to reset these 567 | drives; the effect is the same as if there were open files on the drives. 568 | Returns A=0 if OK, and 0FFh if there is an error. If there is, H will contain 569 | the error_code. 570 | =============================================================================== 571 | **** BDOS function 39 (DRV_FREE) - Free drive **** 572 | * Supported by: MP/M, Concurrent CP/M, CP/Net redirector. * 573 | Releases locks on disc drives. 574 | Entered with C=27h, DE = bitmap of drives to free. 575 | Open files on the drives in question must be closed before this call is made, 576 | or data may be corrupted. 577 | =============================================================================== 578 | **** BDOS function 40 (F_WRITEZF) - Write random with zero fill **** 579 | * Supported by: CP/M 2.2 and later. * 580 | Entered with C=28h, DE=FCB address. Returns error codes in BA and HL. 581 | As function_34, but if the write is to a newly allocated disc block the 582 | remainder of the block is filled with zeroes. 583 | =============================================================================== 584 | **** BDOS function 41 - Test and write record **** 585 | * Supported by: MP/M, Concurrent CP/M. * 586 | Entered with C=29h, DE=FCB address. Returns error codes in BA and HL. 587 | This function compares the contents of the file with the record at the DMA 588 | address. If the comparison is successful, the data at (DMA + record size) are 589 | written to the file. 590 | For example, if the multisector_count is set to 4 records, the DMA address will 591 | have 512 bytes (4x128) to compare with the file, and then another 512 to write 592 | to the file. On return, A is: 593 | 0 594 | OK 595 | 1 596 | Reading unwritten data 597 | 3 598 | Cannot close current extent 599 | 4 600 | Seek to unwritten extent (a 16k portion of file does not exist) 601 | 6 602 | Record number out of range 603 | 7 604 | Records did not match 605 | 8 606 | Record is locked by another process 607 | 10 608 | FCB checksum error 609 | 11 610 | Unlocked file verification error 611 | 0FFh 612 | hardware_error in H. 613 | =============================================================================== 614 | **** BDOS function 42 (F_LOCK) - Lock record **** 615 | * Supported by: MP/M, Concurrent CP/M, CP/Net redirector. * 616 | Entered with C=2Ah, DE=FCB address. Returns error codes in BA and HL. 617 | This function locks the number of records specified by function 44, starting at 618 | the record number at FCB+21h. The DMA address should point to the file ID 619 | returned by function 15. On return, A is: 620 | 0 621 | OK 622 | 1 623 | Reading unwritten data 624 | 3 625 | Cannot close current extent 626 | 4 627 | Seek to unwritten extent (a 16k portion of file does not exist) 628 | 6 629 | Record number out of range 630 | 8 631 | Record is locked by another process 632 | 10 633 | FCB checksum error 634 | 11 635 | Unlocked file verification error 636 | 12 637 | No more locks available for this process 638 | 13 639 | Invalid file ID 640 | 14 641 | No room in the system lock list 642 | 0FFh 643 | hardware_error in H. 644 | =============================================================================== 645 | **** BDOS function 43 (F_UNLOCK) - Unlock record **** 646 | * Supported by: MP/M, Concurrent CP/M, CP/Net redirector. * 647 | Entered with C=2Bh, DE=FCB address. Returns error codes in BA and HL. 648 | This function behaves as function 42 above, but unlocks records rather than 649 | locking them. If a record is locked by a different process, it remains locked; 650 | but no error is returned. 651 | Set F5' to 1 to unlock all records rather than just the ones the FCB refers to. 652 | =============================================================================== 653 | **** BDOS function 44 (F_MULTISEC) - Set number of records to read/write at 654 | once **** 655 | * Supported by: MP/M II and later. * 656 | Entered with C=2Ch, E=number of records. Returns A=0 or 0FFh. 657 | Sets the number of records that functions 14, 15, 31, 32 and 40 should attempt 658 | to read at once. E should be 1-16 in MP/M II; 1-127 in CP/M 3 and later. 659 | Returns A=0 if E was valid, 0FFh otherwise. The random access counters still 660 | operate with 128 bytes/record. 661 | =============================================================================== 662 | **** BDOS function 45 (F_ERRMODE) - Set action on hardware error **** 663 | * Supported by: Personal CP/M, MP/M II and later, CP/Net redirector. * 664 | Entered with C=2Dh, E=action. 665 | Instructs CP/M what action to take if there is a hardware_error: 666 | E < 254 667 | Compatibility mode; program is terminated and an error message printed. 668 | E = 254 669 | Error code is returned in H, error message is printed. 670 | E = 255 671 | Error code is returned in H, no error message is printed. 672 | Note that the messages (if printed) are not followed by a carriage return or 673 | linefeed. 674 | If this function is being provided by CP/Net, then it only affects network 675 | errors. 676 | =============================================================================== 677 | **** BDOS function 46 (DRV_SPACE) - Find free space on a drive **** 678 | * Supported by: MP/M II and later. * 679 | Entered with C=2Eh, E=drive. Returns error codes in BA and HL. 680 | Sets the three bytes at the DMA_address to the number of free 128-byte records 681 | on the disc, low byte first. E is the drive (0 for A:, 1 for B: etc.). If on 682 | return A=0FFh, there is a hardware_error in H. 683 | =============================================================================== 684 | **** BDOS function 47 (P_CHAIN) - Chain to program **** 685 | * Supported by: MP/M II and later; CP/M-86 v1.1 * 686 | Entered with C=2Fh, E=chain flag. Only returns if it fails. 687 | Execute the command line at 80h. The error_return_code is not changed, so the 688 | new program can discover the status returned by the previous one. 689 | If E=255, then the currently set drive and user (as set by calls 25 and 32) 690 | become those used by the CCP; otherwise the CCP drive and user remain the same. 691 | In MP/M II, there is no chain flag; and this call will return if the process is 692 | not attached to the screen. 693 | =============================================================================== 694 | **** BDOS function 48 (DRV_FLUSH) - Empty disc buffers **** 695 | * Supported by: Personal CP/M; MP/M II and later; CP/M-86 v1.1 * 696 | Entered with C=30h, E=flag. Returns error codes in BA and HL. 697 | Forces all pending disc writes to be executed. If E=255, then all disc read 698 | buffers are emptied as well (so that information must be read from the disc - 699 | used by file verification programs). 700 | Returns A=0FFh if there is a hardware_error in H. 701 | =============================================================================== 702 | **** BDOS function 49 - Access the System Control Block **** 703 | * Supported by: CP/M 3. * 704 | Entered with C=31h, DE=address of parameter area. Returned values vary. 705 | The SCB is a 100-byte area of memory (officially) used for storing system 706 | settings. The parameter area is formed: 707 | pb+0: DB offset ;0-99 708 | pb+1: DB command ;0 => Read byte at offset into A, 709 | ; and word at offset into HL. 710 | ;0FEh => Write word at pb+2 to SCB at 711 | ; offset and offset+1 712 | ;0FFh => Write byte at pb+2 to SCB at offset 713 | **** BDOS function 49 - Return address of system variables **** 714 | * Supported by: CP/M-86 v1.1 * 715 | Entered with CL=31h. Returns address in ES:BX. 716 | More information on the System_variables_area_in_CP/M-86. 717 | **** BDOS function 49 (S_SYSVAR) - Access the system variables **** 718 | * Supported by: CP/M-86 v4. * 719 | Entered with CL=31h, DX=address of parameter area. Returns AX=BX=0 if variable 720 | is valid, 0FFFFh if invalid. 721 | The BDOS 4.1 system variables contain various system settings. The parameter 722 | area is formed: 723 | pb+0: DB system variable ;0-5 724 | pb+1: DB command ;0 => Read the specified variable into 725 | ; buffer at pb+2. 726 | ;0FFh => Write data at pb+2 to specified 727 | ; variable. 728 | Variables are: 729 | Number Length Description 730 | 0 1 screen width-1 731 | 1 1 screen height-1 732 | 2 1 page mode - 0 if programs like TYPE should give paged output 733 | 3 1 system clock speed in Hz. Cannot be changed. 734 | 4 1 temporary file drive, 0 for current or 1-16 for A: to P: 735 | 5 5 date and time 736 | DW day count, day 1 is 1/1/78 737 | DB BCD hours 738 | DB BCD minutes 739 | DB BCD seconds 740 | There are also some undocumented variables with numbers between 80h and 92h. 741 | 80h 1 If nonzero, an 8087 CPU is present; otherwise it is not. 742 | 81h 1 Current process ID 743 | 82h 26 Data area for current process: 744 | DB drive 745 | DB user 746 | DD DMA 747 | DB current BDOS disc function 748 | DB ? 749 | DD FCB from the last BDOS search call 750 | DD ? 751 | DB Error mode (set by function_45). 752 | DB Multi-record count (set by function_44). 753 | DB Default password (8 bytes) 754 | 83h 1 Length of previous command (recalled by Control-W) 755 | 84h 255 Previous command (recalled by Control-W) 756 | 85h 2 Either the address of the CCP data segment, or a linked list 757 | of memory allocations. I'm not sure which. 758 | 86h 255 CCP settings area: 759 | DB CCP drive 760 | DB CCP user 761 | DB Cold start flag (0 if CCP has not run yet) 762 | DW Address of next command in CCP data segment 763 | DB Chained command flag (nonzero if a command 764 | is waiting) 765 | DB ? 766 | DB Search order (0=CMD 1=CMD,SUB 2=SUB,CMD) 767 | DB Program name display flag (1=yes 2=no) 768 | DS 7 ;? 769 | DB pending command? 770 | 87h 4 List of drives to search, set by SETDEF. 771 | 88h 1 Set if process is running in the background. 772 | 89h 1 Number of processes in system, 1-4. 773 | 8Ah 1 Value set by SLICE - time slice to give to foreground program. 774 | 8Bh 2 BDOS flags. Bit 7 set if ^S is in operation. Bit 9 set if ^P 775 | is in operation. 776 | 8Ch 1 Nonzero if a process is using the 8087; else zero. 777 | 8Dh 4 Address of code to run in task switch? 778 | 8Eh 2 Pointer to array of 16 near pointers in CP/M data segment - 779 | addresses of current directory structures for each drive. 780 | 8Fh 5 Multitasker settings 781 | DW comsize ;Memory to give to COM files 782 | DW addmem ;Memory to give to EXE files 783 | DB f8087 ;8087 enabled or disabled? 784 | 90h 1 Drive containing COMMAND.COM? 785 | 91h 32 DOS 2.x Country Info (see the Interrupt List, INT 21h AH=38h) 786 | 92h 1 SwitChar (see the Interrupt List, INT 21h AH=37h). The SwitChar 787 | can only be changed this way. 788 | =============================================================================== 789 | **** BDOS function 50 (S_BIOS) - Use the BIOS **** 790 | * Supported by: CP/M 3 and later; CP/M-86 v1.1 * 791 | Entered with C=32h, DE=address of parameter area. Returned values vary. 792 | Under CP/M 3, the BIOS should not be directly called, except possibly the 793 | character I/O and USERF calls. Instead, this function should be used. The 794 | parameter area is formed: 795 | pb+0: DB bios-function ;0-32 796 | pb+1: DB bios-a ;Value for A register 797 | pb+2: DB bios-c ;Value for C register 798 | pb+3: DB bios-b ;Value for B register 799 | pb+4: DB bios-e ;Value for E register 800 | pb+5: DB bios-d ;Value for D register 801 | pb+6: DB bios-l ;Value for L register 802 | pb+7: DB bios-h ;Value for H register 803 | Under 16-bit versions, this function should be used for all BIOS calls; only 804 | character I/O BIOS calls are permitted. The parameter area is formed: 805 | pb+0: DB bios-function 806 | pb+1: DB bios-cl 807 | pb+2: DB bios-ch 808 | pb+3: DB bios-dl 809 | pb+4: DB bios-dh 810 | =============================================================================== 811 | **** BDOS function 51 (F_DMASEG) - Set DMA segment **** 812 | * Supported by: CP/M-86 v1.1 and later. * 813 | Entered with CL=33h, DX=segment value. 814 | =============================================================================== 815 | **** BDOS function 52 (F_DMAGET) - Get DMA address **** 816 | * Supported by: CP/M-86 v3+, CCP/M-86. * 817 | Entered with CL=34h. Returns address in ES:BX. 818 | =============================================================================== 819 | **** BDOS function 53 (MC_MAX) - Allocate maximum memory **** 820 | * Supported by: CP/M-86 v1.1 and later. * 821 | Entered with CL=35h, DX=address of MCB 822 | Set MCB_EXT to 2 if the memory should remain allocated after program 823 | termination. Set MCB_LENGTH to maximum required length. 824 | Returns MCB_EXT=0 if no additional memory is available, 1 if there is. Sets 825 | MCB_SEGMENT and MCB_LENGTH to the values for the memory block allocated, which 826 | will be less than or equal to the size requested. Returns AX=0 if OK, 0FFFFh if 827 | error; CX=error code: 828 | 0 OK 829 | 2 Illegal call number 830 | 3 Out of memory 831 | 12 Out of descriptors 832 | 43 Bad Parameter 833 | =============================================================================== 834 | **** BDOS function 54 (MC_ABSMAX) - Allocate absolute maximum memory **** 835 | * Supported by: CP/M-86 v1.1 and later. * 836 | Entered with CL=36h, DX=address of MCB 837 | Set MCB_EXT to 2 if the memory should remain allocated after program 838 | termination. Set MCB_LENGTH to maximum required length, and MCB_SEGMENT to the 839 | required start paragraph. 840 | Returns MCB_EXT=0 if no additional memory is available, 1 if there is. Sets 841 | MCB_SEGMENT and MCB_LENGTH to the values for the memory block allocated, which 842 | will be less than or equal to the size requested. Returns AX=0 if OK, 0FFFFh if 843 | error; CX=error_code. 844 | **** Z80DOS function 54 - Get file time stamps **** 845 | * Supported by: Z80DOS, ZPM3 * 846 | Entered with C=36h. Returns HL=address of stamp. 847 | This returns the timestamp from the last file used by functions 15, 17 or 18. 848 | The format of a Z80DOS timestamp is: 849 | DW creation date 850 | DW modification date 851 | DB modification time, hours, BCD 852 | DB modification time, minutes, BCD 853 | DW last access date 854 | DB last access time, hours, BCD 855 | DW last access time, minutes, BCD 856 | More_information_on_Z80DOS_and_ZPM3. 857 | =============================================================================== 858 | **** BDOS function 55 (MC_ALLOC) - Allocate memory **** 859 | * Supported by: CP/M-86 v1.1 and later. * 860 | Entered with CL=37h, DX=address of MCB 861 | Set MCB_EXT to 2 if the memory should remain allocated after program 862 | termination. Set MCB_LENGTH to maximum required length. 863 | Returns MCB_EXT=0 if no additional memory is available, 1 if there is. Sets 864 | MCB_SEGMENT and MCB_LENGTH to the values for the memory block allocated, which 865 | will be exactly the size requested. Returns AX=0 if OK, 0FFFFh if error; 866 | CX=error_code. 867 | **** Z80DOS function 55 - Use file time stamps **** 868 | * Supported by: Z80DOS, ZPM3 * 869 | Entered with C=37h. 870 | The next BDOS call will use the timestamp_buffer returned by function 54, 871 | rather than the current time. This would normally done before creating or 872 | closing the file. 873 | =============================================================================== 874 | **** BDOS function 56 (MC_ABSALLOC) - Allocate absolute memory **** 875 | * Supported by: CP/M-86 v1.1 and later. * 876 | Entered with CL=38h, DX=address of MCB 877 | Set MCB_EXT to 2 if the memory should remain allocated after program 878 | termination. Set MCB_LENGTH to maximum required length, and MCB_SEGMENT to the 879 | required start paragraph. 880 | Returns MCB_EXT=0 if no additional memory is available, 1 if there is. Sets 881 | MCB_SEGMENT and MCB_LENGTH to the values for the memory block allocated, which 882 | will be exactly the size requested. Returns AX=0 if OK, 0FFFFh if error; 883 | CX=error_code. 884 | In the 4.1 BDOS (and possibly earlier) it is possible to call this with MCB_EXT 885 | set to 2 on a memory block that has already been allocated; this will make the 886 | block remain after program termination if it didn't before. 887 | =============================================================================== 888 | **** BDOS function 57 (MC_FREE) - Free memory **** 889 | * Supported by: CP/M-86 v1.1 and later. * 890 | Entered with CL=39h, DX=address of MCB 891 | Set MCB_SEGMENT to start of area, MCB_LENGTH to length. It is not possible to 892 | deallocate the middle or start of an area which was allocated as one piece. Set 893 | MCB_EXT to 0 to free the specified MCB, or 0FFh to free all this program's 894 | memory. Returns AX=0 if OK, 0FFFFh if error; CX=error_code. 895 | =============================================================================== 896 | **** BDOS function 58 (MC_ALLFREE) - Free all memory **** 897 | * Supported by: CP/M-86 v1.1 and later. * 898 | Entered with CL=3Ah. This function is obsolete in CP/M-86 v3.x+. In CP/M-86 899 | v1.1, it is called by the CCP to empty memory before a new program is loaded. 900 | In Concurrent CP/M, it should not be called by user programs. 901 | =============================================================================== 902 | **** BDOS function 59 (P_LOAD) - Load overlay **** 903 | * Supported by: CP/M 3 and higher Loaders. * 904 | Entered with C=3Bh, DE=FCB address. Returns error code in A. 905 | Load a program or RSX. The FCB should have been opened before making this call; 906 | the load address should be stored at FCB+21h. If the program is a PRL (Paged 907 | Relocatable), the original CP/M loader will relocate it; some replacement 908 | loaders, such as that in CCP+ need a special flag set to indicate this. 909 | If DE=0, the Loader will not load anything, but it will remove from the RSX 910 | chain any RSXs marked as due for deletion. 911 | Returns A=0 if OK, 0FEh if there was an error, or 0FFh if the Loader is not 912 | present. 913 | In CP/M-86 operating systems, this will load the CMD file at the DMA address, 914 | and returns the segment of the Zero Page allocated to the program in BX. 915 | =============================================================================== 916 | **** BDOS function 60 - Call to RSX **** 917 | * Supported by: CP/M 3 and later RSXs. * 918 | Entered with C=3Ch, DE=RSXPB address 919 | This call is provided for programs to communicate with Resident System 920 | Extensions. There is a separate_list of functions provided by specific RSXs. 921 | The format of the RSXPB is: 922 | DEFB function, 0-127 ;(128-255 used internally by CP/M) 923 | DEFB numwords ;Number of parameter words passed to RSX. 924 | DEFW parameters 925 | A popular convention is for the first parameter word to point to a copy within 926 | the program of the name of the intended RSX; for example: 927 | RSXPB: DEFB 100 ;Function 928 | DEFB 1 ;1 Parameter 929 | DEFW RNAME ;RSX name 930 | RNAME: DEFB 'GETERL ' 931 | This call returns BA=HL=00FFh if the requested RSX is not resident; otherwise, 932 | the values are those returned by the RSX. 933 | =============================================================================== 934 | **** BDOS function 61 - Rename file **** 935 | * Supported by: DOS Plus v2.1 * 936 | Entered with C=3Dh, DE=address of FCB. Returns error codes in BA and HL. 937 | This works the same way as the normal_rename_function, except that it can 938 | rename a file across drives if the two drives are 'really' the same (that is, 939 | one is a floating drive and the other is its host). 940 | =============================================================================== 941 | **** BDOS function 62 - Unknown **** 942 | * Supported by: DOS Plus v2.1 * 943 | This is a function that was internal to DOS Plus 1.2 and earlier. It allocates 944 | a new current directory structure for the active process. 945 | =============================================================================== 946 | **** BDOS function 64 - Log in **** 947 | * Supported by: CP/Net. * 948 | Enter with C=40h, DE=address of login record: 949 | DB network ID of server 950 | DS 8 ;Password 951 | Network IDs are 0-254. The server is normally number 0. 952 | Returns A=0 if successful or already logged in; 0FFh if failed. Logging in can 953 | fail if the server does not exist, or the password was incorrect. 954 | =============================================================================== 955 | **** BDOS function 65 - Log off **** 956 | * Supported by: CP/Net. * 957 | Enter with C=41h, E=network ID of server 958 | Returns A=0 if OK, or 0FFh if not logged on to the server anyway. 959 | =============================================================================== 960 | **** BDOS function 66 - Send message **** 961 | * Supported by: CP/Net. * 962 | Enter with C=42h, DE=address of message. 963 | The format of a normal CP/Net message is: 964 | [--FMT--] Format code, 1 byte. 0-7 are standard CP/Net message formats. 965 | [--DID--] Destination computer's network ID; normally 1 byte. 966 | [--SID--] Source computer's network ID; normally 1 byte. 967 | [--FNC--] Function code; for standard messages, BDOS function number. 968 | Always 1 bytes. 969 | [--SIZ--] Message size - 1; normally 1 byte. 970 | [--MSG--] Message data; normally 1-256 bytes. 971 | The 8 standard message formats are treated as bitfields: 972 | Bit 0: 0 for command, 1 for returned result. 973 | Bit 1: Message data is more than 256 bytes. The "siz" field is a word and 974 | the data can be up to 64k. 975 | Bit 2: Destination ID and source ID are words, not bytes. 976 | Thus giving eight packet types: 977 | 0 Command 5 bytes header 1-256 bytes data 8-bit network IDs 978 | 1 Result 5 bytes header 1-256 bytes data 8-bit network IDs 979 | 2 Command 6 bytes header 1-64k data 8-bit network IDs 980 | 3 Result 6 bytes header 1-64k data 8-bit network IDs 981 | 4 Command 7 bytes header 1-256 bytes data 16-bit network IDs 982 | 5 Result 7 bytes header 1-256 bytes data 16-bit network IDs 983 | 6 Command 8 bytes header 1-64k data 16-bit network IDs 984 | 7 Result 8 bytes header 1-64k data 16-bit network IDs 985 | In 8-bit systems, the NDOS only uses formats 0 and 1. 986 | If the normal format is not used, the computers sending and receiving the 987 | message must have a modified NIOS (Network I/O system) so they can correctly 988 | handle the nonstandard message. 989 | DRI also state that the format code should be greater than 128, so that the 990 | message is not mistaken for a one sent by CP/Net itself. This seems to imply 991 | that formats 128-135 should be used for messages in the 8 normal formats. 992 | This call does not wait for a reply. Use function_67 to get a reply. 993 | Returns BA = HL = 0FFFFh if an error occurred; else 0. 994 | =============================================================================== 995 | **** BDOS function 67 - Receive message **** 996 | * Supported by: CP/Net. * 997 | Enter with C=43h, DE=address of buffer for message. 998 | Message format is as given above. 999 | =============================================================================== 1000 | **** BDOS function 68 - Get network status **** 1001 | * Supported by: CP/Net. * 1002 | Enter with C=44h. Returns A=L=status byte. 1003 | Bit 0 set if error transmitting data. 1004 | Bit 1 set if error in received data. 1005 | Bit 4 set if this computer is logged on to the network. 1006 | Bits 0 and 1 will be cleared after they have been read by this function. 1007 | =============================================================================== 1008 | **** BDOS function 69 - Get configuration table address **** 1009 | * Supported by: CP/Net. * 1010 | Enter with C=45h. Returns HL = address of table. 1011 | Format of the table is: 1012 | DB Network_status_byte 1013 | DB Computer's network ID 1014 | DS 32 ;Redirection status of 16 drives 1015 | DS 2 ;Redirection status of console 1016 | DS 2 ;Redirection status of printer 1017 | DB no. of characters in printer buffer 1018 | DS 5 ;Message_header for printer output 1019 | DB printer device number 1020 | DS 128 ;Printer buffer 1021 | A redirection status entry is two bytes: 1022 | DB Bit 7 set if device is on a different machine. 1023 | Bit 6 set for "set up in disk". 1024 | Bits 0-3 = drive number / device number on its local machine. 1025 | DB Network ID of the machine containing the device. 1026 | =============================================================================== 1027 | **** BDOS function 70 - Set compatibility attributes **** 1028 | * Supported by: CP/Net-86, some 8-bit CP/Net versions. * 1029 | Enter with C=46h, E=compatibility info. No return. 1030 | The compatibility attributes are used to make a network server behave more like 1031 | a local drive on a single-user system, for compatibility with ill-behaved 1032 | programs. They are set from the "user attributes" F1'-F4' of the current .COM 1033 | file being run on the client machine. 1034 | F1' 1035 | Disable file locking. There is no protection against two programs writing 1036 | to the same file at the same time. 1037 | F2' 1038 | File close operations write pending data and update the directory, but 1039 | the file is not closed and can still be accessed. 1040 | F3' 1041 | Disable FCB checksumming for file close operations. 1042 | F4' 1043 | Disable FCB checksumming completely. This can cause filesystem damage. 1044 | These attributes are passed in E: 1045 | Bit 7: F1', disable locking. 1046 | Bit 6: F2', file close is partial. 1047 | Bit 5: F3', disable checksum on close file. 1048 | Bit 4: F4', disable checksum on all FCB operations (also set bits 5 & 6). 1049 | =============================================================================== 1050 | **** BDOS function 71 - Get server configuration **** 1051 | * Supported by: CP/Net-86, some 8-bit CP/Net versions. * 1052 | Enter with C=47h, E=server ID. Returns HL = address of table. 1053 | The table returned is: 1054 | DB server temporary file drive 1055 | DB server network status 1056 | DB server ID 1057 | DB max clients on this server 1058 | DB current no. of clients on this server 1059 | DW bitmap of clients logged in to the server 1060 | DS 16 ;Requester ID table 1061 | Each call to this function uses the same buffer, so take a copy of the data if 1062 | calling this repeatedly. 1063 | =============================================================================== 1064 | **** BDOS function 98 - Clean up disc **** 1065 | * Supported by: CP/M 3 (Internal?). * 1066 | Entered with C=62h. Returns A=0 if OK, 0FFh if current drive is invalid 1067 | This function is called by the CCP when a program terminates. Open files are 1068 | closed; buffered data are not written, so files opened for writing may be 1069 | destroyed if not properly closed. 1070 | =============================================================================== 1071 | **** BDOS function 99 (F_TRUNCATE) - Truncate file **** 1072 | * Supported by: CP/M 3 and later. * 1073 | Entered with C=63h, DE=FCB address. Returns error codes in BA and HL. 1074 | Set the random record count of the FCB to the required file size in records. If 1075 | the file is password protected, point the DMA address at the password. Returns 1076 | A=0-3 for success, 0FFh for failure; H may contain a hardware_error code. 1077 | Under Concurrent CP/M, if the F5' bit is not set and the file has an extended 1078 | file lock, the lock will be released when the file is truncated. If F5' is set 1079 | the lock stays. 1080 | This function can't be used to extend a file, only reduce it; and if the file 1081 | is sparse, then it must truncate it to a point where the last record contains 1082 | actual data. 1083 | =============================================================================== 1084 | **** BDOS function 100 (DRV_SETLABEL) - Set directory label **** 1085 | * Supported by: MP/M II and later. * 1086 | Entered with C=64h, DE=FCB. Returns error codes in BA and HL. 1087 | The EX byte (FCB+0Ch) of the FCB should contain a flags byte: 1088 | Bit Meaning if set 1089 | ---------------------------------------- 1090 | 7 Passwords enabled 1091 | 6 Stamp on access 1092 | 5 Stamp on update 1093 | 4 Stamp on creation 1094 | 0 Assign password at DMA+8 to label. 1095 | This function may need a password at the DMA address, if there is a label on 1096 | the disc which has a password. 1097 | This function returns A=0 if OK, 0FFh for failure. H may contain a hardware 1098 | error code. 1099 | =============================================================================== 1100 | **** BDOS function 101 (DRV_GETLABEL) - Get directory label byte **** 1101 | * Supported by: MP/M II and later. * 1102 | Entered with C=65h, E=drive. Returns A=byte 1103 | The byte returned has the following bits set: 1104 | Bit Meaning if set 1105 | ---------------------------------------- 1106 | 7 Passwords enabled 1107 | 6 Stamp on access 1108 | 5 Stamp on update 1109 | 4 Stamp on creation 1110 | 0 Directory label exists 1111 | If bit 0 is zero, the other bits are meaningless. 1112 | If a label does exist, it can be found using functions 17 and 18 with the first 1113 | 13 bytes of the FCB set to "?", and checking for a file whose user number is 1114 | 32. 1115 | =============================================================================== 1116 | **** BDOS function 102 (F_TIMEDATE) - Get file date and time **** 1117 | * Supported by: MP/M II and later. * 1118 | Entered with C=66h, DE=FCB address. Returns error codes in BA and HL. 1119 | Gets the file date and time stamps, and the password mode. The stamps end up at 1120 | FCB+18h (create or access), FCB+1Ch (update); the password mode ends up at 1121 | FCB+0Ch. This returns A=0-3 for success, or 0FFh for failure. To tell whether a 1122 | stamp is for creation or access, check the directory_label_byte. 1123 | For information on the format of date and time stamps, see the_date_and_time 1124 | calls. The password mode is documented in the next function. 1125 | =============================================================================== 1126 | **** BDOS function 103 (F_WRITEXFCB) - Set file password and protection **** 1127 | * Supported by: MP/M II and later. * 1128 | Entered with C=67h, DE=FCB address. Returns error codes in BA and HL. 1129 | Sets file password and protection mode. Set FCB+0Ch to the required password 1130 | mode, and point the DMA_address at the current password. The password mode byte 1131 | should have the following bits set: 1132 | Bit Meaning if set 1133 | ---------------------------------------- 1134 | 7 Reading requires password 1135 | 6 Writing requires password 1136 | 5 Deletion requires password 1137 | 0 Assign password at DMA+8 to file. 1138 | =============================================================================== 1139 | **** BDOS function 104 (T_SET) - Set date and time **** 1140 | * Supported by: MP/M II and later; Z80DOS, DOS+. * 1141 | Entered with C=68h, DE=address of time stamp. 1142 | The format of the time stamp is: 1143 | DW day ;Day 1 is 1 January 1978 1144 | DB hour ;Packed BCD 1145 | DB minute ;Packed BCD 1146 | [ DB second ;Packed BCD; only on Z80DOS and DOS+ ] 1147 | Under MP/M II and CP/M 3, the "seconds" counter will be reset to zero when this 1148 | function is called. Under Z80DOS and DOS+, the "seconds" counter will be set 1149 | from the time stamp. 1150 | =============================================================================== 1151 | **** BDOS function 105 (T_GET) - Get date and time **** 1152 | * Supported by: MP/M II and later; Z80DOS, DOS+. * 1153 | Entered with C=69h, DE=address of time stamp. Returns A=seconds (packed BCD). 1154 | The format of the time stamp is: 1155 | DW day ;Day 1 is 1 January 1978 1156 | DB hour ;Packed BCD 1157 | DB minute ;Packed BCD 1158 | [ DB second ;Packed BCD - only in Z80DOS and DOS+] 1159 | Under MP/M II and later versions, the "seconds" value will be returned in A. 1160 | =============================================================================== 1161 | **** BDOS function 106 (F_PASSWD) - Set default password **** 1162 | * Supported by: MP/M II and above, CP/Net redirector. * 1163 | Entered with C=6Ah, DE=address of password. 1164 | Sets the default password, used for all file operations requiring passwords 1165 | when no password is given explicitly. 1166 | =============================================================================== 1167 | **** BDOS function 107 (S_SERIAL) - Get serial number **** 1168 | * Supported by: MP/M II and above. * 1169 | Entered with C=6Bh, DE=address of buffer. 1170 | Stores a 6-byte serial number at the address given by DE. Usually this number 1171 | will be printable ASCII. 1172 | =============================================================================== 1173 | **** BDOS function 108 (P_CODE) - Get/put program return code **** 1174 | * Supported by: CP/M 3 and above. * 1175 | Entered with C=6Ch, DE=code. Returns HL=code. 1176 | If DE=0FFFFh, then the current code is returned in HL. Otherwise, it is set to 1177 | the value in DE. Allowable values are: 1178 | 00000h - 0FEFFh 1179 | No fatal error 1180 | 0FF00h - 0FF7Fh 1181 | Fatal error 1182 | 0FF80h - 0FFFCh 1183 | Reserved 1184 | 0FFFDh 1185 | Program terminated because of a hardware_error. 1186 | 0FFFEh 1187 | Program terminated by Control-C. 1188 | If a program was chained by function_47, an error code stored by the previous 1189 | program will be available to it. Otherwise the CCP sets the return code to zero 1190 | when it executes a program (some replacement CCPs do not do this). 1191 | If the error code is 0FF00h or above, and the next command begins with the 1192 | character : then it will not be run. 1193 | =============================================================================== 1194 | **** BDOS function 109 (C_MODE) - Set or get console mode **** 1195 | * Supported by: Personal CP/M; CP/M 3 and above * 1196 | Entered with C=6Dh, DE=mode or 0FFFFh. Returns HL=mode. 1197 | The mode is a bitmapped value. Under CP/M 3, the significant bits are: 1198 | 0 If set, function 11 detects only ^C. 1199 | 1 If set, ^S does not pause screen output. 1200 | 2 If set, output does not expand tabs, nor is it echoed to the printer even 1201 | if ^P was pressed. 1202 | 3 If set, ^C does not cause the program to terminate. 1203 | 4 Behaves like bit 2. The BDOS source comments imply it also signifies 1204 | "escape sequence decoding" but whatever this is, it is not implemented in 1205 | the CP/M 3 BDOS. 1206 | 7 Under CCP/M, if set, ^O does not act as a byte bucket. 1207 | 1208 | 8 } These form a 2-bit number controlling GET and SUBMIT: 1209 | 9 } 1210 | 0 Return "conditional" status to function 11. 1211 | Programs using function 11 to test for interruption (eg SID) are 1212 | not interrupted, but those using it to test for input do get the 1213 | input. It does this by returning "false", then "true". 1214 | 1 Function 11 always returns "true". 1215 | 2 Function 11 always returns "false". 1216 | 3 Temporarily disable GET/SUBMIT, input comes from keyboard. 1217 | Under Personal CP/M, only one bit is used: 1218 | 4 If set, tab expansion, ^S and ^P are disabled during screen output. 1219 | =============================================================================== 1220 | **** BDOS function 110 (C_DELIMIT) - Get/set string delimiter **** 1221 | * Supported by: Personal CP/M; CP/M 3 and above * 1222 | Entered with C=6Eh, DE=0FFFFh or ASCII value. Returns ASCII value in A. 1223 | This function gets or sets the string delimiter (used in function 9 to mark the 1224 | end of the string to print). If DE=0FFFFh, this gets the current ASCII value 1225 | into A; otherwise it sets the delimiter to the value in E. 1226 | =============================================================================== 1227 | **** BDOS function 111 (C_WRITEBLK) - Send block of text to console **** 1228 | * Supported by: Personal CP/M; CP/M 3 and above * 1229 | Entered with C=6Fh, DE=address of character control block. 1230 | For 8-bit CP/M, the character control block is formed: 1231 | DW text_begin 1232 | DW text_length 1233 | For 16-bit CP/M, it is formed: 1234 | DW text_begin_offset 1235 | DW text_begin_segment 1236 | DW text_length 1237 | =============================================================================== 1238 | **** BDOS function 112 (L_WRITEBLK) - Send block of text to printer **** 1239 | * Supported by: Personal CP/M; CP/M 3 and above * 1240 | Entered with C=70h, DE=address of character control block. 1241 | Format_of_character_control_block 1242 | =============================================================================== 1243 | **** BDOS function 113 - Direct screen functions **** 1244 | * Supported by: Personal CP/M. * 1245 | Entered with C=71h, DE=address of parameter block. 1246 | This function is implemented by the PCP/M BIOS. The first byte of the parameter 1247 | block gives an operation code, 0-27. 1248 | =============================================================================== 1249 | **** BDOS function 115 - Reserved for GSX **** 1250 | * Supported by: GSX (Graphics System Extension) * 1251 | Entered with C=73h, DE=parameter block address. 1252 | Follow_this_link for more information on the GSX calls. 1253 | =============================================================================== 1254 | **** BDOS function 116 - Set file date & time **** 1255 | * Supported by: CP/M-86 v4. * 1256 | Entered with CL=74h, DX=FCB address. Returns AX=BX=error code. 1257 | The required stamps should be put at the DMA_address. Returns AL = 0-3 for 1258 | success, or 0FFh for failure. 1259 | =============================================================================== 1260 | **** BDOS function 117 - BDOS v4.x internal **** 1261 | * Supported by: CP/M-86 v4. * 1262 | Entered with CL=75h, DX=PB address. Returns AX=BX=error code. 1263 | This appears to make a call to the disc drivers; INT 13h calls are routed 1264 | through this function. The parameter block is: 1265 | DB 0 to read, 1 to write (2 to verify?) 1266 | DB drive 1267 | DB no. of sectors 1268 | DB head 1269 | DB sector (0 based) 1270 | DB cylinder 1271 | DD address of data 1272 | =============================================================================== 1273 | **** BDOS function 124 - Byte block copy **** 1274 | * Supported by: Personal CP/M. * 1275 | This call is passed directly to the BIOS. On the Sharp MZ-800 version of PCP/M, 1276 | it has no effect and returns with A = 0FFh. 1277 | =============================================================================== 1278 | **** BDOS function 125 - Byte block alter **** 1279 | * Supported by: Personal CP/M. * 1280 | This call is passed directly to the BIOS. On the Sharp MZ-800 version of PCP/M, 1281 | it has no effect and returns with A = 0FFh. 1282 | =============================================================================== 1283 | **** XDOS function 128 (M_ALLOC) - Absolute memory request **** 1284 | * Supported by: MP/M, Concurrent CP/M * 1285 | Entered with C=80h, DE=address of MPB. Returns A=0 if memory allocated, 0FFh if 1286 | error. 1287 | Allocates memory at the requested address. If successful sets the MIN and MAX 1288 | fields to the size allocated. 1289 | In 16-bit operating systems, this function and the next are interchangeable; 1290 | they check the START field of the MPB to determine whether a fixed address is 1291 | requested or not. 1292 | =============================================================================== 1293 | **** BDOS function 129 (M_ALLOC) - Relocatable memory request **** 1294 | * Supported by: MP/M * 1295 | Entered with C=81h, DE=address of MPB. Returns A=0 if memory allocated, 0FFh if 1296 | error. 1297 | Allocates memory. If successful sets the first word of the MPB to the allocated 1298 | address, and the MIN and MAX fields to the size allocated. 1299 | =============================================================================== 1300 | **** BDOS function 130 (M_FREE) - Free memory **** 1301 | * Supported by: MP/M, Concurrent CP/M * 1302 | Entered with C=82h, DE=address of Memory Free Parameter Block. Returns A=0 if 1303 | memory freed, 0FFh if error. 1304 | The MFPB is formed: 1305 | DW address ;of memory previously allocated with M_ALLOC 1306 | DW 0 1307 | =============================================================================== 1308 | **** BDOS function 131 (DEV_POLL) - Poll I/O device **** 1309 | * Supported by: MP/M, Concurrent CP/M * 1310 | Entered with C=83h, E=device number. Returns A=0 if OK, 0FFh if error. 1311 | Blocks until the device in question is ready, polling it on every interrupt. 1312 | The XIOS should call this if a process is waiting on a device that isn't 1313 | interrupt driven. 1314 | =============================================================================== 1315 | **** BDOS function 132 (DEV_WAITFLAG) - Wait on system flag **** 1316 | * Supported by: MP/M, Concurrent CP/M * 1317 | Entered with C=84h, E=flag number. Returns A=0 if OK, 0FFh if error. 1318 | Blocks until DEV_SETFLAG is called on the flag in question. Flags 0-3 are 1319 | reserved for the BDOS; others are system dependent. 1320 | =============================================================================== 1321 | **** BDOS function 133 (DEV_SETFLAG) - Set system flag **** 1322 | * Supported by: MP/M, Concurrent CP/M * 1323 | Entered with C=85h, E=flag number. Returns A=0 if OK, 0FFh if error. 1324 | Called by the XIOS interrupt handler to restart a process that's waiting on the 1325 | given system flag. 1326 | =============================================================================== 1327 | **** BDOS function 134 (Q_MAKE) - Create message queue **** 1328 | * Supported by: MP/M, Concurrent CP/M * 1329 | Entered with C=86h, DE=address of queue descriptor. Returns A=0 if OK, 0FFh if 1330 | error. 1331 | The queue descriptor is formed: 1332 | DW 0,0 ;Reserved 1333 | DW flags ;Bit 0: Mutual exclusion queue 1334 | ;Bit 1: Cannot be deleted 1335 | ;Bit 2: Restricted to system processes 1336 | ;Bit 3: RSP message queue 1337 | ;Bit 4: Reserved 1338 | ;Bit 5: RPL address queue 1339 | ;Bits 6,7: Reserved 1340 | DB ' name ' 1341 | DW msglen ;Number of bytes in a message 1342 | DW nmsg ;Number of messages in this queue 1343 | DW 0,0,0,0 ;Reserved 1344 | DW buffer ;Address of the queue buffer, which must be 1345 | ;at least msglen * nmsg bytes long. 1346 | =============================================================================== 1347 | **** BDOS function 135 (Q_OPEN) - Open message queue **** 1348 | * Supported by: MP/M, Concurrent CP/M * 1349 | Entered with C=87h, DE=address of closed QPB. Returns A=0 if OK, 0FFh if error. 1350 | Opens the specified message queue. All 8 bits of the queue name are compared, 1351 | unlike filename matches. 1352 | =============================================================================== 1353 | **** BDOS function 136 (Q_DELETE) - Delete message queue **** 1354 | * Supported by: MP/M, Concurrent CP/M * 1355 | Entered with C=88h, DE=address of open QPB. Returns A=0 if OK, 0FFh if error. 1356 | Deletes the specified message queue from the system. 1357 | =============================================================================== 1358 | **** BDOS function 137 (Q_READ) - Read from message queue **** 1359 | * Supported by: MP/M, Concurrent CP/M * 1360 | Entered with C=89h, DE=address of open QPB. Returns A=0 if OK, 0FFh if error. 1361 | If there's nothing to read, then the call blocks until there is something. 1362 | =============================================================================== 1363 | **** BDOS function 138 (Q_CREAD) - Conditionally read from message queue **** 1364 | * Supported by: MP/M, Concurrent CP/M * 1365 | Entered with C=8Ah, DE=address of open QPB. Returns A=0 if OK, 0FFh if error or 1366 | there's nothing to read. 1367 | As Q_READ, but does not block. 1368 | =============================================================================== 1369 | **** BDOS function 139 (Q_WRITE) - Write to message queue **** 1370 | * Supported by: MP/M, Concurrent CP/M * 1371 | Entered with C=8Bh, DE=address of open QPB. Returns A=0 if OK, 0FFh if error. 1372 | If there's no room in the message queue buffer, the call blocks until there is. 1373 | =============================================================================== 1374 | **** BDOS function 140 (Q_CWRITE) - Conditionally write to message queue **** 1375 | * Supported by: MP/M, Concurrent CP/M * 1376 | Entered with C=8Ch, DE=address of open QPB. Returns A=0 if OK, 0FFh if error or 1377 | the queue buffer is full. 1378 | As Q_WRITE, but does not block. 1379 | =============================================================================== 1380 | **** BDOS function 141 (P_DELAY) - Delay **** 1381 | * Supported by: MP/M, Concurrent CP/M, CP/M-86 v4. * 1382 | Entered with C=8Dh, DE=no. of ticks to wait. 1383 | Blocks the calling process for at least DE ticks of the system clock (system 1384 | dependent; usually 50Hz or 60Hz). Returns A=0FFh if this function is 1385 | unsupported. 1386 | =============================================================================== 1387 | **** BDOS function 142 (P_DISPATCH) - Call dispatcher **** 1388 | * Supported by: MP/M, Concurrent CP/M, CP/M-86 v4. * 1389 | Entered with C=8Eh. 1390 | Relinquishes the CPU. The process restarts when its next time slice falls due. 1391 | Returns A=0FFh if this function is unsupported. 1392 | =============================================================================== 1393 | **** BDOS function 143 (P_TERM) - Terminate process **** 1394 | * Supported by: MP/M, Concurrent CP/M, CP/M-86 v4. * 1395 | Entered with C=8Fh, DL=termination code. 1396 | In single-user systems, behaves as function_0. 1397 | In multiuser systems, the termination code must be 0FFh to terminate a system 1398 | process, or 0-0FEh to terminate a user process. 1399 | =============================================================================== 1400 | **** BDOS function 144 (P_CREATE) - Create a subprocess **** 1401 | * Supported by: MP/M, Concurrent CP/M * 1402 | Entered with C=90h, DE=address of initial process descriptor. 1403 | Launches a subprocess of the current process. 1404 | The process descriptor passed in DE can be a linked list (to launch several 1405 | processes at a time). If a descriptor is outside the system data segment, it 1406 | will be copied into the process table. 1407 | Format of Concurrent CP/M process descriptor: 1408 | DW link ; Address of next process descriptor. 1409 | DW thread ; Initialise to 0 1410 | DB stat ; Initialise to 0 (runnable). Other values are 1411 | ; 1=Polling device, 2=Delaying, 6=Waiting to read 1412 | ; queue, 7=Waiting to write queue, 8=Waiting on 1413 | ; system flag, 9=Waiting to attach device 1414 | DB prior ; Priority. 200=normal, 255=worst. 1-63 are 1415 | ; used by system processes, 198 by the terminal 1416 | ; message process. 1417 | DW flags ; Bit 0 set for system process. Only system processes 1418 | ; can launch system processes. 1419 | ; Bit 1 set if process can't be terminated. Only 1420 | ; system processes can launch interminable 1421 | ; processes. 1422 | ; Bit 2 set if process is part of the OS. The passed 1423 | ; descriptor must be inside the OS. 1424 | ; Bit 3 set if process descriptor is part of the 1425 | ; system PD table. 1426 | ; Bit 15 set if process uses the 8087. 1427 | DB ' name ' ; 8-byte process name. 1428 | DW uda ; User_data_area segment. Initialise to the number of 1429 | ; paragraphs from the beginning of the calling 1430 | ; process's data segment. 1431 | DB disk ; Current drive 1432 | DB user ; Current user ID 1433 | DW 0 ; Reserved 1434 | DW mem ; Memory Segment Descriptors owned by this process. 1435 | ; Initialise to zero. 1436 | DW 0,0,0 ; Reserved 1437 | DW parent ; Parent process ID; initialise to 0. 1438 | DB cns ; Default console number 1439 | DB 0,0,0 ; Reserved 1440 | DB list ; Default printer number 1441 | DB 0 ; Reserved 1442 | DW sflag ; Bit 0 is set if the process should block when not 1443 | ; in the foreground 1444 | DW 0,0,0,0 ; Reserved 1445 | The UDA is 256 bytes, or 352 bytes if the process uses the 8087, and it must be 1446 | paragraph-aligned. 1447 | DW 0 ; Reserved 1448 | DW DMA ; Initial DMA offset in child process's DS. 1449 | DS 28 ; Reserved, set to 0 1450 | DW ax ; Initial AX value 1451 | DW bx ; Initial BX value 1452 | DW cx ; Initial CX value 1453 | DW dx ; Initial DX value 1454 | DW si ; Initial SI value 1455 | DW di ; Initial DI value 1456 | DW bp ; Initial BP value 1457 | DW 0,0,0 ; Reserved 1458 | DW sp ; Initial SP value. The top of the stack must be set 1459 | ; up IP, CS, flags (so that an IRET launches the 1460 | ; new process). 1461 | DW 0 ; Reserved 1462 | DD int0 ; INT 0 handler; 0 to use the same as the calling 1463 | ; process. 1464 | DD int1 ; INT 1 handler; 0 to use the same as the calling 1465 | ; process. 1466 | DD 0 ; Reserved 1467 | DD int3 ; INT 3 handler; 0 to use the same as the calling 1468 | ; process. 1469 | DD int4 ; INT 4 handler; 0 to use the same as the calling 1470 | ; process. 1471 | DD 0 ; Reserved 1472 | DW cs ; Initial CS value; 0 to use calling process's DS 1473 | DW ds ; Initial DS value; 0 to use calling process's DS 1474 | DW es ; Initial ES value; 0 to use calling process's DS 1475 | DW ss ; Initial SS value; 0 to use calling process's DS 1476 | DD int224 ; INT 0E0h handler; 0 to use the same as the calling 1477 | ; process. This allows a parent process to intercept 1478 | ; BDOS calls made by its child. 1479 | DD int225 ; INT 0E1h handler; 0 to use the same as the calling 1480 | ; process. 1481 | All other fields must be initialised to 0, with one exception. If the process 1482 | being launched uses the 8087, then the word at offset 100h must be set to 1483 | 03FFh. 1484 | =============================================================================== 1485 | **** BDOS function 145 (P_PRIORITY) - Set process priority **** 1486 | * Supported by: MP/M, Concurrent CP/M * 1487 | Entered with C=91h, E = priority. 1488 | The highest priority is 0; the lowest is 0FFh. Transient programs start off at 1489 | priority 200. 1490 | =============================================================================== 1491 | **** BDOS function 146 (C_ATTACH) - Attach console **** 1492 | * Supported by: MP/M, Concurrent CP/M * 1493 | Entered with C=92h. 1494 | Blocks the calling process until the process's default console is available. 1495 | This is automatically called if any console I/O function is used while the 1496 | current process is not attached to the console. 1497 | =============================================================================== 1498 | **** BDOS function 147 (C_DETACH) - Detach console **** 1499 | * Supported by: MP/M, Concurrent CP/M, CP/M-86 v4. * 1500 | Entered with C=93h. 1501 | The program is disconnected from the screen and keyboard and must run in the 1502 | background. 1503 | Under CP/M-86 v4, attempting to use the keyboard will cause the program to be 1504 | terminated. Note that backgrounded programs cannot use the INT 2nh DOS-like 1505 | calls, only the INT E0h CP/M calls. Limited screen output can be achieved using 1506 | XIOS function 8 (write to status line). 1507 | Under MP/M and CCP/M, attempting to output to the screen will cause function 1508 | 146 to be invoked. 1509 | =============================================================================== 1510 | **** BDOS function 148 (C_SET) - Set console **** 1511 | * Supported by: MP/M, Concurrent CP/M * 1512 | Entered with C=94h, E=console number. 1513 | Sets the process's default console number. If the number is out of range, 1514 | returns A=0FFh; else returns 0. 1515 | =============================================================================== 1516 | **** BDOS function 149 (C_ASSIGN) - Assign console **** 1517 | * Supported by: MP/M, Concurrent CP/M * 1518 | Entered with C=95h, DE = address of assign control block. Returns A=0 if OK, 1519 | 0FFh if failed. 1520 | Assigns a console device to another process. The assign control block is 1521 | formed: 1522 | DB cns ;Which console to assign 1523 | DB match ;If 0FFh, the process must have console 1524 | ;'cns' as its default console. If 0, not. 1525 | DW pd ;Process ID of the process to be given the 1526 | ;console. Set to 0 to use the name below. 1527 | DB ' name ' 1528 | =============================================================================== 1529 | **** BDOS function 150 (P_CLI) - Send CLI command **** 1530 | * Supported by: MP/M, Concurrent CP/M * 1531 | Entered with C=96h, DE = address of command-line buffer. Returns A=0 if OK, 1532 | 0FFh if failed. 1533 | This executes a command line, either by passing it to a loaded Resident System 1534 | Process or by launching a program. If the current process is attached to its 1535 | default console, it will be detached and the child process (or RSP) will be 1536 | given it. Before it takes the console back, it should raise its priority to 1537 | less than 198, take the console, and set priority back to 200. 1538 | The format of the command-line buffer is: 1539 | DB 0 ;Reserved 1540 | DS 128 ;128 bytes for 0-terminated command line 1541 | DB 0 ;Reserved 1542 | =============================================================================== 1543 | **** BDOS function 151 (P_RPL) - Call resident procedure library **** 1544 | * Supported by: MP/M, Concurrent CP/M * 1545 | Entered with C=97h, DE=address of parameter. Returns HL=result from RPL, or 1546 | 0001h if RPL not found. 1547 | The parameter is formed: 1548 | DB ' name ' 1549 | DW parameter 1550 | Internally, this is implemented using message queues. 1551 | =============================================================================== 1552 | **** BDOS function 152 (F_PARSE) - Parse filename **** 1553 | * Supported by: MP/M, CP/M 3 and higher. * 1554 | Entered with C=98h, DE=address of PFCB. Returns codes in HL. 1555 | This parses an ASCII filename to a FCB. The format of the PFCB is: 1556 | DEFW ascii-address 1557 | DEFW fcb-address 1558 | The value returned in HL is 0FFFFh if the ASCII filename was invalid; 0 if the 1559 | ASCII filename was terminated with a zero or a carriage return; and otherwise 1560 | the address of the next character after the filename. 1561 | The filename can be of any form up to the full d:filename.typ;password 1562 | (password only under CP/M 3 and above). When the filename is parsed, the 1563 | password is copied to FCB+10h, with its length at FCB+1Ah. 1564 | Under ZPM3, the filename may be of the form du:filename.typ;password; FCB+0Dh 1565 | will be 0 if no user number was specified, or 1+number otherwise. If a Z-System 1566 | is loaded, ZPM3 can also parse filenames of the form dir:filename.typ;password. 1567 | BUG: ZPM3 (version N10) puts the password at FCB+0Ch rather than FCB+10h, 1568 | overwriting the user number stored at FCB+0Dh. It does not zero the last four 1569 | bytes of the FCB, while CP/M does. 1570 | The filename can be terminated by any of: space tab cr nul ; = > < . : , [ ] / 1571 | | . 1572 | =============================================================================== 1573 | **** BDOS function 153 (C_GET) - Return console number **** 1574 | * Supported by: MP/M, Concurrent CP/M * 1575 | Enter with C=99h (153). Returns A = default console number 1576 | Returns the number of the process's default console. 1577 | =============================================================================== 1578 | **** BDOS function 154 (S_SYSDAT) - System data address **** 1579 | * Supported by: MP/M, Concurrent CP/M, CP/M-86 v4. * 1580 | Entered with C=9Ah. Returns HL (or ES:BX) = system data address. 1581 | * CP/M-86_3.x/4.x_System_Data_Area_format 1582 | * CCP/M-86_3.1_System_Data_Area_format 1583 | * REAL/32_System_Data_Area_format 1584 | =============================================================================== 1585 | **** BDOS function 155 (T_SECONDS) - Get date and time **** 1586 | * Supported by: MP/M, Concurrent CP/M * 1587 | Entered with C=9Bh, DE=address of time stamp. 1588 | The format of the time stamp is: 1589 | DW day ;Day 1 is 1 January 1978 1590 | DB hour ;Packed BCD 1591 | DB minute ;Packed BCD 1592 | DB second ;Packed BCD 1593 | Note that this function fills in the "seconds" field, which function 105 does 1594 | not do. 1595 | =============================================================================== 1596 | **** BDOS function 156 (P_PDADR) - Return address of process descriptor **** 1597 | * Supported by: MP/M 2, Concurrent CP/M * 1598 | Entered with C=9Ch. Returns HL = address of descriptor (ES:BX in 16-bit 1599 | systems). 1600 | The process descriptor is described in P_CREATE. 1601 | =============================================================================== 1602 | **** BDOS function 157 (P_ABORT) - Abort a process **** 1603 | * Supported by: MP/M 2, Concurrent CP/M * 1604 | Entered with C=9Dh, DE = address of abort parameter block. Returns A=0 if OK, 1605 | 0FFh if failed. 1606 | Terminates a process by force. The abort parameter block is formed: 1607 | DW pd ;Process ID of the process to be 1608 | ;terminated. If this is zero, uses 1609 | ;the 'name' and 'cns' fields for a match. 1610 | DW term ;Termination code (see P_TERM) 1611 | DB cns ;(if PD=0) Default console of process to be 1612 | ;terminated. 1613 | DB 0 1614 | DB ' name ' ;(if PD=0) Name of process to be terminated. 1615 | The low byte of the termination code is 0FFh to abort a system process, other 1616 | values to abort a user process. 1617 | =============================================================================== 1618 | **** BDOS function 158 (L_ATTACH) - Attach printer **** 1619 | * Supported by: MP/M 2, Concurrent CP/M * 1620 | Entered with C=9Eh. 1621 | Blocks the calling process until the default printer is available. This is 1622 | automatically called if any printer function is used while the current process 1623 | is not attached to the printer. 1624 | =============================================================================== 1625 | **** BDOS function 159 (L_DETACH) - Detach printer **** 1626 | * Supported by: MP/M 2, Concurrent CP/M * 1627 | Entered with C=9Fh. 1628 | Detach this process from the printer, 1629 | =============================================================================== 1630 | **** BDOS function 160 (L_SET) - Select printer **** 1631 | * Supported by: MP/M 2, Concurrent CP/M * 1632 | Entered with C=0A0h, E=printer number. 1633 | Sets the process's default printer number. If the number is out of range, 1634 | returns A=0FFh; else returns 0. 1635 | =============================================================================== 1636 | **** BDOS function 161 (L_CATTACH) - Conditionally attach printer **** 1637 | * Supported by: MP/M 2, Concurrent CP/M * 1638 | Entered with C=0A1h. 1639 | Attempts to attach to the process's default printer without blocking. Returns 0 1640 | if succeeded, 0FFh if something else is using the printer. 1641 | =============================================================================== 1642 | **** BDOS function 162 (C_CATTACH)- Conditionally attach console **** 1643 | * Supported by: MP/M 2, Concurrent CP/M * 1644 | Entered with C=0A2h. 1645 | Attempts to attach to the process's default console without blocking. Returns 0 1646 | if succeeded, 0FFh if something else is using the console. 1647 | =============================================================================== 1648 | **** BDOS function 163 (S_OSVER) - Return version number **** 1649 | * Supported by: DOSPLUS, MP/M 2, Concurrent CP/M * 1650 | Entered with C=A3h. 1651 | The version number returned by this function is a "user-visible" number rather 1652 | than the internal BDOS version. Numbers include: 1653 | 1012h - DOSPLUS v1.2 1654 | 1021h - DOSPLUS v2.1 1655 | 1121h - MP/M-86 v2.1 1656 | 1420h - Concurrent CP/M v2.0 1657 | 1431h - Concurrent CP/M v3.1 1658 | 1432h - Concurrent DOS v3.2 1659 | 1441h - Concurrent DOS v4.1 1660 | 1450h - Concurrent DOS v5.0 1661 | (the high byte has the same meaning as in function 12.) 1662 | =============================================================================== 1663 | **** BDOS function 164 (L_GET) - Return default printer device number **** 1664 | * Supported by: MP/M 2, Concurrent CP/M * 1665 | Enter with C=0A4h (164). Returns A = default printer number 1666 | Returns the number of the process's default printer. 1667 | =============================================================================== 1668 | **** BDOS function 175 - Return real drive ID **** 1669 | * Supported by: DOSPLUS v2.1 * 1670 | Entered with CL=AFh, DL=drive number (0 => A:, 1 => B: etc.) 1671 | DOSPLUS treats drive N:, O: and P: as floating drives, which can be assigned to 1672 | paths on other drives (the same sort of thing as SUBST under MS-DOS). 1673 | * If DL is the number of a non-floating drive, this will return AH=BH=0 and 1674 | AL=BL=drive number passed in. This is true even if the drive in question 1675 | does not exist. 1676 | * If DL is the number of a floating drive which has not been assigned, this 1677 | will return AH=BH=0 and AL=BL=drive number of the boot drive. 1678 | * If DL is the number of a floating drive which has not been assigned, this 1679 | will return AH=BH=0 and AL=BL=drive number of the host drive. 1680 | * If DL is out of range, this will return AX=BX=0FFFFh and CX=17h. 1681 | For example, after the command CD P:=B:\MYDIR, this call will return 1682 | AX=BX=0001h (B:) if it was entered with DL=0Fh (P:). 1683 | =============================================================================== 1684 | **** P2DOS function 200 - get time **** 1685 | This has the same functionality under P2DOS as function_105 does under Z80DOS 1686 | and DOS+. 1687 | Used by P2DOS, SUPRBDOS, NovaDOS. 1688 | =============================================================================== 1689 | **** P2DOS function 201 - set time **** 1690 | This has the same functionality under P2DOS as function_104 does under Z80DOS 1691 | and DOS+. 1692 | Used by P2DOS, SUPRBDOS, NovaDOS. 1693 | =============================================================================== 1694 | **** DOS+ function 210 - Return system information **** 1695 | * Supported by: DOS+ * 1696 | Entered with C=0D2h, E=number, 0-5. Returns values in BA and HL. 1697 | Suitable arguments are: 1698 | 0. Return base address for BDOS module. Used for dynamic reconfiguration. 1699 | 1. Return the current DMA setting. 1700 | 2. Flush any console input pending, emptying all buffers. Returns 0. 1701 | 3. Returns list device status (ready/not ready) 1702 | 4. Returns punch " " " 1703 | 5. Returns reader " " " 1704 | Arguments 3 to 5 represent ready as 0ffh, not ready as 0. If no custom 1705 | installation for punch/reader device status has been made their status is 1706 | returned 0 (not ready). Other arguments return 0. 1707 | =============================================================================== 1708 | **** DOS+ function 211 - Print decimal number **** 1709 | * Supported by: DOS+ * 1710 | Entered with C=0D3h, DE=decimal number. 1711 | Outputs the number as an unsigned 16-bit integer, with leading zero 1712 | suppression. 1713 | =============================================================================== 1714 | -------------------------------------------------------------------------------- /doc/bdosfunc_summary.txt: -------------------------------------------------------------------------------- 1 | ****** BDOS Function Summary ****** 2 | ***** CP/M 2.2, CP/M 3.0, MP/M 2.1 ***** 3 | **** Compiled by Jim Lopushinsky **** 4 | *** Notation: *** 5 | .somethingmeans the address of something. 6 | /-----CP/M 2.2 7 | | /---CP/M 3.0 8 | | | /-MP/M 2.1 9 | | | | INPUT RETURNED 10 | FUNC v v v FUNCTION NAME PARAMETERS VALUES 11 | 12 | 0 X X X System Reset none none 13 | 1 X X X Console Input none A = char 14 | 2 X X X Console Output E = char none 15 | 3 X X - Auxiliary Input none A = char 16 | 3 - - X Raw Console Input none A = char 17 | 4 X X - Auxiliary Output E = char none 18 | 4 - - X Raw Console Output E = char none 19 | 5 X X X List Output E = char none 20 | 6 X X X Direct Console I/O E = 0FFH/ A = char/ 21 | 0FEH/ status/ 22 | 0FDH/ none 23 | char 24 | 7 X - - Get I/O Byte none A = IOBYTE 25 | 7 - X - Auxiliary Input Status none A = 00/0FFH 26 | 8 X - - Set I/O Byte E = IOBYTE none 27 | 8 - X - Auxiliary Output Status none A = 00/0FFH 28 | 9 X X X Print String DE = .String none 29 | 10 X X X Read Console Buffer DE = .Buffer Characters in 30 | buffer 31 | 11 X X X Get Console Status none A = 00/01 32 | 12 X X X Return Version Number none HL = Version 33 | 13 X X X Reset Disk System none see def 34 | 14 X X X Select Disk E = Disk Number see def 35 | 15 X X X Open File DE = .FCB A = Dir Code 36 | 16 X X X Close File DE = .FCB A = Dir Code 37 | 17 X X X Search for First DE = .FCB A = Dir Code 38 | 18 X X X Search for Next none A = Dir Code 39 | 19 X X X Delete File DE = .FCB A = Dir Code 40 | 20 X X X Read Sequential DE = .FCB A = Err Code 41 | 21 X X X Write Sequential DE = .FCB A = Err Code 42 | 22 X X X Make File DE = .FCB A = Dir Code 43 | 23 X X X Rename File DE = .FCB A = Dir Code 44 | 24 X X X Return Login Vector none HL= Login Vector 45 | 25 X X X Return Current Disk none A = Cur Disk # 46 | 26 X X X Set DMA Address DE = .DMA none 47 | 27 X X X Get Addr (Alloc) none HL= .Alloc 48 | 28 X X X Write Protect Disk none none 49 | 29 X X X Get R/O Vector none HL= R/O Vector 50 | 30 X X X Set File Attributes DE = .FCB A = Dir Code 51 | 31 X X X Get Addr (DPB) none HL= .DPB 52 | 32 X X X Set/Get User Code E = 0FFH/ A = Curr User/ 53 | user number none 54 | 33 X X X Read Random DE = .FCB A = Err Code 55 | 34 X X X Write Random DE = .FCB A = Err Code 56 | 35 X X X Compute File Size DE = .FCB r0, r1, r2 57 | 36 X X X Set Random Record DE = .FCB r0, r1, r2 58 | 37 X X X Reset Drive DE = Drive A = Err Code 59 | Vector 60 | 38 - X X Access Drive DE = Drive none 61 | Vector 62 | 39 - X X Free Drive DE = Drive none 63 | Vector 64 | 40 X X X Write Random with Zero Fill DE = .FCB A = Err Code 65 | 41 - - X Test and Write Record DE = .FCB HL= Err Code 66 | 41 - X - Test and Write Record DE = .FCB A = 0FFH 67 | 42 - X X Lock Record DE = .FCB HL = Err Code 68 | 43 - X X Unlock Record DE = .FCB HL = Err Code 69 | 44 - X X Set Multi-sector Count E = # Sectors A = Return Code 70 | 45 - X X Set BDOS Error Mode E = Err Mode none 71 | 46 - X X Get Disk Free Space E = Drive # Number of Free 72 | Sectors 73 | 47 - X X Chain to Program E = Chain Flg none 74 | 48 - X X Flush Buffers E = Purge Flg A = Err Flag 75 | 49 - X - Get/Set System Control DE = .SCB PB A = Returned Byte 76 | Block HL= Returned Word 77 | 50 - X - Direct Bios Calls DE = .BIOS PB BIOS Return 78 | 59 - X - Load Overlay DE = .FCB A = Err Code 79 | 60 - X - Call Resident System DE = .RSX PB A = Err Code 80 | Extension 81 | 98 - X - Free Blocks none none 82 | 99 - X - Truncate File DE = .FCB A = Dir Code 83 | 100 - X X Set Directory Label DE = .FCB A = Dir Code 84 | 101 - X X Return Directory Label Data E = Drive A = Label Data 85 | 102 - X X Read File Date Stamps DE = .FCB A = Dir Code 86 | and Password Mode 87 | 103 - X X Write File XFCB DE = .FCB A = Dir Code 88 | 104 - X X Set Date and Time DE = .DAT none 89 | 105 - X X Get Date and Time DE = .DAT Date and Time 90 | A = seconds 91 | 106 - X X Set Default Password DE = .Passwrd none 92 | 107 - X X Return Serial Number DE = .Serial # Serial Number 93 | field 94 | 108 - X - Get/Set Program Return Code DE = 0FFFFH/ HL= Ret Code/ 95 | Code none 96 | 109 - X - Get/Set Console Mode DE = 0FFFFH/ HL= Console Mode/ 97 | Mode none 98 | 110 - X - Get/Set Output Delimiter DE = 0FFFFH/ A = Output Delim/ 99 | E = Delimiter none 100 | 111 - X - Print Block DE = .CCB none 101 | 112 - X - List Block DE = .CCB none 102 | 128 - - X Absolute Memory Request DE = .MD A = Err Code 103 | 129 - - X Relocatable Memory Request DE = .MD A = Err Code 104 | 130 - - X Memory Free DE = .MD none 105 | 131 - - X Poll E = Device none 106 | 132 - - X Flag Wait E = Flag A = Err Code 107 | 133 - - X Flag Set E = Flag A = Err Code 108 | 134 - - X Make Queue DE = .QCB none 109 | 135 - - X Open Queue DE = .UQCB A = Err Code 110 | 136 - - X Delete Queue DE = .QCB A = Err Code 111 | 137 - - X Read Queue DE = .UQCB none 112 | 138 - - X Conditional Read Queue DE = .UQCB A = Err Code 113 | 139 - - X Write Queue DE = .UQCB none 114 | 140 - - X Conditional Write Queue DE = .UQCB A = Err Code 115 | 141 - - X Delay DE = # ticks none 116 | 142 - - X Dispatch none none 117 | 143 - - X Terminate Process E = Term. Code none 118 | 144 - - X Create Process DE = .PD none 119 | 145 - - X Set Priority E = Priority none 120 | 146 - - X Attach Console none none 121 | 147 - - X Detach Console none none 122 | 148 - - X Set Console E = Console none 123 | 149 - - X Assign Console DE = .APB A = Err Code 124 | 150 - - X Send CLI Command DE = .CLICMD none 125 | 151 - - X Call Resident Sys Proc DE = .CPB HL= result 126 | 152 - X X Parse Filename DE = .PFCB see def 127 | 153 - - X Get Console Number none A = Console # 128 | 154 - - X System Data Address none HL= Sys Data Addr 129 | 155 - - X Get Date and Time DE = .TOD date and time 130 | 156 - - X Return Process Descriptor none HL= PD Addr 131 | 157 - - X Abort Specified Process DE = .ABT PB A = Err Code 132 | 158 - - X Attach List none none 133 | 159 - - X Detach List none none 134 | 160 - - X Set List E = List # none 135 | 161 - - X Conditional Attach List none A = Err Code 136 | 162 - - X Conditional Attach Console none A = Err Code 137 | 163 - - X MPM Version Number none HL= Version # 138 | 164 - - X Get List Number none A = List # 139 | -------------------------------------------------------------------------------- /doc/bios_func.txt: -------------------------------------------------------------------------------- 1 | ****** CP/M Basic Input/Output System ****** 2 | The BIOS is the machine-dependent part of CP/M. In theory, all you need to do 3 | is change the BIOS and CP/M will work on a different machine. The same used to 4 | be true of MSDOS... 5 | The BIOS begins with the following jumps to service routines: 6 | JMP BOOT 7 | ;-3: Cold start routine 8 | JMP WBOOT 9 | ; 0: Warm boot - reload command processor 10 | JMP CONST 11 | ; 3: Console status 12 | JMP CONIN 13 | ; 6: Console input 14 | JMP CONOUT 15 | ; 9: Console output 16 | JMP LIST 17 | ;12: Printer output 18 | JMP PUNCH 19 | ;15: Paper tape punch output 20 | JMP READER 21 | ;18: Paper tape reader input 22 | JMP HOME 23 | ;21: Move disc head to track 0 24 | JMP SELDSK 25 | ;24: Select disc drive 26 | JMP SETTRK 27 | ;27: Set track number 28 | JMP SETSEC 29 | ;30: Set sector number 30 | JMP SETDMA 31 | ;33: Set DMA address 32 | JMP READ 33 | ;36: Read a sector 34 | JMP WRITE 35 | ;39: Write a sector 36 | In CP/M 2 and later, the following extra jumps appear: 37 | JMP LISTST 38 | ;42: Status of list device 39 | JMP SECTRAN 40 | ;45: Sector translation for skewing 41 | In CP/M 3, a further set of jumps is present: 42 | JMP CONOST 43 | ;48: Status of console output 44 | JMP AUXIST 45 | ;51: Status of auxiliary input 46 | JMP AUXOST 47 | ;54: Status of auxiliary output 48 | JMP DEVTBL 49 | ;57: Address of devices table 50 | JMP DEVINI 51 | ;60: Initialise a device 52 | JMP DRVTBL 53 | ;63: Address of discs table 54 | JMP MULTIO 55 | ;66: Read/write multiple sectors 56 | JMP FLUSH 57 | ;69: Flush host buffers 58 | JMP MOVE 59 | ;72: Move a block of memory 60 | JMP TIME 61 | ;75: Real time clock 62 | JMP SELMEM 63 | ;78: Select memory bank 64 | JMP SETBNK 65 | ;81: Select bank for DMA operation 66 | JMP XMOVE 67 | ;84: Preload banks for MOVE 68 | JMP USERF 69 | ;87: System-depedent functions 70 | JMP RESERV1 71 | ;90: Reserved 72 | JMP RESERV2 73 | ;93: Reserved 74 | **** BOOT (function 0) **** 75 | This function is completely implementation-dependent and should never be called 76 | from user code. 77 | **** WBOOT (function 1) **** 78 | Reloads the command processor and (on some systems) the BDOS as well. How it 79 | does this is implementation-dependent; it may use the reserved tracks of a 80 | floppy disc or extra memory. 81 | **** CONST (function 2) **** 82 | Returns its status in A; 0 if no character is ready, 0FFh if one is. 83 | **** CONIN (function 3) **** 84 | Wait until the keyboard is ready to provide a character, and return it in A. 85 | **** CONOUT (function 4) **** 86 | Write the character in C to the screen. 87 | **** LIST (function 5) **** 88 | Write the character in C to the printer. If the printer isn't ready, wait until 89 | it is. 90 | **** PUNCH / AUXOUT (function 6) **** 91 | Write the character in C to the "paper tape punch" - or whatever the current 92 | auxiliary device is. If the device isn't ready, wait until it is. 93 | This function is called PUNCH in CP/M 2.x, AUXOUT in CP/M 3. 94 | **** READER (function 7) **** 95 | Read a character from the "paper tape reader" - or whatever the current 96 | auxiliary device is. If the device isn't ready, wait until it is. The character 97 | will be returned in A. If this device isn't implemented, return character 26 98 | (^Z). 99 | This function is called READER in CP/M 2.x, AUXIN in CP/M 3. 100 | **** HOME (function 8) **** 101 | Move the current drive to track 0. 102 | **** SELDSK (function 9) **** 103 | Select the disc drive in register C (0=A:, 1=B: ...). Called with E=0 or 104 | 0FFFFh. 105 | If bit 0 of E is 0, then the disc is logged in as if new; if the format has to 106 | be determined from the boot sector, for example, this will be done. 107 | If bit 0 if E is 1, then the disc has been logged in before. The disc is not 108 | accessed; the DPH address (or zero) is returned immediately. 109 | SELDSK returns the address of a Disc_Parameter_Header in HL. The exact format 110 | of a DPH varies between CP/M versions; note that under CP/M 3, the DPH is in 111 | memory bank 0 and probably not visible to programs. If the disc could not be 112 | selected it returns HL=0. 113 | **** SETTRK (function 10) **** 114 | Set the track in BC - 0 based. 115 | **** SETSEC (function 11) **** 116 | Set the sector in BC. Under CP/M 1 and 2 a sector is 128 bytes. Under CP/M 3 117 | the sector size is given in the Disk Parameter Block. 118 | There has been discussion in comp.os.cpm about whether the parameter to this 119 | function is a byte or a word. The conclusion (based on examining the BDOS 120 | source) was that it is a word. 121 | **** SETDMA (function 12) **** 122 | The next disc operation will read its data from (or write its data to) the 123 | address given in BC. 124 | **** READ (function 13) **** 125 | Read the currently set track and sector at the current DMA address. Returns A=0 126 | for OK, 1 for unrecoverable error, 0FFh if media changed. 127 | **** WRITE (function 14) **** 128 | Write the currently set track and sector. C contains a deblocking code: 129 | C=0 - Write can be deferred 130 | C=1 - Write must be immediate 131 | C=2 - Write can be deferred, no pre-read is necessary. 132 | Returns A=0 for OK, 1 for unrecoverable error, 2 if disc is readonly, 0FFh if 133 | media changed. 134 | **** LISTST (function 15) **** 135 | Return status of current printer device. 136 | Returns A=0 (not ready) or A=0FFh (ready). 137 | **** SECTRAN (function 16) **** 138 | Translate sector numbers to take account of skewing. 139 | On entry, BC=logical sector number (zero based) and DE=address of translation 140 | table. On exit, HL contains physical sector number. On a system with hardware 141 | skewing, this would normally ignore DE and return either BC or BC+1. 142 | **** CONOST (function 17) **** 143 | Return status of current screen output device. 144 | Returns A=0 (not ready) or A=0FFh (ready). 145 | **** AUXIST (function 18) **** 146 | Return status of current auxiliary input device. 147 | Returns A=0 (not ready) or A=0FFh (ready). 148 | **** AUXOST (function 19) **** 149 | Return status of current auxiliary output device. 150 | Returns A=0 (not ready) or A=0FFh (ready). 151 | **** DEVTBL (function 20) **** 152 | Return in HL the address of the devices table, or 0 if the devices table isn't 153 | implemented. 154 | The devices table will be visible to programs without the need for bank 155 | switching, ie. it will be in common memory. 156 | The device table contains one entry for each character device. Each entry is 157 | formed: 158 | DEFB 'NAME ' ;Name, 6 bytes. If the first byte is zero, 159 | ;this is the end of the table. 160 | DEFB mode ;Bitmapped value: 161 | ;Bit 0 set => can input from this device 162 | ;Bit 1 set => can output to this device 163 | ;Bit 2 set => can change the baud rate 164 | ;Bit 3 set => supports XON/XOFF 165 | ;Bit 4 set => is using XON/XOFF 166 | ;Bits 5,6,7 set to 0. 167 | ; Amstrad extension: If bit 7 is set, output 168 | ;to the device does not time out. 169 | DEFB baudrate ;Coded speed, 1-15 or 0 if speed can't be 170 | ;changed. 171 | ;Rates are 50,75,110,134.5,150,300,600,1200, 172 | ; 1800,2400,3600,4800,7200,9600,19200. 173 | The maximum number of devices allowed in CP/M Plus is unclear. The 174 | documentation variously says there can be 12 or 13, while the DEVICE.COM source 175 | code suggests 15. 176 | **** DEVINI (function 21) **** 177 | Reinitialise character device number C - called when the device's settings 178 | (baud rate, mode etc.) are changed. 179 | **** DRVTBL (function 22) **** 180 | Return in HL the address of the drive table, or 0 (or 0FFFFh, or 0FFFEh) if the 181 | drive table isn't implemented. The drive table contains 16 pointers to the Disc 182 | Parameter Headers of the 16 disc drives A-P; if a pointer is 0 it means that 183 | the corresponding drive does not exist. 184 | The drive table is usually (but not always) in common memory (ie accessible by 185 | user programs). 186 | **** MULTIO (function 23) **** 187 | Notify the BIOS that the BDOS is intending to transfer a number of consecutive 188 | disc sectors with READ or WRITE. Entered with C = number of calls that will be 189 | made; up to 16k of data will be transferred. 190 | The idea is that after the MULTIO call, the BIOS can choose to transfer all the 191 | data in the first READ/WRITE operation, and then not to do anything on the 192 | subsequent (n-1) operations. 193 | **** FLUSH (function 24) **** 194 | Write any pending data to disc. 195 | Returns A=0 if successful, 1 for physical disc error, 2 for drive R/O. 196 | This function is only useful when the BIOS is doing the deblocking - ie, the 197 | physical sector size is not the size that the BIOS reports to the BDOS. 198 | **** MOVE (function 25) **** 199 | Move BC bytes of memory, from the address in DE to the address in HL (the other 200 | way round from the Z80's LDIR instruction). Should return HL and DE pointing to 201 | the first addresses not copied. If XMOVE is used before this function, data are 202 | moved between two memory banks. 203 | **** TIME (function 26) **** 204 | Get the current date and time into the SCB (at BOOT-0Ch). HL and DE must be 205 | preserved. If C=0FFh, then set the time from the SCB instead. 206 | The format of the 5-byte buffer is: 207 | DW day ;Day 1 is 1 Jan 1978 208 | DB hour ;packed BCD 209 | DB minute ;packed BCD 210 | DB second ;packed BCD 211 | **** SELMEM (function 27) **** 212 | Set the current bank to the number in A. Bank 1 is the bank in which user 213 | programs run (the TPA); Bank 0 and any other banks are used by CP/M for disc 214 | buffers or as a RAMdisc. 215 | According to the DRI documentation, this function must preserve all registers 216 | except A. 217 | **** SETBNK (function 28) **** 218 | Set the bank to be used for the next read/write sector operation. The bank 219 | number is passed in A. Note that the BDOS will call SETBNK after calling 220 | SETDMA; some BIOSes insist on this order, so it's safest if your programs do 221 | the same. 222 | **** XMOVE (function 29) **** 223 | After XMOVE, the next call to MOVE will move data between different memory 224 | banks. Call XMOVE with C=source bank and B=destination bank. According to the 225 | CP/M Plus System Guide, the BDOS will only ever use this function to move 128 226 | or fewer bytes in one go; some BIOSes may not support bigger moves between 227 | banks. 228 | **** USERF (function 30) **** 229 | This function is reserved for the author of the BIOS to add any extra features. 230 | On Amstrad computers, for example, this call accesses the extended_BIOS 231 | functions. 232 | **** RESERV1 and RESERV2 (functions 31, 32) **** 233 | These calls are reserved and contain JMP 0 instructions. 234 | =============================================================================== 235 | -------------------------------------------------------------------------------- /doc/fcb.txt: -------------------------------------------------------------------------------- 1 | ****** CP/M File Control Block ****** 2 | The File Control Block is a 36-byte data structure (33 bytes in CP/M 1). It is 3 | laid out as follows: 4 | DR F1 F2 F3 F4 F5 F6 F7 F8 T1 T2 T3 EX S1 S2 RC .FILENAMETYP... 5 | AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL ............... 6 | CR R0 R1 R2 .... 7 | The bytes in it have the following meanings: 8 | 9 | FCB+00h DR - Drive. 0 for default, 1-16 for A-P. In DOSPLUS, 10 | bit 7 can be set to indicate that the operation should work with 11 | subdirectories rather than files. 12 | 13 | FCB+01h Fn - Filename, 7-bit ASCII. The top bits of the filename bytes 14 | (usually referred to as F1' to F8') have the following 15 | meanings: 16 | F1'-F4' - User-defined attributes. Any program can use 17 | them in any way it likes. The filename in the 18 | disc directory has the corresponding bits set. 19 | F5'-F8' - Interface attributes. They modify the 20 | behaviour of various BDOS functions or 21 | indicate error conditions. In the directory 22 | these bits are always zero. 23 | FCB+09h Tn - Filetype, 7-bit ASCII. T1' to T3' have the following 24 | meanings: 25 | T1' - Read-Only. 26 | T2' - System (hidden). System files in user 0 can be 27 | opened from other user areas. 28 | T3' - Archive. Set if the file has not been changed 29 | since it was last copied. 30 | FCB+0Ch EX - Set this to 0 when opening a file and then leave it to 31 | CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. 32 | FCB+0Dh S1 - Reserved. 33 | FCB+0Eh S2 - Reserved. 34 | FCB+0Fh RC - Set this to 0 when opening a file and then leave it to 35 | CP/M. 36 | FCB+10h AL - Image of the second half of the directory entry, 37 | containing the file's allocation (which disc blocks it 38 | owns). 39 | FCB+20h CR - Current record within extent. It is usually best to set 40 | this to 0 immediately after a file has been opened and 41 | then ignore it. 42 | FCB+21h Rn - Random access record number (not CP/M 1). A 16-bit 43 | value in CP/M 2 (with R2 used for overflow); an 18-bit 44 | value in CP/M 3. 45 | If you are writing an emulator at BDOS level, you need to be aware of how CP/ 46 | M uses the bytes EX, S2, and CR. Some programs (such as the Digital Research 47 | linker, LINK.COM) manipulate these bytes to perform "seek" operations in files 48 | without using the random-access calls. 49 | CR = current record, ie (file pointer % 16384) / 128 50 | EX = current extent, ie (file pointer % 524288) / 16384 51 | S2 = extent high byte, ie (file pointer / 524288). The CP/M Plus source 52 | code refers to this use of the S2 byte as 'module number'. 53 | -------------------------------------------------------------------------------- /doc/iobyte.txt: -------------------------------------------------------------------------------- 1 | ****** CP/M 2 Input/Output mapping ****** 2 | Under CP/M 2, input and output device selection are performed by the BIOS. Some 3 | BIOSes may implement a feature called the IOBYTE which allows the user to 4 | change which device the screen, printer etc. output go to. 5 | The IOBYTE lives at address 3 (in the Zero Page) and should be changed using 6 | BDOS calls 7 and 8 (get/set IOBYTE). The value is bitmapped: 7 | Bits Bits 6,7 Bits 4,5 Bits 2,3 Bits 0,1 8 | Device LIST PUNCH READER CONSOLE 9 | 10 | Value 11 | 00 TTY: TTY: TTY: TTY: 12 | 01 CRT: PTP: PTR: CRT: 13 | 10 LPT: UP1: UR1: BAT: 14 | 11 UL1: UP2: UR2: UC1: 15 | * BAT = batch mode. Use the current Reader for console input, and he 16 | current List (printer) device as the console output. 17 | * CRT = Standard console (keyboard and terminal screen). 18 | * LPT = Standard line printer. 19 | * PTP = Standard Paper Tape Punch. 20 | * PTR = Standard Paper Tape Reader. 21 | * TTY = Teletype device, eg a serial port. 22 | * UC1 = User defined (ie implementation dependent) console device. 23 | * UL1 = User defined (ie implementation dependent) printer device. 24 | * UPn = User defined (ie implementation dependent) output device. 25 | * URn = User defined (ie implementation dependent) input device. 26 | -------------------------------------------------------------------------------- /esrc/empty/empty.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "cpmbdos.h" 6 | #include "cprintf.h" 7 | #include "syslib/cpm_sysfunc.h" 8 | #include "syslib/ansi_term.h" 9 | 10 | void sys_init(void) { 11 | cpm_sysfunc_init(); 12 | } 13 | 14 | int main() { 15 | sys_init(); 16 | 17 | cprintf("This does NOTHING!!! %u %u %u\n", 1, 2, 3); 18 | 19 | return (EXIT_SUCCESS); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /esrc/empty/empty.mk: -------------------------------------------------------------------------------- 1 | example-empty: $(BIN_DIR)/empty.com 2 | 3 | $(BIN_DIR)/empty.com: tools $(BIN_DIR)/empty.ihx 4 | $(LBIN_DIR)/load $(BIN_DIR)/empty 5 | 6 | $(BIN_DIR)/empty.ihx: libraries $(BIN_DIR)/empty.rel $(BIN_DIR)/empty.arf 7 | $(CLD) $(CLD_FLAGS) -nf $(BIN_DIR)/empty.arf 8 | 9 | $(BIN_DIR)/empty.rel: $(ESRC_DIR)/empty/empty.c 10 | $(CCC) $(CCC_FLAGS) -o $(BIN_DIR) $(ESRC_DIR)/empty/empty.c 11 | 12 | $(BIN_DIR)/empty.arf: $(BIN_DIR)/generic.arf 13 | $(QUIET)$(SED) 's/$(REPLACE_TAG)/empty/' $(BIN_DIR)/generic.arf > $(BIN_DIR)/empty.arf 14 | -------------------------------------------------------------------------------- /esrc/fileop/fileop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "cpmbdos.h" 6 | #include "cprintf.h" 7 | #include "syslib/cpm_sysfunc.h" 8 | #include "syslib/ansi_term.h" 9 | 10 | #define BUF_SIZE 128 11 | static uint8_t dma_buffer[BUF_SIZE]; 12 | 13 | void sys_init(void) { 14 | cpm_sysfunc_init(); 15 | } 16 | 17 | void print_fcb(FCB *fcb_ptr); 18 | void print_dir(CPM_DIR *dir_ptr); 19 | void print_buf(void); 20 | 21 | int main() { 22 | uint8_t rval; 23 | FCB *fcb_ptr = NULL; 24 | 25 | sys_init(); 26 | 27 | cprintf("CP/M LIB TESTER: File operations.\n\n"); 28 | 29 | fcb_ptr = malloc(sizeof(FCB)); 30 | 31 | cprintf("Trying to open \"fileop.com\"..."); 32 | memset(fcb_ptr, 0, sizeof(FCB)); 33 | cpm_setFCBname("fileop", "com", fcb_ptr); 34 | rval = cpm_performFileOp(fop_open, fcb_ptr); 35 | cprintf(" ret.val %02X\n", rval); 36 | 37 | if (rval != 0xFF) { 38 | cprintf("File found, reading the FCB!\n"); 39 | 40 | print_fcb(fcb_ptr); 41 | 42 | cprintf("Done, closing the file... "); 43 | rval = cpm_performFileOp(fop_close, fcb_ptr); 44 | cprintf(" ret.val %02X\n", rval); 45 | } 46 | 47 | cprintf("Trying a file search... "); 48 | 49 | memset(fcb_ptr, 0, sizeof(FCB)); 50 | cpm_setFCBname("f?l??p", "??m", fcb_ptr); 51 | 52 | cpm_setDMAAddr((uint16_t)dma_buffer); 53 | rval = cpm_performFileOp(fop_firstNameMatch, fcb_ptr); 54 | cprintf("Result %02X\n", rval); 55 | //rval = cpm_performFileOp(fop_firstNameMatch, fcb_ptr); 56 | if (rval != 0xFF) { 57 | CPM_DIR *dirstruct = (CPM_DIR*)((uint16_t)dma_buffer + (rval*32)); 58 | print_dir(dirstruct); 59 | } 60 | 61 | cprintf("Trying a file delete... "); 62 | memset(fcb_ptr, 0, sizeof(FCB)); 63 | cpm_setFCBname("test", "txt", fcb_ptr); 64 | rval = cpm_performFileOp(fop_delFile, fcb_ptr); 65 | cprintf(" ret.val %02X\n", rval); 66 | 67 | cprintf("Trying a read... "); 68 | memset(fcb_ptr, 0, sizeof(FCB)); 69 | cpm_setFCBname("fileop", "com", fcb_ptr); 70 | rval = cpm_performFileOp(fop_open, fcb_ptr); 71 | cpm_setDMAAddr((uint16_t)dma_buffer); 72 | rval = cpm_performFileOp(fop_readRandRecord, fcb_ptr); 73 | cprintf(" ret.val %02X\n", rval); 74 | if (rval == 0) { 75 | print_buf(); 76 | } 77 | 78 | free(fcb_ptr); 79 | 80 | return (EXIT_SUCCESS); 81 | } 82 | 83 | void print_fcb(FCB *fcb_ptr) { 84 | cprintf("\tname ->\t%c%c%c%c%c%c%c%c\n", fcb_ptr->filename[0], fcb_ptr->filename[1], fcb_ptr->filename[2], fcb_ptr->filename[3], fcb_ptr->filename[4], fcb_ptr->filename[5], fcb_ptr->filename[6], fcb_ptr->filename[7]); 85 | cprintf("\ttype ->\t%c%c%c\n", fcb_ptr->filetype[0], fcb_ptr->filetype[1], fcb_ptr->filetype[2]); 86 | cprintf("\t ex ->\t%02X\n",fcb_ptr->ex); 87 | cprintf("\tresv ->\t%04X\n",fcb_ptr->resv); 88 | cprintf("\t rc ->\t%02X\n",fcb_ptr->rc); 89 | cprintf("\talb0 ->\t%04X %04X %04X %04X\n", fcb_ptr->alb[0], fcb_ptr->alb[1], fcb_ptr->alb[2], fcb_ptr->alb[3]); 90 | cprintf("\talb1 ->\t%04X %04X %04X %04X\n", fcb_ptr->alb[4], fcb_ptr->alb[5], fcb_ptr->alb[6], fcb_ptr->alb[7]); 91 | cprintf("\tsreq ->\t%02X\n",fcb_ptr->seqreq); 92 | cprintf("\trrec ->\t%04X\n",fcb_ptr->rrec); 93 | cprintf("\trreo ->\t%02X\n\n",fcb_ptr->rrecob); 94 | } 95 | 96 | void print_dir(CPM_DIR *dir_ptr) { 97 | cprintf("\tname ->\t%c%c%c%c%c%c%c%c\n", dir_ptr->filename[0], dir_ptr->filename[1], dir_ptr->filename[2], dir_ptr->filename[3], dir_ptr->filename[4], dir_ptr->filename[5], dir_ptr->filename[6], dir_ptr->filename[7]); 98 | cprintf("\ttype ->\t%c%c%c\n\n", dir_ptr->filetype[0], dir_ptr->filetype[1], dir_ptr->filetype[2]); 99 | } 100 | 101 | void print_buf(void) { 102 | uint8_t idx; 103 | for (idx = 0; idx < BUF_SIZE; idx++) { 104 | if(!(idx%16)) cprintf("\n%04X --- ", idx/16); 105 | cprintf("%02X ", dma_buffer[idx]); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /esrc/fileop/fileop.mk: -------------------------------------------------------------------------------- 1 | fileop-empty: $(BIN_DIR)/fileop.com 2 | 3 | $(BIN_DIR)/fileop.com: tools $(BIN_DIR)/fileop.ihx 4 | $(LBIN_DIR)/load $(BIN_DIR)/fileop 5 | 6 | $(BIN_DIR)/fileop.ihx: libraries $(BIN_DIR)/fileop.rel $(BIN_DIR)/fileop.arf 7 | $(CLD) $(CLD_FLAGS) -nf $(BIN_DIR)/fileop.arf 8 | 9 | $(BIN_DIR)/fileop.rel: $(ESRC_DIR)/fileop/fileop.c 10 | $(CCC) $(CCC_FLAGS) -o $(BIN_DIR) $(ESRC_DIR)/fileop/fileop.c 11 | 12 | $(BIN_DIR)/fileop.arf: $(BIN_DIR)/generic.arf 13 | $(QUIET)$(SED) 's/$(REPLACE_TAG)/fileop/' $(BIN_DIR)/generic.arf > $(BIN_DIR)/fileop.arf 14 | -------------------------------------------------------------------------------- /esrc/modprnex01/modprnex01.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "cpmbdos.h" 6 | #include "cprintf.h" 7 | #include "syslib/cpm_sysfunc.h" 8 | #include "syslib/ansi_term.h" 9 | 10 | #include "hw/common/hw_common.h" 11 | #include "hw/modprn02/hw_modprn02.h" 12 | 13 | void sys_init(void) { 14 | cpm_sysfunc_init(); 15 | 16 | cprintf("Init...\r\n"); 17 | 18 | hw_setupInterrupts(); 19 | 20 | setup_modprn(Channel_A, BRate_38400, bpc_8, stop_1, parity_none); 21 | setup_modprn(Channel_B, BRate_38400, bpc_8, stop_1, parity_none); 22 | modprn_setupInterrupts(0x00); 23 | 24 | hw_enableInterrupts(); 25 | } 26 | 27 | int main() { 28 | uint16_t idx = 0x0FFF; 29 | uint8_t ch; 30 | 31 | sys_init(); 32 | 33 | /* 34 | while(idx--) { 35 | ch = modprn_getch(Channel_A); 36 | modprn_outch(Channel_A, ch); 37 | } 38 | */ 39 | 40 | 41 | while(idx--) { 42 | modprn_outch(Channel_A, modprn_int_getch(Channel_A)); 43 | } 44 | 45 | return (EXIT_SUCCESS); 46 | } 47 | -------------------------------------------------------------------------------- /esrc/modprnex01/modprnex01.mk: -------------------------------------------------------------------------------- 1 | example-modprnex01: $(BIN_DIR)/modprnex01.com 2 | 3 | $(BIN_DIR)/modprnex01.com: tools $(BIN_DIR)/modprnex01.ihx 4 | $(LBIN_DIR)/load $(BIN_DIR)/modprnex01 5 | 6 | $(BIN_DIR)/modprnex01.ihx: libraries $(BIN_DIR)/modprnex01.rel $(BIN_DIR)/modprnex01.arf 7 | $(CLD) $(CLD_FLAGS) -nf $(BIN_DIR)/modprnex01.arf 8 | 9 | $(BIN_DIR)/modprnex01.rel: $(ESRC_DIR)/modprnex01/modprnex01.c 10 | $(CCC) $(CCC_FLAGS) -o $(BIN_DIR) $(ESRC_DIR)/modprnex01/modprnex01.c 11 | 12 | $(BIN_DIR)/modprnex01.arf: $(BIN_DIR)/generic.arf 13 | $(QUIET)$(SED) 's/$(REPLACE_TAG)/modprnex01/' $(BIN_DIR)/generic.arf > $(BIN_DIR)/modprnex01.arf 14 | -------------------------------------------------------------------------------- /examples.mk: -------------------------------------------------------------------------------- 1 | REPLACE_TAG=REPLACE_ME_PLEASE 2 | 3 | examples: $(BIN_DIR)/generic.arf 4 | 5 | examples-clean: 6 | rm -f $(BIN_DIR)/*.com 7 | rm -f $(BIN_DIR)/*.ihx 8 | rm -f $(BIN_DIR)/*.arf 9 | 10 | $(BIN_DIR)/generic.arf: 11 | $(QUIET)$(ECHO) Generating generic.arf 12 | $(QUIET)$(ECHO) -mjx > $(BIN_DIR)/generic.arf 13 | $(QUIET)$(ECHO) -i $(BIN_DIR)/$(REPLACE_TAG).ihx >> $(BIN_DIR)/generic.arf 14 | $(QUIET)$(ECHO) -k $(COMPILER_LIBS) >> $(BIN_DIR)/generic.arf 15 | $(QUIET)$(ECHO) -l z80 >> $(BIN_DIR)/generic.arf 16 | $(QUIET)$(ECHO) $(BIN_DIR)/cpm0.rel >> $(BIN_DIR)/generic.arf 17 | $(QUIET)$(ECHO) $(BIN_DIR)/cpmbdos.rel >> $(BIN_DIR)/generic.arf 18 | $(QUIET)$(ECHO) $(BIN_DIR)/cprintf.rel >> $(BIN_DIR)/generic.arf 19 | $(QUIET)$(ECHO) $(BIN_DIR)/cpm_sysfunc.rel >> $(BIN_DIR)/generic.arf 20 | $(QUIET)$(ECHO) $(BIN_DIR)/ansi_term.rel >> $(BIN_DIR)/generic.arf 21 | $(QUIET)$(ECHO) $(BIN_DIR)/hw_common.rel >> $(BIN_DIR)/generic.arf 22 | $(QUIET)$(ECHO) $(BIN_DIR)/hw_modprn02.rel >> $(BIN_DIR)/generic.arf 23 | $(QUIET)$(ECHO) $(BIN_DIR)/$(REPLACE_TAG).rel >> $(BIN_DIR)/generic.arf 24 | $(QUIET)$(ECHO) -e >> $(BIN_DIR)/generic.arf 25 | -------------------------------------------------------------------------------- /lbin/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzlab/sdcc-cpm-example/169e48842a7bf96d3baeb16a06e9ef1aafb5c271/lbin/.empty -------------------------------------------------------------------------------- /libraries.mk: -------------------------------------------------------------------------------- 1 | # Here begins the actual creation of destination files 2 | libraries: $(BIN_DIR)/cprintf.rel $(BIN_DIR)/cpm0.rel $(BIN_DIR)/cpmbdos.rel $(BIN_DIR)/ansi_term.rel $(BIN_DIR)/cpm_sysfunc.rel \ 3 | $(BIN_DIR)/hw_common.rel $(BIN_DIR)/hw_modprn02.rel 4 | 5 | libraries-clean: 6 | rm -f $(BIN_DIR)/*.rel 7 | 8 | $(BIN_DIR)/cprintf.rel: $(SYSLIB_SRC_DIR)/cprintf.c 9 | $(CCC) $(CCC_FLAGS) -o $(BIN_DIR) $(SYSLIB_SRC_DIR)/cprintf.c 10 | 11 | $(BIN_DIR)/cpm_sysfunc.rel: $(SYSLIB_SRC_DIR)/cpm_sysfunc.c 12 | $(CCC) $(CCC_FLAGS) -o $(BIN_DIR) $(SYSLIB_SRC_DIR)/cpm_sysfunc.c 13 | 14 | $(BIN_DIR)/ansi_term.rel: $(SYSLIB_SRC_DIR)/ansi_term.c 15 | $(CCC) $(CCC_FLAGS) -o $(BIN_DIR) $(SYSLIB_SRC_DIR)/ansi_term.c 16 | 17 | $(BIN_DIR)/cpmbdos.rel: $(SRC_DIR)/cpm/cpmbdos.c 18 | $(CCC) $(CCC_FLAGS) -o $(BIN_DIR) $(SRC_DIR)/cpm/cpmbdos.c 19 | 20 | $(BIN_DIR)/hw_modprn02.rel: $(HWLIB_SRC_DIR)/modprn02/hw_modprn02.c 21 | $(CCC) $(CCC_FLAGS) -o $(BIN_DIR) $(HWLIB_SRC_DIR)/modprn02/hw_modprn02.c 22 | 23 | $(BIN_DIR)/hw_common.rel: $(HWLIB_SRC_DIR)/common/hw_common.c 24 | $(CCC) $(CCC_FLAGS) -o $(BIN_DIR) $(HWLIB_SRC_DIR)/common/hw_common.c 25 | 26 | # Build CP/M-80 Command File Structure files 27 | $(BIN_DIR)/cpm0.rel: $(CPM_SRC_DIR)/cpm0.rel 28 | $(CAS) $(CAS_FLAGS) $(BIN_DIR)/cpm0.o $(CPM_SRC_DIR)/cpm0.s 29 | $(QUIET)$(COPY) $(CPM_SRC_DIR)/cpm0.rel $(BIN_DIR) 30 | $(QUIET)$(COPY) $(CPM_SRC_DIR)/cpm0.lst $(BIN_DIR) 31 | -------------------------------------------------------------------------------- /lsrc/binpak.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void print_usage(const char *p_name); 8 | 9 | int main(int argc, char *argv[]) { 10 | int opt, user_no = 0; 11 | unsigned int checksum = 0; 12 | long f_size = 0; 13 | FILE *in_file; 14 | 15 | unsigned char r_data; 16 | 17 | opterr = 0; 18 | while ((opt = getopt(argc, argv, "u:")) != -1) { 19 | switch(opt) { 20 | case 'u': 21 | user_no = atoi(optarg); 22 | break; 23 | default: 24 | print_usage(argv[0]); 25 | break; 26 | } 27 | } 28 | 29 | if (optind >= argc) { 30 | print_usage(argv[0]); 31 | exit(EXIT_FAILURE); 32 | } 33 | 34 | in_file = fopen(argv[optind], "rb"); 35 | if (!in_file) { 36 | perror(argv[optind]); 37 | exit(EXIT_FAILURE); 38 | } 39 | 40 | // Get size 41 | fseek(in_file, 0L, SEEK_END); 42 | f_size = ftell(in_file); 43 | fseek(in_file, 0L, SEEK_SET); 44 | 45 | // The checksum is two pairs of HEX values: 46 | // The first one is the low-byte of the length of the file being uploaded. 47 | // Because CP/M files are normally saved in blocks of 128, this is normally 48 | // "80" or "00", but doesn't need to be. 49 | // The second one is the low-byte of the SUM of each byte being uploaded. 50 | // The checksum is very simple, but more than adequate for confirming a simple 51 | // data transfer, as it can detect missing, extra or changed characters. 52 | 53 | fprintf(stdout, "A:DOWNLOAD %s\r\nU%u\r\n:", basename(argv[optind]), user_no); 54 | while (fread(&r_data, 1, 1, in_file) == 1) { 55 | fprintf(stdout, "%.02X", r_data); 56 | checksum += r_data; 57 | } 58 | fprintf(stdout, ">%.04X\r\n", (unsigned short)((checksum & 0xFF) | ((f_size & 0xFF) << 8))); 59 | 60 | 61 | fclose(in_file); 62 | 63 | 64 | return 0; 65 | } 66 | 67 | void print_usage(const char *p_name) { 68 | fprintf(stderr, "Usage: %s [-u user_no] file\n", p_name); 69 | } 70 | -------------------------------------------------------------------------------- /lsrc/load.c: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------- 2 | // load.c 21-May-11 Running on Mac OS X 10.6.6 3 | // S/n 2011-1042-654321 Written by Douglas W. Goodall 4 | // Copyright(c)2011 Douglas W. Goodall, United States. 5 | // --------------------------------------------------- 6 | // This file is part of Vintage Modern Assembler Plus Tools. 7 | // 8 | // VMAPT is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // VMAPT is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with VMAPT. If not, see . 20 | // 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #define DATA_RECORD 0x00 27 | #define EOF_RECORD 0x01 28 | 29 | int main(int argc,char **argv) 30 | { 31 | FILE * fcom, * fhex; 32 | char g_szBuffer2[128]; 33 | char szByteBuffer[2+1]; 34 | char *p; 35 | int iTemp; 36 | 37 | char cColon; 38 | 39 | char szLength[2+1]; 40 | int iLength; 41 | 42 | char szAddress[4+1]; 43 | unsigned int uiAddress; 44 | 45 | char szRecordType[2+1]; 46 | unsigned char ucRecordType; 47 | 48 | char szData[80]; 49 | unsigned char ucBinBuffer[32+1]; 50 | 51 | char szChecksum[2+1]; 52 | unsigned char ucChecksum; 53 | 54 | unsigned int uiLastByte = 0; 55 | 56 | char szComFile[255]; 57 | char szHexFile[255]; 58 | int i; 59 | 60 | unsigned char memory[0xfff0]; 61 | 62 | if(1 == argc) { 63 | printf("usage - load \n"); 64 | exit(EXIT_FAILURE); 65 | } 66 | 67 | strcpy(szHexFile,argv[1]); 68 | strcpy(szComFile,argv[1]); 69 | 70 | strcat(szComFile,".com"); 71 | //strcat(szHexFile,".hex"); 72 | strcat(szHexFile,".ihx"); 73 | 74 | memset(memory,0,sizeof(memory)); 75 | 76 | fhex = fopen(szHexFile,"r"); 77 | if(NULL == fhex) { 78 | printf("Sorry, cannot open %s for input\n",szHexFile); 79 | exit(EXIT_FAILURE); 80 | } 81 | p = fgets(g_szBuffer2,sizeof(g_szBuffer2),fhex); 82 | while(NULL != p) { 83 | g_szBuffer2[strlen(g_szBuffer2)-1] = 0; 84 | cColon = g_szBuffer2[0]; 85 | 86 | memset(szLength,0,sizeof(szLength)); 87 | memcpy(szLength,&g_szBuffer2[1],2); 88 | sscanf(szLength,"%02X",&iLength); 89 | 90 | memset(szAddress,0,sizeof(szAddress)); 91 | memcpy(szAddress,&g_szBuffer2[3],4); 92 | sscanf(szAddress,"%04X",&uiAddress); 93 | 94 | memset(szRecordType,0,sizeof(szRecordType)); 95 | memcpy(szRecordType,&g_szBuffer2[7],2); 96 | 97 | sscanf(szRecordType,"%02X",&iTemp); 98 | ucRecordType = (unsigned char)iTemp; 99 | 100 | if(0 == ucRecordType) { 101 | memset(szData,0,sizeof(szData)); 102 | memcpy(szData,&g_szBuffer2[9],iLength*2); 103 | for(i=0;ifunc8 in register C 37 | 38 | inc hl ; Inrease the address so we point to first byte of p->parm16 39 | ld e,(hl) ; Read first byte... 40 | inc hl 41 | ld d,(hl) ; Read second byte. We have p->parm16 in DE 42 | 43 | call 5 ; Execute BDOS call! 44 | 45 | ; We now have the return values in BA and HL 46 | push bc 47 | push hl 48 | 49 | ld ix,#0x00 50 | add ix,sp 51 | 52 | ld l,10(ix) 53 | ld h,11(ix) ; Load pointer to ret_ba 54 | ld (hl),b 55 | inc hl 56 | ld (hl),a 57 | 58 | ld l,12(ix) 59 | ld h,13(ix) 60 | 61 | pop bc ; Recover the HL we have pushed 62 | ld (hl),b 63 | inc hl 64 | ld (hl),c 65 | 66 | push bc; Put HL back where it belongs 67 | pop hl; 68 | 69 | pop bc ; Restore BC 70 | pop ix ; Restore IX 71 | ret 72 | __endasm; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/hw/common/hw_common.c: -------------------------------------------------------------------------------- 1 | #include "hw_common.h" 2 | 3 | #define VECTOR_ADDRESS_START 0x7F 4 | 5 | void empty_ISR(void); 6 | 7 | void hw_outp(uint8_t port, uint8_t data) __naked { 8 | port; data; 9 | 10 | __asm 11 | ld hl, #3 12 | add hl, sp 13 | ld a, (hl) // Load data from stack 14 | 15 | ld hl, #2 16 | add hl, sp 17 | 18 | push bc 19 | 20 | ld c, (hl) // Load port from stack 21 | out (c), a // Output to port 22 | 23 | pop bc 24 | 25 | ret 26 | __endasm; 27 | } 28 | 29 | uint8_t hw_inp(uint8_t port) __naked { 30 | port; 31 | 32 | __asm 33 | ld hl, #2 34 | add hl, sp 35 | 36 | push bc 37 | 38 | ld c, (hl) 39 | in l,(c) 40 | 41 | pop bc 42 | 43 | ret 44 | __endasm; 45 | } 46 | 47 | void hw_smallDelay(uint8_t delay) { 48 | while(delay--) { 49 | __asm 50 | nop 51 | __endasm; 52 | } 53 | } 54 | 55 | void hw_setupInterrupts(void) { 56 | uint16_t *vec_table = (uint16_t*)((uint16_t)(((uint16_t)VECTOR_ADDRESS_START) << 8)); 57 | uint8_t idx = 0; 58 | 59 | __asm 60 | di // Disable interrupts 61 | 62 | push af 63 | 64 | ld a,#VECTOR_ADDRESS_START // Load the address 65 | ld I,a 66 | 67 | pop af 68 | __endasm; 69 | 70 | // Fill the table with "empty" handlers... 71 | for(idx = 0; idx < 128; idx++) vec_table[idx] = (uint16_t)empty_ISR; 72 | } 73 | 74 | void hw_enableInterrupts(void) { 75 | __asm 76 | im 2 // Enable interrupt mode 2 77 | ei // Enable interrupts 78 | __endasm; 79 | } 80 | 81 | void hw_addInterruptHandler(uint8_t handNo, uint16_t addr) { 82 | uint16_t *vec_table = (uint16_t*)(((uint16_t)(((uint16_t)VECTOR_ADDRESS_START) << 8)) & 0xFF00); 83 | 84 | vec_table[handNo >> 1] = addr; 85 | } 86 | 87 | void empty_ISR(void) __naked { 88 | __asm 89 | halt 90 | __endasm; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/hw/common/hw_common.h: -------------------------------------------------------------------------------- 1 | #ifndef _HWLIB_COMMON_HEADER_ 2 | #define _HWLIB_COMMON_HEADER_ 3 | 4 | #include "common_datatypes.h" 5 | 6 | void hw_outp(uint8_t port, uint8_t data); 7 | uint8_t hw_inp(uint8_t port) __naked; 8 | 9 | void hw_smallDelay(uint8_t delay); 10 | 11 | void hw_setupInterrupts(void); 12 | void hw_addInterruptHandler(uint8_t handNo, uint16_t addr); 13 | void hw_enableInterrupts(void); 14 | 15 | #endif /* _HWLIB_COMMON_HEADER_ */ 16 | -------------------------------------------------------------------------------- /src/hw/modprn02/hw_modprn02.c: -------------------------------------------------------------------------------- 1 | #include "hw_modprn02.h" 2 | #include "../common/hw_common.h" 3 | 4 | #include "cprintf.h" 5 | 6 | #define SIO_VECT_LOC_MASK 0xF1 7 | 8 | #define SIO_BASIC_CMD_NULL 0x00 9 | #define SIO_BASIC_CMD_ABRT 0x08 10 | #define SIO_BASIC_CMD_RST_EXTINT 0x10 11 | #define SIO_BASIC_CMD_RST_CHN 0x18 12 | #define SIO_BASIC_CMD_RST_RXINT 0x20 13 | #define SIO_BASIC_CMD_RST_TXINT 0x28 14 | #define SIO_BASIC_CMD_RST_ERR 0x30 15 | #define SIO_BASIC_CMD_RET_INT 0x38 16 | 17 | #define SIO_CRC_CMD_NULL 0x00 18 | #define SIO_CRC_CMD_RST_RXCRC_CHK 0x40 19 | #define SIO_CRC_CMD_RST_TXCRC_GEN 0x80 20 | #define SIO_CRC_CMD_RST_CRC 0xC0 21 | 22 | #define SIO_REG0_CTS_FLAG 0x20 23 | #define SIO_REG0_RXAVAIL_FLAG 0x01 24 | #define SIO_REG0_TXEMPTY_FLAG 0x04 25 | #define SIO_REG0_BREAK_FLAG 0x80 26 | 27 | #define SIO_REG5_RTS_FLAG 0x02 28 | #define SIO_REG5_BREAK_FLAG 0x10 29 | 30 | static uint8_t reg5_status[] = {0, 0}; 31 | static uint8_t reg3_status[] = {0, 0}; 32 | 33 | #define SIO_BUF_DEPTH 16 34 | typedef struct { 35 | volatile uint8_t avail; 36 | volatile uint8_t buf[SIO_BUF_DEPTH]; 37 | volatile uint8_t idx; 38 | } sio_buf; 39 | 40 | static sio_buf ch_buf[2]; 41 | 42 | // Initialize the CTC IC 43 | void ctc_init(MPRN_Channel chan, MPRN_BaudRate brate); 44 | void sio_init(MPRN_Channel chan, MPRN_BPC bpc, MPRN_Stop sbit, MPRN_Parity parity); 45 | 46 | // Interrupt handlers 47 | // Channel A 48 | void chA_intHandler_rx_specialCond(void) __naked; 49 | void chA_intHandler_rx_charAvail(void) __interrupt; 50 | //void chA_intHandler_tx_bufEmpty(void) __interrupt; 51 | void chA_intHandler_statChng(void) __naked; 52 | 53 | // Channel A 54 | void chB_intHandler_rx_specialCond(void) __naked; 55 | void chB_intHandler_rx_charAvail(void) __interrupt; 56 | //void chB_intHandler_tx_bufEmpty(void) __naked; 57 | void chB_intHandler_statChng(void) __naked; 58 | 59 | /*********************************/ 60 | 61 | void setup_modprn(MPRN_Channel chan, MPRN_BaudRate brate, MPRN_BPC bpc, MPRN_Stop sbit, MPRN_Parity parity) { 62 | ctc_init(chan, brate); 63 | sio_init(chan, bpc, sbit, parity); 64 | 65 | ch_buf[chan].avail = 0; 66 | } 67 | 68 | void ctc_init(MPRN_Channel chan, MPRN_BaudRate brate) { 69 | uint8_t ctc_command = 0x57; // Enable time constant sending, low-to-high pulse front trigger, counter mode, interrupts disabled. See CTC docs. 70 | 71 | hw_outp(MODPRN02_CTC_CHAN_0 + chan, ctc_command); // Send the channel command 72 | hw_outp(MODPRN02_CTC_CHAN_0 + chan, (uint8_t)brate); // Send the time constant. This will divide our input clock. 73 | } 74 | 75 | void sio_init(MPRN_Channel chan, MPRN_BPC bpc, MPRN_Stop sbit, MPRN_Parity parity) { 76 | // Register 0 77 | hw_outp(MODPRN02_SIO_A_CTRL + chan, SIO_BASIC_CMD_RST_CHN); // Reset the channel 78 | 79 | // Delay a bit, to make sure the channel got reset 80 | __asm 81 | nop 82 | nop 83 | nop 84 | __endasm; 85 | 86 | // Register 4 87 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x04); // Select register 4 88 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x40 |sbit | parity); // Set parity, stop bits and x16 clock mode 89 | 90 | // Register 5 91 | reg5_status[chan] = 0x08 | (bpc >> 1); // Enable Tx, set Tx bits, RTS off 92 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x05); // Select register 5 93 | hw_outp(MODPRN02_SIO_A_CTRL + chan, reg5_status[chan]); 94 | 95 | // Register 1 96 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x01); // Select register 1 97 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x00); // Disable interrupts 98 | 99 | // Register 3 100 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x03); // Select register 3 101 | hw_outp(MODPRN02_SIO_A_CTRL + chan, (0x01|bpc)); // Set rx bits and enable RX 102 | 103 | // Register 5 again, set RTS 104 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x05); // Select register 5 105 | hw_outp(MODPRN02_SIO_A_CTRL + chan, reg5_status[chan] | SIO_REG5_RTS_FLAG); 106 | } 107 | 108 | void modprn_setupInterrupts(uint8_t ivect_start) { 109 | ivect_start &= SIO_VECT_LOC_MASK; 110 | 111 | // Channel B handlers 112 | //hw_addInterruptHandler(ivect_start | 0x00, (uint16_t)chB_intHandler_tx_bufEmpty); 113 | hw_addInterruptHandler(ivect_start | 0x02, (uint16_t)chB_intHandler_statChng); 114 | hw_addInterruptHandler(ivect_start | 0x04, (uint16_t)chB_intHandler_rx_charAvail); 115 | hw_addInterruptHandler(ivect_start | 0x06, (uint16_t)chB_intHandler_rx_specialCond); 116 | 117 | // Channel A handlers 118 | //hw_addInterruptHandler(ivect_start | 0x08, (uint16_t)chA_intHandler_tx_bufEmpty); 119 | hw_addInterruptHandler(ivect_start | 0x0A, (uint16_t)chA_intHandler_statChng); 120 | hw_addInterruptHandler(ivect_start | 0x0C, (uint16_t)chA_intHandler_rx_charAvail); 121 | hw_addInterruptHandler(ivect_start | 0x0E, (uint16_t)chA_intHandler_rx_specialCond); 122 | 123 | hw_outp(MODPRN02_SIO_B_CTRL, 0x02); // Select register 2 124 | hw_outp(MODPRN02_SIO_B_CTRL, ivect_start); // Interrupt vector 0 125 | 126 | // Register 1 for Chan A and B 127 | hw_outp(MODPRN02_SIO_A_CTRL, 0x01); // Select register 1 128 | hw_outp(MODPRN02_SIO_A_CTRL, 0x1C); // Enable interrupts for received chars, TX and status affect vector 129 | 130 | hw_outp(MODPRN02_SIO_B_CTRL, 0x01); // Select register 1 131 | hw_outp(MODPRN02_SIO_B_CTRL, 0x1C); // Enable interrupts for received chars, TX and status affect vector 132 | } 133 | 134 | void modprn_outch(MPRN_Channel chan, uint8_t ch) { 135 | uint8_t reg_0 = 0; 136 | 137 | do { 138 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x00); // Select register 0 139 | reg_0 = hw_inp(MODPRN02_SIO_A_CTRL + chan); 140 | } while(!(reg_0 & SIO_REG0_TXEMPTY_FLAG) || !(reg_0 & SIO_REG0_CTS_FLAG)); 141 | 142 | hw_outp(MODPRN02_SIO_A_DATA + chan, ch); 143 | } 144 | 145 | 146 | uint8_t modprn_getch(MPRN_Channel chan) { 147 | uint8_t reg_0 = 0; 148 | 149 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x00); // Select register 0 150 | reg_0 = hw_inp(MODPRN02_SIO_A_CTRL + chan); 151 | 152 | if (!(reg_0 & SIO_REG0_RXAVAIL_FLAG)) { // If we already have a char waiting, raising the RTS line could cause overrun! 153 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x05); // Select register 5 154 | hw_outp(MODPRN02_SIO_A_CTRL + chan, reg5_status[chan] | SIO_REG5_RTS_FLAG); 155 | 156 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x00); // Select register 0 157 | do { 158 | reg_0 = hw_inp(MODPRN02_SIO_A_CTRL + chan); 159 | } while (!(reg_0 & SIO_REG0_RXAVAIL_FLAG)); 160 | 161 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x05); // Select register 5 162 | hw_outp(MODPRN02_SIO_A_CTRL + chan, reg5_status[chan]); 163 | } 164 | 165 | return hw_inp(MODPRN02_SIO_A_DATA + chan); 166 | } 167 | 168 | uint8_t modprn_getchBuf(MPRN_Channel chan, uint8_t *buf, uint8_t bufSize) { 169 | uint8_t reg_0 = 0; 170 | uint8_t buf_used = 0; 171 | 172 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x00); // Select register 0 173 | reg_0 = hw_inp(MODPRN02_SIO_A_CTRL + chan); 174 | 175 | if (!(reg_0 & SIO_REG0_RXAVAIL_FLAG)) { // If we already have a char waiting, raising the RTS line could cause overrun! 176 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x05); // Select register 5 177 | hw_outp(MODPRN02_SIO_A_CTRL + chan, reg5_status[chan] | SIO_REG5_RTS_FLAG); 178 | 179 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x00); // Select register 0 180 | do { 181 | reg_0 = hw_inp(MODPRN02_SIO_A_CTRL + chan); 182 | } while (!(reg_0 & SIO_REG0_RXAVAIL_FLAG)); 183 | 184 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x05); // Select register 5 185 | hw_outp(MODPRN02_SIO_A_CTRL + chan, reg5_status[chan]); 186 | } 187 | 188 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x00); // Select register 0 189 | do { 190 | buf[buf_used] = hw_inp(MODPRN02_SIO_A_DATA + chan); 191 | buf_used++; 192 | 193 | reg_0 = hw_inp(MODPRN02_SIO_A_CTRL + chan); 194 | } while ((reg_0 & SIO_REG0_RXAVAIL_FLAG) && (buf_used < bufSize)); 195 | 196 | return buf_used; 197 | } 198 | 199 | uint8_t modprn_getBreakStatus(MPRN_Channel chan) { 200 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x00); // Select register 0 201 | return (hw_inp(MODPRN02_SIO_A_CTRL + chan) & SIO_REG0_BREAK_FLAG); 202 | } 203 | 204 | void modprn_sendBreak(MPRN_Channel chan) { 205 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x05); // Select register 5 206 | hw_outp(MODPRN02_SIO_A_CTRL + chan, reg5_status[chan] | SIO_REG5_BREAK_FLAG); // Send the break signal 207 | 208 | __asm 209 | nop 210 | __endasm; 211 | 212 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x05); // Select register 5 213 | hw_outp(MODPRN02_SIO_A_CTRL + chan, reg5_status[chan]); // Disable break signal 214 | } 215 | 216 | uint8_t modprn_int_getch(MPRN_Channel chan) { 217 | uint8_t chbuf; 218 | 219 | while(!ch_buf[chan].avail); 220 | 221 | ch_buf[chan].avail--; 222 | chbuf = ch_buf[chan].buf[ch_buf[chan].idx]; 223 | ch_buf[chan].idx++; 224 | 225 | if (!ch_buf[chan].avail) { 226 | hw_outp(MODPRN02_SIO_A_CTRL + chan, 0x05); // Select register 5 227 | hw_outp(MODPRN02_SIO_A_CTRL + chan, reg5_status[chan] | SIO_REG5_RTS_FLAG); // Raise RTS 228 | } 229 | 230 | return chbuf; 231 | } 232 | 233 | // Interrupt Handlers... 234 | // Channel A 235 | void chA_intHandler_rx_specialCond(void) __naked { 236 | __asm 237 | push af 238 | 239 | ld a,#SIO_BASIC_CMD_RST_ERR 240 | out (#MODPRN02_SIO_A_CTRL),a 241 | 242 | ei 243 | 244 | pop af 245 | 246 | reti 247 | __endasm; 248 | } 249 | 250 | void chA_intHandler_rx_charAvail(void) __interrupt { 251 | uint8_t reg_0; 252 | 253 | hw_outp(MODPRN02_SIO_A_CTRL, 0x05); // Select register 5 254 | hw_outp(MODPRN02_SIO_A_CTRL, reg5_status[Channel_A]); // Lower RTS 255 | 256 | ch_buf[Channel_A].idx = 0; 257 | 258 | hw_outp(MODPRN02_SIO_A_CTRL, 0x00); // Select register 0 259 | do { 260 | ch_buf[Channel_A].buf[ch_buf[Channel_A].avail] = hw_inp(MODPRN02_SIO_A_DATA); 261 | ch_buf[Channel_A].avail++; 262 | 263 | reg_0 = hw_inp(MODPRN02_SIO_A_CTRL); 264 | } while (reg_0 & SIO_REG0_RXAVAIL_FLAG); 265 | 266 | __asm 267 | ei 268 | __endasm; 269 | } 270 | 271 | /* 272 | void chA_intHandler_tx_bufEmpty(void) __interrupt { 273 | __asm 274 | ei 275 | __endasm; 276 | } 277 | */ 278 | 279 | void chA_intHandler_statChng(void) __naked { 280 | __asm 281 | push af 282 | 283 | ld a,#SIO_BASIC_CMD_RST_ERR 284 | out (#MODPRN02_SIO_A_CTRL),a 285 | 286 | ei 287 | 288 | pop af 289 | 290 | reti 291 | __endasm; 292 | } 293 | 294 | // Channel B 295 | void chB_intHandler_rx_specialCond(void) __naked { 296 | __asm 297 | push af 298 | 299 | ld a,#SIO_BASIC_CMD_RST_ERR 300 | out (#MODPRN02_SIO_B_CTRL),a 301 | 302 | ei 303 | 304 | pop af 305 | 306 | reti 307 | __endasm; 308 | } 309 | 310 | void chB_intHandler_rx_charAvail(void) __interrupt { 311 | uint8_t reg_0; 312 | 313 | hw_outp(MODPRN02_SIO_B_CTRL, 0x05); // Select register 5 314 | hw_outp(MODPRN02_SIO_B_CTRL, reg5_status[Channel_B]); // Lower RTS 315 | 316 | ch_buf[Channel_B].idx = 0; 317 | 318 | hw_outp(MODPRN02_SIO_B_CTRL, 0x00); // Select register 0 319 | do { 320 | ch_buf[Channel_B].buf[ch_buf[Channel_B].avail] = hw_inp(MODPRN02_SIO_B_DATA); 321 | ch_buf[Channel_B].avail++; 322 | 323 | reg_0 = hw_inp(MODPRN02_SIO_B_CTRL); 324 | } while (reg_0 & SIO_REG0_RXAVAIL_FLAG); 325 | 326 | __asm 327 | ei 328 | __endasm; 329 | } 330 | 331 | /* 332 | void chB_intHandler_tx_bufEmpty(void) __interrupt { 333 | __asm 334 | ei 335 | __endasm; 336 | } 337 | */ 338 | 339 | void chB_intHandler_statChng(void) __naked { 340 | __asm 341 | push af 342 | 343 | ld a,#SIO_BASIC_CMD_RST_ERR 344 | out (#MODPRN02_SIO_B_CTRL),a 345 | 346 | ei 347 | 348 | pop af 349 | 350 | reti 351 | __endasm; 352 | } 353 | -------------------------------------------------------------------------------- /src/hw/modprn02/hw_modprn02.h: -------------------------------------------------------------------------------- 1 | #ifndef _HWLIB_MODPRN02_HEADER_ 2 | #define _HWLIB_MODPRN02_HEADER_ 3 | 4 | #include "common_datatypes.h" 5 | 6 | #define MODPRN02_BASE_PORT 0x48 7 | 8 | #define MODPRN02_CLOCK (2457600L / 2) // Hz 9 | //#define MODPRN02_CLOCK (2457600L / 4) // Hz 10 | 11 | #define MODPRN02_SIO_MULTIPLIER 16 12 | 13 | // SIO 14 | #define MODPRN02_SIO_A_DATA (MODPRN02_BASE_PORT + 0x00) 15 | #define MODPRN02_SIO_B_DATA (MODPRN02_BASE_PORT + 0x01) 16 | #define MODPRN02_SIO_A_CTRL (MODPRN02_BASE_PORT + 0x02) 17 | #define MODPRN02_SIO_B_CTRL (MODPRN02_BASE_PORT + 0x03) 18 | 19 | // CTC 20 | #define MODPRN02_CTC_CHAN_0 (MODPRN02_BASE_PORT + 0x04 + 0x00) 21 | #define MODPRN02_CTC_CHAN_1 (MODPRN02_BASE_PORT + 0x04 + 0x01) 22 | #define MODPRN02_CTC_CHAN_2 (MODPRN02_BASE_PORT + 0x04 + 0x02) 23 | #define MODPRN02_CTC_CHAN_3 (MODPRN02_BASE_PORT + 0x04 + 0x03) 24 | 25 | typedef enum { 26 | Channel_A = 0, 27 | Channel_B = 1 28 | } MPRN_Channel; 29 | 30 | typedef enum { 31 | BRate_2400 = (MODPRN02_CLOCK / (2400L*MODPRN02_SIO_MULTIPLIER)), 32 | BRate_4800 = (MODPRN02_CLOCK / (4800L*MODPRN02_SIO_MULTIPLIER)), 33 | BRate_9600 = (MODPRN02_CLOCK / (9600L*MODPRN02_SIO_MULTIPLIER)), 34 | BRate_19200 = (MODPRN02_CLOCK / (19200L*MODPRN02_SIO_MULTIPLIER)), 35 | BRate_38400 = (MODPRN02_CLOCK / (38400L*MODPRN02_SIO_MULTIPLIER)) 36 | } MPRN_BaudRate; 37 | 38 | typedef enum { 39 | bpc_5 = 0x00, 40 | bpc_6 = 0x40, 41 | bpc_7 = 0x80, 42 | bpc_8 = 0xC0 43 | } MPRN_BPC; 44 | 45 | typedef enum { 46 | stop_1 = 0x04, 47 | stop_1_5 = 0x08, 48 | stop_2 = 0x0C 49 | } MPRN_Stop; 50 | 51 | typedef enum { 52 | parity_even = 0x03, 53 | parity_odd = 0x01, 54 | parity_none = 0x00 55 | } MPRN_Parity; 56 | 57 | 58 | void setup_modprn(MPRN_Channel chan, MPRN_BaudRate brate, MPRN_BPC bpc, MPRN_Stop sbit, MPRN_Parity parity); 59 | void modprn_setupInterrupts(uint8_t ivect_start); 60 | 61 | void modprn_outch(MPRN_Channel chan, uint8_t ch); 62 | uint8_t modprn_getch(MPRN_Channel chan); 63 | uint8_t modprn_int_getch(MPRN_Channel chan); 64 | uint8_t modprn_getchBuf(MPRN_Channel chan, uint8_t *buf, uint8_t bufSize); 65 | 66 | uint8_t modprn_getBreakStatus(MPRN_Channel chan); 67 | void modprn_sendBreak(MPRN_Channel chan); 68 | 69 | #endif /* _HWLIB_MODPRN02_HEADER_ */ 70 | 71 | -------------------------------------------------------------------------------- /src/include/common_datatypes.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_DATATYPES_HEADER_ 2 | #define _COMMON_DATATYPES_HEADER_ 3 | 4 | typedef unsigned char uint8_t; 5 | typedef unsigned int uint16_t; 6 | 7 | #endif /* _COMMON_DATATYPES_HEADER_ */ 8 | -------------------------------------------------------------------------------- /src/include/cpmbdos.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CP/M-80 v2.2 BDOS Interfaces 3 | * Copyright (C) Douglas W. Goodall 4 | * For Non-Commercial use by N8VEM 5 | * 5/10/2011 dwg - initial version 6 | * hkzlab - current modified version 7 | */ 8 | 9 | #ifndef __CPM_BDOS_INTERFACES__ 10 | #define __CPM_BDOS_INTERFACES__ 11 | 12 | #include "common_datatypes.h" 13 | 14 | #define EXIT_SUCCESS 0 15 | #define EXIT_FAILURE 1 16 | 17 | // BDOS calls 18 | // See here: http://www.seasip.demon.co.uk/Cpm/bdos.html 19 | #define S_RESET 0 20 | #define C_READ 1 21 | #define C_WRITE 2 22 | #define A_READ 3 23 | #define A_WRITE 4 24 | #define L_WRITE 5 25 | #define C_RAWIO 6 26 | #define GETIOBYTE 7 27 | #define SETIOBYTE 8 28 | #define C_WRITESTR 9 29 | #define C_READSTR 10 30 | #define DRV_ALLRESET 13 31 | #define DRV_SET 14 32 | #define F_OPEN 15 33 | #define F_CLOSE 16 34 | #define F_SMATCH 17 35 | #define F_NMATCH 18 36 | #define F_DELETE 19 37 | #define F_READ 20 38 | #define F_WRITE 21 39 | #define F_MAKE 22 40 | #define F_RENAME 23 41 | #define DRV_LOGINVEC 24 42 | #define DRV_GET 25 43 | #define F_DMAOFF 26 44 | #define DRV_ALLOCVEC 27 45 | #define DRV_SETRO 28 46 | #define DRV_ROVEC 29 47 | #define F_ATTRIB 30 48 | #define DRV_DPB 31 49 | #define F_USERNUM 32 50 | #define F_READRAND 33 51 | #define F_WRITERAND 34 52 | #define F_SIZE 35 53 | #define F_RANDREC 36 54 | #define DRV_RESET 37 55 | #define F_WRITEZF 40 56 | 57 | 58 | typedef struct { 59 | uint8_t func8; 60 | uint16_t parm16; 61 | } BDOSCALL; 62 | 63 | 64 | /* 65 | * From http://www.tassos-oak.com/NB2/toolbook.html 66 | * The BDOS is located at some address in high storage; that is, it begins at an address higher than the end of our program. 67 | * We don't know what that address is. It varies from system to system, and it can vary from one command to the next in a single 68 | * system, although it normally doesn't. However, there is always a jump instruction at location 5 (address 0005h) which is 69 | * aimed at the lowest instruction in the BDOS. 70 | * To call upon the BDOS, we need only place the correct arguments in registers and call location 5. 71 | * 72 | * One BDOS argument is a service number that specifies what we want done. It is always passed in register C. 73 | * The other argument depends on the service being requested, but if it's a byte, it is passed in register E, and if a word, in DE. 74 | * The second argument is the byte to be typed; it's passed in register E. 75 | */ 76 | uint8_t cpmbdos(BDOSCALL *p); 77 | 78 | uint8_t cpmbdos_extn(BDOSCALL *p, uint16_t* ret_ba, uint16_t *ret_hl); 79 | 80 | #endif /* __CPM_BDOS_INTERFACES__ */ 81 | 82 | -------------------------------------------------------------------------------- /src/include/cprintf.h: -------------------------------------------------------------------------------- 1 | /* cprintf.h */ 2 | 3 | int cprintf(const char *fmt, ...); 4 | 5 | #define printf cprintf 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/syslib/ansi_term.c: -------------------------------------------------------------------------------- 1 | #include "ansi_term.h" 2 | 3 | #include 4 | 5 | #include "cpm_sysfunc.h" 6 | 7 | 8 | #define ESC_CHR 0x1B 9 | #define VT100_ANSI_CMD "<" // Sets VT100 and compatibles to ANSI mode 10 | 11 | #define ANSI_CLRSCRN "[_J" // Erase screen 12 | #define ANSI_CLRLINE "[_K" // Erase line 13 | #define ANSI_CURDIRADR "[___;___H" // Direct address of cursor position Y X 14 | #define ANSI_CURMOVE "[___X" // Move cursor up/down/left/right X spots 15 | #define ANSI_SAVECUR "7" // Save cursor and attributes 16 | #define ANSI_RESTCUR "8" // Restore cursor and attributes 17 | #define ANSI_IDX "D" // Index 18 | #define ANSI_REVIDX "M" // Reverse index 19 | #define ANSI_LM "#_" 20 | 21 | #define ANSI_TREQ "[c" // What are you 22 | 23 | void term_sendCommand(char *cmd); 24 | 25 | void term_ANSIMode(void) { 26 | term_sendCommand(VT100_ANSI_CMD); 27 | } 28 | 29 | void term_ANSILineMode(LineMode lm) { 30 | char cmd[] = ANSI_LM; 31 | 32 | switch (lm) { 33 | case lm_doubleh_top: 34 | cmd[1] = '3'; 35 | break; 36 | case lm_doubleh_bottom: 37 | cmd[1] = '4'; 38 | break; 39 | case lm_singlew_singleh: 40 | cmd[1] = '5'; 41 | break; 42 | case lm_doublew_singleh: 43 | default: 44 | cmd[1] = '6'; 45 | break; 46 | } 47 | 48 | term_sendCommand(cmd); 49 | } 50 | 51 | void term_ANSIDirectCursorAddr(uint8_t column, uint8_t line) { 52 | char cmd[] = ANSI_CURDIRADR; 53 | 54 | // Generate the proper ascii numbers for the command 55 | cmd[5] = (column / 100) + 0x30; 56 | column -= (100 * (column / 100)); 57 | cmd[6] = (column / 10) + 0x30; 58 | column -= (10 * (column / 10)); 59 | cmd[7] = column + 0x30; 60 | 61 | cmd[1] = (line / 100) + 0x30; 62 | line -= (100 * (line / 100)); 63 | cmd[2] = (line / 10) + 0x30; 64 | line -= (10 * (line / 10)); 65 | cmd[3] = line + 0x30; 66 | 67 | term_sendCommand(cmd); 68 | } 69 | 70 | void term_ANSICursorMove(uint8_t spaces, ModeDir dir) { 71 | char cmd[] = ANSI_CURMOVE; 72 | 73 | switch (dir) { 74 | case md_move_up: 75 | cmd[4] = 'A'; 76 | break; 77 | case md_move_down: 78 | cmd[4] = 'B'; 79 | break; 80 | case md_move_right: 81 | cmd[4] = 'C'; 82 | break; 83 | case md_move_left: 84 | default: 85 | cmd[4] = 'D'; 86 | break; 87 | } 88 | 89 | // Convert the spaces value into ASCII 90 | cmd[1] = (spaces / 100) + 0x30; 91 | spaces -= (100 * (spaces / 100)); 92 | cmd[2] = (spaces / 10) + 0x30; 93 | spaces -= (10 * (spaces / 10)); 94 | cmd[3] = spaces + 0x30; 95 | 96 | term_sendCommand(cmd); 97 | } 98 | 99 | void term_ANSIClrLine(EraseDir dir) { 100 | char cmd[] = ANSI_CLRLINE; 101 | 102 | switch(dir) { 103 | case ed_erase_after: 104 | cmd[1] = '0'; 105 | break; 106 | case ed_erase_before: 107 | cmd[1] = '1'; 108 | break; 109 | case ed_erase_all: 110 | default: 111 | cmd[1] = '2'; 112 | break; 113 | } 114 | 115 | term_sendCommand(cmd); 116 | } 117 | 118 | void term_ANSIClrScrn(EraseDir dir) { 119 | char cmd[] = ANSI_CLRSCRN; 120 | 121 | switch(dir) { 122 | case ed_erase_after: 123 | case ed_erase_before: 124 | cmd[1] = '0'; 125 | break; 126 | case ed_erase_all: 127 | default: 128 | cmd[1] = '2'; 129 | break; 130 | } 131 | 132 | term_sendCommand(cmd); 133 | } 134 | 135 | void term_ANSISetParam(uint8_t prm) { 136 | char cmd[] = "[_;_;_;_;_m"; 137 | int idx = 1; 138 | 139 | if (ANSI_P_CHK_AOFF(prm)) { 140 | cmd[idx] = '0'; 141 | idx += 2; 142 | } 143 | 144 | if (ANSI_P_CHK_BOLD(prm)) { 145 | cmd[idx] = '1'; 146 | idx += 2; 147 | } 148 | 149 | if (ANSI_P_CHK_UNDR(prm)) { 150 | cmd[idx] = '4'; 151 | idx += 2; 152 | } 153 | 154 | if (ANSI_P_CHK_BLNK(prm)) { 155 | cmd[idx] = '5'; 156 | idx += 2; 157 | } 158 | 159 | if (ANSI_P_CHK_REVR(prm)) { 160 | cmd[idx] = '7'; 161 | idx += 2; 162 | } 163 | 164 | cmd[idx - 1] = 'm'; 165 | cmd[idx] = '\0'; 166 | 167 | term_sendCommand(cmd); 168 | } 169 | 170 | void term_ANSISaveCursor(void) { 171 | term_sendCommand(ANSI_SAVECUR); 172 | } 173 | 174 | void term_ANSIRestoreCursor(void) { 175 | term_sendCommand(ANSI_RESTCUR); 176 | } 177 | 178 | void term_ANSIIndex(void) { 179 | term_sendCommand(ANSI_IDX); 180 | } 181 | 182 | void term_ANSIReverseIndex(void) { 183 | term_sendCommand(ANSI_REVIDX); 184 | } 185 | 186 | void term_sendCommand(char *cmd) { 187 | int idx = 0; 188 | 189 | cpm_putchar(ESC_CHR); 190 | while (cmd[idx] != '\0') { 191 | cpm_putchar(cmd[idx]); 192 | idx++; 193 | } 194 | } 195 | 196 | 197 | -------------------------------------------------------------------------------- /src/syslib/ansi_term.h: -------------------------------------------------------------------------------- 1 | #ifndef _ANSI_TERM_HEADER_ 2 | #define _ANSI_TERM_HEADER_ 3 | 4 | #include "common_datatypes.h" 5 | 6 | #define ASCII_SHIFTIN 0x0F 7 | #define ASCII_SHIFTOUT 0x0E 8 | 9 | #define ANSI_P_SET_AOFF(a) (a | 0x01) 10 | #define ANSI_P_SET_BOLD(a) (a | 0x02) 11 | #define ANSI_P_SET_UNDR(a) (a | 0x04) 12 | #define ANSI_P_SET_BLNK(a) (a | 0x08) 13 | #define ANSI_P_SET_REVR(a) (a | 0x10) 14 | 15 | #define ANSI_P_CHK_AOFF(a) (a & 0x01) 16 | #define ANSI_P_CHK_BOLD(a) (a & 0x02) 17 | #define ANSI_P_CHK_UNDR(a) (a & 0x04) 18 | #define ANSI_P_CHK_BLNK(a) (a & 0x08) 19 | #define ANSI_P_CHK_REVR(a) (a & 0x10) 20 | 21 | typedef enum { 22 | ed_erase_before = 0, 23 | ed_erase_after = 1, 24 | ed_erase_all = 2 25 | } EraseDir; 26 | 27 | typedef enum { 28 | md_move_up = 0, 29 | md_move_down = 1, 30 | md_move_left = 2, 31 | md_move_right = 3 32 | } ModeDir; 33 | 34 | typedef enum { 35 | lm_doubleh_top = 0, 36 | lm_doubleh_bottom = 1, 37 | lm_singlew_singleh = 2, 38 | lm_doublew_singleh = 3 39 | } LineMode; 40 | 41 | void term_ANSIMode(void); 42 | 43 | void term_ANSIClrScrn(EraseDir dir); 44 | void term_ANSIClrLine(EraseDir dir); 45 | void term_ANSIDirectCursorAddr(uint8_t column, uint8_t line); 46 | void term_ANSICursorMove(uint8_t spaces, ModeDir dir); 47 | void term_ANSISaveCursor(void); 48 | void term_ANSIRestoreCursor(void); 49 | void term_ANSIIndex(void); 50 | void term_ANSIReverseIndex(void); 51 | void term_ANSILineMode(LineMode lm); 52 | 53 | // prm format: 54 | // BIT 0 -> all off 55 | // BIT 1 -> bold on 56 | // BIT 2 -> underscore on 57 | // BIT 3 -> blink on 58 | // BIT 4 -> reverse on 59 | void term_ANSISetParam(uint8_t prm); 60 | 61 | #endif /* _ANSI_TERM_HEADER_ */ 62 | -------------------------------------------------------------------------------- /src/syslib/cpm_sysfunc.c: -------------------------------------------------------------------------------- 1 | #include "cpm_sysfunc.h" 2 | 3 | #include "common_datatypes.h" 4 | #include "cpmbdos.h" 5 | 6 | #include 7 | #include 8 | 9 | typedef struct { 10 | uint8_t size; 11 | uint8_t len; 12 | char bytes[80]; 13 | } RS_BUFFER; 14 | 15 | static BDOSCALL bdos_readstr; 16 | static RS_BUFFER rs_buf; 17 | 18 | static uint16_t ret_ba, ret_hl; 19 | 20 | void cpm_sysfunc_init(void) { 21 | // Initialize READSTR BDOS call 22 | bdos_readstr.func8 = C_READSTR; 23 | bdos_readstr.parm16 = (uint16_t)&rs_buf; 24 | } 25 | 26 | char *cpm_gets(char *p) { 27 | memset(rs_buf.bytes, 0, sizeof(rs_buf.bytes)); 28 | rs_buf.size = sizeof(rs_buf.bytes); 29 | rs_buf.len = 0; 30 | 31 | cpmbdos_extn(&bdos_readstr, &ret_ba, &ret_hl); 32 | 33 | rs_buf.bytes[rs_buf.len] = '\n'; 34 | strcpy(p, rs_buf.bytes); 35 | 36 | return p; 37 | } 38 | 39 | char cpm_getchar(void) { 40 | BDOSCALL cread = { C_READ, { (uint16_t)0 } }; 41 | return cpmbdos_extn(&cread, &ret_ba, &ret_hl); 42 | } 43 | 44 | void cpm_putchar(char c) { 45 | BDOSCALL cwrite = { C_WRITE, { (uint16_t)c } }; 46 | cpmbdos_extn(&cwrite, &ret_ba, &ret_hl); 47 | } 48 | 49 | void cpm_setDMAAddr(uint16_t addr) { 50 | BDOSCALL fdma = { F_DMAOFF, {addr} }; 51 | 52 | cpmbdos_extn(&fdma, &ret_ba, &ret_hl); 53 | } 54 | 55 | uint8_t cpm_getCurDrive(void) { 56 | BDOSCALL drv = { DRV_GET, { 0 } }; 57 | 58 | return cpmbdos_extn(&drv, &ret_ba, &ret_hl); 59 | } 60 | 61 | uint8_t cpm_resetDrives(void) { 62 | BDOSCALL drv = { DRV_ALLRESET, { 0 } }; 63 | 64 | return cpmbdos_extn(&drv, &ret_ba, &ret_hl); 65 | } 66 | 67 | uint8_t cpm_setCurDrive(uint8_t drive) { 68 | BDOSCALL drv = { DRV_SET, { drive } }; 69 | 70 | return cpmbdos_extn(&drv, &ret_ba, &ret_hl); 71 | } 72 | 73 | void cpm_setFCBname(char *fname, char *ftype, FCB *cb) { 74 | int idx; 75 | char c; 76 | 77 | for (idx = 0; (idx < 8) && (fname[idx] != '\0'); idx++) { 78 | c = fname[idx] & 0x7F; 79 | if (c >= 0x61 && c <= 0x7a) 80 | c -= 0x20; 81 | 82 | cb->filename[idx] = c; 83 | } 84 | 85 | while (idx < 8) { 86 | cb->filename[idx] = ' '; // Pad the filename 87 | idx++; 88 | } 89 | 90 | for (idx = 0; (idx < 3) && (ftype[idx] != '\0'); idx++) { 91 | c = ftype[idx] & 0x7F; 92 | if (c >= 0x61 && c <= 0x7a) 93 | c -= 0x20; 94 | 95 | cb->filetype[idx] = c; 96 | } 97 | 98 | while (idx < 3) { 99 | cb->filetype[idx] = ' '; // Pad the filetype 100 | idx++; 101 | } 102 | } 103 | 104 | uint8_t cpm_performFileOp(FileOperation fop, FCB *cb) { 105 | BDOSCALL call = { 0, {(uint16_t)cb} }; 106 | 107 | switch (fop) { 108 | case fop_open: 109 | call.func8 = F_OPEN; 110 | break; 111 | case fop_close: 112 | call.func8 = F_CLOSE; 113 | break; 114 | case fop_firstNameMatch: 115 | call.func8 = F_SMATCH; 116 | break; 117 | case fop_nextMatch: 118 | call.func8 = F_NMATCH; 119 | break; 120 | case fop_makeFile: 121 | call.func8 = F_MAKE; 122 | break; 123 | case fop_delFile: 124 | call.func8 = F_DELETE; 125 | break; 126 | case fop_setFileAttr: 127 | call.func8 = F_ATTRIB; 128 | break; 129 | case fop_readSeqRecord: 130 | call.func8 = F_READ; 131 | break; 132 | case fop_writeSeqRecord: 133 | call.func8 = F_WRITE; 134 | break; 135 | case fop_readRandRecord: 136 | call.func8 = F_READRAND; 137 | break; 138 | case fop_writeRandRecord: 139 | call.func8 = F_WRITERAND; 140 | break; 141 | case fop_updRandRecPtr: 142 | call.func8 = F_RANDREC; 143 | break; 144 | case fop_calcFileSize: 145 | default: 146 | call.func8 = F_SIZE; 147 | break; 148 | } 149 | 150 | return cpmbdos_extn(&call, &ret_ba, &ret_hl); 151 | //return cpmbdos(&call); 152 | } 153 | 154 | void cpm_reset(void) { 155 | BDOSCALL sreset = { S_RESET, { (uint16_t)0 } }; 156 | cpmbdos_extn(&sreset, &ret_ba, &ret_hl); 157 | } 158 | -------------------------------------------------------------------------------- /src/syslib/cpm_sysfunc.h: -------------------------------------------------------------------------------- 1 | #ifndef _CPM_SYSFUNC_HEADER_ 2 | #define _CPM_SYSFUNC_HEADER_ 3 | 4 | #include "common_datatypes.h" 5 | 6 | typedef struct { 7 | uint8_t drive; // 0 -> Searches in default disk drive 8 | char filename[8]; // File name (when opening a '?' means 'any character') 9 | char filetype[3]; // File type 10 | uint8_t ex; // Extent 11 | uint16_t resv; // Reserved for CP/M 12 | uint8_t rc; // Records used in extent 13 | uint8_t alb[16]; // Allocation blocks used 14 | uint8_t seqreq; // Sequential records to read/write 15 | uint16_t rrec; // Random record to read/write 16 | uint8_t rrecob; // Random record overflow byte (MS) 17 | } FCB; /* File Control Block */ 18 | 19 | typedef struct { 20 | uint8_t status; 21 | char filename[8]; 22 | char filetype[3]; 23 | uint8_t xl; 24 | uint8_t bc; 25 | uint8_t xh; 26 | uint8_t rc; 27 | uint8_t alb[16]; 28 | } CPM_DIR; 29 | 30 | typedef enum { 31 | fop_open, 32 | fop_close, 33 | fop_firstNameMatch, 34 | fop_nextMatch, 35 | fop_makeFile, 36 | fop_delFile, 37 | fop_setFileAttr, 38 | fop_readSeqRecord, 39 | fop_writeSeqRecord, 40 | fop_readRandRecord, 41 | fop_writeRandRecord, 42 | fop_updRandRecPtr, 43 | fop_calcFileSize 44 | } FileOperation; 45 | 46 | void cpm_sysfunc_init(void); 47 | 48 | void cpm_reset(void); 49 | 50 | char *cpm_gets(char *p); 51 | char cpm_getchar(void); 52 | void cpm_putchar(char c); 53 | 54 | uint8_t cpm_performFileOp(FileOperation fop, FCB *cb); 55 | 56 | uint8_t cpm_deleteFile(FCB *cb); 57 | uint8_t cpm_setFileAttribs(FCB *cb); 58 | 59 | void cpm_setDMAAddr(uint16_t addr); 60 | 61 | uint8_t cpm_getCurDrive(void); 62 | uint8_t cpm_setCurDrive(uint8_t drive); 63 | uint8_t cpm_resetDrives(void); 64 | 65 | void cpm_setFCBname(char *fname, char *ftype, FCB *cb); 66 | 67 | #endif /* _CPM_SYSFUNC_HEADER_ */ 68 | -------------------------------------------------------------------------------- /src/syslib/cprintf.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1996 Robert de Bath 2 | * This file is part of the Linux-8086 C library and is distributed 3 | * under the GNU Library General Public License. 4 | */ 5 | 6 | /* Modified 14-Jan-2002 by John Coffman for inclusion 7 | * in the set of LILO diagnostics. This code is the property of Robert 8 | * de Bath, and is used with his permission. 9 | */ 10 | 11 | /* Modified 14-Sep-2010 by John Coffman for use with 12 | * the N8VEM SBC-188 BIOS project. 13 | */ 14 | #include 15 | #include 16 | 17 | #include "cpm_sysfunc.h" 18 | 19 | #undef printf 20 | #define ASM_CVT 0 21 | #ifndef strlen 22 | int strlen(char *s); 23 | #endif 24 | 25 | #define putch(ch) cpm_putchar((char)ch) 26 | 27 | 28 | #ifndef NULL 29 | # define NULL ((void*)0L) 30 | #endif 31 | 32 | #define __fastcall 33 | #define NUMLTH 11 34 | static unsigned char *__fastcall __numout(long i, int base, unsigned char out[]); 35 | 36 | int cprintf(const char *fmt, ...) { 37 | register int c; 38 | int count = 0; 39 | int type, base; 40 | long val; 41 | char *cp; 42 | char padch = ' '; 43 | int minsize, maxsize; 44 | unsigned char out[NUMLTH + 1]; 45 | va_list ap; 46 | 47 | va_start(ap, fmt); 48 | 49 | while (c = *fmt++) { 50 | count++; 51 | if (c != '%') { 52 | if (c == '\n') putch('\r'); 53 | putch(c); 54 | } else { 55 | type = 1; 56 | padch = *fmt; 57 | maxsize = minsize = 0; 58 | if (padch == '-') fmt++; 59 | 60 | for (;;) { 61 | c = *fmt++; 62 | if (c < '0' || c > '9') break; 63 | minsize *= 10; 64 | minsize += c - '0'; 65 | } 66 | 67 | if (c == '.') 68 | for (;;) { 69 | c = *fmt++; 70 | if (c < '0' || c > '9') break; 71 | maxsize *= 10; 72 | maxsize += c - '0'; 73 | } 74 | 75 | if (padch == '-') minsize = -minsize; 76 | else if (padch != '0') padch = ' '; 77 | 78 | if (c == 0) break; 79 | if (c == 'h') { 80 | c = *fmt++; 81 | type = 0; 82 | } else if (c == 'l') { 83 | c = *fmt++; 84 | type = 2; 85 | } 86 | 87 | switch (c) { 88 | case 'X': 89 | case 'x': 90 | base = 16; 91 | type |= 4; 92 | if (0) { 93 | case 'o': 94 | base = 8; 95 | type |= 4; 96 | } 97 | if (0) { 98 | case 'u': 99 | base = 10; 100 | type |= 4; 101 | } 102 | if (0) { 103 | case 'd': 104 | base = -10; 105 | } 106 | switch (type) { 107 | case 0: 108 | val = va_arg(ap, short); 109 | break; 110 | case 1: 111 | val = va_arg(ap, int); 112 | break; 113 | case 2: 114 | val = va_arg(ap, long); 115 | break; 116 | case 4: 117 | val = va_arg(ap, unsigned short); 118 | break; 119 | case 5: 120 | val = va_arg(ap, unsigned int); 121 | break; 122 | case 6: 123 | val = va_arg(ap, unsigned long); 124 | break; 125 | default: 126 | val = 0; 127 | break; 128 | } 129 | cp = __numout(val, base, out); 130 | if (0) { 131 | case 's': 132 | cp = va_arg(ap, char *); 133 | } 134 | count--; 135 | c = strlen(cp); 136 | if (!maxsize) maxsize = c; 137 | if (minsize > 0) { 138 | minsize -= c; 139 | while (minsize > 0) { 140 | putch(padch); 141 | count++; 142 | minsize--; 143 | } 144 | minsize = 0; 145 | } 146 | if (minsize < 0) minsize = -minsize - c; 147 | while (*cp && maxsize-- > 0) { 148 | putch(*cp++); 149 | count++; 150 | } 151 | while (minsize > 0) { 152 | putch(' '); 153 | count++; 154 | minsize--; 155 | } 156 | break; 157 | case 'c': 158 | putch(va_arg(ap, int)); 159 | break; 160 | default: 161 | putch(c); 162 | break; 163 | } 164 | } 165 | } 166 | va_end(ap); 167 | return count; 168 | } 169 | 170 | const char nstring[] = "0123456789ABCDEF"; 171 | 172 | #if ASM_CVT==0 173 | 174 | static unsigned char * 175 | __fastcall 176 | __numout(long i, int base, unsigned char *out) { 177 | int n; 178 | int flg = 0; 179 | unsigned long val; 180 | 181 | if (base < 0) { 182 | base = -base; 183 | if (i < 0) { 184 | flg = 1; 185 | i = -i; 186 | } 187 | } 188 | val = i; 189 | 190 | out[NUMLTH] = '\0'; 191 | n = NUMLTH - 1; 192 | do { 193 | #if 1 194 | out[n] = nstring[val % base]; 195 | val /= base; 196 | --n; 197 | #else 198 | out[n--] = nstring[remLS(val, base)]; 199 | val = divLS(val, base); 200 | #endif 201 | } while (val); 202 | if (flg) out[n--] = '-'; 203 | 204 | return &out[n + 1]; 205 | } 206 | #else 207 | 208 | #asm 209 | ! numout.s 210 | ! 211 | #if 0 212 | .data 213 | _nstring: 214 | .ascii "0123456789ABCDEF" 215 | .byte 0 216 | #endif 217 | 218 | .bss 219 | ___out lcomm $C 220 | 221 | .text 222 | ___numout: 223 | push bp 224 | mov bp, sp 225 | push di 226 | push si 227 | add sp, * -4 228 | mov byte ptr - 8[bp], *$0 ! flg = 0 229 | mov si, 4[bp] ; 230 | i or val.lo 231 | mov di, 6[bp] ; 232 | i or val.hi 233 | mov cx, 8[bp] ; 234 | base 235 | test cx, cx ! base < 0 ? 236 | jge .3num 237 | neg cx ! base = -base 238 | or di, di ! i < 0 ? 239 | jns .5num 240 | mov byte ptr - 8[bp], * 1 ! flg = 1 241 | neg di ! i = -i 242 | neg si 243 | sbb di, * 0 244 | .5num : 245 | .3num : 246 | mov byte ptr [___out + $B], *$0 ! out[11] = nul 247 | mov - 6[bp], *$A ! n = 10 248 | 249 | .9num : 250 | !!! out[n--] = nstring[val % base]; 251 | xor dx, dx 252 | xchg ax, di 253 | div cx 254 | xchg ax, di 255 | xchg ax, si 256 | div cx 257 | xchg ax, si ! val(new) = val / base 258 | 259 | mov bx, dx ! dx = val % base 260 | 261 | mov al, _nstring[bx] 262 | mov bx, -6[bp] 263 | dec word ptr - 6[bp] 264 | mov ___out[bx], al 265 | 266 | mov ax, si 267 | or ax, di ! while (val) 268 | jne .9num 269 | 270 | cmp byte ptr - 8[bp], *$0 ! flg == 0 ? 271 | je .Dnum 272 | 273 | mov bx, -6[bp] 274 | dec word ptr - 6[bp] 275 | mov byte ptr ___out[bx], *$2D ! out[n--] = minus 276 | 277 | .Dnum : 278 | mov ax, -6[bp] 279 | add ax, #___out+1 280 | 281 | add sp, * 4 282 | pop si 283 | pop di 284 | pop bp 285 | ret 286 | #endasm 287 | 288 | #endif 289 | -------------------------------------------------------------------------------- /tools.mk: -------------------------------------------------------------------------------- 1 | tools: $(LBIN_DIR)/load $(LBIN_DIR)/binpak 2 | 3 | LCFLAGS=-O2 4 | 5 | tools-clean: 6 | rm -f $(LBIN_DIR)/* 7 | 8 | $(LBIN_DIR)/load: $(LSRC_DIR)/load.c 9 | $(CC) $(LCFLAGS) $(LSRC_DIR)/load.c -o $(LBIN_DIR)/load 10 | 11 | $(LBIN_DIR)/binpak: $(LSRC_DIR)/binpak.c 12 | $(CC) $(LCFLAGS) $(LSRC_DIR)/binpak.c -o $(LBIN_DIR)/binpak 13 | --------------------------------------------------------------------------------