Skip to content

Instantly share code, notes, and snippets.

@yottatsa
Last active January 9, 2024 13:23
Show Gist options
  • Save yottatsa/0f1daa5a5f5c7199096dbc1d0e454ef1 to your computer and use it in GitHub Desktop.
Save yottatsa/0f1daa5a5f5c7199096dbc1d0e454ef1 to your computer and use it in GitHub Desktop.
PCP/M-86 loader for Apricot
; nasm bootcpm.com.asm -fbin -o bootcpm.com
; runs from dos to load the bootloader from drive 0
CPU 8086
USE16
disklabeloff equ 0x040a
disklabelseg equ 0x040c
bootfrom equ 0x0408
boot_locn equ 0x1a
boot_size equ 0x1e
section .text start=0x100
xor ax, ax
mov es, ax ; bios data
mov word es:[bootfrom], 0
mov ax, ds
mov [bufseg], ax
mov es:[disklabelseg], ax
mov si, disklabel
mov es:[disklabeloff], si
mov [bufoff], si
mov word [sector], 0
mov word [sec_n], 1
call diskio
mov ax, disklabel
shr ax, 1
shr ax, 1
shr ax, 1
shr ax, 1
mov bx, ds
add ax, bx
mov es, ax
mov si, loader
mov [bufoff], si
mov ax, word es:boot_locn
mov word [sector], ax
mov ax, es:boot_size
mov word [sec_n], ax
call diskio
mov ax, loader
shr ax, 1
shr ax, 1
shr ax, 1
shr ax, 1
mov bx, ds
add ax, bx
add ax, 8
push ax
mov es, ax
; live-patching LOADER.SYS with new CPM3.SYS location
mov ax, cpm3
shr ax, 1
shr ax, 1
shr ax, 1
shr ax, 1
mov bx, ds
add ax, bx
mov es:[0xc8], ax
mov es:[0xec], ax
mov es:[0xef], ax
add ax, 0x8
mov es:[0x121], ax
mov es:[0x267], ax
xor ax, ax
push ax
retf
diskio:
push bx
push cx
mov bx, 0x39
mov cx, 0x0b
mov dx, ds
mov si, diskio_s
int 0xfc
pop cx
pop bx
or ax, ax
ret
diskio_s:
drvn: dw 0
op: dw 0
sector: dw 0
sec_n: dw 0
bufoff: dw 0
bufseg: dw 0
section .data align=0x10
disklabel:
loader equ disklabel + 0x200
cpm3 equ loader + 0x600
;
; ╔═════════════════════════════════════════════════════════════════════════╗
; ║ This file is generated by The Interactive Disassembler (IDA) ║
; ║ Copyright (c) 2000 by DataRescue sa/nv, <ida@datarescue.com> ║
; ╚═════════════════════════════════════════════════════════════════════════╝
;
; LOADER.SYS from http://actapricot.org/disks/aprid5ks.htm#apr00301.dsk
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Regular
interrupt segment byte private ''
assume cs:interrupt
assume es:nothing, ss:nothing, ds:nothing
db 380h dup(?)
intE0off dw ? ;DATA XREF: _cpmentry+D↓w
; _cpmentry+87↓w ...
intE0seg dw ? ;DATA XREF: _cpmentry+13↓w
; _cpmentry+8D↓w
db 6Ch dup(?)
BIOSCtlDevInt dw ? ;DATA XREF: cpmcode:61BE↓r
; cpmcode:61F3↓r ...
dw ?
dd ?
ExecInt dd ? ;DATA XREF: cpmcode:2BA0↓r
dd ?
interrupt ends
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Regular
bios segment byte private ''
assume cs:bios
;org 400h
assume es:nothing, ss:nothing, ds:nothing
db 7
public machinetype
machinetype db 0 ;DATA XREF: sub_158CE+2D↓r _entry+E↓r
public ramsize
ramsize dw 4000h ;DATA XREF: _entry+13↓r
dd 56781234h
public bootfrom
bootfrom dw 0 ;DATA XREF: read_disk+D↓r
public disklabelptr
disklabelptr dw 0B0h ;DATA XREF: _entry+1C↓r _entry+7D↓r ...
dw 3E00h
dd 0
db 0
db 0
db 0
db 0
public n_flphdd
n_flphdd dw 0 ;DATA XREF: _entry+12F↓r _entry+14C↓r ...
floppybpbptr dd 0
dd 0
dw 7FFFh
public usercodearea
usercodearea dw 0 ;DATA XREF: _entry+1CC↓r _entry+216↓r
dd 0
db 0D8h dup(0)
dq 0FD100F040003h
dq 0
db 0F0h dup(0)
db 0F9h dup(0)
dd 0
dw 0
afontptroff dw 0 ;DATA XREF: _entry+5E↓w
afontptrseg dw 0 ;DATA XREF: _entry+69↓w
dw 0
mfontptroff dw 0 ;DATA XREF: _entry+62↓w
mfontptrseg dw 0 ;DATA XREF: _entry+6D↓w
dw 0
keytabptroff dw 0 ;DATA XREF: _entry+A6↓w
keytabptrseg dw 0 ;DATA XREF: _entry+AA↓w
dw 0
dd 0
dw 0
dd 0
dd 0
dd 0
dd 0
dd 0
db 0CEh dup(0)
bios ends
; File Name : CPM3.SYS
;
; included for the reference
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Pure code
cpmhead segment byte private 'C86CMDHEAD'
assume cs:cpmhead
assume es:nothing, ss:nothing, ds:nothing
cpmcodesize dw 88Ch ;DATA XREF: _entry+F3↓r
dw seg cpmcode
dw 88Ch
dw 0
cpmdatasize dw 65Ch ;DATA XREF: _entry+F7↓r
dw seg cpmdata
dw 723h
dw 0
db 6Eh dup(0)
cpmhead ends
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Pure code
cpmcode segment page private 'CODE'
assume cs:cpmcode
assume es:nothing, ss:nothing, ds:nothing
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
_cpmentry proc far
jmp loc_F088
; ───────────────────────────────────────────────────────────────────────────
jmp near ptr intE0
; ───────────────────────────────────────────────────────────────────────────
cpmdataseg dw seg cpmdata ;DATA XREF: _cpmentry+17↓r intE0+1↓r ...
; ───────────────────────────────────────────────────────────────────────────
loc_F088: ;CODE XREF: _cpmentry↑j
cli
xor ax, ax
mov ds, ax
assume ds:bios
mov intE0off, offset intE0
mov intE0seg, cs
mov bx, cs:cpmdataseg
mov ds, bx
assume ds:cpmdata
mov ax, stackseg
mov cl, 4
shr ax, cl
add ax, bx
mov stackseg, ax
mov word_17990, ax
mov word_1798A, ax
mov ss, ax
mov sp, 160h
loc_F0B5: ;CODE XREF: _cpmentry+53↓j
mov es, ax
mov word ptr es:70h, offset word_F225
mov es:72h, cs
mov ax, es:2
test ax, ax
jz loc_F0D5
shr ax, cl
add ax, bx
mov es:2, ax
jmp short loc_F0B5
; ───────────────────────────────────────────────────────────────────────────
loc_F0D5: ;CODE XREF: _cpmentry+49↑j
mov es, stackseg
mov es:54h, cs
cld
push dx
mov cx, 7
mov bx, 2
loc_F0E6: ;CODE XREF: _cpmentry+6F↓j
push bx
push cx
call word ptr [bx]
pop cx
pop bx
add bx, 4
loop loc_F0E6
pop dx
call dword ptr word_1796C
sti
mov al, byte_179D2
mov es:36h, al
mov byte_17AF6, al
push es
xor si, si
push ds
mov ds, si
assume ds:bios
mov intE0off, offset intE0
mov intE0seg, cs
mov di, 56h ; 'V'
mov cx, 4
repe movsw
mov cl, 4
add si, cx
repe movsw
mov si, 380h
mov cl, 4
repe movsw
pop es
pop ds
assume ds:nothing
mov di, 2B9h
mov si, 56h ; 'V'
mov cl, 0Ch
repe movsw
xor cx, cx
mov dx, cx
int 0E0h ; ; CP/M interupt?
retn
_cpmentry endp
; ───────────────────────────────────────────────────────────────────────────
word_F225 dw 0E9CBh ;DATA XREF: _cpmentry+37↑o
; ───────────────────────────────────────────────────────────────────────────
cpmcode ends
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Pure data
cpmdata segment byte private 'DATA'
assume cs:cpmdata
word_1796C dw 0 ;DATA XREF: _cpmentry+72↑r
off_17978 dd loc_FF31 ;DATA XREF: cpmcode:62B2↑r
off_1797C dd sub_1078B ;DATA XREF: cpmcode:6278↑r
word_1798A dw 8A0h ;DATA XREF: _cpmentry+2D↑w
stackseg dw 8A0h ;DATA XREF: _cpmentry+1E↑r
; _cpmentry+27↑w ...
word_17990 dw 0 ;DATA XREF: _cpmentry+2A↑w
word_17998 dw 10h ;DATA XREF: _entry+231↓r _entry+234↓w ...
word_1799A dw 1000h ;DATA XREF: _entry+22B↓r
byte_179D2 db 0 ;DATA XREF: _cpmentry+77↑r
byte_17AF6 db 0 ;DATA XREF: _cpmentry+7E↑w
word_17C10 dw 0 ;DATA XREF: read_disk+1F↓w
word_18888 dw 11F2h ;DATA XREF: cpmcode:6270↑r
byte_18B9A db 0 ;DATA XREF: cpmcode:6284↑w
cpmdata ends
; File Name : LOADER.SYS
; Format : Binary File
; Base Address: 3E00h Range: 3E000h - 3E580h Loaded length: 0580h
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Regular
head segment byte public 'C86CMDHEAD'
assume cs:head
assume es:nothing, ss:nothing, ds:nothing
db 1
dw 3Eh
dw 0 ; relocatable
dw 3Eh
dw 0
db 2
dw 10h
dw 0 ; relocatable
dw 10h
dw 0
db 6Eh dup(0)
head ends
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Regular
code segment page public ''
assume cs:code
assume es:nothing, ss:nothing, ds:nothing
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
; Attributes: noreturn static
public _entry
_entry proc far
mov ax, cs
mov ds, ax
assume ds:stack
mov ss, ax
assume ss:stack
mov sp, offset stacktop
mov ax, 0
mov es, ax
assume es:bios
mov dl, es:machinetype
mov bx, es:ramsize
mov temp, bx
les di, dword ptr es:disklabelptr
assume es:nothing
mov ax, es:[di+disklabel.CNF_FONT_sec]
or ax, ax
jz loadkeys
mov sector, ax
cmp dl, 0 ; if machine type is Xi
jz xifont
add sector, 10h
mov sec_n, 9
sub bx, 120h
mov bufseg, bx
jmp short loadfont
; ───────────────────────────────────────────────────────────────────────────
xifont: ;CODE XREF: _entry+30↑j
mov bufseg, 80h
mov sec_n, 10h
loadfont: ;CODE XREF: _entry+45↑j
call read_disk
or ax, ax
jnz loadkeys
xor ax, ax
mov es, ax
assume es:bios
mov es:afontptroff, ax
mov es:mfontptroff, ax
mov ax, bufseg
mov es:afontptrseg, ax
mov es:mfontptrseg, ax
mov temp, bx
loadkeys: ;CODE XREF: _entry+28↑j _entry+58↑j
xor ax, ax
mov es, ax
mov bx, temp
les di, dword ptr es:disklabelptr
assume es:nothing
mov ax, es:[di+disklabel.CNF_KEYS_sec]
or ax, ax
jz loadcpmhead
mov sector, ax
mov sec_n, 2
sub bx, 40h ; 2 sectors from the end of RAM
mov bufseg, bx
call read_disk
or ax, ax
jnz loadcpmhead
xor ax, ax
mov es, ax
assume es:bios
mov es:keytabptroff, ax
mov es:keytabptrseg, bx
mov temp, bx
loadcpmhead: ;CODE XREF: _entry+89↑j _entry+A0↑j
xor ax, ax
mov es, ax
les di, dword ptr es:disklabelptr
mov ax, es:[di+disklabel.CNF_DOS_sec]
mov sector, ax
mov bufseg, seg cpmhead
mov sec_n, 1
call read_disk
or ax, ax
jz $+2
xor ax, ax
mov es, ax
les di, dword ptr es:disklabelptr
assume es:cpmhead
mov ax, es:[di+8Ah]
mov sector, ax
mov bufseg, seg cpmhead
mov ax, seg cpmhead
mov es, ax
mov ax, es:cpmcodesize
add ax, es:cpmdatasize
shr ax, 1
shr ax, 1
shr ax, 1
shr ax, 1
shr ax, 1
inc ax
inc ax
mov sec_n, ax
call read_disk
or ax, ax
jz $+2 ; delay after reading the disk
mov si, 0F08h
cmp sec_n, 80h
jb fillfdds ; si=0C18h if CPM3.SYS > 80 sectors
mov si, 0C18h
fillfdds: ;CODE XREF: _entry+11B↑j
mov ax, seg cpmcode
mov es, ax
assume es:cpmcode
mov ax, es:cpmdataseg
mov ds, ax
assume ds:cpmdata
xor ax, ax
mov es, ax
assume es:bios
mov ax, es:n_flphdd
test al, al ; if no floppies
jz fillhdds
mov bx, [si+4]
push ax
call sub_3E301
call sub_3E2EE
pop ax
cmp al, 1
jz fillhdds
mov bx, [si+6]
call sub_3E2EE
fillhdds: ;CODE XREF: _entry+135↑j _entry+144↑j
mov ax, es:n_flphdd
test ah, ah ; if no hdds
jz setfdd2
push ax
mov bx, [si]
call sub_3E301
call sub_3E2EE
mov ax, [bx+0Eh]
mov bx, [si+4]
mov [bx+0Eh], ax
mov bx, [si+6]
mov [bx+0Eh], ax
pop ax
cmp ah, 1
jz setfdd2
mov bx, [si+2]
call sub_3E2EE
setfdd2: ;CODE XREF: _entry+152↑j _entry+170↑j
mov ax, es:n_flphdd
cmp al, 2 ; 2 or more floppies
jnb setfdd1
mov word ptr [si+6], 0
setfdd1: ;CODE XREF: _entry+17E↑j
cmp al, 1 ; 1 or more floppies
jnb sethdd2
mov word ptr [si+4], 0
sethdd2: ;CODE XREF: _entry+187↑j
cmp ah, 2 ; 2 or more hdds
jnb sethdd1
mov cx, [si+4]
mov [si+2], cx
mov cx, [si+6]
mov [si+4], cx
mov word ptr [si+6], 0
sethdd1: ;CODE XREF: _entry+191↑j
cmp ah, 1 ; 1 or more hdds
jnb loc_3E239
mov cx, [si+2]
mov [si], cx
mov cx, [si+4]
mov [si+2], cx
mov word ptr [si+4], 0
loc_3E239: ;CODE XREF: _entry+1A7↑j
cmp cs:sec_n, 80h
jnb loc_3E28D
mov si, 0F56h ; if CPM3.SYS < 80s
xor ax, ax
mov es, ax
mov cx, 7
cmp es:usercodearea, 500h
jnz loc_3E25B
dec cx
add si, 6
jmp short loc_3E26C
; ───────────────────────────────────────────────────────────────────────────
loc_3E25B: ;CODE XREF: _entry+1D3↑j
mov ax, [si+6]
mov [si], ax
mov ax, [si+8]
mov [si+2], ax
mov ax, [si+0Ah]
mov [si+4], ax
loc_3E26C: ;CODE XREF: _entry+1D9↑j
mov ax, cs:temp
sub ax, [si]
mov [si+2], ax
add si, 6
loc_3E278: ;CODE XREF: _entry+208↓j
mov word ptr [si], 0
mov word ptr [si+2], 0
mov byte ptr [si+4], 0FFh
add si, 6
loop loc_3E278
jmp boot
; ───────────────────────────────────────────────────────────────────────────
loc_3E28D: ;CODE XREF: _entry+1C0↑j
xor ax, ax ; if CPM3.SYS >= 80s
mov es, ax
mov cx, cs:temp
mov dx, es:usercodearea
mov bx, 5Ah
loc_3E29E: ;CODE XREF: _entry+229↓j
mov si, bx
mov bx, [bx]
test bx, bx
jz loc_3E2AB
cmp dx, [bx+2]
ja loc_3E29E
loc_3E2AB: ;CODE XREF: _entry+224↑j
cmp word_1799A, bx
jz loc_3E2BA
mov ax, word_17998
mov word_17998, bx
mov [si], ax
loc_3E2BA: ;CODE XREF: _entry+22F↑j
mov bx, 5Ah
loc_3E2BD: ;CODE XREF: _entry+24D↓j
mov si, bx
mov bx, [bx]
test bx, bx
jz boot
mov ax, [bx+2]
add ax, [bx+4]
cmp ax, cx
jbe loc_3E2BD
mov word ptr [si], 0
mov si, bx
loc_3E2D5: ;CODE XREF: _entry+25B↓j
mov di, bx
mov bx, [bx]
test bx, bx
jnz loc_3E2D5
mov ax, word_17998
mov word_17998, si
mov [di], ax
boot: ;CODE XREF: _entry+20A↑j _entry+243↑j
mov ax, seg cpmcode
push ax
xor ax, ax
push ax
retf
_entry endp ; sp = -4
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
sub_3E2EE proc near ;CODE XREF: _entry+13E↑p _entry+149↑p ...
push si
mov dx, cs:temp
sub dx, [bx+12h]
mov [bx+12h], dx
mov cs:temp, dx
pop si
retn
sub_3E2EE endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
sub_3E301 proc near ;CODE XREF: _entry+13B↑p _entry+157↑p
push si
mov si, [bx+10h]
mov si, [si]
mov dx, cs:temp
loc_3E30C: ;CODE XREF: sub_3E301+16↓j
sub dx, [si+0Ah]
mov [si+0Ah], dx
mov si, [si+0Ch]
or si, si
jnz loc_3E30C
mov cs:temp, dx
pop si
retn
sub_3E301 endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
read_disk proc near ;CODE XREF: _entry+53↑p _entry+9B↑p ...
push bx
push cx
push es
mov bx, 39h ; DEV: FDD
mov cx, 0Bh ; COM: READ
xor ax, ax
mov es, ax
mov ax, es:bootfrom
cmp ax, 2 ; if boot from id>=2, means hdd
jb loc_3E33F
sub ax, 2
mov bx, 40h ; DEV: HDD
mov cx, 5 ; COM: READ
loc_3E33F: ;CODE XREF: read_disk+14↑j
mov word_17C10, ax
mov dx, ds
mov si, offset drive_no
int 0FCh
pop es
assume es:nothing
pop cx
pop bx
or ax, ax
retn
read_disk endp
; ───────────────────────────────────────────────────────────────────────────
db 0
drive_no dw 0 ;DATA XREF: read_disk+24↑o
dw 0
sector dw 0 ;DATA XREF: _entry+2A↑w _entry+32↑w ...
sec_n dw 0 ;DATA XREF: _entry+37↑w _entry+4D↑w ...
dw 0
bufseg dw seg bios ;DATA XREF: _entry+41↑w _entry+47↑w ...
temp dw 0 ;DATA XREF: _entry+18↑w _entry+71↑w ...
code ends
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Regular
stack segment byte public ''
assume cs:stack
;org 2DEh
assume es:nothing, ss:nothing, ds:nothing
db 100h dup(0)
stacktop db 0 ; ;DATA XREF: _entry+6↑o
db 121h dup(0)
stack ends
end
@yottatsa
Copy link
Author

yottatsa commented Jan 8, 2024

traced up until LOADER.SYS:code:0129

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment