--- /dev/null
+# $Source$
+# $State$
+
+The pc86 platform
+=================
+
+pc86 is an i86-based BSP that produces bootable floppy disk images that can
+be run on most PCs. It is intended to be quick and dirty rather than actually
+useful, although it may come in handy for hardware test purposes, boot
+loaders, and the like.
+
+The code runs in TINY mode, where CS, DS and SS all share the same segment.
+This means that there's not very much memory available. It would be very easy
+to change it to run in SMALL mode, where CS occupies one segment and DS and SS
+another, which would give 64kB for nearly all programs; I just haven't done it.
+
+This port uses a syscall interface, which means you get *all* of the ACK's
+builtin libc with no shortcuts. File descriptors 0, 1 and 2 represent the
+console. All reads block. There's enough TTY emulation to allow \n conversion
+(set RAW to turn off) and local echo (unset ECHO to turn off); this is needed
+to make Basic work.
+
+Language support
+================
+
+Tested with C (both), Basic, Pascal, Occam, Modula-2.
+
+Basic works, but because Basic programs require access to a data file to work,
+and pc86 doesn't (of course) have a file system, programs won't start unless
+you hack the compiler not to try and open it.
+
+Example command line
+====================
+
+ack -mpc86 -O -ansi -o pc86.img test.c
+
+The file pc86.img can then be copied onto a floppy and booted, or run via qemu
+or somesuch emulator.
+
+David Given
+dg@cowlark.com
--- /dev/null
+#
+! $Source$
+! $State$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+.sect .text
+
+! Some definitions.
+
+BOOT_SEGMENT = 0x07C0 ! Where we've been loaded
+
+#define PRINT(N) push ax; push bx; movb ah, 0x0E; movb al, N; mov bx, 0x0007; int 0x10; pop bx; pop ax
+
+begtext:
+ ! This code makes up the PC boot sector, and is the first thing on the
+ ! floppy disk. The PC will load this sector to 0x07C0:0000 and jump to it
+ ! with dl set to our drive, but it won't necessarily do it in a sane way
+ ! (some BIOSes jump to 0x0000:7C00 instead). So, we need to fix that.
+
+ jmpf BOOT_SEGMENT : start2
+start2:
+ ! Set up the segment descriptors. We're running in tiny mode, so it's just
+ ! a matter of copying the contents of cs (already been set up by the jmpf)
+ ! into the other registers.
+
+ mov ax, cs
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+
+ ! Initialise the stack, which will start at the top of our segment and work
+ ! down.
+
+ mov sp, 0 ! the first push will wrap round to 0xFFFF
+
+ ! Some more startup housekeeping.
+
+ sti
+ cld
+
+ ! We're now set up for actual code. Write out our banner. Remember that
+ ! at this point dl contains our drive number, which we want to keep.
+
+ mov si, banner_msg
+ call write_string
+
+ ! Probe the drive to figure out its geometry.
+
+ push dx
+ mov ax, 0x0800 ! service number
+ int 0x13
+ pop ax
+ jc cant_boot
+
+ ! At this point:
+ ! al: current drive
+ ! cl: maximum sector number (bottom six bits)
+ ! dh: maximum head number
+ ! We don't care about the rest.
+ andb cl, 0x3F
+
+ ! We now need to go through a loop loading each sector in turn.
+ ! During this loop, the registers will be set up as follows:
+ ! al: current cylinder
+ ! ah: maximum head
+ ! bx: address
+ ! cl: current sector (one based)
+ ! ch: maximum sector (one based)
+ ! dl: current drive
+ ! dh: current head
+ ! Why, yes, they are painstakingly shoehorned in to get all the data
+ ! into registers.
+
+ movb dl, al
+ movb ch, cl
+ movb ah, dh
+ movb al, 0 ! start on cylinder 0
+ mov bx, 0x0200 ! don't overwrite boot sector
+ movb cl, 2 ! start on sector 2 (skip boot sector)
+ movb dh, 0 ! start on head 0
+
+1:
+ call read_sector
+
+ ! Next memory area.
+
+ add bx, 0x0200
+ cmp bx, enddata
+ ja finished
+
+ ! Next sector.
+
+ incb cl
+ cmpb cl, ch
+ jle 1b
+ movb cl, 1 ! back to sector 1 again
+
+ ! Next head.
+
+ incb dh
+ cmpb dh, ah
+ jle 1b
+ movb dh, 0 ! back to head 1 again
+
+ ! Next cylinder.
+
+ incb al
+ jmp 1b
+
+cant_boot:
+ mov si, bootfail_msg
+ call write_string
+ jmp EXIT
+
+ ! Reads a sector into memory. The parameters are:
+ ! al: cylinder
+ ! bx: address
+ ! cl: sector
+ ! dl: drive
+ ! dh: head
+ ! If an error occurs, it'll automatically try again. And again.
+ ! And again...
+
+read_sector:
+ push ax
+ push bx
+ push cx
+ push dx
+
+#if 0
+ push dx
+ xorb dh, dh
+ movb dl, cl
+ call write_hex4
+ pop dx
+ PRINT(0x20)
+ push dx
+ movb dl, dh
+ xorb dh, dh
+ call write_hex4
+ pop dx
+ PRINT(0x20)
+ push dx
+ movb dl, al
+ xorb dh, dh
+ call write_hex4
+ pop dx
+#endif
+
+1:
+ movb ch, al
+ mov ax, 0x0201 ! service 2, read one sector
+ int 0x13
+ jc 2f
+
+ mov ax, 0x0E2E ! write out a .
+ mov bx, 0x0007 ! page 0, white
+ int 0x10
+
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+ ! If a read fail occurs, the spec (such as it is) states that we need
+ ! to reset the fd controller and try again.
+2:
+ push ax
+ push bx
+
+ mov ax, 0x0E21 ! write out a !
+ mov bx, 0x0007 ! page 0, white
+ int 0x10
+
+ mov ax, 0x0000
+ int 0x13
+
+ pop bx
+ pop ax
+ jmp 1b
+
+ ! Waits for a keystroke (and then discards it).
+
+pause:
+ push ax
+ xorb ah, ah
+ int 0x16
+ pop ax
+ ret
+
+ ! This utility writes the string pointed to by ds:si out to the console.
+
+write_string:
+ push ax
+ push bx
+1:
+ lodsb
+ andb al, al
+ jz 2f
+ movb ah, 0xE ! service
+ mov bx, 0x0007 ! page 0, white
+ int 0x10
+ jmp 1b
+2:
+ pop bx
+ pop ax
+ ret
+
+ ! Writes out the contents of dx as hex.
+
+write_hex4:
+ push ax
+ push cx
+ mov cx, 4 ! 4 hex digits
+1:
+ rol dx, 1 ! rotate so that highest 4 bits are at the bottom
+ rol dx, 1
+ rol dx, 1
+ rol dx, 1
+ mov ax, 0xE0F ! ah = request, al = mask for nybble
+ andb al, dl
+ addb al, 0x90 ! convert al to ascii hex (four instructions)
+ daa
+ adcb al, 0x40
+ daa
+ int 0x10
+ loop 1b
+ pop cx
+ pop ax
+ ret
+
+ ! Everything loaded successfully!
+ !
+ ! We now need to do some setup and start the program itself.
+
+finished:
+ mov si, running_msg
+ call write_string
+ call pause
+
+ ! Wipe the bss. (I'm a little suprised that __m_a_i_n doesn't do this.)
+
+ mov di, begbss
+ mov cx, endbss
+ sub cx, di
+ mov ax, 0
+ rep stosb
+
+ ! Push standard parameters onto the stack and go.
+
+ mov ax, 1
+ push ax ! argc
+ mov ax, 2
+ push ax ! argc
+ mov ax, 3
+ push ax ! envp
+ call __m_a_i_n
+ ! fall through into the exit routine.
+
+ ! Halts, waits for a keypress, and reboots. This also becomes the
+ ! application termination routine.
+
+.define __exit
+.extern __exit
+.define EXIT
+.extern EXIT
+__exit:
+EXIT:
+ mov si, halted_msg
+ call write_string
+
+1:
+ jmp 1b
+
+ xor ax, ax
+ int 0x16 ! get key
+ int 0x19 ! reboot
+
+ ! Some text messages.
+
+banner_msg: .asciz 'ACKBOOT\n\r'
+nl_msg = banner_msg + 7 ! cheap trick
+
+bootfail_msg: .asciz 'Unable to boot!\n\r'
+loading_msg: .asciz '\n\rLoading...\n\r'
+halted_msg: .asciz '\n\rHalted.\n\r'
+running_msg: .asciz '\n\rRunning.\n\r'
+
+ ! ...and we need this to fool the PC into booting our boot sector.
+
+ .align 510
+ .data2 0xAA55
+
+
+.define begtext,begdata,begbss
+.define hol0,.trppc,.ignmask
+.define ERANGE,ESET,EHEAP,ECASE,EILLINS,EIDIVZ,EODDZ
+.extern _end
+
+.sect .data
+hol0:
+ .data2 0,0
+ .data2 0,0
+.ignmask:
+ .data2 0
+.trppc:
+ .data2 0
+
+! Define symbols at the beginning of our various segments, so that we can find
+! them. (Except .text, which has already been done.)
+
+.sect .data; begdata:
+.sect .rom; begrom:
+.sect .bss; begbss:
--- /dev/null
+# $Revision$
+var w=2
+var p=2
+var s=2
+var l=4
+var f=4
+var d=8
+var ARCH=i86
+var PLATFORM=pc86
+var PLATFORMDIR={EM}/lib/{PLATFORM}
+var CPP_F=-D__unix
+var ALIGN=-a0:1 -a1:1 -a2:1 -a3:1
+var C_LIB={PLATFORMDIR}/tail_cc.1s {PLATFORMDIR}/tail_cc.2g
+var OLD_C_LIB={C_LIB}
+var MACHOPT_F=-m8
+name be
+ from .m.g
+ to .s
+ program {EM}/lib.bin/{PLATFORM}/ncg
+ args <
+ stdout
+ need .e
+end
+name as
+ from .s.so
+ to .o
+ program {EM}/lib.bin/{PLATFORM}/as
+ args - -o > <
+ prep cond
+end
+name led
+ from .o.a
+ to .out
+ program {EM}/lib.bin/em_led
+ mapflag -l* LNAME={PLATFORMDIR}/tail_*
+ mapflag -i SEPID=-b1:0
+ mapflag -fp FLOATS={EM}/{ILIB}fp
+ mapflag -ansi C_LIB={PLATFORMDIR}/tail_ac
+ args {ALIGN} {SEPID?} \
+ (.e:{HEAD}={PLATFORMDIR}/boot.o) \
+ ({RTS}:.ocm.b={PLATFORMDIR}/head_cc) \
+ ({RTS}{ANSI?}:.c={PLATFORMDIR}/head_cc) \
+ ({RTS}{ANSI?}:.cansi={PLATFORMDIR}/head_ac) \
+ ({RTS}:.mod={PLATFORMDIR}/head_m2) \
+ ({RTS}:.p={PLATFORMDIR}/head_pc) \
+ -o > < \
+ (.p:{TAIL}={PLATFORMDIR}/tail_pc) \
+ (.b:{TAIL}={PLATFORMDIR}/tail_bc) \
+ (.mod:{TAIL}={PLATFORMDIR}/tail_m2) \
+ (.ocm:{TAIL}={PLATFORMDIR}/tail_ocm) \
+ (.ocm.b:{TAIL}={OLD_C_LIB}) \
+ (.c:{TAIL}={C_LIB}) \
+ {FLOATS?} \
+ (.e:{TAIL}={PLATFORMDIR}/libsys.a \
+ {PLATFORMDIR}/tail_mon \
+ {PLATFORMDIR}/libsys.a \
+ {PLATFORMDIR}/libem.a \
+ {PLATFORMDIR}/libend.a)
+ linker
+end
+name cv
+ from .out
+ to .img
+ program {EM}/bin/aslod
+ args < >
+ outfile pc86.img
+end
--- /dev/null
+#
+! $Source$
+! $State$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+! This file contains the code necessary to extend the ACK heap. This is called
+! by a i86/libem helper function called .strhp, which takes care of updating
+! some magic global variables --- defined here.
+
+! Pointer to the current top of the heap.
+
+.sect .data
+.define .reghp
+.reghp:
+ .data2 endbss
+
+! Pointer to the current top of memory.
+
+.sect .data
+.define .limhp
+.limhp:
+ .data2 endbss
+
+! Claims more memory from the system, but does not actually change those
+! global variables (.strhp does that). This does not use the C calling
+! convention!
+!
+! Stack: ( desired_limhp : actual_limhp )
+! Also returns: ax = -1 on failure
+
+.sect .text
+.define BRK
+BRK:
+ pop bx ! holds return address
+ pop ax ! holds desired limhp
+
+ cmp ax, sp ! compare sp with si
+ jae fail ! si too big? (Overlaps stack?)
+ cmp ax, endbss ! compare with bottom of heap
+ jb fail ! si too small? (Overlaps bss?)
+
+return:
+ push ax ! success
+ jmp bx
+
+fail:
+ mov ax, -1
+ jmp return
--- /dev/null
+#
+! $Source$
+! $State$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+.sect .bss
+
+.define __sys_params_in
+.comm __sys_params_in, 6
+
+.define __sys_params_out
+.comm __sys_params_out, 2
+
+.comm opcode, 2
+.comm returnto, 2
+
+.sect .text
+
+
+! Called on system call. This does *not* use the C calling convention:
+! ax: syscall number
+! stack: ( param3 param2 param1 - result )
+
+.define .mon
+.mon:
+ mov (opcode), ax
+ pop (returnto)
+
+ cmp ax, 1
+ je exit
+ cmp ax, 3
+ je read
+ cmp ax, 4
+ je write
+ cmp ax, 5
+ je open
+ cmp ax, 6
+ je nop_1 ! close
+ cmp ax, 20
+ je nop_0 ! getpid
+ cmp ax, 35
+ je nop_1 ! time
+ cmp ax, 48
+ je sigtrp
+ cmp ax, 54
+ je ioctl
+
+ ! Syscall not supported --- write out an error message and halt.
+
+unsupported:
+ mov si, msg
+1:
+ lodsb
+ andb al, al
+ jz 2f
+ movb ah, 0xE ! service
+ mov bx, 0x0007 ! page 0, white
+ int 0x10
+ jmp 1b
+2:
+
+ ! Write out the syscall number.
+
+ mov dx, (opcode)
+ mov cx, 4 ! 4 hex digits
+1:
+ rol dx, 1 ! rotate so that highest 4 bits are at the bottom
+ rol dx, 1
+ rol dx, 1
+ rol dx, 1
+ mov ax, 0xE0F ! ah = request, al = mask for nybble
+ andb al, dl
+ addb al, 0x90 ! convert al to ascii hex (four instructions)
+ daa
+ adcb al, 0x40
+ daa
+ int 0x10
+ loop 1b
+
+ ! Exit.
+
+ jmp EXIT
+
+.sect .rom
+msg:
+ .asciz 'NOSYS'
+
+.sect .text
+
+exit:
+ jmp EXIT
+
+read:
+ mov ax, __sys_read
+ jmp in_3_out_1
+
+write:
+ mov ax, __sys_write
+ jmp in_3_out_1
+
+open:
+ add sp, 2*2
+ jmp unimplemented
+
+ioctl:
+ mov ax, __sys_ioctl
+ jmp in_3_out_0
+
+sigtrp:
+ add sp, 4
+ jmp unimplemented
+
+in_3_out_0:
+ pop (__sys_params_in+0)
+ pop (__sys_params_in+2)
+ pop (__sys_params_in+4)
+ call ax
+ jmp out_0
+
+in_3_out_1:
+ pop (__sys_params_in+0)
+ pop (__sys_params_in+2)
+ pop (__sys_params_in+4)
+ call ax
+ jmp out_1
+
+out_0:
+ or ax, ax
+ jnz failed
+ push ax
+ jmp return
+
+out_1:
+ or ax, ax
+ jnz failed
+ push (__sys_params_out)
+ push ax
+ jmp return
+
+unimplemented:
+ mov ax, EBADMON
+failed:
+ push ax
+ push ax
+ jmp return
+
+nop_1:
+ add sp, 1*2
+ jmp nop_0
+nop_3:
+ add sp, 3*2
+nop_0:
+ mov ax, 0
+ push ax
+ jmp return
+
+return:
+ jmp (returnto)
+
\ No newline at end of file
--- /dev/null
+/* There should be a header for brk and sbrk, but there isn't. */
+
+#include <stdlib.h>
+#include <errno.h>
+/* #include <unistd.h> */
+
+extern char _end[];
+static char* brkpointer = _end;
+
+static void prints(char* s)
+{
+ for (;;)
+ {
+ char c = *s++;
+ if (!c)
+ break;
+ write(0, &c, 1);
+ }
+}
+
+static void printc(unsigned int n)
+{
+ char c;
+
+ n &= 0xF;
+ if (n < 10)
+ c = n + '0';
+ else
+ c = n + 'A' - 10;
+
+ write(0, &c, 1);
+}
+
+static void printh(unsigned int n)
+{
+ printc(n>>12);
+ printc(n>>8);
+ printc(n>>4);
+ printc(n);
+}
+
+static void waitforkey(void)
+{
+ char c;
+ read(1, &c, 1);
+}
+
+int _brk(char* newend)
+{
+ char dummy;
+
+ /* Ensure that newend is reasonable. */
+
+ if ((newend < _end) || (newend > (&dummy - 256)))
+ {
+ prints("[brk to ");
+ printh((unsigned int) newend);
+ prints(" failed]\n\r");
+ waitforkey();
+ errno = ENOMEM;
+ return -1;
+ }
+
+ prints("[brk to ");
+ printh((unsigned int) newend);
+ prints("]\n\r");
+ waitforkey();
+ brkpointer = newend;
+ return 0;
+}
+
+char* _sbrk(int delta)
+{
+ char* oldpointer = brkpointer;
+ prints("[sbrk delta ");
+ printh((unsigned int) delta);
+ prints(" from ");
+ printh((unsigned int) oldpointer);
+ prints("]\n\r");
+ printh((unsigned int) brkpointer);
+ prints(" ");
+ printh((unsigned int) _end);
+ if (_brk(oldpointer + delta) == -1)
+ return (char*)-1;
+
+ return oldpointer;
+}
--- /dev/null
+#include <stdlib.h>
+#include <errno.h>
+#include <sgtty.h>
+#include "libsys.h"
+
+int _sys_ttyflags = ECHO;
+
+extern struct
+{
+ int fd;
+ int request;
+ void* argp;
+} _sys_params_in;
+
+extern struct
+{
+ int result;
+} _sys_params_out;
+
+#define P _sys_params_in
+
+static int tiocgetp(void)
+{
+ struct sgttyb* s = P.argp;
+ s->sg_flags = _sys_ttyflags;
+ return 0;
+}
+
+static int tiocsetp(void)
+{
+ struct sgttyb* s = P.argp;
+ _sys_ttyflags = s->sg_flags;
+ return 0;
+}
+
+int _sys_ioctl(void)
+{
+ switch (P.request)
+ {
+ case TIOCGETP:
+ _sys_params_out.result = tiocgetp();
+ return 0;
+
+ case TIOCSETP:
+ _sys_params_out.result = tiocsetp();
+ return 0;
+ }
+
+ _sys_params_out.result = -1;
+ errno = EINVAL;
+ return 0;
+}
--- /dev/null
+#
+! $Source$
+! $State$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+.sect .text
+
+! Reads a single byte.
+
+.define __sys_rawread
+__sys_rawread:
+ xorb ah, ah
+ int 0x16
+ xorb ah, ah
+ ret
+
\ No newline at end of file
--- /dev/null
+#
+! $Source$
+! $State$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+.sect .text
+
+! Writes a single byte to the console.
+
+.define __sys_rawwrite
+.extern __sys_rawwrite
+
+__sys_rawwrite:
+ push bp
+ mov bp, sp
+
+ movb al, 4(bp)
+ movb ah, 0x0E
+ mov bx, 0x0007
+ int 0x10
+ jmp .cret
+
\ No newline at end of file
--- /dev/null
+#include <stdlib.h>
+#include <errno.h>
+#include <sgtty.h>
+#include "libsys.h"
+
+extern struct
+{
+ int fd;
+ char* buffer;
+ size_t count;
+} _sys_params_in;
+
+extern struct
+{
+ size_t bytesread;
+} _sys_params_out;
+
+#define P _sys_params_in
+
+int _sys_read(void)
+{
+ char i;
+
+ /* We're only allowed to read from fd 0, 1 or 2. */
+
+ if ((P.fd < 0) || (P.fd > 2))
+ return EBADF;
+
+ /* Empty buffer? */
+
+ if (P.count == 0)
+ {
+ _sys_params_out.bytesread = 0;
+ return 0;
+ }
+
+ /* Read one byte. */
+
+ i = _sys_rawread();
+ if ((i == '\r') && !(_sys_ttyflags & RAW))
+ i = '\n';
+ if (_sys_ttyflags & ECHO)
+ _sys_write_tty(i);
+
+ *P.buffer = i;
+
+ _sys_params_out.bytesread = 1;
+ return 0;
+}
--- /dev/null
+#include <stdlib.h>
+#include <errno.h>
+#include <sgtty.h>
+#include "libsys.h"
+
+extern struct
+{
+ int fd;
+ const char* buffer;
+ size_t count;
+} _sys_params_in;
+
+extern struct
+{
+ size_t byteswritten;
+} _sys_params_out;
+
+#define P _sys_params_in
+
+void _sys_write_tty(char c)
+{
+ _sys_rawwrite(c);
+ if ((c == '\n') && !(_sys_ttyflags & RAW))
+ _sys_rawwrite('\r');
+}
+
+int _sys_write(void)
+{
+ int i;
+
+ /* We're only allowed to write to fd 0, 1 or 2. */
+
+ if ((P.fd < 0) || (P.fd > 2))
+ return EBADF;
+
+ /* Write all data. */
+
+ i = 0;
+ while (i < P.count)
+ {
+ _sys_write_tty(*P.buffer++);
+
+ i++;
+ }
+
+ /* No failures. */
+
+ _sys_params_out.byteswritten = P.count;
+ return 0;
+}
--- /dev/null
+#
+! $Source$
+! $State$
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+
+#define D(e) .define e; e
+
+.sect .data
+
+D(ERANGE) = 1
+D(ESET) = 2
+D(EIDIVZ) = 6
+D(EHEAP) = 17
+D(EILLINS) = 18
+D(EODDZ) = 19
+D(ECASE) = 20
+D(EBADMON) = 25
+
--- /dev/null
+#ifndef LIBSYS_H
+#define LIBSYS_H
+
+extern void _sys_rawwrite(unsigned char b);
+extern unsigned char _sys_rawread(void);
+
+extern void _sys_write_tty(char c);
+
+extern int _sys_ttyflags;
+
+#endif
--- /dev/null
+-- $Source$
+-- $State$
+
+local d = ROOTDIR.."plat/pc86/libsys/"
+
+libsys_pc86 = acklibrary {
+ ACKBUILDFLAGS = {PARENT, "-ansi"},
+ ACKINCLUDES = {"%BINDIR%include"},
+
+ ackfile (d.."errno.s"),
+ ackfile (d.."_mon.s"),
+ ackfile (d.."_brk.s"),
+ ackfile (d.."_sys_rawread.s"),
+ ackfile (d.."_sys_rawwrite.s"),
+ ackfile (d.."_sys_read.c"),
+ ackfile (d.."_sys_write.c"),
+ ackfile (d.."_sys_ioctl.c"),
+
+ install = pm.install("%BINDIR%lib/%PLATFORM%/libsys.a"),
+}
--- /dev/null
+-- $Source$
+-- $State$
+
+local d = ROOTDIR.."plat/pc86/"
+
+include (d.."libsys/pmfile")
+
+local bootsector = ackfile {
+ file (d.."boot.s"),
+ install = pm.install("%BINDIR%lib/pc86/boot.o"),
+}
+
+local descr = group {
+ install = pm.install(d.."descr", "%BINDIR%%PLATIND%/%PLATFORM%/descr")
+}
+
+platform_pc86 = group {
+ ARCH = "i86",
+ PLATFORM = "pc86",
+ OPTIMISATION = "-O",
+
+ -- Ensure the descr file is installed first because we'll need it
+ -- to build the libraries.
+
+ descr,
+
+ -- Build the back-end support.
+
+ mach_i86,
+ support_i86,
+ lang_runtimes,
+
+ -- Build the PC standalone syscall library.
+
+ libsys_pc86,
+ bootsector,
+}