Merge branch 'master' into rv64-upstream
This commit is contained in:
commit
91a22ea902
|
@ -4,6 +4,7 @@
|
|||
*.a
|
||||
*.o
|
||||
*.d
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.ini
|
||||
*.log
|
||||
|
|
|
@ -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)__ \
|
||||
|
|
31
README.md
31
README.md
|
@ -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
31
am/am.h
|
@ -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) =================
|
||||
|
||||
|
|
24
am/amdev.h
24
am/amdev.h
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
CFLAGS += -m64 -fPIC -mno-sse
|
||||
ASFLAGS += -m64 -fPIC
|
||||
LDFLAGS += -melf_x86_64
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
struct _Context {
|
||||
union {
|
||||
struct _AddressSpace *as;
|
||||
void *pdir;
|
||||
uint32_t gpr[32];
|
||||
};
|
||||
uint32_t lo, hi;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
struct _Context {
|
||||
union {
|
||||
struct _AddressSpace *as;
|
||||
void *pdir;
|
||||
uint32_t gpr[32];
|
||||
};
|
||||
uint32_t cause;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -4,9 +4,6 @@
|
|||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
||||
struct _Context {
|
||||
};
|
||||
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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; }
|
||||
|
|
383
am/include/x86.h
383
am/include/x86.h
|
@ -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__
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)];
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#include "x86-qemu.h"
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
pushl $MAINARG_ADDR
|
||||
pushl $0
|
||||
jmp _start_c
|
|
@ -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:
|
|
@ -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;
|
|
@ -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;
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
'''
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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).
|
||||
*/
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -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 (绘图、定时).
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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[] =
|
||||
{
|
||||
{ ®0, 1, "REG0" },
|
||||
{ ®1, 1, "REG1" },
|
||||
{ ®2, 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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[] =
|
||||
{
|
||||
{ ®, 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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[] =
|
||||
{
|
||||
{ ®, 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);
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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[] =
|
||||
{
|
||||
{ ®, 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");
|
||||
}
|
|
@ -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[] =
|
||||
{
|
||||
{ ®, 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);
|
||||
}
|
|
@ -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[] =
|
||||
{
|
||||
{ ®, 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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[] =
|
||||
{
|
||||
{ ®, 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
Loading…
Reference in New Issue