Skip to main content

native_v86_core/cpu/
misc_instr.rs

1use crate::cpu::cpu::*;
2use crate::cpu::fpu::{
3    fpu_load_m80, fpu_load_status_word, fpu_set_status_word, fpu_store_m80, set_control_word,
4};
5use crate::cpu::global_pointers::*;
6use crate::paging::OrPageFault;
7
8pub unsafe fn getcf() -> bool {
9    if 0 != *flags_changed & 1 {
10        let m = (2u32.wrapping_shl(*last_op_size as u32)).wrapping_sub(1);
11        dbg_assert!((*last_op1 as u32) <= m);
12        dbg_assert!((*last_result as u32) <= m);
13
14        let sub_mask = *flags_changed >> 31;
15
16        // sub: last_op1 < last_result  (or last_op1 < last_op2) (or (result ^ ((result ^ b) & (b ^ a))))
17        // add: last_result < last_op1  (or last_result < last_op2) (or a ^ ((a ^ b) & (b ^ result)))
18        return ((*last_result as i32 ^ sub_mask) as u32) < (*last_op1 ^ sub_mask) as u32;
19    } else {
20        return 0 != *flags & 1;
21    };
22}
23#[no_mangle]
24pub unsafe fn getpf() -> bool {
25    if 0 != *flags_changed & FLAG_PARITY {
26        // inverted lookup table
27        return 0 != 0x9669 << 2 >> ((*last_result ^ *last_result >> 4) & 15) & FLAG_PARITY;
28    } else {
29        return 0 != *flags & FLAG_PARITY;
30    };
31}
32pub unsafe fn getaf() -> bool {
33    if 0 != *flags_changed & FLAG_ADJUST {
34        let is_sub = *flags_changed & FLAG_SUB != 0;
35        let last_op2 = (*last_result).wrapping_sub(*last_op1) * if is_sub { -1 } else { 1 };
36        return 0 != (*last_op1 ^ last_op2 ^ *last_result) & FLAG_ADJUST;
37    } else {
38        return 0 != *flags & FLAG_ADJUST;
39    };
40}
41pub unsafe fn getzf() -> bool {
42    if 0 != *flags_changed & FLAG_ZERO {
43        return 0 != (!*last_result & (*last_result).wrapping_sub(1)) >> *last_op_size & 1;
44    } else {
45        return 0 != *flags & FLAG_ZERO;
46    };
47}
48pub unsafe fn getsf() -> bool {
49    if 0 != *flags_changed & FLAG_SIGN {
50        return 0 != *last_result >> *last_op_size & 1;
51    } else {
52        return 0 != *flags & FLAG_SIGN;
53    };
54}
55pub unsafe fn getof() -> bool {
56    if 0 != *flags_changed & FLAG_OVERFLOW {
57        let is_sub = (*flags_changed as u32) >> 31;
58
59        // add: (a ^ result) & (b ^ result)
60        // sub: (a ^ result) & (b ^ result ^ 1) (or (a ^ b) & (result ^ a))
61        let b_xor_1_if_sub = (*last_result)
62            .wrapping_sub(*last_op1)
63            .wrapping_sub(is_sub as i32);
64        return 0
65            != ((*last_op1 ^ *last_result) & (b_xor_1_if_sub ^ *last_result)) >> *last_op_size & 1;
66    } else {
67        return 0 != *flags & FLAG_OVERFLOW;
68    };
69}
70
71pub unsafe fn test_o() -> bool {
72    return getof();
73}
74pub unsafe fn test_b() -> bool {
75    return getcf();
76}
77pub unsafe fn test_z() -> bool {
78    return getzf();
79}
80pub unsafe fn test_s() -> bool {
81    return getsf();
82}
83#[no_mangle]
84pub unsafe fn test_p() -> bool {
85    return getpf();
86}
87pub unsafe fn test_be() -> bool {
88    return getcf() || getzf();
89}
90pub unsafe fn test_l() -> bool {
91    return getsf() != getof();
92}
93pub unsafe fn test_le() -> bool {
94    return getzf() || getsf() != getof();
95}
96pub unsafe fn test_no() -> bool {
97    return !test_o();
98}
99pub unsafe fn test_nb() -> bool {
100    return !test_b();
101}
102pub unsafe fn test_nz() -> bool {
103    return !test_z();
104}
105pub unsafe fn test_ns() -> bool {
106    return !test_s();
107}
108#[no_mangle]
109pub unsafe fn test_np() -> bool {
110    return !test_p();
111}
112pub unsafe fn test_nbe() -> bool {
113    return !test_be();
114}
115pub unsafe fn test_nl() -> bool {
116    return !test_l();
117}
118pub unsafe fn test_nle() -> bool {
119    return !test_le();
120}
121
122pub unsafe fn jmp_rel16(rel16: i32) {
123    let cs_offset = get_seg_cs();
124    // limit ip to 16 bit
125    *instruction_pointer = cs_offset + (*instruction_pointer - cs_offset + rel16 & 0xFFFF);
126}
127pub unsafe fn jmpcc16(condition: bool, imm16: i32) {
128    if condition {
129        jmp_rel16(imm16);
130    };
131}
132pub unsafe fn jmpcc32(condition: bool, imm32: i32) {
133    if condition {
134        *instruction_pointer += imm32
135    };
136}
137pub unsafe fn loope16(imm8s: i32) {
138    jmpcc16(0 != decr_ecx_asize(is_asize_32()) && getzf(), imm8s);
139}
140pub unsafe fn loopne16(imm8s: i32) {
141    jmpcc16(0 != decr_ecx_asize(is_asize_32()) && !getzf(), imm8s);
142}
143pub unsafe fn loop16(imm8s: i32) {
144    jmpcc16(0 != decr_ecx_asize(is_asize_32()), imm8s);
145}
146pub unsafe fn jcxz16(imm8s: i32) {
147    jmpcc16(get_reg_asize(ECX) == 0, imm8s);
148}
149pub unsafe fn loope32(imm8s: i32) {
150    jmpcc32(0 != decr_ecx_asize(is_asize_32()) && getzf(), imm8s);
151}
152pub unsafe fn loopne32(imm8s: i32) {
153    jmpcc32(0 != decr_ecx_asize(is_asize_32()) && !getzf(), imm8s);
154}
155pub unsafe fn loop32(imm8s: i32) {
156    jmpcc32(0 != decr_ecx_asize(is_asize_32()), imm8s);
157}
158pub unsafe fn jcxz32(imm8s: i32) {
159    jmpcc32(get_reg_asize(ECX) == 0, imm8s);
160}
161
162pub unsafe fn cmovcc16(condition: bool, value: i32, r: i32) {
163    if condition {
164        write_reg16(r, value);
165    };
166}
167pub unsafe fn cmovcc32(condition: bool, value: i32, r: i32) {
168    if condition {
169        write_reg32(r, value);
170    };
171}
172
173pub unsafe fn get_stack_pointer(offset: i32) -> i32 {
174    if *stack_size_32 {
175        return get_seg_ss() + read_reg32(ESP) + offset;
176    } else {
177        return get_seg_ss() + (read_reg16(SP) + offset & 0xFFFF);
178    };
179}
180pub unsafe fn adjust_stack_reg(adjustment: i32) {
181    if *stack_size_32 {
182        write_reg32(ESP, read_reg32(ESP) + adjustment);
183    } else {
184        write_reg16(SP, read_reg16(SP) + adjustment);
185    };
186}
187
188pub unsafe fn push16_ss16(imm16: i32) -> OrPageFault<()> {
189    let sp = get_seg_ss() + (read_reg16(SP) - 2 & 0xFFFF);
190    safe_write16(sp, imm16)?;
191    write_reg16(SP, read_reg16(SP) - 2);
192    Ok(())
193}
194pub unsafe fn push16_ss32(imm16: i32) -> OrPageFault<()> {
195    let sp = get_seg_ss() + read_reg32(ESP) - 2;
196    safe_write16(sp, imm16)?;
197    write_reg32(ESP, read_reg32(ESP) - 2);
198    Ok(())
199}
200
201pub unsafe fn push16_ss16_mem(addr: i32) -> OrPageFault<()> {
202    push16_ss16(safe_read16(addr)?)
203}
204pub unsafe fn push16_ss32_mem(addr: i32) -> OrPageFault<()> {
205    push16_ss32(safe_read16(addr)?)
206}
207
208pub unsafe fn push16(imm16: i32) -> OrPageFault<()> {
209    if *stack_size_32 {
210        push16_ss32(imm16)
211    } else {
212        push16_ss16(imm16)
213    }
214}
215
216pub unsafe fn push32_ss16(imm32: i32) -> OrPageFault<()> {
217    let new_sp = read_reg16(SP) - 4 & 0xFFFF;
218    safe_write32(get_seg_ss() + new_sp, imm32)?;
219    write_reg16(SP, new_sp);
220    Ok(())
221}
222pub unsafe fn push32_ss32(imm32: i32) -> OrPageFault<()> {
223    let new_esp = read_reg32(ESP) - 4;
224    safe_write32(get_seg_ss() + new_esp, imm32)?;
225    write_reg32(ESP, new_esp);
226    Ok(())
227}
228
229pub unsafe fn push32_ss16_mem(addr: i32) -> OrPageFault<()> {
230    push32_ss16(safe_read32s(addr)?)
231}
232pub unsafe fn push32_ss32_mem(addr: i32) -> OrPageFault<()> {
233    push32_ss32(safe_read32s(addr)?)
234}
235
236pub unsafe fn push32(imm32: i32) -> OrPageFault<()> {
237    if *stack_size_32 {
238        push32_ss32(imm32)
239    } else {
240        push32_ss16(imm32)
241    }
242}
243
244pub unsafe fn push32_sreg(i: i32) -> OrPageFault<()> {
245    // you can't make this up ...
246    if *stack_size_32 {
247        let new_esp = read_reg32(ESP) - 4;
248        safe_write16(get_seg_ss() + new_esp, *sreg.offset(i as isize) as i32)?;
249        write_reg32(ESP, new_esp);
250    } else {
251        let new_sp = read_reg16(SP) - 4 & 0xFFFF;
252        safe_write16(get_seg_ss() + new_sp, *sreg.offset(i as isize) as i32)?;
253        write_reg16(SP, new_sp);
254    }
255    Ok(())
256}
257
258pub unsafe fn pop16() -> OrPageFault<i32> {
259    if *stack_size_32 {
260        pop16_ss32()
261    } else {
262        pop16_ss16()
263    }
264}
265pub unsafe fn pop16_ss16() -> OrPageFault<i32> {
266    let sp = get_seg_ss() + read_reg16(SP);
267    let result = safe_read16(sp)?;
268    write_reg16(SP, read_reg16(SP) + 2);
269    Ok(result)
270}
271pub unsafe fn pop16_ss32() -> OrPageFault<i32> {
272    let esp = get_seg_ss() + read_reg32(ESP);
273    let result = safe_read16(esp)?;
274    write_reg32(ESP, read_reg32(ESP) + 2);
275    Ok(result)
276}
277pub unsafe fn pop32s() -> OrPageFault<i32> {
278    if *stack_size_32 {
279        pop32s_ss32()
280    } else {
281        pop32s_ss16()
282    }
283}
284pub unsafe fn pop32s_ss16() -> OrPageFault<i32> {
285    let sp = read_reg16(SP);
286    let result = safe_read32s(get_seg_ss() + sp)?;
287    write_reg16(SP, sp + 4);
288    Ok(result)
289}
290pub unsafe fn pop32s_ss32() -> OrPageFault<i32> {
291    let esp = read_reg32(ESP);
292    let result = safe_read32s(get_seg_ss() + esp)?;
293    write_reg32(ESP, read_reg32(ESP) + 4);
294    Ok(result)
295}
296pub unsafe fn pusha16() {
297    let temp = read_reg16(SP);
298    // make sure we don't get a pagefault after having
299    // pushed several registers already
300    return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-16), 16));
301    push16(read_reg16(AX)).unwrap();
302    push16(read_reg16(CX)).unwrap();
303    push16(read_reg16(DX)).unwrap();
304    push16(read_reg16(BX)).unwrap();
305    push16(temp as i32).unwrap();
306    push16(read_reg16(BP)).unwrap();
307    push16(read_reg16(SI)).unwrap();
308    push16(read_reg16(DI)).unwrap();
309}
310pub unsafe fn pusha32() {
311    let temp = read_reg32(ESP);
312    return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-32), 32));
313    push32(read_reg32(EAX)).unwrap();
314    push32(read_reg32(ECX)).unwrap();
315    push32(read_reg32(EDX)).unwrap();
316    push32(read_reg32(EBX)).unwrap();
317    push32(temp).unwrap();
318    push32(read_reg32(EBP)).unwrap();
319    push32(read_reg32(ESI)).unwrap();
320    push32(read_reg32(EDI)).unwrap();
321}
322
323pub unsafe fn lss16(addr: i32, reg: i32, seg: i32) {
324    let new_reg = return_on_pagefault!(safe_read16(addr));
325    let new_seg = return_on_pagefault!(safe_read16(addr + 2));
326
327    if !switch_seg(seg, new_seg) {
328        return;
329    }
330
331    write_reg16(reg, new_reg);
332}
333
334pub unsafe fn lss32(addr: i32, reg: i32, seg: i32) {
335    let new_reg = return_on_pagefault!(safe_read32s(addr));
336    let new_seg = return_on_pagefault!(safe_read16(addr + 4));
337
338    if !switch_seg(seg, new_seg) {
339        return;
340    }
341
342    write_reg32(reg, new_reg);
343}
344
345pub unsafe fn enter16(size: i32, mut nesting_level: i32) {
346    nesting_level &= 31;
347
348    if nesting_level > 0 {
349        dbg_log!(
350            "enter16 stack={} size={} nest={}",
351            (if *stack_size_32 { 16 } else { 32 }),
352            size,
353            nesting_level,
354        );
355    }
356
357    let ss_mask = if *stack_size_32 { -1 } else { 0xFFFF };
358    let ss = get_seg_ss();
359    let frame_temp = read_reg32(ESP) - 2;
360
361    if nesting_level > 0 {
362        let mut tmp_ebp = read_reg32(EBP);
363        for _ in 1..nesting_level {
364            tmp_ebp -= 2;
365            push16(safe_read16(ss + (tmp_ebp & ss_mask)).unwrap()).unwrap();
366        }
367        push16(frame_temp).unwrap();
368    }
369
370    return_on_pagefault!(safe_write16(ss + (frame_temp & ss_mask), read_reg16(BP)));
371    write_reg16(BP, frame_temp);
372    adjust_stack_reg(-size - 2);
373}
374
375pub unsafe fn enter32(size: i32, mut nesting_level: i32) {
376    nesting_level &= 31;
377
378    if nesting_level > 0 {
379        dbg_log!(
380            "enter32 stack={} size={} nest={}",
381            (if *stack_size_32 { 16 } else { 32 }),
382            size,
383            nesting_level,
384        );
385    }
386
387    let ss_mask = if *stack_size_32 { -1 } else { 0xFFFF };
388    let ss = get_seg_ss();
389    let frame_temp = read_reg32(ESP) - 4;
390
391    if nesting_level > 0 {
392        let mut tmp_ebp = read_reg32(EBP);
393        for _ in 1..nesting_level {
394            tmp_ebp -= 4;
395            push32(safe_read32s(ss + (tmp_ebp & ss_mask)).unwrap()).unwrap();
396        }
397        push32(frame_temp).unwrap();
398    }
399
400    return_on_pagefault!(safe_write32(ss + (frame_temp & ss_mask), read_reg32(EBP)));
401    write_reg32(EBP, frame_temp);
402    adjust_stack_reg(-size - 4);
403}
404
405pub unsafe fn setcc_reg(condition: bool, r: i32) {
406    write_reg8(r, condition as i32);
407}
408pub unsafe fn setcc_mem(condition: bool, addr: i32) {
409    return_on_pagefault!(safe_write8(addr, condition as i32));
410}
411
412pub unsafe fn fxsave(addr: i32) {
413    dbg_assert!(addr & 0xF == 0, "TODO: #gp");
414    return_on_pagefault!(writable_or_pagefault(addr, 288));
415
416    safe_write16(addr + 0, (*fpu_control_word).into()).unwrap();
417    safe_write16(addr + 2, fpu_load_status_word().into()).unwrap();
418    safe_write8(addr + 4, !*fpu_stack_empty as i32 & 0xFF).unwrap();
419    safe_write16(addr + 6, *fpu_opcode).unwrap();
420    safe_write32(addr + 8, *fpu_ip).unwrap();
421    safe_write16(addr + 12, *fpu_ip_selector).unwrap();
422    safe_write32(addr + 16, *fpu_dp).unwrap();
423    safe_write16(addr + 20, *fpu_dp_selector).unwrap();
424
425    safe_write32(addr + 24, *mxcsr).unwrap();
426    safe_write32(addr + 28, MXCSR_MASK).unwrap();
427
428    for i in 0..8 {
429        let reg_index = i + *fpu_stack_ptr as i32 & 7;
430        fpu_store_m80(addr + 32 + (i << 4), *fpu_st.offset(reg_index as isize));
431    }
432
433    // If the OSFXSR bit in control register CR4 is not set, the FXSAVE
434    // instruction may not save these registers. This behavior is
435    // implementation dependent.
436    for i in 0..8 {
437        safe_write128(addr + 160 + (i << 4), *reg_xmm.offset(i as isize)).unwrap();
438    }
439}
440pub unsafe fn fxrstor(addr: i32) {
441    dbg_assert!(addr & 0xF == 0, "TODO: #gp");
442    return_on_pagefault!(readable_or_pagefault(addr, 288));
443
444    let new_mxcsr = safe_read32s(addr + 24).unwrap();
445
446    if 0 != new_mxcsr & !MXCSR_MASK {
447        dbg_log!("#gp Invalid mxcsr bits");
448        trigger_gp(0);
449        return;
450    }
451
452    set_control_word(safe_read16(addr + 0).unwrap() as u16);
453    fpu_set_status_word(safe_read16(addr + 2).unwrap() as u16);
454    *fpu_stack_empty = !safe_read8(addr + 4).unwrap() as u8;
455    *fpu_opcode = safe_read16(addr + 6).unwrap();
456    *fpu_ip = safe_read32s(addr + 8).unwrap();
457    *fpu_ip_selector = safe_read16(addr + 12).unwrap();
458    *fpu_dp = safe_read32s(addr + 16).unwrap();
459    *fpu_dp_selector = safe_read16(addr + 20).unwrap();
460
461    set_mxcsr(new_mxcsr);
462
463    for i in 0..8 {
464        let reg_index = *fpu_stack_ptr as i32 + i & 7;
465        *fpu_st.offset(reg_index as isize) = fpu_load_m80(addr + 32 + (i << 4)).unwrap();
466    }
467
468    for i in 0..8 {
469        *reg_xmm.offset(i as isize) = safe_read128s(addr + 160 + (i << 4)).unwrap();
470    }
471}
472
473pub unsafe fn xchg8(data: i32, r8: i32) -> i32 {
474    let tmp = read_reg8(r8);
475    write_reg8(r8, data);
476    return tmp;
477}
478pub unsafe fn xchg16(data: i32, r16: i32) -> i32 {
479    let tmp = read_reg16(r16);
480    write_reg16(r16, data);
481    return tmp;
482}
483pub unsafe fn xchg16r(r16: i32) {
484    let tmp = read_reg16(AX);
485    write_reg16(AX, read_reg16(r16));
486    write_reg16(r16, tmp);
487}
488pub unsafe fn xchg32(data: i32, r32: i32) -> i32 {
489    let tmp = read_reg32(r32);
490    write_reg32(r32, data);
491    return tmp;
492}
493pub unsafe fn xchg32r(r32: i32) {
494    let tmp = read_reg32(EAX);
495    write_reg32(EAX, read_reg32(r32));
496    write_reg32(r32, tmp);
497}
498
499pub unsafe fn bswap(r: i32) {
500    write_reg32(r, read_reg32(r).swap_bytes())
501}
502
503pub unsafe fn lar(selector: i32, original: i32) -> i32 {
504    if false {
505        dbg_log!("lar sel={:x}", selector);
506    }
507
508    const LAR_INVALID_TYPE: u32 =
509        1 << 0 | 1 << 6 | 1 << 7 | 1 << 8 | 1 << 0xA | 1 << 0xD | 1 << 0xE | 1 << 0xF;
510
511    let sel = SegmentSelector::of_u16(selector as u16);
512    match lookup_segment_selector(sel) {
513        Err(()) => {
514            // pagefault
515            return original;
516        }
517        Ok(Err(_)) => {
518            *flags_changed &= !FLAG_ZERO;
519            *flags &= !FLAG_ZERO;
520            dbg_log!("lar: invalid selector={:x}: null or invalid", selector);
521            return original;
522        }
523        Ok(Ok((desc, _))) => {
524            *flags_changed &= !FLAG_ZERO;
525            let dpl_bad = desc.dpl() < *cpl || desc.dpl() < sel.rpl();
526
527            if if desc.is_system() {
528                (LAR_INVALID_TYPE >> desc.system_type() & 1 == 1) || dpl_bad
529            } else {
530                !desc.is_conforming_executable() && dpl_bad
531            } {
532                dbg_log!(
533                    "lar: invalid selector={:x} is_null={} is_system={}",
534                    selector,
535                    false,
536                    desc.is_system()
537                );
538                *flags &= !FLAG_ZERO;
539                return original;
540            } else {
541                *flags |= FLAG_ZERO;
542                return (desc.raw >> 32) as i32 & 0x00FFFF00;
543            }
544        }
545    }
546}
547
548pub unsafe fn lsl(selector: i32, original: i32) -> i32 {
549    if false {
550        dbg_log!("lsl sel={:x}", selector);
551    }
552
553    const LSL_INVALID_TYPE: i32 = 1 << 0
554        | 1 << 4
555        | 1 << 5
556        | 1 << 6
557        | 1 << 7
558        | 1 << 8
559        | 1 << 0xA
560        | 1 << 0xC
561        | 1 << 0xD
562        | 1 << 0xE
563        | 1 << 0xF;
564
565    let sel = SegmentSelector::of_u16(selector as u16);
566    match lookup_segment_selector(sel) {
567        Err(()) => {
568            // pagefault
569            return original;
570        }
571        Ok(Err(_)) => {
572            *flags_changed &= !FLAG_ZERO;
573            *flags &= !FLAG_ZERO;
574            dbg_log!("lsl: invalid selector={:x}: null or invalid", selector);
575            return original;
576        }
577        Ok(Ok((desc, _))) => {
578            *flags_changed &= !FLAG_ZERO;
579            let dpl_bad = desc.dpl() < *cpl || desc.dpl() < sel.rpl();
580
581            if if desc.is_system() {
582                (LSL_INVALID_TYPE >> desc.system_type() & 1 == 1) || dpl_bad
583            } else {
584                !desc.is_conforming_executable() && dpl_bad
585            } {
586                dbg_log!(
587                    "lsl: invalid  selector={:x} is_null={} is_system={}",
588                    selector,
589                    false,
590                    desc.is_system(),
591                );
592                *flags &= !FLAG_ZERO;
593                return original;
594            } else {
595                *flags |= FLAG_ZERO;
596                return desc.effective_limit() as i32;
597            }
598        }
599    }
600}
601
602pub unsafe fn verr(selector: i32) {
603    *flags_changed &= !FLAG_ZERO;
604    let sel = SegmentSelector::of_u16(selector as u16);
605    match return_on_pagefault!(lookup_segment_selector(sel)) {
606        Err(_) => {
607            *flags &= !FLAG_ZERO;
608            dbg_log!("verr -> invalid. selector={:x}", selector);
609        }
610        Ok((desc, _)) => {
611            if desc.is_system()
612                || !desc.is_readable()
613                || (!desc.is_conforming_executable()
614                    && (desc.dpl() < *cpl || desc.dpl() < sel.rpl()))
615            {
616                dbg_log!("verr -> invalid. selector={:x}", selector);
617                *flags &= !FLAG_ZERO;
618            } else {
619                dbg_log!("verr -> valid. selector={:x}", selector);
620                *flags |= FLAG_ZERO;
621            }
622        }
623    }
624}
625
626pub unsafe fn verw(selector: i32) {
627    *flags_changed &= !FLAG_ZERO;
628    let sel = SegmentSelector::of_u16(selector as u16);
629    match return_on_pagefault!(lookup_segment_selector(sel)) {
630        Err(_) => {
631            *flags &= !FLAG_ZERO;
632            dbg_log!("verw -> invalid. selector={:x}", selector);
633        }
634        Ok((desc, _)) => {
635            if desc.is_system()
636                || !desc.is_writable()
637                || desc.dpl() < *cpl
638                || desc.dpl() < sel.rpl()
639            {
640                dbg_log!(
641                    "verw invalid selector={:x} is_system={} is_writable={}",
642                    selector,
643                    desc.is_system(),
644                    desc.is_writable(),
645                );
646                *flags &= !FLAG_ZERO;
647            } else {
648                *flags |= FLAG_ZERO;
649            }
650        }
651    }
652}