Post

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.