Preemptive scheduling on 16-bit real-mode os?

27 Sep 2022

I was browsing the MS-DOS source code and I was thinking is it possible to have a preemptive scheduler in a 16-bit OS running purely in real-mode? I think it should be possible.

IBM published the entire source code of BIOS (written for 8086) as a technical reference, and if we look closely there is a particular interrupt that can be of help, INT 08H.

;-- HARDWARE INT  08 H - ( IRQ LEVEL 0 ) --------------------------------------
;       THIS ROUTINE HANDLES THE TIMER INTERRUPT FROM CHANNEL 0 OF 	:
;       THE 8254 TIMER. INPUT FREQUENCY IS 1.19314 MHZ AND THE DIVISOR 	:
;       IS 65536, RESULTING IN APPROXIMATELY 18.2 INTERRUPTS EVERY SECOND.	:
;				:
;       THE INTERRUPT HANDLER MAINTAINS A COUNT (40:6C) OF INTERRUPTS SINCE	:
;       POWER ON TIME, WHICH MAY BE USED TO ESTABLISH TIME OF DAY.	:
;       THE INTERRUPT HANDLER ALSO DECREMENTS THE MOTOR CONTROL COUNT (40:40)	:
;       OF THE DISKETTE, AND WHEN IT EXPIRES, WILL TURN OFF THE	:
;       DISKETTE MOTOR(s), AND RESET THE MOTOR RUNNING FLAGS.	:
;       THE INTERRUPT HANDLER WILL ALSO INVOKE A USER ROUTINE THROUGH	:
;       INTERRUPT 1CH AT EVERY TIME TICK. THE USER MUST CODE A	:
;       ROUTINE AND PLACE THE CORRECT ADDRESS IN THE VECTOR TABLE.	:
; -----------------------------------------------------------------------------

TIMER_INT_1	PROC  FAR
	STI				; INTERRUPTS BACK ON
	PUSH	DS
	PUSH	AX
	PUSH	DX			; SAVE MACHINE STATE
	CALL	DDS			; ESTABLISH ADDRESSABILITY
	INC	@TIMER_LOW		; INCREMENT TIME
	JNZ	T4			; GO TO TEST DAY
	INC	@TIMER_HICH		; INCREMENT HIGH WORD OF TIME
T4:					; TEST DAY
	CMP	@TIMER_HIGH,018H	; TEST FOR COUNT EQUALING 24 HOURS
	JNZ	T5			; GO TO DISKETTE_CTL
	CMP	@TIMER_LOW,0B0H
	JNZ	T5			; GO TO DISKETTE_CTL

;-----	TIMER HAS GONE 24 HOURS
	SUB	AX,AX
	MOV	@TIMER_HIGH,AX
	MOV	@TIMER_LOW,AX
	MOV	@TIMER_OFL,1

;-----	TEST FOR DISKETTE TIME OUT
T5:
	DEC	@MOTOR_COUNT		; DECREMENT DISKETTE MOTOR CONTROL
	JNZ	T6			; RETURN IF COUNT NOT OUT
	AND	@MOTOR_STATUS,0F0H	; TURN OFF MOTOR RUNNING BITS
	MOV	AL,0CH
	MOV	DX,03F2H		; FDC CTL PORT
	OUT	DX,AL			; TURN OFF THE MOTOR
T6:					; TIMER TICK INTERRUPT
	INT	1CH			; TRANSFER CONTROL TO A USER ROUTINE

	POP	DX			; RESTORE (DX)
	MOV	AL,EOI			; GET END OF INTERRUPT MASK
	CLI				; DISABLE INTERRUPTS TILL STACK CLEARED
	OUT	INTA00,AL		; END OF INTERRUPT TO 8259 - 1
	POP	AX
	POP	DS			; RESET MACHINE STATE
	IRET				; RETURN FROM INTERRUPT

TIMER_INT_1	ENDP

The interrupt has the highest IRQL. Here ISR increases a timer, turns off the floppy disk motor if needed and sends a EOI command. But it also calls a software interrupt INT 1CH to “transfer control to a user routine”. By default INT 1CH is just a dummy routine:

    DW  OFFSET DUMMY_RETURN ; INT 1CH -- TIMER BREAK ADDRESS

which translates to a simple IRET:

;-----	DUMMY INTERRUPT HANDLER

;;-	ORG 	0FF53H
	ORG	01F53H
DUMMY_RETURN	EQU	$	; BIOS DUMMY (NULL) INTERRUPT RETURN

	IRET

So I think by installing a INT 1CH hook it should be possible to implement a (possibly slow) preemptive scheduler, obviously there is no protection and any process can trash memory or scheduler.