snapshot of carry flag implementation
Tests failing. This approach seems wrong. I'm not sure even the tests are correct. Also, some open questions: 1. Should setting the overflow flag always set the carry flag? 2. Should the carry flag only be set on add/subtract/compare, or by all arithmetic ops? 3. Had to turn off the -ftrapv flag in `build`. Is there a way to detect overflow without actually causing overflow? Once we start setting CF correctly we have to implement jump above/below instructions (8- and 32-bit displacement variants). https://github.com/akkartik/mu/issues/30
This commit is contained in:
parent
d5d43e044d
commit
8c1a69089b
|
@ -62,7 +62,10 @@ put_new(Help, "registers",
|
|||
"- the sign flag (SF): usually set if an arithmetic result is negative, or\n"
|
||||
" reset if not.\n"
|
||||
"- the zero flag (ZF): usually set if a result is zero, or reset if not.\n"
|
||||
"- the overflow flag (OF): usually set if an arithmetic result overflows.\n"
|
||||
"- the carry flag (CF): usually set if an arithmetic result overflows by just one bit.\n"
|
||||
" Useful for operating on unsigned numbers.\n"
|
||||
"- the overflow flag (OF): usually set if an arithmetic result overflows by more than one bit.\n"
|
||||
" Useful for operating on signed numbers.\n"
|
||||
"The flag bits are read by conditional jumps.\n"
|
||||
"\n"
|
||||
"For complete details on how different instructions update the flags, consult the IA-32\n"
|
||||
|
@ -78,9 +81,10 @@ put_new(Help, "registers",
|
|||
// the subset of x86 flag registers we care about
|
||||
bool SF = false; // sign flag
|
||||
bool ZF = false; // zero flag
|
||||
bool CF = false; // carry flag
|
||||
bool OF = false; // overflow flag
|
||||
:(before "End Reset")
|
||||
SF = ZF = OF = false;
|
||||
SF = ZF = CF = OF = false;
|
||||
|
||||
//: how the flag registers are updated after each instruction
|
||||
|
||||
|
@ -88,25 +92,35 @@ SF = ZF = OF = false;
|
|||
// Combine 'arg1' and 'arg2' with arithmetic operation 'op' and store the
|
||||
// result in 'arg1', then update flags.
|
||||
// beware: no side-effects in args
|
||||
#define BINARY_ARITHMETIC_OP(op, arg1, arg2) { \
|
||||
/* arg1 and arg2 must be signed */ \
|
||||
int64_t tmp = arg1 op arg2; \
|
||||
arg1 = arg1 op arg2; \
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << arg1 << end(); \
|
||||
SF = (arg1 < 0); \
|
||||
ZF = (arg1 == 0); \
|
||||
OF = (arg1 != tmp); \
|
||||
#define BINARY_ARITHMETIC_OP(op, signed_arg1, signed_arg2) { \
|
||||
cerr << signed_arg1 << " vs " << signed_arg2 << '\n'; \
|
||||
int64_t signed_full_result = signed_arg1 op signed_arg2; \
|
||||
signed_arg1 = signed_arg1 op signed_arg2; \
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << signed_arg1 << end(); \
|
||||
SF = (signed_arg1 < 0); \
|
||||
ZF = (signed_arg1 == 0); \
|
||||
OF = (signed_arg1 != signed_full_result); \
|
||||
/* CF is more complex */ \
|
||||
uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1); \
|
||||
uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); \
|
||||
cerr << unsigned_arg1 << " vs " << unsigned_arg2 << '\n'; \
|
||||
uint32_t unsigned_result = unsigned_arg1 op unsigned_arg2; \
|
||||
cerr << "result: " << unsigned_result << '\n'; \
|
||||
uint64_t unsigned_full_result = unsigned_arg1 op unsigned_arg2; \
|
||||
CF = (unsigned_result != unsigned_full_result); \
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \
|
||||
}
|
||||
|
||||
// Combine 'arg1' and 'arg2' with bitwise operation 'op' and store the result
|
||||
// in 'arg1', then update flags.
|
||||
#define BINARY_BITWISE_OP(op, arg1, arg2) { \
|
||||
/* arg1 and arg2 must be unsigned */ \
|
||||
arg1 = arg1 op arg2; \
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << arg1 << end(); \
|
||||
SF = (arg1 >> 31); \
|
||||
ZF = (arg1 == 0); \
|
||||
#define BINARY_BITWISE_OP(op, unsigned_arg1, unsigned_arg2) { \
|
||||
unsigned_arg1 = unsigned_arg1 op unsigned_arg2; \
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << unsigned_arg1 << end(); \
|
||||
SF = (unsigned_arg1 >> 31); \
|
||||
ZF = (unsigned_arg1 == 0); \
|
||||
CF = false; \
|
||||
OF = false; \
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \
|
||||
}
|
||||
|
||||
//:: simulated RAM
|
||||
|
@ -374,7 +388,7 @@ void dump_registers() {
|
|||
if (i > 0) out << "; ";
|
||||
out << " " << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u;
|
||||
}
|
||||
out << " -- SF: " << SF << "; ZF: " << ZF << "; OF: " << OF;
|
||||
out << " -- SF: " << SF << "; ZF: " << ZF << "; CF: " << CF << "; OF: " << OF;
|
||||
trace(Callstack_depth+1, "run") << out.str() << end();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,24 @@ case 0x01: { // add r32 to r/m32
|
|||
break;
|
||||
}
|
||||
|
||||
:(code)
|
||||
void test_add_r32_to_r32_unsigned() {
|
||||
Reg[EAX].i = 0x7fffffff; // largest positive signed number
|
||||
Reg[EBX].i = 1;
|
||||
run(
|
||||
"== 0x1\n" // code segment
|
||||
// op ModR/M SIB displacement immediate
|
||||
" 01 d8 \n" // add EBX to EAX
|
||||
// ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
|
||||
);
|
||||
CHECK_TRACE_CONTENTS(
|
||||
"run: add EBX to r/m32\n"
|
||||
"run: r/m32 is EAX\n"
|
||||
"run: storing 0x80000000\n"
|
||||
"run: SF=1; ZF=0; CF=1; OF=0\n" // carry flag set
|
||||
);
|
||||
}
|
||||
|
||||
:(code)
|
||||
// Implement tables 2-2 and 2-3 in the Intel manual, Volume 2.
|
||||
// We return a pointer so that instructions can write to multiple bytes in
|
||||
|
@ -116,6 +134,24 @@ case 0x29: { // subtract r32 from r/m32
|
|||
break;
|
||||
}
|
||||
|
||||
:(code)
|
||||
void test_subtract_r32_from_r32_unsigned() {
|
||||
Reg[EAX].i = 0x7ffffffd;
|
||||
Reg[EBX].i = 0x7fffffff;
|
||||
run(
|
||||
"== 0x1\n" // code segment
|
||||
// op ModR/M SIB displacement immediate
|
||||
" 29 d8 \n" // subtract EBX from EAX
|
||||
// ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
|
||||
);
|
||||
CHECK_TRACE_CONTENTS(
|
||||
"run: subtract EBX from r/m32\n"
|
||||
"run: r/m32 is EAX\n"
|
||||
"run: storing 0xfffffffe\n"
|
||||
"run: SF=1; ZF=0; CF=1; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
//:: multiply
|
||||
|
||||
:(before "End Initialize Op Names")
|
||||
|
@ -686,7 +722,7 @@ void test_compare_r32_with_r32_greater() {
|
|||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EBX with r/m32\n"
|
||||
"run: r/m32 is EAX\n"
|
||||
"run: SF=0; ZF=0; OF=0\n"
|
||||
"run: SF=0; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -695,14 +731,23 @@ case 0x39: { // set SF if r/m32 < r32
|
|||
const uint8_t modrm = next();
|
||||
const uint8_t reg2 = (modrm>>3)&0x7;
|
||||
trace(Callstack_depth+1, "run") << "compare " << rname(reg2) << " with r/m32" << end();
|
||||
const int32_t* arg1 = effective_address(modrm);
|
||||
const int32_t arg2 = Reg[reg2].i;
|
||||
const int32_t tmp1 = *arg1 - arg2;
|
||||
SF = (tmp1 < 0);
|
||||
ZF = (tmp1 == 0);
|
||||
const int64_t tmp2 = *arg1 - arg2;
|
||||
OF = (tmp1 != tmp2);
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
const int32_t* signed_arg1 = effective_address(modrm);
|
||||
const int32_t signed_arg2 = Reg[reg2].i;
|
||||
cerr << *signed_arg1 << " vs " << signed_arg2 << '\n';
|
||||
const int32_t signed_difference = *signed_arg1 - signed_arg2;
|
||||
SF = (signed_difference < 0);
|
||||
ZF = (signed_difference == 0);
|
||||
const int64_t signed_full_difference = *signed_arg1 - signed_arg2;
|
||||
OF = (signed_difference != signed_full_difference);
|
||||
const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
|
||||
const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2);
|
||||
cerr << unsigned_arg1 << " vs " << unsigned_arg2 << '\n';
|
||||
const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2;
|
||||
cerr << "result: " << unsigned_difference << '\n';
|
||||
const uint64_t unsigned_full_difference = unsigned_arg1 - unsigned_arg2;
|
||||
cerr << "full result: " << unsigned_full_difference << '\n';
|
||||
CF = (unsigned_difference != unsigned_full_difference);
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -719,7 +764,23 @@ void test_compare_r32_with_r32_lesser() {
|
|||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EBX with r/m32\n"
|
||||
"run: r/m32 is EAX\n"
|
||||
"run: SF=1; ZF=0; OF=0\n"
|
||||
"run: SF=1; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
void test_compare_r32_with_r32_lesser_unsigned() {
|
||||
Reg[EAX].i = 0x7ffffffd;
|
||||
Reg[EBX].i = 0x7fffffff;
|
||||
run(
|
||||
"== 0x1\n" // code segment
|
||||
// op ModR/M SIB displacement immediate
|
||||
" 39 d8 \n" // compare EBX with EAX
|
||||
// ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
|
||||
);
|
||||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EBX with r/m32\n"
|
||||
"run: r/m32 is EAX\n"
|
||||
"run: SF=1; ZF=0; CF=1; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -735,7 +796,7 @@ void test_compare_r32_with_r32_equal() {
|
|||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EBX with r/m32\n"
|
||||
"run: r/m32 is EAX\n"
|
||||
"run: SF=0; ZF=1; OF=0\n"
|
||||
"run: SF=0; ZF=1; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -320,7 +320,7 @@ void test_compare_mem_at_r32_with_r32_greater() {
|
|||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EBX with r/m32\n"
|
||||
"run: effective address is 0x00002000 (EAX)\n"
|
||||
"run: SF=0; ZF=0; OF=0\n"
|
||||
"run: SF=0; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -339,7 +339,7 @@ void test_compare_mem_at_r32_with_r32_lesser() {
|
|||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EBX with r/m32\n"
|
||||
"run: effective address is 0x00002000 (EAX)\n"
|
||||
"run: SF=1; ZF=0; OF=0\n"
|
||||
"run: SF=1; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -358,7 +358,7 @@ void test_compare_mem_at_r32_with_r32_equal() {
|
|||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EBX with r/m32\n"
|
||||
"run: effective address is 0x00002000 (EAX)\n"
|
||||
"run: SF=0; ZF=1; OF=0\n"
|
||||
"run: SF=0; ZF=1; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -382,7 +382,7 @@ void test_compare_r32_with_mem_at_r32_greater() {
|
|||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare r/m32 with EBX\n"
|
||||
"run: effective address is 0x00002000 (EAX)\n"
|
||||
"run: SF=0; ZF=0; OF=0\n"
|
||||
"run: SF=0; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -391,14 +391,19 @@ case 0x3b: { // set SF if r32 < r/m32
|
|||
const uint8_t modrm = next();
|
||||
const uint8_t reg1 = (modrm>>3)&0x7;
|
||||
trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg1) << end();
|
||||
const int32_t arg1 = Reg[reg1].i;
|
||||
const int32_t* arg2 = effective_address(modrm);
|
||||
const int32_t tmp1 = arg1 - *arg2;
|
||||
SF = (tmp1 < 0);
|
||||
ZF = (tmp1 == 0);
|
||||
int64_t tmp2 = arg1 - *arg2;
|
||||
OF = (tmp1 != tmp2);
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
const int32_t signed_arg1 = Reg[reg1].i;
|
||||
const int32_t* signed_arg2 = effective_address(modrm);
|
||||
const int32_t signed_difference = signed_arg1 - *signed_arg2;
|
||||
SF = (signed_difference < 0);
|
||||
ZF = (signed_difference == 0);
|
||||
int64_t full_signed_difference = signed_arg1 - *signed_arg2;
|
||||
OF = (signed_difference != full_signed_difference);
|
||||
const uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1);
|
||||
const uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
|
||||
const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2;
|
||||
const uint64_t full_unsigned_difference = unsigned_arg1 - unsigned_arg2;
|
||||
CF = (unsigned_difference != full_unsigned_difference);
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -417,7 +422,7 @@ void test_compare_r32_with_mem_at_r32_lesser() {
|
|||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare r/m32 with EBX\n"
|
||||
"run: effective address is 0x00002000 (EAX)\n"
|
||||
"run: SF=1; ZF=0; OF=0\n"
|
||||
"run: SF=1; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -436,7 +441,7 @@ void test_compare_r32_with_mem_at_r32_equal() {
|
|||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare r/m32 with EBX\n"
|
||||
"run: effective address is 0x00002000 (EAX)\n"
|
||||
"run: SF=0; ZF=1; OF=0\n"
|
||||
"run: SF=0; ZF=1; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -571,21 +571,26 @@ void test_compare_imm32_with_eax_greater() {
|
|||
);
|
||||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EAX and imm32 0x0d0c0b07\n"
|
||||
"run: SF=0; ZF=0; OF=0\n"
|
||||
"run: SF=0; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0x3d: { // compare EAX with imm32
|
||||
const int32_t arg1 = Reg[EAX].i;
|
||||
const int32_t arg2 = next32();
|
||||
trace(Callstack_depth+1, "run") << "compare EAX and imm32 0x" << HEXWORD << arg2 << end();
|
||||
const int32_t tmp1 = arg1 - arg2;
|
||||
SF = (tmp1 < 0);
|
||||
ZF = (tmp1 == 0);
|
||||
const int64_t tmp2 = arg1 - arg2;
|
||||
OF = (tmp1 != tmp2);
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
const int32_t signed_arg1 = Reg[EAX].i;
|
||||
const int32_t signed_arg2 = next32();
|
||||
trace(Callstack_depth+1, "run") << "compare EAX and imm32 0x" << HEXWORD << signed_arg2 << end();
|
||||
const int32_t signed_difference = signed_arg1 - signed_arg2;
|
||||
SF = (signed_difference < 0);
|
||||
ZF = (signed_difference == 0);
|
||||
const int64_t full_signed_difference = signed_arg1 - signed_arg2;
|
||||
OF = (signed_difference != full_signed_difference);
|
||||
const uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1);
|
||||
const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2);
|
||||
const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2;
|
||||
const uint64_t full_unsigned_difference = unsigned_arg1 - unsigned_arg2;
|
||||
CF = (unsigned_difference != full_unsigned_difference);
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -599,7 +604,7 @@ void test_compare_imm32_with_eax_lesser() {
|
|||
);
|
||||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EAX and imm32 0x0d0c0b0a\n"
|
||||
"run: SF=1; ZF=0; OF=0\n"
|
||||
"run: SF=1; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -613,7 +618,7 @@ void test_compare_imm32_with_eax_equal() {
|
|||
);
|
||||
CHECK_TRACE_CONTENTS(
|
||||
"run: compare EAX and imm32 0x0d0c0b0a\n"
|
||||
"run: SF=0; ZF=1; OF=0\n"
|
||||
"run: SF=0; ZF=1; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -632,7 +637,7 @@ void test_compare_imm32_with_r32_greater() {
|
|||
"run: combine imm32 with r/m32\n"
|
||||
"run: r/m32 is EBX\n"
|
||||
"run: imm32 is 0x0d0c0b07\n"
|
||||
"run: SF=0; ZF=0; OF=0\n"
|
||||
"run: SF=0; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -644,7 +649,7 @@ case 7: {
|
|||
ZF = (tmp1 == 0);
|
||||
const int64_t tmp2 = *arg1 - arg2;
|
||||
OF = (tmp1 != tmp2);
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -661,7 +666,7 @@ void test_compare_imm32_with_r32_lesser() {
|
|||
"run: combine imm32 with r/m32\n"
|
||||
"run: r/m32 is EBX\n"
|
||||
"run: imm32 is 0x0d0c0b0a\n"
|
||||
"run: SF=1; ZF=0; OF=0\n"
|
||||
"run: SF=1; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -678,7 +683,7 @@ void test_compare_imm32_with_r32_equal() {
|
|||
"run: combine imm32 with r/m32\n"
|
||||
"run: r/m32 is EBX\n"
|
||||
"run: imm32 is 0x0d0c0b0a\n"
|
||||
"run: SF=0; ZF=1; OF=0\n"
|
||||
"run: SF=0; ZF=1; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -697,7 +702,7 @@ void test_compare_imm32_with_mem_at_r32_greater() {
|
|||
"run: combine imm32 with r/m32\n"
|
||||
"run: effective address is 0x00002000 (EBX)\n"
|
||||
"run: imm32 is 0x0d0c0b07\n"
|
||||
"run: SF=0; ZF=0; OF=0\n"
|
||||
"run: SF=0; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -716,7 +721,7 @@ void test_compare_imm32_with_mem_at_r32_lesser() {
|
|||
"run: combine imm32 with r/m32\n"
|
||||
"run: effective address is 0x00002000 (EBX)\n"
|
||||
"run: imm32 is 0x0d0c0b0a\n"
|
||||
"run: SF=1; ZF=0; OF=0\n"
|
||||
"run: SF=1; ZF=0; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -736,7 +741,7 @@ void test_compare_imm32_with_mem_at_r32_equal() {
|
|||
"run: combine imm32 with r/m32\n"
|
||||
"run: effective address is 0x00002000 (EBX)\n"
|
||||
"run: imm32 is 0x0d0c0b0a\n"
|
||||
"run: SF=0; ZF=1; OF=0\n"
|
||||
"run: SF=0; ZF=1; CF=0; OF=0\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ UNTIL_LAYER=${2:-zzz}
|
|||
test "$CXX" || export CXX=c++
|
||||
test "$CC" || export CC=cc
|
||||
test "$CFLAGS" || export CFLAGS="-g -O3"
|
||||
export CFLAGS="$CFLAGS -Wall -Wextra -ftrapv -fno-strict-aliasing"
|
||||
export CFLAGS="$CFLAGS -Wall -Wextra -fno-strict-aliasing"
|
||||
|
||||
# return 1 if $1 is older than _any_ of the remaining args
|
||||
older_than() {
|
||||
|
|
Loading…
Reference in New Issue