Skip to content

Instantly share code, notes, and snippets.

@pts
Created January 23, 2023 00:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pts/b9d7297a0ee80b6136cf59a52581e8c2 to your computer and use it in GitHub Desktop.
Save pts/b9d7297a0ee80b6136cf59a52581e8c2 to your computer and use it in GitHub Desktop.
hello86.nasm: hello-world program for Xenix 86 (and Xenix 286)
; by pts@fazekas.hu at Sun Jan 22 01:50:33 CET 2023
;
; Compile: nasm -O0 -f bin -o hello86 hello86.nasm
;
; !! This is experimental code for hello-world.
;
; Xenix 86 syscall ABI (reverse engineered):
;
; Call: mov ax, syscall_number; mov bx, arg1; mov cx, arg2; mov si, arg3; mov di, arg4; call section_text+0x17
; On error, sets CF=1, AX=errno (error code), destroys BX, CX, DX, SI, DI, BP, FLAGS.
; On success, sets CF=0, (AL, AX or BX:AX)=result, destroys AX, BX, CX, DX, SI, DI, BP, FLAGS, except for the result.
; !! How are argc, argv and envp passed?
;
; Xenix 86 libc C ABI (not used in this program):
;
; https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
; Caller pushes args in reverse order, caller pops args.
; Result in AL, AX or DX:AX, callee destroys AX, BX, CX, DX (except for result).
; Example: ssize_t write(int fd, const void *buf, size_t count);
; Call: push arg_count; arg_push buf; push arg_fd; call func_write; pop 3 words
;
;
bits 16
cpu 8086
;org ... ; Later.
XC:
.8086 equ 4
.hbfs equ 0x80 ; high byte first in short
.lwfl equ 0x40 ; low word first in long
XR: ; Relocation table format.
.RXOUT equ 0x00 ; x.out long form, linkable
.RXEXEC equ 0x10 ; x.out short form, executable
.RBOUT equ 0x20 ; b.out format
.RAOUT equ 0x30 ; a.out format
.R86REL equ 0x40 ; 8086 relocatable format
.R86ABS equ 0x50 ; 8086 absolute format
.R286ABS equ 0x60 ; 80286 absolute format. Why this?
.R286REL equ 0x70 ; 80286 relocatable format
XSTF: ; Symbol table format.
.SXOUT equ 0x00 ; trailing string, struct sym
.SBOUT equ 0x01 ; trailing string, struct bsym
.SAOUT equ 0x02 ; struct asym (nlist)
.S86REL equ 0x03 ; 8086 relocatable format
.S86ABS equ 0x04 ; 8086 absolute format
.SUCBVAX equ 0x05 ; separate string table
.S286ABS equ 0x06 ; 80286 absolute format
.S286REL equ 0x07 ; 80286 relocatable format
.SXSEG equ 0x08 ; segmented format
.SYM equ 0x0f ; symbol format mask
XE:
.OSXENIX equ 1
RENV:
.EXECUTABLE equ 1
.SEPARATE_I_D equ 2
.TEXT_PURE equ 4
.FS equ 8 ; Fixed stack.
; * o set if text overlay
; * d set if large model data
; * t set if large model text
; * f set if floating point hardware required
; * v set if virtual kernel module or shared library was (h) but this was never used.
; * i set if segment table contains iterated text/data
; * a set if absolute (set up for physical address)
.SEG equ 0x800 ; segmented x.out: segment table present.
.V1 equ 0x4000
.V2 equ 0x8000
.VUSE_OSVERS equ 0xc000
XST:
.TTEXT equ 1 ; text (code) segment.
.TDATA equ 2 ; data segment.
XS:
.AMEM equ 0x8000 ; segment represents a memory image.
.ABSS equ 0x0004 ; contains implicit bss.
.APURE equ 0x0008 ; read-only, may be shared.
; Executable-specific.
CODE_SEGMENT_NUMBER equ 0x3f
DATA_DELTA equ 2 ; Should be 0. How much is x_data larger than it should be.
xexec_header: ; Based on /usr/include/sys/a.out.h on Xenix 286 2.3.2b.
.x_magic dw 0x206 ; X_MAGIC magic number
.x_ext dw xext_header.end-xext_header ; size of header extension
.x_text dd section_text_end-section_text ; size of text segment (s)
.x_data dd section_data_end-section_data+DATA_DELTA ; size of initialized data (s)
.x_bss dd section_bss_end-section_bss-DATA_DELTA ; size of uninitialized data (s)
.x_syms dd 0 ; size of symbol table (s)
.x_reloc dd 0 ; size of relocation table (s)
.x_entry dd _start-section_text ; entry point
.x_cpu db XC.8086|XC.lwfl ; cpu type & byte/word order
.x_relsym db XR.R286ABS|XSTF.S86ABS ; relocation & symbol format (u) TODO(pts): Why not XR.RXEXEC etc.? Would it be shorter? It doesn't matter, the file doesn't have symbols or relocations.
.x_renv dw RENV.EXECUTABLE|RENV.SEPARATE_I_D|RENV.TEXT_PURE|RENV.FS|RENV.SEG|RENV.VUSE_OSVERS ; run-time environment
xext_header: ; Based on /usr/include/sys/a.out.h on Xenix 286 2.3.2b.
.xe_trsize dd 0 ; size of text relocation (s) unused
.xe_drsize dd 0 ; size of data relocation (s) unused
.xe_tbase dd 0 ; text relocation base (u) unused
.xe_dbase dd 0 ; data relocation base (u) unused
.xe_stksize dd 0x1000 ; stack size (if RENV.FS set)
.xe_segpos dd xseg0-xexec_header ; segment table position this and all following must be present if RENV.SEG is set
.xe_segsize dd xseg_end-xseg0 ; segment table size
.xe_mdtpos dd 0 ; machine dependent table position
.xe_mdtsize dd 0 ; machine dependent table size
.xe_mdttype db 0 ; machine dependent table type
.xe_pagesize db 0 ; file pagesize, in multiples of 512. 0 means unaligned (?).
.xe_ostype db XE.OSXENIX ; operating system type
.xe_osvers db 2 ; operating system version
.xe_eseg dw CODE_SEGMENT_NUMBER ; entry segment
.xe_sres dw 0 ; reserved
.end:
times 0x60-($-xexec_header) db 0 ; !! Can we do without it?
xseg0:
.xs_type dw XST.TTEXT ; segment type
.xs_attr dw XS.AMEM|XS.APURE ; segment attributes
.xs_seg dw CODE_SEGMENT_NUMBER ; segment number (arbitrary)
.xs_align db 0 ; log base 2 of alignment
.xs_cres db 0 ; unused
.xs_filpos dd section_text-xexec_header ; file position
.xs_psize dd section_text_end-section_text ; physical size (in file)
.xs_vsize dd section_text_end-section_text ; virtual size (in core), must be the same as xs_psize
.xs_rbase dd 0 ; relocation base address/offset
.xs_noff dw 0 ; segment name string table offset
.xs_sres dw 0 ; unused
.xs_lres dd 0 ; unused
xseg1:
.xs_type dw XST.TDATA ; segment type
.xs_attr dw XS.AMEM|XS.ABSS ; segment attributes
.xs_seg dw 0x47 ; segment number (arbitrary)
.xs_align db 0 ; log base 2 of alignment
.xs_cres db 0 ; unused
.xs_filpos dd section_data-xexec_header ; file position
.xs_psize dd section_data_end-section_data ; physical size (in file)
.xs_vsize dd section_data_end-section_data+section_bss_end-section_bss ; virtual size (in core)
.xs_rbase dd 0 ; relocation base address/offset
.xs_noff dw 0 ; segment name string table offset
.xs_sres dw 0 ; unused
.xs_lres dd 0 ; unused
xseg_end:
$SYSCALL: ; Xenix 86 syscall numbers.
.exit equ 0x1
.write equ 0x4
.sys0x6 equ 0x6
.sys0x14 equ 0x14
.sys0x25 equ 0x25
.sys0x36 equ 0x36
.sys0x828 equ 0x828
.sys0x1328 equ 0x1328
section_text:
..@0x00a0:
_start:
; !! Why is all this needed (between _start and _start2)?
..@t0x0000: jmp strict short _start2
syscall_part2:
..@t0x0002: jmp strict short syscall_part3
do_syscall_sys0x828:
..@t0x0004: jmp strict short proc0x14
proc0x6_unused:
..@t0x0006: jmp strict short proc0x14
proc0x8_unused:
..@t0x0008: jmp strict short $ ; Infinite loop.
..@t0x000a: jmp strict short $ ; Infinite loop.
..@t0x000c: jmp strict short $ ; Infinite loop.
..@t0x000e: jmp strict short $ ; Infinite loop.
..@t0x0010: jmp strict short $ ; Infinite loop.
..@t0x0012: jmp strict short $ ; Infinite loop.
proc0x14: ; Only called from do_syscall_sys0x828.
..@t0x0014: mov ax, $SYSCALL.sys0x828
syscall_part3:
..@t0x0017: int 5 ; Xenix 86 syscall.
..@t0x0019: jmp strict short .ret
..@t0x001b: times 0x7f-0x1b db 0 ; Why?
.ret:
..@t0x007f: ret
_start2:
..@t0x0080:
; Experimental hello-world code. !!! It works (both write(2) and exit(2)) on the Xenix 286 2.3.2b installer floppy (n1.img).
mov ax, $SYSCALL.write
mov bx, 1 ; STDOUT_FILENO.
mov cx, msg
mov si, msg.end-msg
call syscall_part3 ; !! syscall_part2 and syscall_part3 work, syscall_part3b fails with Memory fault ; Xenix 86 syscall.
mov ax, $SYSCALL.exit
xor bx, bx ; EXIT_SUCCESS.
call syscall_part3 ; !! syscall_part2 and syscall_part3 work, syscall_part3b fails with Memory fault ; Xenix 86 syscall.
; Not reached.
syscall_part3b: int 5
ret
section_text_end:
org xexec_header-$
section_data:
db 0 ; Without this, the address of msg would be 0, and $SYSCALL.write doesn't allow that.
msg db 'Hello, World!', 10
.end:
section_data_end:
absolute $
section_bss: ; Must follow section_data.
;resb 2
section_bss_end:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment