Compare commits
10 Commits
11097add40
...
06160c1ed3
| Author | SHA1 | Date | |
|---|---|---|---|
| 06160c1ed3 | |||
| 14fac398c5 | |||
|
|
bc671ff92b | ||
| 8ad0446359 | |||
| e74cf22714 | |||
|
|
a9a1ad666d | ||
|
|
98601ac381 | ||
| b1f7092fe2 | |||
| c9c820840f | |||
| 8336f11b2a |
@ -1,6 +1,10 @@
|
|||||||
# FAT16 bootloader
|
# FAT16 bootloader
|
||||||
x86 FAT16 bootloader
|
x86 FAT16 bootloader
|
||||||
|
|
||||||
|
# Feature
|
||||||
|
* [serial console](https://github.com/Hojun-Cho/bootloader/blob/master/boot/sricon.c)
|
||||||
|
* [prompt](https://github.com/Hojun-Cho/bootloader/blob/98601ac381e30d98b4f3f2c20a733beebf9c2ee3/boot/boot.c#L105)
|
||||||
|
|
||||||
### How to run
|
### How to run
|
||||||
1. install image to device
|
1. install image to device
|
||||||
install to device
|
install to device
|
||||||
|
|||||||
@ -5,7 +5,7 @@ PROG = boot
|
|||||||
SRCS = srt0.S gdt.S boot.c
|
SRCS = srt0.S gdt.S boot.c
|
||||||
OBJS = srt0.o gdt.o boot.o
|
OBJS = srt0.o gdt.o boot.o
|
||||||
|
|
||||||
ESRCS = pccon.c sricon.c print.c a20.c time.c
|
ESRCS = pccon.c sricon.c print.c fmt.c a20.c time.c mem.c cpu.c alloc.c disk.c
|
||||||
OBJS += $(ESRCS:.c=.o)
|
OBJS += $(ESRCS:.c=.o)
|
||||||
|
|
||||||
include $(SDIR)/Makefile.inc
|
include $(SDIR)/Makefile.inc
|
||||||
|
|||||||
67
boot/alloc.c
Normal file
67
boot/alloc.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include <u.h>
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fn.h"
|
||||||
|
|
||||||
|
// only for booting
|
||||||
|
|
||||||
|
typedef struct FreeList FreeList;
|
||||||
|
static struct FreeList{
|
||||||
|
uint size;
|
||||||
|
FreeList *next;
|
||||||
|
} *freed;
|
||||||
|
|
||||||
|
extern char end[];
|
||||||
|
static char *top = end;
|
||||||
|
|
||||||
|
static void
|
||||||
|
putfreed(FreeList *p)
|
||||||
|
{
|
||||||
|
p->next = nil;
|
||||||
|
if(freed){
|
||||||
|
p->next = freed;
|
||||||
|
freed = p;
|
||||||
|
}else
|
||||||
|
freed = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FreeList*
|
||||||
|
getfreed(uint n)
|
||||||
|
{
|
||||||
|
for(FreeList *p=freed,*l=freed; p; p=p->next){
|
||||||
|
if(p->size >= n){
|
||||||
|
l->next = p->next;
|
||||||
|
if(p == freed)
|
||||||
|
freed = nil;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
l = p;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
alloc(uint n)
|
||||||
|
{
|
||||||
|
FreeList *p;
|
||||||
|
|
||||||
|
p = getfreed(n);
|
||||||
|
if(p == nil){
|
||||||
|
p = (FreeList*)top;
|
||||||
|
p->size = n;
|
||||||
|
p->next = nil;
|
||||||
|
memset(p+sizeof(*p), 0, n);
|
||||||
|
top += n + sizeof(*p);
|
||||||
|
}
|
||||||
|
if(debug) print("alloc %x %ud\n", &p[1], n);
|
||||||
|
return &p[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
free(void *src)
|
||||||
|
{
|
||||||
|
FreeList *p;
|
||||||
|
|
||||||
|
p = (FreeList*)((char*)src-sizeof(FreeList));
|
||||||
|
if(debug) print("free %x %ud\n", src, p->size);
|
||||||
|
putfreed(p);
|
||||||
|
}
|
||||||
15
boot/boot.c
15
boot/boot.c
@ -6,9 +6,11 @@ static void coninit(void);
|
|||||||
static void machdep(void);
|
static void machdep(void);
|
||||||
|
|
||||||
void (*probe1[])(void) = {
|
void (*probe1[])(void) = {
|
||||||
a20up, coninit,
|
a20up, coninit, memprobe,
|
||||||
|
cpuidprobe,
|
||||||
};
|
};
|
||||||
void (*probe2[])(void) = {
|
void (*probe2[])(void) = {
|
||||||
|
diskprobe,
|
||||||
};
|
};
|
||||||
|
|
||||||
BootProbe probes[] = {
|
BootProbe probes[] = {
|
||||||
@ -31,7 +33,11 @@ ConDev contab[CON_END] = {
|
|||||||
.init = cominit,
|
.init = cominit,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
ConDev *con = &contab[0];
|
ConDev *con = &contab[0];
|
||||||
|
BIOSmmap biosmmap[64];
|
||||||
|
uint cnvmem, extmem;
|
||||||
|
int debug = BOOT_DEBUG;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
coninit(void)
|
coninit(void)
|
||||||
@ -92,9 +98,14 @@ done:
|
|||||||
void
|
void
|
||||||
boot(int bootdev)
|
boot(int bootdev)
|
||||||
{
|
{
|
||||||
print("\n\n===> Hello world <===\n\tBooted on disk 0x%x\n", bootdev);
|
BIOSdisk d = {0,};
|
||||||
|
|
||||||
|
fmtinstall('D', fmtdisk);
|
||||||
|
print("\n===> Hello world <===\n\t"
|
||||||
|
"Booted on disk 0x%x debug:%d\n\t", bootdev, debug);
|
||||||
machdep();
|
machdep();
|
||||||
|
|
||||||
|
bdiskget(bootdev, &d);
|
||||||
for(;;){
|
for(;;){
|
||||||
char buf[8192];
|
char buf[8192];
|
||||||
|
|
||||||
|
|||||||
56
boot/cpu.c
Normal file
56
boot/cpu.c
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#include <u.h>
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fn.h"
|
||||||
|
|
||||||
|
#define EFLAG_ID 0x00200000
|
||||||
|
#define CPUID(code, a, b, c, d) \
|
||||||
|
__asm volatile("cpuid" \
|
||||||
|
: "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
|
||||||
|
: "a" (code))
|
||||||
|
|
||||||
|
void*
|
||||||
|
memset(void *dst, int v, int l)
|
||||||
|
{
|
||||||
|
char *p = dst;
|
||||||
|
for(int i = 0; i < l; ++i)
|
||||||
|
*p++ = 0;
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cpuidprobe(void)
|
||||||
|
{
|
||||||
|
int canuse;
|
||||||
|
union{
|
||||||
|
struct{u32 a,b,d,c;};
|
||||||
|
char arr[16];
|
||||||
|
}r = {0,};
|
||||||
|
|
||||||
|
__asm volatile("pushfl\n\t"
|
||||||
|
"popl %2\n\t"
|
||||||
|
"xorl %2, %0\n\t" /* Invert ID sotred in EFLAGS */
|
||||||
|
"pushl %0\n\t"
|
||||||
|
"popfl\n\t"
|
||||||
|
"pushfl\n\t"
|
||||||
|
"popl %0\n\t"
|
||||||
|
"xorl %2, %0\n\t"
|
||||||
|
: "=r" (canuse)
|
||||||
|
: "0" (EFLAG_ID), "r" (0) /* "EFLAGS_ID" same location "canuse" */
|
||||||
|
: "cc");
|
||||||
|
if(canuse != EFLAG_ID){
|
||||||
|
print("cpuid not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// print vendor
|
||||||
|
CPUID(0x00, r.a, r.b, r.c, r.d);
|
||||||
|
print("CPU vender: %s\n", r.arr+4);
|
||||||
|
// Is running on Hypervisor?
|
||||||
|
// But nothing to do...
|
||||||
|
memset(&r, 0,sizeof(r));
|
||||||
|
CPUID(0x01, r.a, r.b, r.c, r.d);
|
||||||
|
if(r.c & (1<<31)){
|
||||||
|
CPUID(1<<30, r.a, r.b, r.c, r.d);
|
||||||
|
print("Running on Hypervisor: %s\n", r.arr+4);
|
||||||
|
}
|
||||||
|
}
|
||||||
67
boot/dat.h
67
boot/dat.h
@ -2,6 +2,13 @@
|
|||||||
// 0x60 Read/Write Data Port
|
// 0x60 Read/Write Data Port
|
||||||
// 0x64 Read Status Register
|
// 0x64 Read Status Register
|
||||||
// 0x64 Write Command Register
|
// 0x64 Write Command Register
|
||||||
|
#define TABWIDTH 8
|
||||||
|
#ifndef BOOT_DEBUG
|
||||||
|
#define BOOT_DEBUG 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DOSPARTOFF 446
|
||||||
|
#define NDOSPART 4
|
||||||
|
|
||||||
enum{
|
enum{
|
||||||
CON_PC,
|
CON_PC,
|
||||||
@ -36,6 +43,63 @@ struct ConDev{
|
|||||||
uint dev;
|
uint dev;
|
||||||
uchar pri; // the higher the better
|
uchar pri; // the higher the better
|
||||||
};
|
};
|
||||||
|
typedef struct{
|
||||||
|
u64 addr; /* Beginning of block */
|
||||||
|
u64 size; /* Size of block */
|
||||||
|
u32 type; /* Type of block */
|
||||||
|
} __attribute__((packed)) BIOSmmap;
|
||||||
|
|
||||||
|
// fmt.c
|
||||||
|
typedef struct{
|
||||||
|
int ucase;
|
||||||
|
int padch;
|
||||||
|
char *p;
|
||||||
|
char *ep;
|
||||||
|
int f1, f2, f3;
|
||||||
|
va_list ap;
|
||||||
|
}Op;
|
||||||
|
|
||||||
|
// disk.c
|
||||||
|
typedef struct{
|
||||||
|
u8 flag; /* bootstrap flags */
|
||||||
|
u8 bhd; /* begin head */
|
||||||
|
u8 bsec; /* begin sector */
|
||||||
|
u8 bcyl; /* begin cylinder */
|
||||||
|
u8 type; /* partition type (see below) */
|
||||||
|
u8 ehd; /* end head */
|
||||||
|
u8 esec; /* end sec2r */
|
||||||
|
u8 ecyl; /* end cylinder */
|
||||||
|
u32 beg; /* absolute starting sectoff number */
|
||||||
|
u32 size; /* partition size in sec2rs */
|
||||||
|
} __attribute__((packed)) DOSpart;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
u8 boot[DOSPARTOFF];
|
||||||
|
DOSpart parts[NDOSPART];
|
||||||
|
u16 sign;
|
||||||
|
} __attribute__((packed)) DOSmbr;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
int n;
|
||||||
|
u32 ncyl;
|
||||||
|
u32 nhead;
|
||||||
|
u32 nsec;
|
||||||
|
i32 edd;
|
||||||
|
u32 dev ;
|
||||||
|
u32 checksum;
|
||||||
|
u32 flag;
|
||||||
|
} __attribute__((packed)) BIOSdisk;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
|
||||||
|
}Disklabel;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
BIOSdisk bdsk; // bios disk info
|
||||||
|
Disklabel label;
|
||||||
|
|
||||||
|
int bootdev, osdev;
|
||||||
|
}Disk;
|
||||||
|
|
||||||
// gdt.S
|
// gdt.S
|
||||||
extern volatile struct BIOSreg BIOSreg;
|
extern volatile struct BIOSreg BIOSreg;
|
||||||
@ -44,5 +108,8 @@ extern volatile struct BIOSreg BIOSreg;
|
|||||||
extern void (*probe1[])(void);
|
extern void (*probe1[])(void);
|
||||||
extern void (*probe2[])(void);
|
extern void (*probe2[])(void);
|
||||||
extern BootProbe probes[];
|
extern BootProbe probes[];
|
||||||
|
extern BIOSmmap biosmmap[64];
|
||||||
extern ConDev contab[CON_END];
|
extern ConDev contab[CON_END];
|
||||||
extern ConDev *con;
|
extern ConDev *con;
|
||||||
|
extern uint cnvmem, extmem;
|
||||||
|
extern int debug;
|
||||||
174
boot/disk.c
Normal file
174
boot/disk.c
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
#include <u.h>
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fn.h"
|
||||||
|
|
||||||
|
#define BDA_DISK 0x0475 // of hard disk drives detected
|
||||||
|
#define MAX_DISK_ENTRY 1 // for now
|
||||||
|
|
||||||
|
enum{
|
||||||
|
Bread = 0x4200,
|
||||||
|
Bwrite = 0x4300,
|
||||||
|
};
|
||||||
|
|
||||||
|
// interrupt 41h exension support bitmap
|
||||||
|
enum{
|
||||||
|
Eeda = 0x01, // Extended disk access funcions 42-44,47,48
|
||||||
|
Erdc = 0x02, // removable drive controller functions 45,46,48,49
|
||||||
|
Eedd = 0x04, // Enhanced disk drive functions
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
u8 len;
|
||||||
|
u8 res1;
|
||||||
|
u8 nblk;
|
||||||
|
u8 res2;
|
||||||
|
u16 off;
|
||||||
|
u16 seg;
|
||||||
|
u64 daddr; // starting block
|
||||||
|
}EDDcb;
|
||||||
|
|
||||||
|
Disk disks[MAX_DISK_ENTRY];
|
||||||
|
|
||||||
|
static int
|
||||||
|
ireset(int dev)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
__asm volatile("int $0x33\n\t"
|
||||||
|
"setc %b0"
|
||||||
|
: "=a" (rv)
|
||||||
|
: "0" (0), "d" (dev)
|
||||||
|
: "%ecx", "%cc");
|
||||||
|
return (rv&0xff) ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bit(s) Description (Table 00271)
|
||||||
|
// 0 extended disk access functions (AH=42h-44h,47h,48h) supported
|
||||||
|
// 1 removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h) supported
|
||||||
|
// 2 enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported.
|
||||||
|
// Extended drive parameter table is valid (see #00273,#00278)
|
||||||
|
// 3-15 reserved (0)
|
||||||
|
static __inline int
|
||||||
|
ieddsuport(BIOSdisk *d)
|
||||||
|
{
|
||||||
|
int bmap, rv;
|
||||||
|
|
||||||
|
__asm volatile("int $0x33\n\t"
|
||||||
|
"setc %b0"
|
||||||
|
: "=a" (rv), "=c" (bmap)
|
||||||
|
: "0" (0x4100), "b" (0x55aa), "d" (d->n)
|
||||||
|
: "cc");
|
||||||
|
if(rv&0xff || (BIOSreg.bx&0xffff) != 0xaa55)
|
||||||
|
return -1;
|
||||||
|
d->edd = (bmap&0xffff) | ((rv&0xff)<<16);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __inline int
|
||||||
|
bdiskinfo(BIOSdisk *d)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
// CL = maximum sector number (bits 5-0)
|
||||||
|
// high two bits of maximum cylinder number (bits 7-6)
|
||||||
|
__asm volatile("int $0x33\n\t"
|
||||||
|
"setc %b0\n\t"
|
||||||
|
"movzbl %h1, %1\n\t"
|
||||||
|
"movzbl %%cl, %3; andb $0x3f, %b3\n\t"
|
||||||
|
"xchgb %%cl, %%ch; rolb $2, %%ch"
|
||||||
|
: "=a" (rv), "=d" (d->nhead), "=c" (d->ncyl), "=b" (d->nsec)
|
||||||
|
: "0" (0x0800), "1" (d->n)
|
||||||
|
: "cc");
|
||||||
|
return (rv&0xff) == 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
bdiskget(int dev, BIOSdisk *d)
|
||||||
|
{
|
||||||
|
if(ireset(dev)){
|
||||||
|
print("Can't reset disk\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
d->n = dev;
|
||||||
|
if(bdiskinfo(d)){
|
||||||
|
print("Can't read disk\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(ieddsuport(d)){
|
||||||
|
print("edd not supported\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
diskrw(uint rw, int dev, u32 daddr, u32 blk, void *buf)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
volatile EDDcb cb = {0,};
|
||||||
|
|
||||||
|
cb.len = sizeof(cb);
|
||||||
|
cb.nblk = blk;
|
||||||
|
cb.seg = ((u32)buf>>4 & 0xffff);
|
||||||
|
cb.off = (u32)buf & 0xf;
|
||||||
|
cb.daddr = daddr;
|
||||||
|
if(cb.seg==0 || cb.off==0)
|
||||||
|
return -1;
|
||||||
|
BIOSreg.ds = (u32)&cb >> 4;
|
||||||
|
__asm volatile ("int $0x33\n\t"
|
||||||
|
"setc %b0\n\t"
|
||||||
|
: "=a" (rv)
|
||||||
|
: "0" (rw), "d" (dev), "S" ((int)(&cb)&0xf)
|
||||||
|
: "%ecx", "cc");
|
||||||
|
return rv&0xff? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
findos(BIOSdisk *bd)
|
||||||
|
{
|
||||||
|
DOSmbr mbr = {0,};
|
||||||
|
|
||||||
|
if(diskrw(Bread, bd->n, 0, 1, &mbr)){
|
||||||
|
print("Can't find os disk %d\n", bd->n);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
print("found os disk signature: 0x%x\n", mbr.sign);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
findlabel(BIOSdisk *b, Disklabel *dl)
|
||||||
|
{
|
||||||
|
if(b->n & 0x80){
|
||||||
|
findos(b);
|
||||||
|
return 0; // for now
|
||||||
|
}
|
||||||
|
print("Can't found disk label:%x \n", b->n);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
diskprobe(void)
|
||||||
|
{
|
||||||
|
int n = MIN((int)*(char*)BDA_DISK, MAX_DISK_ENTRY);
|
||||||
|
|
||||||
|
for(int i = 0x80; i < 0x80+n; ++i){
|
||||||
|
if(bdiskget(i, &disks[i].bdsk))
|
||||||
|
return;
|
||||||
|
print("Disnk info: %D\n", disks[i].bdsk);
|
||||||
|
if(findlabel(&disks[i].bdsk, &disks[i].label)){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
fmtdisk(Op *op)
|
||||||
|
{
|
||||||
|
dofmt(op, "disk:%x cyl:%d head:%d sec:%d edd:%d");
|
||||||
|
// for skip va_list
|
||||||
|
USED(va_arg(op->ap, u32));
|
||||||
|
USED(va_arg(op->ap, u32));
|
||||||
|
USED(va_arg(op->ap, u32));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
304
boot/fmt.c
Normal file
304
boot/fmt.c
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
#include <u.h>
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fn.h"
|
||||||
|
|
||||||
|
#define MAXCON 30
|
||||||
|
#define IDIGIT 30
|
||||||
|
|
||||||
|
enum{
|
||||||
|
FLONG = (1<<0),
|
||||||
|
FSHORT = (1<<2),
|
||||||
|
FUNSIGN = (1<<3),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void put(Op*, int);
|
||||||
|
static int noconv(Op*);
|
||||||
|
static int cconv(Op*);
|
||||||
|
static int dconv(Op*);
|
||||||
|
static int hconv(Op*);
|
||||||
|
static int lconv(Op*);
|
||||||
|
static int sconv(Op*);
|
||||||
|
static int uconv(Op*);
|
||||||
|
static int xconv(Op*);
|
||||||
|
static int Xconv(Op*);
|
||||||
|
static int percent(Op*);
|
||||||
|
|
||||||
|
static int (*fmtconv[MAXCON])(Op*) = {
|
||||||
|
noconv,
|
||||||
|
cconv, dconv, hconv, lconv,
|
||||||
|
sconv, uconv, xconv, Xconv,
|
||||||
|
percent,
|
||||||
|
};
|
||||||
|
|
||||||
|
int fmtindex[128] = {
|
||||||
|
['c'] = 1,
|
||||||
|
['d'] = 2,
|
||||||
|
['h'] = 3,
|
||||||
|
['l'] = 4,
|
||||||
|
['s'] = 5,
|
||||||
|
['u'] = 6,
|
||||||
|
['x'] = 7,
|
||||||
|
['X'] = 8,
|
||||||
|
['%'] = 9,
|
||||||
|
};
|
||||||
|
int convcnt = 11;
|
||||||
|
|
||||||
|
int
|
||||||
|
strlen(char *s)
|
||||||
|
{
|
||||||
|
char *p = s;
|
||||||
|
|
||||||
|
while(*p)
|
||||||
|
++p;
|
||||||
|
return p-s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
fmtinstall(int c, int (*f)(Op*))
|
||||||
|
{
|
||||||
|
c &= 0177;
|
||||||
|
if(fmtindex[c] == 0){
|
||||||
|
if(convcnt + 1 >= MAXCON)
|
||||||
|
return -1;
|
||||||
|
fmtindex[c] = convcnt++;
|
||||||
|
}
|
||||||
|
fmtconv[fmtindex[c]] = f;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
dofmt(Op *op, char *fmt)
|
||||||
|
{
|
||||||
|
int sf1, c;
|
||||||
|
|
||||||
|
while(1){
|
||||||
|
c = *fmt++;
|
||||||
|
if(c != '%'){
|
||||||
|
if(c == 0){
|
||||||
|
if(op->p < op->ep)
|
||||||
|
*op->p = 0;
|
||||||
|
return op->p;
|
||||||
|
}
|
||||||
|
put(op, c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
op->f2 = -1;
|
||||||
|
op->f1 = op->f3 = 0;
|
||||||
|
op->padch = sf1 = 0;
|
||||||
|
c = *fmt++;
|
||||||
|
if(c == '-'){
|
||||||
|
sf1 = 1;
|
||||||
|
c = *fmt++;
|
||||||
|
}else if(c == '0' || c == ' '){
|
||||||
|
op->padch = c;
|
||||||
|
c = *fmt++;
|
||||||
|
}
|
||||||
|
while(c >= '0' && c <= '9'){
|
||||||
|
op->f1 = op->f1*10 + c-'0';
|
||||||
|
c = *fmt++;
|
||||||
|
}
|
||||||
|
if(sf1)
|
||||||
|
op->f1 = -op->f1;
|
||||||
|
if(c != '.')
|
||||||
|
goto conv;
|
||||||
|
c = *fmt++;
|
||||||
|
while(c >= '0' && c <= '9'){
|
||||||
|
if(op->f2 < 0)
|
||||||
|
op->f2 = 0;
|
||||||
|
op->f2 = op->f2*10 + c-'0';
|
||||||
|
c = *fmt++;
|
||||||
|
}
|
||||||
|
conv:
|
||||||
|
if(c == 0)
|
||||||
|
fmt -= 1;
|
||||||
|
c = (*fmtconv[fmtindex[c&0177]])(op);
|
||||||
|
if(c < 0){
|
||||||
|
op->f3 |= -c;
|
||||||
|
c = *fmt++;
|
||||||
|
goto conv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
strconv(char *s, Op *op, int f1, int f2)
|
||||||
|
{
|
||||||
|
int n, c;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
n = strlen(s);
|
||||||
|
if(f1 >= 0)
|
||||||
|
while(n < f1){
|
||||||
|
put(op, op->padch);
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
for(p=s; (c = *p++);)
|
||||||
|
if(f2){
|
||||||
|
put(op, c);
|
||||||
|
f2 -= 1;
|
||||||
|
}
|
||||||
|
if(f1 < 0){
|
||||||
|
f1 = -f1;
|
||||||
|
while(n < f1){
|
||||||
|
put(op, ' ');
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
numconv(Op *op, int base)
|
||||||
|
{
|
||||||
|
char b[IDIGIT];
|
||||||
|
int i,f,n;
|
||||||
|
long v;
|
||||||
|
short h;
|
||||||
|
|
||||||
|
i = IDIGIT-1;
|
||||||
|
f = 0;
|
||||||
|
b[i] = 0;
|
||||||
|
switch(op->f3 & (FLONG|FSHORT|FUNSIGN)){
|
||||||
|
case FLONG:
|
||||||
|
v = va_arg(op->ap, long);
|
||||||
|
break;
|
||||||
|
case FUNSIGN|FLONG:
|
||||||
|
v = va_arg(op->ap, ulong);
|
||||||
|
break;
|
||||||
|
case FSHORT:
|
||||||
|
v = h = va_arg(op->ap, short);
|
||||||
|
break;
|
||||||
|
case FUNSIGN|FSHORT:
|
||||||
|
h = va_arg(op->ap, ushort);
|
||||||
|
v = (ushort)h;
|
||||||
|
break;
|
||||||
|
case FUNSIGN:
|
||||||
|
v = va_arg(op->ap, unsigned);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
v = va_arg(op->ap, int);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if((op->f3 & FUNSIGN) && v < 0){
|
||||||
|
v = -v;
|
||||||
|
f = 1;
|
||||||
|
}
|
||||||
|
while(--i){
|
||||||
|
n = v % base;
|
||||||
|
n += '0';
|
||||||
|
if(n > '9'){
|
||||||
|
n += 'a' - ('9'+1);
|
||||||
|
if(op->ucase)
|
||||||
|
n += 'A'-'a';
|
||||||
|
}
|
||||||
|
b[i] = n;
|
||||||
|
v = (ulong)v / base;
|
||||||
|
if(i < 2)
|
||||||
|
break;
|
||||||
|
if(op->f2 >= 0 && i >= IDIGIT - op->f2)
|
||||||
|
continue;
|
||||||
|
if(v <= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(f)
|
||||||
|
b[--i] = '-';
|
||||||
|
strconv(b+i, op, op->f1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
noconv(Op *op)
|
||||||
|
{
|
||||||
|
strconv("***ERROR: noconv***", op, 0, -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cconv(Op *op)
|
||||||
|
{
|
||||||
|
char b[2];
|
||||||
|
|
||||||
|
b[0] = va_arg(op->ap, char);
|
||||||
|
b[1] = 0;
|
||||||
|
strconv(b, op, op->f1, -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dconv(Op *op)
|
||||||
|
{
|
||||||
|
numconv(op, 10);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hconv(Op *op)
|
||||||
|
{
|
||||||
|
return -FSHORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
lconv(Op *op)
|
||||||
|
{
|
||||||
|
return -FLONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
uconv(Op *op)
|
||||||
|
{
|
||||||
|
return -FUNSIGN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sconv(Op *op)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
p = va_arg(op->ap, char*);
|
||||||
|
strconv(p?p:"<nil>", op, op->f1, op->f2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xconv(Op *op)
|
||||||
|
{
|
||||||
|
numconv(op, 16);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
Xconv(Op *op)
|
||||||
|
{
|
||||||
|
op->ucase = 1;
|
||||||
|
numconv(op, 16);
|
||||||
|
op->ucase = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
percent(Op *op)
|
||||||
|
{
|
||||||
|
put(op, '%');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
put(Op *op, int c)
|
||||||
|
{
|
||||||
|
static int pos;
|
||||||
|
int opos;
|
||||||
|
|
||||||
|
if(c == 0)
|
||||||
|
return;
|
||||||
|
if(c == '\t'){
|
||||||
|
opos = pos;
|
||||||
|
pos = (opos+TABWIDTH) & (~(TABWIDTH-1));
|
||||||
|
while(opos++ < pos && op->p < op->ep)
|
||||||
|
*op->p++ = ' ';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(op->p < op->ep){
|
||||||
|
*op->p++ = c;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if(c == '\n')
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
34
boot/fn.h
34
boot/fn.h
@ -1,3 +1,6 @@
|
|||||||
|
#define MAX(x, y) ((x)>(y)?(x):(y))
|
||||||
|
#define MIN(x, y) ((x)<(y)?(x):(y))
|
||||||
|
|
||||||
// console.c
|
// console.c
|
||||||
void cominit(ConDev *d);
|
void cominit(ConDev *d);
|
||||||
void pcinit(ConDev *d);
|
void pcinit(ConDev *d);
|
||||||
@ -8,17 +11,44 @@ int comprobe(ConDev *d);
|
|||||||
int comgetc(int dev);
|
int comgetc(int dev);
|
||||||
void computc(int dev, int c);
|
void computc(int dev, int c);
|
||||||
|
|
||||||
|
// fmt.c
|
||||||
|
char* dofmt(Op *op, char *fmt);
|
||||||
|
int fmtinstall(int c, int (*f)(Op*));
|
||||||
|
char *doprint(char *p, char *ep, char *fmt, va_list ap);
|
||||||
|
int strlen(char *s);
|
||||||
|
|
||||||
// print.c
|
// print.c
|
||||||
void putchar(int c);
|
void putchar(int c);
|
||||||
|
void putstr(char *s, int l);
|
||||||
int getchar(void);
|
int getchar(void);
|
||||||
void print(const char *fmt, ...);
|
char* doprint(char *p, char *ep, char *fmt, va_list);
|
||||||
|
int print(char *fmt, ...);
|
||||||
|
int snprint(char *buf, int len, char *fmt, ...);
|
||||||
|
|
||||||
// a20.c
|
// a20.c
|
||||||
void a20up(void);
|
void a20up(void);
|
||||||
|
|
||||||
// long.c
|
// mem.c
|
||||||
|
void memprobe(void);
|
||||||
|
|
||||||
|
// cpu.c
|
||||||
|
void cpuidprobe(void);
|
||||||
|
|
||||||
|
// time.c
|
||||||
long getsecs(void);
|
long getsecs(void);
|
||||||
|
|
||||||
|
// alloc.c
|
||||||
|
void *alloc(uint);
|
||||||
|
void free(void*);
|
||||||
|
|
||||||
|
// disk.c
|
||||||
|
int bdiskget(int dev, BIOSdisk *d);
|
||||||
|
int fmtdisk(Op *op);
|
||||||
|
void diskprobe(void);
|
||||||
|
|
||||||
|
// util
|
||||||
|
void* memset(void *dst, int v, int l);
|
||||||
|
|
||||||
static __inline int
|
static __inline int
|
||||||
major(int x)
|
major(int x)
|
||||||
{
|
{
|
||||||
|
|||||||
51
boot/gdt.S
51
boot/gdt.S
@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
|
|
||||||
|
#define AX 0
|
||||||
|
#define CX 4
|
||||||
|
#define DX 8
|
||||||
|
#define BX 12
|
||||||
|
#define BP 16
|
||||||
|
#define SI 20
|
||||||
|
#define DI 24
|
||||||
|
#define DS 28
|
||||||
|
#define ES 32
|
||||||
|
|
||||||
#define GEN_LABEL(n) X##n
|
#define GEN_LABEL(n) X##n
|
||||||
|
|
||||||
// idt entry
|
// idt entry
|
||||||
@ -63,22 +73,12 @@
|
|||||||
\
|
\
|
||||||
lidt Idtr;
|
lidt Idtr;
|
||||||
|
|
||||||
.text
|
|
||||||
.globl BIOSreg
|
|
||||||
BIOSreg:
|
|
||||||
BIOS_AX: .long 0
|
|
||||||
BIOS_CX: .long 0
|
|
||||||
BIOS_DX: .long 0
|
|
||||||
BIOS_BX: .long 0
|
|
||||||
BIOS_BP: .long 0
|
|
||||||
BIOS_SI: .long 0
|
|
||||||
BIOS_DI: .long 0
|
|
||||||
BIOS_DS: .long 0
|
|
||||||
BIOS_ES: .long 0
|
|
||||||
|
|
||||||
.text
|
.text
|
||||||
.code32
|
.code32
|
||||||
.globl pmode_init
|
.globl pmode_init
|
||||||
|
.globl BIOSreg
|
||||||
|
BIOSreg:
|
||||||
|
.space 36, 0
|
||||||
|
|
||||||
// Table
|
// Table
|
||||||
// IDTR offset + 0 : entry 0
|
// IDTR offset + 0 : entry 0
|
||||||
@ -246,11 +246,9 @@ EMUh: // build stack for real mode
|
|||||||
movb %al, intno // save BIOS int vector
|
movb %al, intno // save BIOS int vector
|
||||||
|
|
||||||
// BIOS_regs is area for saving the contents of registers returned by the BIOS during a BIOS CALL
|
// BIOS_regs is area for saving the contents of registers returned by the BIOS during a BIOS CALL
|
||||||
movl BIOS_ES, %eax
|
movl BIOSreg+ES, %eax
|
||||||
movl $0x00, %eax
|
|
||||||
mov %eax, 7f
|
mov %eax, 7f
|
||||||
movl BIOS_DS, %eax
|
movl BIOSreg+DS, %eax
|
||||||
movl $0x00, %eax
|
|
||||||
mov %eax, 6f
|
mov %eax, 6f
|
||||||
|
|
||||||
prot2real
|
prot2real
|
||||||
@ -300,12 +298,13 @@ intno = . -1;
|
|||||||
// movl $Leax, %eax
|
// movl $Leax, %eax
|
||||||
.byte 0xb8
|
.byte 0xb8
|
||||||
4: .long 0x90909090
|
4: .long 0x90909090
|
||||||
mov %eax, BIOS_BX
|
movl %eax, BIOSreg+BX
|
||||||
|
|
||||||
// movl $Leax, %eax
|
// movl $Leax, %eax
|
||||||
.byte 0xb8
|
.byte 0xb8
|
||||||
3: .long 0x90909090
|
3: .long 0x90909090
|
||||||
mov %eax, BIOS_ES
|
movl %eax, BIOSreg+ES
|
||||||
|
|
||||||
|
|
||||||
// movl $Leax, %eax
|
// movl $Leax, %eax
|
||||||
.byte 0xb8
|
.byte 0xb8
|
||||||
@ -318,13 +317,13 @@ intno = . -1;
|
|||||||
movb %bh, 0xe*4(%esp) // restore eflags
|
movb %bh, 0xe*4(%esp) // restore eflags
|
||||||
|
|
||||||
// save register into BIOSREG
|
// save register into BIOSREG
|
||||||
.code32
|
movl %eax, BIOSreg+AX
|
||||||
movl %eax, BIOS_AX
|
movl %ecx, BIOSreg+CX
|
||||||
movl %ecx, BIOS_CX
|
movl %edx, BIOSreg+DX
|
||||||
movl %edx, BIOS_DX
|
movl %ebp, BIOSreg+BP
|
||||||
movl %ebp, BIOS_BP
|
movl %esi, BIOSreg+SI
|
||||||
movl %esi, BIOS_SI
|
movl %edi, BIOSreg+DI
|
||||||
movl %edi, BIOS_DI
|
|
||||||
|
|
||||||
// clear NT(Nested Task Flag: 14) flag in eflag
|
// clear NT(Nested Task Flag: 14) flag in eflag
|
||||||
// if 1 : interrupting
|
// if 1 : interrupting
|
||||||
|
|||||||
81
boot/mem.c
Normal file
81
boot/mem.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include <u.h>
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fn.h"
|
||||||
|
|
||||||
|
#define IOMEM_BEGIN 0x0A0000
|
||||||
|
#define IOMEM_END 0x100000
|
||||||
|
|
||||||
|
enum{
|
||||||
|
MAP_END = 0x00,
|
||||||
|
MAP_FREE = 0x01,
|
||||||
|
MAP_RES = 0x02,
|
||||||
|
MAP_ACPI_RECLAM = 0x03,
|
||||||
|
MAP_ACPI_NVS = 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
|
static BIOSmmap*
|
||||||
|
int15_E820(BIOSmmap *m)
|
||||||
|
{
|
||||||
|
int rc, sig, off = 0;
|
||||||
|
do{
|
||||||
|
BIOSreg.es = ((uint)(m) >> 4);
|
||||||
|
__asm volatile("int $0x35; setc %b1"
|
||||||
|
: "=a" (sig), "=d" (rc), "=b" (off)
|
||||||
|
: "0" (0xE820), "1" (0x534d4150), "b" (off),
|
||||||
|
"c" (sizeof(*m)), "D" (((uint)m) & 0xf)
|
||||||
|
: "cc", "memory");
|
||||||
|
|
||||||
|
off = BIOSreg.bx;
|
||||||
|
if(rc &0xff || sig !=0x534d4150){
|
||||||
|
break;
|
||||||
|
if(m->type == 0)
|
||||||
|
m->type = MAP_RES;
|
||||||
|
}
|
||||||
|
m++;
|
||||||
|
}while(off);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dumpmem(BIOSmmap *m)
|
||||||
|
{
|
||||||
|
ulong tot = 0;
|
||||||
|
|
||||||
|
for(BIOSmmap *p=m; p->type != MAP_END; ++p){
|
||||||
|
print("MEM % 2ud type %ud size % 10udKB at %08x:%08x\n",
|
||||||
|
p-m, p->type, (uint)(p->size/1024), (uint)(p->addr>>32), (uint)p->addr);
|
||||||
|
if(p->type == MAP_FREE)
|
||||||
|
tot += p->size/1024;
|
||||||
|
}
|
||||||
|
print("RAM low:%udKB high:%udKB\n", cnvmem, extmem);
|
||||||
|
print("Total free memory: %udKB\n", tot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
isa20done(void)
|
||||||
|
{
|
||||||
|
register char *a = (char *)0x100000;
|
||||||
|
register char *b = (char *)0x000000;
|
||||||
|
|
||||||
|
return *a != *b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
memprobe(void)
|
||||||
|
{
|
||||||
|
BIOSmmap *m;
|
||||||
|
|
||||||
|
cnvmem = extmem = 0;
|
||||||
|
m = int15_E820(biosmmap);
|
||||||
|
m->type = MAP_END;
|
||||||
|
for(m = biosmmap; m->type != MAP_END; ++m){
|
||||||
|
if(m->type != MAP_FREE || m->size <= 0)
|
||||||
|
continue;
|
||||||
|
if(m->addr < IOMEM_BEGIN)
|
||||||
|
cnvmem = MAX(cnvmem, m->addr + m->size)/1024;
|
||||||
|
if(m->addr >= IOMEM_END)
|
||||||
|
extmem += m->size/1024;
|
||||||
|
}
|
||||||
|
dumpmem(biosmmap);
|
||||||
|
print("A20:%s\n", isa20done() ? "ok" : "no");
|
||||||
|
}
|
||||||
91
boot/print.c
91
boot/print.c
@ -2,54 +2,42 @@
|
|||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
#include "fn.h"
|
#include "fn.h"
|
||||||
|
|
||||||
#define TABWIDTH 4
|
#define BUFSIZE 1024
|
||||||
|
|
||||||
static void conputc(int c);
|
static void conputc(int);
|
||||||
static int congetc(void);
|
static int congetc(void);
|
||||||
static void doprint(void (*put)(int), const char *fmt, va_list ap);
|
|
||||||
static void putint(void (*put)(int), int n, const char *sym, int base);
|
|
||||||
static int pos = 0;
|
|
||||||
|
|
||||||
void
|
char*
|
||||||
print(const char *fmt, ...)
|
doprint(char *p, char *ep, char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
|
Op op = { .p = p, .ep = ep, .ap = ap };
|
||||||
|
return dofmt(&op, fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
print(char *fmt, ...)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZE], *p;
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
doprint(putchar, fmt, args);
|
p = doprint(buf, buf+sizeof(buf), fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
putstr(buf, p-buf);
|
||||||
|
return p-buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
int
|
||||||
doprint(void (*put)(int), const char *s, va_list ap)
|
snprint(char *buf, int len, char *fmt, ...)
|
||||||
{
|
{
|
||||||
while(*s){
|
char *p;
|
||||||
if(*s != '%'){
|
va_list args;
|
||||||
put(*s++);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++s;
|
|
||||||
switch(*s){
|
|
||||||
case 'd':
|
|
||||||
putint(put, va_arg(ap,int), "0123456789", 10);
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
putint(put, va_arg(ap,int), "0123456789abcdef", 16);
|
|
||||||
break;
|
|
||||||
case 'X':
|
|
||||||
putint(put, va_arg(ap,int), "0123456789ABCDEF", 16);
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
for(char *p=va_arg(ap,char*); *p; ++p)
|
|
||||||
put(*p);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
++s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
p = doprint(buf, buf+len, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return p-buf;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
getchar(void)
|
getchar(void)
|
||||||
@ -65,6 +53,13 @@ getchar(void)
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
putstr(char *s, int l)
|
||||||
|
{
|
||||||
|
while(l--)
|
||||||
|
putchar(*s++);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
putchar(int c)
|
putchar(int c)
|
||||||
{
|
{
|
||||||
@ -72,24 +67,20 @@ putchar(int c)
|
|||||||
case '\177':
|
case '\177':
|
||||||
conputc('\b');
|
conputc('\b');
|
||||||
conputc(' ');
|
conputc(' ');
|
||||||
|
break;
|
||||||
case '\b':
|
case '\b':
|
||||||
conputc('\b');
|
conputc('\b');
|
||||||
if(pos > 0)
|
|
||||||
--pos;
|
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
do{
|
for(int i = 0; i < TABWIDTH; ++i)
|
||||||
conputc(' ');
|
conputc(' ');
|
||||||
}while(++pos % TABWIDTH);
|
|
||||||
break;
|
break;
|
||||||
case '\n':
|
case '\n':
|
||||||
case '\r':
|
case '\r':
|
||||||
conputc(c);
|
conputc(c);
|
||||||
pos = 0;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
conputc(c);
|
conputc(c);
|
||||||
++pos;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,19 +100,3 @@ congetc(void)
|
|||||||
{
|
{
|
||||||
return con->getc(con->dev);
|
return con->getc(con->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
putint(void (*put)(int), int n, const char *sym, int base)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
char buf[16];
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
do{
|
|
||||||
buf[i++] = n%base;
|
|
||||||
n /= base;
|
|
||||||
}while(n);
|
|
||||||
do{
|
|
||||||
put((int)sym[(int)buf[--i]]);
|
|
||||||
}while(i);
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user