Sinclair QDOS
QDOS is the multitasking operating system found on the Sinclair QL personal computer and its clones. It was designed by Tony Tebby whilst working at Sinclair Research, as an in-house alternative to 68K/OS, which was later cancelled by Sinclair, but released by original authors GST Computer Systems. Its name is not regarded as an acronym and sometimes written as Qdos in official literature (see also the identically-pronounced word kudos).
QDOS was implemented in Motorola 68000 assembly language, and on the QL, resided in 48 kB of ROM, consisting of either three 16 kB EPROM chips or one 32 kB and one 16 kB ROM chip. These ROMs also held the SuperBASIC interpreter, an advanced variant of BASIC with structured programming additions. This also acted as the QDOS command line interpreter.
Facilities provided by QDOS included management of processes (or "jobs" in QDOS terminology), memory allocation, and an extensible "redirectable I/O system", providing a generic framework for filesystems and device drivers. Very basic screen window functionality was also provided. This, and several other features, were never fully implemented in the released versions of QDOS, but were improved in later extensions to the operating system produced by Tebby's own company, QJUMP.
Rewritten, enhanced versions of QDOS were also developed, including Laurence Reeves' Minerva and Tebby's SMS2 and SMSQ/E. The last is the most modern variant and is still being improved.
Versions
QDOS versions were identified by numerical version numbers. However, the QL firmware ROMs as a whole (including SuperBASIC) were given two- or three-letter alphabetic identifiers (returned by the SuperBASIC function VER$).
The following versions of QDOS were released (dates indicate estimated first customer shipments):
- 0.08: the last pre-production version.
- 1.00: corresponded to the FB version QL ROMs, released in April 1984.
- 1.01: corresponded to the PM version ROMs. This was faster and had improved Microdrive support.
- 1.02: corresponded to the AH ROM version released in June 1984. This fixed many bugs and was the first ROM version to be produced in quantity.
- 1.03: included in ROM versions JM and TB; a minor bug-fix release issued in late 1984.
- 1.10: corresponded to the JS and JSU (US export version) ROMs, released in early 1985. This was the last version used in QLs manufactured for the UK market.
- 1.13: corresponding to the MGx series of ROM versions for European export markets. Included a significant number of bug fixes. The following localised versions of the MG firmware are known to exist:
- MGE: Spanish
- MGF: French
- MGG: German
- MGI: Italian
- MGS: Swedish
The localised versions of QDOS were identified by the "." in the version number being replaced by the ROM version suffix letter used to identify the territory, e.g. the MGE ROMs contained QDOS version 1E13. All MG firmware versions shared the same bottom 32 kB ROM chip. Qdos 1.13 was also reported to be included in a Greek localised ROM version, known as ΣFP (marked on the ROMs as EFP).
QDOSMSQ Documentation
The following text is a direct dump from the http://www.qdosmsq.dunbar-it.co.uk website which is operated by Norm, with a lot of the Wiki content provided by George Gwilt. Norm, please get in touch if we've overstepped our bounds by duplicating your content here! All links below refer back directly to your site.
Traps
There are 5 different traps in use within QDOS and SMSQ/E. These are:
-
Trap #0 - Enter Supervisor Mode.
-
Trap #1 - Manager Traps.
-
Trap #2 - Input Output Traps.
-
Trap #3 - System Traps.
-
Trap #4 - SuperBasic Absolute Address Trap.
Vectors
The following table gives details of the vectors that fitted into the first half of the QL ROM. They can all be accessed by code, similar to the following :
start move.w UT_CON,a2 ; Fetch the routine address from the vector table ; ; Set parameter registers etc here jsr (a2) ; Call the routine tst.l d0 ; Set the Z flag if all ok beq.s ok ; All worked ok oops ; ; Process errors here ; ; Exit or carry on appropriately ok ; ; Carry on processing here
The following table gives details of the vectors that didn't fit into the first half of the QL ROM. They can all be accessed by code, similar to the following :
start move.w MD_READ,a2 ; Fetch the routine address from the vector table ; ; Set parameter registers etc here jsr $4000(a2) ; Call the routine in the second half of the ROM bra.s oops ; Routine returns here on error. Ignore D0 on return bra.s ok ; Routine returns here on ok. oops ; ; Process errors here ; ; Exit or carry on appropriately ok ; ; Carry on processing here
Vector | QDOS Mnemonic | SMS Mnemonic | Description |
---|---|---|---|
$124 | MD_READ | MD_READ | Read a microdrive sector |
$126 | MD_WRITE | MD_WRITE | Write a microdrive sector |
$128 | MD_VERIN | MD_VERIF | Verify a microdrive sector |
$12A | MD_SECTR | MD_RDHDR | Read a microdrive sector header |
The following are not, to my knowledge, documented anywhere else. I have found these undocumented vectors in various places, not least being a DEA dissassembly of the Superbasic ED command (pre SBasic as ED is different in SBasic). The mnemonics are my own and unless someone complains, I shall not change them.
They can all be accessed by some code, similar to the following :
start move.w SB_SNTX,a2 ; Fetch the routine address from the vector table ; ; Set parameter registers etc here jsr $4000(a2) ; Call the routine in the second half of the ROM
Beware : I am not sure about the return mechanism. It could be an error code in D0.L or it could be a bra.s pair of instructions similar to the MD_XXXX routines. More investigation is required.
Vector | My Mnemonic | Description |
---|---|---|
$12C | SB_SNTX | SuperBasic syntax analyser |
$12E | SB_FSTFC | First syntax table for SuperBasic commands |
$130 | SB_SSTFC | Second syntax table for SuperBasic commands |
$132 | SB_FPCBL | Format a pre-compiled SuperBasic line |
$134 | SB_CMPER | Error when pre-compiling a SuperBasic line |
$136 | SB_SPFBL | Store a pre-compiled SuperBasic line |
$138 | SB_CCBTA | Convert pre-compiled SuperBasic line to ASCII |
There is always one awkward one isn't there ? The following vectored routine is difficult. If the QDOS version is 1.03 or less then call it like this :
start move.w SB_INSTK,a2 ; Fetch the routine address from the vector table ; ; Set parameter registers etc here jsr $4318(a2) ; Call the routine in the second half of the ROM
and if it is greater than 1.03, call it like this :
start move.w SB_INSTK,a2 ; Fetch the routine address from the vector table ; ; Set parameter registers etc here jsr $4000(a2) ; Call the routine in the second 16K of ROM
Beware : I am not sure about the return mechanism. It could be an error code in D0.L or it could be a bra.s pair of instructions similar to the MD_XXXX routines. More investigation is required.
Vector | My Mnemonic | Description |
---|---|---|
$13A | SB_INSTK | Initialise SuperBasic internal stacks |
Trap #3 - Pointer Environment
D0 Key | Mnemonic | Description | |
---|---|---|---|
$6C | IOP.FLIM | Find window limits | |
$6D | IOP.SVPW | Partial window save | |
$6E | IOP.RSPW | Partial window restore | |
$6F | IOP.SLNK | Set linkage block | |
$70 | IOP.PINF | Information enquiry | |
$71 | IOP.RPTR | Read pointer | |
$72 | IOP.RPXL | Read pixel at x,y | |
$73 | IOP.WBLB | Write blob at x,y | |
$74 | IOP.LBLB | Write line of blobs | |
$75 | Not implemented | ||
$76 | IOP.WSPT | Write sprite at x,y | |
$77 | IOP.SPRY | Spray pixels in blob | |
$78 | Not implemented | ||
$79 | Not implemented | ||
$7A | IOP.OUTL | Set window outline | |
$7B | IOP.SPTR | Set pointer position | |
$7C | IOP.PICK | Pick window | |
$7D | IOP.SWDF | Set window definition pointer | |
$7E | IOP.WSAV | Save window area | |
$7F | IOP.WRST | Restore window area |
WMAN Vectors
Pointer Interface Vectors
In addition to the WMAN vectors listed above there are some CON driver vectors which are listed below. These can be obtained by the following code:
sys_clnk equ $C4 pt_vecs equ $150 moveq #mt_inf,d0 ; $00 trap #1 ; A0 -> sys vars movea.l sys_clnk(a0),a3 ; Pointer to CON linkage movea.l pt_vecs(a3),a0 ; Vectors jsr pv_pinf(a0) ; Call the vector
Vector | Mnemonic | Description |
---|---|---|
$00 | PV_PINF | Get pointer information (IOP.PINF equivalent) |
$06 | PV_FSPR | Find correct sprite definition |
$0C | PV_SSPR | Set/Get system sprite |
$12 | PV_SIZE | Get pixel rounding sizes |
$18 | PV_MBLK | Move blocks |
$1C | PV_CURSP | Set/unset sprite cursor |
$24 | PV_BGCTL | Control background updating |
Pointer Environment Data Structures
This section details all the various data structures required to set up and display various menus, windows etc under the Pointer Environment.
System Variables
The system variables are used by QDOSMSQ to hold details of how the system looks. Some of these are helpful to user written programs, others are not.
In the old days, QDOS was written on the assumption that the system variables would move around in memory. Then this was changed, and they became resident at address $28000 or 163840.
Recent changes have put things back where they were again, so never assume that the variables are fixed in memory.
For this reason, the table below gives offsets from the start of the system variables and not absolute addresses.
Offset | Size | QDOS Mnemonic | SMSQ Mnemonic | Description |
---|---|---|---|---|
$00 | Long | SV_IDENT | SYS_IDNT | Identification word. QDOS = $D2540000 SMS = 'S2AT' SMSQ = 'SMSQ' Thor (ARGOS) = $DC010000 |
$04 | Long | SV_CHEAP | SYS_CHPB | Pointer to start of common heap |
$08 | Long | SV_CHPFR | SYS_CHPF | Pointer to first free space in common heap |
$0C | Long | SV_FREE | SYS_FSBB | Pointer to start of free memory |
$10 | Long | SV_BASIC | SYS_SBAB | Pointer to SuperBasic area |
$14 | Long | SV_TRNSP | SYS_TPAB | Pointer to start of Transient Program area |
$18 | Long | SV_TRNFR | SYS_TPAF | Pointer to first free space in Transient Program area |
$1C | Long | SV_RESPR | SYS_RPAB | Pointer to start of Resident Procedure area |
$20 | Long | SV_RAMT | SYS_RAMT | Pointer to the end of RAM + 1 |
$24 | Long | Unused | SYS_MXFR | Maximum return from free memory call |
$28 | Long | Unused | SYS_RTC | Real time in seconds |
$2C | Word | Unused | SYS_RTCF | Real time fractional countdown |
$2E | Word | SV_RAND | SYS_RAND | Pseudo random number |
$30 | Word | SV_POLLM | SYS_PICT | Count of poll interrupts that have been missed |
$32 | Byte | SV_TVMOD | SYS_DTYP | TV/Monitor mode. 0 = Monitor. 1 = PAL TV (SMSQ only). 2 = NTSC TV 1). <>2 = PAL TV (QDOS Only) 2) |
$33 | Byte | SV_SCRST | SYS_DFRZ | Screen status (MODE corrupts this variable). 0 = Screen is active. <>0 = Screen is 'frozen' |
$34 | Byte | SV_MCSTA | SYS_QLMR | Copy of TV hardware register |
$35 | Byte | SV_PCINT | SYS_QLIR | Copy of Interrupt hardware register |
$36 | Byte | Unused | SYS_RSHD | OK to reschedule |
$37 | Byte | SV_NETNR | SYS_NNNR | Network station number (1 to 64) |
$38 | Long | SV_I2LST | SYS_EXIL | Pointer to start of external interrup list |
$3C | Long | SV_PLIST | SYS_POLL | Pointer to start of polled tasks list |
$40 | Long | SV_SHLST | SYS_SHDL | Pointer to start of scheduler task list |
$44 | Long | SV_DRLST | SYS_IODL | Pointer to start of simple device driver list |
$48 | Long | SV_DDLST | SYS_FSDL | Pointer to start of directory device list |
$4C | Long | SV_KEYQ | SYS_CKYQ | Pointer to current keyboard queue (zero if no queue) |
$50 | Long | SV_TRAPV | SYS_ERTB | Pointer to current RAM vector table (zero if none) |
$54 | Long | SV_BTPNT | SYS_SBRP | Pointer to most recent slave block entry |
$58 | Long | SV_BTBAS | SYS_SBTB | Pointer to base of slave block table |
$5C | Long | SV_BTTOP | SYS_SBTT | Pointer to end of slave block table +1 |
$60 | Word | SV_JBTAG | SYS_JBTG | Current value of job tag |
$62 | Word | SV_JBMAX | SYS_JBTP | Highest job number so far |
$64 | Long | SV_JBPNT | SYS_JBPT | Pointer to current job's entry in job table |
$68 | Long | SV_JBBAS | SYS_JBTB | Pointer to start of job table |
$6C | Long | SV_JBTOP | SYS_JBTT | Pointer to top of job table +1 |
$70 | Word | SV_CHTAG | SYS_CHTG | Current value of channel tag |
$72 | Word | SV_CHMAX | SYS_CHTP | Highest channel number so far |
$74 | Long | SV_CHPNT | SYS_CHPT | Last channel checked by the waiting for I/O scheduler routine |
$78 | Long | SV_CHBAS | SYS_CHTB | Pointer to start of channel table |
$7C | Long | SV_CHTOP | SYS_CHTT | Pointer to end of channel table +1 |
$80 | Long | Unused | SYS_FRBL | Free block list. (List of blocks to be returned to the common heap) |
$84 | Long | Unused | Unused | Unused |
$88 | Word | SV_CAPS | SYS_CAPS | Caps lock indicator. 0 = off. $FF00 = on |
$8A | Word | SV_ARBUF | SYS_LCHR | Last key pressed |
$8C | Word | SV_ARDEL | SYS_RDEL | Key repeat delay (default = 30 (QDOS), 25 (SMSQ)) |
$8E | Word | SV_ARFRQ | SYS_RTIM | Key repeat frequency (default = 4 (QDOS), 2 (SMSQ)) |
$90 | Word | SV_ARCNT | SYS_RCNT | Key repeat counter |
$92 | Word | SV_CQCH | SYS_SWTC | Change keyboard queue code (3 = CTRL C) |
$94 | Word | SV_WP | Write protect status of microdrives (Not implemented) | |
$96 | Word | SV_SOUND | SYS_QLBP | Sound status. 0 = off. $FF00 = on |
$9C | Long | SV_SER1C | SYS_SER1 | Pointer to serial port 1's input queue |
$9C | Long | SV_SER2C | SYS_SER2 | Pointer to serial port 2's input queue |
$A0 | Byte | SV_TMODE | SYS_TMOD | ULA transmit mode. Bits 0 - 2 = baud rate number. Bit 3 = 0 = ser1. = 1 = ser2. Bit 4 = A microdrive is running? |
$A1 | Byte | SV_PTYP | SYS_PTYP | Processor type. $00 = 68000 or 68008. $10 = 68010. $20 = 68020 |
$A2 | Long | SV_CSUB | SYS_CSUB | Pointer to a routine to call when CAPS held down (or is it?) |
$A6 | Word | SV_TIMO | SYS_TMOT | Counter for timing serial output. 3) |
$A8 | Word | SV_TIMOV | SYS_TMOV | Value of timeout for the above. (= 1200/baud +1) ???? |
$AA | Word | SV_FSTAT | SYS_FSTT | Cursor flash counter |
$AC | Long | SV_PROGD | SYS_PRGD | Pointer to current TK2 PROGD$, or zero |
$B0 | Long | SV_DATAD | SYS_DATD | Pointer to current TK2 DATAD$, or zero |
$B4 | Long | SV_SPLD | SYS_DSTD | Pointer to current TK2 spool device name |
$B8 | Long | SV_THGLST | SYS_THGL | Pointer to thing list (PE) |
$BC | Long | SYS_PSF | Primary stack frame pointer (SMSQ) | |
$C0 | Byte | SYS_200I | 200 Hz in service (SMSQ) | |
$C1 | Byte | SYS_50I | 50 Hz in service (SMSQ) | |
$C2 | Byte | SYS_10I | 10 Hz in service (SMSQ) | |
$C3 | Byte | SYS_PLRQ | Poll requested (-ve for request) (SMSQ) | |
$C4 | Long | SYS_CLNK | Pointer to console linkage (SMSQ) | |
$C8 | Byte | SYS_CASTAT | -1 cache on, +1 instruction cache temp off (SMSQ) | |
$C9 | Byte | SYS_CASUP | Cache suppressed timer (SMSQ) | |
$CA | Word | SYS_IOPR | IO priority (SMSQ) | |
$CC | Long | SYS_CBAS | Current basic (copy of SYS_JBPT) (SMSQ) | |
$D0 | Byte | SYS_FPU | Type of FPU (FPSAVE) -1 = no FPU 1 = 68881 and no FPSP 2 = 68882 and no FPSP 4 = 68040 and no FPSP 6 = 68060 and no FPSP 9 = 68881 and FPSP (library version) 10 = 68882 and FPSP (library version) 12 = 68040 and FPSP (library version) 14 = 68060 and FPSP (library version) |
|
$D1 | Byte | SYS_MMU | Type of MMU (FPSAVE) -1 = no MMU 1 = 68851 3 = 68030 4 = 68040 or 68LC040 7 = 68060 or 68LC060 |
|
$D2 | Word | SYS_FPZS | Maximum length of FPSAVE area (FPSAVE) | |
$D4 | Long | SYS_FPSL | Address of save area list (FPSAVE) | |
$D8 | Long | SYS_CLFP | Address to access the FPSP (FPSAVE) | |
$DC | Long | Unused | ||
$E0 | Byte | SYS_PRTC | Set if real time clock protected (SMSQ) | |
$E1 | Byte | SYS_PMEM | Memory protection level (SMSQ ST) | |
$E2 | Word | SYS_SLUG | Slug level (SMSQ) | |
$E4 | Long | Unused | ||
$E8 | Long | SYS_KLNK | Pointer to keyboard linkage | |
$EC | Word | Unused | ||
$EE | Byte | SV_MDRUN | SYS_MDRN | Which microdrive is running (1 - 8) |
$EF | Byte | SV_MDCNT | SYS_MDCT | Microdrive run up/down counter |
$F0 | 8 Bytes | SV_MDDID | SYS_MDID | Drive id * 4 for 8 microdrives |
$F8 | 8 Bytes | SV_MDSTA | SYS_MDST | Status of each microdrive. 0 = no pending operations |
$100 | 16 Longs | SV_FSDEF | SYS_FSDD | 16 Pointers to file system definitions |
$140 | Long | SV_FSLST | SYS_FSDT | Pointer to list of file channel definitions |
$144 | Byte | SV_TRAA | SYS_XACT | TRAnslate is active flag. 0 = inactive, <>0 = active |
$145 | Byte | Unused | ||
$146 | Long | SV_TRATAB | SYS_XTAB | Pointer to TRAnslate table |
$14A | Long | SV_MSGTAB | SYS_ERMS | Pointer to (TRA) message table |
$14E | Long | SYS_MSTAB | Pointer to SMSQ message table | |
$154 | 4 Longs | SYS_TASKM | used by Taskmaster - conflicts with . . | |
$160 | Long | SYS_TURBO | used by Turbo | |
$164 | Long | SYS_QSOUND | used by QSound - conflicts with . . | |
$166 | Long | SYS_LANG | Current language code (SMSQ) | |
$168 | Long | SYS_LDMLST | Language dependent module list (SMSQ) | |
$16C | Word | SYS_LANG | Current language (SMSQ) | |
$16E | Word | Unused | ||
$170 | Long | SYS_VERS | Operating system version (SMSQ) |
SuperBasic Internals
Memory Organisation
File Systems
Errors
These are the error codes that QDOSMSQ can return from a vectored utility, a Trap or from SuperBasic extensions.
Always check for errors
When writing assembler routines, it is always advised to explicitly check the value in D0.L for an error code. This is done quickest using :
: trap #n ; Do the trap. tst.l d0 ; Did it work? beq.s all_ok ; Yes, all is well errors : ; Handle errors here bra kill_job ; And exit the job, or code somehow. all_ok : ; Carry on from here if all worked.
Some traps and vectored utilities do not set the Z flag before returning which is why you have to do it. Well, actually, it's not that they don't set it but when you call a trap, the status register is stacked for the duration and restored at the end, so while D0 may have an appropriate error code in it, the Z flag's state is unknown. A vector should always have correctly set the Z flag, so save some coding and execution time by simply checking the Z flag, as follows:
move.w <whatever>,a2 ; Get the vectored routine. jsr (a2) ; Do it. beq.s vec_ok ; All is well, skip error handler. v_error : ; Handle errors here bra kill_job ; And exit the job, or code somehow. vec_ok : ; Carry on from here if all worked.
Error codes
Value | QDOS Mnemonic | SMS Mnemonic | Description |
---|---|---|---|
-1 | ERR_NC | ERR_NC | Operation not complete . |
-2 | ERR_NJ | ERR_IJOB | Not a (valid) job. |
-3 | ERR_OM | ERR_IMEM | Out of memory. |
-4 | ERR_OR | ERR_ORNG | Out of range. |
-5 | ERR_BO | ERR_BFFL | Buffer overflow. |
-6 | ERR_NO | ERR_ICHN | Channel not open. |
-7 | ERR_NF | ERR_FDNF | File or device not found. |
-8 | ERR_EX | ERR_FEX | File already exists. |
-9 | ERR_IU | ERR_FDIU | File or device already in use. |
-10 | ERR_EF | ERR_EOF | End of file. |
-11 | ERR_DF | ERR_DRFL | Drive full. |
-12 | ERR_BN | ERR_INAM | Bad device. |
-13 | ERR_TE | ERR_TRNS ERR_PRTY |
Transmission error Parity error (SMS only). |
-14 | ERR_FF | ERR_FMTF | Format failed. |
-15 | ERR_BP | ERR_IPAR | Bad parameter. |
-16 | ERR_FE | ERR_MCHK | File error. |
-17 | ERR_EX | ERR_IEXP | Expression error. |
-18 | ERR_OV | ERR_OVFL | Arithmetic overflow. |
-19 | ERR_NI | ERR_NIMP | Not implemented. |
-20 | ERR_RO | ERR_RDO | Read only. |
-21 | ERR_BL | ERR_ISYN | Bad line of Basic. |
-22 | ERR_RWF | Read/write failed (SMS only). |