Merge branch 'master' into rv64-upstream

This commit is contained in:
Zihao Yu 2020-03-05 12:42:48 +08:00
commit 91a22ea902
423 changed files with 43682 additions and 4362 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
*.a
*.o
*.d
__pycache__
*.pyc
*.ini
*.log

View File

@ -8,7 +8,7 @@ LD = $(CROSS_COMPILE)ld
OBJDUMP = $(CROSS_COMPILE)objdump
OBJCOPY = $(CROSS_COMPILE)objcopy
READELF = $(CROSS_COMPILE)readelf
INCLUDES = $(addprefix -I, $(INC_DIR)) -I$(AM_HOME)/am/
INCLUDES += $(addprefix -I, $(INC_DIR)) -I$(AM_HOME)/am/
INCLUDES += -I$(AM_HOME)/am/include
CFLAGS += -O2 -MMD -Wall -Werror -ggdb $(INCLUDES) \
-D__ISA__=\"$(ISA)\" -D__ISA_$(shell echo $(ISA) | tr a-z A-Z)__ \

View File

@ -1,32 +1 @@
# The Abstract Machine (AM)
抽象计算机:必要的抽象,帮你编写一个完整的计算机系统!
目录组织:
* `am/`AM头文件、每个体系结构分别实现的AM代码。
* `libs/`建立在AM上、体系结构无关的运行库如软件模拟浮点数、基础libc功能等。
* `apps/`一些运行在AM上应用程序示例。
* `tests/`: 用来测试AM实现的简单测试程序。
## 创建一个AM应用
在任何目录都可以创建运行在AM上的应用程序。程序用C/C++语言编写除AM之外无法调用其他库函数但可以引用`stdarg.h`, `limits.h`等包含体系结构相关数据定义的头文件)。
为此你需要在应用程序项目的根目录添加一个Makefile
```
NAME = app-name
SRCS = src/main.c src/help.c src/lib.cpp
include $(AM_HOME)/Makefile.app
```
一些注意事项:
* `NAME`定义了应用的名字。编译后会在`build/`目录里出现以此命名的应用程序。
* `SRCS`指定了编译应用所需的源文件。可以放在应用目录中的任意位置。
* 应用目录下的`include/`目录会被添加到编译的`-I`选项中。
* 环境变量`AM_HOME`需要包含**nexus-am项目的根目录的绝对路径**。
编译时,首先确保`AM_HOME`正确设置,然后执行`make ARCH=体系结构名`编译。例如`make ARCH=native`将会编译成本地可运行的项目,`make ARCH=mips32-minimal`生成用于仿真的MIPS32程序。`ARCH`缺省时默认编译到本地。

31
am/am.h
View File

@ -22,21 +22,21 @@ enum {
_EVENT_IRQ_TIMER,
_EVENT_IRQ_IODEV,
_EVENT_PAGEFAULT,
_EVENT_YIELD,
_EVENT_SYSCALL,
_EVENT_YIELD,
};
enum {
_PROT_NONE = 1, // no access
_PROT_READ = 2, // can read
_PROT_WRITE = 4, // can write
_PROT_EXEC = 8, // can execute
_PROT_NONE = 0x1, // no access
_PROT_READ = 0x2, // can read
_PROT_WRITE = 0x4, // can write
_PROT_EXEC = 0x8, // can execute
};
// Memory area for [@start, @end)
typedef struct _Area {
void *start, *end;
} _Area;
} _Area;
// An event of type @event, caused by @cause of pointer @ref
typedef struct _Event {
@ -64,26 +64,25 @@ void _halt(int code) __attribute__((__noreturn__));
// ======================= I/O Extension (IOE) =======================
int _ioe_init();
size_t _io_read(uint32_t dev, uintptr_t reg, void *buf, size_t size);
int _ioe_init();
size_t _io_read (uint32_t dev, uintptr_t reg, void *buf, size_t size);
size_t _io_write(uint32_t dev, uintptr_t reg, void *buf, size_t size);
// ====================== Context Extension (CTE) ====================
int _cte_init(_Context *(*handler)(_Event ev, _Context *ctx));
int _cte_init(_Context *(*handler)(_Event ev, _Context *ctx));
void _yield();
int _intr_read();
int _intr_read();
void _intr_write(int enable);
_Context *_kcontext(_Area kstack, void (*entry)(void *), void *arg);
_Context* _kcontext(_Area kstack, void (*entry)(void *), void *arg);
// ================= Virtual Memory Extension (VME) ==================
int _vme_init(void *(*pgalloc)(size_t size), void (*pgfree)(void *));
int _protect(_AddressSpace *as);
int _vme_init(void *(*pgalloc)(size_t size), void (*pgfree)(void *));
void _protect(_AddressSpace *as);
void _unprotect(_AddressSpace *as);
int _map(_AddressSpace *as, void *va, void *pa, int prot);
_Context *_ucontext(_AddressSpace *as, _Area ustack, _Area kstack,
void *entry, void *args);
void _map(_AddressSpace *as, void *va, void *pa, int prot);
_Context *_ucontext(_AddressSpace *as, _Area kstack, void *entry);
// ================= Multi-Processor Extension (MPE) =================

View File

@ -9,11 +9,12 @@ extern "C" {
// =========================== AM Devices ============================
#define _DEV_PERFCNT 0x0000ac01 // AM Virtual Performance Counter
#define _DEV_PERFCNT 0x0000ac01 // AM Virtual Performance Counter
#define _DEV_INPUT 0x0000ac02 // AM Virtual Input Device
#define _DEV_TIMER 0x0000ac03 // AM Virtual Timer
#define _DEV_VIDEO 0x0000ac04 // AM Virtual Video Controller
#define _DEV_SERIAL 0x0000ac05 // AM Virtual Serial
#define _DEV_STORAGE 0x0000ac06 // AM Virtual Persistent Storage
#define _DEV_PCICONF 0x00000080 // PCI Configuration Space
#define _AM_DEVREG(dev, reg, id, ...) \
@ -23,15 +24,18 @@ extern "C" {
// ================= Device Register Specifications ==================
_AM_DEVREG(INPUT, KBD, 1, int keydown, keycode);
_AM_DEVREG(TIMER, UPTIME, 1, uint32_t hi, lo);
_AM_DEVREG(TIMER, DATE, 2, int year, month, day, hour, minute, second);
_AM_DEVREG(VIDEO, INFO, 1, int width, height);
_AM_DEVREG(VIDEO, FBCTL, 2, int x, y; uint32_t *pixels; int w, h, sync);
_AM_DEVREG(SERIAL, RECV, 1, uint8_t data);
_AM_DEVREG(SERIAL, SEND, 2, uint8_t data);
_AM_DEVREG(SERIAL, STAT, 3, uint8_t data);
_AM_DEVREG(SERIAL, CTRL, 4, uint8_t data);
_AM_DEVREG(INPUT, KBD, 1, int keydown, keycode);
_AM_DEVREG(TIMER, UPTIME, 1, uint32_t hi, lo);
_AM_DEVREG(TIMER, DATE, 2, int year, month, day, hour, minute, second);
_AM_DEVREG(VIDEO, INFO, 1, int width, height);
_AM_DEVREG(VIDEO, FBCTRL, 2, int x, y; uint32_t *pixels; int w, h, sync);
_AM_DEVREG(SERIAL, RECV, 1, uint8_t data);
_AM_DEVREG(SERIAL, SEND, 2, uint8_t data);
_AM_DEVREG(SERIAL, STAT, 3, uint8_t data);
_AM_DEVREG(SERIAL, CTRL, 4, uint8_t data);
_AM_DEVREG(STORAGE, INFO, 1, uint32_t blksz, blkcnt);
_AM_DEVREG(STORAGE, RDCTRL, 2, void *buf; uint32_t blkno, blkcnt);
_AM_DEVREG(STORAGE, WRCTRL, 3, void *buf; uint32_t blkno, blkcnt);
#define _DEVREG_PCICONF(bus, slot, func, offset) \
((uint32_t)( 1) << 31) | ((uint32_t)( bus) << 16) | \
((uint32_t)(slot) << 11) | ((uint32_t)(func) << 8) | (offset)

3
am/arch/isa/x86_64.mk Normal file
View File

@ -0,0 +1,3 @@
CFLAGS += -m64 -fPIC -mno-sse
ASFLAGS += -m64 -fPIC
LDFLAGS += -melf_x86_64

View File

@ -1,25 +1,20 @@
include $(AM_HOME)/am/arch/isa/x86.mk
AM_SRCS := x86/qemu/trm.c \
x86/qemu/ioe.c \
x86/qemu/cte.c \
x86/qemu/cte_trap.S \
x86/qemu/vme.c \
x86/qemu/mpe.c \
x86/qemu/devices/apic.c \
x86/qemu/devices/cpu.c \
x86/qemu/devices/input.c \
x86/qemu/devices/pcdev.c \
x86/qemu/devices/timer.c \
x86/qemu/devices/video.c \
AM_SRCS := x86/qemu/start32.S \
x86/qemu/trap32.S \
x86/qemu/trm.c \
x86/qemu/cte.c \
x86/qemu/ioe.c \
x86/qemu/vme.c \
x86/qemu/mpe.c
image:
@make -s -C $(AM_HOME)/am/src/x86/qemu/boot
@echo + LD "->" $(BINARY_REL).o
@$(LD) $(LDFLAGS) -Ttext 0x00100000 -o $(BINARY).o $(LINK_FILES)
@echo + CREATE "->" $(BINARY_REL)
@( cat $(AM_HOME)/am/src/x86/qemu/boot/mbr; head -c 512 /dev/zero; cat $(BINARY).o ) > $(BINARY)
@( cat $(AM_HOME)/am/src/x86/qemu/boot/mbr; head -c 1024 /dev/zero; cat $(BINARY).o ) > $(BINARY)
run:
@( echo -n $(mainargs); ) | dd if=/dev/stdin of=$(BINARY) bs=512 count=1 seek=1 conv=notrunc status=none
@qemu-system-i386 -serial stdio -machine accel=kvm:tcg -smp "$(smp)" -drive format=raw,file=$(BINARY)
@( echo -n $(mainargs); ) | dd if=/dev/stdin of=$(BINARY) bs=512 count=2 seek=1 conv=notrunc status=none
@qemu-system-i386 -serial stdio -machine accel=tcg -smp "$(smp)" -drive format=raw,file=$(BINARY)

20
am/arch/x86_64-qemu.mk Normal file
View File

@ -0,0 +1,20 @@
include $(AM_HOME)/am/arch/isa/x86_64.mk
AM_SRCS := x86/qemu/start64.S \
x86/qemu/trap64.S \
x86/qemu/trm.c \
x86/qemu/cte.c \
x86/qemu/ioe.c \
x86/qemu/vme.c \
x86/qemu/mpe.c
image:
@make -s -C $(AM_HOME)/am/src/x86/qemu/boot
@echo + LD "->" $(BINARY_REL).o
@$(LD) $(LDFLAGS) -N -Ttext-segment=0x00100000 -o $(BINARY).o $(LINK_FILES)
@echo + CREATE "->" $(BINARY_REL)
@( cat $(AM_HOME)/am/src/x86/qemu/boot/mbr; head -c 1024 /dev/zero; cat $(BINARY).o ) > $(BINARY)
run:
@( echo -n $(mainargs); ) | dd if=/dev/stdin of=$(BINARY) bs=512 count=2 seek=1 conv=notrunc status=none
@qemu-system-x86_64 -serial stdio -machine accel=tcg -smp "$(smp)" -drive format=raw,file=$(BINARY)

View File

@ -3,7 +3,7 @@
struct _Context {
union {
struct _AddressSpace *as;
void *pdir;
uint32_t gpr[32];
};
uint32_t lo, hi;

View File

@ -5,31 +5,21 @@
# define __USE_GNU
#endif
#include <unistd.h>
#include <ucontext.h>
#include <sys/types.h>
#define false 0
#define true 1
struct _Context {
union {
uint8_t pad[1024];
struct {
ucontext_t uc;
void *as;
};
};
uintptr_t rdi;
uintptr_t rax;
uintptr_t rip;
uintptr_t ksp;
void *vm_head;
ucontext_t uc;
// skip the red zone of the stack frame, see the amd64 ABI manual for details
uint8_t redzone[128];
};
#define GPR1 rax
#define GPR2 rdi
#define GPR3 uc.uc_mcontext.gregs[REG_RSI]
#define GPR4 uc.uc_mcontext.gregs[REG_RDX]
#define GPRx rax
#define GPR1 uc.uc_mcontext.gregs[REG_RDI]
#define GPR2 uc.uc_mcontext.gregs[REG_RSI]
#define GPR3 uc.uc_mcontext.gregs[REG_RDX]
#define GPR4 uc.uc_mcontext.gregs[REG_RCX]
#define GPRx uc.uc_mcontext.gregs[REG_RAX]
#undef __USE_GNU

View File

@ -3,7 +3,7 @@
struct _Context {
union {
struct _AddressSpace *as;
void *pdir;
uint32_t gpr[32];
};
uint32_t cause;

View File

@ -2,11 +2,12 @@
#define __ARCH_H__
struct _Context {
struct _AddressSpace *as;
void *cr3;
uintptr_t edi, esi, ebp, esp;
uintptr_t ebx, edx, ecx, eax; // Register saved by pushal
int irq; // # of irq
uintptr_t err, eip, cs, eflags; // Execution state before trap
uintptr_t usp;
uintptr_t eip, cs, eflags; // Execution state before trap
};
#define GPR1 eax

View File

@ -2,12 +2,10 @@
#define __ARCH_H__
struct _Context {
struct _AddressSpace *uvm;
uint32_t eax, ebx, ecx, edx,
esi, edi, ebp, esp3,
eip, eflags,
cs, ds, es, ss,
ss0, esp0;
void *uvm;
uint32_t ds, eax, ebx, ecx, edx,
esp0, esi, edi, ebp,
eip, cs, eflags, esp, ss3;
};
#define GPR1 eax
@ -16,4 +14,4 @@ struct _Context {
#define GPR4 edx
#define GPRx eax
#endif
#endif

View File

@ -0,0 +1,21 @@
#ifndef __ARCH_H__
#define __ARCH_H__
struct _Context {
void *uvm;
uint64_t rax, rbx, rcx, rdx,
rbp, rsi, rdi,
r8, r9, r10, r11,
r12, r13, r14, r15,
rip, cs, rflags,
rsp, ss, rsp0;
};
#define GPR1 rdi
#define GPR2 rsi
#define GPR3 rdx
#define GPR4 rcx
#define GPRx rax
#endif

View File

@ -4,9 +4,6 @@
#include <unistd.h>
#include <sys/types.h>
#define false 0
#define true 1
struct _Context {
};

View File

@ -1,6 +1,8 @@
#ifndef __NEMU_H__
#define __NEMU_H__
#include <klib-macros.h>
#include ISA_H // "x86.h", "mips32.h", ...
#if defined(__ISA_X86__)

View File

@ -10,9 +10,6 @@ typedef union {
int64_t val;
} R64;
#define true 1
#define false 0
static inline uint8_t inb(uintptr_t addr) { return *(volatile uint8_t *)addr; }
static inline uint16_t inw(uintptr_t addr) { return *(volatile uint16_t *)addr; }
static inline uint32_t inl(uintptr_t addr) { return *(volatile uint32_t *)addr; }

View File

@ -1,58 +1,34 @@
#ifndef __X86_H__
#define __X86_H__
// CPU rings
#define DPL_KERN 0x0 // Kernel (ring 0)
#define DPL_USER 0x3 // User (ring 3)
// Application segment type bits
// Application Segment type bits
#define STA_X 0x8 // Executable segment
#define STA_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)
// System segment type bits
// System Segment type bits
#define STS_T32A 0x9 // Available 32-bit TSS
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate
#define STS_IG 0xe // 32/64-bit Interrupt Gate
#define STS_TG 0xf // 32/64-bit Trap Gate
// Eflags register
#define FL_TF 0x00000100 // Trap Flag
// EFLAGS register
#define FL_IF 0x00000200 // Interrupt Enable
// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_PG 0x80000000 // Paging
// Page directory and page table constants
#define PGSIZE 4096 // Bytes mapped by a page
#define PGMASK 4095 // Mask for bit ops
#define NR_PDE 1024 // # directory entries per page directory
#define NR_PTE 1024 // # PTEs per page table
#define PGSHFT 12 // log2(PGSIZE)
#define PTXSHFT 12 // Offset of PTX in a linear address
#define PDXSHFT 22 // Offset of PDX in a linear address
#define CR4_PAE 0x00000020 // Physical Address Extension
// Page table/directory entry flags
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PWT 0x008 // Write-Through
#define PTE_PCD 0x010 // Cache-Disable
#define PTE_A 0x020 // Accessed
#define PTE_D 0x040 // Dirty
#define PTE_PS 0x080 // Large Page (1 GiB or 2 MiB)
// GDT entries
#define NR_SEG 6 // GDT size
#define SEG_KCODE 1 // Kernel code
#define SEG_KDATA 2 // Kernel data/stack
#define SEG_UCODE 3 // User code
#define SEG_UDATA 4 // User data/stack
#define SEG_TSS 5 // Global unique task state segement
#define KSEL(desc) (((desc) << 3) | DPL_KERN)
#define USEL(desc) (((desc) << 3) | DPL_USER)
// IDT size
#define NR_IRQ 256 // IDT size
// GDT selectors
#define KSEL(seg) (((seg) << 3) | DPL_KERN)
#define USEL(seg) (((seg) << 3) | DPL_USER)
// Interrupts and exceptions
#define T_IRQ0 32
@ -60,7 +36,7 @@
#define IRQ_KBD 1
#define IRQ_ERROR 19
#define IRQ_SPURIOUS 31
#define EX_DIV 0
#define EX_DE 0
#define EX_UD 6
#define EX_NM 7
#define EX_DF 8
@ -71,137 +47,175 @@
#define EX_PF 14
#define EX_MF 15
#define EX_SYSCALL 0x80
#define EX_YIELD 0x81
// List of interrupts and exceptions (#irq, DPL, hardware errorcode)
#define IRQS(_) \
_( 0, KERN, NOERR) _( 1, KERN, NOERR) \
_( 2, KERN, NOERR) _( 3, KERN, NOERR) \
_( 4, KERN, NOERR) _( 5, KERN, NOERR) \
_( 6, KERN, NOERR) _( 7, KERN, NOERR) \
_( 8, KERN, ERR) _( 9, KERN, NOERR) \
_( 10, KERN, ERR) _( 11, KERN, ERR) \
_( 12, KERN, ERR) _( 13, KERN, ERR) \
_( 14, KERN, ERR) _( 15, KERN, NOERR) \
_( 16, KERN, NOERR) _( 19, KERN, NOERR) \
_( 0, KERN, NOERR) \
_( 1, KERN, NOERR) \
_( 2, KERN, NOERR) \
_( 3, KERN, NOERR) \
_( 4, KERN, NOERR) \
_( 5, KERN, NOERR) \
_( 6, KERN, NOERR) \
_( 7, KERN, NOERR) \
_( 8, KERN, ERR) \
_( 9, KERN, NOERR) \
_( 10, KERN, ERR) \
_( 11, KERN, ERR) \
_( 12, KERN, ERR) \
_( 13, KERN, ERR) \
_( 14, KERN, ERR) \
_( 15, KERN, NOERR) \
_( 16, KERN, NOERR) \
_( 19, KERN, NOERR) \
_( 31, KERN, NOERR) \
_( 32, KERN, NOERR) _( 33, KERN, NOERR) \
_( 34, KERN, NOERR) _( 35, KERN, NOERR) \
_( 36, KERN, NOERR) _( 37, KERN, NOERR) \
_( 38, KERN, NOERR) _( 39, KERN, NOERR) \
_( 40, KERN, NOERR) _( 41, KERN, NOERR) \
_( 42, KERN, NOERR) _( 43, KERN, NOERR) \
_( 44, KERN, NOERR) _( 45, KERN, NOERR) \
_( 46, KERN, NOERR) _( 47, KERN, NOERR) \
_(128, USER, NOERR)
_( 32, KERN, NOERR) \
_( 33, KERN, NOERR) \
_( 34, KERN, NOERR) \
_( 35, KERN, NOERR) \
_( 36, KERN, NOERR) \
_( 37, KERN, NOERR) \
_( 38, KERN, NOERR) \
_( 39, KERN, NOERR) \
_( 40, KERN, NOERR) \
_( 41, KERN, NOERR) \
_( 42, KERN, NOERR) \
_( 43, KERN, NOERR) \
_( 44, KERN, NOERR) \
_( 45, KERN, NOERR) \
_( 46, KERN, NOERR) \
_( 47, KERN, NOERR) \
_(128, USER, NOERR) \
_(129, USER, NOERR)
// Below are only defined for c/cpp files
// AM-specific configurations
#define MAX_CPU 8
#define BOOTREC_ADDR 0x07000
#define MAINARG_ADDR 0x10000
// Below are only visible to c/cpp files
#ifndef __ASSEMBLER__
// +--------10------+-------10-------+---------12----------+
// | Page Directory | Page Table | Offset within Page |
// | Index | Index | |
// +----------------+----------------+---------------------+
// \--- PDX(va) --/ \--- PTX(va) --/\------ OFF(va) ------/
typedef uint32_t PTE;
typedef uint32_t PDE;
#define PDX(va) (((uint32_t)(va) >> PDXSHFT) & 0x3ff)
#define PTX(va) (((uint32_t)(va) >> PTXSHFT) & 0x3ff)
#define OFF(va) ((uint32_t)(va) & 0xfff)
#define ROUNDUP(a, sz) ((((uintptr_t)a)+(sz)-1) & ~((sz)-1))
#define ROUNDDOWN(a, sz) ((((uintptr_t)a)) & ~((sz)-1))
#define PTE_ADDR(pte) ((uint32_t)(pte) & ~0xfff)
#define PGADDR(d, t, o) ((uint32_t)((d) << PDXSHFT | (t) << PTXSHFT | (o)))
#include <stdint.h>
// Segment Descriptor
typedef struct SegDesc {
uint32_t lim_15_0 : 16; // Low bits of segment limit
uint32_t base_15_0 : 16; // Low bits of segment base address
uint32_t base_23_16 : 8; // Middle bits of segment base address
uint32_t type : 4 ; // Segment type (see STS_ constants)
uint32_t s : 1; // 0 = system, 1 = application
uint32_t dpl : 2; // Descriptor Privilege Level
uint32_t p : 1; // Present
uint32_t lim_19_16 : 4; // High bits of segment limit
uint32_t avl : 1; // Unused (available for software use)
uint32_t rsv1 : 1; // Reserved
uint32_t db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
uint32_t g : 1; // Granularity: limit scaled by 4K when set
uint32_t base_31_24 : 8; // High bits of segment base address
typedef struct {
uint32_t lim_15_0 : 16; // Low bits of segment limit
uint32_t base_15_0 : 16; // Low bits of segment base address
uint32_t base_23_16 : 8; // Middle bits of segment base address
uint32_t type : 4; // Segment type (see STS_ constants)
uint32_t s : 1; // 0 = system, 1 = application
uint32_t dpl : 2; // Descriptor Privilege Level
uint32_t p : 1; // Present
uint32_t lim_19_16 : 4; // High bits of segment limit
uint32_t avl : 1; // Unused (available for software use)
uint32_t l : 1; // 64-bit segment
uint32_t db : 1; // 32-bit segment
uint32_t g : 1; // Granularity: limit scaled by 4K when set
uint32_t base_31_24 : 8; // High bits of segment base address
} SegDesc;
#define SEG(type, base, lim, dpl) (SegDesc) \
{ ((lim) >> 12) & 0xffff, (uint32_t)(base) & 0xffff, \
((uint32_t)(base) >> 16) & 0xff, type, 1, dpl, 1, \
(uint32_t)(lim) >> 28, 0, 0, 1, 1, (uint32_t)(base) >> 24 }
#define SEG16(type, base, lim, dpl) (SegDesc) \
{ (lim) & 0xffff, (uint32_t)(base) & 0xffff, \
((uint32_t)(base) >> 16) & 0xff, type, 0, dpl, 1, \
(uint32_t)(lim) >> 16, 0, 0, 1, 0, (uint32_t)(base) >> 24 }
// Gate descriptors for interrupts and traps
typedef struct GateDesc {
uint32_t off_15_0 : 16; // Low 16 bits of offset in segment
uint32_t cs : 16; // Code segment selector
uint32_t args : 5; // # args, 0 for interrupt/trap gates
uint32_t rsv1 : 3; // Reserved(should be zero I guess)
uint32_t type : 4; // Type(STS_{TG,IG32,TG32})
uint32_t s : 1; // Must be 0 (system)
uint32_t dpl : 2; // Descriptor(meaning new) privilege level
uint32_t p : 1; // Present
uint32_t off_31_16 : 16; // High bits of offset in segment
} GateDesc;
typedef struct {
uint32_t off_15_0 : 16; // Low 16 bits of offset in segment
uint32_t cs : 16; // Code segment selector
uint32_t args : 5; // # args, 0 for interrupt/trap gates
uint32_t rsv1 : 3; // Reserved(should be zero I guess)
uint32_t type : 4; // Type(STS_{TG,IG32,TG32})
uint32_t s : 1; // Must be 0 (system)
uint32_t dpl : 2; // Descriptor(meaning new) privilege level
uint32_t p : 1; // Present
uint32_t off_31_16 : 16; // High bits of offset in segment
} GateDesc32;
#define GATE(type, cs, entry, dpl) (GateDesc) \
{ (uint32_t)(entry) & 0xffff, (cs), 0, 0, (type), 0, (dpl), \
1, (uint32_t)(entry) >> 16 }
typedef struct {
uint32_t off_15_0 : 16;
uint32_t cs : 16;
uint32_t isv : 3;
uint32_t zero1 : 5;
uint32_t type : 4;
uint32_t zero2 : 1;
uint32_t dpl : 2;
uint32_t p : 1;
uint32_t off_31_16 : 16;
uint32_t off_63_32 : 32;
uint32_t rsv : 32;
} GateDesc64;
// Task state segment format
typedef struct TSS {
// Task State Segment (TSS)
typedef struct {
uint32_t link; // Unused
uint32_t esp0; // Stack pointers and segment selectors
uint32_t ss0; // after an increase in privilege level
char padding[88];
} TSS;
uint32_t padding[23];
} __attribute__((packed)) TSS32;
// Interrupt and exception frame
typedef struct TrapFrame {
uint32_t edi, esi, ebp, esp_;
uint32_t ebx, edx, ecx, eax; // Register saved by pushal
uint32_t es, ds; // Segment register
int32_t irq; // # of irq
uint32_t err, eip, cs, eflags; // Execution state before trap
uint32_t esp, ss; // Used only when returning to DPL=3
} TrapFrame;
typedef struct {
uint32_t rsv;
uint64_t rsp0, rsp1, rsp2;
uint32_t padding[19];
} __attribute__((packed)) TSS64;
// Multiprocesor configuration
typedef struct MPConf { // configuration table header
uint8_t signature[4]; // "PCMP"
uint16_t length; // total table length
uint8_t version; // [14]
uint8_t checksum; // all bytes must add up to 0
uint8_t product[20]; // product id
uint32_t *oemtable; // OEM table pointer
uint16_t oemlength; // OEM table length
uint16_t entry; // entry count
uint32_t *lapicaddr; // address of local APIC
uint16_t xlength; // extended table length
uint8_t xchecksum; // extended table checksum
typedef struct { // configuration table header
uint8_t signature[4]; // "PCMP"
uint16_t length; // total table length
uint8_t version; // [14]
uint8_t checksum; // all bytes must add up to 0
uint8_t product[20]; // product id
uint32_t oemtable; // OEM table pointer
uint16_t oemlength; // OEM table length
uint16_t entry; // entry count
uint32_t lapicaddr; // address of local APIC
uint16_t xlength; // extended table length
uint8_t xchecksum; // extended table checksum
uint8_t reserved;
} MPConf;
typedef struct MPDesc {
int magic;
MPConf *conf; // MP config table addr
uint8_t length; // 1
uint8_t specrev; // [14]
uint8_t checksum; // all bytes add to 0
uint8_t type; // config type
uint8_t imcrp;
uint8_t reserved[3];
typedef struct {
int magic;
uint32_t conf; // MP config table addr
uint8_t length; // 1
uint8_t specrev; // [14]
uint8_t checksum; // all bytes add to 0
uint8_t type; // config type
uint8_t imcrp;
uint8_t reserved[3];
} MPDesc;
#define asm __asm__
typedef struct {
uint32_t jmp_code;
int32_t is_ap;
} BootRecord;
#define SEG16(type, base, lim, dpl) (SegDesc) \
{ (lim) & 0xffff, (uintptr_t)(base) & 0xffff, \
((uintptr_t)(base) >> 16) & 0xff, type, 0, dpl, 1, \
(uintptr_t)(lim) >> 16, 0, 0, 1, 0, (uintptr_t)(base) >> 24 }
#define SEG32(type, base, lim, dpl) (SegDesc) \
{ ((lim) >> 12) & 0xffff, (uintptr_t)(base) & 0xffff, \
((uintptr_t)(base) >> 16) & 0xff, type, 1, dpl, 1, \
(uintptr_t)(lim) >> 28, 0, 0, 1, 1, (uintptr_t)(base) >> 24 }
#define SEG64(type, dpl) (SegDesc) \
{ 0, 0, 0, type, 1, dpl, 1, 0, 0, 1, 0, 0 }
#define SEGTSS64(type, base, lim, dpl) (SegDesc) \
{ (lim) & 0xffff, (uint32_t)(base) & 0xffff, \
((uint32_t)(base) >> 16) & 0xff, type, 0, dpl, 1, \
(uint32_t)(lim) >> 16, 0, 0, 0, 0, (uint32_t)(base) >> 24 }
#define GATE32(type, cs, entry, dpl) (GateDesc32) \
{ (uint32_t)(entry) & 0xffff, (cs), 0, 0, (type), 0, (dpl), \
1, (uint32_t)(entry) >> 16 }
#define GATE64(type, cs, entry, dpl) (GateDesc64) \
{ (uint64_t)(entry) & 0xffff, (cs), 0, 0, (type), 0, (dpl), \
1, ((uint64_t)(entry) >> 16) & 0xffff, (uint64_t)(entry) >> 32, 0 }
// Instruction wrappers
static inline uint8_t inb(int port) {
uint8_t data;
@ -250,52 +264,89 @@ static inline void pause() {
}
static inline uint32_t get_efl() {
volatile uint32_t efl;
volatile uintptr_t efl;
asm volatile ("pushf; pop %0": "=r"(efl));
return efl;
}
static inline uint32_t get_cr0(void) {
volatile uint32_t val;
asm volatile ("movl %%cr0, %0" : "=r"(val));
static inline uintptr_t get_cr0(void) {
volatile uintptr_t val;
asm volatile ("mov %%cr0, %0" : "=r"(val));
return val;
}
static inline void set_cr0(uint32_t cr0) {
asm volatile ("movl %0, %%cr0" : : "r"(cr0));
static inline void set_cr0(uintptr_t cr0) {
asm volatile ("mov %0, %%cr0" : : "r"(cr0));
}
static inline void set_idt(GateDesc *idt, int size) {
volatile static uint16_t data[3];
data[0] = size - 1;
data[1] = (uint32_t)idt;
data[2] = (uint32_t)idt >> 16;
asm volatile ("lidt (%0)" : : "r"(data));
static inline void set_idt(void *idt, int size) {
static volatile struct {
int16_t size;
void *idt;
} __attribute__((packed)) data;
data.size = size;
data.idt = idt;
asm volatile ("lidt (%0)" : : "r"(&data));
}
static inline void set_gdt(SegDesc *gdt, int size) {
volatile static uint16_t data[3];
data[0] = size - 1;
data[1] = (uint32_t)gdt;
data[2] = (uint32_t)gdt >> 16;
asm volatile ("lgdt (%0)" : : "r"(data));
static inline void set_gdt(void *gdt, int size) {
static volatile struct {
int16_t size;
void *gdt;
} __attribute__((packed)) data;
data.size = size;
data.gdt = gdt;
asm volatile ("lgdt (%0)" : : "r"(&data));
}
static inline void set_tr(int selector) {
asm volatile ("ltr %0" : : "r"((uint16_t)selector));
}
static inline uint32_t get_cr2() {
volatile uint32_t val;
asm volatile ("movl %%cr2, %0" : "=r"(val));
static inline uintptr_t get_cr2() {
volatile uintptr_t val;
asm volatile ("mov %%cr2, %0" : "=r"(val));
return val;
}
static inline uintptr_t get_cr3() {
volatile uintptr_t val;
asm volatile ("mov %%cr3, %0" : "=r"(val));
return val;
}
static inline void set_cr3(void *pdir) {
asm volatile ("movl %0, %%cr3" : : "r"(pdir));
asm volatile ("mov %0, %%cr3" : : "r"(pdir));
}
#endif
static inline intptr_t xchg(volatile intptr_t *addr, intptr_t newval) {
intptr_t result;
asm volatile ("lock xchg %0, %1":
"+m"(*addr), "=a"(result) : "1"(newval) : "cc");
return result;
}
static inline uint64_t rdtsc() {
uint32_t lo, hi;
asm volatile ("rdtsc": "=a"(lo), "=d"(hi));
return ((uint64_t)hi << 32) | lo;
}
#define interrupt(id) \
asm volatile ("int $" #id);
static inline void stack_switch_call(void *sp, void *entry, uintptr_t arg) {
asm volatile (
#if __x86_64__
"movq %0, %%rsp; movq %2, %%rdi; jmp *%1" : : "b"((uintptr_t)sp), "d"(entry), "a"(arg)
#else
"movl %0, %%esp; movl %2, 4(%0); jmp *%1" : : "b"((uintptr_t)sp - 8), "d"(entry), "a"(arg)
#endif
);
}
static inline volatile BootRecord *boot_record() {
return (BootRecord *)BOOTREC_ADDR;
}
#endif // __ASSEMBLER__

View File

@ -11,7 +11,6 @@ void __am_tlb_refill(void);
_Context* __am_irq_handle(_Context *c) {
__am_get_cur_as(c);
_Context *next = c;
if (user_handler) {
_Event ev = {0};
uint32_t ex_code = (c->cause >> 2) & 0x1f;
@ -19,8 +18,8 @@ _Context* __am_irq_handle(_Context *c) {
switch (ex_code) {
case 0: ev.event = _EVENT_IRQ_TIMER; break;
case 2:
case 3: __am_tlb_refill(); return next;
case 8:
case 3: __am_tlb_refill(); return c;
case 8:
syscall_instr = *(uint32_t *)(c->epc);
ev.event = ((syscall_instr >> 6) == 1) ? _EVENT_YIELD : _EVENT_SYSCALL;
c->epc += 4;
@ -28,15 +27,13 @@ _Context* __am_irq_handle(_Context *c) {
default: ev.event = _EVENT_ERROR; break;
}
next = user_handler(ev, c);
if (next == NULL) {
next = c;
}
c = user_handler(ev, c);
assert(c != NULL);
}
__am_switch(next);
__am_switch(c);
return next;
return c;
}
extern void __am_asm_trap(void);
@ -52,16 +49,20 @@ int _cte_init(_Context*(*handler)(_Event, _Context*)) {
*(uint32_t *)0x80000000 = instr; // TLB refill exception
*(uint32_t *)(0x80000000 + 4) = 0; // delay slot
asm volatile("move $k0, $zero");
// register event handler
user_handler = handler;
return 0;
}
_Context *_kcontext(_Area stack, void (*entry)(void *), void *arg) {
_Context *c = (_Context*)stack.end - 1;
_Context *_kcontext(_Area kstack, void (*entry)(void *), void *arg) {
_Context *c = (_Context*)kstack.end - 1;
c->pdir = NULL;
c->epc = (uintptr_t)entry;
c->GPR2 = (uintptr_t)arg; // a0
c->gpr[29] = 0; // sp slot, used as usp
return c;
}

View File

@ -22,15 +22,22 @@ f(30) f(31)
#define CP0_CAUSE 13
#define CP0_EPC 14
#define SWAP(r1, r2, t) move $t, $r1; move $r1, $r2; move $r2, $t;
.set noat
.globl __am_asm_trap
__am_asm_trap:
move $k0, $sp
beqz $k0, in_kernel
SWAP(k0, sp, k1)
in_kernel:
addiu $sp, $sp, -CONTEXT_SIZE
MAP(REGS, PUSH)
sw $k0, OFFSET_SP($sp)
sw $k0, OFFSET_SP($sp) # k0 = (from user ? usp : 0)
move $k0, $zero
mflo $t0
mfhi $t1
mfc0 $t2, $CP0_CAUSE
@ -68,6 +75,12 @@ __am_asm_trap:
MAP(REGS, POP)
addiu $sp, $sp, CONTEXT_SIZE
lw $k0, OFFSET_SP($sp) # k0 = (from user ? usp : 0)
addiu $sp, $sp, CONTEXT_SIZE # ksp
beqz $k0, return
# return to user
SWAP(k0, sp, k1)
return:
eret

View File

@ -2,6 +2,7 @@
#include <klib.h>
#include <nemu.h>
#define USER_SPACE RANGE(0x40000000, 0x80000000)
#define PG_ALIGN __attribute((aligned(PGSIZE)))
static void* (*pgalloc_usr)(size_t) = NULL;
@ -16,35 +17,37 @@ int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
return 0;
}
int _protect(_AddressSpace *as) {
as->ptr = (PDE*)(pgalloc_usr(1));
return 0;
void _protect(_AddressSpace *as) {
as->ptr = (PDE*)(pgalloc_usr(PGSIZE));
as->pgsize = PGSIZE;
as->area = USER_SPACE;
}
void _unprotect(_AddressSpace *as) {
}
static _AddressSpace *cur_as = NULL;
static PDE *cur_pdir = NULL;
void __am_get_cur_as(_Context *c) {
c->as = cur_as;
c->pdir = cur_pdir;
}
void __am_tlb_clear();
void __am_switch(_Context *c) {
if (vme_enable) {
if (cur_as != NULL && cur_as->ptr != c->as->ptr) {
if (vme_enable && c->pdir != NULL) {
if (cur_pdir != c->pdir) {
__am_tlb_clear();
cur_pdir = c->pdir;
}
cur_as = c->as;
}
}
int _map(_AddressSpace *as, void *va, void *pa, int prot) {
PDE *pt = (PDE*)as->ptr;
PDE *pde = &pt[PDX(va)];
void _map(_AddressSpace *as, void *va, void *pa, int prot) {
assert((uintptr_t)va % PGSIZE == 0);
assert((uintptr_t)pa % PGSIZE == 0);
PDE *pdir = (PDE*)as->ptr;
PDE *pde = &pdir[PDX(va)];
if (!(*pde & PTE_V)) {
*pde = PTE_V | (uint32_t)pgalloc_usr(1);
*pde = PTE_V | (uint32_t)pgalloc_usr(PGSIZE);
}
PTE *pte = &((PTE*)PTE_ADDR(*pde))[PTX(va)];
if (!(*pte & PTE_V)) {
@ -63,16 +66,14 @@ int _map(_AddressSpace *as, void *va, void *pa, int prot) {
asm volatile ("tlbwi");
}
}
return 0;
}
_Context *_ucontext(_AddressSpace *as, _Area ustack, _Area kstack, void *entry, void *args) {
_Context *c = (_Context*)ustack.end - 1;
c->as = as;
_Context *_ucontext(_AddressSpace *as, _Area kstack, void *entry) {
_Context *c = (_Context*)kstack.end - 1;
c->pdir = as->ptr;
c->epc = (uintptr_t)entry;
c->status = 0x1;
c->gpr[29] = 1; // sp slot, used as usp, non-zero
return c;
}
@ -81,11 +82,8 @@ void __am_tlb_refill() {
asm volatile ("mfc0 %0, $10": "=r"(hi));
uint32_t va = hi & ~0x1fff;
PDE *pt = (PDE*)cur_as->ptr;
PDE *pde = &pt[PDX(va)];
// if (!(*pde & PTE_V)) {
// printf("hi = 0x%x, pt = 0x%x\n", hi, pt);
// }
assert(cur_pdir != NULL);
PDE *pde = &cur_pdir[PDX(va)];
assert(*pde & PTE_V);
PTE *pte = &((PTE*)PTE_ADDR(*pde))[PTX(va)];

View File

@ -24,7 +24,7 @@ C++ to perform the initialization. See `src/platform.cpp` for details. Also see
* `_DEVREG_TIMER_UPTIME` -> `gettimeofday()`
* `_DEVREG_TIMER_DATE` -> `localtime()`
* `_DEVREG_INPUT_KBD` -> SDL key events
* `_DEVREG_VIDEO_FBCTL` -> SDL texture update & render
* `_DEVREG_VIDEO_FBCTRL` -> SDL texture update & render
We provide an auto-sync frame buffer by periodically call SDL APIs to render the screen.
The contents written into frame buffer by applications will be eventually rendered.
@ -94,4 +94,5 @@ binary itselt, since these mappings are managed by Linux kernel. This is the oth
## MPE
NOT IMPLEMENTED YET
Each (virtual) processor is a Linux native process. Processors are forked upon `_mpe_init()`.
Code, data, and `_heap` are mapped as shared memory maps across children processes.

View File

@ -1,56 +1,195 @@
#include <am.h>
#include <klib.h>
#include <sys/time.h>
#include "platform.h"
#define TIMER_HZ 100
#define YIELD_INSTR "0xff,0x14,0x25,0x08,0x00,0x10,0x00" // callq *0x100008
#define YIELD_INSTR_LEN ((sizeof(YIELD_INSTR)) / 5) // sizeof() counts the '\0' byte
#define SYSCALL_INSTR_LEN YIELD_INSTR_LEN
// if this fails, allocate larger space in trap.S for ucontext
static_assert(sizeof(ucontext_t) < 1024);
static_assert(SYSCALL_INSTR_LEN == 7);
static _Context* (*user_handler)(_Event, _Context*) = NULL;
void __am_asm_trap();
void __am_ret_from_trap();
void __am_get_example_uc(_Context *c);
void __am_get_cur_as(_Context *c);
void __am_kcontext_start();
void __am_switch(_Context *c);
int __am_in_userspace(void *addr);
void __am_pmem_protect();
void __am_pmem_unprotect();
void __am_irq_handle(_Context *c) {
getcontext(&c->uc);
__am_get_cur_as(c);
void __am_panic_on_return() { panic("should not reach here\n"); }
_Event e;
e.event = ((uint32_t)c->rax == -1 ? _EVENT_YIELD : _EVENT_SYSCALL);
_Context *ret = user_handler(e, c);
if (ret != NULL) {
c = ret;
}
static void irq_handle(_Context *c) {
c->vm_head = thiscpu->vm_head;
c->ksp = thiscpu->ksp;
c = user_handler(thiscpu->ev, c);
assert(c != NULL);
__am_switch(c);
c->uc.uc_mcontext.gregs[REG_RIP] = (uintptr_t)__am_ret_from_trap;
c->uc.uc_mcontext.gregs[REG_RSP] = (uintptr_t)c;
setcontext(&c->uc);
// magic call to restore context
asm volatile("call *0x100010" : : "a" (c));
__am_panic_on_return();
}
static void setup_stack(uintptr_t event, ucontext_t *uc) {
void *rip = (void *)uc->uc_mcontext.gregs[REG_RIP];
extern uint8_t _start, _etext;
int trap_from_user = __am_in_userspace(rip);
int signal_safe = IN_RANGE(rip, RANGE(&_start, &_etext)) || trap_from_user ||
// Hack here: "+13" points to the instruction after syscall. This is the
// instruction which will trigger the pending signal if interrupt is enabled.
(rip == (void *)&sigprocmask + 13);
if (((event == _EVENT_IRQ_IODEV) || (event == _EVENT_IRQ_TIMER)) && !signal_safe) {
// Shared libraries contain code which are not reenterable.
// If the signal comes when executing code in shared libraries,
// the signal handler can not call any function which is not signal-safe,
// else the behavior is undefined (may be dead lock).
// To handle this, we just refuse to handle the signal and return directly
// to pretend missing the interrupt.
// See man 7 signal-safety for more information.
return;
}
if (trap_from_user) __am_pmem_unprotect();
// skip the instructions causing SIGSEGV for syscall and yield
if (event == _EVENT_SYSCALL) { rip += SYSCALL_INSTR_LEN; }
else if (event == _EVENT_YIELD) { rip += YIELD_INSTR_LEN; }
uc->uc_mcontext.gregs[REG_RIP] = (uintptr_t)rip;
// switch to kernel stack if we were previously in user space
_Context *c = (void *)(trap_from_user ? thiscpu->ksp : uc->uc_mcontext.gregs[REG_RSP]);
c --;
// save the context on the stack
c->uc = *uc;
// disable interrupt
__am_get_intr_sigmask(&uc->uc_sigmask);
// call irq_handle after returning from the signal handler
uc->uc_mcontext.gregs[REG_RDI] = (uintptr_t)c;
uc->uc_mcontext.gregs[REG_RIP] = (uintptr_t)irq_handle;
uc->uc_mcontext.gregs[REG_RSP] = (uintptr_t)c;
}
static void iret(ucontext_t *uc) {
_Context *c = (void *)uc->uc_mcontext.gregs[REG_RAX];
// restore the context
*uc = c->uc;
thiscpu->ksp = c->ksp;
if (__am_in_userspace((void *)uc->uc_mcontext.gregs[REG_RIP])) __am_pmem_protect();
}
static void sig_handler(int sig, siginfo_t *info, void *ucontext) {
thiscpu->ev = (_Event) {0};
thiscpu->ev.event = _EVENT_ERROR;
switch (sig) {
case SIGUSR1: thiscpu->ev.event = _EVENT_IRQ_IODEV; break;
case SIGVTALRM: thiscpu->ev.event = _EVENT_IRQ_TIMER; break;
case SIGSEGV:
if (info->si_code == SEGV_ACCERR) {
switch ((uintptr_t)info->si_addr) {
case 0x100000: thiscpu->ev.event = _EVENT_SYSCALL; break;
case 0x100008: thiscpu->ev.event = _EVENT_YIELD; break;
case 0x100010: iret(ucontext); return;
}
}
if (__am_in_userspace(info->si_addr)) {
assert(thiscpu->ev.event == _EVENT_ERROR);
thiscpu->ev.event = _EVENT_PAGEFAULT;
switch (info->si_code) {
case SEGV_MAPERR: thiscpu->ev.cause = _PROT_READ; break;
// we do not support mapped user pages with _PROT_NONE
case SEGV_ACCERR: thiscpu->ev.cause = _PROT_WRITE; break;
default: assert(0);
}
thiscpu->ev.ref = (uintptr_t)info->si_addr;
}
if (thiscpu->ev.event == _EVENT_ERROR) {
uintptr_t rip = ((ucontext_t *)ucontext)->uc_mcontext.gregs[REG_RIP];
printf("Unhandle SIGSEGV at rip = %p, badaddr = %p\n", rip, info->si_addr);
}
break;
default: assert(0);
}
assert(thiscpu->ev.event != _EVENT_ERROR);
setup_stack(thiscpu->ev.event, ucontext);
}
// signal handlers are inherited across fork()
static void install_signal_handler() {
struct sigaction s;
memset(&s, 0, sizeof(s));
s.sa_sigaction = sig_handler;
s.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK;
__am_get_intr_sigmask(&s.sa_mask);
int ret = sigaction(SIGVTALRM, &s, NULL);
assert(ret == 0);
ret = sigaction(SIGUSR1, &s, NULL);
assert(ret == 0);
ret = sigaction(SIGSEGV, &s, NULL);
assert(ret == 0);
}
// setitimer() are inherited across fork(), should be called again from children
void __am_init_timer_irq() {
_intr_write(0);
struct itimerval it = {};
it.it_value.tv_sec = 0;
it.it_value.tv_usec = 1000000 / TIMER_HZ;
it.it_interval = it.it_value;
int ret = setitimer(ITIMER_VIRTUAL, &it, NULL);
assert(ret == 0);
}
int _cte_init(_Context*(*handler)(_Event, _Context*)) {
void *start = (void *)0x100000;
*(uintptr_t *)start = (uintptr_t)__am_asm_trap;
user_handler = handler;
install_signal_handler();
__am_init_timer_irq();
return 0;
}
_Context *_kcontext(_Area stack, void (*entry)(void *), void *arg) {
_Context *c = (_Context*)stack.end - 1;
_Context* _kcontext(_Area kstack, void (*entry)(void *), void *arg) {
_Context *c = (_Context*)kstack.end - 1;
__am_get_example_uc(c);
c->rip = (uintptr_t)entry;
c->uc.uc_mcontext.gregs[REG_RIP] = (uintptr_t)__am_kcontext_start;
c->uc.uc_mcontext.gregs[REG_RSP] = (uintptr_t)kstack.end;
int ret = sigemptyset(&(c->uc.uc_sigmask)); // enable interrupt
assert(ret == 0);
c->vm_head = NULL;
c->GPR1 = (uintptr_t)arg;
c->GPR2 = (uintptr_t)entry;
return c;
}
void _yield() {
asm volatile("call *0x100000": : "a"(-1));
asm volatile (".byte " YIELD_INSTR);
}
int _intr_read() {
return 0;
sigset_t set;
int ret = sigprocmask(0, NULL, &set);
assert(ret == 0);
return __am_is_sigmask_sti(&set);
}
void _intr_write(int enable) {
extern sigset_t __am_intr_sigmask;
// NOTE: sigprocmask does not supported in multithreading
int ret = sigprocmask(enable ? SIG_UNBLOCK : SIG_BLOCK, &__am_intr_sigmask, NULL);
assert(ret == 0);
}

View File

@ -1,6 +1,6 @@
#include <am.h>
#include <amdev.h>
#include <SDL2/SDL.h>
#include "../platform.h"
#define KEYDOWN_MASK 0x8000
@ -19,8 +19,8 @@ static int event_thread(void *args) {
while (1) {
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_QUIT: _exit(0); break;
case SDL_KEYDOWN:
case SDL_QUIT: _halt(0);
case SDL_KEYDOWN:
case SDL_KEYUP:
{
SDL_Keysym k = event.key.keysym;
@ -32,6 +32,7 @@ static int event_thread(void *args) {
key_queue[key_r] = am_code;
key_r = (key_r + 1) % KEY_QUEUE_LEN;
SDL_UnlockMutex(key_queue_lock);
kill(getpid(), SIGUSR1);
}
}
break;

View File

@ -4,7 +4,7 @@
#define W 400
#define H 300
#define FPS 30
#define FPS 60
static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
@ -48,8 +48,8 @@ size_t __am_video_read(uintptr_t reg, void *buf, size_t size) {
size_t __am_video_write(uintptr_t reg, void *buf, size_t size) {
switch (reg) {
case _DEVREG_VIDEO_FBCTL: {
_DEV_VIDEO_FBCTL_t *ctl = (_DEV_VIDEO_FBCTL_t *)buf;
case _DEVREG_VIDEO_FBCTRL: {
_DEV_VIDEO_FBCTRL_t *ctl = (_DEV_VIDEO_FBCTRL_t *)buf;
int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h;
uint32_t *pixels = ctl->pixels;
int cp_bytes = sizeof(uint32_t) * min(w, W - x);

View File

@ -10,7 +10,17 @@ size_t __am_timer_read(uintptr_t reg, void *buf, size_t size);
size_t __am_video_read(uintptr_t reg, void *buf, size_t size);
size_t __am_video_write(uintptr_t reg, void *buf, size_t size);
static int init_flag = 0;
int _ioe_init() {
if (_cpu() != 0) return 0;
init_flag ++;
if (init_flag == 1) {
// Calling fork() in MPE after SDL_Init() will cause trouble.
// Postpone the ioe initialization to fix this issue.
return 0;
}
__am_timer_init();
__am_video_init();
__am_input_init();
@ -18,6 +28,7 @@ int _ioe_init() {
}
size_t _io_read(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
if (init_flag == 1) { _ioe_init(); }
switch (dev) {
case _DEV_INPUT: return __am_input_read(reg, buf, size);
case _DEV_TIMER: return __am_timer_read(reg, buf, size);
@ -27,6 +38,7 @@ size_t _io_read(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
}
size_t _io_write(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
if (init_flag == 1) { _ioe_init(); }
switch (dev) {
case _DEV_VIDEO: return __am_video_write(reg, buf, size);
}

View File

@ -1,21 +1,33 @@
#include <am.h>
#include <stdatomic.h>
#include "platform.h"
int __am_mpe_init = 0;
int _mpe_init(void (*entry)()) {
__am_mpe_init = 1;
for (int i = 1; i < _ncpu(); i++) {
if (fork() == 0) {
thiscpu->cpuid = i;
__am_init_timer_irq();
entry();
}
}
entry();
return 1;
}
int _ncpu() {
return 1;
}
int _cpu() {
printf("MP entry should not return\n");
assert(0);
return 0;
}
intptr_t _atomic_xchg(volatile intptr_t *addr, intptr_t newval) {
intptr_t result;
asm volatile ("lock xchg %0, %1":
"+m"(*addr), "=a"(result) : "1"(newval) : "cc");
return result;
int _ncpu() {
extern int __am_ncpu;
return __am_ncpu;
}
int _cpu() {
return thiscpu->cpuid;
}
intptr_t _atomic_xchg(volatile intptr_t *addr, intptr_t newval) {
return atomic_exchange((atomic_intptr_t *)addr, newval);
}

View File

@ -1,56 +1,187 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/auxv.h>
#include <fcntl.h>
#include <elf.h>
#include <stdlib.h>
#include <klib.h>
#include "platform.h"
#define PMEM_SHM_FILE "/native-pmem"
#define MAX_CPU 16
#define TRAP_PAGE_START (void *)0x100000
#define PMEM_START (void *)0x3000000 // for nanos-lite with vme disabled
#define PMEM_SIZE (128 * 1024 * 1024) // 128MB
#define PMEM_MAP_START (uintptr_t)0x100000
#define PMEM_MAP_END (uintptr_t)PMEM_SIZE
#define PMEM_MAP_SIZE (PMEM_MAP_END - PMEM_MAP_START)
static int pmem_fd = 0;
static char pmem_shm_file[] = "/native-pmem-XXXXXX";
static void *pmem = NULL;
static ucontext_t uc_example = {};
static int sys_pgsz;
sigset_t __am_intr_sigmask = {};
__am_cpu_t *__am_cpu_struct = NULL;
int __am_ncpu = 0;
int __am_pgsize;
static void save_context_handler(int sig, siginfo_t *info, void *ucontext) {
memcpy(&uc_example, ucontext, sizeof(uc_example));
}
static void save_example_context() {
// getcontext() does not save segment registers. In the signal
// handler, restoring a context previously saved by getcontext()
// will trigger segmentation fault because of the invalid segment
// registers. So we save the example context during signal handling
// to get a context with everything valid.
struct sigaction s;
memset(&s, 0, sizeof(s));
s.sa_sigaction = save_context_handler;
s.sa_flags = SA_SIGINFO;
int ret = sigaction(SIGUSR1, &s, NULL);
assert(ret == 0);
raise(SIGUSR1);
s.sa_flags = 0;
s.sa_handler = SIG_DFL;
ret = sigaction(SIGUSR1, &s, NULL);
assert(ret == 0);
}
static void setup_sigaltstack() {
stack_t ss;
ss.ss_sp = thiscpu->sigstack;
ss.ss_size = sizeof(thiscpu->sigstack);
ss.ss_flags = 0;
int ret = sigaltstack(&ss, NULL);
assert(ret == 0);
}
int main(const char *args);
static void init_platform() __attribute__((constructor));
static void init_platform() {
pmem_fd = shm_open(PMEM_SHM_FILE, O_RDWR | O_CREAT, 0700);
// create shared memory object and set up mapping to simulate the physical memory
assert(access("/", W_OK) != 0);
assert(mkstemp(pmem_shm_file) < 0);
pmem_fd = shm_open(pmem_shm_file, O_RDWR | O_CREAT | O_EXCL, 0700);
assert(pmem_fd != -1);
assert(0 == ftruncate(pmem_fd, PMEM_SIZE));
void *ret = mmap((void *)PMEM_MAP_START, PMEM_MAP_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED | MAP_FIXED, pmem_fd, PMEM_MAP_START);
pmem = mmap(PMEM_START, PMEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED | MAP_FIXED, pmem_fd, 0);
assert(_heap.start != (void *)-1);
// allocate private per-cpu structure
thiscpu = mmap(NULL, sizeof(*thiscpu), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
assert(thiscpu != (void *)-1);
thiscpu->cpuid = 0;
thiscpu->vm_head = NULL;
// create trap page to receive syscall and yield by SIGSEGV
sys_pgsz = sysconf(_SC_PAGESIZE);
void *ret = mmap(TRAP_PAGE_START, sys_pgsz, PROT_NONE,
MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
assert(ret != (void *)-1);
_heap.start = (void *)(PMEM_MAP_START + 4096); // this is to skip the trap entry
_heap.end = (void *)PMEM_MAP_END;
// remap writable sections as MAP_SHARED
Elf64_Phdr *phdr = (void *)getauxval(AT_PHDR);
int phnum = (int)getauxval(AT_PHNUM);
int i;
int ret2;
for (i = 0; i < phnum; i ++) {
if (phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_W)) {
// allocate temporary memory
extern char end;
void *vaddr = (void *)&end - phdr[i].p_memsz;
uintptr_t pad = (uintptr_t)vaddr & 0xfff;
void *vaddr_align = vaddr - pad;
uintptr_t size = phdr[i].p_memsz + pad;
void *temp_mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
assert(temp_mem != (void *)-1);
getcontext(&uc_example);
// save data and bss sections
memcpy(temp_mem, vaddr_align, size);
// save the addresses of library functions which will be used after munamp()
// since calling the library functions requires accessing GOT, which will be unmapped
void *(*volatile mmap_libc)(void *, size_t, int, int, int, off_t) = &mmap;
void *(*volatile memcpy_libc)(void *, const void *, size_t) = &memcpy;
// unmap the data and bss sections
ret2 = munmap(vaddr_align, size);
assert(ret2 == 0);
// map the sections again with MAP_SHARED, which will be shared across fork()
ret = mmap_libc(vaddr_align, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
assert(ret == vaddr_align);
// restore the data in the sections
memcpy_libc(vaddr_align, temp_mem, size);
// unmap the temporary memory
ret2 = munmap(temp_mem, size);
assert(ret2 == 0);
}
}
// set up the AM heap
_heap = RANGE(pmem, pmem + PMEM_SIZE);
// initialize sigmask for interrupts
ret2 = sigemptyset(&__am_intr_sigmask);
assert(ret2 == 0);
ret2 = sigaddset(&__am_intr_sigmask, SIGVTALRM);
assert(ret2 == 0);
ret2 = sigaddset(&__am_intr_sigmask, SIGUSR1);
assert(ret2 == 0);
// setup alternative signal stack
setup_sigaltstack();
// save the context template
save_example_context();
__am_get_intr_sigmask(&uc_example.uc_sigmask);
// disable interrupts by default
_intr_write(0);
// set ncpu
const char *smp = getenv("smp");
__am_ncpu = smp ? atoi(smp) : 1;
assert(0 < __am_ncpu && __am_ncpu <= MAX_CPU);
// set pgsize
const char *pgsize = getenv("pgsize");
__am_pgsize = pgsize ? atoi(pgsize) : sys_pgsz;
assert(__am_pgsize > 0 && __am_pgsize % sys_pgsz == 0);
const char *args = getenv("mainargs");
exit(main(args ? args : "")); // call main here!
_halt(main(args ? args : "")); // call main here!
}
static void exit_platform() __attribute__((constructor));
static void exit_platform() {
int ret = munmap((void *)PMEM_MAP_START, PMEM_MAP_SIZE);
assert(ret == 0);
close(pmem_fd);
ret = shm_unlink(PMEM_SHM_FILE);
assert(ret == 0);
void __am_exit_platform(int code) {
int ret = shm_unlink(pmem_shm_file);
printf("Unlink pmem_shm_file %s\n", (ret == 0 ? "successfully" : "fail"));
// let Linux clean up other resource
extern int __am_mpe_init;
if (__am_mpe_init && _ncpu() > 1) kill(0, SIGKILL);
exit(code);
}
void __am_shm_mmap(void *va, void *pa, int prot) {
void *ret = mmap(va, 4096, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED | MAP_FIXED, pmem_fd, (uintptr_t)pa);
// translate AM prot to mmap prot
int mmap_prot = PROT_NONE;
// we do not support executable bit, so mark
// all readable pages executable as well
if (prot & _PROT_READ) mmap_prot |= PROT_READ | PROT_EXEC;
if (prot & _PROT_WRITE) mmap_prot |= PROT_WRITE;
void *ret = mmap(va, __am_pgsize, mmap_prot,
MAP_SHARED | MAP_FIXED, pmem_fd, (uintptr_t)(pa - pmem));
assert(ret != (void *)-1);
}
void __am_shm_munmap(void *va) {
int ret = munmap(va, 4096);
int ret = munmap(va, __am_pgsize);
assert(ret == 0);
}
@ -58,6 +189,24 @@ void __am_get_example_uc(_Context *r) {
memcpy(&r->uc, &uc_example, sizeof(uc_example));
}
void __am_get_intr_sigmask(sigset_t *s) {
memcpy(s, &__am_intr_sigmask, sizeof(__am_intr_sigmask));
}
int __am_is_sigmask_sti(sigset_t *s) {
return !sigismember(s, SIGVTALRM);
}
void __am_pmem_protect() {
int ret = mprotect(PMEM_START, PMEM_SIZE, PROT_NONE);
assert(ret == 0);
}
void __am_pmem_unprotect() {
int ret = mprotect(PMEM_START, PMEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
assert(ret == 0);
}
// This dummy function will be called in trm.c.
// The purpose of this dummy function is to let linker add this file to the object
// file set. Without it, the constructor of @_init_platform will not be linked.

28
am/src/native/platform.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef __PLATFORM_H__
#define __PLATFORM_H__
#include <am.h>
#include <unistd.h>
#include <signal.h>
#include <klib.h>
#include <klib-macros.h>
void __am_get_example_uc(_Context *r);
void __am_get_intr_sigmask(sigset_t *s);
int __am_is_sigmask_sti(sigset_t *s);
void __am_init_timer_irq();
void __am_shm_mmap(void *va, void *pa, int prot);
void __am_shm_munmap(void *va);
// per-cpu structure
typedef struct {
void *vm_head;
uintptr_t ksp;
int cpuid;
_Event ev; // similar to cause register in mips/riscv
uint8_t sigstack[SIGSTKSZ];
} __am_cpu_t;
extern __am_cpu_t *__am_cpu_struct;
#define thiscpu __am_cpu_struct
#endif

View File

@ -1,19 +1,10 @@
.global __am_ret_from_trap
.global __am_asm_trap
.global __am_kcontext_start
__am_kcontext_start:
// rdi = arg, rsi = entry
__am_asm_trap:
# getcontext() does not preserve %rax, save it manually
pushq %rax
pushq %rdi
# must have sizeof(ucontect_t) < 1024
subq $1024, %rsp
movq %rsp, %rdi
call __am_irq_handle
__am_ret_from_trap:
addq $1024, %rsp
popq %rdi
popq %rax
retq
// (rsp + 8) should be multiple of 16 when
// control is transfered to the function entry point.
// See amd64 ABI manual for more details
andq $0xfffffffffffffff0, %rsp
call *%rsi
call __am_panic_on_return

View File

@ -1,19 +1,22 @@
#include <am.h>
#include <stdio.h>
#include <stdlib.h>
extern void __am_platform_dummy();
void __am_platform_dummy();
void __am_exit_platform(int code);
void _trm_init() {
__am_platform_dummy();
}
void _putc(char ch) {
putchar(ch);
fputc(ch, stderr);
}
void _halt(int code) {
printf("Exit (%d)\n", code);
exit(code);
__am_exit_platform(code);
printf("Should not reach here!\n");
while (1);
}
_Area _heap = {};

View File

@ -1,120 +1,116 @@
#include <am.h>
#include <stdlib.h>
#include <klib.h>
#include "platform.h"
#define PGSIZE 4096
#define PGSHIFT 12
#define USER_SPACE RANGE(0x40000000, 0xc0000000)
typedef struct PageMap {
uintptr_t vpn;
uintptr_t ppn;
void *va;
void *pa;
struct PageMap *next;
int prot;
int is_mapped;
} PageMap;
#define list_foreach(p, head) \
for (p = head; p != NULL; p = p->next)
void __am_shm_mmap(void *va, void *pa, int prot);
void __am_shm_munmap(void *va);
for (p = ((PageMap *)(head))->next; p != NULL; p = p->next)
extern int __am_pgsize;
static int vme_enable = 0;
static void* (*pgalloc)(size_t) = NULL;
static void (*pgfree)(void *) = NULL;
int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
// we do not need to ask MM to get a page from OS,
// since we can call malloc() in native
pgalloc = pgalloc_f;
pgfree = pgfree_f;
vme_enable = 1;
return 0;
}
int _protect(_AddressSpace *as) {
as->ptr = NULL;
as->pgsize = PGSIZE;
return 0;
void _protect(_AddressSpace *as) {
assert(as != NULL);
as->ptr = pgalloc(__am_pgsize); // used as head of the list
as->pgsize = __am_pgsize;
as->area = USER_SPACE;
}
void _unprotect(_AddressSpace *as) {
}
static _AddressSpace empty_as = { .ptr = NULL };
static _AddressSpace *cur_as = &empty_as;
void __am_get_cur_as(_Context *c) {
c->as = cur_as;
}
void __am_switch(_Context *c) {
if (!vme_enable) return;
_AddressSpace *as = c->as;
PageMap *head = c->vm_head;
if (head == thiscpu->vm_head) return;
PageMap *pp;
if (thiscpu->vm_head != NULL) {
// munmap all mappings
list_foreach(pp, thiscpu->vm_head) {
if (pp->is_mapped) {
__am_shm_munmap(pp->va);
pp->is_mapped = false;
}
}
}
if (head != NULL) {
// mmap all mappings
list_foreach(pp, head) {
assert(IN_RANGE(pp->va, USER_SPACE));
__am_shm_mmap(pp->va, pp->pa, pp->prot);
pp->is_mapped = true;
}
}
thiscpu->vm_head = head;
}
void _map(_AddressSpace *as, void *va, void *pa, int prot) {
assert(IN_RANGE(va, USER_SPACE));
assert((uintptr_t)va % __am_pgsize == 0);
assert((uintptr_t)pa % __am_pgsize == 0);
assert(as != NULL);
if (as == cur_as) return;
PageMap *pp;
// munmap all mappings
list_foreach(pp, cur_as->ptr) {
if (pp->is_mapped) {
__am_shm_munmap((void *)(pp->vpn << PGSHIFT));
pp->is_mapped = false;
}
PageMap *pp = NULL;
PageMap *vm_head = as->ptr;
assert(vm_head != NULL);
list_foreach(pp, vm_head) {
if (pp->va == va) break;
}
// mmap all mappings
list_foreach(pp, as->ptr) {
__am_shm_mmap((void *)(pp->vpn << PGSHIFT), (void *)(pp->ppn << PGSHIFT), 0);
pp->is_mapped = true;
if (pp == NULL) {
pp = pgalloc(__am_pgsize); // this will waste memory, any better idea?
}
pp->va = va;
pp->pa = pa;
pp->prot = prot;
pp->is_mapped = false;
// add after to vm_head to keep vm_head unchanged,
// since vm_head is a key to describe an address space
pp->next = vm_head->next;
vm_head->next = pp;
cur_as = as;
}
int _map(_AddressSpace *as, void *va, void *pa, int prot) {
uintptr_t vpn = (uintptr_t)va >> PGSHIFT;
PageMap *pp;
list_foreach(pp, as->ptr) {
// can not remap
// Actually this is allowed according to the semantics of AM API,
// but we do this to catch unexcepted behavior from Nanos-lite
if (pp->vpn == vpn) {
printf("check remap: %p -> %p, but previously %p -> %p\n", va, pa, pp->vpn << PGSHIFT, pp->ppn << PGSHIFT);
assert(pp->ppn == ((uintptr_t)pa >> PGSHIFT));
return 0;
}
}
pp = malloc(sizeof(PageMap));
pp->vpn = vpn;
pp->ppn = (uintptr_t)pa >> PGSHIFT;
pp->next = as->ptr;
as->ptr = pp;
if (as == cur_as) {
if (vm_head == thiscpu->vm_head) {
// enforce the map immediately
__am_shm_mmap((void *)(pp->vpn << PGSHIFT), (void *)(pp->ppn << PGSHIFT), 0);
__am_shm_mmap(pp->va, pp->pa, pp->prot);
pp->is_mapped = true;
}
else {
pp->is_mapped = false;
}
return 0;
}
void __am_get_example_uc(_Context *r);
_Context* _ucontext(_AddressSpace *as, _Area kstack, void *entry) {
_Context *c = (_Context*)kstack.end - 1;
_Context *_ucontext(_AddressSpace *as, _Area ustack, _Area kstack, void *entry, void *args) {
ustack.end -= 1 * sizeof(uintptr_t); // 1 = retaddr
uintptr_t ret = (uintptr_t)ustack.end;
*(uintptr_t *)ret = 0;
_Context *c = (_Context*)ustack.end - 1;
__am_get_example_uc(c);
c->rip = (uintptr_t)entry;
c->as = as;
c->uc.uc_mcontext.gregs[REG_RIP] = (uintptr_t)entry;
c->uc.uc_mcontext.gregs[REG_RSP] = (uintptr_t)USER_SPACE.end;
c->uc.uc_mcontext.gregs[REG_RDI] = 0;
c->uc.uc_mcontext.gregs[REG_RSI] = ret; // ???
c->uc.uc_mcontext.gregs[REG_RDX] = ret; // ???
int ret = sigemptyset(&(c->uc.uc_sigmask)); // enable interrupt
assert(ret == 0);
c->vm_head = as->ptr;
c->ksp = (uintptr_t)kstack.end;
return c;
}
int __am_in_userspace(void *addr) {
return vme_enable && thiscpu->vm_head != NULL && IN_RANGE(addr, USER_SPACE);
}

View File

@ -22,7 +22,7 @@ int __am_event_thread(void) {
if (event.type == NDL_EVENT_TIMER) {
__am_set_systime(event.data);
}
if (event.type == NDL_EVENT_KEYUP || event.type == NDL_EVENT_KEYDOWN) {
int keydown = event.type == NDL_EVENT_KEYDOWN;
int scancode = event.data;
@ -31,10 +31,10 @@ int __am_event_thread(void) {
key_queue[key_r] = am_code;
key_r = (key_r + 1) % KEY_QUEUE_LEN;
}
return true;
return 1;
}
return false;
return 0;
}
size_t __am_input_read(uintptr_t reg, void *buf, size_t size) {

View File

@ -23,8 +23,8 @@ size_t __am_video_read(uintptr_t reg, void *buf, size_t size) {
size_t __am_video_write(uintptr_t reg, void *buf, size_t size) {
switch (reg) {
case _DEVREG_VIDEO_FBCTL: {
_DEV_VIDEO_FBCTL_t *ctl = (_DEV_VIDEO_FBCTL_t *)buf;
case _DEVREG_VIDEO_FBCTRL: {
_DEV_VIDEO_FBCTRL_t *ctl = (_DEV_VIDEO_FBCTRL_t *)buf;
int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h;
uint32_t *pixels = ctl->pixels;
NDL_DrawRect(pixels, x, y, w, h);

View File

@ -1,14 +1,11 @@
#include <am.h>
#include <stdio.h>
#include <stdlib.h>
#include <klib-macros.h>
#define HEAP_SIZE (8 * 1024 * 1024)
static uint8_t heap[HEAP_SIZE] = {};
_Area _heap = {
.start = heap,
.end = heap + HEAP_SIZE
};
_Area _heap = RANGE(heap, heap + HEAP_SIZE);
void _trm_init() {
}

View File

@ -20,8 +20,8 @@ size_t __am_video_read(uintptr_t reg, void *buf, size_t size) {
size_t __am_video_write(uintptr_t reg, void *buf, size_t size) {
switch (reg) {
case _DEVREG_VIDEO_FBCTL: {
_DEV_VIDEO_FBCTL_t *ctl = (_DEV_VIDEO_FBCTL_t *)buf;
case _DEVREG_VIDEO_FBCTRL: {
_DEV_VIDEO_FBCTRL_t *ctl = (_DEV_VIDEO_FBCTRL_t *)buf;
int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h;
uint32_t *pixels = ctl->pixels;
assert(x + w <= W && y + h <= H);

View File

@ -5,10 +5,7 @@ extern char _heap_start;
extern char _heap_end;
int main(const char *args);
_Area _heap = {
.start = &_heap_start,
.end = &_heap_end,
};
_Area _heap = RANGE(&_heap_start, &_heap_end);
void _putc(char ch) {
outb(SERIAL_PORT, ch);

View File

@ -10,7 +10,6 @@ void __am_switch(_Context *c);
_Context* __am_irq_handle(_Context *c) {
__am_get_cur_as(c);
_Context *next = c;
if (user_handler) {
_Event ev = {0};
switch (c->cause) {
@ -22,15 +21,13 @@ _Context* __am_irq_handle(_Context *c) {
default: ev.event = _EVENT_ERROR; break;
}
next = user_handler(ev, c);
if (next == NULL) {
next = c;
}
c = user_handler(ev, c);
assert(c != NULL);
}
__am_switch(next);
__am_switch(c);
return next;
return c;
}
extern void __am_asm_trap(void);
@ -39,17 +36,22 @@ int _cte_init(_Context*(*handler)(_Event, _Context*)) {
// initialize exception entry
asm volatile("csrw stvec, %0" : : "r"(__am_asm_trap));
asm volatile("csrw sscratch, zero");
// register event handler
user_handler = handler;
return 0;
}
_Context *_kcontext(_Area stack, void (*entry)(void *), void *arg) {
_Context *c = (_Context*)stack.end - 1;
_Context *_kcontext(_Area kstack, void (*entry)(void *), void *arg) {
_Context *c = (_Context*)kstack.end - 1;
c->pdir = NULL;
c->epc = (uintptr_t)entry;
c->GPR2 = (uintptr_t)arg;
c->status = 0x000c0100;
c->gpr[2] = 0; // sp slot, used as usp
return c;
}

View File

@ -12,7 +12,7 @@ f(30) f(31)
#define PUSH(n) sw concat(x, n), (n * 4)(sp);
#define POP(n) lw concat(x, n), (n * 4)(sp);
#define CONTEXT_SIZE ((31 + 4) * 4)
#define CONTEXT_SIZE ((32 + 3) * 4)
#define OFFSET_SP ( 2 * 4)
#define OFFSET_CAUSE (32 * 4)
#define OFFSET_STATUS (33 * 4)
@ -20,12 +20,17 @@ f(30) f(31)
.globl __am_asm_trap
__am_asm_trap:
csrrw sp, sscratch, sp
bnez sp, save_context
# trap from kernel, restore the original sp
csrrw sp, sscratch, sp
save_context:
addi sp, sp, -CONTEXT_SIZE
MAP(REGS, PUSH)
mv t0, sp
addi t0, t0, CONTEXT_SIZE
csrrw t0, sscratch, x0 # t0 = (from user ? usp : 0)
sw t0, OFFSET_SP(sp)
csrr t0, scause
@ -50,4 +55,12 @@ __am_asm_trap:
addi sp, sp, CONTEXT_SIZE
csrw sscratch, sp # ksp
lw sp, (OFFSET_SP - CONTEXT_SIZE)(sp) # sp = (from user ? usp : 0)
bnez sp, return
# return to kernel
csrrw sp, sscratch, sp
return:
sret

View File

@ -11,16 +11,22 @@ static void (*pgfree_usr)(void*) = NULL;
static int vme_enable = 0;
static _Area segments[] = { // Kernel memory mappings
{.start = (void*)0x80000000u, .end = (void*)(0x80000000u + PMEM_SIZE)},
{.start = (void*)MMIO_BASE, .end = (void*)(MMIO_BASE + MMIO_SIZE)}
RANGE(0x80000000u, 0x80000000u + PMEM_SIZE),
RANGE(MMIO_BASE, MMIO_BASE + MMIO_SIZE),
};
#define NR_KSEG_MAP (sizeof(segments) / sizeof(segments[0]))
#define USER_SPACE RANGE(0x40000000, 0x80000000)
static inline void set_satp(void *pdir) {
asm volatile("csrw satp, %0" : : "r"(0x80000000 | ((uintptr_t)pdir >> 12)));
}
static inline uintptr_t get_satp() {
uintptr_t satp;
asm volatile("csrr %0, satp" : "=r"(satp));
return satp << 12;
}
int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
pgalloc_usr = pgalloc_f;
pgfree_usr = pgfree_f;
@ -32,7 +38,7 @@ int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
}
PTE *ptab = kptabs;
for (i = 0; i < NR_KSEG_MAP; i ++) {
for (i = 0; i < LENGTH(segments); i ++) {
uint32_t pdir_idx = (uintptr_t)segments[i].start / (PGSIZE * NR_PTE);
uint32_t pdir_idx_end = (uintptr_t)segments[i].end / (PGSIZE * NR_PTE);
for (; pdir_idx < pdir_idx_end; pdir_idx ++) {
@ -55,51 +61,50 @@ int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
return 0;
}
int _protect(_AddressSpace *as) {
PDE *updir = (PDE*)(pgalloc_usr(1));
void _protect(_AddressSpace *as) {
PDE *updir = (PDE*)(pgalloc_usr(PGSIZE));
as->ptr = updir;
as->area = USER_SPACE;
as->pgsize = PGSIZE;
// map kernel space
for (int i = 0; i < NR_PDE; i ++) {
updir[i] = kpdirs[i];
}
return 0;
}
void _unprotect(_AddressSpace *as) {
}
static _AddressSpace *cur_as = NULL;
void __am_get_cur_as(_Context *c) {
c->as = cur_as;
c->pdir = (vme_enable ? (void *)get_satp() : NULL);
}
void __am_switch(_Context *c) {
if (vme_enable) {
set_satp(c->as->ptr);
cur_as = c->as;
if (vme_enable && c->pdir != NULL) {
set_satp(c->pdir);
}
}
int _map(_AddressSpace *as, void *va, void *pa, int prot) {
void _map(_AddressSpace *as, void *va, void *pa, int prot) {
assert((uintptr_t)va % PGSIZE == 0);
assert((uintptr_t)pa % PGSIZE == 0);
PDE *pt = (PDE*)as->ptr;
PDE *pde = &pt[PDX(va)];
if (!(*pde & PTE_V)) {
*pde = PTE_V | ((uint32_t)pgalloc_usr(1) >> PGSHFT << 10);
*pde = PTE_V | ((uint32_t)pgalloc_usr(PGSIZE) >> PGSHFT << 10);
}
PTE *pte = &((PTE*)PTE_ADDR(*pde))[PTX(va)];
if (!(*pte & PTE_V)) {
*pte = PTE_V | PTE_R | PTE_W | PTE_X | ((uint32_t)pa >> PGSHFT << 10);
}
return 0;
}
_Context *_ucontext(_AddressSpace *as, _Area ustack, _Area kstack, void *entry, void *args) {
_Context *c = (_Context*)ustack.end - 1;
_Context *_ucontext(_AddressSpace *as, _Area kstack, void *entry) {
_Context *c = (_Context*)kstack.end - 1;
c->as = as;
c->pdir = as->ptr;
c->epc = (uintptr_t)entry;
c->status = 0x000c0120;
c->gpr[2] = 1; // sp slot, used as usp, non-zero is ok
return c;
}

View File

@ -1,6 +1,7 @@
#include <am.h>
#include <riscv32.h>
#include <klib.h>
#include <klib-macros.h>
static uint32_t mul(uint32_t a, uint32_t b, int sign, int hi) {
if (a == 0x80000000 && b == 0x80000000) {

View File

@ -8,10 +8,7 @@ int main(const char *args);
void __am_init_uartlite(void);
void __am_uartlite_putchar(char ch);
_Area _heap = {
.start = &_heap_start,
.end = &_heap_end,
};
_Area _heap = RANGE(&_heap_start, &_heap_end);
void _putc(char ch) {
__am_uartlite_putchar(ch);

View File

@ -1,7 +1,12 @@
#include <am.h>
#include <x86.h>
#include "x86-nemu.h"
#include <klib.h>
#define NR_IRQ 256 // IDT size
#define SEG_KCODE 1
#define SEG_KDATA 2
static _Context* (*user_handler)(_Event, _Context*) = NULL;
void __am_irq0();
@ -12,10 +17,18 @@ void __am_vecnull();
void __am_get_cur_as(_Context *c);
void __am_switch(_Context *c);
uintptr_t __am_ksp = 0;
_Context* __am_irq_handle(_Context *c) {
__am_get_cur_as(c);
_Context *next = c;
if (__am_ksp != 0) {
// trap from user
memcpy(&c->irq, (void *)__am_ksp, 5 * sizeof(uintptr_t));
c->usp = __am_ksp + 5 * sizeof(uintptr_t);
__am_ksp = 0;
}
if (user_handler) {
_Event ev = {0};
switch (c->irq) {
@ -25,30 +38,33 @@ _Context* __am_irq_handle(_Context *c) {
default: ev.event = _EVENT_ERROR; break;
}
next = user_handler(ev, c);
if (next == NULL) {
next = c;
}
c = user_handler(ev, c);
assert(c != NULL);
}
__am_switch(next);
__am_switch(c);
return next;
if (c->usp != 0) {
// return to user, set ksp for the next use
__am_ksp = (uintptr_t)(c + 1);
}
return c;
}
int _cte_init(_Context*(*handler)(_Event, _Context*)) {
static GateDesc idt[NR_IRQ];
static GateDesc32 idt[NR_IRQ];
// initialize IDT
for (unsigned int i = 0; i < NR_IRQ; i ++) {
idt[i] = GATE(STS_TG32, KSEL(SEG_KCODE), __am_vecnull, DPL_KERN);
idt[i] = GATE32(STS_TG, KSEL(SEG_KCODE), __am_vecnull, DPL_KERN);
}
// ----------------------- interrupts ----------------------------
idt[32] = GATE(STS_IG32, KSEL(SEG_KCODE), __am_irq0, DPL_KERN);
idt[32] = GATE32(STS_IG, KSEL(SEG_KCODE), __am_irq0, DPL_KERN);
// ---------------------- system call ----------------------------
idt[0x80] = GATE(STS_TG32, KSEL(SEG_KCODE), __am_vecsys, DPL_USER);
idt[0x81] = GATE(STS_TG32, KSEL(SEG_KCODE), __am_vectrap, DPL_KERN);
idt[0x80] = GATE32(STS_TG, KSEL(SEG_KCODE), __am_vecsys, DPL_USER);
idt[0x81] = GATE32(STS_TG, KSEL(SEG_KCODE), __am_vectrap, DPL_KERN);
set_idt(idt, sizeof(idt));
@ -58,18 +74,18 @@ int _cte_init(_Context*(*handler)(_Event, _Context*)) {
return 0;
}
_Context *_kcontext(_Area stack, void (*entry)(void *), void *arg) {
// stack.end -= 4 * sizeof(uintptr_t); // 4 = retaddr + argc + argv + envp
// uintptr_t *esp = stack.end;
// esp[1] = esp[2] = esp[3] = 0;
_Context *c = (_Context*)stack.end - 1;
void __am_kcontext_start();
_Context* _kcontext(_Area kstack, void (*entry)(void *), void *arg) {
_Context *c = (_Context *)kstack.end - 1;
c->cr3 = NULL;
c->cs = 0x8;
c->eip = (uintptr_t)entry;
c->eip = (uintptr_t)__am_kcontext_start;
c->eflags = 0x2 | FL_IF;
c->usp = 0;
c->GPR1 = (uintptr_t)arg;
c->GPR2 = (uintptr_t)entry;
return c;
// return NULL;
}
void _yield() {

View File

@ -1,10 +1,23 @@
#----|------------entry------------|-errorcode-|---irq id---|-----handler-----|
.globl __am_vecsys; __am_vecsys: pushl $0; pushl $0x80; jmp __am_asm_trap
.globl __am_vectrap; __am_vectrap: pushl $0; pushl $0x81; jmp __am_asm_trap
.globl __am_irq0; __am_irq0: pushl $0; pushl $32; jmp __am_asm_trap
.globl __am_vecnull; __am_vecnull: pushl $0; pushl $-1; jmp __am_asm_trap
#----|------------entry------------|---usp---|---irq id---|-----handler-----|
.globl __am_vecsys; __am_vecsys: pushl $0; pushl $0x80; jmp __am_asm_trap
.globl __am_vectrap; __am_vectrap: pushl $0; pushl $0x81; jmp __am_asm_trap
.globl __am_irq0; __am_irq0: pushl $0; pushl $32; jmp __am_asm_trap
.globl __am_vecnull; __am_vecnull: pushl $0; pushl $-1; jmp __am_asm_trap
.globl __am_kcontext_start
__am_kcontext_start:
pushl %eax
call *%ebx
__am_asm_trap:
cmpl $0, __am_ksp
je in_kernel
# switch to kernel stack
xchg %esp, __am_ksp
subl $20, %esp
in_kernel:
pushal
pushl $0
@ -16,6 +29,6 @@ __am_asm_trap:
addl $4, %esp
popal
addl $8, %esp
addl $4, %esp
iret
iret # customized with usp

View File

@ -1,7 +1,22 @@
#include <am.h>
#include <x86.h>
#include <nemu.h>
#include <klib.h>
#include "x86-nemu.h"
typedef uint32_t PTE;
typedef uint32_t PDE;
#define NR_PDE 1024 // # directory entries per page directory
#define NR_PTE 1024 // # PTEs per page table
#define PGSHFT 12 // log2(PGSIZE)
#define PTXSHFT 12 // Offset of PTX in a linear address
#define PDXSHFT 22 // Offset of PDX in a linear address
#define PDX(va) (((uint32_t)(va) >> PDXSHFT) & 0x3ff)
#define PTX(va) (((uint32_t)(va) >> PTXSHFT) & 0x3ff)
#define OFF(va) ((uint32_t)(va) & 0xfff)
#define PTE_ADDR(pte) ((uint32_t)(pte) & ~0xfff)
#define PGADDR(d, t, o) ((uint32_t)((d) << PDXSHFT | (t) << PTXSHFT | (o)))
#define PG_ALIGN __attribute((aligned(PGSIZE)))
static PDE kpdirs[NR_PDE] PG_ALIGN = {};
@ -11,11 +26,11 @@ static void (*pgfree_usr)(void*) = NULL;
static int vme_enable = 0;
static _Area segments[] = { // Kernel memory mappings
{.start = (void*)0, .end = (void*)PMEM_SIZE},
{.start = (void*)MMIO_BASE, .end = (void*)(MMIO_BASE + MMIO_SIZE)}
RANGE(0, PMEM_SIZE),
RANGE(MMIO_BASE, MMIO_BASE + MMIO_SIZE),
};
#define NR_KSEG_MAP (sizeof(segments) / sizeof(segments[0]))
#define USER_SPACE RANGE(0x40000000, 0xc0000000)
int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
pgalloc_usr = pgalloc_f;
@ -29,7 +44,7 @@ int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
}
PTE *ptab = kptabs;
for (i = 0; i < NR_KSEG_MAP; i ++) {
for (i = 0; i < LENGTH(segments); i ++) {
uint32_t pdir_idx = (uintptr_t)segments[i].start / (PGSIZE * NR_PTE);
uint32_t pdir_idx_end = (uintptr_t)segments[i].end / (PGSIZE * NR_PTE);
for (; pdir_idx < pdir_idx_end; pdir_idx ++) {
@ -53,57 +68,48 @@ int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
return 0;
}
int _protect(_AddressSpace *as) {
PDE *updir = (PDE*)(pgalloc_usr(1));
void _protect(_AddressSpace *as) {
PDE *updir = (PDE*)(pgalloc_usr(PGSIZE));
as->ptr = updir;
as->area = USER_SPACE;
as->pgsize = PGSIZE;
// map kernel space
for (int i = 0; i < NR_PDE; i ++) {
updir[i] = kpdirs[i];
}
return 0;
}
void _unprotect(_AddressSpace *as) {
}
static _AddressSpace *cur_as = NULL;
void __am_get_cur_as(_Context *c) {
c->as = cur_as;
c->cr3 = (vme_enable ? (void *)get_cr3() : NULL);
}
void __am_switch(_Context *c) {
if (vme_enable) {
set_cr3(c->as->ptr);
cur_as = c->as;
}
if (vme_enable && c->cr3 != NULL) { set_cr3(c->cr3); }
}
int _map(_AddressSpace *as, void *va, void *pa, int prot) {
void _map(_AddressSpace *as, void *va, void *pa, int prot) {
assert((uintptr_t)va % PGSIZE == 0);
assert((uintptr_t)pa % PGSIZE == 0);
PDE *pt = (PDE*)as->ptr;
PDE *pde = &pt[PDX(va)];
if (!(*pde & PTE_P)) {
*pde = PTE_P | PTE_W | PTE_U | (uint32_t)pgalloc_usr(1);
*pde = PTE_P | PTE_W | PTE_U | (uint32_t)pgalloc_usr(PGSIZE);
}
PTE *pte = &((PTE*)PTE_ADDR(*pde))[PTX(va)];
if (!(*pte & PTE_P)) {
*pte = PTE_P | PTE_W | PTE_U | (uint32_t)pa;
}
return 0;
}
_Context *_ucontext(_AddressSpace *as, _Area ustack, _Area kstack, void *entry, void *args) {
ustack.end -= 4 * sizeof(uintptr_t); // 4 = retaddr + argc + argv + envp
uintptr_t *esp = ustack.end;
esp[1] = esp[2] = esp[3] = 0;
_Context *c = (_Context*)ustack.end - 1;
c->as = as;
_Context* _ucontext(_AddressSpace *as, _Area kstack, void *entry) {
_Context *c = (_Context *)kstack.end - 1;
c->cr3 = as->ptr;
c->cs = 0x8;
c->eip = (uintptr_t)entry;
c->eflags = 0x2 | FL_IF;
c->usp = (uintptr_t)kstack.end; // non-zero but should be used to safely construct context
return c;
// return NULL;
}

View File

View File

@ -2,7 +2,7 @@ $(info # Building bootblock [$(ARCH)])
mbr: start.S main.c
@echo + CC start.S main.c
@gcc -nostdlib -Os start.S main.c -m32 -Ttext 0x7C00 -o bootblock.o
@gcc -nostdlib -I$(AM_HOME)/am/include -Os start.S main.c -m32 -Ttext 0x7C00 -o bootblock.o
@objcopy -S -O binary -j .text bootblock.o mbr
@perl genboot.pl mbr

View File

@ -1,115 +1,89 @@
#include <stdint.h>
struct ELFHeader {
uint32_t magic;
uint8_t elf[12];
uint16_t type;
uint16_t machine;
uint32_t version;
uint32_t entry;
uint32_t phoff;
uint32_t shoff;
uint32_t flags;
uint16_t ehsize;
uint16_t phentsize;
uint16_t phnum;
uint16_t shentsize;
uint16_t shnum;
uint16_t shstrndx;
};
struct ProgramHeader {
uint32_t type;
uint32_t off;
uint32_t vaddr;
uint32_t paddr;
uint32_t filesz;
uint32_t memsz;
uint32_t flags;
uint32_t align;
};
static inline char in_byte(short port) {
char data;
__asm__ volatile ("in %1,%0" : "=a" (data) : "d" (port));
return data;
}
static inline int in_long(short port) {
int data;
__asm__ volatile ("in %1, %0" : "=a" (data) : "d" (port));
return data;
}
static inline void out_byte(short port, char data) {
__asm__ volatile ("out %0,%1" : : "a" (data), "d" (port));
}
static inline void hlt() {
__asm__ volatile ("hlt");
}
#include <elf.h>
#include <x86.h>
#define SECTSIZE 512
void readseg(void *, int, int);
struct boot_info {
int is_ap;
void (*entry)();
};
void
bootmain(void) {
volatile struct boot_info *boot = (void*)0x7000;
int is_ap = boot->is_ap;
if (is_ap == 1) {
boot->entry();
}
struct ELFHeader *elf;
struct ProgramHeader *ph, *eph;
unsigned char *pa, *i;
elf = (struct ELFHeader*)0x8000;
readseg(elf, 4096, 0);
ph = (struct ProgramHeader*)((char *)elf + elf->phoff);
eph = ph + elf->phnum;
for(; ph < eph; ph ++) {
pa = (void *)(ph->paddr);
readseg(pa, ph->filesz, ph->off);
for (i = pa + ph->filesz; i < pa + ph->memsz; *i++ = 0);
}
char *mainargs = (void *)0x7e00;
readseg(mainargs, 512, -512);
((void(*)(const char *))elf->entry)(mainargs);
static inline void wait_disk(void) {
while ((inb(0x1f7) & 0xc0) != 0x40);
}
void waitdisk(void) {
while ((in_byte(0x1F7) & 0xC0) != 0x40);
}
void readsect(volatile void *dst, int offset) {
waitdisk();
out_byte(0x1F2, 1);
out_byte(0x1F3, offset);
out_byte(0x1F4, offset >> 8);
out_byte(0x1F5, offset >> 16);
out_byte(0x1F6, (offset >> 24) | 0xE0);
out_byte(0x1F7, 0x20);
waitdisk();
static inline void read_disk(void *buf, int sect) {
wait_disk();
outb(0x1f2, 1);
outb(0x1f3, sect);
outb(0x1f4, sect >> 8);
outb(0x1f5, sect >> 16);
outb(0x1f6, (sect >> 24) | 0xE0);
outb(0x1f7, 0x20);
wait_disk();
for (int i = 0; i < SECTSIZE / 4; i ++) {
((int *)dst)[i] = in_long(0x1F0);
((uint32_t *)buf)[i] = inl(0x1f0);
}
}
void readseg(void *addr, int count, int offset) {
unsigned char *pa = addr;
unsigned char *epa = pa + count;
pa -= offset % SECTSIZE;
offset = (offset / SECTSIZE) + 1 + 1 /* args */;
for(; pa < epa; pa += SECTSIZE, offset ++)
readsect(pa, offset);
static inline void copy_from_disk(void *buf, int nbytes, int disk_offset) {
uint32_t cur = (uint32_t)buf & ~511;
uint32_t ed = (uint32_t)buf + nbytes;
uint32_t sect = (disk_offset >> 9) + 3;
for(; cur < ed; cur += SECTSIZE, sect ++)
read_disk((void *)cur, sect);
}
static void load_program(uint32_t filesz, uint32_t memsz, uint32_t paddr, uint32_t offset) {
copy_from_disk((void *)paddr, filesz, offset);
char *bss = (void *)(paddr + filesz);
for (uint32_t i = filesz; i != memsz; i++) {
*bss++ = 0;
}
}
static void load_elf64(Elf64_Ehdr *elf) {
Elf64_Phdr *ph = (Elf64_Phdr *)((char *)elf + elf->e_phoff);
for (int i = 0; i < elf->e_phnum; i++, ph++) {
load_program(
(uint32_t)ph->p_filesz,
(uint32_t)ph->p_memsz,
(uint32_t)ph->p_paddr,
(uint32_t)ph->p_offset
);
}
}
static void load_elf32(Elf32_Ehdr *elf) {
Elf32_Phdr *ph = (Elf32_Phdr *)((char *)elf + elf->e_phoff);
for (int i = 0; i < elf->e_phnum; i++, ph++) {
load_program(
(uint32_t)ph->p_filesz,
(uint32_t)ph->p_memsz,
(uint32_t)ph->p_paddr,
(uint32_t)ph->p_offset
);
}
}
void load_kernel(void) {
Elf32_Ehdr *elf32 = (void *)0x8000;
Elf64_Ehdr *elf64 = (void *)0x8000;
int is_ap = boot_record()->is_ap;
if (!is_ap) {
// load argument (string) to memory
copy_from_disk((void *)MAINARG_ADDR, 1024, -1024);
// load elf header to memory
copy_from_disk(elf32, 4096, 0);
if (elf32->e_machine == EM_X86_64) {
load_elf64(elf64);
} else {
load_elf32(elf32);
}
} else {
// everything should be loaded
}
if (elf32->e_machine == EM_X86_64) {
((void(*)())(uint32_t)elf64->e_entry)();
} else {
((void(*)())(uint32_t)elf32->e_entry)();
}
}

View File

@ -45,8 +45,8 @@ start32:
movw %ax, %es
movw %ax, %ss
movl $0x7000, %esp
call bootmain
movl $0xa000, %esp
call load_kernel
# GDT
.p2align 2

View File

@ -1,120 +1,64 @@
#include "../x86-qemu.h"
#include <stdarg.h>
#include "x86-qemu.h"
static _Context* (*user_handler)(_Event, _Context*) = NULL;
static GateDesc idt[NR_IRQ];
#if __x86_64__
static GateDesc64 idt[NR_IRQ];
#define GATE GATE64
#else
static GateDesc32 idt[NR_IRQ];
#define GATE GATE32
#endif
#define IRQHANDLE_DECL(id, dpl, err) \
void __am_irq##id();
#define IRQHANDLE_DECL(id, dpl, err) void __am_irq##id();
IRQS(IRQHANDLE_DECL)
void __am_irqall();
void __am_kcontext_start();
int _cte_init(_Context *(*handler)(_Event, _Context *)) {
if (_cpu() != 0) panic("init CTE in non-bootstrap CPU");
for (int i = 0; i < NR_IRQ; i ++) {
idt[i] = GATE(STS_TG32, KSEL(SEG_KCODE), __am_irqall, DPL_KERN);
}
#define IDT_ENTRY(id, dpl, err) \
idt[id] = GATE(STS_TG32, KSEL(SEG_KCODE), __am_irq##id, DPL_##dpl);
IRQS(IDT_ENTRY)
user_handler = handler;
__am_percpu_initirq();
return 0;
}
void _yield() {
if (!user_handler) panic("no interrupt handler");
asm volatile ("int $0x80" : : "a"(-1));
}
int _intr_read() {
if (!user_handler) panic("no interrupt handler");
return (get_efl() & FL_IF) != 0;
}
void _intr_write(int enable) {
if (!user_handler) panic("no interrupt handler");
if (enable) {
sti();
} else {
cli();
}
}
static void panic_on_return() { panic("kernel context returns"); }
_Context *_kcontext(_Area stack, void (*entry)(void *), void *arg) {
_Area stk_safe = {
(void *)ROUNDUP(stack.start, 64),
(void *)ROUNDDOWN(stack.end, 64),
};
_Context *ctx = (_Context *)stk_safe.start;
*ctx = (_Context) {
.eax = 0, .ebx = 0, .ecx = 0, .edx = 0,
.esi = 0, .edi = 0, .ebp = 0, .esp3 = 0,
.ss0 = 0, .esp0 = (uint32_t)stk_safe.end,
.cs = KSEL(SEG_KCODE), .eip = (uint32_t)entry, .eflags = FL_IF,
.ds = KSEL(SEG_KDATA), .es = KSEL(SEG_KDATA), .ss = KSEL(SEG_KDATA),
.uvm = NULL,
};
void *values[] = { panic_on_return, arg }; // copy to stack
ctx->esp0 -= sizeof(values);
for (int i = 0; i < LENGTH(values); i++) {
((uintptr_t *)ctx->esp0)[i] = (uintptr_t)values[i];
}
return ctx;
}
#define IRQ T_IRQ0 +
#define MSG(m) ev.msg = m;
void __am_irq_handle(TrapFrame *tf) {
// saving processor context
_Context ctx = {
.eax = tf->eax, .ebx = tf->ebx, .ecx = tf->ecx, .edx = tf->edx,
.esi = tf->esi, .edi = tf->edi, .ebp = tf->ebp, .esp3 = 0,
.eip = tf->eip, .eflags = tf->eflags,
.cs = tf->cs, .ds = tf->ds, .es = tf->es, .ss = 0,
.ss0 = KSEL(SEG_KDATA), .esp0 = (uint32_t)(tf + 1),
.uvm = CPU->uvm,
};
if (tf->cs & DPL_USER) { // interrupt at user code
ctx.ss = tf->ss;
ctx.esp3 = tf->esp;
} else { // interrupt at kernel code
// tf (without ss0/esp0) is everything saved on the stack
ctx.esp0 -= sizeof(uint32_t) * 2;
}
// sending end-of-interrupt
if (IRQ 0 <= tf->irq && tf->irq < IRQ 32) {
__am_lapic_eoi();
}
// creating an event
void __am_irq_handle(struct trap_frame *tf) {
_Context *saved_ctx = &tf->saved_context;
_Event ev = {
.event = _EVENT_NULL,
.cause = 0, .ref = 0,
.msg = "(no message)",
};
#if __x86_64
saved_ctx->rip = tf->rip;
saved_ctx->cs = tf->cs;
saved_ctx->rflags = tf->rflags;
saved_ctx->rsp = tf->rsp;
saved_ctx->rsp0 = CPU->tss.rsp0;
saved_ctx->ss = tf->ss;
#else
saved_ctx->eip = tf->eip;
saved_ctx->cs = tf->cs;
saved_ctx->eflags = tf->eflags;
saved_ctx->esp0 = CPU->tss.esp0;
saved_ctx->ss3 = USEL(SEG_UDATA);
// no ss/esp saved for DPL_KERNEL
saved_ctx->esp = (tf->cs & DPL_USER ? tf->esp : (uint32_t)(tf + 1) - 8);
#endif
saved_ctx->uvm = (void *)get_cr3();
#define IRQ T_IRQ0 +
#define MSG(m) ev.msg = m;
if (IRQ 0 <= tf->irq && tf->irq < IRQ 32) {
__am_lapic_eoi();
}
switch (tf->irq) {
case IRQ 0: MSG("timer interrupt (lapic)")
ev.event = _EVENT_IRQ_TIMER; break;
case IRQ 1: MSG("I/O device IRQ1 (keyboard)")
ev.event = _EVENT_IRQ_IODEV; break;
case EX_SYSCALL: MSG("int $0x80 trap: _yield() or system call")
if ((int32_t)tf->eax == -1) {
ev.event = _EVENT_YIELD;
} else {
ev.event = _EVENT_SYSCALL;
}
break;
case EX_DIV: MSG("divide by zero")
case EX_SYSCALL: MSG("int $0x80 system call")
ev.event = _EVENT_SYSCALL; break;
case EX_YIELD: MSG("int $0x81 yield")
ev.event = _EVENT_YIELD; break;
case EX_DE: MSG("DE #0 divide by zero")
ev.event = _EVENT_ERROR; break;
case EX_UD: MSG("UD #6 invalid opcode")
ev.event = _EVENT_ERROR; break;
@ -132,69 +76,90 @@ void __am_irq_handle(TrapFrame *tf) {
ev.event = _EVENT_ERROR; break;
case EX_PF: MSG("PF #14, page fault, @cause: _PROT_XXX")
ev.event = _EVENT_PAGEFAULT;
if (tf->err & 0x1) ev.cause |= _PROT_NONE;
if (tf->err & 0x2) ev.cause |= _PROT_WRITE;
else ev.cause |= _PROT_READ;
if (tf->errcode & 0x1) ev.cause |= _PROT_NONE;
if (tf->errcode & 0x2) ev.cause |= _PROT_WRITE;
else ev.cause |= _PROT_READ;
ev.ref = get_cr2();
break;
default: MSG("unrecognized interrupt/exception")
ev.event = _EVENT_ERROR;
ev.cause = tf->err;
ev.cause = tf->errcode;
break;
}
// call user handlers (registered in _cte_init)
_Context *ret_ctx = &ctx;
if (user_handler) {
_Context *next = user_handler(ev, &ctx);
if (!next) {
panic("return to a null context");
}
ret_ctx = next;
_Context *ret_ctx = user_handler(ev, saved_ctx);
panic_on(!ret_ctx, "returning to NULL context");
if (ret_ctx->uvm) {
set_cr3(ret_ctx->uvm);
#if __x86_64__
CPU->tss.rsp0 = ret_ctx->rsp0;
#else
CPU->tss.ss0 = KSEL(SEG_KDATA);
CPU->tss.esp0 = ret_ctx->esp0;
#endif
}
// Return to context @ret_ctx
#define REGS_KERNEL(_) \
_(eflags) _(cs) _(eip) _(ds) _(es) \
_(eax) _(ecx) _(edx) _(ebx) _(esp0) _(ebp) _(esi) _(edi)
#define REGS_USER(_) \
_(ss) _(esp3) REGS_KERNEL(_)
#define push(r) "push %[" #r "];" // -> push %[eax]
#define def(r) , [r] "m"(ret_ctx->r) // -> [eax] "m"(ret_ctx->eax)
CPU->uvm = ret_ctx->uvm;
if (ret_ctx->cs & DPL_USER) { // return to user
_AddressSpace *uvm = ret_ctx->uvm;
if (uvm) {
set_cr3(uvm->ptr);
}
__am_thiscpu_setstk0(ret_ctx->ss0, ret_ctx->esp0);
asm volatile goto (
"movl %[esp], %%esp;" // move stack
REGS_USER(push) // push reg context onto stack
"jmp %l[iret]" // goto iret
: : [esp] "m"(ret_ctx->esp0)
REGS_USER(def) : : iret );
} else { // return to kernel
asm volatile goto (
"movl %[esp], %%esp;" // move stack
REGS_KERNEL(push) // push reg context onto stack
"jmp %l[iret]" // goto iret
: : [esp] "m"(ret_ctx->esp0)
REGS_KERNEL(def) : : iret );
__am_iret(ret_ctx);
}
int _cte_init(_Context *(*handler)(_Event, _Context *)) {
panic_on(_cpu() != 0, "init CTE in non-bootstrap CPU");
panic_on(!handler, "no interrupt handler");
for (int i = 0; i < NR_IRQ; i ++) {
idt[i] = GATE(STS_TG, KSEL(SEG_KCODE), __am_irqall, DPL_KERN);
}
iret:
asm volatile (
"popal;" // restore context
"popl %es;"
"popl %ds;"
"iret;" // interrupt return
);
#define IDT_ENTRY(id, dpl, err) \
idt[id] = GATE(STS_TG, KSEL(SEG_KCODE), __am_irq##id, DPL_##dpl);
IRQS(IDT_ENTRY)
user_handler = handler;
return 0;
}
void _yield() {
interrupt(0x81);
}
int _intr_read() {
return (get_efl() & FL_IF) != 0;
}
void _intr_write(int enable) {
if (enable) {
sti();
} else {
cli();
}
}
void __am_panic_on_return() { panic("kernel context returns"); }
_Context* _kcontext(_Area kstack, void (*entry)(void *), void *arg) {
_Context *ctx = kstack.end - sizeof(_Context);
*ctx = (_Context) { 0 };
#if __x86_64__
ctx->cs = KSEL(SEG_KCODE);
ctx->rip = (uintptr_t)__am_kcontext_start;
ctx->rflags = FL_IF;
ctx->rsp = (uintptr_t)kstack.end;
#else
ctx->ds = KSEL(SEG_KDATA);
ctx->cs = KSEL(SEG_KCODE);
ctx->eip = (uintptr_t)__am_kcontext_start;
ctx->eflags = FL_IF;
ctx->esp = (uintptr_t)kstack.end;
#endif
ctx->GPR1 = (uintptr_t)arg;
ctx->GPR2 = (uintptr_t)entry;
return ctx;
}
void __am_percpu_initirq() {
if (user_handler) {
__am_ioapic_enable(IRQ_KBD, 0);
set_idt(idt, sizeof(idt));
}
__am_ioapic_enable(IRQ_KBD, 0);
set_idt(idt, sizeof(idt));
}

View File

@ -1,20 +0,0 @@
#include <x86.h>
trap:
pushl %ds
pushl %es
pushal
movw $KSEL(SEG_KDATA), %ax
movw %ax, %ds
movw %ax, %es
pushl %esp
call __am_irq_handle // iret in the C code
#define NOERR pushl $0
#define ERR
#define IRQ_DEF(id, dpl, err) \
.globl __am_irq##id; __am_irq##id: cli; err; pushl $id; jmp trap;
IRQS(IRQ_DEF)
.globl __am_irqall; __am_irqall: cli; pushl $0; pushl $-1; jmp trap;

View File

@ -1,128 +0,0 @@
// LAPIC/IOAPIC related code
// from xv6
#include "../../x86-qemu.h"
#define ID (0x0020/4) // ID
#define VER (0x0030/4) // Version
#define TPR (0x0080/4) // Task Priority
#define EOI (0x00B0/4) // EOI
#define SVR (0x00F0/4) // Spurious Interrupt Vector
#define ENABLE 0x00000100 // Unit Enable
#define ESR (0x0280/4) // Error Status
#define ICRLO (0x0300/4) // Interrupt Command
#define INIT 0x00000500 // INIT/RESET
#define STARTUP 0x00000600 // Startup IPI
#define DELIVS 0x00001000 // Delivery status
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
#define DEASSERT 0x00000000
#define LEVEL 0x00008000 // Level triggered
#define BCAST 0x00080000 // Send to all APICs, including self.
#define BUSY 0x00001000
#define FIXED 0x00000000
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
#define X1 0x0000000B // divide counts by 1
#define PERIODIC 0x00020000 // Periodic
#define PCINT (0x0340/4) // Performance Counter LVT
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
#define MASKED 0x00010000 // Interrupt masked
#define TICR (0x0380/4) // Timer Initial Count
#define TCCR (0x0390/4) // Timer Current Count
#define TDCR (0x03E0/4) // Timer Divide Configuration
#define IOAPIC_ADDR 0xFEC00000 // Default physical address of IO APIC
#define REG_ID 0x00 // Register index: ID
#define REG_VER 0x01 // Register index: version
#define REG_TABLE 0x10 // Redirection table base
#define INT_DISABLED 0x00010000 // Interrupt disabled
#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-)
#define INT_ACTIVELOW 0x00002000 // Active low (vs high)
#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID)
volatile unsigned int *__am_lapic = NULL; // Initialized in mp.c
struct IOAPIC {
uint32_t reg, pad[3], data;
};
typedef struct IOAPIC IOAPIC;
static volatile IOAPIC *ioapic;
static void lapicw(int index, int value) {
__am_lapic[index] = value;
__am_lapic[ID];
}
void __am_percpu_initlapic(void) {
lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
lapicw(TDCR, X1);
lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
lapicw(TICR, 10000000);
lapicw(LINT0, MASKED);
lapicw(LINT1, MASKED);
if(((__am_lapic[VER]>>16) & 0xFF) >= 4)
lapicw(PCINT, MASKED);
lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
lapicw(ESR, 0);
lapicw(ESR, 0);
lapicw(EOI, 0);
lapicw(ICRHI, 0);
lapicw(ICRLO, BCAST | INIT | LEVEL);
while(__am_lapic[ICRLO] & DELIVS) ;
lapicw(TPR, 0);
}
// Acknowledge interrupt.
void __am_lapic_eoi(void) {
if(__am_lapic)
lapicw(EOI, 0);
}
void __am_lapic_bootap(unsigned int apicid, unsigned int addr) {
int i;
unsigned short *wrv;
outb(0x70, 0xF);
outb(0x71, 0x0A);
wrv = (unsigned short*)((0x40<<4 | 0x67));
wrv[0] = 0;
wrv[1] = addr >> 4;
lapicw(ICRHI, apicid<<24);
lapicw(ICRLO, INIT | LEVEL | ASSERT);
lapicw(ICRLO, INIT | LEVEL);
for(i = 0; i < 2; i++){
lapicw(ICRHI, apicid<<24);
lapicw(ICRLO, STARTUP | (addr>>12));
}
}
static unsigned int ioapicread(int reg) {
ioapic->reg = reg;
return ioapic->data;
}
static void ioapicwrite(int reg, unsigned int data) {
ioapic->reg = reg;
ioapic->data = data;
}
void __am_ioapic_init(void) {
int i, maxintr;
ioapic = (volatile IOAPIC*)IOAPIC_ADDR;
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
for(i = 0; i <= maxintr; i++){
ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i));
ioapicwrite(REG_TABLE+2*i+1, 0);
}
}
void __am_ioapic_enable(int irq, int cpunum) {
ioapicwrite(REG_TABLE+2*irq, T_IRQ0 + irq);
ioapicwrite(REG_TABLE+2*irq+1, cpunum << 24);
}

View File

@ -1,54 +0,0 @@
#include "../../x86-qemu.h"
void __am_bootcpu_init() {
#define MAGIC 0x5f504d5f
for (char *st = (char *)0xf0000; st != (char *)0xffffff; st ++) {
if (*(volatile uint32_t *)st == MAGIC) {
volatile MPConf *conf = ((volatile MPDesc *)st)->conf;
__am_lapic = conf->lapicaddr;
for (volatile char *ptr = (char *)(conf + 1);
ptr < (char *)conf + conf->length; ptr += 8) {
if (*ptr == '\0') {
ptr += 12;
if (++__am_ncpu > MAX_CPU) {
panic("cannot support > MAX_CPU processors");
}
}
}
return;
}
}
panic("seems not an x86-qemu virtual machine");
}
void __am_percpu_initgdt() {
SegDesc *gdt = CPU->gdt;
TSS *tss = &CPU->tss;
gdt[SEG_KCODE] = SEG (STA_X | STA_R, 0, 0xffffffff, DPL_KERN);
gdt[SEG_KDATA] = SEG (STA_W, 0, 0xffffffff, DPL_KERN);
gdt[SEG_UCODE] = SEG (STA_X | STA_R, 0, 0xffffffff, DPL_USER);
gdt[SEG_UDATA] = SEG (STA_W, 0, 0xffffffff, DPL_USER);
gdt[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, DPL_KERN);
set_gdt(gdt, sizeof(SegDesc) * NR_SEG);
set_tr(KSEL(SEG_TSS));
}
void __am_thiscpu_setstk0(uintptr_t ss0, uintptr_t esp0) {
CPU->tss.ss0 = ss0;
CPU->tss.esp0 = esp0;
}
void __am_thiscpu_halt() {
cli();
while (1) hlt();
}
void __am_othercpu_halt() {
BOOTREC->is_ap = 1;
BOOTREC->entry = __am_thiscpu_halt;
for (int cpu = 0; cpu < __am_ncpu; cpu++) {
if (cpu != _cpu()) {
__am_lapic_bootap(cpu, 0x7c00);
}
}
}

View File

@ -1,41 +0,0 @@
#include "../../x86-qemu.h"
static int scan_code[] = {
0, 1, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
41, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 43,
58, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 28,
42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
29, 91, 56, 57, 56, 29,
72, 80, 75, 77, 0, 0, 0, 0, 0, 0
};
size_t __am_input_read(uintptr_t reg, void *buf, size_t size) {
_DEV_INPUT_KBD_t *kbd = (_DEV_INPUT_KBD_t *)buf;
int status = inb(0x64);
kbd->keydown = 0;
kbd->keycode = _KEY_NONE;
if ((status & 0x1) == 0) {
} else {
if (status & 0x20) { // mouse
} else {
int code = inb(0x60) & 0xff;
for (int i = 0; i < sizeof(scan_code) / sizeof(scan_code[0]); i ++) {
if (scan_code[i] == 0) continue;
if (scan_code[i] == code) {
kbd->keydown = 1;
kbd->keycode = i;
break;
} else if (scan_code[i] + 128 == code) {
kbd->keydown = 0;
kbd->keycode = i;
break;
}
}
}
}
return sizeof(*kbd);
}

View File

@ -1,38 +0,0 @@
#include "../../x86-qemu.h"
static inline uintptr_t port_read(int port, size_t nmemb) {
switch (nmemb) {
case 1: return inb(port);
case 2: return inw(port);
case 4: return inl(port);
}
return 0;
}
static inline void port_write(int port, size_t nmemb, uintptr_t data) {
switch (nmemb) {
case 1: return outb(port, data);
case 2: return outw(port, data);
case 4: return outl(port, data);
}
}
size_t __am_pciconf_read(uintptr_t reg, void *buf, size_t size) {
outl(0xcf8, reg);
switch (size) {
case 1: *(uint8_t *) buf = inb(0xcfc + (reg & 3));
case 2: *(uint16_t *)buf = inw(0xcfc + (reg & 2));
case 4: *(uint32_t *)buf = inl(0xcfc);
}
return size;
}
size_t __am_pciconf_write(uintptr_t reg, void *buf, size_t size) {
outl(0xcf8, reg);
switch (size) {
case 1: outb(0xcfc + (reg & 3), *(uint8_t *) buf);
case 2: outw(0xcfc + (reg & 2), *(uint16_t *)buf);
case 4: outl(0xcfc , *(uint32_t *)buf);
}
return size;
}

View File

@ -1,84 +0,0 @@
#include "../../x86-qemu.h"
static _DEV_TIMER_DATE_t boot_date;
static uint32_t freq_mhz = 2000;
static uint64_t uptsc;
static inline uint64_t rdtsc() {
uint32_t lo, hi;
asm volatile ("rdtsc": "=a"(lo), "=d"(hi));
return ((uint64_t)hi << 32) | lo;
}
static int read_rtc(int reg) {
outb(0x70, reg);
int ret = inb(0x71);
return (ret & 0xf) + (ret >> 4) * 10;
}
static void wait_sec() {
while (1) {
int volatile s1 = read_rtc(0);
for (int volatile i = 0; i < 10000; i++) ;
int volatile s2 = read_rtc(0);
if (s1 != s2) {
return;
}
}
}
static uint32_t estimate_freq() {
int h, m, s, t1, t2;
uint64_t tsc1, tsc2;
wait_sec();
tsc1 = rdtsc();
h = read_rtc(4); m = read_rtc(2); s = read_rtc(0);
t1 = h * 3600 + m * 60 + s;
wait_sec();
tsc2 = rdtsc();
h = read_rtc(4); m = read_rtc(2); s = read_rtc(0);
t2 = h * 3600 + m * 60 + s;
if (t1 >= t2) return 0; // passed a day, unlikely to happen
uint32_t freq = (tsc2 - tsc1) >> 20;
freq /= (t2 - t1);
return freq;
}
static void get_date(_DEV_TIMER_DATE_t *rtc) {
int tmp;
do {
rtc->second = read_rtc(0);
rtc->minute = read_rtc(2);
rtc->hour = read_rtc(4);
rtc->day = read_rtc(7);
rtc->month = read_rtc(8);
rtc->year = read_rtc(9) + 2000;
tmp = read_rtc(0);
} while (tmp != rtc->second);
}
void __am_timer_init() {
freq_mhz = estimate_freq();
get_date(&boot_date);
uptsc = rdtsc();
}
size_t __am_timer_read(uintptr_t reg, void *buf, size_t size) {
switch (reg) {
case _DEVREG_TIMER_UPTIME: {
uint64_t tsc = rdtsc() - uptsc;
uint32_t mticks = (tsc >> 20);
uint32_t ms = mticks * 1000 / freq_mhz;
_DEV_TIMER_UPTIME_t *uptime = (_DEV_TIMER_UPTIME_t *)buf;
uptime->hi = 0;
uptime->lo = ms;
return sizeof(_DEV_TIMER_UPTIME_t);
}
case _DEVREG_TIMER_DATE: {
get_date((_DEV_TIMER_DATE_t *)buf);
return sizeof(_DEV_TIMER_DATE_t);
}
}
return 0;
}

View File

@ -1,100 +0,0 @@
#include "../../x86-qemu.h"
struct VBEInfo {
uint16_t attributes;
uint8_t window_a;
uint8_t window_b;
uint16_t granularity;
uint16_t window_size;
uint16_t segment_a;
uint16_t segment_b;
uint32_t win_func_ptr;
uint16_t pitch;
uint16_t width;
uint16_t height;
uint8_t w_char;
uint8_t y_char;
uint8_t planes;
uint8_t bpp;
uint8_t banks;
uint8_t memory_model;
uint8_t bank_size;
uint8_t image_pages;
uint8_t reserved0;
uint8_t red_mask;
uint8_t red_position;
uint8_t green_mask;
uint8_t green_position;
uint8_t blue_mask;
uint8_t blue_position;
uint8_t reserved_mask;
uint8_t reserved_position;
uint8_t direct_color_attributes;
uint32_t framebuffer;
uint32_t off_screen_mem_off;
uint16_t off_screen_mem_size;
uint8_t reserved1[206];
} __attribute__ ((packed));
typedef struct VBEInfo VBEInfo;
static inline uint32_t pixel(uint8_t r, uint8_t g, uint8_t b) {
return (r << 16) | (g << 8) | b;
}
static uint8_t R(uint32_t p) { return p >> 16; }
static uint8_t G(uint32_t p) { return p >> 8; }
static uint8_t B(uint32_t p) { return p; }
static struct FBPixel {
uint8_t b, g, r;
} __attribute__ ((packed)) *fb;
typedef struct FBPixel FBPixel;
static int W, H;
void __am_vga_init() {
VBEInfo *info = (VBEInfo *)0x00004000;
W = info->width;
H = info->height;
fb = (FBPixel*)(info->framebuffer);
}
size_t __am_video_read(uintptr_t reg, void *buf, size_t size) {
switch(reg) {
case _DEVREG_VIDEO_INFO: {
_DEV_VIDEO_INFO_t *info = (_DEV_VIDEO_INFO_t *)buf;
info->width = W;
info->height = H;
return sizeof(_DEV_VIDEO_INFO_t);
}
}
return 0;
}
size_t __am_video_write(uintptr_t reg, void *buf, size_t size) {
switch(reg) {
case _DEVREG_VIDEO_FBCTL: {
_DEV_VIDEO_FBCTL_t *ctl = (_DEV_VIDEO_FBCTL_t *)buf;
int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h;
uint32_t *pixels = ctl->pixels;
int len = (x + w >= W) ? W - x : w;
FBPixel *v;
for (int j = 0; j < h; j ++) {
if (y + j < H) {
v = &fb[x + (j + y) * W];
for (int i = 0; i < len; i ++, v ++) {
uint32_t p = pixels[i];
v->r = R(p); v->g = G(p); v->b = B(p);
}
}
pixels += w;
}
if (ctl->sync) {
// do nothing, hardware syncs.
}
return sizeof(*ctl);
}
}
return 0;
}

View File

@ -1,4 +1,5 @@
#include "../x86-qemu.h"
#include "x86-qemu.h"
#include <amdev.h>
void __am_vga_init();
void __am_timer_init();
@ -10,15 +11,245 @@ DEF_DEVOP(__am_input_read);
DEF_DEVOP(__am_timer_read);
DEF_DEVOP(__am_video_read);
DEF_DEVOP(__am_video_write);
DEF_DEVOP(__am_pciconf_read);
DEF_DEVOP(__am_pciconf_write);
// AM INPUT (keyboard)
static int scan_code[] = {
0, 1, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
41, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 43,
58, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 28,
42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
29, 91, 56, 57, 56, 29,
72, 80, 75, 77
};
size_t __am_input_read(uintptr_t reg, void *buf, size_t size) {
_DEV_INPUT_KBD_t *kbd = (_DEV_INPUT_KBD_t *)buf;
int status = inb(0x64);
kbd->keydown = 0;
kbd->keycode = _KEY_NONE;
if ((status & 0x1) == 0) {
} else {
if (status & 0x20) { // mouse
} else {
int code = inb(0x60) & 0xff;
for (int i = 0; i < LENGTH(scan_code); i ++) {
if (scan_code[i] == 0) continue;
if (scan_code[i] == code) {
kbd->keydown = 1;
kbd->keycode = i;
break;
} else if (scan_code[i] + 128 == code) {
kbd->keydown = 0;
kbd->keycode = i;
break;
}
}
}
}
return sizeof(*kbd);
}
// AM TIMER (based on rdtsc)
static _DEV_TIMER_DATE_t boot_date;
static uint32_t freq_mhz = 2000;
static uint64_t uptsc;
static inline int read_rtc(int reg) {
outb(0x70, reg);
int ret = inb(0x71);
return (ret & 0xf) + (ret >> 4) * 10;
}
static void read_rtc_async(_DEV_TIMER_DATE_t *rtc) {
rtc->second = read_rtc(0);
rtc->minute = read_rtc(2);
rtc->hour = read_rtc(4);
rtc->day = read_rtc(7);
rtc->month = read_rtc(8);
rtc->year = read_rtc(9) + 2000;
}
static void wait_sec(_DEV_TIMER_DATE_t *t1) {
_DEV_TIMER_DATE_t t0;
while (1) {
read_rtc_async(&t0);
for (int volatile i = 0; i < 100000; i++) ;
read_rtc_async(t1);
if (t0.second != t1->second) {
return;
}
}
}
static uint32_t estimate_freq() {
_DEV_TIMER_DATE_t rtc1, rtc2;
uint64_t tsc1, tsc2, t1, t2;
wait_sec(&rtc1); tsc1 = rdtsc(); t1 = rtc1.minute * 60 + rtc1.second;
wait_sec(&rtc2); tsc2 = rdtsc(); t2 = rtc2.minute * 60 + rtc2.second;
if (t1 >= t2) return estimate_freq(); // passed an hour; try again
return ((tsc2 - tsc1) >> 20) / (t2 - t1);
}
static void get_date(_DEV_TIMER_DATE_t *rtc) {
int tmp;
do {
read_rtc_async(rtc);
tmp = read_rtc(0);
} while (tmp != rtc->second);
}
void __am_timer_init() {
freq_mhz = estimate_freq();
get_date(&boot_date);
uptsc = rdtsc();
}
size_t __am_timer_read(uintptr_t reg, void *buf, size_t size) {
switch (reg) {
case _DEVREG_TIMER_UPTIME: {
_DEV_TIMER_UPTIME_t *uptime = (_DEV_TIMER_UPTIME_t *)buf;
uint64_t tsc = rdtsc() - uptsc;
uint32_t mticks = (tsc >> 20);
uint32_t ms = mticks * 1000 / freq_mhz;
uptime->hi = 0;
uptime->lo = ms;
return sizeof(_DEV_TIMER_UPTIME_t);
}
case _DEVREG_TIMER_DATE: {
get_date((_DEV_TIMER_DATE_t *)buf);
return sizeof(_DEV_TIMER_DATE_t);
}
}
return 0;
}
// AM VIDEO
struct vbe_info {
uint8_t ignore[18];
uint16_t width;
uint16_t height;
uint8_t ignore1[18];
uint32_t framebuffer;
} __attribute__ ((packed));
static inline uint32_t pixel(uint8_t r, uint8_t g, uint8_t b) { return (r << 16) | (g << 8) | b; }
static inline uint8_t R(uint32_t p) { return p >> 16; }
static inline uint8_t G(uint32_t p) { return p >> 8; }
static inline uint8_t B(uint32_t p) { return p; }
struct pixel {
uint8_t b, g, r;
} __attribute__ ((packed));
struct pixel *fb;
static int W, H;
void __am_vga_init() {
struct vbe_info *info = (struct vbe_info *)0x00004000;
W = info->width;
H = info->height;
fb = upcast(info->framebuffer);
}
size_t __am_video_read(uintptr_t reg, void *buf, size_t size) {
switch (reg) {
case _DEVREG_VIDEO_INFO: {
_DEV_VIDEO_INFO_t *info = (_DEV_VIDEO_INFO_t *)buf;
info->width = W;
info->height = H;
return sizeof(_DEV_VIDEO_INFO_t);
}
}
return 0;
}
size_t __am_video_write(uintptr_t reg, void *buf, size_t size) {
switch (reg) {
case _DEVREG_VIDEO_FBCTRL: {
_DEV_VIDEO_FBCTRL_t *ctl = (_DEV_VIDEO_FBCTRL_t *)buf;
int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h;
uint32_t *pixels = ctl->pixels;
int len = (x + w >= W) ? W - x : w;
for (int j = 0; j < h; j ++, pixels += w) {
if (y + j < H) {
struct pixel *px = &fb[x + (j + y) * W];
for (int i = 0; i < len; i ++, px ++) {
uint32_t p = pixels[i];
px->r = R(p); px->g = G(p); px->b = B(p);
}
}
}
return sizeof(*ctl);
}
}
return 0;
}
// AM STORAGE
size_t __am_storage_read(uintptr_t reg, void *buf, size_t size) {
switch (reg) {
case _DEVREG_STORAGE_INFO: {
_DEV_STORAGE_INFO_t *info = (void *)buf;
info->blksz = 512;
info->blkcnt = 524288;
return sizeof(*info);
}
}
return 0;
}
static inline void wait_disk(void) {
while ((inb(0x1f7) & 0xc0) != 0x40);
}
size_t __am_storage_write(uintptr_t reg, void *buf, size_t size) {
_DEV_STORAGE_RDCTRL_t *ctl = (void *)buf;
int is_read = 0;
switch (reg) {
case _DEVREG_STORAGE_RDCTRL: is_read = 1; break;
case _DEVREG_STORAGE_WRCTRL: break;
default: return 0;
}
uint32_t blkno = ctl->blkno, remain = ctl->blkcnt;
uint32_t *ptr = ctl->buf;
for (remain = ctl->blkcnt; remain; remain--, blkno++) {
wait_disk();
outb(0x1f2, 1);
outb(0x1f3, blkno);
outb(0x1f4, blkno >> 8);
outb(0x1f5, blkno >> 16);
outb(0x1f6, (blkno >> 24) | 0xe0);
outb(0x1f7, is_read ? 0x20 : 0x30);
wait_disk();
if (is_read) {
for (int i = 0; i < 512 / 4; i ++) {
*ptr++ = inl(0x1f0);
}
} else {
for (int i = 0; i < 512 / 4; i ++) {
outl(0x1f0, *ptr++);
}
}
}
return sizeof(*ctl);
}
size_t _io_read(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
switch (dev) {
case _DEV_INPUT: return __am_input_read(reg, buf, size);
case _DEV_TIMER: return __am_timer_read(reg, buf, size);
case _DEV_VIDEO: return __am_video_read(reg, buf, size);
case _DEV_PCICONF: return __am_pciconf_read(reg, buf, size);
case _DEV_STORAGE: return __am_storage_read(reg, buf, size);
}
return 0;
}
@ -26,14 +257,139 @@ size_t _io_read(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
size_t _io_write(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
switch (dev) {
case _DEV_VIDEO: return __am_video_write(reg, buf, size);
case _DEV_PCICONF: return __am_pciconf_write(reg, buf, size);
case _DEV_STORAGE: return __am_storage_write(reg, buf, size);
}
return 0;
}
int _ioe_init() {
if (_cpu() != 0) panic("init IOE in non-bootstrap CPU");
panic_on(_cpu() != 0, "init IOE in non-bootstrap CPU");
__am_timer_init();
__am_vga_init();
return 0;
}
// LAPIC/IOAPIC (from xv6)
#define ID (0x0020/4) // ID
#define VER (0x0030/4) // Version
#define TPR (0x0080/4) // Task Priority
#define EOI (0x00B0/4) // EOI
#define SVR (0x00F0/4) // Spurious Interrupt Vector
#define ENABLE 0x00000100 // Unit Enable
#define ESR (0x0280/4) // Error Status
#define ICRLO (0x0300/4) // Interrupt Command
#define INIT 0x00000500 // INIT/RESET
#define STARTUP 0x00000600 // Startup IPI
#define DELIVS 0x00001000 // Delivery status
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
#define DEASSERT 0x00000000
#define LEVEL 0x00008000 // Level triggered
#define BCAST 0x00080000 // Send to all APICs, including self.
#define BUSY 0x00001000
#define FIXED 0x00000000
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
#define X1 0x0000000B // divide counts by 1
#define PERIODIC 0x00020000 // Periodic
#define PCINT (0x0340/4) // Performance Counter LVT
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
#define MASKED 0x00010000 // Interrupt masked
#define TICR (0x0380/4) // Timer Initial Count
#define TCCR (0x0390/4) // Timer Current Count
#define TDCR (0x03E0/4) // Timer Divide Configuration
#define IOAPIC_ADDR 0xFEC00000 // Default physical address of IO APIC
#define REG_ID 0x00 // Register index: ID
#define REG_VER 0x01 // Register index: version
#define REG_TABLE 0x10 // Redirection table base
#define INT_DISABLED 0x00010000 // Interrupt disabled
#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-)
#define INT_ACTIVELOW 0x00002000 // Active low (vs high)
#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID)
volatile unsigned int *__am_lapic = NULL; // Initialized in mp.c
struct IOAPIC {
uint32_t reg, pad[3], data;
} __attribute__((packed));
typedef struct IOAPIC IOAPIC;
static volatile IOAPIC *ioapic;
static void lapicw(int index, int value) {
__am_lapic[index] = value;
__am_lapic[ID];
}
void __am_percpu_initlapic(void) {
lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
lapicw(TDCR, X1);
lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
lapicw(TICR, 10000000);
lapicw(LINT0, MASKED);
lapicw(LINT1, MASKED);
if (((__am_lapic[VER]>>16) & 0xFF) >= 4)
lapicw(PCINT, MASKED);
lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
lapicw(ESR, 0);
lapicw(ESR, 0);
lapicw(EOI, 0);
lapicw(ICRHI, 0);
lapicw(ICRLO, BCAST | INIT | LEVEL);
while(__am_lapic[ICRLO] & DELIVS) ;
lapicw(TPR, 0);
}
void __am_lapic_eoi(void) {
if (__am_lapic)
lapicw(EOI, 0);
}
void __am_lapic_bootap(uint32_t apicid, void *addr) {
int i;
uint16_t *wrv;
outb(0x70, 0xF);
outb(0x71, 0x0A);
wrv = (unsigned short*)((0x40<<4 | 0x67));
wrv[0] = 0;
wrv[1] = (uintptr_t)addr >> 4;
lapicw(ICRHI, apicid<<24);
lapicw(ICRLO, INIT | LEVEL | ASSERT);
lapicw(ICRLO, INIT | LEVEL);
for (i = 0; i < 2; i++){
lapicw(ICRHI, apicid<<24);
lapicw(ICRLO, STARTUP | ((uintptr_t)addr>>12));
}
}
static unsigned int ioapicread(int reg) {
ioapic->reg = reg;
return ioapic->data;
}
static void ioapicwrite(int reg, unsigned int data) {
ioapic->reg = reg;
ioapic->data = data;
}
void __am_ioapic_init(void) {
int i, maxintr;
ioapic = (volatile IOAPIC*)IOAPIC_ADDR;
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
for (i = 0; i <= maxintr; i++){
ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i));
ioapicwrite(REG_TABLE+2*i+1, 0);
}
}
void __am_ioapic_enable(int irq, int cpunum) {
ioapicwrite(REG_TABLE+2*irq, T_IRQ0 + irq);
ioapicwrite(REG_TABLE+2*irq+1, cpunum << 24);
}

View File

@ -1,20 +1,36 @@
#include "../x86-qemu.h"
#include "x86-qemu.h"
int __am_ncpu = 0;
struct cpu_local __am_cpuinfo[MAX_CPU] = {};
static void (* volatile user_entry)();
static volatile intptr_t apboot_done = 0;
static volatile intptr_t ap_ready = 0;
static void percpu_entry();
static void ap_entry();
static void jump_to(void (*entry)());
static void call_user_entry() {
user_entry();
panic("MPE entry should not return");
}
int _mpe_init(void (*entry)()) {
user_entry = entry;
jump_to(percpu_entry);
panic("bug: should not return");
return 1;
boot_record()->jmp_code = 0x000bfde9; // (16-bit) jmp (0x7c00)
for (int cpu = 1; cpu < __am_ncpu; cpu++) {
boot_record()->is_ap = 1;
__am_lapic_bootap(cpu, (void *)boot_record());
while (_atomic_xchg(&ap_ready, 0) != 1) {
pause();
}
}
call_user_entry();
return 0;
}
static void othercpu_entry() {
__am_percpu_init();
_atomic_xchg(&ap_ready, 1);
call_user_entry();
}
void __am_othercpu_entry() {
stack_switch_call(stack_top(&CPU->stack), othercpu_entry, 0);
}
int _ncpu() {
@ -26,41 +42,14 @@ int _cpu(void) {
}
intptr_t _atomic_xchg(volatile intptr_t *addr, intptr_t newval) {
intptr_t result;
asm volatile ("lock xchgl %0, %1":
"+m"(*addr), "=a"(result) : "1"(newval) : "cc");
return result;
return xchg(addr, newval);
}
static void percpu_entry() {
if (_cpu() == 0) { // bootstrap cpu, boot all aps
for (int cpu = 1; cpu < __am_ncpu; cpu++) {
BOOTREC->is_ap = 1;
BOOTREC->entry = percpu_entry;
__am_lapic_bootap(cpu, 0x7c00);
while (_atomic_xchg(&apboot_done, 0) != 1) {
pause();
}
void __am_stop_the_world() {
boot_record()->jmp_code = 0x0000feeb; // (16-bit) jmp .
for (int cpu = 0; cpu < __am_ncpu; cpu++) {
if (cpu != _cpu()) {
__am_lapic_bootap(cpu, (void *)boot_record());
}
user_entry();
} else { // this is an ap
jump_to(ap_entry);
}
}
static void ap_entry() {
__am_percpu_initgdt();
__am_percpu_initirq();
__am_percpu_initlapic();
__am_percpu_initpg();
_atomic_xchg(&apboot_done, 1);
user_entry();
}
static void jump_to(void (*entry)()) {
void *esp = CPU->stack + sizeof(CPU->stack);
asm volatile (
"movl %0, %%esp;" // switch stack, and the bootstrap stack at
"call *%1" // 0x7000 can be reused by ap's bootloader
: : "r"(esp) , "r"(entry));
}

View File

@ -0,0 +1,7 @@
#include "x86-qemu.h"
.globl _start
_start:
pushl $MAINARG_ADDR
pushl $0
jmp _start_c

69
am/src/x86/qemu/start64.S Normal file
View File

@ -0,0 +1,69 @@
#include <x86.h>
#include "x86-qemu.h"
.code32
.globl _start
_start:
movl $(PDPT_ADDR | PTE_P | PTE_W), %eax
cmpl (PML4_ADDR), %eax
je .long_mode_init
movl $(PDPT_ADDR | PTE_P | PTE_W), %eax
movl %eax, (PML4_ADDR)
movl $0, %ecx
movl $512, %esi // 512 pages
// |
.loop: // x
movl %ecx, %eax // |
shll $30, %eax // |
orl $(PTE_P | PTE_W | PTE_PS), %eax // 1 GiB page
movl %eax, PDPT_ADDR(, %ecx, 8)
movl %ecx, %eax
shrl $2, %eax
movl %eax, PDPT_ADDR + 4(, %ecx, 8)
inc %ecx
cmp %esi, %ecx
jne .loop
.long_mode_init:
movl $PML4_ADDR, %eax
movl %eax, %cr3 // %cr3 = PML4 base
movl $CR4_PAE, %eax
movl %eax, %cr4 // %cr4.PAE = 1
movl $0xc0000080, %ecx
rdmsr
orl $0x100, %eax
wrmsr // %EFER.LME = 1
movl %cr0, %eax
orl $CR0_PG, %eax
movl %eax, %cr0 // %cr0.PG = 1
lgdt gdt_ptr // bootstrap GDT
ljmp $8, $_start64 // should not return
.code64
_start64:
movw $0, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw %ax, %fs
movw %ax, %gs
movq $MAINARG_ADDR, %rdi
pushq $0
jmp _start_c
.align 16
gdt_ptr:
.word gdt64_end - gdt64_begin - 1
.quad gdt64_begin
gdt64_begin:
.long 0x00000000 // 0: null desc
.long 0x00000000
.long 0x00000000 // 1: code
.long 0x00209800
gdt64_end:

99
am/src/x86/qemu/trap32.S Normal file
View File

@ -0,0 +1,99 @@
#include "x86-qemu.h"
.globl __am_kcontext_start
__am_kcontext_start:
// eax = arg, ebx = entry
pushl %eax
pushl $__am_panic_on_return
jmpl *%ebx
trap:
cli
subl $20, %esp
pushl %ebp
pushl %edi
pushl %esi
pushl $0
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movw %ds, %ax
pushl %eax
pushl $0
movw $KSEL(SEG_KDATA), %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
pushl %esp
call __am_irq_handle
.globl __am_iret
__am_iret:
addl $4, %esp
popl %eax
movl %eax, %esp
addl $4, %esp
popl %eax
movw %ax, %ds
movw %ax, %es
cmpw $KSEL(SEG_KCODE), 36(%esp)
je .kernel_iret
.user_iret:
popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4, %esp
popl %esi
popl %edi
popl %ebp
iret
.kernel_iret:
popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4, %esp
/* stack frame:
28 ss
24 esp (not popped by iret when returning to ring0)
20 eflags ---> move to new-esp
16 cs
12 eip
8 ebp
4 edi
0 esi <--- %esp
*/
movl %esp, %ebp
movl 24(%ebp), %edi // %edi is new-esp
movl 20(%ebp), %esi; movl %esi, -4(%edi)
movl 16(%ebp), %esi; movl %esi, -8(%edi)
movl 12(%ebp), %esi; movl %esi, -12(%edi)
movl 8(%ebp), %esi; movl %esi, -16(%edi)
movl 4(%ebp), %esi; movl %esi, -20(%edi)
movl 0(%ebp), %esi; movl %esi, -24(%edi)
leal -24(%edi), %esp
popl %esi
popl %edi
popl %ebp
iret
#define NOERR push $0
#define ERR
#define IRQ_DEF(id, dpl, err) \
.globl __am_irq##id; __am_irq##id: cli; err; push $id; jmp trap;
IRQS(IRQ_DEF)
.globl __am_irqall; __am_irqall: cli; push $0; push $-1; jmp trap;

61
am/src/x86/qemu/trap64.S Normal file
View File

@ -0,0 +1,61 @@
#include "x86-qemu.h"
.globl __am_kcontext_start
__am_kcontext_start:
// rdi = arg, rsi = entry
pushq $__am_panic_on_return
jmpq *%rsi
trap:
cli
subq $48, %rsp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %r11
pushq %r10
pushq %r9
pushq %r8
pushq %rdi
pushq %rsi
pushq %rbp
pushq %rdx
pushq %rcx
pushq %rbx
pushq %rax
pushq $0 // uvm, saved in __am_irq_handle
movq %rsp, %rdi
call __am_irq_handle
.globl __am_iret
__am_iret:
movq %rdi, %rsp
movq 160(%rsp), %rax
movw %ax, %ds
movw %ax, %es
addq $8, %rsp
popq %rax
popq %rbx
popq %rcx
popq %rdx
popq %rbp
popq %rsi
popq %rdi
popq %r8
popq %r9
popq %r10
popq %r11
popq %r12
popq %r13
popq %r14
popq %r15
iretq
#define NOERR push $0
#define ERR
#define IRQ_DEF(id, dpl, err) \
.globl __am_irq##id; __am_irq##id: cli; err; push $id; jmp trap;
IRQS(IRQ_DEF)
.globl __am_irqall; __am_irqall: cli; push $0; push $-1; jmp trap;

View File

@ -1,44 +1,114 @@
#include "../x86-qemu.h"
#include "x86-qemu.h"
_Area _heap = {}; // the heap memory defined in AM spec
_Area _heap = {};
volatile uint32_t *__am_lapic;
int __am_ncpu = 0;
struct cpu_local __am_cpuinfo[MAX_CPU];
int main(const char *args);
static void heap_init();
void _start(const char *args) { // the bootloader jumps here
__am_bootcpu_init();
heap_init();
__am_percpu_initgdt();
__am_percpu_initlapic();
__am_ioapic_init();
static void call_main(const char *args) {
_halt(main(args));
}
void _putc(char ch) { // only works for x86-qemu
void _start_c(char *args) {
if (boot_record()->is_ap) {
__am_othercpu_entry();
} else {
__am_bootcpu_init();
stack_switch_call(stack_top(&CPU->stack), call_main, (uintptr_t)args);
}
}
void __am_bootcpu_init() {
_heap = __am_heap_init();
__am_lapic_init();
__am_ioapic_init();
__am_percpu_init();
}
void __am_percpu_init() {
__am_percpu_initgdt();
__am_percpu_initlapic();
__am_percpu_initirq();
}
void _putc(char ch) {
#define COM1 0x3f8
outb(COM1, ch); // first -serial device
outb(COM1, ch);
}
void _halt(int code) {
const char *hex = "0123456789abcdef";
const char *fmt = "CPU #$ Halt (40).\n";
cli();
__am_othercpu_halt();
char buf[] = "Exited (#).\n";
for (char *p = buf; *p; p++) {
_putc((*p == '#') ? ('0' + code) : *p);
}
__am_thiscpu_halt();
}
static void heap_init() {
#define MAGIC 0x5a5aa5a5
#define STEP (1L << 20)
extern char end;
uintptr_t st, ed;
for (st = ed = ROUNDUP(&end, STEP); ; ed += STEP) {
volatile uint32_t *ptr = (uint32_t *)ed;
if ((*ptr = MAGIC, *ptr) != MAGIC) {
break; // read-after-write fail
__am_stop_the_world();
for (const char *p = fmt; *p; p++) {
char ch = *p;
switch (ch) {
case '$':
_putc(hex[_cpu()]);
break;
case '0': case '4':
_putc(hex[(code >> (ch - '0')) & 0xf]);
break;
default:
_putc(ch);
}
}
_heap = RANGE(st, ed);
outw(0x604, 0x2000); // offer of qemu :)
while (1) hlt();
}
_Area __am_heap_init() {
extern char end;
outb(0x70, 0x34);
uint32_t lo = inb(0x71);
outb(0x70, 0x35);
uint32_t hi = inb(0x71) + 1;
return RANGE(ROUNDUP(&end, 1 << 20), (uintptr_t)((lo | hi << 8) << 16));
}
void __am_lapic_init() {
for (char *st = (char *)0xf0000; st != (char *)0xffffff; st ++) {
if (*(volatile uint32_t *)st == 0x5f504d5f) {
uint32_t mpconf_ptr = ((volatile MPDesc *)st)->conf;
MPConf *conf = upcast(mpconf_ptr);
__am_lapic = upcast(conf->lapicaddr);
for (volatile char *ptr = (char *)(conf + 1);
ptr < (char *)conf + conf->length; ptr += 8) {
if (*ptr == '\0') {
ptr += 12;
panic_on(++__am_ncpu > MAX_CPU, "cannot support > MAX_CPU processors");
}
}
return;
}
}
bug();
}
void __am_percpu_initgdt() {
#if __x86_64__
SegDesc *gdt = CPU->gdt;
TSS64 *tss = &CPU->tss;
gdt[SEG_KCODE] = SEG64(STA_X | STA_R, DPL_KERN);
gdt[SEG_KDATA] = SEG64(STA_W, DPL_KERN);
gdt[SEG_UCODE] = SEG64(STA_X | STA_R, DPL_USER);
gdt[SEG_UDATA] = SEG64(STA_W, DPL_USER);
gdt[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, DPL_KERN);
bug_on((uintptr_t)tss >> 32);
set_gdt(gdt, sizeof(gdt[0]) * (NR_SEG + 1));
set_tr(KSEL(SEG_TSS));
#else
SegDesc *gdt = CPU->gdt;
TSS32 *tss = &CPU->tss;
gdt[SEG_KCODE] = SEG32(STA_X | STA_R, 0, 0xffffffff, DPL_KERN);
gdt[SEG_KDATA] = SEG32(STA_W, 0, 0xffffffff, DPL_KERN);
gdt[SEG_UCODE] = SEG32(STA_X | STA_R, 0, 0xffffffff, DPL_USER);
gdt[SEG_UDATA] = SEG32(STA_W, 0, 0xffffffff, DPL_USER);
gdt[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, DPL_KERN);
set_gdt(gdt, sizeof(gdt[0]) * NR_SEG);
set_tr(KSEL(SEG_TSS));
#endif
}

View File

@ -1,135 +1,183 @@
#include "../x86-qemu.h"
#include "x86-qemu.h"
struct vm_area {
_Area area;
int physical;
const struct mmu_config mmu = {
.pgsize = 4096,
#if __x86_64__
.ptlevels = 4,
.pgtables = {
{ "CR3", 0x000000000000, 0, 0 },
{ "PML4", 0xff8000000000, 39, 9 },
{ "PDPT", 0x007fc0000000, 30, 9 },
{ "PD", 0x00003fe00000, 21, 9 },
{ "PT", 0x0000001ff000, 12, 9 },
},
#else
.ptlevels = 2,
.pgtables = {
{ "CR3", 0x00000000, 0, 0 },
{ "PD", 0xffc00000, 22, 10 },
{ "PT", 0x003ff000, 12, 10 },
},
#endif
};
static const struct vm_area areas[] = {
{ RANGE(0x00000000, 0x20000000), 1 }, // kernel code/data
{ RANGE(0x40000000, 0x80000000), 0 }, // protected address space range
{ RANGE(0xf0000000, 0x00000000), 1 }, // system mmap area
static const struct vm_area vm_areas[] = {
#ifdef __x86_64__
{ RANGE(0x100000000000, 0x108000000000), 0 }, // 512 GiB user space
{ RANGE(0x000000000000, 0x008000000000), 1 }, // 512 GiB kernel
#else
{ RANGE( 0x40000000, 0x80000000), 0 }, // 1 GiB user space
{ RANGE( 0x00000000, 0x40000000), 1 }, // 1 GiB kernel
{ RANGE( 0xfd000000, 0x00000000), 1 }, // memory-mapped I/O
#endif
};
#define uvm_area (areas[1].area)
#define uvm_area (vm_areas[0].area)
static void *pgalloc();
static void pgfree(void *ptr);
static uintptr_t *kpt;
static void *(*pgalloc)(size_t size);
static void (*pgfree)(void *);
static PDE *kpt;
static void *(*user_pgalloc)(size_t);
static void (*user_pgfree)(void *);
static void *pgallocz() {
uintptr_t *base = pgalloc(mmu.pgsize);
panic_on(!base, "cannot allocate page");
for (int i = 0; i < mmu.pgsize / sizeof(uintptr_t); i++) {
base[i] = 0;
}
return base;
}
int _vme_init(void *(*pgalloc_f)(size_t), void (*pgfree_f)(void *)) {
if (_cpu() != 0) panic("init VME in non-bootstrap CPU");
static int indexof(uintptr_t addr, const struct ptinfo *info) {
return ((uintptr_t)addr & info->mask) >> info->shift;
}
user_pgalloc = pgalloc_f;
user_pgfree = pgfree_f;
static uintptr_t baseof(uintptr_t addr) {
return addr & ~(mmu.pgsize - 1);
}
kpt = pgalloc();
for (int i = 0; i < LENGTH(areas); i++) {
const struct vm_area *seg = &areas[i];
if (!seg->physical) continue;
for (uint32_t pa = (uint32_t)seg->area.start;
pa != (uint32_t)seg->area.end;
pa += PGSIZE) {
PTE *ptab;
if (!(kpt[PDX(pa)] & PTE_P)) {
ptab = pgalloc();
kpt[PDX(pa)] = PTE_P | PTE_W | (uint32_t)ptab;
} else {
ptab = (PTE*)PTE_ADDR(kpt[PDX(pa)]);
static uintptr_t *ptwalk(_AddressSpace *as, uintptr_t addr, int flags) {
uintptr_t cur = (uintptr_t)&as->ptr;
for (int i = 0; i <= mmu.ptlevels; i++) {
const struct ptinfo *ptinfo = &mmu.pgtables[i];
uintptr_t *pt = (uintptr_t *)cur, next_page;
int index = indexof(addr, ptinfo);
if (i == mmu.ptlevels) return &pt[index];
if (!(pt[index] & PTE_P)) {
next_page = (uintptr_t)pgallocz();
pt[index] = next_page | PTE_P | flags;
} else {
next_page = baseof(pt[index]);
}
cur = next_page;
}
bug();
}
static void teardown(int level, uintptr_t *pt) {
if (level > mmu.ptlevels) return;
for (int index = 0; index < (1 << mmu.pgtables[level].bits); index++) {
if ((pt[index] & PTE_P) && (pt[index] & PTE_U)) {
teardown(level + 1, (void *)baseof(pt[index]));
}
}
if (level >= 1) {
pgfree(pt);
}
}
int _vme_init(void *(*_pgalloc)(size_t size), void (*_pgfree)(void *)) {
panic_on(_cpu() != 0, "init VME in non-bootstrap CPU");
pgalloc = _pgalloc;
pgfree = _pgfree;
#if __x86_64__
kpt = (void *)PML4_ADDR;
#else
_AddressSpace as;
as.ptr = NULL;
for (int i = 0; i < LENGTH(vm_areas); i++) {
const struct vm_area *vma = &vm_areas[i];
if (vma->kernel) {
for (uintptr_t cur = (uintptr_t)vma->area.start;
cur != (uintptr_t)vma->area.end;
cur += mmu.pgsize) {
*ptwalk(&as, cur, PTE_W) = cur | PTE_P | PTE_W;
}
ptab[PTX(pa)] = PTE_P | PTE_W | pa;
}
}
__am_percpu_initpg(); // set CR3 and CR0 if kpt is not NULL
kpt = (void *)baseof((uintptr_t)as.ptr);
#endif
set_cr3(kpt);
set_cr0(get_cr0() | CR0_PG);
return 0;
}
void __am_percpu_initpg() { // called by all cpus
if (kpt) {
set_cr3(kpt);
set_cr0(get_cr0() | CR0_PG);
}
}
void _protect(_AddressSpace *as) {
uintptr_t *upt = pgallocz();
int _protect(_AddressSpace *p) {
PDE *upt = pgalloc();
for (int i = 0; i < PGSIZE / sizeof(PDE *); i++) {
upt[i] = kpt[i];
}
*p = (_AddressSpace) {
.pgsize = PGSIZE,
.area = uvm_area,
.ptr = upt,
};
return 0;
}
void _unprotect(_AddressSpace *p) {
PDE *upt = p->ptr;
for (uint32_t va = (uint32_t)uvm_area.start;
va != (uint32_t)uvm_area.end;
va += (1 << PDXSHFT)) {
PDE pde = upt[PDX(va)];
if (pde & PTE_P) {
pgfree((void *)PTE_ADDR(pde));
for (int i = 0; i < LENGTH(vm_areas); i++) {
const struct vm_area *vma = &vm_areas[i];
if (vma->kernel) {
const struct ptinfo *info = &mmu.pgtables[1]; // level-1 page table
for (uintptr_t cur = (uintptr_t)vma->area.start;
cur != (uintptr_t)vma->area.end;
cur += (1L << info->shift)) {
int index = indexof(cur, info);
upt[index] = kpt[index];
}
}
}
pgfree(upt);
as->pgsize = mmu.pgsize;
as->area = uvm_area;
as->ptr = (void *)((uintptr_t)upt | PTE_P | PTE_U);
}
int _map(_AddressSpace *p, void *va, void *pa, int prot) {
// panic because the below cases are likely bugs
if ((prot & _PROT_NONE) && (prot != _PROT_NONE))
panic("invalid protection flags");
if ((uintptr_t)va != ROUNDDOWN(va, PGSIZE) ||
(uintptr_t)pa != ROUNDDOWN(pa, PGSIZE)) {
panic("unaligned memory address");
}
if (!IN_RANGE(va, uvm_area)) {
return 1; // mapping an out-of-range address
}
PDE *upt = (PDE*)p->ptr;
PDE *pde = &upt[PDX(va)];
void _unprotect(_AddressSpace *as) {
teardown(0, (void *)&as->ptr);
}
if (!(*pde & PTE_P)) {
*pde = PTE_P | PTE_W | PTE_U | (uint32_t)(pgalloc());
}
void _map(_AddressSpace *as, void *va, void *pa, int prot) {
panic_on(!IN_RANGE(va, uvm_area), "mapping an invalid address");
panic_on((prot & _PROT_NONE) && (prot != _PROT_NONE), "invalid protection flags");
panic_on((uintptr_t)va != ROUNDDOWN(va, mmu.pgsize) ||
(uintptr_t)pa != ROUNDDOWN(pa, mmu.pgsize), "non-page-boundary address");
PTE *pte = &((PTE*)PTE_ADDR(*pde))[PTX(va)];
uintptr_t *ptentry = ptwalk(as, (uintptr_t)va, PTE_W | PTE_U);
if (prot & _PROT_NONE) {
*pte = 0; // unmap @va
panic_on(!(*ptentry & PTE_P), "unmapping a non-mapped page");
*ptentry = 0;
} else {
*pte = PTE_P | ((prot & _PROT_WRITE) ? PTE_W : 0) | PTE_U
| (uint32_t)(pa); // map @va -> @pa
panic_on(*ptentry & PTE_P, "remapping a mapped page");
uintptr_t pte = (uintptr_t)pa | PTE_P | PTE_U | ((prot & _PROT_WRITE) ? PTE_W : 0);
*ptentry = pte;
}
return 0;
ptwalk(as, (uintptr_t)va, PTE_W | PTE_U);
}
_Context *_ucontext(_AddressSpace *as, _Area ustack, _Area kstack,
void *entry, void *args) {
_Context *ctx = (_Context*)kstack.start;
*ctx = (_Context) {
.cs = USEL(SEG_UCODE), .eip = (uint32_t)entry, .eflags = FL_IF,
.ds = USEL(SEG_UDATA), .es = USEL(SEG_UDATA),
.ss = USEL(SEG_UDATA), .esp3 = (uint32_t)ustack.end,
.ss0 = KSEL(SEG_KDATA), .esp0 = (uint32_t)kstack.end,
.eax = (uint32_t)args, // just use eax to pass @args
.uvm = as,
};
_Context *_ucontext(_AddressSpace *as, _Area kstack, void *entry) {
_Context *ctx = kstack.end - sizeof(_Context);
*ctx = (_Context) { 0 };
#if __x86_64__
ctx->cs = USEL(SEG_UCODE);
ctx->ss = USEL(SEG_UDATA);
ctx->rip = (uintptr_t)entry;
ctx->rflags = FL_IF;
ctx->rsp = (uintptr_t)uvm_area.end;
ctx->rsp0 = (uintptr_t)kstack.end;
#else
ctx->cs = USEL(SEG_UCODE);
ctx->ds = USEL(SEG_UDATA);
ctx->ss3 = USEL(SEG_UDATA);
ctx->eip = (uintptr_t)entry;
ctx->eflags = FL_IF;
ctx->esp = (uintptr_t)uvm_area.end;
ctx->esp0 = (uintptr_t)kstack.end;
#endif
ctx->uvm = as->ptr;
return ctx;
}
static void *pgalloc() {
void *ret = user_pgalloc(PGSIZE);
if (!ret) panic("page allocation fail"); // for ease of debugging
for (int i = 0; i < PGSIZE / sizeof(uint32_t); i++) {
((uint32_t *)ret)[i] = 0;
}
return ret;
}
static void pgfree(void *ptr) {
user_pgfree(ptr);
}

101
am/src/x86/qemu/x86-qemu.h Normal file
View File

@ -0,0 +1,101 @@
#include <x86.h>
#define PML4_ADDR 0x1000
#define PDPT_ADDR 0x2000
#define NR_SEG 6 // GDT size
#define SEG_KCODE 1 // Kernel code
#define SEG_KDATA 2 // Kernel data/stack
#define SEG_UCODE 3 // User code
#define SEG_UDATA 4 // User data/stack
#define SEG_TSS 5 // Global unique task state segement
#define NR_IRQ 256 // IDT size
#ifndef __ASSEMBLER__
#include <am.h>
#include <klib-macros.h>
struct kernel_stack {
uint8_t stack[8192];
};
static inline void *stack_top(struct kernel_stack *stk) {
return stk->stack + sizeof(stk->stack);
}
struct mmu_config {
int ptlevels, pgsize;
struct ptinfo {
const char *name;
uintptr_t mask;
int shift, bits;
} pgtables[];
};
struct vm_area {
_Area area;
int kernel;
};
void __am_iret(_Context *ctx);
struct cpu_local {
_AddressSpace *uvm;
#if __x86_64__
SegDesc gdt[NR_SEG + 1];
TSS64 tss;
#else
SegDesc gdt[NR_SEG];
TSS32 tss;
#endif
struct kernel_stack stack;
struct kernel_stack irq_stack;
};
#if __x86_64__
struct trap_frame {
_Context saved_context;
uint64_t irq, errcode;
uint64_t rip, cs, rflags, rsp, ss;
};
#else
struct trap_frame {
_Context saved_context;
uint32_t irq, errcode;
uint32_t eip, cs, eflags, esp, ss;
};
#endif
extern volatile uint32_t *__am_lapic;
extern int __am_ncpu;
extern struct cpu_local __am_cpuinfo[MAX_CPU];
#define CPU (&__am_cpuinfo[_cpu()])
#define bug_on(cond) \
do { \
if (cond) panic("internal error (likely a bug in AM)"); \
} while (0)
#define bug() bug_on(1)
// apic utils
void __am_lapic_eoi();
void __am_ioapic_init();
void __am_lapic_bootap(uint32_t cpu, void *address);
void __am_ioapic_enable(int irq, int cpu);
// x86-specific operations
void __am_bootcpu_init();
void __am_percpu_init();
_Area __am_heap_init();
void __am_lapic_init();
void __am_othercpu_entry();
void __am_percpu_initirq();
void __am_percpu_initgdt();
void __am_percpu_initlapic();
void __am_stop_the_world();
#endif

View File

@ -1,63 +0,0 @@
#ifndef __AM_X86_H__
#define __AM_X86_H__
#include <am.h>
#include <amdev.h>
#include <x86.h>
#include <klib.h> // for debugging
#define MAX_CPU 8
struct boot_info {
int is_ap;
void (*entry)();
};
struct cpu_local {
_AddressSpace *uvm;
SegDesc gdt[NR_SEG];
TSS tss;
uint8_t stack[4096];
};
extern volatile uint32_t *__am_lapic;
extern int __am_ncpu;
extern struct cpu_local __am_cpuinfo[MAX_CPU];
#define CPU (&__am_cpuinfo[_cpu()])
#define BOOTREC ((volatile struct boot_info *)0x7000)
#define LENGTH(arr) (sizeof(arr) / sizeof((arr)[0]))
#define RANGE(st, ed) (_Area) { .start = (void *)st, .end = (void *)ed }
#define IN_RANGE(ptr, area) ((area).start <= (ptr) && (ptr) < (area).end)
#define STRINGIFY(s) #s
#define TOSTRING(s) STRINGIFY(s)
#define panic(s) \
do { \
puts("AM Panic: "); puts(s); \
puts(" @ " __FILE__ ":" TOSTRING(__LINE__) " \n"); \
_halt(1); \
} while(0)
static inline void puts(const char *s) {
for (; *s; s++)
_putc(*s);
}
// apic utils
void __am_lapic_eoi();
void __am_ioapic_init();
void __am_lapic_bootap(unsigned int cpu, uint32_t address);
void __am_ioapic_enable(int irq, int cpu);
// per-cpu x86-specific operations
void __am_bootcpu_init();
void __am_percpu_initirq();
void __am_percpu_initgdt();
void __am_percpu_initlapic();
void __am_percpu_initpg();
void __am_thiscpu_setstk0(uintptr_t ss0, uintptr_t esp0);
void __am_thiscpu_halt() __attribute__((__noreturn__));
void __am_othercpu_halt();
#endif

View File

@ -1,231 +1,231 @@
# Coremark
'''
File: CoreMark
Topic: Welcome
Copyright <20> 2009 EEMBC All rights reserved.
CoreMark is a trademark of EEMBC and EEMBC is a registered trademark of the Embedded Microprocessor Benchmark Consortium.
CoreMark<EFBFBD>s primary goals are simplicity and providing a method for testing only a processor<6F>s core features.
For more information about EEMBC's comprehensive embedded benchmark suites, please see www.eembc.org.
Topic: Building and running
Download the release files from the www.coremark.org.
You can verify the download using the coremark_<version>.md5 file
> md5sum -c coremark_<version>.md5
Unpack the distribution (tar -vzxf coremark_<version>.tgz && tar -vzxf coremark_<version>_docs.tgz)
then change to the coremark_<version> folder.
To build and run the benchmark, type
> make
Full results are available in the files run1.log and run2.log.
CoreMark result can be found in run1.log.
For self hosted Linux or Cygwin platforms, a simple make should work.
Cross Compile:
For cross compile platforms please adjust <core_portme.mak>, <core_portme.h> (and possibly <core_portme.c>)
according to the specific platform used.
When porting to a new platform, it is recommended to copy one of the default port folders
(e.g. mkdir <platform> && cp linux/* <platform>), adjust the porting files, and run
> make PORT_DIR=<platform>
Systems without make:
The following files need to be compiled:
- <core_list_join.c>
- <core_main.c>
- <core_matrix.c>
- <core_state.c>
- <core_util.c>
- <PORT_DIR>/<core_portme.c>
For example
> gcc -O2 -o coremark.exe core_list_join.c core_main.c core_matrix.c core_state.c core_util.c simple/core_portme.c -DPERFORMANCE_RUN=1 -DITERATIONS=1000
> ./coremark.exe > run1.log
The above will compile the benchmark for a performance run and 1000 iterations. Output is redirected to run1.log.
Make targets:
run - Default target, creates run1.log and run2.log.
run1.log - Run the benchmark with performance parameters, and output to run1.log
run2.log - Run the benchmark with validation parameters, and output to run2.log
run3.log - Run the benchmark with profile generation parameters, and output to run3.log
compile - compile the benchmark executable
link - link the benchmark executable
check - test MD5 of sources that may not be modified
clean - clean temporary files
ITERATIONS:
By default, the benchmark will run between 10-100 seconds.
To override, use ITERATIONS=N
> make ITERATIONS=10
Will run the benchmark for 10 iterations.
It is recommended to set a specific number of iterations in certain situations e.g.:
- Running with a simulator
- Measuring power/energy
- Timing cannot be restarted
Minimum required run time:
Results are only valid for reporting if the benchmark ran for at least 10 secs!
XCFLAGS:
To add compiler flags from the command line, use XCFLAGS e.g.
> make XCFLAGS="-g -DMULTITHREAD=4 -DUSE_FORK=1"
o CORE_DEBUG
Define to compile for a debug run if you get incorrect CRC.
> make XCFLAGS="-DCORE_DEBUG=1"
o Parallel Execution
Use XCFLAGS=-DMULTITHREAD=N where N is number of threads to run in parallel.
Several implementations are available to execute in multiple contexts,
or you can implement your own in <core_portme.c>.
> make XCFLAGS="-DMULTITHREAD=4 -DUSE_PTHREAD"
Above will compile the benchmark for execution on 4 cores, using POSIX Threads API.
REBUILD:
To force rebuild, add the flag REBUILD to the command line
> make REBUILD=1
Check core_portme.mak for more important options.
Run parameters for the benchmark executable:
Coremark executable takes several parameters as follows (if main accepts arguments).
1st - A seed value used for initialization of data.
2nd - A seed value used for initialization of data.
3rd - A seed value used for initialization of data.
4th - Number of iterations (0 for auto : default value)
5th - Reserved for internal use.
6th - Reserved for internal use.
7th - For malloc users only, ovreride the size of the input data buffer.
The run target from make will run coremark with 2 different data initialization seeds.
Alternative parameters:
If not using malloc or command line arguments are not supported, the buffer size
for the algorithms must be defined via the compiler define TOTAL_DATA_SIZE.
TOTAL_DATA_SIZE must be set to 2000 bytes (default) for standard runs.
The default for such a target when testing different configurations could be ...
> make XCFLAGS="-DTOTAL_DATA_SIZE=6000 -DMAIN_HAS_NOARGC=1"
Topic: Documentation
When you unpack the documentation (tar -vzxf coremark_<version>_docs.tgz) a docs folder will be created.
Check the file docs/html/index.html and the website http://www.coremark.org for more info.
Topic: Submitting results
CoreMark results can be submitted on the web.
Open a web browser and go to http://www.coremark.org/benchmark/index.php?pg=benchmark
Select the link to add a new score and follow the instructions.
Topic: Run rules
What is and is not allowed.
Required:
1 - The benchmark needs to run for at least 10 seconds.
2 - All validation must succeed for seeds 0,0,0x66 and 0x3415,0x3415,0x66,
buffer size of 2000 bytes total.
o If not using command line arguments to main:
> make XCFLAGS="-DPERFORMANCE_RUN=1" REBUILD=1 run1.log
> make XCFLAGS="-DVALIDATION_RUN=1" REBUILD=1 run2.log
3 - If using profile guided optimization, profile must be generated using seeds of 8,8,8,
and buffer size of 1200 bytes total.
> make XCFLAGS="-DTOTAL_DATA_SIZE=1200 -DPROFILE_RUN=1" REBUILD=1 run3.log
4 - All source files must be compiled with the same flags.
5 - All data type sizes must match size in bits such that:
o ee_u8 is an 8 bits datatype.
o ee_s16 is an 16 bits datatype.
o ee_u16 is an 16 bits datatype.
o ee_s32 is an 32 bits datatype.
o ee_u32 is an 32 bits datatype.
Allowed:
- Changing number of iterations
- Changing toolchain and build/load/run options
- Changing method of acquiring a data memory block
- Changing the method of acquiring seed values
- Changing implementation in core_portme.c
- Changing configuration values in core_portme.h
- Changing core_portme.mak
Not allowed:
- Changing of source file other then core_portme* (use make check to validate)
Topic: Reporting rules
How to report results on a data sheet?
CoreMark 1.0 : N / C [/ P] [/ M]
N - Number of iterations per second with seeds 0,0,0x66,size=2000)
C - Compiler version and flags
P - Parameters such as data and code allocation specifics
- This parameter *may* be omitted if all data was allocated on the heap in RAM.
- This parameter *may not* be omitted when reporting CoreMark/MHz
M - Type of parallel execution (if used) and number of contexts
This parameter may be omitted if parallel execution was not used.
e.g.
> CoreMark 1.0 : 128 / GCC 4.1.2 -O2 -fprofile-use / Heap in TCRAM / FORK:2
or
> CoreMark 1.0 : 1400 / GCC 3.4 -O4
If reporting scaling results, the results must be reported as follows:
CoreMark/MHz 1.0 : N / C / P [/ M]
P - When reporting scaling results, memory parameter must also indicate memory frequency:core frequency ratio.
- If the core has cache and cache frequency to core frequency ratio is configurable, that must also be included.
e.g.
> CoreMark/MHz 1.0 : 1.47 / GCC 4.1.2 -O2 / DDR3(Heap) 30:1 Memory 1:1 Cache
Topic: Log File Format
The log files have the following format
(start example)
2K performance run parameters for coremark. (Run type)
CoreMark Size : 666 (Buffer size)
Total ticks : 25875 (platform dependent value)
Total time (secs) : 25.875000 (actual time in seconds)
Iterations/Sec : 3864.734300 (Performance value to report)
Iterations : 100000 (number of iterations used)
Compiler version : GCC3.4.4 (Compiler and version)
Compiler flags : -O2 (Compiler and linker flags)
Memory location : Code in flash, data in on chip RAM
seedcrc : 0xe9f5 (identifier for the input seeds)
[0]crclist : 0xe714 (validation for list part)
[0]crcmatrix : 0x1fd7 (validation for matrix part)
[0]crcstate : 0x8e3a (validation for state part)
[0]crcfinal : 0x33ff (iteration dependent output)
Correct operation validated. See readme.txt for run and reporting rules. (*Only when run is successful*)
CoreMark 1.0 : 6508.490622 / GCC3.4.4 -O2 / Heap (*Only on a successful performance run*)
(end example)
Topic: Legal
See LICENSE.txt or the word document file under docs/LICENSE.doc.
For more information on your legal rights to use this benchmark, please see
http://www.coremark.org/download/register.php?pg=register
Topic: Credits
Many thanks to all of the individuals who helped with the development or testing of CoreMark including (Sorted by company name)
o Alan Anderson, ADI
o Adhikary Rajiv, ADI
o Elena Stohr, ARM
o Ian Rickards, ARM
o Andrew Pickard, ARM
o Trent Parker, CAVIUM
o Shay Gal-On, EEMBC
o Markus Levy, EEMBC
o Ron Olson, IBM
o Eyal Barzilay, MIPS
o Jens Eltze, NEC
o Hirohiko Ono, NEC
o Ulrich Drees, NEC
o Frank Roscheda, NEC
o Rob Cosaro, NXP
o Shumpei Kawasaki, RENESAS
'''
# Coremark
'''
File: CoreMark
Topic: Welcome
Copyright <20> 2009 EEMBC All rights reserved.
CoreMark is a trademark of EEMBC and EEMBC is a registered trademark of the Embedded Microprocessor Benchmark Consortium.
CoreMark<EFBFBD>s primary goals are simplicity and providing a method for testing only a processor<6F>s core features.
For more information about EEMBC's comprehensive embedded benchmark suites, please see www.eembc.org.
Topic: Building and running
Download the release files from the www.coremark.org.
You can verify the download using the coremark_<version>.md5 file
> md5sum -c coremark_<version>.md5
Unpack the distribution (tar -vzxf coremark_<version>.tgz && tar -vzxf coremark_<version>_docs.tgz)
then change to the coremark_<version> folder.
To build and run the benchmark, type
> make
Full results are available in the files run1.log and run2.log.
CoreMark result can be found in run1.log.
For self hosted Linux or Cygwin platforms, a simple make should work.
Cross Compile:
For cross compile platforms please adjust <core_portme.mak>, <core_portme.h> (and possibly <core_portme.c>)
according to the specific platform used.
When porting to a new platform, it is recommended to copy one of the default port folders
(e.g. mkdir <platform> && cp linux/* <platform>), adjust the porting files, and run
> make PORT_DIR=<platform>
Systems without make:
The following files need to be compiled:
- <core_list_join.c>
- <core_main.c>
- <core_matrix.c>
- <core_state.c>
- <core_util.c>
- <PORT_DIR>/<core_portme.c>
For example
> gcc -O2 -o coremark.exe core_list_join.c core_main.c core_matrix.c core_state.c core_util.c simple/core_portme.c -DPERFORMANCE_RUN=1 -DITERATIONS=1000
> ./coremark.exe > run1.log
The above will compile the benchmark for a performance run and 1000 iterations. Output is redirected to run1.log.
Make targets:
run - Default target, creates run1.log and run2.log.
run1.log - Run the benchmark with performance parameters, and output to run1.log
run2.log - Run the benchmark with validation parameters, and output to run2.log
run3.log - Run the benchmark with profile generation parameters, and output to run3.log
compile - compile the benchmark executable
link - link the benchmark executable
check - test MD5 of sources that may not be modified
clean - clean temporary files
ITERATIONS:
By default, the benchmark will run between 10-100 seconds.
To override, use ITERATIONS=N
> make ITERATIONS=10
Will run the benchmark for 10 iterations.
It is recommended to set a specific number of iterations in certain situations e.g.:
- Running with a simulator
- Measuring power/energy
- Timing cannot be restarted
Minimum required run time:
Results are only valid for reporting if the benchmark ran for at least 10 secs!
XCFLAGS:
To add compiler flags from the command line, use XCFLAGS e.g.
> make XCFLAGS="-g -DMULTITHREAD=4 -DUSE_FORK=1"
o CORE_DEBUG
Define to compile for a debug run if you get incorrect CRC.
> make XCFLAGS="-DCORE_DEBUG=1"
o Parallel Execution
Use XCFLAGS=-DMULTITHREAD=N where N is number of threads to run in parallel.
Several implementations are available to execute in multiple contexts,
or you can implement your own in <core_portme.c>.
> make XCFLAGS="-DMULTITHREAD=4 -DUSE_PTHREAD"
Above will compile the benchmark for execution on 4 cores, using POSIX Threads API.
REBUILD:
To force rebuild, add the flag REBUILD to the command line
> make REBUILD=1
Check core_portme.mak for more important options.
Run parameters for the benchmark executable:
Coremark executable takes several parameters as follows (if main accepts arguments).
1st - A seed value used for initialization of data.
2nd - A seed value used for initialization of data.
3rd - A seed value used for initialization of data.
4th - Number of iterations (0 for auto : default value)
5th - Reserved for internal use.
6th - Reserved for internal use.
7th - For malloc users only, ovreride the size of the input data buffer.
The run target from make will run coremark with 2 different data initialization seeds.
Alternative parameters:
If not using malloc or command line arguments are not supported, the buffer size
for the algorithms must be defined via the compiler define TOTAL_DATA_SIZE.
TOTAL_DATA_SIZE must be set to 2000 bytes (default) for standard runs.
The default for such a target when testing different configurations could be ...
> make XCFLAGS="-DTOTAL_DATA_SIZE=6000 -DMAIN_HAS_NOARGC=1"
Topic: Documentation
When you unpack the documentation (tar -vzxf coremark_<version>_docs.tgz) a docs folder will be created.
Check the file docs/html/index.html and the website http://www.coremark.org for more info.
Topic: Submitting results
CoreMark results can be submitted on the web.
Open a web browser and go to http://www.coremark.org/benchmark/index.php?pg=benchmark
Select the link to add a new score and follow the instructions.
Topic: Run rules
What is and is not allowed.
Required:
1 - The benchmark needs to run for at least 10 seconds.
2 - All validation must succeed for seeds 0,0,0x66 and 0x3415,0x3415,0x66,
buffer size of 2000 bytes total.
o If not using command line arguments to main:
> make XCFLAGS="-DPERFORMANCE_RUN=1" REBUILD=1 run1.log
> make XCFLAGS="-DVALIDATION_RUN=1" REBUILD=1 run2.log
3 - If using profile guided optimization, profile must be generated using seeds of 8,8,8,
and buffer size of 1200 bytes total.
> make XCFLAGS="-DTOTAL_DATA_SIZE=1200 -DPROFILE_RUN=1" REBUILD=1 run3.log
4 - All source files must be compiled with the same flags.
5 - All data type sizes must match size in bits such that:
o ee_u8 is an 8 bits datatype.
o ee_s16 is an 16 bits datatype.
o ee_u16 is an 16 bits datatype.
o ee_s32 is an 32 bits datatype.
o ee_u32 is an 32 bits datatype.
Allowed:
- Changing number of iterations
- Changing toolchain and build/load/run options
- Changing method of acquiring a data memory block
- Changing the method of acquiring seed values
- Changing implementation in core_portme.c
- Changing configuration values in core_portme.h
- Changing core_portme.mak
Not allowed:
- Changing of source file other then core_portme* (use make check to validate)
Topic: Reporting rules
How to report results on a data sheet?
CoreMark 1.0 : N / C [/ P] [/ M]
N - Number of iterations per second with seeds 0,0,0x66,size=2000)
C - Compiler version and flags
P - Parameters such as data and code allocation specifics
- This parameter *may* be omitted if all data was allocated on the heap in RAM.
- This parameter *may not* be omitted when reporting CoreMark/MHz
M - Type of parallel execution (if used) and number of contexts
This parameter may be omitted if parallel execution was not used.
e.g.
> CoreMark 1.0 : 128 / GCC 4.1.2 -O2 -fprofile-use / Heap in TCRAM / FORK:2
or
> CoreMark 1.0 : 1400 / GCC 3.4 -O4
If reporting scaling results, the results must be reported as follows:
CoreMark/MHz 1.0 : N / C / P [/ M]
P - When reporting scaling results, memory parameter must also indicate memory frequency:core frequency ratio.
- If the core has cache and cache frequency to core frequency ratio is configurable, that must also be included.
e.g.
> CoreMark/MHz 1.0 : 1.47 / GCC 4.1.2 -O2 / DDR3(Heap) 30:1 Memory 1:1 Cache
Topic: Log File Format
The log files have the following format
(start example)
2K performance run parameters for coremark. (Run type)
CoreMark Size : 666 (Buffer size)
Total ticks : 25875 (platform dependent value)
Total time (secs) : 25.875000 (actual time in seconds)
Iterations/Sec : 3864.734300 (Performance value to report)
Iterations : 100000 (number of iterations used)
Compiler version : GCC3.4.4 (Compiler and version)
Compiler flags : -O2 (Compiler and linker flags)
Memory location : Code in flash, data in on chip RAM
seedcrc : 0xe9f5 (identifier for the input seeds)
[0]crclist : 0xe714 (validation for list part)
[0]crcmatrix : 0x1fd7 (validation for matrix part)
[0]crcstate : 0x8e3a (validation for state part)
[0]crcfinal : 0x33ff (iteration dependent output)
Correct operation validated. See readme.txt for run and reporting rules. (*Only when run is successful*)
CoreMark 1.0 : 6508.490622 / GCC3.4.4 -O2 / Heap (*Only on a successful performance run*)
(end example)
Topic: Legal
See LICENSE.txt or the word document file under docs/LICENSE.doc.
For more information on your legal rights to use this benchmark, please see
http://www.coremark.org/download/register.php?pg=register
Topic: Credits
Many thanks to all of the individuals who helped with the development or testing of CoreMark including (Sorted by company name)
o Alan Anderson, ADI
o Adhikary Rajiv, ADI
o Elena Stohr, ARM
o Ian Rickards, ARM
o Andrew Pickard, ARM
o Trent Parker, CAVIUM
o Shay Gal-On, EEMBC
o Markus Levy, EEMBC
o Ron Olson, IBM
o Eyal Barzilay, MIPS
o Jens Eltze, NEC
o Hirohiko Ono, NEC
o Ulrich Drees, NEC
o Frank Roscheda, NEC
o Rob Cosaro, NXP
o Shumpei Kawasaki, RENESAS
'''

View File

@ -14,10 +14,10 @@
/************************/
/* Data types and settings */
/************************/
/* Configuration : HAS_FLOAT
/* Configuration : HAS_FLOAT
Define to 1 if the platform supports floating point.
*/
#ifndef HAS_FLOAT
#ifndef HAS_FLOAT
#define HAS_FLOAT 0
#endif
/* Configuration : HAS_TIME_H
@ -55,23 +55,23 @@ typedef uint32_t CORE_TICKS;
/* Definitions : COMPILER_VERSION, COMPILER_FLAGS, MEM_LOCATION
Initialize these strings per platform
*/
#ifndef COMPILER_VERSION
#ifndef COMPILER_VERSION
#ifdef __GNUC__
#define COMPILER_VERSION "GCC"__VERSION__
#else
#define COMPILER_VERSION "Please put compiler version here (e.g. gcc 4.1)"
#endif
#endif
#ifndef COMPILER_FLAGS
#define COMPILER_FLAGS
#ifndef COMPILER_FLAGS
#define COMPILER_FLAGS
#endif
#ifndef MEM_LOCATION
#ifndef MEM_LOCATION
#define MEM_LOCATION "STACK"
#endif
/* Data Types :
To avoid compiler issues, define the data types that need ot be used for 8b, 16b and 32b in <core_portme.h>.
*Imprtant* :
ee_ptr_int needs to be the data type used to hold pointers, otherwise coremark may fail!!!
*/
@ -90,7 +90,7 @@ typedef size_t ee_size_t;
/* Configuration : SEED_METHOD
Defines method to get seed values that cannot be computed at compile time.
Valid values :
SEED_ARG - from command line.
SEED_FUNC - from a system function.
@ -102,7 +102,7 @@ typedef size_t ee_size_t;
/* Configuration : MEM_METHOD
Defines method to get a block of memry.
Valid values :
MEM_MALLOC - for platforms that implement malloc and have malloc.h.
MEM_STATIC - to use a static memory array.
@ -113,19 +113,19 @@ typedef size_t ee_size_t;
#endif
/* Configuration : MULTITHREAD
Define for parallel execution
Define for parallel execution
Valid values :
1 - only one context (default).
N>1 - will execute N copies in parallel.
Note :
Note :
If this flag is defined to more then 1, an implementation for launching parallel contexts must be defined.
Two sample implementations are provided. Use <USE_PTHREAD> or <USE_FORK> to enable them.
It is valid to have a different implementation of <core_start_parallel> and <core_end_parallel> in <core_portme.c>,
to fit a particular architecture.
to fit a particular architecture.
*/
#ifndef MULTITHREAD
#define MULTITHREAD 1
@ -135,22 +135,22 @@ typedef size_t ee_size_t;
#endif
/* Configuration : MAIN_HAS_NOARGC
Needed if platform does not support getting arguments to main.
Needed if platform does not support getting arguments to main.
Valid values :
0 - argc/argv to main is supported
1 - argc/argv to main is not supported
Note :
Note :
This flag only matters if MULTITHREAD has been defined to a value greater then 1.
*/
#ifndef MAIN_HAS_NOARGC
#ifndef MAIN_HAS_NOARGC
#define MAIN_HAS_NOARGC 0
#endif
/* Configuration : MAIN_HAS_NORETURN
Needed if platform does not support returning a value from main.
Needed if platform does not support returning a value from main.
Valid values :
0 - main returns an int, and return value will be 0.
1 - platform does not support returning a value from main

View File

@ -1,21 +1,21 @@
/*
Author : Shay Gal-On, EEMBC
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
EEMBC CoreMark Software is a product of EEMBC and is provided under the terms of the
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
make sure that you are in compliance with Run and Reporting rules specified in the accompanying readme.txt file.
EEMBC
EEMBC
4354 Town Center Blvd. Suite 114-200
El Dorado Hills, CA, 95762
*/
El Dorado Hills, CA, 95762
*/
/* Topic: Description
This file contains declarations of the various benchmark functions.
*/
@ -23,7 +23,7 @@ El Dorado Hills, CA, 95762
/* Configuration: TOTAL_DATA_SIZE
Define total size for data algorithms will operate on
*/
#ifndef TOTAL_DATA_SIZE
#ifndef TOTAL_DATA_SIZE
#define TOTAL_DATA_SIZE 2*1000
#endif
@ -48,7 +48,7 @@ El Dorado Hills, CA, 95762
void *iterate(void *pres);
/* Typedef: secs_ret
For machines that have floating point support, get number of seconds as a double.
For machines that have floating point support, get number of seconds as a double.
Otherwise an unsigned int.
*/
#if HAS_FLOAT
@ -58,12 +58,12 @@ typedef ee_u32 secs_ret;
#endif
#if MAIN_HAS_NORETURN
#define MAIN_RETURN_VAL
#define MAIN_RETURN_VAL
#define MAIN_RETURN_TYPE void
#else
#define MAIN_RETURN_VAL 0
#define MAIN_RETURN_TYPE int
#endif
#endif
void start_time(void);
void stop_time(void);
@ -130,7 +130,7 @@ typedef enum CORE_STATE {
NUM_CORE_STATES
} core_state_e ;
/* Helper structure to hold results */
typedef struct RESULTS_S {
/* inputs */
@ -165,7 +165,7 @@ ee_u16 core_bench_list(core_results *res, ee_s16 finder_idx);
/* state benchmark functions */
void core_init_state(ee_u32 size, ee_s16 seed, ee_u8 *p);
ee_u16 core_bench_state(ee_u32 blksize, ee_u8 *memblock,
ee_u16 core_bench_state(ee_u32 blksize, ee_u8 *memblock,
ee_s16 seed1, ee_s16 seed2, ee_s16 step, ee_u16 crc);
/* matrix benchmark functions */

View File

@ -1,21 +1,21 @@
/*
Author : Shay Gal-On, EEMBC
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
EEMBC CoreMark Software is a product of EEMBC and is provided under the terms of the
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
make sure that you are in compliance with Run and Reporting rules specified in the accompanying readme.txt file.
EEMBC
EEMBC
4354 Town Center Blvd. Suite 114-200
El Dorado Hills, CA, 95762
*/
El Dorado Hills, CA, 95762
*/
#include "coremark.h"
/*
@ -23,29 +23,29 @@ Topic: Description
Benchmark using a linked list.
Linked list is a common data structure used in many applications.
For our purposes, this will excercise the memory units of the processor.
In particular, usage of the list pointers to find and alter data.
We are not using Malloc since some platforms do not support this library.
Instead, the memory block being passed in is used to create a list,
and the benchmark takes care not to add more items then can be
accomodated by the memory block. The porting layer will make sure
that we have a valid memory block.
All operations are done in place, without using any extra memory.
The list itself contains list pointers and pointers to data items.
Data items contain the following:
idx - An index that captures the initial order of the list.
data - Variable data initialized based on the input parameters. The 16b are divided as follows:
o Upper 8b are backup of original data.
o Bit 7 indicates if the lower 7 bits are to be used as is or calculated.
o Bits 0-2 indicate type of operation to perform to get a 7b value.
o Bits 3-6 provide input for the operation.
*/
/* local functions */
@ -88,7 +88,7 @@ ee_s16 calc_func(ee_s16 *pdata, core_results *res) {
break;
}
res->crc=crcu16(retval,res->crc);
retval &= 0x007f;
retval &= 0x007f;
*pdata = (data & 0xff00) | 0x0080 | retval; /* cache the result */
return retval;
}
@ -232,7 +232,7 @@ list_head *core_list_init(ee_u32 blksize, list_head *memblock, ee_s16 seed) {
info.idx=0x7fff;
info.data16=(ee_s16)0xffff;
core_list_insert_new(list,&info,&memblock,&datablock,memblock_end,datablock_end);
/* then insert size items */
for (i=0; i<size; i++) {
ee_u16 datpat=((ee_u16)(seed^i) & 0xf);
@ -246,7 +246,7 @@ list_head *core_list_init(ee_u32 blksize, list_head *memblock, ee_s16 seed) {
while (finder->next!=NULL) {
if (i<size/5) /* first 20% of the list in order */
finder->info->idx=i++;
else {
else {
ee_u16 pat=(ee_u16)(i++ ^ seed); /* get a pseudo random number */
finder->info->idx=0x3fff & (((i & 0x07) << 8) | pat); /* make sure the mixed items end up after the ones in sequence */
}
@ -282,21 +282,21 @@ list_head *core_list_init(ee_u32 blksize, list_head *memblock, ee_s16 seed) {
list_head *core_list_insert_new(list_head *insert_point, list_data *info, list_head **memblock, list_data **datablock
, list_head *memblock_end, list_data *datablock_end) {
list_head *newitem;
if ((*memblock+1) >= memblock_end)
return NULL;
if ((*datablock+1) >= datablock_end)
return NULL;
newitem=*memblock;
(*memblock)++;
newitem->next=insert_point->next;
insert_point->next=newitem;
newitem->info=*datablock;
(*datablock)++;
copy_info(newitem->info,info);
return newitem;
}
@ -304,10 +304,10 @@ list_head *core_list_insert_new(list_head *insert_point, list_data *info, list_h
Remove an item from the list.
Operation:
For a singly linked list, remove by copying the data from the next item
For a singly linked list, remove by copying the data from the next item
over to the current cell, and unlinking the next item.
Note:
Note:
since there is always a fake item at the end of the list, no need to check for NULL.
Returns:
@ -331,7 +331,7 @@ list_head *core_list_remove(list_head *item) {
Operation:
Since we want each iteration of the benchmark to be exactly the same,
we need to be able to undo a remove.
we need to be able to undo a remove.
Link the removed item back into the list, and switch the info items.
Parameters:
@ -340,7 +340,7 @@ list_head *core_list_remove(list_head *item) {
Returns:
The item that was linked back to the list.
*/
list_head *core_list_undo_remove(list_head *item_removed, list_head *item_modified) {
list_data *tmp;
@ -406,7 +406,7 @@ list_head *core_list_reverse(list_head *list) {
Sort the list in place without recursion.
Description:
Use mergesort, as for linked list this is a realistic solution.
Use mergesort, as for linked list this is a realistic solution.
Also, since this is aimed at embedded, care was taken to use iterative rather then recursive algorithm.
The sort can either return the list to original order (by idx) ,
or use the data item to invoke other other algorithms and change the order of the list.
@ -418,7 +418,7 @@ list_head *core_list_reverse(list_head *list) {
Returns:
New head of the list.
Note:
Note:
We have a special header for the list that will always be first,
but the algorithm could theoretically modify where the list starts.
@ -480,7 +480,7 @@ list_head *core_list_mergesort(list_head *list, list_cmp cmp, core_results *res)
/* now p has stepped `insize' places along, and q has too */
p = q;
}
tail->next = NULL;
/* If we have done only one merge, we're finished. */

View File

@ -1,21 +1,21 @@
/*
Author : Shay Gal-On, EEMBC
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
EEMBC CoreMark Software is a product of EEMBC and is provided under the terms of the
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
make sure that you are in compliance with Run and Reporting rules specified in the accompanying readme.txt file.
EEMBC
EEMBC
4354 Town Center Blvd. Suite 114-200
El Dorado Hills, CA, 95762
*/
El Dorado Hills, CA, 95762
*/
/* File: core_main.c
This file contains the framework to acquire a block of memory, seed initial parameters, tun t he benchmark and report the results.
*/
@ -144,7 +144,7 @@ MAIN_RETURN_TYPE main(int argc, char *argv[]) {
#elif (MEM_METHOD==MEM_MALLOC)
for (i=0 ; i<MULTITHREAD; i++) {
ee_s32 malloc_override=get_seed(7);
if (malloc_override != 0)
if (malloc_override != 0)
results[i].size=malloc_override;
else
results[i].size=TOTAL_DATA_SIZE;
@ -168,13 +168,13 @@ MAIN_RETURN_TYPE main(int argc, char *argv[]) {
#else
#error "Please define a way to initialize a memory block."
#endif
/* Data init */
/* Data init */
/* Find out how space much we have based on number of algorithms */
for (i=0; i<NUM_ALGORITHMS; i++) {
if ((1<<(ee_u32)i) & results[0].execs)
num_algorithms++;
}
for (i=0 ; i<MULTITHREAD; i++)
for (i=0 ; i<MULTITHREAD; i++)
results[i].size=results[i].size/num_algorithms;
/* Assign pointers */
for (i=0; i<NUM_ALGORITHMS; i++) {
@ -197,9 +197,9 @@ MAIN_RETURN_TYPE main(int argc, char *argv[]) {
core_init_state(results[0].size,results[i].seed1,results[i].memblock[3]);
}
}
/* automatically determine number of iterations if not set */
if (results[0].iterations==0) {
if (results[0].iterations==0) {
secs_ret secs_passed=0;
ee_u32 divisor;
results[0].iterations=1;
@ -240,7 +240,7 @@ MAIN_RETURN_TYPE main(int argc, char *argv[]) {
seedcrc=crc16(results[0].seed2,seedcrc);
seedcrc=crc16(results[0].seed3,seedcrc);
seedcrc=crc16(results[0].size,seedcrc);
switch (seedcrc) { /* test known output for common seeds */
case 0x8a02: /* seed1=0, seed2=0, seed3=0x66, size 2000 per algorithm */
known_id=0;
@ -269,7 +269,7 @@ MAIN_RETURN_TYPE main(int argc, char *argv[]) {
if (known_id>=0) {
for (i=0 ; i<default_num_contexts; i++) {
results[i].err=0;
if ((results[i].execs & ID_LIST) &&
if ((results[i].execs & ID_LIST) &&
(results[i].crclist!=list_known_crc[known_id])) {
ee_printf("[%u]ERROR! list crc 0x%04x - should be 0x%04x\n",i,results[i].crclist,list_known_crc[known_id]);
results[i].err++;
@ -294,7 +294,7 @@ MAIN_RETURN_TYPE main(int argc, char *argv[]) {
ee_printf("Total time (ms) : %f\n",time_in_secs(total_time));
if (time_in_secs(total_time) > 0)
ee_printf("Iterations/mSec : %f\n",default_num_contexts*results[0].iterations/time_in_secs(total_time));
#else
#else
ee_printf("Total time (ms) : %d\n",time_in_secs(total_time));
#endif
ee_printf("Iterations : %d\n",(int)default_num_contexts*results[0].iterations);
@ -305,15 +305,15 @@ MAIN_RETURN_TYPE main(int argc, char *argv[]) {
/* output for verification */
ee_printf("seedcrc : 0x%04x\n",seedcrc);
if (results[0].execs & ID_LIST)
for (i=0 ; i<default_num_contexts; i++)
for (i=0 ; i<default_num_contexts; i++)
ee_printf("[%d]crclist : 0x%04x\n",i,results[i].crclist);
if (results[0].execs & ID_MATRIX)
for (i=0 ; i<default_num_contexts; i++)
if (results[0].execs & ID_MATRIX)
for (i=0 ; i<default_num_contexts; i++)
ee_printf("[%d]crcmatrix : 0x%04x\n",i,results[i].crcmatrix);
if (results[0].execs & ID_STATE)
for (i=0 ; i<default_num_contexts; i++)
for (i=0 ; i<default_num_contexts; i++)
ee_printf("[%d]crcstate : 0x%04x\n",i,results[i].crcstate);
for (i=0 ; i<default_num_contexts; i++)
for (i=0 ; i<default_num_contexts; i++)
ee_printf("[%d]crcfinal : 0x%04x\n",i,results[i].crc);
ee_printf("Finised in %d ms.\n", (int)total_time);
if (total_errors==0) {
@ -327,13 +327,13 @@ MAIN_RETURN_TYPE main(int argc, char *argv[]) {
ee_printf("Cannot validate operation for these seed values, please compare with results on a known platform.\n");
#if (MEM_METHOD==MEM_MALLOC)
for (i=0 ; i<MULTITHREAD; i++)
for (i=0 ; i<MULTITHREAD; i++)
portable_free(results[i].memblock[0]);
#endif
/* And last call any target specific code for finalizing */
portable_fini(&(results[0].port));
return MAIN_RETURN_VAL;
return MAIN_RETURN_VAL;
}

View File

@ -1,31 +1,31 @@
/*
Author : Shay Gal-On, EEMBC
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
EEMBC CoreMark Software is a product of EEMBC and is provided under the terms of the
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
make sure that you are in compliance with Run and Reporting rules specified in the accompanying readme.txt file.
EEMBC
EEMBC
4354 Town Center Blvd. Suite 114-200
El Dorado Hills, CA, 95762
*/
El Dorado Hills, CA, 95762
*/
#include "coremark.h"
/*
Topic: Description
Matrix manipulation benchmark
This very simple algorithm forms the basis of many more complex algorithms.
The tight inner loop is the focus of many optimizations (compiler as well as hardware based)
and is thus relevant for embedded processing.
This very simple algorithm forms the basis of many more complex algorithms.
The tight inner loop is the focus of many optimizations (compiler as well as hardware based)
and is thus relevant for embedded processing.
The total available data space will be divided to 3 parts:
NxN Matrix A - initialized with small values (upper 3/4 of the bits all zero).
NxN Matrix B - initialized with medium values (upper half of the bits all zero).
@ -75,7 +75,7 @@ void printmatC(MATRES *C, ee_u32 N, char *name) {
/* Function: core_bench_matrix
Benchmark function
Iterate <matrix_test> N times,
Iterate <matrix_test> N times,
changing the matrix values slightly by a constant amount each time.
*/
ee_u16 core_bench_matrix(mat_params *p, ee_s16 seed, ee_u16 crc) {
@ -101,11 +101,11 @@ ee_u16 core_bench_matrix(mat_params *p, ee_s16 seed, ee_u16 crc) {
Returns:
A CRC value that captures all results calculated in the function.
In particular, crc of the value calculated on the result matrix
In particular, crc of the value calculated on the result matrix
after each step by <matrix_sum>.
Operation:
1 - Add a constant value to all elements of a matrix.
2 - Multiply a matrix by a constant.
3 - Multiply a matrix by a vector.
@ -142,7 +142,7 @@ ee_s16 matrix_test(ee_u32 N, MATRES *C, MATDAT *A, MATDAT *B, MATDAT val) {
#if CORE_DEBUG
printmatC(C,N,"matrix_mul_matrix_bitextract");
#endif
matrix_add_const(N,A,-val); /* return matrix to initial value */
return crc;
}
@ -158,7 +158,7 @@ ee_s16 matrix_test(ee_u32 N, MATRES *C, MATDAT *A, MATDAT *B, MATDAT val) {
Returns:
Matrix dimensions.
Note:
The seed parameter MUST be supplied from a source that cannot be determined at compile time
*/
@ -173,7 +173,7 @@ ee_u32 core_init_matrix(ee_u32 blksize, void *memblk, ee_s32 seed, mat_params *p
seed=1;
while (j<blksize) {
i++;
j=i*i*2*4;
j=i*i*2*4;
}
N=i-1;
A=(MATDAT *)align_mem(memblk);
@ -207,10 +207,10 @@ ee_u32 core_init_matrix(ee_u32 blksize, void *memblk, ee_s32 seed, mat_params *p
Calculate a function that depends on the values of elements in the matrix.
For each element, accumulate into a temporary variable.
As long as this value is under the parameter clipval,
As long as this value is under the parameter clipval,
add 1 to the result if the element is bigger then the previous.
Otherwise, reset the accumulator and add 10 to the result.
*/
ee_s16 matrix_sum(ee_u32 N, MATRES *C, MATDAT clipval) {

View File

@ -19,7 +19,7 @@
volatile ee_s32 seed5_volatile=0;
/* Porting : Timing functions
How to capture time and convert to seconds must be ported to whatever is supported by the platform.
e.g. Read value from on board RTC, read value from cpu clock cycles performance counter etc.
e.g. Read value from on board RTC, read value from cpu clock cycles performance counter etc.
Sample implementation for standard time.h and windows.h definitions included.
*/
/* Define : TIMER_RES_DIVIDER
@ -29,7 +29,7 @@
If there are issues with the return value overflowing, increase this value.
*/
#define NSECS_PER_SEC CLOCKS_PER_SEC
#define CORETIMETYPE clock_t
#define CORETIMETYPE clock_t
#define GETMYTIME(_t) (*_t=clock())
#define MYTIMEDIFF(fin,ini) ((fin)-(ini))
#define TIMER_RES_DIVIDER 1
@ -42,7 +42,7 @@ unsigned long start_time_val, stop_time_val;
/* Function : start_time
This function will be called right before starting the timed portion of the benchmark.
Implementation may be capturing a system timer (as implemented in the example code)
Implementation may be capturing a system timer (as implemented in the example code)
or zeroing some system parameters - e.g. setting the cpu clocks cycles to 0.
*/
void start_time(void) {
@ -51,7 +51,7 @@ void start_time(void) {
/* Function : stop_time
This function will be called right after ending the timed portion of the benchmark.
Implementation may be capturing a system timer (as implemented in the example code)
Implementation may be capturing a system timer (as implemented in the example code)
or other system parameters - e.g. reading the current value of cpu cycles counter.
*/
void stop_time(void) {
@ -59,11 +59,11 @@ void stop_time(void) {
}
/* Function : get_time
Return an abstract "ticks" number that signifies time on the system.
Actual value returned may be cpu cycles, milliseconds or any other value,
as long as it can be converted to seconds by <time_in_secs>.
This methodology is taken to accomodate any hardware or simulated platform.
The sample implementation returns millisecs by default,
The sample implementation returns millisecs by default,
and the resolution is controlled by <TIMER_RES_DIVIDER>
*/
CORE_TICKS get_time(void) {
@ -83,7 +83,7 @@ secs_ret time_in_secs(CORE_TICKS ticks) {
ee_u32 default_num_contexts=1;
/* Function : portable_init
Target specific initialization code
Target specific initialization code
Test for some common mistakes.
*/
void portable_init(core_portable *p, int *argc, char *argv[])
@ -97,7 +97,7 @@ void portable_init(core_portable *p, int *argc, char *argv[])
p->portable_id=1;
}
/* Function : portable_fini
Target specific final code
Target specific final code
*/
void portable_fini(core_portable *p)
{

View File

@ -1,21 +1,21 @@
/*
Author : Shay Gal-On, EEMBC
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
EEMBC CoreMark Software is a product of EEMBC and is provided under the terms of the
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
make sure that you are in compliance with Run and Reporting rules specified in the accompanying readme.txt file.
EEMBC
EEMBC
4354 Town Center Blvd. Suite 114-200
El Dorado Hills, CA, 95762
*/
El Dorado Hills, CA, 95762
*/
#include "coremark.h"
/* local functions */
enum CORE_STATE core_state_transition( ee_u8 **instr , ee_u32 *transition_count);
@ -23,13 +23,13 @@ enum CORE_STATE core_state_transition( ee_u8 **instr , ee_u32 *transition_count)
/*
Topic: Description
Simple state machines like this one are used in many embedded products.
For more complex state machines, sometimes a state transition table implementation is used instead,
For more complex state machines, sometimes a state transition table implementation is used instead,
trading speed of direct coding for ease of maintenance.
Since the main goal of using a state machine in CoreMark is to excercise the switch/if behaviour,
we are using a small moore machine.
we are using a small moore machine.
In particular, this machine tests type of string input,
trying to determine whether the input is a number or something else.
(see core_state.png).
@ -38,10 +38,10 @@ Topic: Description
/* Function: core_bench_state
Benchmark function
Go over the input twice, once direct, and once after introducing some corruption.
Go over the input twice, once direct, and once after introducing some corruption.
*/
ee_u16 core_bench_state(ee_u32 blksize, ee_u8 *memblock,
ee_s16 seed1, ee_s16 seed2, ee_s16 step, ee_u16 crc)
ee_u16 core_bench_state(ee_u32 blksize, ee_u8 *memblock,
ee_s16 seed1, ee_s16 seed2, ee_s16 step, ee_u16 crc)
{
ee_u32 final_counts[NUM_CORE_STATES];
ee_u32 track_counts[NUM_CORE_STATES];
@ -109,7 +109,7 @@ static ee_u8 *errpat[4] ={(ee_u8 *)"T0.3e-1F",(ee_u8 *)"-T.T++Tq",(ee_u8 *)"1T3
Populate the input with several predetermined strings, interspersed.
Actual patterns chosen depend on the seed parameter.
Note:
The seed parameter MUST be supplied from a source that cannot be determined at compile time
*/
@ -177,7 +177,7 @@ static ee_u8 ee_isdigit(ee_u8 c) {
The state machine will continue scanning until either:
1 - an invalid input is detcted.
2 - a valid number has been detected.
The input pointer is updated to point to the end of the token, and the end state is returned (either specific format determined or invalid).
*/

View File

@ -1,33 +1,33 @@
/*
Author : Shay Gal-On, EEMBC
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
This file is part of EEMBC(R) and CoreMark(TM), which are Copyright (C) 2009
All rights reserved.
EEMBC CoreMark Software is a product of EEMBC and is provided under the terms of the
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
CoreMark License that is distributed with the official EEMBC COREMARK Software release.
If you received this EEMBC CoreMark Software without the accompanying CoreMark License,
you must discontinue use and download the official release from www.coremark.org.
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
Also, if you are publicly displaying scores generated from the EEMBC CoreMark software,
make sure that you are in compliance with Run and Reporting rules specified in the accompanying readme.txt file.
EEMBC
EEMBC
4354 Town Center Blvd. Suite 114-200
El Dorado Hills, CA, 95762
*/
El Dorado Hills, CA, 95762
*/
#include "coremark.h"
/* Function: get_seed
Get a values that cannot be determined at compile time.
Since different embedded systems and compilers are used, 3 different methods are provided:
1 - Using a volatile variable. This method is only valid if the compiler is forced to generate code that
reads the value of a volatile variable from memory at run time.
reads the value of a volatile variable from memory at run time.
Please note, if using this method, you would need to modify core_portme.c to generate training profile.
2 - Command line arguments. This is the preferred method if command line arguments are supported.
3 - System function. If none of the first 2 methods is available on the platform,
a system function which is not a stub can be used.
e.g. read the value on GPIO pins connected to switches, or invoke special simulator functions.
*/
#if (SEED_METHOD==SEED_VOLATILE)
@ -153,7 +153,7 @@ ee_u16 crcu8(ee_u8 data, ee_u16 crc )
crc ^= 0x4002;
carry = 1;
}
else
else
carry = 0;
crc >>= 1;
if (carry)
@ -162,7 +162,7 @@ ee_u16 crcu8(ee_u8 data, ee_u16 crc )
crc &= 0x7fff;
}
return crc;
}
}
ee_u16 crcu16(ee_u16 newval, ee_u16 crc) {
crc=crcu8( (ee_u8) (newval) ,crc);
crc=crcu8( (ee_u8) ((newval)>>8) ,crc);

View File

@ -25,12 +25,12 @@
*
* Collection of Results:
* Reinhold Weicker (address see above) and
*
*
* Rick Richardson
* PC Research. Inc.
* 94 Apple Orchard Drive
* Tinton Falls, NJ 07724
* Phone: (201) 389-8963 (9-17 EST)
* Phone: (201) 389-8963 (9-17 EST)
* Usenet: ...!uunet!pcrat!rick
*
* Please send results to Rick Richardson and/or Reinhold Weicker.
@ -69,7 +69,7 @@
* -DTIME
* The "times" function of UNIX (returning process times)
* or the "time" function (returning wallclock time)
* is used for measurement.
* is used for measurement.
* For single user machines, "time ()" is adequate. For
* multi-user machines where you cannot get single-user
* access, use the "times ()" function. If you have
@ -123,7 +123,7 @@
* version previously distributed by Reinhold Weicker.
*
* At several places in the benchmark, code has been added,
* but within the measurement loop only in branches that
* but within the measurement loop only in branches that
* are not executed. The intention is that optimizing compilers
* should be prevented from moving code out of the measurement
* loop, or from removing code altogether. Since the statements
@ -175,7 +175,7 @@
* - Output says which sort of clock it is using, and the HZ value
* - You can use -DREG instead of the -DREG=register of previous versions
* - Some stylistic cleanups.
*
*
***************************************************************************
*
* Compilation model and measurement (IMPORTANT):
@ -201,23 +201,23 @@
* different from the Ada version.]
*
* The following program contains statements of a high level programming
* language (here: C) in a distribution considered representative:
* language (here: C) in a distribution considered representative:
*
* assignments 52 (51.0 %)
* control statements 33 (32.4 %)
* procedure, function calls 17 (16.7 %)
*
* 103 statements are dynamically executed. The program is balanced with
* respect to the three aspects:
* respect to the three aspects:
*
* - statement type
* - operand type
* - operand locality
* operand global, local, parameter, or constant.
* operand global, local, parameter, or constant.
*
* The combination of these three aspects is balanced only approximately.
* The combination of these three aspects is balanced only approximately.
*
* 1. Statement Type:
* 1. Statement Type:
* ----------------- number
*
* V1 = V2 9
@ -261,9 +261,9 @@
* library procedure 1
* X = F (...)
* function call 6
* user function 5
* library function 1
* --
* user function 5
* library function 1
* --
* 17 17
* ---
* 103
@ -276,10 +276,10 @@
* number approximate
* percentage
*
* Arithmetic 32 50.8
* Arithmetic 32 50.8
*
* + 21 33.3
* - 7 11.1
* + 21 33.3
* - 7 11.1
* * 3 4.8
* / (int div) 1 1.6
*
@ -297,7 +297,7 @@
* && (AND-THEN) 1 1.6
* | (OR) 1 1.6
* ! (NOT) 2 3.2
*
*
* -- -----
* 63 100.1
*
@ -317,10 +317,10 @@
* 242 100.0 %
*
* When there is an access path leading to the final operand (e.g. a record
* component), only the final data type on the access path is counted.
* component), only the final data type on the access path is counted.
*
*
* 4. Operand Locality:
* 4. Operand Locality:
* -------------------
* number approximate
* percentage
@ -352,6 +352,7 @@
#include <am.h>
#include <klib.h>
#include <klib-macros.h>
#define Start_Timer() Begin_Time = uptime()
#define Stop_Timer() End_Time = uptime()
@ -381,10 +382,8 @@
/* General definitions: */
#define Null 0
#define Null 0
/* Value of a Null pointer */
#define true 1
#define false 0
typedef int One_Thirty;
typedef int One_Fifty;
@ -394,7 +393,7 @@ typedef char Str_30 [31];
typedef int Arr_1_Dim [50];
typedef int Arr_2_Dim [50] [50];
typedef struct record
typedef struct record
{
struct record *Ptr_Comp;
Enumeration Discr;
@ -473,27 +472,27 @@ void Proc_1 (Ptr_Val_Par)
REG Rec_Pointer Ptr_Val_Par;
/* executed once */
{
REG Rec_Pointer Next_Record = Ptr_Val_Par->Ptr_Comp;
REG Rec_Pointer Next_Record = Ptr_Val_Par->Ptr_Comp;
/* == Ptr_Glob_Next */
/* Local variable, initialized with Ptr_Val_Par->Ptr_Comp, */
/* corresponds to "rename" in Ada, "with" in Pascal */
structassign (*Ptr_Val_Par->Ptr_Comp, *Ptr_Glob);
structassign (*Ptr_Val_Par->Ptr_Comp, *Ptr_Glob);
Ptr_Val_Par->variant.var_1.Int_Comp = 5;
Next_Record->variant.var_1.Int_Comp
Next_Record->variant.var_1.Int_Comp
= Ptr_Val_Par->variant.var_1.Int_Comp;
Next_Record->Ptr_Comp = Ptr_Val_Par->Ptr_Comp;
Proc_3 (&Next_Record->Ptr_Comp);
/* Ptr_Val_Par->Ptr_Comp->Ptr_Comp
/* Ptr_Val_Par->Ptr_Comp->Ptr_Comp
== Ptr_Glob->Ptr_Comp */
if (Next_Record->Discr == Ident_1)
/* then, executed */
{
Next_Record->variant.var_1.Int_Comp = 6;
Proc_6 (Ptr_Val_Par->variant.var_1.Enum_Comp,
Proc_6 (Ptr_Val_Par->variant.var_1.Enum_Comp,
&Next_Record->variant.var_1.Enum_Comp);
Next_Record->Ptr_Comp = Ptr_Glob->Ptr_Comp;
Proc_7 (Next_Record->variant.var_1.Int_Comp, 10,
Proc_7 (Next_Record->variant.var_1.Int_Comp, 10,
&Next_Record->variant.var_1.Int_Comp);
}
else /* not executed */
@ -508,7 +507,7 @@ void Proc_2 (Int_Par_Ref)
One_Fifty *Int_Par_Ref;
{
One_Fifty Int_Loc;
One_Fifty Int_Loc;
Enumeration Enum_Loc;
Int_Loc = *Int_Par_Ref + 10;
@ -600,10 +599,10 @@ Enumeration *Enum_Ref_Par;
*Enum_Ref_Par = Ident_4;
switch (Enum_Val_Par)
{
case Ident_1:
case Ident_1:
*Enum_Ref_Par = Ident_1;
break;
case Ident_2:
case Ident_2:
if (Int_Glob > 100)
/* then */
*Enum_Ref_Par = Ident_1;
@ -613,7 +612,7 @@ Enumeration *Enum_Ref_Par;
*Enum_Ref_Par = Ident_2;
break;
case Ident_4: break;
case Ident_5:
case Ident_5:
*Enum_Ref_Par = Ident_3;
break;
} /* switch */
@ -773,7 +772,7 @@ int main ()
Ptr_Glob->Discr = Ident_1;
Ptr_Glob->variant.var_1.Enum_Comp = Ident_3;
Ptr_Glob->variant.var_1.Int_Comp = 40;
strcpy (Ptr_Glob->variant.var_1.Str_Comp,
strcpy (Ptr_Glob->variant.var_1.Str_Comp,
"DHRYSTONE PROGRAM, SOME STRING");
strcpy (Str_1_Loc, "DHRYSTONE PROGRAM, 1'ST STRING");
@ -939,10 +938,10 @@ int main ()
}
printf ("Finished in %d ms\n", (int)User_Time);
printk("==================================================\n");
printk("Dhrystone %s %d Marks\n", pass ? "PASS" : "FAIL",
printf("==================================================\n");
printf("Dhrystone %s %d Marks\n", pass ? "PASS" : "FAIL",
880900 / (int)User_Time * NUMBER_OF_RUNS/ 500000);
printk(" vs. 100000 Marks (i7-7700K @ 4.20GHz)\n");
printf(" vs. 100000 Marks (i7-7700K @ 4.20GHz)\n");
return 0;
}

18
apps/fceux/Makefile Normal file
View File

@ -0,0 +1,18 @@
NAME := fceux
ROM_PATH := $(AM_HOME)/share/games/nes
ROMS := $(shell ls $(ROM_PATH)/rom/)
ROM_SRC := $(addprefix $(ROM_PATH)/gen/, $(addsuffix .c, $(basename $(ROMS))))
FCEUX_SRC := $(shell find -L ./src/ -name "*.c" -o -name "*.cpp")
INCLUDES += -I$(ROM_PATH)/gen/
SRCS := $(FCEUX_SRC) $(ROM_SRC)
CFLAGS += -DPSS_STYLE=1 -DFRAMESKIP -D__NO_FILE_SYSTEM__
include $(AM_HOME)/Makefile.app
$(FCEUX_SRC): $(ROM_SRC)
$(ROM_SRC): rom
.PHONY: rom
rom:
@make -C $(ROM_PATH)

22
apps/fceux/README.md Normal file
View File

@ -0,0 +1,22 @@
# FCEUX
Nintendo Entertainment System模拟器, 可以正确运行大多数ROM.
移植自 https://github.com/TASVideos/fceux ,
git commit版本为`ed4f5d0000e17b6ae88c4e93e2f9e0695dbceac0`.
内置一些游戏ROM, 见`src/roms/rom`目录.
可通过`mainargs`选择运行的游戏, 如:
```
make ARCH=native run mainargs=mario
```
操作方式:
* U — SELECT
* I — START
* J — A键
* K — B键
* W/S/A/D — UP/DOWN/LEFT/RIGHT
需要正确的IOE (绘图、定时).

View File

@ -0,0 +1,111 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2006 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* TXC mappers, originally much complex banksitching
*
* P/N PRG MAP, UNIF Name
* 01-22111-000 (05-00002-010) (132, 22211) - MGC-001 Qi Wang
* 01-22110-000 (52S ) - MGC-002 2-in-1 Gun
* 01-22111-100 (02-00002-010) (173 ) - MGC-008 Mahjong Block
* (079 ) - MGC-012 Poke Block
* 01-22110-200 (05-00002-010) (036 ) - MGC-014 Strike Wolf
* 01-22000-400 (05-00002-010) (036 ) - MGC-015 Policeman
* 01-22017-000 (05-PT017-080) (189 ) - MGC-017 Thunder Warrior
* 01-11160-000 (04-02310-000) ( , 11160) - MGC-023 6-in-1
* 01-22026-000 (05-04010-090) ( ) - MGC-026 4-in-1
* 01-22270-000 (05-00002-010) (132, 22211) - MGC-xxx Creatom
* 01-22200-400 (------------) (079 ) - ET.03 F-15 City War
* (172 ) - 1991 Du Ma Racing
*
*/
#include "mapinc.h"
static uint8 reg[4], cmd, is172, is173;
static SFORMAT StateRegs[] =
{
{ reg, 4, "REGS" },
{ &cmd, 1, "CMD" },
{ 0 }
};
static void Sync(void) {
setprg32(0x8000, (reg[2] >> 2) & 1);
if (is172)
setchr8((((cmd ^ reg[2]) >> 3) & 2) | (((cmd ^ reg[2]) >> 5) & 1)); // 1991 DU MA Racing probably CHR bank sequence is WRONG, so it is possible to
// rearrange CHR banks for normal UNIF board and mapper 172 is unneccessary
else
setchr8(reg[2] & 3);
}
static DECLFW(UNL22211WriteLo) {
// FCEU_printf("bs %04x %02x\n",A,V);
reg[A & 3] = V;
}
static DECLFW(UNL22211WriteHi) {
// FCEU_printf("bs %04x %02x\n",A,V);
cmd = V;
Sync();
}
static DECLFR(UNL22211ReadLo) {
return (reg[1] ^ reg[2]) | (is173 ? 0x01 : 0x40);
// if(reg[3])
// return reg[2];
// else
// return X.DB;
}
static void UNL22211Power(void) {
Sync();
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetReadHandler(0x4100, 0x4100, UNL22211ReadLo);
SetWriteHandler(0x4100, 0x4103, UNL22211WriteLo);
SetWriteHandler(0x8000, 0xFFFF, UNL22211WriteHi);
}
static void StateRestore(int version) {
Sync();
}
void UNL22211_Init(CartInfo *info) {
is172 = 0;
is173 = 0;
info->Power = UNL22211Power;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}
void Mapper172_Init(CartInfo *info) {
is172 = 1;
is173 = 0;
info->Power = UNL22211Power;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}
void Mapper173_Init(CartInfo *info) {
is172 = 0;
is173 = 1;
info->Power = UNL22211Power;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,97 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2007 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* FDS Conversions
*
* Super Mario Bros 2j (Alt Full) is a BAD incomplete dump, should be mapper 43
*
* Both Voleyball and Zanac by Whirlind Manu shares the same PCB, but with
* some differences: Voleyball has 8K CHR ROM and 8K ROM at 6000K, Zanac
* have 8K CHR RAM and banked 16K ROM mapper at 6000 as two 8K banks.
*
* Super Mario Bros 2j (Alt Small) uses additionally IRQ timer to drive framerate
*
* PCB for this mapper is "09-034A"
*/
#include "mapinc.h"
static uint8 prg;
static uint32 IRQCount, IRQa;
static SFORMAT StateRegs[] =
{
{ &IRQCount, 4, "IRQC" },
{ &IRQa, 4, "IRQA" },
{ &prg, 1, "PRG" },
{ 0 }
};
static void Sync(void) {
setprg8r(1, 0x6000, prg);
setprg32(0x8000, 0);
setchr8(0);
}
static DECLFW(UNLSMB2JWrite1) {
prg = V & 1;
Sync();
}
static DECLFW(UNLSMB2JWrite2) {
IRQa = V & 1;
IRQCount = 0;
X6502_IRQEnd(FCEU_IQEXT);
}
static DECLFR(UNLSMB2JRead) {
return 0xFF;
}
static void UNLSMB2JPower(void) {
prg = 0;
Sync();
SetReadHandler(0x6000, 0xFFFF, CartBR);
SetReadHandler(0x4042, 0x4055, UNLSMB2JRead);
SetWriteHandler(0x4068, 0x4068, UNLSMB2JWrite2);
SetWriteHandler(0x4027, 0x4027, UNLSMB2JWrite1);
}
static void UNLSMB2JIRQHook(int a) {
if (IRQa)
{
if (IRQCount < 5750) // completely by guess
IRQCount += a;
else {
IRQa = 0;
X6502_IRQBegin(FCEU_IQEXT);
}
}
}
static void StateRestore(int version) {
Sync();
}
void UNLSMB2J_Init(CartInfo *info) {
info->Power = UNLSMB2JPower;
MapIRQHook = UNLSMB2JIRQHook;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,112 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2007 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 reg0, reg1, reg2;
static uint8 *WRAM = NULL;
static uint32 WRAMSIZE;
static SFORMAT StateRegs[] =
{
{ &reg0, 1, "REG0" },
{ &reg1, 1, "REG1" },
{ &reg2, 1, "REG2" },
{ 0 }
};
static void Sync(void) {
setchr8(0);
setprg8(0x8000, 0xc);
setprg8(0xe000, 0xf);
if (reg2 & 0x10) {
setprg8(0x6000, reg0);
setprg8(0xa000, 0xd);
setprg8(0xc000, 0xe);
} else {
setprg8r(0x10, 0x6000, 0);
setprg4(0xa000, (0xd << 1));
setprg2(0xb000, (0xd << 2) + 2);
setprg2r(0x10, 0xb800, 4);
setprg2r(0x10, 0xc000, 5);
setprg2r(0x10, 0xc800, 6);
setprg2r(0x10, 0xd000, 7);
setprg2(0xd800, (0xe << 2) + 3);
}
setmirror(reg1 ^ 1);
}
static DECLFW(M103RamWrite0) {
WRAM[A & 0x1FFF] = V;
}
static DECLFW(M103RamWrite1) {
WRAM[0x2000 + ((A - 0xB800) & 0x1FFF)] = V;
}
static DECLFW(M103Write0) {
reg0 = V & 0xf;
Sync();
}
static DECLFW(M103Write1) {
reg1 = (V >> 3) & 1;
Sync();
}
static DECLFW(M103Write2) {
reg2 = V;
Sync();
}
static void M103Power(void) {
reg0 = reg1 = 0; reg2 = 0;
Sync();
SetReadHandler(0x6000, 0x7FFF, CartBR);
SetWriteHandler(0x6000, 0x7FFF, M103RamWrite0);
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetWriteHandler(0xB800, 0xD7FF, M103RamWrite1);
SetWriteHandler(0x8000, 0x8FFF, M103Write0);
SetWriteHandler(0xE000, 0xEFFF, M103Write1);
SetWriteHandler(0xF000, 0xFFFF, M103Write2);
}
static void M103Close(void) {
if (WRAM)
FCEU_gfree(WRAM);
WRAM = NULL;
}
static void StateRestore(int version) {
Sync();
}
void Mapper103_Init(CartInfo *info) {
info->Power = M103Power;
info->Close = M103Close;
GameStateRestore = StateRestore;
WRAMSIZE = 16384;
WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE);
SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1);
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,108 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2007 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 reg[16], IRQa;
static uint32 IRQCount;
static uint8 *WRAM = NULL;
static uint32 WRAMSIZE;
static SFORMAT StateRegs[] =
{
{ &IRQa, 1, "IRQA" },
{ &IRQCount, 4, "IRQC" },
{ reg, 16, "REGS" },
{ 0 }
};
static void Sync(void) {
setchr1(0x0000, reg[0] & 0xfe);
setchr1(0x0400, reg[1] | 1);
setchr1(0x0800, reg[2] & 0xfe);
setchr1(0x0c00, reg[3] | 1);
setchr1(0x1000, reg[4]);
setchr1(0x1400, reg[5]);
setchr1(0x1800, reg[6]);
setchr1(0x1c00, reg[7]);
setprg8r(0x10, 0x6000, 0);
setprg8(0x8000, (reg[0x8] & 0xf) | 0x10);
setprg8(0xA000, (reg[0x9] & 0x1f));
setprg8(0xC000, (reg[0xa] & 0x1f));
setprg8(0xE000, (reg[0xb] & 0xf) | 0x10);
setmirror((reg[0xc] & 1) ^ 1);
}
static DECLFW(M106Write) {
A &= 0xF;
switch (A) {
case 0xD: IRQa = 0; IRQCount = 0; X6502_IRQEnd(FCEU_IQEXT); break;
case 0xE: IRQCount = (IRQCount & 0xFF00) | V; break;
case 0xF: IRQCount = (IRQCount & 0x00FF) | (V << 8); IRQa = 1; break;
default: reg[A] = V; Sync(); break;
}
}
static void M106Power(void) {
reg[8] = reg[9] = reg[0xa] = reg[0xb] = -1;
Sync();
SetReadHandler(0x6000, 0x7FFF, CartBR);
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetWriteHandler(0x6000, 0x7FFF, CartBW);
SetWriteHandler(0x8000, 0xFFFF, M106Write);
}
static void M106Reset(void) {
}
static void M106Close(void) {
if (WRAM)
FCEU_gfree(WRAM);
WRAM = NULL;
}
void M106CpuHook(int a) {
if (IRQa) {
IRQCount += a;
if (IRQCount > 0x10000) {
X6502_IRQBegin(FCEU_IQEXT);
IRQa = 0;
}
}
}
static void StateRestore(int version) {
Sync();
}
void Mapper106_Init(CartInfo *info) {
info->Reset = M106Reset;
info->Power = M106Power;
info->Close = M106Close;
MapIRQHook = M106CpuHook;
GameStateRestore = StateRestore;
WRAMSIZE = 8192;
WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE);
SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1);
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,58 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2007 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 reg;
static SFORMAT StateRegs[] =
{
{ &reg, 1, "REG" },
{ 0 }
};
static void Sync(void) {
setprg8(0x6000, reg);
setprg32(0x8000, ~0);
setchr8(0);
}
static DECLFW(M108Write) {
reg = V;
Sync();
}
static void M108Power(void) {
Sync();
SetReadHandler(0x6000, 0x7FFF, CartBR);
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetWriteHandler(0x8000, 0x8FFF, M108Write); // regular 108
SetWriteHandler(0xF000, 0xFFFF, M108Write); // simplified Kaiser BB Hack
}
static void StateRestore(int version) {
Sync();
}
void Mapper108_Init(CartInfo *info) {
info->Power = M108Power;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,90 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2005 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* NTDEC, ASDER games
*
*/
#include "mapinc.h"
static uint8 reg[8];
static uint8 mirror, cmd, bank;
static uint8 *WRAM = NULL;
static SFORMAT StateRegs[] =
{
{ &cmd, 1, "CMD" },
{ &mirror, 1, "MIRR" },
{ &bank, 1, "BANK" },
{ reg, 8, "REGS" },
{ 0 }
};
static void Sync(void) {
setmirror(mirror ^ 1);
setprg8(0x8000, reg[0]);
setprg8(0xA000, reg[1]);
setchr2(0x0000, (reg[2] >> 1));
setchr2(0x0800, (reg[3] >> 1));
setchr1(0x1000, ((bank & 0x10) << 4) | reg[4]);
setchr1(0x1400, ((bank & 0x20) << 3) | reg[5]);
setchr1(0x1800, ((bank & 0x40) << 2) | reg[6]);
setchr1(0x1C00, ((bank & 0x80) << 1) | reg[7]);
}
static DECLFW(M112Write) {
switch (A) {
case 0xe000: mirror = V & 1; Sync();; break;
case 0x8000: cmd = V & 7; break;
case 0xa000: reg[cmd] = V; Sync(); break;
case 0xc000: bank = V; Sync(); break;
}
}
static void M112Close(void) {
if (WRAM)
FCEU_gfree(WRAM);
WRAM = NULL;
}
static void M112Power(void) {
bank = 0;
setprg16(0xC000, ~0);
setprg8r(0x10, 0x6000, 0);
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetWriteHandler(0x8000, 0xFFFF, M112Write);
SetWriteHandler(0x4020, 0x5FFF, M112Write);
SetReadHandler(0x6000, 0x7FFF, CartBR);
SetWriteHandler(0x6000, 0x7FFF, CartBW);
FCEU_CheatAddRAM(8, 0x6000, WRAM);
}
static void StateRestore(int version) {
Sync();
}
void Mapper112_Init(CartInfo *info) {
info->Power = M112Power;
info->Close = M112Close;
GameStateRestore = StateRestore;
WRAM = (uint8*)FCEU_gmalloc(8192);
SetupCartPRGMapping(0x10, WRAM, 8192, 1);
AddExState(WRAM, 8192, 0, "WRAM");
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,324 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2011 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* SL12 Protected 3-in-1 mapper hardware (VRC2, MMC3, MMC1)
* the same as 603-5052 board (TODO: add reading registers, merge)
* SL1632 2-in-1 protected board, similar to SL12 (TODO: find difference)
*
* Known PCB:
*
* Garou Densetsu Special (G0904.PCB, Huang-1, GAL dip: W conf.)
* Kart Fighter (008, Huang-1, GAL dip: W conf.)
* Somari (008, C5052-13, GAL dip: P conf., GK2-P/GK2-V maskroms)
* Somari (008, Huang-1, GAL dip: W conf., GK1-P/GK1-V maskroms)
* AV Mei Shao Nv Zhan Shi (aka AV Pretty Girl Fighting) (SL-12 PCB, Hunag-1, GAL dip: unk conf. SL-11A/SL-11B maskroms)
* Samurai Spirits (Full version) (Huang-1, GAL dip: unk conf. GS-2A/GS-4A maskroms)
* Contra Fighter (603-5052 PCB, C5052-3, GAL dip: unk conf. SC603-A/SCB603-B maskroms)
*
*/
#include "mapinc.h"
static uint8 mode;
static uint8 vrc2_chr[8], vrc2_prg[2], vrc2_mirr;
static uint8 mmc3_regs[10], mmc3_ctrl, mmc3_mirr;
static uint8 IRQCount, IRQLatch, IRQa;
static uint8 IRQReload;
static uint8 mmc1_regs[4], mmc1_buffer, mmc1_shift;
static SFORMAT StateRegs[] =
{
{ &mode, 1, "MODE" },
{ vrc2_chr, 8, "VRCC" },
{ vrc2_prg, 2, "VRCP" },
{ &vrc2_mirr, 1, "VRCM" },
{ mmc3_regs, 10, "M3RG" },
{ &mmc3_ctrl, 1, "M3CT" },
{ &mmc3_mirr, 1, "M3MR" },
{ &IRQReload, 1, "IRQR" },
{ &IRQCount, 1, "IRQC" },
{ &IRQLatch, 1, "IRQL" },
{ &IRQa, 1, "IRQA" },
{ mmc1_regs, 4, "M1RG" },
{ &mmc1_buffer, 1, "M1BF" },
{ &mmc1_shift, 1, "M1MR" },
{ 0 }
};
static void SyncPRG(void) {
switch (mode & 3) {
case 0:
setprg8(0x8000, vrc2_prg[0]);
setprg8(0xA000, vrc2_prg[1]);
setprg8(0xC000, ~1);
setprg8(0xE000, ~0);
break;
case 1:
{
uint32 swap = (mmc3_ctrl >> 5) & 2;
setprg8(0x8000, mmc3_regs[6 + swap]);
setprg8(0xA000, mmc3_regs[7]);
setprg8(0xC000, mmc3_regs[6 + (swap ^ 2)]);
setprg8(0xE000, mmc3_regs[9]);
break;
}
case 2:
case 3:
{
uint8 bank = mmc1_regs[3] & 0xF;
if (mmc1_regs[0] & 8) {
if (mmc1_regs[0] & 4) {
setprg16(0x8000, bank);
setprg16(0xC000, 0x0F);
} else {
setprg16(0x8000, 0);
setprg16(0xC000, bank);
}
} else
setprg32(0x8000, bank >> 1);
break;
}
}
}
static void SyncCHR(void) {
uint32 base = (mode & 4) << 6;
switch (mode & 3) {
case 0:
setchr1(0x0000, base | vrc2_chr[0]);
setchr1(0x0400, base | vrc2_chr[1]);
setchr1(0x0800, base | vrc2_chr[2]);
setchr1(0x0c00, base | vrc2_chr[3]);
setchr1(0x1000, base | vrc2_chr[4]);
setchr1(0x1400, base | vrc2_chr[5]);
setchr1(0x1800, base | vrc2_chr[6]);
setchr1(0x1c00, base | vrc2_chr[7]);
break;
case 1: {
uint32 swap = (mmc3_ctrl & 0x80) << 5;
setchr1(0x0000 ^ swap, base | ((mmc3_regs[0]) & 0xFE));
setchr1(0x0400 ^ swap, base | (mmc3_regs[0] | 1));
setchr1(0x0800 ^ swap, base | ((mmc3_regs[1]) & 0xFE));
setchr1(0x0c00 ^ swap, base | (mmc3_regs[1] | 1));
setchr1(0x1000 ^ swap, base | mmc3_regs[2]);
setchr1(0x1400 ^ swap, base | mmc3_regs[3]);
setchr1(0x1800 ^ swap, base | mmc3_regs[4]);
setchr1(0x1c00 ^ swap, base | mmc3_regs[5]);
break;
}
case 2:
case 3:
if (mmc1_regs[0] & 0x10) {
setchr4(0x0000, mmc1_regs[1]);
setchr4(0x1000, mmc1_regs[2]);
} else
setchr8(mmc1_regs[1] >> 1);
break;
}
}
static void SyncMIR(void) {
switch (mode & 3) {
case 0: {
setmirror((vrc2_mirr & 1) ^ 1);
break;
}
case 1: {
setmirror((mmc3_mirr & 1) ^ 1);
break;
}
case 2:
case 3: {
switch (mmc1_regs[0] & 3) {
case 0: setmirror(MI_0); break;
case 1: setmirror(MI_1); break;
case 2: setmirror(MI_V); break;
case 3: setmirror(MI_H); break;
}
break;
}
}
}
static void Sync(void) {
SyncPRG();
SyncCHR();
SyncMIR();
}
static DECLFW(UNLSL12ModeWrite) {
// FCEU_printf("%04X:%02X\n",A,V);
if ((A & 0x4100) == 0x4100) {
mode = V;
if (A & 1) { // hacky hacky, there are two configuration modes on SOMARI HUANG-1 PCBs
// Solder pads with P1/P2 shorted called SOMARI P,
// Solder pads with W1/W2 shorted called SOMARI W
// Both identical 3-in-1 but W wanted MMC1 registers
// to be reset when switch to MMC1 mode P one - doesn't
// There is issue with W version of Somari at starting copyrights
mmc1_regs[0] = 0xc;
mmc1_regs[3] = 0;
mmc1_buffer = 0;
mmc1_shift = 0;
}
Sync();
}
}
static DECLFW(UNLSL12Write) {
// FCEU_printf("%04X:%02X\n",A,V);
switch (mode & 3) {
case 0: {
if ((A >= 0xB000) && (A <= 0xE003)) {
int32 ind = ((((A & 2) | (A >> 10)) >> 1) + 2) & 7;
int32 sar = ((A & 1) << 2);
vrc2_chr[ind] = (vrc2_chr[ind] & (0xF0 >> sar)) | ((V & 0x0F) << sar);
SyncCHR();
} else
switch (A & 0xF000) {
case 0x8000: vrc2_prg[0] = V; SyncPRG(); break;
case 0xA000: vrc2_prg[1] = V; SyncPRG(); break;
case 0x9000: vrc2_mirr = V; SyncMIR(); break;
}
break;
}
case 1: {
switch (A & 0xE001) {
case 0x8000: {
uint8 old_ctrl = mmc3_ctrl;
mmc3_ctrl = V;
if ((old_ctrl & 0x40) != (mmc3_ctrl & 0x40))
SyncPRG();
if ((old_ctrl & 0x80) != (mmc3_ctrl & 0x80))
SyncCHR();
break;
}
case 0x8001:
mmc3_regs[mmc3_ctrl & 7] = V;
if ((mmc3_ctrl & 7) < 6)
SyncCHR();
else
SyncPRG();
break;
case 0xA000:
mmc3_mirr = V;
SyncMIR();
break;
case 0xC000:
IRQLatch = V;
break;
case 0xC001:
IRQReload = 1;
break;
case 0xE000:
X6502_IRQEnd(FCEU_IQEXT);
IRQa = 0;
break;
case 0xE001:
IRQa = 1;
break;
}
break;
}
case 2:
case 3: {
if (V & 0x80) {
mmc1_regs[0] |= 0xc;
mmc1_buffer = mmc1_shift = 0;
SyncPRG();
} else {
uint8 n = (A >> 13) - 4;
mmc1_buffer |= (V & 1) << (mmc1_shift++);
if (mmc1_shift == 5) {
mmc1_regs[n] = mmc1_buffer;
mmc1_buffer = mmc1_shift = 0;
switch (n) {
case 0: SyncMIR();
case 2: SyncCHR();
case 3:
case 1: SyncPRG();
}
}
}
break;
}
}
}
static void UNLSL12HBIRQ(void) {
if ((mode & 3) == 1) {
int32 count = IRQCount;
if (!count || IRQReload) {
IRQCount = IRQLatch;
IRQReload = 0;
} else
IRQCount--;
if (!IRQCount) {
if (IRQa)
X6502_IRQBegin(FCEU_IQEXT);
}
}
}
static void StateRestore(int version) {
Sync();
}
static void UNLSL12Power(void) {
mode = 0;
vrc2_chr[0] = ~0;
vrc2_chr[1] = ~0;
vrc2_chr[2] = ~0;
vrc2_chr[3] = ~0; // W conf. of Somari wanted CHR3 has to be set to BB bank (or similar), but doesn't do that directly
vrc2_chr[4] = 4;
vrc2_chr[5] = 5;
vrc2_chr[6] = 6;
vrc2_chr[7] = 7;
vrc2_prg[0] = 0;
vrc2_prg[1] = 1;
vrc2_mirr = 0;
mmc3_regs[0] = 0;
mmc3_regs[1] = 2;
mmc3_regs[2] = 4;
mmc3_regs[3] = 5;
mmc3_regs[4] = 6;
mmc3_regs[5] = 7;
mmc3_regs[6] = ~3;
mmc3_regs[7] = ~2;
mmc3_regs[8] = ~1;
mmc3_regs[9] = ~0;
mmc3_ctrl = mmc3_mirr = IRQCount = IRQLatch = IRQa = 0;
mmc1_regs[0] = 0xc;
mmc1_regs[1] = 0;
mmc1_regs[2] = 0;
mmc1_regs[3] = 0;
mmc1_buffer = 0;
mmc1_shift = 0;
Sync();
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetWriteHandler(0x4100, 0x7FFF, UNLSL12ModeWrite);
SetWriteHandler(0x8000, 0xFFFF, UNLSL12Write);
}
void UNLSL12_Init(CartInfo *info) {
info->Power = UNLSL12Power;
GameHBIRQHook = UNLSL12HBIRQ;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,91 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 prgreg[4], chrreg[8], mirror;
static uint8 IRQa, IRQCount, IRQLatch;
static SFORMAT StateRegs[] =
{
{ &IRQa, 1, "IRQA" },
{ &IRQCount, 1, "IRQC" },
{ &IRQLatch, 1, "IRQL" },
{ prgreg, 4, "PREG" },
{ chrreg, 8, "CREG" },
{ &mirror, 1, "MREG" },
{ 0 }
};
static void Sync(void) {
setprg8(0x8000, prgreg[0]);
setprg8(0xa000, prgreg[1]);
setprg8(0xc000, prgreg[2]);
setprg8(0xe000, prgreg[3]);
int i;
for (i = 0; i < 8; i++)
setchr1(i << 10, chrreg[i]);
setmirror(mirror ^ 1);
}
static DECLFW(M117Write) {
if (A < 0x8004) {
prgreg[A & 3] = V;
Sync();
} else if ((A >= 0xA000) && (A <= 0xA007)) {
chrreg[A & 7] = V;
Sync();
} else switch (A) {
case 0xc001: IRQLatch = V; break;
case 0xc003: IRQCount = IRQLatch; IRQa |= 2; break;
case 0xe000: IRQa &= ~1; IRQa |= V & 1; X6502_IRQEnd(FCEU_IQEXT); break;
case 0xc002: X6502_IRQEnd(FCEU_IQEXT); break;
case 0xd000: mirror = V & 1;
}
}
static void M117Power(void) {
prgreg[0] = ~3; prgreg[1] = ~2; prgreg[2] = ~1; prgreg[3] = ~0;
Sync();
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetWriteHandler(0x8000, 0xFFFF, M117Write);
}
static void M117IRQHook(void) {
if (IRQa == 3 && IRQCount) {
IRQCount--;
if (!IRQCount) {
IRQa &= 1;
X6502_IRQBegin(FCEU_IQEXT);
}
}
}
static void StateRestore(int version) {
Sync();
}
void Mapper117_Init(CartInfo *info) {
info->Power = M117Power;
GameHBIRQHook = M117IRQHook;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,59 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2007 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 reg;
static SFORMAT StateRegs[] =
{
{ &reg, 1, "REG" },
{ 0 }
};
static void Sync(void) {
setprg8(0x6000, reg);
setprg32(0x8000, 2);
setchr8(0);
}
static DECLFW(M120Write) {
if (A == 0x41FF) {
reg = V & 7;
Sync();
}
}
static void M120Power(void) {
reg = 0;
Sync();
SetReadHandler(0x6000, 0xFFFF, CartBR);
SetWriteHandler(0x4100, 0x5FFF, M120Write);
}
static void StateRestore(int version) {
Sync();
}
void Mapper120_Init(CartInfo *info) {
info->Power = M120Power;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,129 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2007-2008 Mad Dumper, CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Panda prince pirate.
* MK4, MK6, A9711/A9713 board
* 6035052 seems to be the same too, but with prot array in reverse
* A9746 seems to be the same too, check
* 187 seems to be the same too, check (A98402 board)
*
*/
#include "mapinc.h"
#include "mmc3.h"
static void Sync() {
switch (EXPREGS[5] & 0x3F) {
case 0x20: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break;
case 0x29: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break;
case 0x26: EXPREGS[7] = 0; EXPREGS[0] = EXPREGS[6]; break;
case 0x2B: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break;
case 0x2C: EXPREGS[7] = 1; if (EXPREGS[6]) EXPREGS[0] = EXPREGS[6]; break;
case 0x3C:
case 0x3F: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break;
case 0x28: EXPREGS[7] = 0; EXPREGS[1] = EXPREGS[6]; break;
case 0x2A: EXPREGS[7] = 0; EXPREGS[2] = EXPREGS[6]; break;
case 0x2F: break;
default: EXPREGS[5] = 0; break;
}
}
static void M121CW(uint32 A, uint8 V) {
if (PRGsize[0] == CHRsize[0]) { // A9713 multigame extension hack!
setchr1(A, V | ((EXPREGS[3] & 0x80) << 1));
} else {
if ((A & 0x1000) == ((MMC3_cmd & 0x80) << 5))
setchr1(A, V | 0x100);
else
setchr1(A, V);
}
}
static void M121PW(uint32 A, uint8 V) {
if (EXPREGS[5] & 0x3F) {
// FCEU_printf("prot banks: %02x %02x %02x %02x\n",V,EXPREGS[2],EXPREGS[1],EXPREGS[0]);
setprg8(A, (V & 0x1F) | ((EXPREGS[3] & 0x80) >> 2));
setprg8(0xE000, (EXPREGS[0]) | ((EXPREGS[3] & 0x80) >> 2));
setprg8(0xC000, (EXPREGS[1]) | ((EXPREGS[3] & 0x80) >> 2));
setprg8(0xA000, (EXPREGS[2]) | ((EXPREGS[3] & 0x80) >> 2));
} else {
// FCEU_printf("gen banks: %04x %02x\n",A,V);
setprg8(A, (V & 0x1F) | ((EXPREGS[3] & 0x80) >> 2));
}
}
static DECLFW(M121Write) {
// FCEU_printf("write: %04x:%04x\n",A&0xE003,V);
switch (A & 0xE003) {
case 0x8000:
// EXPREGS[5] = 0;
// FCEU_printf("gen: %02x\n",V);
MMC3_CMDWrite(A, V);
FixMMC3PRG(MMC3_cmd);
break;
case 0x8001:
EXPREGS[6] = ((V & 1) << 5) | ((V & 2) << 3) | ((V & 4) << 1) | ((V & 8) >> 1) | ((V & 0x10) >> 3) | ((V & 0x20) >> 5);
// FCEU_printf("bank: %02x (%02x)\n",V,EXPREGS[6]);
if (!EXPREGS[7]) Sync();
MMC3_CMDWrite(A, V);
FixMMC3PRG(MMC3_cmd);
break;
case 0x8003:
EXPREGS[5] = V;
// EXPREGS[7] = 0;
// FCEU_printf("prot: %02x\n",EXPREGS[5]);
Sync();
MMC3_CMDWrite(0x8000, V);
FixMMC3PRG(MMC3_cmd);
break;
}
}
static uint8 prot_array[16] = { 0x83, 0x83, 0x42, 0x00 };
static DECLFW(M121LoWrite) {
EXPREGS[4] = prot_array[V & 3]; // 0x100 bit in address seems to be switch arrays 0, 2, 2, 3 (Contra Fighter)
if ((A & 0x5180) == 0x5180) { // A9713 multigame extension
EXPREGS[3] = V;
FixMMC3PRG(MMC3_cmd);
FixMMC3CHR(MMC3_cmd);
}
// FCEU_printf("write: %04x:%04x\n",A,V);
}
static DECLFR(M121Read) {
// FCEU_printf("read: %04x->\n",A,EXPREGS[0]);
return EXPREGS[4];
}
static void M121Power(void) {
EXPREGS[3] = 0x80;
EXPREGS[5] = 0;
GenMMC3Power();
SetReadHandler(0x5000, 0x5FFF, M121Read);
SetWriteHandler(0x5000, 0x5FFF, M121LoWrite);
SetWriteHandler(0x8000, 0x9FFF, M121Write);
}
void Mapper121_Init(CartInfo *info) {
GenMMC3_Init(info, 128, 256, 8, 0);
pwrap = M121PW;
cwrap = M121CW;
info->Power = M121Power;
AddExState(EXPREGS, 8, 0, "EXPR");
}

View File

@ -0,0 +1,73 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2009 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* 7-in-1 Darkwing Duck, Snake, MagicBlock (PCB marked as "12 in 1")
* 12-in-1 1991 New Star Co. Ltd.
*
*/
#include "mapinc.h"
static uint8 prgchr[2], ctrl;
static SFORMAT StateRegs[] =
{
{ prgchr, 2, "REGS" },
{ &ctrl, 1, "CTRL" },
{ 0 }
};
static void Sync(void) {
uint8 bank = (ctrl & 3) << 3;
setchr4(0x0000, (prgchr[0] >> 3) | (bank << 2));
setchr4(0x1000, (prgchr[1] >> 3) | (bank << 2));
if (ctrl & 8) {
setprg16(0x8000, bank | (prgchr[0] & 6) | 0); // actually, both 0 and 1 registers used, but they will switch each PA12 transition
setprg16(0xc000, bank | (prgchr[0] & 6) | 1); // if bits are different for both registers, so they must be programmed strongly the same!
} else {
setprg16(0x8000, bank | (prgchr[0] & 7));
setprg16(0xc000, bank | 7 );
}
setmirror(((ctrl & 4) >> 2) ^ 1);
}
static DECLFW(BMC12IN1Write) {
switch (A & 0xE000) {
case 0xA000: prgchr[0] = V; Sync(); break;
case 0xC000: prgchr[1] = V; Sync(); break;
case 0xE000: ctrl = V & 0x0F; Sync(); break;
}
}
static void BMC12IN1Power(void) {
prgchr[0] = prgchr[1] = ctrl = 0;
Sync();
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetWriteHandler(0x8000, 0xFFFF, BMC12IN1Write);
}
static void StateRestore(int version) {
Sync();
}
void BMC12IN1_Init(CartInfo *info) {
info->Power = BMC12IN1Power;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,117 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2006 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "mapinc.h"
static uint16 latchea;
static uint8 latched;
static uint8 *WRAM = NULL;
static uint32 WRAMSIZE;
static SFORMAT StateRegs[] =
{
{ &latchea, 2, "AREG" },
{ &latched, 1, "DREG" },
{ 0 }
};
static void Sync(void) {
int i;
setmirror(((latched >> 6) & 1) ^ 1);
switch (latchea & 3) {
case 0:
for (i = 0; i < 4; i++)
setprg8(0x8000 + (i << 13), ((latched & 0x3F) << 1) + i);
break;
case 2:
for (i = 0; i < 4; i++)
setprg8(0x8000 + (i << 13), ((latched & 0x3F) << 1) + (latched >> 7));
break;
case 1:
case 3:
for (i = 0; i < 4; i++) {
unsigned int b;
b = latched & 0x3F;
if (i >= 2 && !(latchea & 0x2))
b = b | 0x07;
setprg8(0x8000 + (i << 13), (i & 1) + (b << 1));
}
break;
}
setchr8(0);
}
static DECLFW(M15Write) {
latchea = A;
latched = V;
// cah4e3 02.10.19 once again, there may be either two similar mapper 15 exist. the one for 110in1 or 168in1 carts with complex multi game features.
// and another implified version for subor/waixing chinese originals and hacks with no different modes, working only in mode 0 and which does not
// expect there is any CHR write protection. protecting CHR writes only for mode 3 fixes the problem, all roms may be run on the same source again.
if((latchea & 3) == 3)
SetupCartCHRMapping(0, CHRptr[0], 0x2000, 0);
else
SetupCartCHRMapping(0, CHRptr[0], 0x2000, 1);
Sync();
}
static void StateRestore(int version) {
Sync();
}
static void M15Power(void) {
latchea = 0x8000;
latched = 0;
setprg8r(0x10, 0x6000, 0);
SetReadHandler(0x6000, 0x7FFF, CartBR);
SetWriteHandler(0x6000, 0x7FFF, CartBW);
SetWriteHandler(0x8000, 0xFFFF, M15Write);
SetReadHandler(0x8000, 0xFFFF, CartBR);
FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM);
Sync();
}
static void M15Reset(void) {
latchea = 0x8000;
latched = 0;
Sync();
}
static void M15Close(void) {
if (WRAM)
FCEU_gfree(WRAM);
WRAM = NULL;
}
void Mapper15_Init(CartInfo *info) {
info->Power = M15Power;
info->Reset = M15Reset;
info->Close = M15Close;
GameStateRestore = StateRestore;
WRAMSIZE = 8192;
WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE);
SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1);
if (info->battery) {
info->SaveGame[0] = WRAM;
info->SaveGameLen[0] = WRAMSIZE;
}
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,59 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2012 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 regs[8];
static SFORMAT StateRegs[] =
{
{ regs, 8, "REGS" },
{ 0 }
};
static void Sync(void) {
setprg8(0x8000, regs[0]);
setprg8(0xA000, regs[2]);
setprg8(0xC000, regs[4]);
setprg8(0xE000, ~0);
setchr4(0x0000, regs[6]);
setchr4(0x1000, regs[7]);
}
static DECLFW(M151Write) {
regs[(A >> 12) & 7] = V;
Sync();
}
static void M151Power(void) {
Sync();
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetWriteHandler(0x8000, 0xFFFF, M151Write);
}
static void StateRestore(int version) {
Sync();
}
void Mapper151_Init(CartInfo *info) {
info->Power = M151Power;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,114 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2009 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 chrlo[8], chrhi[8], prg, mirr, mirrisused = 0;
static uint8 *WRAM = NULL;
static uint32 WRAMSIZE;
static SFORMAT StateRegs[] =
{
{ &prg, 1, "PREG" },
{ chrlo, 8, "CRGL" },
{ chrhi, 8, "CRGH" },
{ &mirr, 1, "MIRR" },
{ 0 }
};
static void Sync(void) {
uint32 i;
for (i = 0; i < 8; i++)
setchr1(i << 10, chrlo[i] | (chrhi[i] << 8));
setprg8r(0x10, 0x6000, 0);
setprg16(0x8000, prg);
setprg16(0xC000, ~0);
if (mirrisused)
setmirror(mirr ^ 1);
else
setmirror(MI_0);
}
static DECLFW(M156Write) {
switch (A) {
case 0xC000:
case 0xC001:
case 0xC002:
case 0xC003: chrlo[A & 3] = V; Sync(); break;
case 0xC004:
case 0xC005:
case 0xC006:
case 0xC007: chrhi[A & 3] = V; Sync(); break;
case 0xC008:
case 0xC009:
case 0xC00A:
case 0xC00B: chrlo[4 + (A & 3)] = V; Sync(); break;
case 0xC00C:
case 0xC00D:
case 0xC00E:
case 0xC00F: chrhi[4 + (A & 3)] = V; Sync(); break;
case 0xC010: prg = V; Sync(); break;
case 0xC014: mirr = V; mirrisused = 1; Sync(); break;
}
}
static void M156Reset(void) {
uint32 i;
for (i = 0; i < 8; i++) {
chrlo[i] = 0;
chrhi[i] = 0;
}
prg = 0;
mirr = 0;
mirrisused = 0;
}
static void M156Power(void) {
M156Reset();
Sync();
SetReadHandler(0x6000, 0xFFFF, CartBR);
SetWriteHandler(0x6000, 0x7FFF, CartBW);
SetWriteHandler(0xC000, 0xCFFF, M156Write);
FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM);
}
static void M156Close(void) {
if (WRAM)
FCEU_gfree(WRAM);
WRAM = NULL;
}
static void StateRestore(int version) {
Sync();
}
void Mapper156_Init(CartInfo *info) {
info->Reset = M156Reset;
info->Power = M156Power;
info->Close = M156Close;
WRAMSIZE = 8192;
WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE);
SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1);
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,71 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2015 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* "Blood Of Jurassic" protected MMC3 based board (GD-98 Cart ID, 158B PCB ID)
*
*/
#include "mapinc.h"
#include "mmc3.h"
static uint8 lut[8] = { 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x0F, 0x00 };
static void UNL158BPW(uint32 A, uint8 V) {
if (EXPREGS[0] & 0x80) {
uint32 bank = EXPREGS[0] & 7;
if(EXPREGS[0] & 0x20) { // 32Kb mode
setprg32(0x8000, bank >> 1);
} else { // 16Kb mode
setprg16(0x8000, bank);
setprg16(0xC000, bank);
}
} else {
setprg8(A, V & 0xF);
}
}
static DECLFW(UNL158BProtWrite) {
EXPREGS[A & 7] = V;
switch(A & 7) {
case 0:
FixMMC3PRG(MMC3_cmd);
break;
case 7:
FCEU_printf("UNK PROT WRITE\n");
break;
}
}
static DECLFR(UNL158BProtRead) {
return X.DB | lut[A & 7];
}
static void UNL158BPower(void) {
GenMMC3Power();
SetWriteHandler(0x5000, 0x5FFF, UNL158BProtWrite);
SetReadHandler(0x5000, 0x5FFF, UNL158BProtRead);
}
void UNL158B_Init(CartInfo *info) {
GenMMC3_Init(info, 128, 128, 0, 0);
pwrap = UNL158BPW;
info->Power = UNL158BPower;
AddExState(EXPREGS, 8, 0, "EXPR");
}

View File

@ -0,0 +1,232 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2002 Xodnizel 2006 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* It seems that 162/163/164 mappers are the same mapper with just different
* mapper modes enabled or disabled in software or hardware, need more nanjing
* carts
*/
#include "mapinc.h"
static uint8 laststrobe, trigger;
static uint8 reg[8];
static uint8 *WRAM = NULL;
static uint32 WRAMSIZE;
static writefunc pcmwrite;
static void (*WSync)(void);
static SFORMAT StateRegs[] =
{
{ &laststrobe, 1, "STB" },
{ &trigger, 1, "TRG" },
{ reg, 8, "REGS" },
{ 0 }
};
static void Sync(void) {
setprg8r(0x10, 0x6000, 0);
setprg32(0x8000, (reg[0] << 4) | (reg[1] & 0xF));
setchr8(0);
}
static void StateRestore(int version) {
WSync();
}
static DECLFR(ReadLow) {
switch (A & 0x7700) {
case 0x5100: return reg[2] | reg[0] | reg[1] | (reg[3] ^ 0xff); break;
case 0x5500:
if (trigger)
return reg[2] | reg[1]; // Lei Dian Huang Bi Ka Qiu Chuan Shuo (NJ046) may broke other games
else
return 0;
}
return 4;
}
static void M163HB(void) {
if (reg[1] & 0x80) {
if (scanline == 239) {
setchr4(0x0000, 0);
setchr4(0x1000, 0);
} else if (scanline == 127) {
setchr4(0x0000, 1);
setchr4(0x1000, 1);
}
/*
if(scanline>=127) // Hu Lu Jin Gang (NJ039) (Ch) [!] don't like it
{
setchr4(0x0000,1);
setchr4(0x1000,1);
}
else
{
setchr4(0x0000,0);
setchr4(0x1000,0);
}
*/
}
}
static DECLFW(Write) {
switch (A & 0x7300) {
case 0x5100: reg[0] = V; WSync(); break;
case 0x5000: reg[1] = V; WSync(); break;
case 0x5300: reg[2] = V; break;
case 0x5200: reg[3] = V; WSync(); break;
}
}
static void Power(void) {
memset(reg, 0, 8);
reg[1] = 0xFF;
SetWriteHandler(0x5000, 0x5FFF, Write);
SetReadHandler(0x6000, 0xFFFF, CartBR);
SetWriteHandler(0x6000, 0x7FFF, CartBW);
FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM);
WSync();
}
static void Close(void) {
if (WRAM)
FCEU_gfree(WRAM);
WRAM = NULL;
}
void Mapper164_Init(CartInfo *info) {
info->Power = Power;
info->Close = Close;
WSync = Sync;
WRAMSIZE = 8192;
WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE);
SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1);
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
if (info->battery) {
info->SaveGame[0] = WRAM;
info->SaveGameLen[0] = WRAMSIZE;
}
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}
static DECLFW(Write2) {
if (A == 0x5101) {
if (laststrobe && !V) {
trigger ^= 1;
}
laststrobe = V;
} else if (A == 0x5100 && V == 6) //damn thoose protected games
setprg32(0x8000, 3);
else
switch (A & 0x7300) {
case 0x5200: reg[0] = V; WSync(); break;
case 0x5000: reg[1] = V; WSync(); if (!(reg[1] & 0x80) && (scanline < 128)) setchr8(0); /* setchr8(0); */ break;
case 0x5300: reg[2] = V; break;
case 0x5100: reg[3] = V; WSync(); break;
}
}
static void Power2(void) {
memset(reg, 0, 8);
laststrobe = 1;
pcmwrite = GetWriteHandler(0x4011);
SetReadHandler(0x5000, 0x5FFF, ReadLow);
SetWriteHandler(0x5000, 0x5FFF, Write2);
SetReadHandler(0x6000, 0xFFFF, CartBR);
SetWriteHandler(0x6000, 0x7FFF, CartBW);
FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM);
WSync();
}
void Mapper163_Init(CartInfo *info) {
info->Power = Power2;
info->Close = Close;
WSync = Sync;
GameHBIRQHook = M163HB;
WRAMSIZE = 8192;
WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE);
SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1);
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
if (info->battery) {
info->SaveGame[0] = WRAM;
info->SaveGameLen[0] = WRAMSIZE;
}
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}
static void Sync3(void) {
setchr8(0);
setprg8r(0x10, 0x6000, 0);
switch (reg[3] & 7) {
case 0:
case 2: setprg32(0x8000, (reg[0] & 0xc) | (reg[1] & 2) | ((reg[2] & 0xf) << 4)); break;
case 1:
case 3: setprg32(0x8000, (reg[0] & 0xc) | (reg[2] & 0xf) << 4); break;
case 4:
case 6: setprg32(0x8000, (reg[0] & 0xe) | ((reg[1] >> 1) & 1) | ((reg[2] & 0xf) << 4)); break;
case 5:
case 7: setprg32(0x8000, (reg[0] & 0xf) | ((reg[2] & 0xf) << 4)); break;
}
}
static DECLFW(Write3) {
// FCEU_printf("bs %04x %02x\n",A,V);
reg[(A >> 8) & 3] = V;
WSync();
}
static void Power3(void) {
reg[0] = 3;
reg[1] = 0;
reg[2] = 0;
reg[3] = 7;
SetWriteHandler(0x5000, 0x5FFF, Write3);
SetReadHandler(0x6000, 0xFFFF, CartBR);
SetWriteHandler(0x6000, 0x7FFF, CartBW);
FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM);
WSync();
}
void UNLFS304_Init(CartInfo *info) {
info->Power = Power3;
info->Close = Close;
WSync = Sync3;
WRAMSIZE = 8192;
WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE);
SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1);
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
if (info->battery) {
info->SaveGame[0] = WRAM;
info->SaveGameLen[0] = WRAMSIZE;
}
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,78 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2009 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 reg;
static uint8 *CHRRAM = NULL;
static uint32 CHRRAMSIZE;
static SFORMAT StateRegs[] =
{
{ &reg, 1, "REGS" },
{ 0 }
};
static void Sync(void) {
setchr4r(0x10, 0x0000, 0);
setchr4r(0x10, 0x1000, reg & 0x0f);
setprg16(0x8000, reg >> 6);
setprg16(0xc000, ~0);
}
static DECLFW(M168Write) {
reg = V;
Sync();
}
static DECLFW(M168Dummy) {
}
static void M168Power(void) {
reg = 0;
Sync();
SetWriteHandler(0x4020, 0x7fff, M168Dummy);
SetWriteHandler(0xB000, 0xB000, M168Write);
SetWriteHandler(0xF000, 0xF000, M168Dummy);
SetWriteHandler(0xF080, 0xF080, M168Dummy);
SetReadHandler(0x8000, 0xFFFF, CartBR);
}
static void M168Close(void) {
if (CHRRAM)
FCEU_gfree(CHRRAM);
CHRRAM = NULL;
}
static void StateRestore(int version) {
Sync();
}
void Mapper168_Init(CartInfo *info) {
info->Power = M168Power;
info->Close = M168Close;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
CHRRAMSIZE = 8192 * 8;
CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE);
SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1);
AddExState(CHRRAM, CHRRAMSIZE, 0, "CRAM");
}

View File

@ -0,0 +1,62 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2011 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 reg;
static SFORMAT StateRegs[] =
{
{ &reg, 1, "REGS" },
{ 0 }
};
static void Sync(void) {
setprg16(0x8000, 0);
setprg16(0xc000, ~0);
setchr8(0);
}
static DECLFW(M170ProtW) {
reg = V << 1 & 0x80;
}
static DECLFR(M170ProtR) {
return reg | (X.DB & 0x7F);
}
static void M170Power(void) {
Sync();
SetWriteHandler(0x6502, 0x6502, M170ProtW);
SetWriteHandler(0x7000, 0x7000, M170ProtW);
SetReadHandler(0x7001, 0x7001, M170ProtR);
SetReadHandler(0x7777, 0x7777, M170ProtR);
SetReadHandler(0x8000, 0xFFFF, CartBR);
}
static void StateRestore(int version) {
Sync();
}
void Mapper170_Init(CartInfo *info) {
info->Power = M170Power;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,79 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2007 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 reg, delay, mirr;
static SFORMAT StateRegs[] =
{
{ &reg, 1, "REG" },
{ &mirr, 1, "MIRR" },
{ 0 }
};
static void Sync(void) {
setchr8(reg);
if (!delay) {
setprg16(0x8000, reg);
setprg8(0xC000, reg << 1);
}
setprg8(0xE000, (reg << 1) + 1);
setmirror(((mirr & 4) >> 2) ^ 1);
}
static DECLFW(M175Write1) {
mirr = V;
delay = 1;
Sync();
}
static DECLFW(M175Write2) {
reg = V & 0x0F;
delay = 1;
Sync();
}
static DECLFR(M175Read) {
if (A == 0xFFFC) {
delay = 0;
Sync();
}
return CartBR(A);
}
static void M175Power(void) {
reg = mirr = delay = 0;
SetReadHandler(0x8000, 0xFFFF, M175Read);
SetWriteHandler(0x8000, 0x8000, M175Write1);
SetWriteHandler(0xA000, 0xA000, M175Write2);
Sync();
}
static void StateRestore(int version) {
Sync();
}
void Mapper175_Init(CartInfo *info) {
info->Power = M175Power;
GameStateRestore = StateRestore;
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,159 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2007 CaH4e3
* Copyright (C) 2012 FCEUX team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
extern uint32 ROM_size;
static uint8 prg[4], chr, sbw, we_sram;
static uint8 *WRAM = NULL;
static uint32 WRAMSIZE;
static SFORMAT StateRegs[]=
{
{prg, 4, "PRG"},
{&chr, 1, "CHR"},
{&sbw, 1, "SBW"},
{0}
};
static void Sync(void)
{
setprg8r(0x10,0x6000,0);
setprg8(0x8000,prg[0]);
setprg8(0xA000,prg[1]);
setprg8(0xC000,prg[2]);
setprg8(0xE000,prg[3]);
setchr8(chr);
}
static DECLFW(M176Write_5001)
{
printf("%04X = $%02X\n",A,V);
if(sbw)
{
prg[0] = V*4;
prg[1] = V*4+1;
prg[2] = V*4+2;
prg[3] = V*4+3;
}
Sync();
}
static DECLFW(M176Write_5010)
{
printf("%04X = $%02X\n",A,V);
if(V == 0x24) sbw = 1;
Sync();
}
static DECLFW(M176Write_5011)
{
printf("%04X = $%02X\n",A,V);
V >>= 1;
if(sbw)
{
prg[0] = V*4;
prg[1] = V*4+1;
prg[2] = V*4+2;
prg[3] = V*4+3;
}
Sync();
}
static DECLFW(M176Write_5FF1)
{
printf("%04X = $%02X\n",A,V);
V >>= 1;
prg[0] = V*4;
prg[1] = V*4+1;
prg[2] = V*4+2;
prg[3] = V*4+3;
Sync();
}
static DECLFW(M176Write_5FF2)
{
printf("%04X = $%02X\n",A,V);
chr = V;
Sync();
}
static DECLFW(M176Write_A001)
{
we_sram = V & 0x03;
}
static DECLFW(M176Write_WriteSRAM)
{
// if(we_sram)
CartBW(A,V);
}
static void M176Power(void)
{
SetReadHandler(0x6000,0x7fff,CartBR);
SetWriteHandler(0x6000,0x7fff,M176Write_WriteSRAM);
SetReadHandler(0x8000,0xFFFF,CartBR);
SetWriteHandler(0xA001,0xA001,M176Write_A001);
SetWriteHandler(0x5001,0x5001,M176Write_5001);
SetWriteHandler(0x5010,0x5010,M176Write_5010);
SetWriteHandler(0x5011,0x5011,M176Write_5011);
SetWriteHandler(0x5ff1,0x5ff1,M176Write_5FF1);
SetWriteHandler(0x5ff2,0x5ff2,M176Write_5FF2);
FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM);
we_sram = 0;
sbw = 0;
prg[0] = 0;
prg[1] = 1;
prg[2] = (ROM_size-2)&63;
prg[3] = (ROM_size-1)&63;
Sync();
}
static void M176Close(void)
{
if(WRAM)
FCEU_gfree(WRAM);
WRAM = NULL;
}
static void StateRestore(int version)
{
Sync();
}
void Mapper176_Init(CartInfo *info)
{
info->Power=M176Power;
info->Close=M176Close;
GameStateRestore=StateRestore;
WRAMSIZE=8192;
WRAM=(uint8*)FCEU_gmalloc(WRAMSIZE);
SetupCartPRGMapping(0x10,WRAM,WRAMSIZE,1);
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
AddExState(&StateRegs, ~0, 0, 0);
}

View File

@ -0,0 +1,81 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2007 CaH4e3
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mapinc.h"
static uint8 reg;
static uint8 *WRAM = NULL;
static uint32 WRAMSIZE;
static SFORMAT StateRegs[] =
{
{ &reg, 1, "REG" },
{ 0 }
};
static void Sync(void) {
setchr8(0);
setprg8r(0x10, 0x6000, 0);
setprg32(0x8000, reg & 0x1f);
setmirror(((reg & 0x20) >> 5) ^ 1);
}
static DECLFW(M177Write) {
reg = V;
Sync();
}
static void M177Power(void) {
reg = 0;
Sync();
SetReadHandler(0x6000, 0x7fff, CartBR);
SetWriteHandler(0x6000, 0x7fff, CartBW);
SetReadHandler(0x8000, 0xFFFF, CartBR);
SetWriteHandler(0x8000, 0xFFFF, M177Write);
FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM);
}
static void M177Close(void) {
if (WRAM)
FCEU_gfree(WRAM);
WRAM = NULL;
}
static void StateRestore(int version) {
Sync();
}
void Mapper177_Init(CartInfo *info) {
info->Power = M177Power;
info->Close = M177Close;
GameStateRestore = StateRestore;
WRAMSIZE = 8192;
WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE);
SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1);
AddExState(WRAM, WRAMSIZE, 0, "WRAM");
if (info->battery) {
info->SaveGame[0] = WRAM;
info->SaveGameLen[0] = WRAMSIZE;
}
AddExState(&StateRegs, ~0, 0, 0);
}

Some files were not shown because too many files have changed in this diff Show More