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.



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 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.


There are 5 different traps in use within QDOS and SMSQ/E. These are:



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
Vector QDOS Mnemonic SMS Mnemonic Description
$C0 MM_ALCHP MEM_ACHP Allocate space in the common heap
$C2 MM_RECHP MEM_RCHP Release space in the common heap
$C4 UT_WINDW OPW_WIND Create a window channel
$C6 UT_CON OPW_CON Create a console channel
$C8 UT_SCR OPW_SCR Create a screen channel
$CA UT_ERR0 UT_WERSY Print error message on #0
$CC UT_ERR UT_WERMS Print error message to any channel
$CE UT_MINT UT_WINT Print a decimal number to any channel
$D0 UT_MTEXT UT_WTEXT Send a message to a channel
$D2 UT_LINK MEM_LLST Link an item into a list
$D4 UT_UNLNK MEM_RLST Unlink an item from a list
$D6 UT_CRASH CV_DATIL QDOS - Crashes the QL. SMS - Convert date & time (6 words) into a long integer
$D8 MM_ALCHP MEM_ALHP Allocate space from a user heap
$DA MM_LNKFR MEM_REHP Link free space (back) into a user heap
$DC IO_QSET IOQ_SETQ Set up a queue header
$DE IO_QTEST IOQ_TEST Test a queue's status
$E0 IO_QIN IOQ_PBYT Put a byte into a queue
$E2 IO_QOUT IOQ_GBYT Get a byte from a queue
$E4 IO_QEOF IOQ_SEOF Put EOF marker into a queue
$E6 UT_CSTR UT_CSTR Compare two strings
$E8 IO_SERQ IOU_SSQ Direct queue handling
$EA IO_SERIO IOU_SSIO General I/O handling
$EC CN_DATE CV_ILDAT Get date as a string
$EE CN_DAY CV_ILDAY Get day as a string
$F0 CN_FTOD CV_FPDEC Convert a floating point number to ASCII
$F2 CN_ITOD CV_IWDEC Convert an integer to ASCII
$F4 CN_ITOBB CV_IBBIN Convert binary byte (8 bits) to ASCII
$F6 CN_ITOBW CV_IWBIN Convert binary word (16 bits) to ASCII
$F8 CN_ITOBL CV_ILBIN Convert binary long word (32 bits) to ASCII
$FA CN_ITOHB CV_IBHEX Convert hex byte (8 bits) to ASCII
$FC CN_ITOHW CV_IWHEX Convert hex word (16 bits) to ASCII
$FE CN_ITOHL CV_ILHEX Convert hex long word (32 bits) to ASCII
$100 CN_DTOF CV_DECFP Convert ASCII to floating point
$102 CN_DTOI CV_DECIW Convert ASCII to word integer
$104 CN_BTOIB CV_BINIB Convert ASCII to binary byte (8 bits)
$106 CN_BTOIW CV_BINIW Convert ASCII to binary word (16 bits)
$108 CN_BTOIL CV_BINIL Convert ASCII to binary long word (32 bits)
$10A CN_HTOIB CV_HEXIB Convert ASCII to hex byte (8 bits)
$10C CN_HTOIW CV_HEXIW Convert ASCII to hex word (16 bits)
$10E CN_HTOIL CV_HEXIL Convert ASCII to hex long word (32 bits)
$110 BP_INIT SB_INIPR Add new SuperBasic procedures & functions
$112 CA_GTINT SB_GTINT Fetch word integer parameters
$114 CA_GTFP SB_GTFP Fetch floating point parameters
$116 CA_GTSTR SB_GTSTR Fetch string parameters
$118 CA_GTLIN SB_GTLIN Fetch long integer parameters
$11A BV_CHRIX QA_RESRI Reserve space on the maths stack
$11C RI_EXEC QA_OP Perform a single maths operation
$11E RI_EXECB QA_MOP Perform a list of maths operations
$120 BP_LET SB_PUTP Return a result into a parameter
$122 IO_NAME IOU_DNAM Decode a device name

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.

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

Vector Mnemonic Description
$04 WM.SETUP Set up from standard window definition
$08 WM.SMENU Set up from standard menu definition
$0C WM.PRPOS Primary window position and clear
$10 WM.PULLD Pull-down window position and clear
$14 WM.UNSET Unset working definition
$18 WM.WRSET Reset working definition
$1C WM.WDRAW Draw window contents
$20 WM.MDRAW Draw menu sub-window contents
$24 WM.INDEX Draw index items
$28 WM.SWDEF Set sub-window definition
$2C WM.LDRAW Draw loose items
$30 WM.RPTR Read pointer
$34 WM.MHIT Standard menu sub-window hit routine
$38 WM.PANSC Standard menu sub-window control routine
$3C WM.IDRAW Re-draw information window(s)
$40 WM.CHWIN Change window position or size
$44 WM.DRBDR Draw current item border
$48 WM.MSECT Find menu section
$4C WM.STLOB Set loose object
$50 WM.STIOB Set information object
$54 WM.FSIZE Find layout size
$58 WM.SWINF Set window to information sub-window
$5C WM.SWLIT Set window to loose item
$60 WM.SWAPP Set window to application sub-window
$64 WM.SWSEC Set window to section of sub-window
$68 WM.RNAME Read name
$6C WM.ENAME Edit name
$70 WM.UPBAR Update pan/scroll bars
$74 WM.ERSTR Get error string
$78 WM.RPTRT Read pointer with timeout/event
$7C WM.SETSP Set system palette
$80 WM.GETSP Read system palette
$84 WM.TRAP3 Trap#3 with GD2 colours
$88 WM.OPW Window vectors with GD2 colours
$8C WM.SSCLR Set single colour pattern
$90 WM.JBPAL Set system palette of job

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


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'
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)

525 Lines.
625 lines.
On QDOS, the word at offset $A6 has the use described here. It appears, however, that in SMS[Q] on an Atari ST, the byte at $A6 is used to define if DMA (Direct Memory Access) is on or off.







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.
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).