Central Data DOS

On the website from Amigan Software I found a binary and a bare source listing of CD DOS, a Disk Operating System for the Central Data board (see About the binary and source listing). Just that, no documentation and no manual. So what do you do? You reverse-engineer.

Using the DASMx disassembler, lots of puzzles, conjecture and hours of work I re-created the sources and manual for CD DOS. Based on code similarities between CD DOS and the ALP Assembler Language Package I believe that Central Data were the original authors, but I’m not sure.

CD_DOS.2650 Fully documented source code, with lots of comments to explain how everything works. It works fine with my asm2650 assembler.

CD_DOS.list Assembler output listing. Same as the source, but with the addresses and code.

In september 2024 the sources were updated for version 2 of asm2650. I also fixed a transcription error that made it difficult to enter the number of disk drives on boot.

Note: if you want to use a Disk Operating System with your Central Data-based computer, then consider using P1 DOS instead.


CD DOS uses a fairly sophisticated (for its time) security system. See below for my understanding how all of this works.

I am currently working on correcting and augmenting CD DOS, including some external utilities to format a disk, copy files, modify user accounts etc. This will be P1 DOS, and I will release sources and manuals soon.

CD DOS Manual

Entering commands

Entering text on the command line:

Backspace = erase last character
Ctrl-U = repeat (pre-fill) previous input
Return/Enter = confirm input
Esc = cancel input, return with no text

Some commands require numbers to be entered, e.g. a memory address or a number of blocks. Numbers can be entered either in normal decimal notation, or as hexadecimal. Hexadecimal numbers start with the letter “H”. For example the command “EXEC H2000” starts execution of the program at memory address H2000.

Sometimes a secret word must be entered. For security the characters typed are not echoed to the display and X’s are displayed instead. To obfuscate the number of characters typed a single character may cause 1, 2 or 3 X’s to be shown. When entering a secret word the Backspace key does not erase the last character, but erases the entire input.

Filenames

Filenames consist of three parts: the name, the type and the drive unit. When specifying a filename the type and unit are optional, and will be substituted by the defaults set by the DTYPE and DUNIT commands respectively.

The name can be at most 8 characters long, and must consist of letters and digits only. The type can be at most 4 characters long, and can consist of letters, digits and the dollar-sign. The type follows the name, separated by a period. The drive unit can be a single digit from zero to one less than the number of drive units in the system. The unit follows the name and type, separated by a forward slash.

SDOS, the operating system for the Signetics TWIN, also places the drive name after the filename, separated by a slash. Filenames in SDOS, however, are only 8 characters long and have no type as such.

The types NAME and IMAG have a special meaning in CD DOS, and should not be used. NAME-files are used to store signon-information (see Privilege and protection). IMAG-files are used for images, executable programs that are to be loaded at specific locations in memory (see Images).

Examples:

MYFILE01.BAS/3   full filename
MYFILE01/0       file using default type on drive unit 0
MYFILE01.SAV$    file using default drive unit
MYFILE01         file using default type and default drive unit

Privilege and protection

CD DOS is unique in that is has a sophisticated access and permissions system, consisting of two concepts: multiple users and file restrictions.

Users

CD DOS can handle multiple users. Only one user can be signed on. At startup CD DOS asks for the current user. At any time another user can sign on using the SIGNON command.

A user can be a normal user or a privileged user. Only privileged users can use certain commands (DISKCOPY, DISKRD, DISKWR, and INSPECT). Although normal users can use the LIST command, a privileged user will see more information.

To sign on, the proper password must be typed. After signon, an optional command is run automatically. After that, the command prompt is presented again to the new user. Each user has a NAME-file (a file with type NAME on drive unit 0). NAME-files are short (one block only), and contain the password, last signon date, number of signons and the optional autorun command. See the documentation in the assembler source for a detailed description of NAME-files.

Because of the NAME-files, usernames can be at most 8 characters, and can contain letters and digits only. The change code and inspect code of NAME-files are ignored; it is best to set both to Personal for that user.

Access restrictions

Access to files can be restricted on two levels: for access to the file’s contents, and for modifying the file. The right to modify the file implies the right to access the file. For either level, access can be restricted to:

  • privileged users only (System)
  • a specific named user (Personal)
  • any user who knows the right codeword (Regular)
  • any user (Regular, with a blank codeword).

Only a privileged user can restrict access to the System level. Any user (with rights to modify the file) can restrict a file to Personal or Regular.

To set or modify restrictions for accessing the file you use the ICODE (“inspect code”) command. To set or modify restrictions to change the contents you use the CCODE (“change code”) command.

The right to modify the file implies the right to access its contents. When testing the right to access the file’s contents, CD DOS will first test the change code. If this passes, access is granted. If the change code does not pass then the inspect code is tested. If this passess, access is granted. Otherwise access is refused.

Note: This means that if an ICODE is set without a CCODE, anyone can access the file regardless!

A privileged user bypasses all these checks. Privileged users can modify and access any file, regardless of the change and inspect codes of that file.

Note that files do not have an owner. Any file can in priciple be used by any user. CD DOS will record the signon of the last user to modify a file, and the date when it was modified. Files also have a creation date, which cannot be changed.

Whenever a codeword has been provided (either by the ENTER command, by QueryChangeCodeByFP/QueryInspectCodeByFP), that codeword will be remembered and reused for subsequent access attempts.

To make a user privileged, set the change code for the user’s NAME-file to System. Use the command

CCODE «user».NAME

then choose “S” for System restrictions. To demote a privileged user to a normal user, use the command

CCODE «user».NAME

then choose “P” for Personal restrictions and type the user’s name when prompted.

Only a privileged user can create another privileged user, which means that each installation of CD DOS must have at least one privileged user.

Floppy disks and drive units

CD DOS handles one to four drive units, numbered from zero to 3. At the start of CD DOS it will ask for the number of attached drives. Drives can be single sided or double sided. If double sided drives are used then the sides are treated as if they were separate drives: unit 0 will be the first side on the first drive, unit 1 will be the second side of the first drive.

Unit 0 is somewhat special: when signing on the NAME-file of the user is always read from unit 0. One unit can be set as default using DUNIT. If a filename is specified without an explicit unit, the default unit will be assumed. This applies to image files (external commands) as well as to normal files.

CD DOS was designed with single density floppy disks in mind. Disks have 35 tracks of 9 sectors. Each sector contains a single 256-byte block. The raw capacity of disks is therefore 78.75 kilobytes. The first two tracks of each disk are reserved for file directory information. The usable capacity is therefore 74.25 kilobytes; the FREE command on an empty disk shows 297 free blocks. Because of limited space in the first two tracks each disk can contain at most 64 files.

Files consist of one or more blocks, from a certain starting track and sector up to their final track and sector. The LIST command shows the starting and ending tracks and sectors. Files must be contiguous. Deleting a file will leave a ‘gap’ that can only be used for other files of the same size or smaller.

Disk layout of other DOSs

Other disk operating systems for 2650-based computers use incompatible disk layouts. For example, VHSDos and forks use 37 tracks of 10 sectors, for 17% more data per floppy. VHSdos also uses a more flexible disk layout, where each block contains a pointer to the next track and sector. In VHSdos files do not need to be contiguous, and can more easily expand in length when more space is necessary.

Images

Executable programs must be loaded at a specific memory address before they can be run. Image files (created using the IMAGE command) contain the necessary information. Image files are stored in a special format. They consist of two or more sections. Each section has the following format:

AA AA LN LN By .. By

where AA AA is the start address in memory where the section is to be stored, and LN LN is the length of the section (the exact number of bytes). The following bytes are the actual content of the section.

Multiple sections can thus be specified. The last section is indicated by a length of zero. The starting address in that case specifies the entry point of the image, the address where execution commences when the image is run.

To execute an image, use the RUN command or simply type the name of the file.

Missing utilities in CD DOS

There are several command missing from CD DOS that are necessary to use it as a full operating system. For example:

  • format: to erase and initialise a disk for use by CD DOS.
  • copy: to make a duplicate of a file.
  • password: to change the password for the current signon.
  • user: to create new signon accounts.

CD DOS memory map and startup

Memory map:
0000 - 0FFF ROM contents
1000 - 14FF Display memory
1500 - 17FF scratch static RAM (onboard)
1800 - 1FFF scratch RAM
2000 - 3FFF program space, page 1
4000 - 5FFF program space, page 2
6000 - 7AFF DOS program code
7B00 - 7BFF open files block
7C00 - 7FFF disk buffers

This gives 18 kbyte (the last 2 kbyte of page 0, and the entire pages 1 and 2) for utility programs and applications. CD DOS makes no assumptions, and has no requirements, on what memory addresses should be available. The only requirement is that the entire page 3 is available to CD DOS as RAM. CD DOS cannot be installed in ROM.

I have a ROM that is marked as “IPL v1.1”, which probably stands for Initial Program Load. IPL read the contents of tracks 2..4 into memory at 6000. With 27 sectors this exactly covers 6000..7AFF. See the separate page on IPL for more information.

You can make a blank disk bootable with the following commands:

ALLOC DOS.SYS/0 27
DISKWR 0 2 1 H6000 27

About the binary and source listing

https://amigan.yatho.com/games.rar contains games/cd2650/DOS.pgm. This is the binary. The pgm-format counts bytes from address zero, so the first 24 Kbytes of this file need to be deleted to obtain the true binary starting at address h’6000′.

I disassembled this file using DASMx, then manually renamed the labels to longer and more meaningful ones, and added comments and documentation. In the process I encountered and corrected a few bugs (see Corrections and Real Bugs).

https://amigan.yatho.com/y-docs.rar contains SentByFrankPhillipse/dos.pdf. This is a
scanned program listing. The listing and the binary are mostly the same, but differ in a few places (see below). The assembly file that I created tries to remain as close as possible to the original.

The binary file appears to be a dump from the CD DOS in action, which means that several scratch memory locations contain ‘live’ values instead of the h’00’ they would normally contain in an assembly listing. For example, the Command buffer is not empty, but still contains “LIST”. The buffer containing the current signon contains username “POL”. This username is the same as the name in the heading of the scanned program listing. The date field still contains “07-29-81”, the same as the date of the listing. Both therefore appear to be from the same source, but the bin file is a modified version, and the source listing is the more original version.

Then there are some true differences:

  • The binary was patched so that the DATE command is the same as the WHEN command.
  • The original string “Enter the number of disk drives (1-3):” was changed in-place to
    “Number of disk drives:”.
  • The original code (DOS1, line 119) asked for the number of disk drives, checked, and stored the number in TOTUNS. This took 24 bytes. This was replaced by code that simply writes ‘2’ into TOTUN/NumUnits. The remaining bytes were used to check on a particular monitor program and, if present, skip the query for a signon name.
    Effectively these modifications removed the question for a date, the number of drives
    and a signon name, and replaced these by fixed values. The modifications appear to have been made in-place, by overwriting selected bytes in the binary output. This explains the dummy bytes at 6748 and other places in the binary.
  • For the ACONs at the start, the last one used to point to PRNT at 7ACC. It was
    patched to point to 1E00 instead. The code at 7ACC assumes a printer at extended
    ports h'FC' and h'FD'.

The scanned source listing uses files DOS1/0, DOS2/0, DOS3/0, DOS4/1 and DOS5/1.
DOS1/0: from the start to address 66D6, the end of the string table.
DOS2/0: from end of string table to 6BA0, ParseFilename
DOS3/0: from ParseFilename to 701E, DrvWaitWhileBusy
DOS4/1: from DrvWaitWhileBusy to 75C0, halfway down the FreeSpaceMap subroutine.
DOS5/1: from FreeSpaceMap to the end of the disassembly.
But the listing then continues with another DOS1/0 printout containing just a literal copy of the TrkSctToBlock function with some RES and EQU. The “extra” DOS4 printout seems to be a shortened and modified version of the full DOS4/1 file. All of these can be ignored.

Corrections and Real Bugs

Code at several addresses is lodz,r0 (byte h'00'). This was changed to iorz,r0
(byte h'60'). See the Assembler patterns for the reason.
Applies to addresses 6878, 6885, 689C, 69B3, 6AB4, 6ABF, 6B2A, 70FE, 76F2, 77E7, 790E, 7926, 797C, 7A12, 7AC6.

Code at address 6CBB (page 24 line 164 in the scanned program listing) is andi,r0 h'3F' (bytes 44 3F). This may be suitable for the Central Data display PROMs, but not for the (my) P1 PROMs. This was changed to andi,r0 h'7F' (bytes 44 7F). Change this back if you need.

ParseNumber will “eat trailing spaces”. Code at 6F3A (page 31 line 509 in the scanned program listing). It stops at the first non-space, but unfortunately reads that non-space in the process. It should back up the CmdLinePtr by one, so that further arguments on the commandline can be parsed completely. Change 6F3A .. 6F42 to NOP-codes.

ParseFilename has he same issue: code at 6C11 (page 22 line 55 in the scanned program listing). Change 6C11 .. 6C19 to NOP-codes.

All routines that parse names or numbers from the command line will skip preceding spaces anyway, so there is no need to skip trailing spaces as well.

Code at 70e8 (page 36, line 114 in the scanned program listing) should be stra,r0 io_SECThi instead of stra,r1 io_SECThi. This is incorrect in the binary as well.

Code at 791e (page 54 line 405 in the scanned program listing) should be retc,gt instead of retc,eq. Change 791e from h'14' to h'15'. This is correct in the scanned program listing, but not in the binary.

There is unreachable code at 690B (page 16 line 286 in the scanned program listing).

Programming using CD DOS

The programming interface consists of a list of subroutines and routines that can be called from other programs. These (sub)routines provide all necessary functions for handling files and for input/output to the display, keyboard. and printer. The routines can be accessed using a table at the start of CD DOS, by using indirect branch instructions. Do not branch to subroutines directly, but always through the entry points at the start of CD DOS, so that your programs will work correctly with versions of CD DOS. The table starts at address 6004. See the assembly file for more information. Most of the entries are for subroutines that return to the caller when done. A few a routines, that perform some function and then present the CD DOS command prompt for the next command. Also, a few entries behave as subroutines normally but drop to the command prompt on errors. Be careful when using the latter in your programs, as calls to those subroutines may not return.

Subroutines that operate on files come in two styles: subroutines that use a filename that was parsed from the commandline, and subroutines that use a file that was just opened. The names of these subroutines end in “…ByName” or “…ByFP” (by file pointer) respectively.

Builtin commands

ALLOC – reserve disk space for a new file

alloc «filename» «number»

Creates the file and allocates «number» blocks to it. The file must not already exist, and there must be sufficient free space on the drive unit. The command will then ask for the file’s initial change code (see CCODE).

CCODE – Set or modify the change code on a file ** Change code required **

ccode «filename»

The command will ask for Regular file or Personal file. If the user is privileged allow the System option too. Based on the chosen answer, restrict file modification to privileged access, codeword-based access, or access based on signon.

CLOSE – Close an open file

close «filename»

Close the indicated file. If the file was not open then simply ignore the command. Also see OPEN and LOPEN.

DATE – set the system date

date

Asks for the current date, and checks that it is valid. The system does not contain a real-time clock, so only syntactic checks are possible. Dates conform to MM-DD-YY, where MM denotes two digits for the month (e.g. use 08 for August), DD denotes two digits for the day of the month, and YY denotes the last two digits of the year. The checks are incomplete, so take care to enter the right digits.

DOS uses the date for files (creation date, date last modified) and for NAME-files (date of last signon).

DEALLOC – truncate a file ** Change code required **

dealloc «filename» «number»

Reduce the length of the file to «number» blocks. If «number» is zero, delete the file without asking for confirmation. The information at the end of the file is removed and lost.

DELETE – Remove a file ** Change code required **

delete «filename»

Remove the indicated filename after confirmation. Any space taken up by the file becomes free, and all information in the file is lost.

DISKCOPY – duplicate a disk ** Privileged **

diskcopy «unitto» «unitfrom»

Makes an exact duplicate of all contents of the disk in «unitfrom» onto the disk in «unitto». The previous contents of «unitto» are lost. The destination disk must be formatted. Unit «unitto» cannot be zero, as it contains the system disk. Note: this will erase the memory in pages 2 and 3 (from address 2000 to 5FFF).

DISKRD – transfer data from disk into memory ** Privileged **

diskrd «unit» «track» «sector» «address» «numblocks»

Read the number of blocks from the indicated drive unit, starting from the indicated track and sector, and place their contents at the indicated address. Previous memory contents are lost. Take care not to overwrite the memory used by DOS (page 3, address 6000 to 7FFF).

The data from disk may or may not be in use by a file. This command can also be used to read from tracks 0 and 1, which contain file directory information.

If possible use the READ command instead.

DISKWR – transfer data from memory onto disk ** Privileged **

diskwr «unit» «track» «sector» «address» «numblocks»

Store the contents starting from the indicated memory address onto the disk in the indicated drive unit, starting at the indicated track and sector. Writes the indicated number of blocks.

This command will not create a file for the data. If possible use the WRITE command instead.

DTYPE – Set default file type

dtype «type»

Set the default file type to «type». The initial default extension is “SRC”. Note that the previous default is lost when «type» contains an illegal file type (e.g. when containing illegal characters, or when longer than 4 characters).

DUNIT – Set default drive

dunit «unit»

Set the default unit for filenames. The initial default unit is 0 (the system disk). «unit» must be less than the number of drives installed in the system.

ENTER – Enter the default codeword

enter

Prompts for a codeword, which will be tried for file access to files protected by a codeword (Regular). The default codeword is only used when checking for access rights to files, not when creating new files.

EXEC – Jump to a memory address

exec «address»

Execution continues at the indicated address. The address is often entered using hexadecimal notation. Use “EXEC 0” to jump to the monitor program, or “EXEC H6000” to restart DOS, for example.

FREE – show available disk space

free «num» +

Enter one or more drive units. The command then displays the number of free sectors on each of the drive units. Free space may not be contiguous, but be spread out in small portions between existing files. Since files are always contiguous, having sufficient free space does not mean that a file can be saved or created successfully.

ICODE – Set or modify the inspect code on a file ** Change code required **

icode «filename»

The command will ask for Regular file or Personal file. If the user is privileged allow the System option too. Based on the chosen answer, restrict file inspection to privileged access, codeword-based access, or access based on signon.

IMAGE – create image file ** Change code required **

image «filename» («address» «bytes»)+ «start» ?

Creates an image file consisting of one or more chuncks. Each chunck starts at the indicated address and is the indicated number of bytes in length. Optionally a start address can be specified; when the image is run (by typing its name, or using the RUN
command) execution commences at this start address.

If no start address is specified, the image cannot be run but you can still use the LOAD command to restore its contents into memory.

The type of the filename is ignored, and assumed to be IMAG. If the image file already exists then the change code is required to overwrite it.

INSPECT – view raw disk sectors ** Privileged **

inspect «unit» «track» «sector»

Shows the hex contents of that sector. Press Return to see the next sector, or any other key to stop.

LIST – show the contents of a disk

list «unit» *

Show the directory entries on the drive units (or the default unit). May specify zero or more drive units. The listing pauses at the bottom of the screen to prevent information from scrolling off the page. Press Esc to terminate the listing at any time.

filename.type USES SECTORS St-Es TO Et-Es lastModbBy lastModDate creationDate

Shows the full filename, and the starting and ending tracks and sectors. Only a privileged user will see the name of the user who last modified the file, and the dates of last modification and creation.

LOAD – place an image into memory ** Inspect code required **

load «filename»

Filename contains an image; its contents are loaded into memory but the program is not executed. The type of the file, if any, is ignored and assumed to be IMAG.

LOPEN – list open files

lopen

Shows at most four line containing the names of currently open files (filename, type and drive). See the OPEN and CLOSE commands.

OPEN – open a file

open «filename»

Open the indicated file, prepare it for further actions. The file exists, but the command does not check whether the user has access rights to the file. (It cannot anyway, for DOS does not know whether the file will be read or written).

READ – Copy file contents into memory ** Inspect code required **

read «filename» «skip» «address» «num»

Reads the contents of «filename» and place them into memory at the indicated address, reading «num» blocks. Start reading at block «skip»; use 0 to start reading from the beginning of the file. «num» can be at most 255.

«skip»+«length» cannot be greater than the length of the file, and the Invalid Operation message is shown when you try to.

Reading is always in multiples of blocks; you cannot read only part of a block.

RENAME – Change the name of a file ** Change code required **

rename «oldname» «newname»

Changes the name of the file to the new name. The drive of the new name, if any is given, is ignored.

RESTORE – Reset disk drives

restore «num» +

Reset the drive controller and return the head on each of the drives to track 0. Note that with double-sided drives you physically cannot restore drive units 0 and 1 independently.

RUN – place an image into memory and execute ** Inspect code required **

run «filename»
«filename»

Filename contains an image; its contents are loaded into memory. If the image contains a starting address, execution then continues at that address: the program is run. The type of the file, if any, is ignored and assumed to me IMAG.

The RUN command-word is optional. If you do not specify a valid builtin DOS command, the command is tried as an external DOS command, from an image. Use RUN when an image file has the same name as an builtin command. For example, to run the program “RESTORE.IMAG”.

Note that you can run images from units other than the default unit by specifying the drive number of the filename.

SIGNON – Logon to DOS

signon

DOS will ask for a username, and then sign on as that user. File «username».NAME/0 is read, the password is checked (if set), and logon statistics are updated. If the user has a startup command, that command will be run.

If you try to sign on as a non-existing user (a user without a corresponding NAME-file) the error “File not found” will be shown.

WHEN – show the system date

when

Prints the current system date. Change this date using the DATE command.

WRITE – Store memory contents into a file ** Change code required **

write «filename» «skip» «address» «num»

Store the memory contents starting at «address» into «filename», writing «num» blocks. Start writing at block «skip». Use 0 to start writing at the beginning of the file. «num» can be at most 255.

«skip»+«length» cannot be greater than the length of the file, and the Invalid Operation message is shown when you try to.

Writing is always in multiples of blocks; you cannot write only part of a block.