Skip to content

Commit

Permalink
Update the README and comments with detailed compatibility notes
Browse files Browse the repository at this point in the history
I looked at every interrupt used and noted which PC models supported it.
Turns out it's closer than I thought to working on the original 5150 PC.

I learned a lot about the history of PC models today.

Also
 - Refactored the BIOS_PRINT_STRING constant in the bootsector to not
   include the al setting for what to do with the cursor
 - Fixed some spelling mistakes
  • Loading branch information
fsmv committed Sep 9, 2024
1 parent 830ce0e commit 4400a8d
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 20 deletions.
30 changes: 21 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,40 @@ exactly that.

## Compatibility

This code will work on any x86 CPU made between 2020 and 1981 with EGA support.
So any modern x86 PC with BIOS support. I believe the earliest machine it would
work on is the original 1981 IBM PC with a 1984 EGA card expansion.
This code should run correctly on any x86 PC compatible machine made between
1984 and 2020. I believe the earilest machine that will run it is the IBM PC AT
with the EGA card expansion released in October 1984.

If it doesn't boot on your machine that supports BIOS, please let me know. I'd
love to find out why.

### Compatibility details

This code is 16 bit real mode x86 assembly which Intel has kept available on all
x86 CPUs since the original 8086 processor from 1976.
x86 CPUs since the original 8086 processor from 1976. The assembly code itself
is compatible with the 8086.

It also depends on the IBM PC BIOS hardware interface standard from 1981 which
modern computers still implement. Sadly Intel has partially ended this
It also depends on the IBM PC BIOS hardware interface standard started in 1981
which modern computers still implement. Sadly Intel has partially ended this
incredible nealy 40 year backwards compatibility story by officially
[ending support for BIOS](https://www.bleepingcomputer.com/news/hardware/intel-plans-to-end-legacy-bios-support-by-2020/)
as of 2020, so now there are some machines on the market that only support UEFI
booting.

Finally it depends on EGA, the Enhanced Graphics Adapter card API, because it
uses the 80x25 16 colors text video mode which was added with that card. This is
the predicessor to CGA and VGA.
The latest BIOS call used here is int 0x10 with ah = 0x13 for printing a string.
This was introduced with PC AT although the earlier XT machines got this feature
later with a BIOS update. Additionally it uses BIOS functions to set the
overscan color and blink bit behavior which where added with the EGA card, so it
is technically not compatible with the CGA card although it will mostly work.

If we had a replacement for BIOS print string based on the print character
interrupts, technically it could be compatible with the original PC from 1981.
However the lisp interpreter code is more than the 8 sectors per track the PC
supported and the bootsector code expects to have multi-track read support which
the PC didn't have originially (maybe you could get a BIOS that does have it).

Additionally the text editor requires 256k of RAM which was the maximum possible
PC configuration.

## Why?

Expand Down
42 changes: 36 additions & 6 deletions bootloader/bootsect.asm
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,26 @@
; check here because nasm requires that to go after NUM_EXTRA_SECTORS is
; defined.

%define BIOS_PRINT_STRING 0x1301
%define BIOS_PRINT_CHAR 0x0E
; NOTE: The write string BIOS call only works for:
; PC XT BIOS dated 1/10/86 and after, AT, EGA, PC Convertible,
; and Personal System/2 products
%define BIOS_PRINT_STRING 0x13
%define BIOS_PRINT_CHAR 0x0E ; Works on the original PC
%define QEMU_DEBUG_PORT 0xE9

%include "bootloader/bootsect-header.asm"

mov [BOOT_DISK], dl ; Save the boot disk number

; NOTE: the PC with CGA card has int 0x10 ah = 0 to 0xF the rest are ignored

; TODO: Do some sort of video mode detection. Error if only mono is supported
; unless it turns out the attribute bytes are somehow compatible.
; It may not be necessary to do the full detection procedure although it would
; be nice to know if we have EGA support at least.

; Set video mode, 16 color 80x25 chars
; This is supported by CGA cards and PCjr
;
; The IBM BIOS manual describes a long procedure for determining which video
; modes are supported and all the possible options for supporting both mono and
Expand All @@ -49,13 +60,16 @@ mov ax, 0x0003
int 0x10

; Make the 8th bit of the colors bg-intensity instead of blink
; This works on EGA and VGA
; TODO: support doing the out port address for CGA and detect CGA
mov ax, 0x1003
mov bl, 0
int 0x10

; Make the 4th bit of the colors fg-intensity instead of font selection
; Use block 0 for the font
; Apparently this is the default in VGA so maybe we don't need it
; Use block 0 for the font.
; This means we only have 256 characters instead of 512.
; Apparently this is the default in EGA and VGA so maybe we don't need it
mov ax, 0x1103
mov bl, 0
int 0x10
Expand All @@ -68,6 +82,8 @@ mov cx, 0x0607
int 0x10

; Set the keyboard repeat speed
; For AT BIOS dated 11/15/85 and after, PC XT Model 286, and Personal System/2 products
; The original PC ignores this
mov ax, 0x0305
;mov bx, 0x0107 ; 500 ms delay before repeat ; 16 characters per second
mov bx, 0x0100 ; 500 ms delay before repeat ; 30 characters per second
Expand All @@ -81,9 +97,14 @@ mov al, NUM_EXTRA_SECTORS ; number of sectors to read
test al, al
je start_

; TODO: do the proper thing and detect the max sectors per track and don't
; assume multi-track read support.

; Load the code from the extra sectors
; Try to do the full read every time to take advantage of possible BIOS support
; for multi-track reads if NUM_EXTRA_SECTORS > 63
;
; This worked on the original PC, but the max sectors to read was 8 (max track was 39, head 1)
mov ah, 0x02
mov bx, SECTOR_SIZE ; es:bx is address to write to. es = cs, so write directly after the boot sector
mov dl, [BOOT_DISK] ; Drive number
Expand All @@ -96,8 +117,13 @@ jnc start_ ; if there was no error, jump to the loaded user code

push ax ; push the error code

; TODO: maybe make a print_string function that can also write to QEMU
; maybe I could even have a fallback for if ah = 13 isn't available, but I'm not
; totally sure how to detect. Maybe just always replace it for CGA cards.

; Print the error message
mov ax, 0x1301 ; Write String, move cursor mode in al
mov ah, BIOS_PRINT_STRING
mov al, 1 ; no attr bytes, move cursor
mov bp, disk_error_msg ; String pointer in es:bp (es is at code start from bootsect-header.asm)
mov cx, disk_error_msg_len ; String length
xor dx, dx ; top left
Expand All @@ -107,7 +133,8 @@ int 0x10
pop cx ; pop the error code
call print_hex ; print the error code

mov ax, 0x1301 ; Write String, move cursor mode in al
mov ah, BIOS_PRINT_STRING
mov al, 1 ; no attr bytes, move cursor
mov bp, retry_msg ; String pointer in es:bp (es is at code start from bootsect-header.asm)
mov cx, retry_msg_len ; String length
mov dx, 0x0100 ; second line; left edge
Expand All @@ -121,6 +148,7 @@ cmp byte [READ_RETRIES], 0
je .no_more_tries

; Reset the disk and retry
; Works on the original PC
dec byte [READ_RETRIES]
xor ax, ax
mov dl, [BOOT_DISK] ; Drive number
Expand All @@ -134,6 +162,7 @@ jmp $ ; stop forever
; Only accepts 0x0 <= al <= 0xF, anything else is garbage output
; e.g. al = 12 prints "C"
; clobbers ax, and bx
; Works on the original PC
print_hex_char:
mov ah, BIOS_PRINT_CHAR ; Scrolling teletype BIOS routine (used with int 0x10)
xor bx, bx ; Clear bx. bh = page, bl = color
Expand All @@ -159,6 +188,7 @@ print_hex_char:

; cx = two bytes to write at current cursor
; clobbers ax, and bx
; Works on the original PC
print_hex:
; Nibble 0 (most significant)
mov al, ch
Expand Down
16 changes: 11 additions & 5 deletions text-editor.asm
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
%define END_ROW 0x16
%define END_COL 0x4B

; TODO: detect the RAM and give an error if we don't have 256k which is 0x40000
; which is exactly the 4 segments we use (the 4th is the code segment)

; Segment register value (so the actual start is at the address 0x10*this)
; This is the first sector after the editor's code
;
Expand Down Expand Up @@ -76,6 +79,7 @@
; configured gdb to breakpoint
start_:

; TODO: CGA support
; Set the overscan color to the border color
mov ax, 0x1001
mov bh, OVERSCAN_COLOR
Expand Down Expand Up @@ -103,7 +107,7 @@ int 0x10

; Memory map:
;
; Each segment gets a full non-overlaping 64k block of memory and we don't move
; Each segment gets a full non-overlapping 64k block of memory and we don't move
; the segment so 64k is the limit for each part.
;
; SS: 0x050
Expand All @@ -117,7 +121,7 @@ int 0x10
; - Extra memory passed to the plugin program that runs the code you typed.
; Only set when ctrl+D is pressed and passed to the plugin.
; - Normally the default for memory address reads and our bootloader leaves
; this set to CODE_SEGMENT for that. But this code alwasy uses cs: to
; this set to CODE_SEGMENT for that. But this code always uses cs: to
; reference strings stored in the binary.

; --- typing_loop global register variables ---
Expand All @@ -133,7 +137,7 @@ int 0x10
; Note: We must have at least one char of gap i.e. si-di >= 1 at all times
; (because we use it as scratch space when printing sometimes)
;
; Additonally cx,bp are callee save while ax,bx are caller save (clobbered)
; Additionally cx,bp are callee save while ax,bx are caller save (clobbered)
mov ax, USER_CODE_LOC
mov es, ax
mov ax, COMPILER_DATA_LOC
Expand Down Expand Up @@ -1395,7 +1399,8 @@ print_line:
; dx is the cursor position to print at
xor bh, bh ; page number 0
mov bl, MAIN_COLOR
mov ax, 0x1301 ; Write String, with moving the cursor
mov ah, BIOS_PRINT_STRING ; Write String
mov al, 1 ; no attr bytes, move the cursor
int 0x10
ret

Expand Down Expand Up @@ -1608,7 +1613,8 @@ print_error:
mov dx, MAIN_TOP_LEFT-0x0100 ; one line above the top left corner
xor bh, bh ; page number 0
mov bl, ERROR_COLOR
mov ax, 0x1301 ; write string without moving the cursor
mov ah, BIOS_PRINT_STRING
mov al, 0 ; no attr bytes, don't move the cursor
int 0x10

; reset the es segment
Expand Down

0 comments on commit 4400a8d

Please sign in to comment.