Zig Note - 01
Zig Note - 01
均以zig 0.16为标准
定义type
1
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
28
29
30
31
32
// enum
pub const Mapping = enum {
lorom,
hirom,
exhirom,
unknown,
};
// struct
pub const RomHeader = struct {
title: [21]u8,
map_mode: u8,
cartridge_type: u8,
rom_size_code: u8,
ram_size_code: u8,
region_code: u8,
developer_id: u8,
version: u8,
checksum_complement: u16,
checksum: u16,
reset_vector: u16,
};
// custom error type
const RomError = error{
HeaderOutOfBounds,
InvalidValues,
ChecksumMismatch,
};
闭包
1
2
3
4
5
for (title) |c| {
if (c < 0x20 or c > 0x7d) {
return error.InvalidValues;
}
}
函数返回可能的Error & 处理
返回值类型前加 ! 表示可能返回Error
1
2
3
4
// This function may return Error
fn parse_internal_header(data: []const u8, header_offset: u32) !RomHeader {
...
}
调用函数的时候,可以用下列方式捕获:
1
2
3
4
5
6
if(parse_internal_header(data, offset)) |parsed_header| {
... // success
}
else |err| {
... // error
}
也可以使用catch关键词,只有当前面的结果出现了error后,才会向后执行. 利用该关键字可以将下列逻辑:
1
2
3
4
5
6
7
8
9
10
11
var parsed_header: RomHeader = undefined;
if (parse_internal_header(data, rom_start + 0x7fc0)) |header| {
parsed_header = header;
} else if (parse_internal_header(data, rom_start + 0xffc0)) |header| {
parsed_header = header;
} else if (parse_internal_header(data, rom_start + 0x40FFC0)) |header| {
parsed_header = header;
} else {
... // all failed
}
简化成:
1
const parsed_header =
zig block
如果被包进了{}中,就要在后面加分号。但是下面的语句会创建两个变量,并没有赋给rom_data
1
2
3
4
5
6
7
8
9
10
const rom_data: []const u8 =
if (data_length % 1024 == 512){
data[512..];
}
else if (data_length % 1024 == 0){
data[0..];
}
else {
return error.InvalidValue; // Error
};
正确写法:
1
2
3
4
5
6
7
8
const rom_data: []const u8 =
if (data_length % 1024 == 512)
data[512..]
else if (data_length % 1024 == 0)
data[0..]
else {
return error.InvalidValue; // Error
};
comptime_int
1
2
const size = 1 << 10; // 合法
const size2 = 1 << header.rom_size_code; // 非法
单独写一个1时,属于zig中的comptime_int,它没有固定的位数,可以任意增长. 第一个例子中,因为二者都是固定的数值,所以可以在编译期间确定;但是后一个例子却不行.
解决方法是使用固定长度的1:
1
const size = @as(usize, 1) << @intCast(header.rom_size_code);
这里@intCast根据语境将一个数据转化成对应类型.
类似的有@truncate, 但是它在数据比目标类型长的时候,会强制截断.
1
2
3
fn bankOf(addr: u24) u8 {
return @truncate(addr >> 16);
}
explict type
尽管 .{}能够推断类型,但是建立在周围的环境提供了确定类型的前提下。不要以为可以通过{}内的字段来推断!
比如下面的rom_info没有写具体类型,会报错:error: expected type 'src.parser.parser.RomHeader', found 'src.parser.parser.parse_internal_header__struct_43088
1
2
3
4
5
6
7
8
9
fn parse_internal_header(data: []const u8, header_offset: u32) !RomInfo {
......
const rom_info = .{
......
};
return rom_info;
}
加上具体类型后解决.
1
2
3
4
5
6
7
8
9
fn parse_internal_header(data: []const u8, header_offset: u32) !RomInfo {
......
const rom_info: RomInfo = .{
......
};
return rom_info;
}
执行一个block, 返回一个值
1
2
3
4
5
6
7
8
9
const final_value = blk: {
const a: i32 = 5;
const b: i32 = 10;
// Execute any commands or logic here
std.debug.print("Processing components...\n", .{});
break :blk a + b; // This is the last value returned
};
读取文件
1
2
3
4
// 读取到 []u8 数组中.
const ff3_rom = @embedFile(
"roms/Final Fantasy III (USA).es.smc",
);
分配堆内存
1
2
const allocator = init.gpa;
self.wram = try allocator.alloc(u8, 0x20000);
slice
zig中的slice
1
2
3
4
5
6
7
8
9
10
11
12
pub const Cartridge = struct {
rom: []const u8,
sram: []u8,
mapping: Mapping,
pub fn init(self: *Cartridge, rom_info: RomInfo) void {
self.mapping = rom_info.mapping;
// 错误,因为rom是const类型,无法使用@memcpy
@memcpy(self.rom[0..rom_info.declared_rom_size], rom_info.rom_data[0..]);
}
};
array repetition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pub const INVALID = Instruction{
.mnemonic = .unknown,
.mode = .implied,
.base_cycles = 0,
};
pub const opcode_table: [256]Instruction = blk: {
var table = [_]Instruction{INVALID} ** 256;
table[0xEA] = {
.mnemonic = .nop,
.mode = .implied,
.base_cycles = 2,
}
break :blk table;
}
blk:代表一个block expression.
[_]代表让编译器自动推断大小. **是array repetition operator.
This post is licensed under CC BY 4.0 by the author.