springboard to rust; VGA text mode driver and basics of TTY system
This commit is contained in:
commit
b5aea81535
|
@ -0,0 +1,6 @@
|
|||
[build]
|
||||
target = "x86_64-unknown-shadeos-gnu.json"
|
||||
|
||||
[unstable]
|
||||
build-std = ["core", "compiler_builtins"]
|
||||
build-std-features = ["compiler-builtins-mem"]
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/Cargo.lock
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MakefileSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<MakefileProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="version" value="2" />
|
||||
</MakefileProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
<component name="MakefileWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
<component name="MarkdownSettingsMigration">
|
||||
<option name="stateVersion" value="1" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="Python" name="Python facet">
|
||||
<configuration sdkName="Python 3.10" />
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "shadeos"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
lazy_static = {version = "1.4.0", features = ["spin_no_std"]}
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
[profile.release]
|
||||
panic = "abort"
|
|
@ -0,0 +1,45 @@
|
|||
ASM := nasm
|
||||
ASMARGS := -f elf64
|
||||
|
||||
LD := x86_64-elf-ld
|
||||
LDARGS := --nmagic
|
||||
|
||||
CARGO_ENV := debug
|
||||
CARGO_EXTRA_ARG := #--release
|
||||
|
||||
.PHONY = target clean run rustc build debug
|
||||
default: build
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
build: target/shade.iso
|
||||
|
||||
rustc:
|
||||
RUST_TARGET_PATH=$(shell pwd) cargo build $(CARGO_EXTRA_ARG)
|
||||
|
||||
run: target/shade.iso
|
||||
qemu-system-x86_64 -cdrom target/shade.iso
|
||||
debug: target/shade.iso
|
||||
qemu-system-x86_64 -cdrom target/shade.iso -s -S
|
||||
|
||||
target/shade.iso: target/shade.bin iso/boot/grub/grub.cfg
|
||||
cp -r iso target
|
||||
cp target/shade.bin target/iso/boot/shade.bin
|
||||
cd target
|
||||
cd target && grub-mkrescue -o shade.iso iso
|
||||
|
||||
|
||||
target/shade.bin: target/llb/entry32.o target/llb/mb2_header.o rustc
|
||||
$(LD) $(LDARGS) --output=target/shade.bin --script=linker.ld target/llb/entry32.o target/llb/mb2_header.o target/x86_64-unknown-shadeos-gnu/$(CARGO_ENV)/libshadeos.a
|
||||
|
||||
target/llb.a: target/llb/mb2_header.o target/llb/entry32.o
|
||||
mkdir -p target/llb
|
||||
ar rvs target/llb.a target/llb/mb2_header.o target/llb/entry32.o
|
||||
|
||||
target/llb/mb2_header.o: src/llb/mb2_header.asm
|
||||
mkdir -p target/llb
|
||||
$(ASM) $(ASMARGS) src/llb/mb2_header.asm -o target/llb/mb2_header.o
|
||||
target/llb/entry32.o: src/llb/entry32.asm
|
||||
mkdir -p target/llb
|
||||
$(ASM) $(ASMARGS) src/llb/entry32.asm -o target/llb/entry32.o
|
|
@ -0,0 +1,7 @@
|
|||
set timeout=0
|
||||
set default=0
|
||||
|
||||
menuentry "shadeOS" {
|
||||
multiboot2 /boot/shade.bin
|
||||
boot
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
.boot :
|
||||
{
|
||||
*(.mb2_header)
|
||||
}
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub mod tty;
|
|
@ -0,0 +1 @@
|
|||
pub mod vga;
|
|
@ -0,0 +1,28 @@
|
|||
use crate::tty::{TTYCharacter, TTYDriver, TTYDriverError};
|
||||
use crate::unsafe_utils::writes::write_dword;
|
||||
|
||||
/// Implementation of TTYDriver for 0xb8000 VGA Text Mode
|
||||
|
||||
pub struct VGATTYDriver {}
|
||||
impl TTYDriver for VGATTYDriver {
|
||||
fn set_char(&self, row: usize, col: usize, char: TTYCharacter) -> Result<(), TTYDriverError> {
|
||||
if row > 25 {
|
||||
return Err(TTYDriverError::RowOutOfBounds { got: row })
|
||||
}
|
||||
if col > 80 {
|
||||
return Err(TTYDriverError::ColOutOfBounds { got: col })
|
||||
}
|
||||
// calculate the memory offset
|
||||
let write_location = 0xb8000 + (row * 80 + col) * 2;
|
||||
|
||||
// convert the TTYCharacter into a raw u16 to be written to memory
|
||||
let raw_char = (char.format as u16) << 8 | char.char as u16;
|
||||
|
||||
// and then write it using unsafe_utils
|
||||
unsafe {
|
||||
write_dword(raw_char, write_location);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#![no_std]
|
||||
#![feature(error_in_core)]
|
||||
#![feature(panic_info_message)]
|
||||
|
||||
pub mod tty;
|
||||
pub mod drivers;
|
||||
pub mod unsafe_utils;
|
||||
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use crate::drivers::tty::vga::VGATTYDriver;
|
||||
use crate::tty::{Color, TTY, TTYCharacter, TTYDriver, TTYDriverError};
|
||||
|
||||
/// This is our kpanic function. This will eventually use any functioning video driver to dump the kernel, but for now it just hangs
|
||||
#[panic_handler]
|
||||
fn kpanic(_info: &PanicInfo) -> ! {
|
||||
let panic_str = _info.location().unwrap();
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn kmain() -> ! {
|
||||
// initialize ttys and the kernel buffer
|
||||
// just one for now
|
||||
let vga_driver = VGATTYDriver {};
|
||||
let mut ttys: TTY<2> = TTY::new();
|
||||
ttys.select(0);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][0] = TTYCharacter::new('s', Color::White, Color::Black);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][1] = TTYCharacter::new('h', Color::White, Color::Black);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][2] = TTYCharacter::new('a', Color::White, Color::Black);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][3] = TTYCharacter::new('d', Color::White, Color::Black);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][4] = TTYCharacter::new('e', Color::White, Color::Black);
|
||||
|
||||
ttys.select(1);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][0] = TTYCharacter::new('o', Color::White, Color::Black);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][1] = TTYCharacter::new('s', Color::White, Color::Black);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][2] = TTYCharacter::new(' ', Color::White, Color::Black);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][3] = TTYCharacter::new(':', Color::White, Color::Black);
|
||||
ttys.ttys[ttys.selected].screen_buffer[0][4] = TTYCharacter::new(')', Color::White, Color::Black);
|
||||
|
||||
match ttys.select_and_update(0, &vga_driver) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
match e {
|
||||
TTYDriverError::ColOutOfBounds { got } => {
|
||||
vga_driver.set_char(0, 5, TTYCharacter::new('y', Color::Red, Color::Black)).unwrap();
|
||||
loop {}
|
||||
},
|
||||
TTYDriverError::RowOutOfBounds { got } => {
|
||||
vga_driver.set_char(0, 5, TTYCharacter::new('y', Color::Red, Color::Black)).unwrap();
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..2000000 {
|
||||
continue;
|
||||
}
|
||||
|
||||
ttys.select_and_update(1, &vga_driver).unwrap();
|
||||
|
||||
//vga_driver.set_char(0, 0, TTYCharacter::new('s', Color::White, Color::Black)).unwrap();
|
||||
//vga_driver.set_char(0, 1, TTYCharacter::new('h', Color::White, Color::Black)).unwrap();
|
||||
|
||||
loop{}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
global _start
|
||||
extern kmain
|
||||
|
||||
section .text
|
||||
bits 32
|
||||
|
||||
; LLB MESSAGE CODES:
|
||||
; llb:01 - llb execution started
|
||||
; llb:02 - llb got execution back, system panic
|
||||
; llb:03 - configuring page tables
|
||||
; llb:04 - page tables configured with no issues
|
||||
; llb:05 - gdt loaded, handoff to rbt
|
||||
; llb:6 - handoff to long mode successful
|
||||
|
||||
_start:
|
||||
; llb:01
|
||||
mov word [0xb8000], 0x026c
|
||||
mov word [0xb8002], 0x026c
|
||||
mov word [0xb8004], 0x0262
|
||||
mov word [0xb8006], 0x023a
|
||||
mov word [0xb8008], 0x0230
|
||||
mov word [0xb800a], 0x0231
|
||||
|
||||
; llb:03
|
||||
mov word [0xb8010], 0x026c
|
||||
mov word [0xb8012], 0x026c
|
||||
mov word [0xb8014], 0x0262
|
||||
mov word [0xb8016], 0x023a
|
||||
mov word [0xb8018], 0x0230
|
||||
mov word [0xb801a], 0x0233
|
||||
|
||||
; Point the first entry of the level 4 page table to the first entry in the
|
||||
; p3 table
|
||||
mov eax, p3_table
|
||||
or eax, 0b11
|
||||
mov dword [p4_table + 0], eax
|
||||
|
||||
mov eax, p2_table
|
||||
or eax, 0b11
|
||||
mov dword [p3_table + 0], eax
|
||||
|
||||
mov ecx, 0 ; loop counter
|
||||
.map_p2_table:
|
||||
mov eax, 0x200000 ; 2 MiB huge pages
|
||||
mul ecx
|
||||
or eax, 0b10000011 ; huge pages
|
||||
mov [p2_table + ecx * 8], eax ; mark in the page table
|
||||
inc ecx
|
||||
cmp ecx, 512 ; 512 2 MiB pages = 1024 MiB paged memory; 1 GiB usable memory
|
||||
jne .map_p2_table
|
||||
|
||||
; put address of page table in cr3
|
||||
mov eax, p4_table
|
||||
mov cr3, eax
|
||||
|
||||
; enable pae
|
||||
mov eax, cr4
|
||||
or eax, 1 << 5
|
||||
mov cr4, eax
|
||||
|
||||
; set long mode bit
|
||||
mov ecx, 0xc0000080
|
||||
rdmsr
|
||||
or eax, 1 << 8
|
||||
wrmsr
|
||||
|
||||
; enable paging
|
||||
mov eax, cr0
|
||||
or eax, 1 << 31
|
||||
or eax, 1 << 16
|
||||
mov cr0, eax
|
||||
|
||||
; llb:04
|
||||
mov word [0xb8020], 0x026c
|
||||
mov word [0xb8022], 0x026c
|
||||
mov word [0xb8024], 0x0262
|
||||
mov word [0xb8026], 0x023a
|
||||
mov word [0xb8028], 0x0230
|
||||
mov word [0xb802a], 0x0234
|
||||
|
||||
lgdt [gdt64.pointer]
|
||||
|
||||
; update selectors
|
||||
mov ax, gdt64.data
|
||||
mov ss, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
|
||||
; llb:05
|
||||
mov word [0xb8030], 0x026c
|
||||
mov word [0xb8032], 0x026c
|
||||
mov word [0xb8034], 0x0262
|
||||
mov word [0xb8036], 0x023a
|
||||
mov word [0xb8038], 0x0230
|
||||
mov word [0xb803a], 0x0235
|
||||
|
||||
jmp gdt64.code:rbt_entry
|
||||
|
||||
; llb:02
|
||||
mov word [0xb8200], 0x026c
|
||||
mov word [0xb8202], 0x026c
|
||||
mov word [0xb8204], 0x0262
|
||||
mov word [0xb8206], 0x023a
|
||||
mov word [0xb8208], 0x0230
|
||||
mov word [0xb820a], 0x0232
|
||||
hlt
|
||||
|
||||
section .bss
|
||||
align 4096
|
||||
|
||||
p4_table:
|
||||
resb 4096
|
||||
p3_table:
|
||||
resb 4096
|
||||
p2_table:
|
||||
resb 4096
|
||||
|
||||
section .rodata
|
||||
section .rodata
|
||||
gdt64:
|
||||
dq 0
|
||||
.code: equ $ - gdt64
|
||||
dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53)
|
||||
.data: equ $ - gdt64
|
||||
dq (1<<44) | (1<<47) | (1<<41)
|
||||
.pointer:
|
||||
dw .pointer - gdt64 - 1
|
||||
dq gdt64
|
||||
|
||||
section .text
|
||||
bits 64
|
||||
rbt_entry:
|
||||
|
||||
; llb:06
|
||||
mov word [0xb8040], 0x026c
|
||||
mov word [0xb8042], 0x026c
|
||||
mov word [0xb8044], 0x0262
|
||||
mov word [0xb8046], 0x023a
|
||||
mov word [0xb8048], 0x0230
|
||||
mov word [0xb804a], 0x0236
|
||||
|
||||
jmp kmain
|
||||
|
||||
hlt
|
|
@ -0,0 +1,14 @@
|
|||
section .mb2_header
|
||||
header_start:
|
||||
dd 0xe85250d6 ; magic number
|
||||
dd 0 ; protected mode code
|
||||
dd header_end - header_start ; header length
|
||||
|
||||
; checksum
|
||||
dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start))
|
||||
|
||||
; required end tag
|
||||
dw 0 ; type
|
||||
dw 0 ; flags
|
||||
dd 8 ; size
|
||||
header_end:
|
|
@ -0,0 +1,121 @@
|
|||
use core::error::Error;
|
||||
use core::fmt::{Display, Formatter};
|
||||
|
||||
pub struct TTY<const C: usize> {
|
||||
pub ttys: [VirtualTTY; C],
|
||||
pub selected: usize
|
||||
}
|
||||
impl<const C: usize> TTY<C> {
|
||||
pub fn new() -> TTY<C> {
|
||||
Self {
|
||||
ttys: [
|
||||
VirtualTTY::new(); C
|
||||
],
|
||||
selected: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select(&mut self, new: usize) { self.selected = new; }
|
||||
|
||||
pub fn update(&self, driver: &impl TTYDriver) -> Result<(), TTYDriverError> {
|
||||
let selected_tty = &self.ttys[self.selected];
|
||||
for (row_num, row) in selected_tty.screen_buffer.iter().enumerate() {
|
||||
for (col_num, col) in row.iter().enumerate() {
|
||||
driver.set_char(row_num, col_num, *col)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_and_update(&mut self, new: usize, driver: &impl TTYDriver) -> Result<(), TTYDriverError> {
|
||||
self.select(new);
|
||||
self.update(driver)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum Color {
|
||||
Black = 0,
|
||||
Blue = 1,
|
||||
Green = 2,
|
||||
Cyan = 3,
|
||||
Red = 4,
|
||||
Magenta = 5,
|
||||
Brown = 6,
|
||||
LightGray = 7,
|
||||
DarkGray = 8,
|
||||
LightBlue = 9,
|
||||
LightGreen = 10,
|
||||
LightCyan = 11,
|
||||
LightRed = 12,
|
||||
Pink = 13,
|
||||
Yellow = 14,
|
||||
White = 15,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TTYCharacter {
|
||||
pub format: u8,
|
||||
pub char: char
|
||||
}
|
||||
|
||||
impl TTYCharacter {
|
||||
pub fn blank() -> Self { Self { format: 0u8, char: ' ' } }
|
||||
pub fn from(char: char) -> Self { Self { format: 0x0fu8, char }}
|
||||
pub fn new(char: char, fg: Color, bg: Color) -> Self {
|
||||
Self {
|
||||
format: (fg as u8) << 4 | bg as u8,
|
||||
char
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const ROWS_ON_SCREEN: usize = 25;
|
||||
pub const COLS: usize = 80;
|
||||
|
||||
pub const ROWS_IN_TTY: usize = 80;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct VirtualTTY {
|
||||
pub full_buffer: VirtualTTYBuffer<ROWS_IN_TTY, COLS>,
|
||||
pub screen_buffer: VirtualTTYBuffer<ROWS_ON_SCREEN, COLS>,
|
||||
pub line_offset: usize
|
||||
}
|
||||
impl Default for VirtualTTY {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
full_buffer: [[TTYCharacter::blank(); COLS]; ROWS_IN_TTY],
|
||||
screen_buffer: [[TTYCharacter::blank(); COLS]; ROWS_ON_SCREEN],
|
||||
line_offset: ROWS_IN_TTY - ROWS_ON_SCREEN, // assume we are scrolled all the way down
|
||||
}
|
||||
}
|
||||
}
|
||||
impl VirtualTTY {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
type VirtualTTYBuffer<const R: usize, const C: usize> = [[TTYCharacter; C]; R];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TTYDriverError {
|
||||
RowOutOfBounds { got: usize },
|
||||
ColOutOfBounds { got: usize }
|
||||
}
|
||||
impl Display for TTYDriverError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::RowOutOfBounds { got } => write!(f, "Character write out of bounds: row {} too high", got),
|
||||
Self::ColOutOfBounds { got } => write!(f, "Character write out of bounds: col {} too high", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Error for TTYDriverError {}
|
||||
|
||||
pub trait TTYDriver {
|
||||
fn set_char(&self, row: usize, col: usize, char: TTYCharacter) -> Result<(), TTYDriverError>;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/// Unsafe utility functions
|
||||
pub mod writes;
|
|
@ -0,0 +1,5 @@
|
|||
/// Write the given dword to any arbitrary memory location. This is an inherently unsafe operation; hence the function is unsafe
|
||||
pub unsafe fn write_dword(dword: u16, at: usize) {
|
||||
let ptr = at as *mut u16;
|
||||
*ptr = dword;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/python3
|
||||
import sys
|
||||
|
||||
|
||||
def help():
|
||||
print('Usage: mkasmmsg.py -b<32/64> <format> <message>')
|
||||
|
||||
|
||||
if len(sys.argv) != 4:
|
||||
help()
|
||||
sys.exit(1)
|
||||
|
||||
format = sys.argv[2]
|
||||
msg = sys.argv[3]
|
||||
base = 0xb8000
|
||||
|
||||
index = {
|
||||
'1': 'word',
|
||||
'2': 'dword',
|
||||
'4': 'qword'
|
||||
}
|
||||
|
||||
command = 'mov {} [{}], 0x{}'
|
||||
|
||||
if sys.argv[1] == '-b32':
|
||||
groupsize = 2
|
||||
elif sys.argv[1] == '-b64':
|
||||
groupsize = 4
|
||||
else:
|
||||
help()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def chunk(iterator, n):
|
||||
for i in range(0, len(iterator), n):
|
||||
yield iterator[i:i + n]
|
||||
|
||||
|
||||
raw_nums = []
|
||||
|
||||
for char in list(msg):
|
||||
raw_nums.append(format + hex(ord(char))[2:])
|
||||
|
||||
total = 0
|
||||
|
||||
for num, block in enumerate(list(chunk(raw_nums, groupsize))):
|
||||
if len(block) == 3:
|
||||
total += 2
|
||||
print(command.format('word', hex(base + total), block[0]))
|
||||
total += 4
|
||||
print(command.format('dword', hex(base + total), ''.join(block[1:])))
|
||||
continue
|
||||
total += len(block) * groupsize
|
||||
print(command.format(index[str(len(block))], hex(base + total), ''.join(block)))
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"arch": "x86_64",
|
||||
"cpu": "x86-64",
|
||||
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
|
||||
"llvm-target": "x86_64-unknown-none-gnu",
|
||||
"linker-flavor": "gcc",
|
||||
"no-compiler-rt": true,
|
||||
"os": "intermezzos",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "64",
|
||||
"target-c-int-width": "32",
|
||||
"features": "-mmx,-fxsr,-sse,-sse2,+soft-float",
|
||||
"disable-redzone": true,
|
||||
"eliminate-frame-pointer": false
|
||||
}
|
Loading…
Reference in New Issue